Import & Landed Cost Engine
How we capture the full Ethiopian import transaction — proforma, bank permit, customs declaration and local receipts — and build it up into a correct per-item landed cost, with the right capitalize-vs-recover tax treatment and GL postings.

An Ethiopian import is not one transaction — it is a chain of documents that each add cost to the same shipment over several weeks. This post walks through how we model that chain in EthioHisab and turn it into a correct, per-item landed cost.
The commercial value is committed on a proforma invoice, approved for foreign exchange on a bank permit, taxed and released on a customs declaration, and finally topped up with local receipts for warehousing, transport and clearance. Only once all four stages have landed can the true cost of each item be known — and only then can inventory be valued and the ledger posted correctly.
The four capture stages
Each stage is additive to a single consignment. The fields map one-to-one onto the process as it runs at ERCA and the commercial banks:
- Proforma Invoice — proforma no. & date, supplier, currency, and per line the HS code, country of origin, description, unit of measure, unit cost, quantity and FOB total, plus any bank/processing charges.
- Bank Permit — carries all proforma data plus the permit no., bank, approved forex amount and rate, bank charges and insurance. Establishes the permitted quantities that later shipments draw down against.
- Customs Declaration — the commercial invoice (which may differ from the proforma on a partial shipment), the freight build-up (external freight, Djibouti, inland freight, insurance), and the customs taxes.
- Local Receipts — warehouse fee, transportation, loading/unloading, transitor (clearance) fee and service charges. Once captured, the cost sheet is computed.
Partial shipments: one bank-permitted proforma can arrive in several shipments, each with its own commercial invoice and declaration, so we track a running drawdown of permitted quantities and let a permit span many consignments.
The organising principle: capitalize vs. recover
Every charge is classified once, and that classification decides where it lands. This single split is the most important correctness property of the whole engine.
- Capitalized → Inventory: FOB goods value, external freight, insurance, Djibouti port cost, inland freight, duty, excise, surtax, social welfare levy, scanning fee, warehouse, transport, loading/unloading, transitor fee, and non-recoverable bank & service charges.
- Recoverable → Receivable: import VAT and VAT on the scanning fee (input VAT, credited against output VAT) and withholding tax (a 3% advance payment of business income tax). These are assets, never cost.
The Ethiopian customs tax cascade
Each tax is computed on a defined base; several build on the ones before them. Rates live in our Tax Engine, keyed by HS code and effective date — never hard-coded. The order matters: surtax excludes VAT from its base, and VAT is computed last.
| # | Tax | Base | Rate | Treatment |
|---|---|---|---|---|
| 1 | Duty | CIF | 0–35% | Capitalized |
| 2 | Excise | CIF + Duty | 0–500% | Capitalized |
| 3 | Surtax | CIF + Duty + Excise | 10% | Capitalized |
| 4 | Social Welfare Levy | CIF | 3% | Capitalized |
| 5 | VAT | CIF + Duty + Excise + Surtax + Social Welfare | 15% | Recoverable |
| 6 | Withholding tax | CIF | 3% | Recoverable |
| 7 | Scanning fee | 0.07% of CIF | 0.07% | Capitalized |
| 8 | VAT on scanning | Scanning fee | 15% | Recoverable |
Which of surtax and the levy applies is HS-code-driven by the Tax Engine. Whichever are present are added to the VAT base, since VAT is computed last. The data model keeps both columns so either — or both — can be recorded.
Building up the cost
The buildup runs in three moves. First we reach the customs value: CIF = FOB + external freight + insurance — the Duty Paying Value the cascade runs on. Costs incurred after the goods reach the port of entry (Djibouti handling, inland freight, local costs) are added to landed cost but are not part of CIF.
Second, we run the tax cascade above, per line, using the HS-code rates. Duty, excise and the surtax/levy are line-specific and assigned directly.
Third, we allocate the shared costs — freight, insurance and the local costs — across the line items by an allocation driver configured per cost category: by value (default), by weight/volume (typical for freight), or by quantity.
landed_cost(line) = FOB
+ share_of(freight + insurance)
+ duty + excise + surtax + social_welfare
+ allocated_local_costs
landed_unit_cost = landed_cost(line) / qty_received
# Recoverable taxes (VAT, VAT-on-scanning, withholding)
# are excluded from cost and booked as receivables.
A worked example
A two-item consignment. Item A (100 units @ USD 50 FOB) is subject to surtax; Item B (200 units @ USD 20 FOB) to the social welfare levy instead. Duty 10%, VAT 15%, withholding 3%, rate 145 ETB/USD — all illustrative. Amounts in ETB.
| Item | Qty | FOB | CIF | Duty | Surtax/Levy | Landed unit |
|---|---|---|---|---|---|---|
| A (surtax) | 100 | 725,000 | 783,333 | 78,333 | 86,167 | 10,489.37 |
| B (levy) | 200 | 580,000 | 626,667 | 62,667 | 18,800 | 3,945.08 |
| Total | 300 | 1,305,000 | 1,410,000 | 141,000 | 104,967 | — |
Total landed cost (into inventory): 1,837,954 ETB. Recoverable receivables: 290,843 ETB (VAT 248,395 + scanning VAT 148 + withholding 42,300). If those recoverable taxes were folded into unit cost, inventory would be overstated by ~16% and we would double-count taxes we reclaim from ERCA — which is exactly why the split matters.
The ledger: an import clearing account
Capitalizable costs don't post straight to inventory. They accumulate in an Import Clearing (goods-in-transit / suspense) account as each document is recorded. At goods receipt the whole balance is relieved into inventory in one entry, so clearing returns to zero and inventory is stated at exactly the landed cost. Recoverable taxes and the supplier liability post to their own accounts and never pass through clearing.
① In transit Dr Import Clearing 1,495,000
Cr Accounts Payable 1,495,000
② Cleared Dr Import Clearing 246,954
Dr Input VAT Receivable 248,543
Dr Withholding Receivable 42,300
Cr Bank / ERCA Payable 537,797
③ Local costs Dr Import Clearing 96,000
Cr Accounts Payable / Bank 96,000
④ Goods receipt Dr Inventory 1,837,954
Cr Import Clearing 1,837,954 ← nets to 0
Debits to clearing across ①–③ (1,495,000 + 246,954 + 96,000) equal the credit at ④ exactly. The account netting to zero is a hard invariant the engine asserts before it will post to the GL.
How it fits the platform
The engine is a focused microservice in the EthioHisab domain, built in Django clean architecture (domain → application → infrastructure → presentation). It doesn't re-implement items, vendors, tax rates, the ledger or the fiscal calendar — it consumes them from the services that own them and publishes results back over RabbitMQ and Kafka, with heavy work (cost-sheet computation, GL posting) running as Celery tasks. Money is fixed-precision throughout, every amount is stored in both transaction currency and ETB, and posting periods resolve through the Ethiopian 13-month fiscal calendar. Every cost sheet stores its inputs and rates, so any landed unit cost can be reproduced and explained line by line.
Rates shown here are illustrative reference values, configured in the Tax Engine and verified against current ERCA / Ministry of Finance regulation before go-live.