Appearance
Payment Reconciliation
| Backlog field | Value |
|---|---|
| Story ID | AP-FIN-007 |
| Epic | Finance |
| Story type | Business workflow |
| Review status | Draft - business analyst and business owner review required |
| Source evidence | AppsPortal.Apis controller, application command/query handlers, validators, domain entities, and matching MFE workflow where available |
| Last source review | 2026-07-10 |
User story
As finance accountants, I want to match payments to invoices or other open balances so that settled and outstanding amounts are accurate.
Posting reference: AppsPortal Posting Ledger explains debit/credit construction. Delegation and Interaction Contracts explains module ownership, delegated commands, linked records, commit boundaries, and reversal propagation.
Acceptance criteria
- Only authorized users can view or execute the feature's actions.
- Required business data, active dependencies, tenant/branch scope, and current document state are validated.
- Invalid requests return actionable validation errors without applying a partial change.
- Search, list, view, print, and export operations use the same authorized business scope.
- Material create, edit, status, approval, posting, reversal, and integration actions are auditable where applicable.
- Financial or stock effects are applied atomically and no more than once.
Posting details
- Reconciliation Post creates no new cash journal because Payment In already recorded the receipt.
- Validate the Payment In detail is Posted and has sufficient Amount To Reconcile; validate every selected Sales Invoice, Asset Sales Invoice, or Customer Opening Balance due line is eligible and sufficient.
- Delegate target-balance changes to the owning Sales/Fixed Assets/opening-balance logic.
- Reduce payment availability and target due balances, create reconciliation records, update fully-reconciled flags, assign sequence, and mark Posted.
- All balance changes commit together. Payment In Unpost must restore every linked balance before reversing cash.
General ledger posting
| Condition | Debit | Credit | Result |
|---|---|---|---|
| Reconciliation Post | - | - | No new cash/GL journal; the Payment In already recorded cash and party account. |
Other module and document effects
| Target | Effect | Conditional/no effect |
|---|---|---|
| Payment In | Reduces Amount To Reconcile and updates fully reconciled state. | Cannot allocate beyond available amount. |
| Sales/Asset invoice/customer opening | Reduces selected due/outstanding balances and stores reconciliation links. | No Inventory effect and no new cash movement. |
| Unpost | Restores target balances and payment availability before cash reversal. | Blocked if dependents cannot reverse. |
Core acceptance scenarios
- Given a posted Payment In detail and target due balances with remaining amounts, when the user posts reconciliation lines within both available limits, then payment availability and each target due balance decrease and reconciliation records become Posted.
- Given the total exceeds payment availability or a target due balance, when the operation is attempted, then the entire reconciliation is rejected.
- Given the same Post request is retried after success, when the source is no longer Draft, then no duplicate journal, stock, balance, payment, or lifecycle effect is created.
Required and conditional data
A posted Payment In detail with remaining amount and one or more positive reconciliation lines are required. Each line identifies a supported source (Sales Invoice, Asset Sales Invoice, or Customer Opening Balance), target document/balance, and amount. Total cannot exceed payment remaining amount or any target local due balance.
Clicking Post
Post validates payment/detail, target status, open periods, currency/local balance, and line amounts. It decreases payment amount-to-reconcile, decreases each selected due balance, creates source-specific reconciliation records, updates fully-reconciled flags, assigns a reconciliation sequence, marks the reconciliation Posted, and commits atomically. It does not create a second cash journal because the cash movement was posted by Payment In.
Unposting the source Payment In must undo these reconciliation records and restore all payment and target balances; locked, closed-period, missing, or changed targets block unpost.
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/page | Component | Mode | Route permission/guard | Mapping confidence | Source |
|---|---|---|---|---|---|
| (module default) | ListPaymentReconciliationComponent | List/navigation | Permission: AppFinancePermissions.PaymentReconciliation_List; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/payment-reconcilation-routing.module.ts:24 |
| (module default) | MainPaymentReconciliationComponent | List/navigation | Permission: AppFinancePermissions.PaymentReconciliation_List; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/payment-reconcilation-routing.module.ts:14 |
| add | AddPaymentReconciliationComponent | Add | Permission: AppFinancePermissions.PaymentReconciliation_Add; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/payment-reconcilation-routing.module.ts:35 |
| payment-reconcilation | Lazy-loaded route | List/navigation | No route-level permission/guard found in source window | Explicit frontend feature rule | apps/finance/src/app/remote-entry/transactions/transactions.module.ts:176 |
| sequence | SequenceComponent | Sequence | No route-level permission/guard found in source window | Explicit frontend feature rule | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/payment-reconcilation-routing.module.ts:46 |
Observed frontend fields and validation
| Component | Control | Observed frontend rule | Component source |
|---|---|---|---|
| - | - | No reactive formControlName controls were found for the routed components; review template-driven/shared child components. | Route sources above |
Observed frontend actions
| Component | Click action/handler | Business review requirement | Component source |
|---|---|---|---|
| ListPaymentReconciliationComponent | onDelete | Available 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/finance/src/app/remote-entry/transactions/payment-reconcilation/pages/list-payment-reconciliation/list-payment-reconciliation.component.ts |
| ListPaymentReconciliationComponent | onPost | Available 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/finance/src/app/remote-entry/transactions/payment-reconcilation/pages/list-payment-reconciliation/list-payment-reconciliation.component.ts |
| ReconciliationCustomerInfoComponent | onSelectCustomer | Document the enabled state, permission, input context, resulting API or navigation effect, success refresh, and concrete failure behavior for this handler. | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/components/reconciliation-customer-info/reconciliation-customer-info.component.ts |
| ReconciliationInvoiceDetailsComponent | onAutoDistribute | Document the enabled state, permission, input context, resulting API or navigation effect, success refresh, and concrete failure behavior for this handler. | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/components/reconciliation-invoice-details/reconciliation-invoice-details.component.ts |
| ReconciliationInvoiceDetailsComponent | onReconcile | Document the enabled state, permission, input context, resulting API or navigation effect, success refresh, and concrete failure behavior for this handler. | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/components/reconciliation-invoice-details/reconciliation-invoice-details.component.ts |
| ReconciliationInvoiceDetailsComponent | resetReconcile | Document the enabled state, permission, input context, resulting API or navigation effect, success refresh, and concrete failure behavior for this handler. | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/components/reconciliation-invoice-details/reconciliation-invoice-details.component.ts |
| ReconciliationPaymentInfoComponent | onSelectPayment | Document the enabled state, permission, input context, resulting API or navigation effect, success refresh, and concrete failure behavior for this handler. | apps/finance/src/app/remote-entry/transactions/payment-reconcilation/components/reconciliation-payment-info/reconciliation-payment-info.component.ts |
Page-specific frontend acceptance criteria
- (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: AppFinancePermissions.PaymentReconciliation_List; Guards: PolicyAuthGuard
- add - Add: Render permitted fields, required indicators, defaults, dependent lookups, validation, Save/Cancel, loading, success, and API-error states. Security observed: Permission: AppFinancePermissions.PaymentReconciliation_Add; Guards: PolicyAuthGuard
- payment-reconcilation - 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
- 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