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.
| Type | Quantity tracked | Typical use |
|---|---|---|
service | No | Time, expertise, consulting, design, anything you sell that isn't a thing |
inventory | Yes (asset + COGS layers) | Physical goods you stock and resell |
non_inventory | No | Physical 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
| Field | Required | Notes |
|---|---|---|
| Name | yes | What appears on invoice/bill pickers |
| Description | no | Default description on lines; override per line |
| SKU | no | Internal identifier; useful for searching, optional |
| Type | yes | One of service, inventory, non_inventory |
| Income account | service + inventory | Where revenue posts when sold |
| Expense account / COGS | inventory + non_inventory | Where cost posts |
| Asset account | inventory only | The Inventory Asset account this item posts to on receipt |
| Sales price | optional | Default unit price on new invoice lines |
| Cost | optional (default for non_inventory; computed for inventory) | Default unit cost on new bill lines |
| Cost method | inventory only | fifo or average |
| Average cost | inventory only | Computed; updates with each receipt for average-method items |
| Active flag | no | Inactive 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
| Transaction | Item 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 order | Pick an item per line. POs don't post to GL but reserve quantity. |
| Inventory adjustment | Item is required; adjusts on-hand quantity |
| Estimate | Pick an item; estimate gets the item's pricing as a starting point |
| Receipt of goods | Pick 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
- Inventory module — the deep dive on inventory-specific behavior
- Accounts Receivable → Invoicing — how items appear on invoices
- Accounts Payable → Bills — how items appear on bills
- Estimates module — how items populate estimate lines