Items

Items are the things you buy and sell, abstracted as reusable records. The same Consulting item can land on every invoice you write to a consulting client; the same Widget item can be ordered, received, sold, and counted in inventory — all driven by one entry in the items list.

This page covers the items module. For the inventory-specific behaviors (FIFO, weighted-average, purchase orders, adjustments), see the Inventory module.

The three item types

Every item is one of three types. The type determines which accounts the item can post to and whether quantity tracking happens.

TypeQuantity trackedTypical use
serviceNoTime, expertise, consulting, design, anything you sell that isn't a thing
inventoryYes (asset + COGS layers)Physical goods you stock and resell
non_inventoryNoPhysical goods you sell but don't stock — drop-ship, special order, custom builds

The differences:

  • Service items post to one account: revenue. No COGS, no asset side.
  • Inventory items post to three: revenue (on sale), COGS (on sale), and inventory asset (on receipt). Quantity-on-hand is tracked; cost flows through FIFO or weighted-average as configured.
  • Non-inventory items post to two: revenue and an expense (the cost of the special order, recorded at sale time, not from a stock).

What an item stores

FieldRequiredNotes
NameyesWhat appears on invoice/bill pickers
DescriptionnoDefault description on lines; override per line
SKUnoInternal identifier; useful for searching, optional
TypeyesOne of service, inventory, non_inventory
Income accountservice + inventoryWhere revenue posts when sold
Expense account / COGSinventory + non_inventoryWhere cost posts
Asset accountinventory onlyThe Inventory Asset account this item posts to on receipt
Sales priceoptionalDefault unit price on new invoice lines
Costoptional (default for non_inventory; computed for inventory)Default unit cost on new bill lines
Cost methodinventory onlyfifo or average
Average costinventory onlyComputed; updates with each receipt for average-method items
Active flagnoInactive items hidden from pickers; history preserved

Account mapping — why three accounts

The three account fields on an inventory item exist to let one item flow correctly through multiple stages:

1. You buy 100 widgets at $5
   → DR Inventory Asset (asset_account)         500
   → CR Bank or AP                              500

2. You sell 30 widgets at $12
   → DR Bank or AR                              360
   → CR Sales Revenue (income_account)          360

3. The COGS posting (auto, simultaneous with sale)
   → DR Cost of Goods Sold (expense_account)    150
   → CR Inventory Asset (asset_account)         150

4. After all that:
   - Inventory Asset has 70 × $5 = $350
   - Sales Revenue has $360
   - COGS has $150
   - Gross profit on the sale: $360 − $150 = $210

Three accounts capture three concerns: what you own (asset), what you earn (revenue), what you spent (COGS). The model only works because every item reliably knows which account is which.

For service and non-inventory items the picture simplifies: service uses income_account only; non-inventory uses income_account (revenue on sale) and expense_account (cost recognized at sale time, no asset stage).

How items flow through transactions

TransactionItem interaction
Invoice (sales)Pick an item per line. Solid pre-fills description, unit price, and revenue account.
Sales receipt (cash sale)Same as invoice — items populate lines
Bill (purchases)Pick an item per line. For inventory, this creates or extends a cost layer.
Purchase orderPick an item per line. POs don't post to GL but reserve quantity.
Inventory adjustmentItem is required; adjusts on-hand quantity
EstimatePick an item; estimate gets the item's pricing as a starting point
Receipt of goodsPick an item; updates quantity on hand and cost layer (for inventory)

The line-level item_id, quantity, and unit_price_cents columns on journal_entry_lines are how this flow gets recorded — every line that came from an item picker has those fields populated.

Hierarchy and grouping

Items support parent/child relationships. A Consulting parent might have Strategy, Implementation, and Training as children, each posting to the same revenue account but with distinct names and prices.

This is most useful for:

  • Service categorization (above)
  • Inventory line-extending (a T-shirt parent with Small / Medium / Large children)
  • Project-based services (a Quarterly Review item with year-specific children for tracking purposes)

The hierarchy is purely organizational — it doesn't affect accounting (every item still posts to its own configured accounts).

Pricing

Two prices on each item:

  • Sales price — default unit price when the item appears on an invoice/sales receipt/estimate
  • Cost — default unit cost when the item appears on a bill / PO / receipt of goods

Both are defaults — every line lets you override. Common cases for override:

  • Customer-specific pricing (this customer gets 10% off)
  • Volume discount on a single line
  • Time-and-materials work where the unit price is the actual hours billed

Some products implement customer-pricing-tiers as a separate feature. Solid leans on the per-line override path: if a customer always gets a discount, just type the discounted price in. Power-users can use the REST API to apply pricing rules at invoice generation, but that's outside the standard flow.

Multi-currency

Items don't have a currency — they're priced in whatever currency the transaction uses. The same item on an invoice to a USD customer and an invoice to an EUR customer simply has the same numeric default price interpreted in each currency.

If you sell to multiple currencies and want different prices per currency, the cleanest approach is one item per currency (a Consulting (USD) and a Consulting (EUR)), each priced for its market. The item ledger isn't currency-aware itself.

Inactive items

Items can be marked inactive to hide them from pickers without deleting the historical record. Use this for:

  • Discontinued products
  • Old service lines no longer offered
  • Imported items from a migration that need cleanup

History stays — every transaction that referenced the now-inactive item still shows the item name, account mapping, and pricing as they were at the time. Reports that look at item-level data treat inactive items as separate-but-included unless you filter them out.

Common gotchas

Created an inventory item but the COGS posting isn't happening. Check that the item type is inventory, not non_inventory. Non-inventory items post cost as expense at sale time without going through the inventory asset — that's correct for drop-ship/special-order, wrong for stocked items.

Item shows quantity on hand but the inventory asset balance doesn't match. Usually a setup issue — the item's asset_account_id doesn't match the actual inventory asset account that was debited on receipt. Edit the item and fix the asset account; the balance should reconcile after the next adjustment posts.

Switched an item from service to inventory. Risky — Solid blocks the switch on items with posted history because the GL effect of past transactions is incompatible. To migrate: deactivate the original service item and create a new inventory item with the same name (the name is allowed to clash with an inactive item).

Default sales price not appearing on new invoice lines. The price is a default, not a hard rule — it only fills when the item is picked from the picker. Typing a description manually without picking the item won't fill the price.

Item picker is slow with thousands of items. Solid's picker is indexed; if it's slow, consider the active-flag trick — most users have hundreds of historically-imported items but only ~30 they actively use. Mark old ones inactive and the picker filters them out.

Cross-references