Your Inventory Cost Is Probably Wrong—and Your P&L Knows It
Most Odoo implementations get inventory valuation wrong on the first pass. The warehouse team picks FIFO because it sounds right. The accountant wants Standard Cost because it simplifies variance analysis. Nobody configures the stock valuation accounts, so every goods receipt posts to the default expense account instead of creating a proper balance sheet asset. Three months later, the CFO stares at a P&L where COGS fluctuates wildly and the inventory balance on the balance sheet bears no resemblance to what is sitting on the shelves.
The business impact compounds silently. Incorrect valuation distorts gross margins by product line, makes landed cost allocation guesswork, and turns year-end inventory audits into multi-week reconciliation projects. If you import goods internationally, missing landed costs (freight, customs duties, insurance) mean your unit costs are understated by 8–15%—and every pricing decision built on those costs is wrong.
Odoo 19 supports all three major costing methods (FIFO, AVCO, Standard), perpetual and periodic valuation modes, landed cost distribution, and inventory revaluation. This guide covers how each method works under the hood, how to configure the stock valuation accounts, how to allocate landed costs correctly, and the mistakes that silently corrupt your financial statements.
FIFO, AVCO, and Standard Cost: How Odoo 19 Calculates Unit Cost
Odoo 19 offers three costing methods at the product category level. The choice determines how the system computes the unit cost when goods leave the warehouse—and therefore what posts to COGS on every delivery order. Choosing the wrong method doesn't cause an error; it causes months of silently wrong financial data.
| Method | Unit Cost Calculation | Best For | COGS Behavior |
|---|---|---|---|
| FIFO (First In, First Out) | Outgoing goods use the cost of the oldest incoming layer | Perishables, serial-tracked goods, industries with volatile purchase prices | COGS reflects actual historical purchase price per unit |
| AVCO (Average Cost) | Weighted average recalculated on every receipt | Commodities, bulk materials, products with stable pricing | COGS smooths out price fluctuations across receipts |
| Standard Cost | Fixed cost set manually; variances posted to a separate account | Manufacturing, budgeting environments, products with contract pricing | COGS is predictable; purchase price variances are isolated |
FIFO: Layer-Based Costing
FIFO tracks inventory in cost layers. Each purchase receipt creates a new layer with its own unit cost and quantity. When goods ship out, Odoo consumes layers from oldest to newest. The key data model is stock.valuation.layer—one record per receipt, adjusted as units are consumed.
# Receipt 1: 100 units @ $10.00 (Layer A)
# Receipt 2: 50 units @ $12.00 (Layer B)
# Current stock: 150 units
# Delivery: 120 units
# Odoo consumes:
# Layer A: 100 units @ $10.00 = $1,000.00
# Layer B: 20 units @ $12.00 = $ 240.00
# Total COGS: = $1,240.00
# Remaining stock:
# Layer B: 30 units @ $12.00 = $ 360.00AVCO: Weighted Average Recalculation
Average cost recalculates on every incoming move. The formula is straightforward, but the timing matters—returns, scrap, and inter-warehouse transfers all trigger recalculations:
# Before receipt:
# On hand: 100 units, total value: $1,000 (avg $10.00)
# New receipt: 50 units @ $13.00 = $650.00
# New average cost:
# ($1,000 + $650) / (100 + 50) = $1,650 / 150 = $11.00
# Next delivery of 80 units:
# COGS = 80 x $11.00 = $880.00Standard Cost: Fixed Price with Variance Tracking
Standard cost uses a fixed unit price that you set manually on the product form. When the actual purchase price differs from the standard, Odoo posts the difference to a price difference account (purchase price variance). This isolates procurement efficiency from production costing:
# Product standard cost: $10.00
# Actual purchase: 200 units @ $10.75
# Journal entry on receipt:
# Debit Stock Valuation $2,000.00 (200 x $10.00)
# Debit Price Difference $ 150.00 (200 x $0.75)
# Credit Stock Input $2,150.00 (200 x $10.75)
# COGS on delivery always uses $10.00 per unit
# Variance of $150 is visible in the price difference accountIf your purchase prices fluctuate more than 10% quarter-over-quarter, FIFO gives the most accurate per-unit COGS. If prices are stable and you want smooth margins, AVCO is simpler to maintain. If your finance team budgets using predetermined rates and wants to track procurement variances separately, Standard Cost is the right choice. You set the method per product category, so you can use different methods for different product lines.
Perpetual vs. Periodic Valuation: Configuring Stock Accounts in Odoo 19
The costing method determines how Odoo calculates the unit cost. The inventory valuation mode determines when those costs hit your general ledger. This is where most implementations go wrong—the product category is set to "Manual" (periodic) when the finance team expects real-time journal entries, or vice versa.
| Mode | Journal Entries Created | When to Use |
|---|---|---|
| Automated (Perpetual) | Every stock move (receipt, delivery, scrap, transfer) generates a journal entry in real time | Companies that need real-time inventory on the balance sheet. Required for accurate FIFO/AVCO tracking. |
| Manual (Periodic) | No automatic journal entries. Accountant posts valuation adjustments at period end. | Small businesses, or companies whose accounting system of record is external to Odoo. |
Setting Up the Stock Valuation Accounts
When using automated (perpetual) valuation, you must configure four accounts on the product category. Missing any one of these causes posting errors or, worse, silent mispostings to default accounts:
# Navigate to:
# Inventory > Configuration > Product Categories
# Account Properties (on the product category form):
# ─────────────────────────────────────────────────────
# 1. Stock Valuation Account (Balance Sheet - Asset)
# e.g., 1400 - Inventory / Stock Valuation
# Purpose: Holds the current value of on-hand inventory.
# Debited on receipt, credited on delivery.
#
# 2. Stock Input Account (Balance Sheet - Liability)
# e.g., 2100 - Goods Received Not Invoiced
# Purpose: Transit account for incoming goods.
# Credited on receipt, debited when vendor bill is posted.
# Also called "Goods In Transit" or "GR/IR".
#
# 3. Stock Output Account (Balance Sheet - Asset)
# e.g., 1410 - Goods Delivered Not Invoiced
# Purpose: Transit account for outgoing goods.
# Debited on delivery, credited when customer invoice is posted.
#
# 4. Price Difference Account (P&L - Expense)
# e.g., 5150 - Purchase Price Variance
# Purpose: Captures difference between standard/expected
# cost and actual purchase price.
# Required for Standard Cost. Recommended for all methods.Journal Entry Flow: Receipt to Sale
Understanding the full journal entry chain from purchase receipt through sale helps you verify that your configuration is correct. Here is the complete flow for perpetual valuation with AVCO:
# ── Step 1: Goods Receipt (Validate incoming shipment) ──
# Debit 1400 Stock Valuation $5,000
# Credit 2100 Stock Input (GR/IR) $5,000
# ── Step 2: Vendor Bill (Match to PO) ──
# Debit 2100 Stock Input (GR/IR) $5,000
# Credit 2000 Accounts Payable $5,000
# Note: GR/IR account nets to zero once bill is matched
# ── Step 3: Delivery Order (Ship to customer) ──
# Debit 1410 Stock Output $5,000
# Credit 1400 Stock Valuation $5,000
# ── Step 4: Customer Invoice ──
# Debit 1200 Accounts Receivable $8,000
# Credit 4000 Revenue $8,000
# Debit 5100 COGS $5,000
# Credit 1410 Stock Output $5,000
# Result: Inventory asset decreased, COGS recognized,
# gross profit = $8,000 - $5,000 = $3,000 Odoo 19 uses Anglo-Saxon accounting by default for perpetual valuation, which means COGS is recognized at the time of sale (delivery + invoice). In Continental accounting, the expense is recognized at the time of purchase. If your country follows Continental conventions, ensure the anglo_saxon_accounting setting on your company is disabled—otherwise your COGS timing will be wrong, and your monthly P&L will show inventory purchases as expenses immediately.
Landed Costs in Odoo 19: Freight, Customs, Insurance, and Allocation Methods
If you import goods, the purchase price on the vendor bill is not your true cost. Freight charges, customs duties, insurance premiums, brokerage fees, and warehousing costs all increase the real unit cost of your inventory. Without landed cost allocation, your inventory is understated on the balance sheet and your gross margins are overstated on the P&L—until the freight invoice hits as a standalone expense, at which point your margins collapse in a different period than the sale.
Enabling Landed Costs
# Enable the feature:
# Inventory > Configuration > Settings > Valuation
# Check "Landed Costs"
# Requirements:
# - Product category must use Automated (Perpetual) valuation
# - Costing method must be FIFO or AVCO
# (Standard Cost ignores landed costs because the
# unit cost is fixed manually)
# Create landed cost product types:
# - Product: "Ocean Freight"
# Type: Service
# Cost: variable (entered per shipment)
# Account: 5200 - Freight Costs
#
# - Product: "Customs Duty"
# Type: Service
# Default Split Method: By Value
# Account: 5210 - Import DutiesAllocation Methods
When a landed cost is applied to a receipt, Odoo distributes the additional cost across the received products. The split method determines the allocation logic:
| Split Method | Allocation Logic | Use When |
|---|---|---|
| By Quantity | Cost distributed equally per unit received | All items are similar size/weight (e.g., same product in bulk) |
| By Current Cost | Proportional to each line's current inventory value | Higher-value items should absorb more of the freight cost |
| By Weight | Proportional to total weight of each line | Freight charges are weight-based (ocean/air freight) |
| By Volume | Proportional to total volume of each line | Container shipping where volume is the cost driver |
| Equal | Split equally across all lines regardless of qty/value | Fixed per-line charges (e.g., inspection fees per SKU) |
Creating and Validating a Landed Cost
# 1. Receive goods (validate the picking)
# Receipt WH/IN/00042: 500 units Product A @ $20 = $10,000
# 200 units Product B @ $50 = $10,000
# 2. Create Landed Cost record
# Inventory > Operations > Landed Costs > Create
# - Pick the receipt: WH/IN/00042
# - Add cost lines:
# Ocean Freight: $1,200 (Split: By Weight)
# Customs Duty: $ 800 (Split: By Current Cost)
# Insurance: $ 200 (Split: By Current Cost)
# 3. Click "Compute" to preview allocation
# Product A: Weight 60%, Value 50%
# Freight: $1,200 x 60% = $720
# Customs: $ 800 x 50% = $400
# Insurance:$ 200 x 50% = $100
# Added cost per unit: ($720+$400+$100) / 500 = $2.44
# New unit cost: $20.00 + $2.44 = $22.44
#
# Product B: Weight 40%, Value 50%
# Freight: $1,200 x 40% = $480
# Customs: $ 800 x 50% = $400
# Insurance:$ 200 x 50% = $100
# Added cost per unit: ($480+$400+$100) / 200 = $4.90
# New unit cost: $50.00 + $4.90 = $54.90
# 4. Validate — Odoo creates journal entries:
# Debit 1400 Stock Valuation $2,200
# Credit 5200 Freight Costs $1,200
# Credit 5210 Import Duties $ 800
# Credit 5220 Insurance Costs $ 200
# The valuation layers are updated in real time.
# FIFO: new layer adjustment records are created.
# AVCO: the average cost is recalculated.Apply landed costs before any of the received goods are delivered to customers. If you allocate landed costs after partial delivery, Odoo will correctly adjust the remaining stock layers, but the units already shipped will have been costed at the pre-landed cost. Your COGS for those deliveries will be understated, and there is no automatic retroactive correction. If this happens, you need a manual journal entry to adjust the COGS difference.
Inventory Revaluation: Adjusting Stock Value Without Physical Movement
Sometimes the value of your inventory changes without any physical movement. Raw material market prices shift, obsolete stock needs to be written down, or an accounting adjustment requires restating the inventory value at period end. Odoo 19 handles this through the inventory revaluation feature.
When to Use Revaluation
- Market price drops: A commodity you hold has lost market value and IFRS/GAAP requires writing inventory down to net realizable value (NRV).
- Obsolescence: Slow-moving stock needs a write-down based on aging analysis.
- Standard cost updates: Your finance team has revised standard costs for the new fiscal year and needs to adjust the valuation of existing stock.
- Audit adjustments: External auditors require a valuation correction based on their testing.
# Navigate to:
# Inventory > Products > [Select Product] > Product Form
# Click "Inventory Valuation" smart button
# Click "Revaluation" button
# Options:
# - Add: Increase stock value (e.g., discovered undervaluation)
# - Deduct: Decrease stock value (e.g., obsolescence write-down)
# Example: Write down 30% of obsolete stock
# Current value: $50,000 (1,000 units @ $50.00 avg)
# Write-down amount: $15,000
# Journal entry created:
# Debit 5300 Inventory Write-Down Expense $15,000
# Credit 1400 Stock Valuation $15,000
# New valuation: $35,000 (1,000 units @ $35.00 avg)
# For FIFO: the adjustment is applied to the oldest layers
# For AVCO: the average cost is recalculated
# For Standard: you update the standard price field directly The revaluation debit account defaults to the expense account on the product category. For proper financial reporting, create a dedicated Inventory Revaluation or Inventory Write-Down account under COGS/Expenses. This separates normal cost of goods sold from valuation adjustments, making it easy for auditors to identify revaluation impact on the P&L.
Inventory Valuation Reports: Reconciling Stock Value with the General Ledger
Configuring valuation correctly is only half the job. You need to verify that the stock valuation matches your general ledger at every month-end close. Odoo 19 provides three key reports for this reconciliation:
| Report | Location | What It Shows |
|---|---|---|
| Inventory Valuation | Inventory > Reporting > Inventory Valuation | Current stock value per product, grouped by category. Drill down to individual valuation layers. |
| Stock Valuation Layers | Inventory > Reporting > Valuation Layers (developer mode) | Every valuation layer record: creation date, remaining qty, unit cost, total value. Essential for FIFO auditing. |
| General Ledger (Stock Account) | Accounting > Reporting > General Ledger, filtered to account 1400 | All journal entries posted to the stock valuation account. Should match the Inventory Valuation report total. |
Month-End Reconciliation Checklist
# 1. Run Inventory Valuation report at period-end date
# Note the total: e.g., $487,250.00
# 2. Run General Ledger for account 1400 (Stock Valuation)
# Note the closing balance: should match $487,250.00
# 3. If they don't match, check for:
# a) Unvalidated receipts (stock moved but no JE)
# b) Draft vendor bills (GR/IR not cleared)
# c) Manual journal entries posted directly to 1400
# d) Multi-company transactions with wrong valuation
# e) Scrap orders that bypassed valuation
# 4. Common query to find discrepancies:
# Inventory > Reporting > Valuation Layers
# Filter: "Remaining Value != 0" and group by product
# Compare totals against GL sub-ledger
# 5. Check the Stock Input account (GR/IR) balance
# A non-zero balance means receipts without vendor bills
# This is expected for in-transit goods, but stale
# balances indicate missing bills or matching errors Create a scheduled action (ir.cron) that compares the sum of stock.valuation.layer remaining values against the GL balance of your stock valuation account. If the difference exceeds a threshold (e.g., $100), have it send an email alert to the accounting team. Catching discrepancies within 24 hours is infinitely easier than finding them during year-end audit.
3 Inventory Valuation Mistakes That Silently Corrupt Your Financial Statements
Changing the Costing Method on a Category That Already Has Stock
Your warehouse has 10,000 units valued at AVCO. Someone changes the product category to FIFO because "it's more accurate." Odoo doesn't retroactively rebuild valuation layers for existing stock—it creates a single FIFO layer with the current average cost. From this point forward, FIFO layer consumption starts from this artificial base. The result: your FIFO costs are wrong for months until the pre-switch inventory is fully consumed. Meanwhile, the valuation layers report shows a massive single layer that doesn't correspond to any actual purchase.
Never change costing methods mid-stream with existing inventory. If you must switch, do it at a period boundary when the product's on-hand quantity is zero (or as close to zero as possible). Document the switch date and the artificial base layer for your auditors. Better yet, create a new product category with the desired method and migrate products when their stock is depleted.
Leaving Stock Valuation Accounts on the Default "Expenses" Account
You enable perpetual valuation but skip the account configuration on the product category. Odoo falls back to the product's expense account (typically a P&L account like "Cost of Revenue") for stock valuation postings. Every goods receipt now debits a P&L expense account instead of a balance sheet inventory asset. Your inventory never appears on the balance sheet. Your monthly expenses spike on receipt months and disappear on delivery months. The P&L and balance sheet are both wrong, but no error message tells you.
Before validating your first receipt, verify that all four stock accounts are configured on every product category that uses automated valuation. The Stock Valuation Account must be a balance sheet account (asset). Write a server action or data validation script that checks all product categories and flags any where the stock valuation account is a P&L account. Run this check as part of your go-live validation checklist.
Applying Landed Costs After Partial Delivery Without Adjusting COGS
You receive 500 units, deliver 300 to customers, then apply landed costs of $2,500 to the original receipt. Odoo correctly updates the remaining 200 units with the additional cost (adding $12.50 per unit). But the 300 units already shipped? Their COGS was recorded at the pre-landed-cost value. Your delivered goods are undercosted by $7.50 each—a total of $2,250 in understated COGS that no automatic process will correct. Your gross margin for those sales is overstated.
Establish a business process: landed costs must be applied before the first delivery from that receipt. If the freight invoice hasn't arrived yet, estimate it and apply a provisional landed cost. Adjust when the actual invoice arrives. If you miss the window, create a manual journal entry to debit COGS and credit the landed cost account for the shortfall on already-delivered units. Document the calculation for audit trail.
What Correct Inventory Valuation Saves Your Business
Getting inventory valuation right is not an accounting exercise. It directly impacts pricing decisions, tax obligations, and executive confidence in financial reporting:
Landed cost allocation reveals the actual unit cost of imported goods. Companies that skip this routinely underprice products by the freight and duty margin, eroding profit they think they're earning.
When stock valuation reconciles with the GL monthly, year-end inventory audit is a verification exercise—not a forensic investigation. Teams close 2–3 weeks faster.
Proper FIFO layers, documented revaluations, and automated GL reconciliation give auditors a clear trail. Fewer audit queries means lower audit fees and faster sign-off.
For a mid-size importer with $5M in annual purchases, a 10% landed cost margin that is properly allocated instead of expensed separately means $500,000 in inventory is correctly stated on the balance sheet. This affects working capital ratios, bank covenant compliance, insurance coverage adequacy, and the accuracy of every margin report the executive team uses for pricing decisions.
Optimization Metadata
Complete guide to Odoo 19 inventory valuation. Configure FIFO, AVCO, and Standard costing methods, set up stock valuation accounts, allocate landed costs, and reconcile inventory with the general ledger.
1. "FIFO, AVCO, and Standard Cost: How Odoo 19 Calculates Unit Cost"
2. "Perpetual vs. Periodic Valuation: Configuring Stock Accounts in Odoo 19"
3. "Landed Costs in Odoo 19: Freight, Customs, Insurance, and Allocation Methods"
4. "3 Inventory Valuation Mistakes That Silently Corrupt Your Financial Statements"