Skip to content

Inventory Reconciliation

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

Compare physical counts with system stock and post controlled adjustments for variances.

User stories

  • As a stock controller, I want to start a reconciliation for a warehouse and selected items so that I can record physical counts.
  • As a stock controller, I want to enter counts manually or upload an Excel file so that large counts are practical.
  • As a supervisor, I want to review expected, actual, and variance quantities/values before posting.
  • As an auditor, I want to view, print, and export the reconciliation and its adjustments.

Acceptance criteria

  1. A reconciliation identifies its warehouse, count date, and item scope.
  2. Expected quantity is retained as an auditable snapshot or is clearly refreshed before posting.
  3. Actual quantities cannot be negative and tracked-item counts reconcile by tracking number.
  4. Import validates template, item identity, duplicates, units, tracking, and numeric values before confirmation.
  5. Variance is calculated as actual quantity minus expected quantity.
  6. Posting creates the required stock increase/decrease and accounting adjustment exactly once.
  7. Zero-variance lines do not create unnecessary stock movements.
  8. Posted reconciliations cannot be edited through the draft workflow.
  9. List, view, edit, delete, post, printout, export, and import actions follow their permissions.

Posting details

  1. Post freezes the reviewed system-count snapshot, validates the physical quantities and tracking totals, confirms the warehouse/open period, and ensures the count still owns the stock lock. Variance is physical quantity minus system quantity; zero-variance lines produce no stock or journal entry.
  2. A shortage delegates to Stock Out. The current implementation debits the configured Cost of Goods Sold account and credits the Warehouse GL for the shortage value. A surplus delegates to Stock In, debiting the Warehouse GL and crediting the configured Goods in Transit account.
  3. These COGS and Goods-in-Transit mappings are the implemented reconciliation behavior; they must not be described as a generic adjustment account unless the code and Finance policy are changed. Missing settings or warehouse accounts block Post.
  4. Posting records the delegated Stock Out/Stock In identifiers, their stock layers and tracking movements, linked journals, final variance values, reconciliation status, and closure of the physical-count lock.
  5. All variance delegates, journals, reconciliation links, status, and lock release commit together. Failure leaves the reconciliation Draft/open with the lock intact and no partial adjustment. Any approved reversal must undo each linked variance movement and restore a consistent count state.

General ledger posting

VarianceDebitCreditAmount basisDelegate
Shortage: physical below systemCost of Goods SoldWarehouse GLShortage quantity x issue costStock Out
Surplus: physical above systemWarehouse GLGoods in TransitSurplus quantity x receipt costStock In
Zero variance--NoneNo stock delegate or journal

Module and data effects

Affected areaEffectConditional/no-effect rule
InventoryShortages reduce layers; surpluses create layers; balances become physical count.Zero-variance lines do nothing.
TrackingAdjusts tracked quantities using the approved physical identities.No tracking effect on zero variance.
Physical countStores linked adjustment IDs, marks reconciliation Posted, and closes lock.Failure keeps Draft/open and retains lock.
AccountingPosts only the shortage/surplus rows above.No customer, vendor, tax, or cash effect.

Core acceptance scenarios

  1. Given a Draft physical count, when Post is clicked, then shortages delegate to Stock Out, surpluses delegate to Stock In, zero variances create no movement, linked IDs are stored, and the count lock closes atomically.
  2. Given any variance delegate cannot post, when reconciliation Post is attempted, then the count remains Draft/open and no partial variance movement is committed.

Required and derived data

  • Required header: warehouse, transaction/count date, and at least one item count line.
  • Required line: item, variant, UOM, system count, physical count, and tracking identity where applicable.
  • Optional: description and lines with no variance.
  • Derived: variance, shortage/surplus classification, cost from the configured costing method, and linked adjustment document codes.
  • Physical counts cannot be negative. Serial/batch/expiry data must match item configuration.

While a count is open

The warehouse is marked as having an open Inventory Count. Other Stock In/Out posting checks this condition and is blocked so system quantities do not change during the physical count.

Clicking Post

  1. Requires a Draft with at least one item and revalidates active references.
  2. Closes the warehouse count lock.
  3. For System Count > Physical Count, creates a posted Stock Out for the shortage.
  4. For Physical Count > System Count, creates a posted Stock In for the surplus.
  5. Uses Weighted Average or Last Purchase cost from Inventory settings; service/missing-stock cost is zero.
  6. Preserves batch/serial/expiry identity on adjustment lines.
  7. Stores linked Stock Out/Stock In IDs and codes, sets reconciliation date, and marks the count Posted.
  8. Zero-variance lines create no adjustment movement.

GL effect

  • Shortage: debit Inventory Settings COGS and credit Warehouse GL.
  • Surplus: debit Warehouse GL and credit Goods in Transit under the current internal Stock In default.

The use of COGS/Goods in Transit rather than the Adjustment account is source-confirmed current behavior and should be reviewed with Finance.

Status, action, and reversal controls

ActionRequired state/authorityPreconditions and affected dataFailure/reversal rule
Start countReconciliation AddWarehouse/date/item scope; no conflicting countCaptures expected snapshot and locks affected stock scope
Enter/import countsDraft/open count plus Edit/ImportNon-negative physical values, unique valid rows, tracking totalsInvalid file/row does not silently change accepted counts
ReviewView authorityExpected, physical, variance quantity/value availableReview itself posts nothing
PostDraft plus PostCount owns lock, open period, valid costs/accounts/trackingShortage Stock Out, surplus Stock In, journals, links, status, and lock release are atomic
ReverseApproved adjustment reversalNo later dependent stock and allowed period/count policyReverse every linked variance movement and re-establish a consistent counted balance; zero-variance lines remain no-op

Reconciliation controls

ControlRequired equality
VariancePhysical quantity minus expected snapshot
Shortage quantity/valueLinked Stock Out and COGS/Warehouse journal
Surplus quantity/valueLinked Stock In and Warehouse/GIT journal
Posted final balanceApproved physical count by tracking identity
Zero varianceNo stock detail and no journal line

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)InventReconcileListComponentList/navigationPermission: AppInventoryPermissions.InventoryReconcile_List; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:284
edit/:idEditInventReconcileComponentEditPermission: AppInventoryPermissions.InventoryReconcile_Edit; Guards: PolicyAuthGuard, preventPostedGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:295
inventory-reconciliationMainInventReconcileListComponentList/navigationPermission: AppInventoryPermissions.InventoryReconcile_List; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:276
view/:idViewInventReconcileComponentView/detailPermission: AppInventoryPermissions.InventoryReconcile_View; Guards: PolicyAuthGuardExplicit frontend feature ruleapps/inventory/src/app/remote-entry/transactions/transactions.module.ts:306

Observed frontend fields and validation

ComponentControlObserved frontend ruleComponent source
ActualInventoryTableComponentcategoryIdVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentitemVariantIdRequired in component validatorapps/inventory/src/app/remote-entry/transactions/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentphysicalCountRequired in component validatorapps/inventory/src/app/remote-entry/transactions/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentstatusVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponenttrackingNoVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/components/actual-inventory-table/actual-inventory-table.component.ts
EditInventReconcileComponentcountDateVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/pages/inventory-reconcile/edit-invent-reconcile/edit-invent-reconcile.component.ts
EditInventReconcileComponentdescriptionVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/pages/inventory-reconcile/edit-invent-reconcile/edit-invent-reconcile.component.ts
EditInventReconcileComponentreconcileTypeVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/pages/inventory-reconcile/edit-invent-reconcile/edit-invent-reconcile.component.ts
EditInventReconcileComponenttransactionCodeVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/pages/inventory-reconcile/edit-invent-reconcile/edit-invent-reconcile.component.ts
EditInventReconcileComponentwarehouseNameVisible control; optional or API-dependentapps/inventory/src/app/remote-entry/transactions/pages/inventory-reconcile/edit-invent-reconcile/edit-invent-reconcile.component.ts

Observed frontend actions

ComponentClick action/handlerBusiness review requirementComponent source
ActualInventoryTableComponentgetFileDocument 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/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentonAddLineAdd 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/transactions/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentonApplyFilterDocument 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/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentonDeleteByFormAvailable 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/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentonResetExcelDocument 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/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentonResetFilterDocument 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/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponentopenDialogOpen 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/components/actual-inventory-table/actual-inventory-table.component.ts
ActualInventoryTableComponenttoggleSidebarDocument 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/components/actual-inventory-table/actual-inventory-table.component.ts
EditInventReconcileComponentonOpenAttachmentDocument 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/inventory-reconcile/edit-invent-reconcile/edit-invent-reconcile.component.ts
InventReconcileListComponentnavigateHelpPageComponentDocument 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/inventory-reconcile/invent-reconcile-list/invent-reconcile-list.component.ts
InventReconcileListComponentonAddDocument 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/inventory-reconcile/invent-reconcile-list/invent-reconcile-list.component.ts
InventReconcileListComponentonDeleteAvailable 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/inventory-reconcile/invent-reconcile-list/invent-reconcile-list.component.ts
InventReconcileListComponentonEditOpen 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/inventory-reconcile/invent-reconcile-list/invent-reconcile-list.component.ts
InventReconcileListComponentonPostAvailable 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/pages/inventory-reconcile/invent-reconcile-list/invent-reconcile-list.component.ts
InventReconcileListComponentonViewOpen 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/inventory-reconcile/invent-reconcile-list/invent-reconcile-list.component.ts
InventReconcileListComponentprintoutGenerate 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/inventory-reconcile/invent-reconcile-list/invent-reconcile-list.component.ts
ViewInventReconcileComponentonOpenAttachmentDocument 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/inventory-reconcile/view-invent-reconcile/view-invent-reconcile.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.InventoryReconcile_List; Guards: PolicyAuthGuard
  2. 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.InventoryReconcile_Edit; Guards: PolicyAuthGuard, preventPostedGuard
  3. inventory-reconciliation - 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.InventoryReconcile_List; Guards: PolicyAuthGuard
  4. 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.InventoryReconcile_View; Guards: PolicyAuthGuard

Internal Documentation — Microtec