Skip to content

Landed Cost

Backlog fieldValue
Story IDINV-DASH-017
EpicInventory
Story typeDashboard
Review statusDraft - business analyst and business owner review required
Source evidenceInventory MFE route/form, Inventory.Apis controller, application command/query handlers, validators, domain entities, and posting services
Last source review2026-07-10

Purpose

Allocate freight, customs, insurance, and other acquisition costs to purchase-invoice inventory so item cost reflects the full landed value.

User stories

  • As a cost accountant, I want to open a purchase invoice and add additional cost lines so that full acquisition cost is captured.
  • As a cost accountant, I want to allocate each cost using an allowed allocation basis so that allocation follows company policy.
  • As a reviewer, I want to compare invoice value, allocated amount, and adjusted item cost before finalization.
  • As an auditor, I want to view, search, print, export, and trace landed-cost calculations.

Acceptance criteria

  1. A Landed Cost references an eligible purchase invoice and its inventory lines.
  2. Additional cost lines have valid expense/account, currency/amount, and allocation method data.
  3. Allocation produces deterministic per-line allocated amounts with an explicit rounding rule.
  4. Total allocated local amount equals the additional cost total within the configured rounding tolerance.
  5. Adjusted item cost equals original inventory cost plus allocated landed cost.
  6. Editing recalculates dependent allocations and totals before save.
  7. Final posting updates inventory valuation/accounting exactly once and preserves the calculation for audit.
  8. Delete/edit are blocked after finalization except through an approved reversal process.
  9. Add, edit, view, list, delete, printout, and export actions follow permissions.

Posting details

  1. Landed Cost is posted as part of the eligible Purchase Invoice flow. It validates invoice/vendor/currency/tax data, service-cost lines, allocation bases, allocation totals and rounding, item variants, cost centers, duplicate posting, and the receiving stock context before returning posting groups to the caller.
  2. Expense account resolution is service item first, then item category; failure at both levels blocks Post. Cost-center resolution follows the corresponding item then category mapping. Vendor payable resolves vendor first, then vendor category. Tax accounts resolve from the applicable tax group/configuration, and inventory capitalization uses the purchase/Inventory Goods-in-Transit and warehouse mappings.
  3. The Landed Cost handler delegates the assembled expense, vendor, VAT/tax, currency, and cost-center groups to Purchase Invoice, and returns deterministic per-item/per-variant allocated amounts to the delegated Stock In valuation flow. It does not independently commit a disconnected journal or stock update.
  4. Successful posting increases the received inventory valuation and item cost basis by the allocated amounts, preserves the original and allocated values for audit, links the purchase, landed-cost, stock, and journal effects, and prevents the same cost from being capitalized twice.
  5. Allocation, Purchase Invoice accounting, Stock In valuation, tax/vendor effects, links, and statuses commit atomically under the parent transaction. Any missing account, invalid allocation, delegate failure, or persistence error leaves every affected document unchanged. Reversal must remove the capitalization and reverse the related purchase/accounting effects together.

General ledger posting

ComponentDebitCreditAmount basisAccount resolution
Capitalized landed costInventory/Warehouse through Stock In valuationGoods in Transit / purchase clearingAllocated item amountPurchase and Inventory source mappings.
Service/expense chargeService ExpenseVendor PayableCharge local amount before/with taxExpense: service item then category. Payable: vendor then vendor category.
Recoverable taxInput TaxVendor Payable / tax clearingCalculated local taxTax then Tax Group mapping.

Module and data effects

Affected areaEffectConditional/no-effect rule
Purchase InvoiceReceives expense, vendor, tax, currency, and cost-center posting groups.Landed Cost does not commit a disconnected purchase journal.
Inventory/Stock InAdds allocations to received layers and item cost basis.Service/non-capitalizable allocations do not change item quantity.
QuantityNo additional units are received solely because landed cost is added.Only valuation changes for already received eligible units.
Vendor/TaxUpdates payable and recoverable tax through Purchase posting.No vendor/tax effect for internal allocations with no vendor/tax line.
AuditStores original cost, basis, allocation, links, and final cost.Duplicate capitalization is blocked.

Core acceptance scenarios

  1. Given valid service expense lines and allocations totaling each local amount within tolerance, when Landed Cost posts through Purchase Invoice, then expense/vendor/VAT groups and item-variant allocations are returned to Purchase/Stock In and capitalized atomically.
  2. Given item expense is missing, when resolution runs, then category expense is used; if both are missing, Post fails. Vendor payable resolves vendor then vendor category.
  3. Given any allocation, currency, tax, vendor, account, or duplicate-post rule fails, when Post is attempted, then no purchase, stock valuation, or journal effect is committed.

Data classification

DataRule
Purchase invoiceRequired and must be eligible; posting locates Landed Cost by purchase invoice ID.
Cost linesAt least one required for add/edit.
Service itemRequired, must exist, and must be Item Type Service.
VendorRequired per cost line.
Allocation methodRequired enum.
CurrencyID and English/Arabic names required.
AmountRequired and greater than zero.
Exchange rateRequired and greater than zero; must equal 1 when line currency is company currency.
Local amountDerived/validated as Amount multiplied by Exchange Rate within 0.01 tolerance.
TaxOptional; when selected its definition/account must exist and VAT is included in vendor payable.
DescriptionOptional.
AllocationsOptional while editing, but if present every row is validated and active allocations must total Local Amount within 0.01.
Allocation rowRequires invoice detail, UOM identity/names, item variant identity/names, and non-negative quantities/prices/amount. Inactive rows must allocate zero.

Duplicate invoice details in one cost line are rejected.

Account and cost-center resolution

  • Expense account: service item expense account, then service item category expense account, otherwise fail.
  • Cost center: service item cost center, then category cost center, otherwise no cost-center allocation.
  • Vendor payable: vendor payable account, then vendor category payable account, otherwise fail.
  • VAT: selected tax definition's account; missing tax blocks posting.

Clicking Post

  1. Finds the Landed Cost for the purchase invoice and rejects an already-posted record.
  2. Confirms Accounting can add a posted journal.
  3. Resolves service items, expense accounts/cost centers, vendor payable accounts, and tax definitions.
  4. Groups expense totals by expense account/cost center, vendor totals by payable account, and VAT by tax account.
  5. Marks Landed Cost Posted and returns the grouped accounting amounts plus allocated cost by item variant to the purchase/stock-in workflow.
  6. The purchase Stock In applies allocated item cost to inventory valuation; its journal incorporates the returned expense entries so inventory receives the landed amount.

Posting failure

Posting stops for missing service items, expense/payable/tax accounts, invalid allocation totals, invalid exchange-rate math, unavailable Accounting posting, or repeat posting. No partial purchase/stock/journal effect should commit.

Status, action, and reversal controls

ActionRequired state/authorityPreconditions and affected dataFailure/reversal rule
Add/Edit allocationLanded Cost Add/Edit; parent eligiblePurchase invoice/receipt lines, charge currency, allocation method, expense/vendor/tax and cost centersRecalculation replaces Draft allocations only; received quantity does not change
ReviewView authorityOriginal cost, charge totals, basis, allocated/adjusted values visibleReview creates no journal or valuation change
Finalize/Post through PurchaseParent Purchase Post authorityAllocations equal charges within tolerance; all accounts/currency/tax/stock context validPurchase journal groups and Stock In capitalization commit under parent or all roll back
DeleteDraft plus DeleteNot finalized/capitalizedPosted allocation requires parent reversal, not deletion
ReversePurchase return/reversal authorityRemaining inventory/layers and vendor/tax dependencies permit reversalRemove only attributable capitalization and reverse payable/tax/accounting consistently

Reconciliation controls

ControlRequired equality
Sum of item allocationsEach landed-cost charge local amount within rounding tolerance
Adjusted receipt valueOriginal eligible inventory value plus capitalized allocation
QuantityUnchanged by landed cost alone
Vendor/expense/tax groupsPurchase journal detail and parent payable totals
Capitalized amountIncrease in linked Stock In valuation/layers without duplicate application

Frontend page coverage

The following UI coverage is sourced from the pulled MFE route configuration. A Module-level supporting artifact mapping is evidence of a reachable screen, but requires BA confirmation that it belongs to this story rather than a more specific feature story.

Route/pageComponentModeRoute permission/guardMapping confidenceSource
(module default)LandedCostListComponentList/navigationPermission: AppInventoryPermissions.LANDED_COST_List; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:343
add/:invoiceIdAddLandedCostComponentAddPermission: AppInventoryPermissions.Purchase_LandedCost_Add; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:373
edit/:landedCostId/:invoiceIdAddLandedCostComponentAddPermission: AppInventoryPermissions.Purchase_LandedCost_Edit; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:385
fixed-cost/:idItemFixedCostComponentView/detailNo route-level permission/guard found in source windowFeature token matchapps/inventory/src/app/remote-entry/items/items.module.ts:147
landed-costLandedCostMainComponentList/navigationPermission: AppInventoryPermissions.LANDED_COST_List; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:335
view/:idLandedCostViewComponentView/detailPermission: AppInventoryPermissions.LANDED_COST_VIEW; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:353

Observed frontend fields and validation

ComponentControlObserved frontend ruleComponent source
AddLandedCostComponentcreationDateVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/pages/landed-cost/add-landed-cost/add-landed-cost.component.ts
AddLandedCostComponentvendorIdVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/pages/landed-cost/add-landed-cost/add-landed-cost.component.ts
AddLandedCostComponentwarehouseNameVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/pages/landed-cost/add-landed-cost/add-landed-cost.component.ts
ItemFixedCostComponentfixedCostRequired in component validatorapps/inventory/src/app/remote-entry/items/pages/item-definition/item-fixed-cost/item-fixed-cost.component.ts
ItemFixedCostComponentitemVariantIdRequired in component validatorapps/inventory/src/app/remote-entry/items/pages/item-definition/item-fixed-cost/item-fixed-cost.component.ts
ItemFixedCostComponentuomIdRequired in component validatorapps/inventory/src/app/remote-entry/items/pages/item-definition/item-fixed-cost/item-fixed-cost.component.ts

Observed frontend actions

ComponentClick action/handlerBusiness review requirementComponent source
AdditionalCostsComponentopenServiceItemDialogOpen the item/lookup/tracking dialog with the current warehouse, item, UOM, and remaining quantity context; accepted values must return to the correct line and cancel must not mutate it.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/add-landed-cost/components/additional-costs/additional-costs.component.ts
AdjustedValuesComponentvaluesChangedDocument the enabled state, permission, input context, resulting API or navigation effect, success refresh, and concrete failure behavior for this handler.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/add-landed-cost/components/adjusted-values/adjusted-values.component.ts
ItemFixedCostComponentaddLineFixedAdd one initialized detail row without changing existing rows; apply defaults and line validators, preserve stable row identity, and exclude incomplete rows from Save/Post.apps/inventory/src/app/remote-entry/items/pages/item-definition/item-fixed-cost/item-fixed-cost.component.ts
ItemFixedCostComponentonCancelLeave or close without persisting; warn when the current form has unsaved changes and restore the prior list/filter context.apps/inventory/src/app/remote-entry/items/pages/item-definition/item-fixed-cost/item-fixed-cost.component.ts
ItemFixedCostComponentonDeleteFixedCostAvailable only in deletable states with Delete permission; identify the record in confirmation, call delete once, remove/refresh the list row on success, and retain the row with the API reason on failure.apps/inventory/src/app/remote-entry/items/pages/item-definition/item-fixed-cost/item-fixed-cost.component.ts
ItemFixedCostComponentonSaveFixedCostValidate the concrete form and line collection, prevent duplicate submission, persist once, show API field/business errors in context, and navigate/refresh only after success.apps/inventory/src/app/remote-entry/items/pages/item-definition/item-fixed-cost/item-fixed-cost.component.ts
LandedCostListComponentonDeleteAvailable only in deletable states with Delete permission; identify the record in confirmation, call delete once, remove/refresh the list row on success, and retain the row with the API reason on failure.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/landed-cost-list/landed-cost-list.component.ts
LandedCostListComponentonEditOpen the identified editable record only with Edit permission; posted/locked records remain blocked by route or action state and list context must be recoverable on return.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/landed-cost-list/landed-cost-list.component.ts
LandedCostListComponentonVeiwOpen the authorized record in view mode with its status, totals, lines, source and generated-document links; handle deleted, forbidden, and missing records without exposing stale data.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/landed-cost-list/landed-cost-list.component.ts
LandedCostListComponentprintoutGenerate the printout for the selected record and authorized branch/tenant; use posted/current values and surface print, template, ZATCA/ETA, or popup failures without changing transaction state.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/landed-cost-list/landed-cost-list.component.ts
LandedCostListComponentrouteToJournalentryViewOpen the authorized record in view mode with its status, totals, lines, source and generated-document links; handle deleted, forbidden, and missing records without exposing stale data.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/landed-cost-list/landed-cost-list.component.ts
LandedCostListComponentrouteToPurchaseInvoiceViewOpen the authorized record in view mode with its status, totals, lines, source and generated-document links; handle deleted, forbidden, and missing records without exposing stale data.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/landed-cost-list/landed-cost-list.component.ts
LandedCostListComponentrouteToStockInViewOpen the authorized record in view mode with its status, totals, lines, source and generated-document links; handle deleted, forbidden, and missing records without exposing stale data.apps/inventory/src/app/remote-entry/transactions/pages/landed-cost/landed-cost-list/landed-cost-list.component.ts

Page-specific frontend acceptance criteria

  1. (module default) - List/navigation: Load an authorized paged/searchable list or navigation page; preserve filters/sort/paging and expose only permitted row and bulk actions. Security observed: Permission: AppInventoryPermissions.LANDED_COST_List; Guards: PolicyAuthGuard
  2. add/:invoiceId - Add: Render permitted fields, required indicators, defaults, dependent lookups, validation, Save/Cancel, loading, success, and API-error states. Security observed: Permission: AppInventoryPermissions.Purchase_LandedCost_Add; Guards: PolicyAuthGuard
  3. edit/:landedCostId/:invoiceId - Add: Render permitted fields, required indicators, defaults, dependent lookups, validation, Save/Cancel, loading, success, and API-error states. Security observed: Permission: AppInventoryPermissions.Purchase_LandedCost_Edit; Guards: PolicyAuthGuard
  4. fixed-cost/:id - View/detail: Load an authorized immutable/detail representation with status-aware actions, linked records, print/export where exposed, and not-found/forbidden handling. Security observed: No route-level permission/guard found in source window
  5. landed-cost - List/navigation: Load an authorized paged/searchable list or navigation page; preserve filters/sort/paging and expose only permitted row and bulk actions. Security observed: Permission: AppInventoryPermissions.LANDED_COST_List; Guards: PolicyAuthGuard
  6. view/:id - View/detail: Load an authorized immutable/detail representation with status-aware actions, linked records, print/export where exposed, and not-found/forbidden handling. Security observed: Permission: AppInventoryPermissions.LANDED_COST_VIEW; Guards: PolicyAuthGuard

Internal Documentation — Microtec