A Landed Cost Voucher in ERPNext adds extra purchase costs — freight, customs duty, insurance, clearing charges — onto the valuation of the items you received, so your inventory value and cost of goods sold reflect the true landed cost, not just the supplier's invoice price. You create it against a Purchase Receipt and choose how the charges are distributed across items.
If you import goods or pay significant freight, the price on the supplier invoice is not what the stock actually costs you. Duty, shipping, insurance, and clearing agent fees can add 10–30% to the real cost. Booking those as plain expenses hides them from your item cost and quietly understates your cost of goods sold. The Landed Cost Voucher fixes exactly that.
The whole point of a Landed Cost Voucher is truthful inventory valuation: it pushes the "extra" costs of getting goods to your warehouse into the item's cost, so margins and COGS are real. I am Manoj, an ERPNext and Frappe implementation consultant at MithTech in Bengaluru, and I set these up for importers and distributors.
Try it: your real per-unit cost, live
Nudge the numbers to your actual last consignment. If additional costs come out to more than a couple of percent of the supplier invoice, booking them as plain expenses instead of an LCV is quietly overstating your gross margin.
Live calculator: supplier invoice + freight + duty + insurance + CHA + inland = true landed cost and per-unit landed cost.
What is a Landed Cost Voucher in ERPNext?
A Landed Cost Voucher in ERPNext is a document that distributes additional purchase costs — such as freight, customs duty, insurance, and handling — across the items on a Purchase Receipt, increasing each item's valuation rate. It exists because the true cost of stock is the supplier price plus everything spent getting the goods into your warehouse, and those extra costs usually arrive on separate bills from freight forwarders and clearing agents.
Once posted, the voucher raises the valuation of the received items by their share of the landed costs. Your inventory is then valued at true landed cost, and when the items sell, the cost of goods sold includes freight and duty. This is what turns a rough margin into an accurate one for any business that imports or pays material inbound logistics.
Why every accounting standard says these costs belong in inventory
This is not an ERPNext design choice — it's what every major inventory standard requires. The definition of "cost of purchase" converges across regimes: all costs incurred in bringing inventory to its present location and condition are capitalised into inventory cost, not expensed as incurred. The LCV is simply the ERPNext mechanism that honours this.
- Ind AS 2 / IAS 2 Inventories — cost of purchase comprises the purchase price, import duties and other non-recoverable taxes, and transport, handling and other costs directly attributable to acquisition (¶10–11).
- US GAAP — ASC 330 Inventory — inventory cost includes all applicable expenditures directly or indirectly incurred in bringing an article to its existing condition and location (¶330-10-30-1).
- UK GAAP — FRS 102 §13 — costs of purchase include import duties, transport, handling and other costs directly attributable to acquisition (§13.6).
- CAS 1 (China) Inventories — same treatment: purchase costs include purchase price, taxes not deductible, transport, loading/unloading and other costs directly attributable to acquisition.
If freight and duty are material, expensing them instead of running an LCV isn't just an ERPNext bad habit — it fails the standard.
Skimmable summary: A Landed Cost Voucher distributes freight, duty, insurance, and handling across received items, raising their valuation to true landed cost. Inventory and cost of goods sold then reflect the real cost of getting goods to your warehouse — which is what Ind AS 2 / IAS 2 / ASC 330 / FRS 102 / CAS 1 all require.
How do I create a Landed Cost Voucher in ERPNext?
You create a Landed Cost Voucher in ERPNext by opening a new Landed Cost Voucher, selecting the relevant Purchase Receipt, adding the extra charges, choosing a distribution method, and submitting. The voucher then updates each item's valuation with its share of those charges. It should be posted while the items are still in stock so the cost attaches correctly.
Click through the demo below — each step shows what you do in the UI and what the ERPNext v16 controller does with your input, cited to file and line in the local checkout.
Four-step click-through: New LCV → Fetch PR → Fill Charges → Distribute and Submit, with the source-file citation for each stage.
Stock module → Landed Cost Voucher → New. Set Posting Date and Company. The naming series MAT-LCV-.YYYY.- is pre-wired.
Here is the same sequence as a Steps block for readers who prefer the linear form:
Open a new Landed Cost Voucher
Stock module → Landed Cost Voucher → New. Set Company and Posting Date. Naming series MAT-LCV-.YYYY.- is pre-wired in landed_cost_voucher.json.
Fetch the Purchase Receipt
In the Purchase Receipts child table add rows pointing at the GRN(s) you're landing costs against. Click Get Items From Purchase Receipts — the server method at landed_cost_voucher.py:56 pulls items via get_pr_items (landed_cost_voucher.py:421) and copies base_rate / base_amount into the LCV Item table.
Fill the Taxes & Charges child table
One row per charge — freight, customs BCD, insurance, CHA, inland transport — each with a Description, Expense Account (Indirect Expenses head), and Amount. validate_expense_accounts (landed_cost_voucher.py:178) refuses accounts belonging to a different company when perpetual inventory is enabled.
Choose distribution method and submit
Pick Distribute Charges Based On — Amount, Qty or Distribute Manually (the three enum values on the DocType). On submit, update_landed_cost at landed_cost_voucher.py:307 re-writes item.landed_cost_voucher_amount on the linked PR, then calls update_stock_ledger and make_gl_entries with via_landed_cost_voucher=True, and finally repost_future_sle_and_gle so units already consumed pick up the corrected valuation.
Pre-flight: check every charge row has an expense account
validate_expense_accounts throws an Incorrect Account error if any row in the Taxes & Charges table points at an account belonging to another company (or is blank). The error is opaque to end users — it looks like a data-entry mismatch. The Client Script in the Automation section below catches this on before_save so accounts hears about it before hitting submit.
Skimmable summary: Create a Landed Cost Voucher, fetch the Purchase Receipt, add each charge (freight, duty, insurance) in the charges table with its expense account, pick a distribution method, and submit. ERPNext updates item valuations, reposts SLEs and re-books GL entries — even for units already sold.
How are landed costs distributed across items?
Landed costs are distributed across items in ERPNext by Amount, Qty, or Distribute Manually, and the method you pick changes how fairly the cost lands on each item. Distribution by Amount spreads charges in proportion to each item's value; distribution by Qty spreads them per unit; Distribute Manually lets you assign exact shares when neither default is fair.
The core set_applicable_charges_on_item method at landed_cost_voucher.py:202 is where the split happens — it reads the enum value, scrubs it to a field name, and multiplies each item's share by the total charge. Weight-based distribution isn't in core; you need a small custom script that overrides this method to read total_weight instead.
Which method fits which charge type? Pick a charge below and the picker will tell you which basis to use on the LCV.
Pick a charge type (freight, insurance, customs, CHA, inland, bank) — get the recommended ERPNext Distribute Charges Based On value and one-line rationale.
Freight is priced by weight or volume, not by invoice value.
Core ERPNext supports Amount / Qty / Distribute Manually out of the box (see landed_cost_voucher.py:44). Weight-based distribution needs a custom script that overrides set_applicable_charges_on_item.
Skimmable summary: Landed costs distribute by Amount (value share), Qty (per unit), or Distribute Manually. Freight and inland transport go by Qty (or weight if you customise), insurance / customs / CHA / bank charges by Amount. Weight distribution requires a custom script.
When should I use a Landed Cost Voucher?
You should use a Landed Cost Voucher whenever the extra costs of receiving goods are significant enough to matter to your item cost — chiefly imports with customs duty and freight, or any purchase with substantial inbound logistics. If freight and duty are trivial, booking them as expenses is fine; once they materially change item cost, they belong in valuation.
Importers are the clearest case: duty, ocean or air freight, insurance, and clearing agent fees together can be a large share of cost, and leaving them out makes every margin report wrong. Distributors with heavy inbound freight benefit too — the discipline is covered in my ERP guide for distributors and wholesalers, and the wider inventory picture in the ERPNext inventory management guide. For manufacturers, landed raw-material cost then flows into finished-goods costing and any standard costing variance analysis you run.
If your imports are in a foreign currency
The moment there's a foreign-currency element, a stale exchange rate on the Goods Receipt silently corrupts your inventory valuation before an LCV even gets involved. That's covered in a separate deep-dive with a live FX calculator, worked ledger example and copy-paste automation gate — ERPNext Landed Cost & FX Gone Wrong: the interactive fix guide.
Skimmable summary: Use a Landed Cost Voucher when inbound costs materially affect item cost — imports with duty and freight, or heavy inbound logistics. Trivial charges can stay as expenses; significant ones belong in valuation so margins and COGS stay accurate.
Automation: seed the LCV, gate the mistakes
The single biggest waste in a busy imports team is retyping the same freight / CHA / duty numbers from a clearing-agent PDF into a manual LCV every week. Three small automations remove that friction entirely — none of them need GitHub Actions or a paid CI service; they run on your existing bench scheduler or a self-hosted n8n.
Three copy-paste automations: a Server Script that seeds a draft LCV on GRN submit, a Client Script that warns on missing charge accounts, and an n8n workflow that surfaces clearing-agent PDFs into Slack.
On Purchase Receipt submit, seed a draft Landed Cost Voucher with the freight/CHA charges already known from the item metadata or a linked freight PI. Accounts opens the draft, verifies figures and submits.
# Server Script → DocType Event → Purchase Receipt → on_submit
import frappe
from frappe.utils import flt
def after_purchase_receipt_submit(doc, method=None):
if doc.currency == frappe.db.get_default("currency"):
# skip pure-domestic GRNs unless you want LCV for them too
return
# Skip if we've already seeded one for this PR
if frappe.db.exists("Landed Cost Voucher Purchase Receipt", {
"receipt_document": doc.name, "docstatus": ["!=", 2]
}):
return
lcv = frappe.new_doc("Landed Cost Voucher")
lcv.company = doc.company
lcv.posting_date = doc.posting_date
lcv.distribute_charges_based_on = "Amount"
lcv.append("purchase_receipts", {
"receipt_document_type": "Purchase Receipt",
"receipt_document": doc.name,
"supplier": doc.supplier,
"posting_date": doc.posting_date,
"grand_total": doc.base_grand_total,
})
# ---- Fill in your actual heads here -----------------------------------
# These accounts must exist under the company's Indirect Expenses.
# Left as placeholders — swap for your chart-of-accounts codes.
charges = [
{"description": "Ocean freight", "expense_account": "Freight & Forwarding - YC", "amount": 0.0},
{"description": "Customs BCD", "expense_account": "Customs Duty - YC", "amount": 0.0},
{"description": "CHA / clearing", "expense_account": "Clearing Charges - YC", "amount": 0.0},
{"description": "Insurance premium", "expense_account": "Insurance - Import - YC", "amount": 0.0},
]
for c in charges:
lcv.append("taxes", c)
lcv.get_items_from_purchase_receipts()
lcv.insert(ignore_permissions=True) # DRAFT — accounts reviews & submits
frappe.msgprint(f"Draft LCV {lcv.name} created against {doc.name}")Deploy them in this order and you get the compounding effect: the n8n workflow makes sure the charge amounts land in a place someone will see, the Server Script pre-fills the draft so accounts only has to check and submit, and the Client Script stops the two errors that most commonly waste a submit attempt.
Related issues you may also hit
Landed cost is the entry point into a whole family of ERPNext costing questions — none of these are hypothetical, all of them are posts you can read next.
- FX drift on imports — if any part of the chain is in USD/EUR/CNY, the GRN's exchange rate matters more than you'd think. See ERPNext Landed Cost & FX gone wrong.
- Finished-goods costing — landed raw-material cost has to flow into the BOM cost roll-up. See ERPNext finished-goods costing.
- Inventory valuation basics — moving average vs FIFO, warehouse trees, negative-stock policy. See ERPNext inventory management.
- Standard costing variance — once landed cost is truthful, you can actually compute meaningful purchase price variance. See ERPNext standard costing.
Import-heavy business? Get landed cost right.
I set up landed cost, valuation, and import workflows in ERPNext so your inventory and margins reflect true cost. See the ERPNext product page or, if you're in the trading hub, ERPNext implementation in Mumbai.
Frequently asked questions
+What is a Landed Cost Voucher used for in ERPNext?
It adds extra purchase costs — freight, customs duty, insurance, and clearing charges — to the valuation of received items so inventory is valued at true landed cost. It is created against a Purchase Receipt and distributes the charges across items, which makes COGS and margins accurate for importers and distributors.
+How do I add freight or customs duty to item cost in ERPNext?
Create a Landed Cost Voucher, fetch the Purchase Receipt for the shipment, and enter each charge in the Taxes & Charges table with its expense account. Choose Distribute Charges Based On (Amount / Qty / Manual) and submit. ERPNext raises each item's valuation rate by its share, so the cost is embedded in stock rather than booked as a plain expense.
+Can I add landed cost after the items are already sold?
Yes — update_landed_cost at landed_cost_voucher.py:307 reposts future SLEs, so units already consumed pick up the corrected valuation and the delta flows to COGS. The caveat is closed accounting periods: if your policy blocks reposting into them, book a manual Journal Entry in the current period instead.
+What distribution method should I use for landed costs?
Match the method to how the charge was incurred: Amount for value-based charges (customs BCD, insurance, CHA, bank), Qty for per-unit or per-weight charges (ocean freight, inland transport), and Distribute Manually only when neither default is fair. Use the picker widget above — it names the ERPNext enum value for each case.
+Which ERPNext version do the file references in this post apply to?
The code paths cited — landed_cost_voucher.py:56, landed_cost_voucher.py:307, stock_controller.py:1101, purchase_receipt.py:626 — are from the ERPNext v16 checkout (git tag v16.22.0-495-gde591661b9, effectively v16.28.x). The same functions exist in v14 and v15 with cosmetic differences; the LCV DocType itself has been stable since v11.
+Does the Landed Cost Voucher affect accounting as well as stock?
Yes. Submitting an LCV re-runs make_gl_entries(via_landed_cost_voucher=True) on the linked Purchase Receipt, which credits each charge's expense account (make_landed_cost_gl_entries at purchase_receipt.py:626) and debits the inventory account by the same amount. Stock valuation and general ledger stay consistent.
+What happens if we skip the LCV and just expense freight and duty?
Your inventory sits on the balance sheet at the supplier invoice value (understated), and your P&L shows the freight and duty as period expenses (COGS understated). Gross margin looks artificially high until the stock sells — and even then, it's the next period's COGS that gets hit, so margin comparability across periods is broken. It also fails the "cost of purchase" test under Ind AS 2, IAS 2, ASC 330 and FRS 102 §13.
About the author
Manoj is an ERPNext and Frappe implementation consultant at MithTech in Bengaluru. He configures landed cost, import workflows, and inventory valuation in ERPNext so importers and distributors get accurate item costs and margins.