Skip to content

Inventory Opening Balance

Backlog fieldValue
Story IDINV-DASH-018
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

Load the starting stock quantities, costs, warehouses, and tracking details when inventory begins operation or is migrated.

User stories

  • As an implementation user, I want to enter opening stock by item and warehouse so that the system starts from an agreed balance.
  • As an implementation user, I want to capture cost and tracking information so that valuation and traceability begin correctly.
  • As an authorized accountant, I want to review and post the opening balance so that it becomes the inventory baseline.
  • As an auditor, I want to view and print the source opening-balance document.

Acceptance criteria

  1. The document has a valid date/period and at least one item line.
  2. Each line has a valid warehouse, item variant, unit, quantity, and required cost/tracking data.
  3. Duplicate lines are rejected or consolidated deterministically.
  4. Tracking details reconcile with line quantities.
  5. Posting increases opening stock and creates configured opening accounting impact exactly once.
  6. Posting is blocked when the period is closed or required accounts/settings are missing.
  7. Posted opening balances are immutable through normal editing.
  8. Add, edit, view, list, delete, post, print, and sequence actions follow permissions.

Posting details

  1. Post validates a Draft opening document, allowed date/period, warehouse and item eligibility, positive base quantities, UOM conversions, costs, duplicate handling, and complete batch/serial/expiry allocations.
  2. The current Inventory opening-balance handler creates stock only: it creates the initial stock headers, quantity and tracking records, valuation layers, and weighted-moving-average/last-purchase cost baseline, then marks the document Posted.
  3. No Inventory journal is created by the current handler. The acceptance statement about a configured accounting impact is therefore a target/business-policy item, not implemented behavior. Accounting opening balances must be posted separately unless an approved integration is added; the documentation must not imply an automatic debit or credit.
  4. A posted warehouse opening becomes a prerequisite consumed by later Stock In and related inventory transactions. It establishes inventory availability and valuation but does not itself create customer, vendor, cash, tax, revenue, or expense balances.
  5. All stock, tracking, valuation, and status changes are atomic and idempotent. A failed Post leaves no partial opening quantities. Correction after posting requires an authorized reversal or adjustment process that preserves the original audit trail.

General ledger posting

ConditionDebitCreditImplemented result
Current Inventory opening-balance Post--No journal is created by the current handler.
Separate Accounting opening processPolicy-defined opening asset/equity accountsPolicy-defined balancing accountsMust be posted separately; it is not delegated automatically from Inventory.

Module and data effects

Affected areaEffectConditional/no-effect rule
Inventory quantityEstablishes opening on-hand/available quantity by warehouse/item.Failed Post creates no quantity.
ValuationCreates initial layers and WMA/last-purchase cost baseline.No sales or purchase price effect.
TrackingEstablishes opening batch/serial/expiry balances.Untracked items create no identities.
Later Inventory transactionsSatisfies the posted-opening prerequisite for Stock In and related operations.Does not create a Purchase Invoice or Stock In source document.
Accounting/FinanceNo automatic GL, receivable, payable, tax, or cash effect.Separate opening journal is required by Finance policy.

Core acceptance scenarios

  1. Given valid Draft opening quantities, costs, UOMs, warehouses, and tracking, when Post is clicked, then stock headers/layers and WMA/last-purchase costs update and the opening balance becomes Posted.
  2. Given the current implementation, when opening balance posts, then no Inventory journal is created; Accounting opening must be handled separately until Finance confirms another policy.
  3. Given later Stock In requires warehouse opening, when opening is absent/unposted, then the receipt is blocked.

Required and optional data

  • Required: warehouse, opening-balance date, and at least one item line.
  • Required per line: item, variant, UOM, positive quantity, and cost.
  • Conditional: vendor batch for batch tracking; serial ID for serial tracking; expiry date for expiry-enabled stock; a system batch is used for untracked expiry stock.
  • Optional: descriptions/notes where exposed.
  • Derived: base UOM, base quantity, UOM cost, fixed cost, subcost, weighted-average cost, and last-purchase cost.

Clicking Post

  1. Accepts only Draft and revalidates active item/variant/UOM/warehouse references.
  2. Converts entered quantity and cost to base UOM values.
  3. Creates or updates item/warehouse and batch/serial stock headers.
  4. Adds inbound stock-detail history with source type Inventory Opening Balance.
  5. Increases available quantity and recalculates Weighted Average and Last Purchase costs.
  6. Marks the opening balance Posted and commits.

GL effect

The current PostInvOpeningBalanceCommandHandler does not create a journal or resolve Warehouse/Adjustment/Opening accounts. This is a confirmed code behavior, not an assumed business rule. Finance must confirm whether the opening GL balance is expected to be loaded separately through Accounting Opening Balance Journals.

Downstream effect

Stock In posting checks that a warehouse opening balance has been posted. Therefore a missing/unposted warehouse opening balance blocks later receipts.

Status, action, and reversal controls

ActionRequired state/authorityPreconditions and affected dataFailure/reversal rule
Add/SaveOpening Add; DraftWarehouse/date, positive item/UOM quantity, cost, trackingDraft has no stock baseline
Edit/DeleteDraft plus permissionNo Posted opening or later stock dependencyPosted baseline is immutable through Draft workflow
PostDraft plus PostValid migration scope, duplicates, conversions, cost and trackingCreates opening quantity/layers/WMA/LPC/tracking and Posted state atomically; current handler creates no GL
View/PrintView/print authorityAuthorized recordRead-only migration evidence
CorrectApproved migration correction processLater Stock In/Out/transfer/count dependencies assessedUse controlled reversal/adjustment; separately reconcile Accounting opening because it was not delegated

Reconciliation controls

ControlRequired equality
Opening base quantitySum of opening tracking identities
Opening valueQuantity x accepted opening cost, subject to precision policy
Created inventory baselinePosted opening details/layers by warehouse and item
Inventory GLNo automatic journal in current handler; must reconcile to separate Accounting opening policy

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)ListOpeningBalanceComponentList/navigationPermission: AppInventoryPermissions.InventoryOpeningBalance_List; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/modules/opening-balance/opening-balance-routing.module.ts:25
(module default)OpeningBalanceComponentList/navigationPermission: AppInventoryPermissions.InventoryOpeningBalance_List; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/modules/opening-balance/opening-balance-routing.module.ts:14
addAddOpeningBalanceComponentAddPermission: AppInventoryPermissions.InventoryOpeningBalance_Add; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/modules/opening-balance/opening-balance-routing.module.ts:58
edit/:idEditOpeningBalanceComponentEditPermission: AppInventoryPermissions.InventoryOpeningBalance_Edit; Guards: PolicyAuthGuard, preventPostedGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/modules/opening-balance/opening-balance-routing.module.ts:35
opening-balanceLazy-loaded routeList/navigationGuards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:328
sequenceSequenceComponentSequenceNo route-level permission/guard found in source windowExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/modules/opening-balance/opening-balance-routing.module.ts:69
sequenceSequenceComponentSequenceGuards: PolicyAuthGuardFeature token matchapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:112
sequenceSequenceComponentSequencePermission: AppInventoryPermissions.StockOut_View; Guards: PolicyAuthGuardFeature token matchapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:176
sequenceSequenceComponentSequenceNo route-level permission/guard found in source windowFeature token matchapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:264
sequenceSequenceComponentSequenceNo route-level permission/guard found in source windowFeature token matchapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:316
sequenceSequenceComponentSequencePermission: AppInventoryPermissions.Purchase_LandedCost_AddFeature token matchapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:363
transactionsLazy-loaded routeList/navigationNo route-level permission/guard found in source windowFeature token matchapps/inventory/src/app/remote-entry/entry.routes.ts:27
view/:idViewOpeningBalanceComponentView/detailPermission: AppInventoryPermissions.InventoryOpeningBalance_View; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/modules/opening-balance/opening-balance-routing.module.ts:47

Observed frontend fields and validation

ComponentControlObserved frontend ruleComponent source
HeaderOpeningBalanceComponentaccumulatedDepOpeningBalanceJournalEntryLineIdRequired in component validatorapps/fixedAssets/src/app/remote-entry/transactions/assets-openinga-balance/component/header-opening-balance/header-opening-balance.component.ts
HeaderOpeningBalanceComponentassetOpeningBalanceJournalEntryLineIdRequired in component validatorapps/fixedAssets/src/app/remote-entry/transactions/assets-openinga-balance/component/header-opening-balance/header-opening-balance.component.ts
HeaderOpeningBalanceComponentopeningBalanceJournalEntryLineIdRequired in component validatorapps/fixedAssets/src/app/remote-entry/transactions/assets-openinga-balance/component/header-opening-balance/header-opening-balance.component.ts

Observed frontend actions

ComponentClick action/handlerBusiness review requirementComponent source
BodyOpeningBalanceComponentdeleteAssetsLineAvailable 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/fixedAssets/src/app/remote-entry/transactions/assets-openinga-balance/component/body-opening-balance/body-opening-balance.component.ts
BodyOpeningBalanceComponentopenDialogOpen 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/fixedAssets/src/app/remote-entry/transactions/assets-openinga-balance/component/body-opening-balance/body-opening-balance.component.ts
ListOpeningBalanceComponentdeleteOpeningBalanceAvailable 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/modules/opening-balance/pages/list-opening-balance/list-opening-balance.component.ts
ListOpeningBalanceComponentonEditOpen 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/modules/opening-balance/pages/list-opening-balance/list-opening-balance.component.ts
ListOpeningBalanceComponentonPostAvailable only for a postable Draft and Post permission; confirm the document, prevent a second submission, call the posting API, and refresh status, journal/stock links, and row actions. Failure must keep the pre-post state.apps/inventory/src/app/remote-entry/transactions/modules/opening-balance/pages/list-opening-balance/list-opening-balance.component.ts
ListOpeningBalanceComponentonUnPostAvailable only for a postable Draft and Post permission; confirm the document, prevent a second submission, call the posting API, and refresh status, journal/stock links, and row actions. Failure must keep the pre-post state.apps/inventory/src/app/remote-entry/transactions/modules/opening-balance/pages/list-opening-balance/list-opening-balance.component.ts
ListOpeningBalanceComponentonViewOpen 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/modules/opening-balance/pages/list-opening-balance/list-opening-balance.component.ts
ListOpeningBalanceComponentprintoutGenerate 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/modules/opening-balance/pages/list-opening-balance/list-opening-balance.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.InventoryOpeningBalance_List; Guards: PolicyAuthGuard
  2. add - Add: Render permitted fields, required indicators, defaults, dependent lookups, validation, Save/Cancel, loading, success, and API-error states. Security observed: Permission: AppInventoryPermissions.InventoryOpeningBalance_Add; Guards: PolicyAuthGuard
  3. edit/:id - Edit: Load the identified record, enforce state/permission field locks, preserve concurrency identity, validate changes, and return without losing filters. Security observed: Permission: AppInventoryPermissions.InventoryOpeningBalance_Edit; Guards: PolicyAuthGuard, preventPostedGuard
  4. opening-balance - 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: Guards: PolicyAuthGuard
  5. sequence - Sequence: Show current numbering scope and permit only authorized, valid sequence configuration without retroactively renumbering posted documents. Security observed: No route-level permission/guard found in source window
  6. transactions - 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: No route-level permission/guard found in source window
  7. 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.InventoryOpeningBalance_View; Guards: PolicyAuthGuard

Internal Documentation — Microtec