Appearance
Purchase Returns
| Backlog field | Value |
|---|---|
| Story ID | AP-PUR-005 |
| Epic | Purchase |
| 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 accounts-payable users, I want to return eligible purchased quantities and post the supplier credit effect so that stock and payables are corrected without exceeding the source invoice.
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
- Validate Draft return, posted source invoice, open period, vendor, positive quantities within remaining returnable quantity, taxes, and sufficient matching warehouse/tracking stock.
- Resolve Vendor Payable from vendor then category, Landed Cost Expense from service item then item category, and tax from Tax then Tax Group.
- Delegate posted Stock Out to Inventory: debit Goods in Transit/returned landed expense and credit Warehouse GL while reducing stock.
- Purchase builds the commercial return journal, debits Vendor Payable, reverses tax and purchase value, links Stock Out/journals, and updates source return quantities/status.
- All effects commit together; stock shortage, over-return, missing account, or delegate failure leaves the return Draft and source unchanged.
General ledger posting
| Component | Debit | Credit | Condition |
|---|---|---|---|
| Vendor reversal | Vendor Payable | Purchase/GIT or expense reversal | Payable vendor then category. |
| Tax reversal | Vendor Payable/tax clearing | Input Tax | Direction follows original tax; Tax then Tax Group. |
| Storable-item issue | Goods in Transit / returned landed expense | Warehouse GL | Delegated Stock Out at returned inventory cost. |
| Service return | Vendor Payable | Expense reversal | No Warehouse GL entry. |
Other module and document effects
| Area | Effect | Conditional/no effect |
|---|---|---|
| Inventory | Storable lines create Stock Out and consume layers/tracking. | Service lines create no Stock Out. |
| Source Purchase Invoice | Updates returned quantity and partial/full return status. | Over-return is blocked. |
| Vendor/Finance | Reduces payable/due balance. | Cash refund requires separate Payment In unless another workflow supplies it. |
Core acceptance scenarios
- Given a Draft return against a posted purchase with sufficient returnable quantity and warehouse stock, when the user clicks Post, then Stock Out, purchase-return journal, landed/tax reversal, vendor balance, and source return status commit together.
- Given requested quantity exceeds source remaining quantity or warehouse availability, when the operation is attempted, then Post is rejected without reducing either source quantity or stock.
- 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
An eligible posted Purchase Invoice, Draft return, vendor, open-period date, returnable lines, positive quantities within remaining purchased quantity, and correct warehouse/UOM/tracking for storable items are required. Reason/notes are optional; tax and landed-cost reversal data are conditional.
Account resolution and Post effects
- Vendor payable: vendor then vendor category.
- Inventory: linked Stock Out credits Warehouse GL and debits Goods in Transit.
- Landed-cost return portion: service item expense then item category expense, grouped by account/cost center.
- VAT: tax account then group account according to reversal effect.
Post decreases stock using current batch WMA cost in the return builder, creates/links Stock Out, reverses purchase/landed/tax value, debits vendor payable, updates source invoice returned quantities/status, marks Posted, and commits atomically. It fails on insufficient stock, excessive return quantity, missing accounts/tax/tracking, closed period, or non-Draft state.
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) | PurchaseReturnListComponent | List/navigation | Permission: AppPurchasePermissions.ReturnInvoice_List; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/purchase/src/app/remote-entry/purchase-transactions/purchase-transactions.module.ts:122 |
| add | AddReturnPurchaseComponent | Add | Permission: AppPurchasePermissions.ReturnInvoice_Add; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/purchase/src/app/remote-entry/purchase-transactions/purchase-transactions.module.ts:132 |
| edit/:id | EditReturnPurchaseComponent | Edit | Permission: AppPurchasePermissions.ReturnInvoice_Edit; Guards: PolicyAuthGuard, preventPostedGuard | Explicit frontend feature rule | apps/purchase/src/app/remote-entry/purchase-transactions/purchase-transactions.module.ts:143 |
| return-purchase-invoice | MainReturnPurchaseComponent | List/navigation | Permission: AppPurchasePermissions.ReturnInvoice_List; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/purchase/src/app/remote-entry/purchase-transactions/purchase-transactions.module.ts:112 |
| view/:id | PurchaseReturnViewComponent | View/detail | Permission: AppPurchasePermissions.ReturnInvoice_View; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/purchase/src/app/remote-entry/purchase-transactions/purchase-transactions.module.ts:155 |
Observed frontend fields and validation
| Component | Control | Observed frontend rule | Component source |
|---|---|---|---|
| AddReturnPurchaseComponent | invoiceDate | Required in component validator | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/add-return-purchase/add-return-purchase.component.ts |
| AddReturnPurchaseComponent | paymentTermName | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/add-return-purchase/add-return-purchase.component.ts |
| AddReturnPurchaseComponent | returnDescription | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/add-return-purchase/add-return-purchase.component.ts |
| AddReturnPurchaseComponent | returnQuantity | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/add-return-purchase/add-return-purchase.component.ts |
| AddReturnPurchaseComponent | vendorId | Required in component validator | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/add-return-purchase/add-return-purchase.component.ts |
| AddReturnPurchaseComponent | warehouseId | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/add-return-purchase/add-return-purchase.component.ts |
| EditReturnPurchaseComponent | invoiceDate | Required in component validator | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/edit-return-purchase/edit-return-purchase.component.ts |
| EditReturnPurchaseComponent | returnDescription | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/edit-return-purchase/edit-return-purchase.component.ts |
| EditReturnPurchaseComponent | returnQuantity | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/edit-return-purchase/edit-return-purchase.component.ts |
| PurchaseReturnViewComponent | currency | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | currencyRate | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | invoiceCode | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | invoiceDate | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | invoiceDescription | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | vendorId | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | vendorName | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | warehouseId | Visible control; optional or API-dependent | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
Observed frontend actions
| Component | Click action/handler | Business review requirement | Component source |
|---|---|---|---|
| AddReturnPurchaseComponent | deleteRow | 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/add-return-purchase/add-return-purchase.component.ts |
| EditReturnPurchaseComponent | deleteRow | 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/edit-return-purchase/edit-return-purchase.component.ts |
| EditReturnPurchaseComponent | routeTojournal | Navigate only when a linked journal ID exists and the user can view Accounting; otherwise the action is absent/disabled and no transaction data changes. | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/edit-return-purchase/edit-return-purchase.component.ts |
| EditReturnPurchaseComponent | routeToStockOut | Navigate only when the transaction produced a linked Stock In/Stock Out record and Inventory view permission is available; service-only/no-stock cases expose no stock link. | apps/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/edit-return-purchase/edit-return-purchase.component.ts |
| PurchaseReturnListComponent | 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-list/purchase-return-list.component.ts |
| PurchaseReturnListComponent | onEdit | Open 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-list/purchase-return-list.component.ts |
| PurchaseReturnListComponent | 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-list/purchase-return-list.component.ts |
| PurchaseReturnListComponent | onView | Open 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-list/purchase-return-list.component.ts |
| PurchaseReturnListComponent | printout | Generate 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-list/purchase-return-list.component.ts |
| PurchaseReturnListComponent | routeToJournalentryView | Open 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-list/purchase-return-list.component.ts |
| PurchaseReturnListComponent | routeToStockOuttryView | Open 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-list/purchase-return-list.component.ts |
| PurchaseReturnViewComponent | routeToJournalentryView | Open 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | routeToPurchaseInvoiceView | Open 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.component.ts |
| PurchaseReturnViewComponent | routeToStockOuttryView | Open 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/purchase/src/app/remote-entry/purchase-transactions/pages/return-purchase-invoice/purchase-return-view/purchase-return-view.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: AppPurchasePermissions.ReturnInvoice_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: AppPurchasePermissions.ReturnInvoice_Add; Guards: PolicyAuthGuard
- 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: AppPurchasePermissions.ReturnInvoice_Edit; Guards: PolicyAuthGuard, preventPostedGuard
- return-purchase-invoice - 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: AppPurchasePermissions.ReturnInvoice_List; Guards: PolicyAuthGuard
- 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: AppPurchasePermissions.ReturnInvoice_View; Guards: PolicyAuthGuard