Inventory Tracking for Product Sellers: COGS Calculation in Beancount

Hey everyone,

I’ve been helping a few e-commerce clients get their books set up in Beancount, and inventory tracking for COGS (Cost of Goods Sold) is always one of the trickiest parts. Figured I’d share what I’ve learned and see how others are handling this.

The COGS Formula

The fundamental formula we need to track is:

COGS = Beginning Inventory + Purchases + Direct Costs - Ending Inventory

Sounds simple, right? But in plain text accounting, you need to actually structure your accounts to capture each piece correctly.

My Recommended Account Structure

Here’s what I set up for product-based businesses:

; Inventory accounts
Assets:Inventory:RawMaterials
Assets:Inventory:FinishedGoods
Assets:Inventory:PackagingSupplies

; COGS expense accounts
Expenses:COGS:ProductCost
Expenses:COGS:ShippingIn
Expenses:COGS:PackagingMaterials

; Purchase tracking
Expenses:Purchases:Inventory

Recording a Purchase

When inventory arrives:

2026-02-15 * "Supplier Co" "Widget inventory purchase - 100 units @ $15"
  Assets:Inventory:FinishedGoods    1500.00 USD
  Assets:Bank:Checking             -1500.00 USD

Recording a Sale (with COGS)

This is the key part - when you sell, you need two transactions:

2026-02-18 * "Customer" "Sold 10 widgets @ $25"
  Assets:Bank:Checking              250.00 USD
  Income:Sales:Products            -250.00 USD

2026-02-18 * "COGS" "Cost basis for 10 widgets sold"
  Expenses:COGS:ProductCost         150.00 USD
  Assets:Inventory:FinishedGoods   -150.00 USD

Choosing an Inventory Valuation Method

There are three common approaches:

  1. FIFO (First In, First Out) - Oldest inventory sells first. Best for perishables and matches how most physical inventory actually moves.

  2. LIFO (Last In, First Out) - Newest inventory sells first. Can provide tax advantages in inflationary periods (though not accepted for IFRS).

  3. Weighted Average Cost - Simplifies calculations for non-perishables where exact lot tracking isn’t practical.

For most of my small business clients using Beancount, I recommend FIFO since it aligns with physical reality and is accepted everywhere.

Questions for the Community

  • How are you handling lot tracking in Beancount for inventory that has varying purchase prices?
  • Anyone using the built-in cost basis tracking for inventory (not just investments)?
  • What queries do you run to calculate your month-end COGS automatically?

Would love to hear what’s working for others!

Great overview, Bob! As a CPA, I want to add some important tax considerations for inventory tracking.

IRS Requirements for COGS

The IRS cares deeply about your COGS calculation because it directly impacts your taxable income. A few things to keep in mind:

  1. Section 471 Regulations - If your average annual gross receipts exceed $29 million (2026 threshold), you must use the accrual method for inventory. Smaller businesses have more flexibility.

  2. Consistency Requirement - Once you choose FIFO, LIFO, or weighted average, you need to stick with it. The IRS doesn’t look kindly on switching methods to reduce taxes.

  3. Uniform Capitalization Rules (UNICAP) - If you manufacture or produce goods, Section 263A may require you to capitalize certain indirect costs into inventory (like storage, handling, administrative overhead).

Beancount Cost Basis Syntax for Lot Tracking

For your question about varying purchase prices, I use Beancount’s built-in cost tracking syntax - the same one people use for investments. Here’s an example:

; Purchase inventory at different prices
2026-01-10 * "Supplier" "Purchase 50 widgets @ $12"
  Assets:Inventory:Widgets     50 WIDGET {12.00 USD}
  Assets:Bank:Checking        -600.00 USD

2026-02-01 * "Supplier" "Purchase 75 widgets @ $14"  
  Assets:Inventory:Widgets     75 WIDGET {14.00 USD}
  Assets:Bank:Checking        -1050.00 USD

; Sell using FIFO (automatic in Beancount)
2026-02-15 * "Sale" "Sold 60 widgets"
  Expenses:COGS:Widgets        60 WIDGET {12.00 USD} @ 25.00 USD
  Assets:Inventory:Widgets    -60 WIDGET {12.00 USD}
  Assets:Bank:Checking         1500.00 USD
  Income:Sales:Widgets        -1500.00 USD

This way Beancount tracks each lot separately, and you can run bean-query to see exactly what inventory you have at what cost basis.

Pro Tip: Year-End Physical Count

No matter how good your Beancount tracking is, you need a physical inventory count at year-end. I usually help clients add an adjustment entry:

2026-12-31 * "Year-end inventory adjustment"
  Assets:Inventory:Widgets    -5 WIDGET {13.20 USD}  ; shrinkage
  Expenses:COGS:InventoryShrinkage    66.00 USD

The $13.20 would be your weighted average cost, which is acceptable for adjustments even if you use FIFO for sales.

Happy to answer any tax-specific questions!

Both of you have shared excellent technical details! Let me add a more practical perspective from someone who’s been using Beancount for 4+ years.

Start Simple, Grow Complexity

When I first tried tracking inventory in Beancount, I over-engineered it immediately. Lot tracking with custom commodities for every SKU? Yeah, that lasted about two weeks before I gave up.

Here’s my advice for anyone just starting:

Phase 1: Don’t use lot tracking at all

If you’re a small seller with fairly consistent pricing, just use weighted average cost and track inventory in dollars, not units:

Assets:Inventory:Products    5000.00 USD

; When you sell, estimate your COGS as a percentage
2026-02-18 * "Monthly COGS estimate" "Based on 65% cost ratio"
  Expenses:COGS               3250.00 USD
  Assets:Inventory:Products  -3250.00 USD

Is this perfect? No. Is it 95% accurate and takes 10 minutes a month? Yes.

Phase 2: Add unit tracking when it matters

Once you have the discipline of monthly reconciliation, then add complexity. Alice’s lot tracking syntax is the way to go when you’re ready.

My COGS Query

For Bob’s question about automating month-end COGS, here’s the query I use:

SELECT 
  account,
  sum(position) as total_cost
WHERE 
  account ~ 'Expenses:COGS'
  AND date >= 2026-01-01
  AND date < 2026-02-01
GROUP BY account

Run with bean-query ledger.beancount cogs.query and you get a nice summary.

One Thing Nobody Mentions

The hardest part isn’t the Beancount syntax - it’s actually knowing your cost per unit in real life.

Shipping costs, platform fees, returns, damaged goods… Do these go into COGS or operating expenses? The answer affects your gross margin calculation.

My rule: If it’s a cost that scales directly with each unit sold, it goes in COGS. If it’s a fixed cost regardless of volume, it’s an operating expense.

Keep it practical, folks! The perfect is the enemy of the good when it comes to inventory tracking.

Adding some tax season perspective here since we’re in the thick of it!

Schedule C Line 38: COGS Matters

For sole proprietors selling products, your COGS flows directly into Schedule C. Getting this wrong can trigger IRS attention because it directly reduces your self-employment tax.

The IRS specifically looks at your gross profit margin (Sales - COGS) / Sales. If yours is dramatically different from industry averages, expect questions.

Common COGS Mistakes I See During Tax Season

1. Mixing inventory purchases with operating expenses

I can’t tell you how many clients dump everything into “Supplies” or “Materials” without distinguishing between:

  • Items purchased for resale (COGS)
  • Items consumed in operations (Office Supplies expense)
; WRONG - all lumped together
Expenses:Supplies    500.00 USD

; RIGHT - separated properly  
Assets:Inventory:ForResale       350.00 USD   ; Goes to COGS when sold
Expenses:Office:Supplies         150.00 USD   ; Direct deduction

2. Forgetting to capitalize shipping-in costs

When you pay shipping to receive inventory, that should be added to your inventory cost basis, not expensed immediately:

2026-02-15 * "Supplier" "100 widgets + shipping"
  Assets:Inventory:Widgets    1550.00 USD  ; $1500 widgets + $50 shipping
  Assets:Bank:Checking       -1550.00 USD

3. Not tracking inventory separately by year

For tax purposes, you need to know your beginning and ending inventory values for each calendar year. I recommend adding year-end balance assertions:

2025-12-31 balance Assets:Inventory:Products    8500.00 USD
2026-12-31 balance Assets:Inventory:Products    9200.00 USD

; The difference tells you:
; Purchases added + COGS removed = $700 net increase

Quick Tax Check Before Filing

Before you file, run this sanity check:

  1. Beginning Inventory + Purchases - Ending Inventory = COGS (approximately)
  2. COGS should be between 40-80% of revenue for most product businesses
  3. If your gross margin suddenly changed from prior years, have an explanation ready

Hope this helps everyone avoid audit triggers this season!