Appearance
Asset Transfers
| Backlog field | Value |
|---|---|
| Story ID | AP-FA-007 |
| Epic | Fixed Assets |
| 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 asset custodians, I want to move assets between locations or responsibility contexts so that custody and location history remain traceable.
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
- Asset Transfer Post usually creates no GL entry because it changes location/custody rather than asset value.
- Validate Draft, transfer date, different source/target location/custody, and each asset's current eligible status/location.
- Record previous and target assignment, update asset custody/location history, and mark transfer Posted.
- Commit all asset assignments and transfer status together.
- Unpost restores the previous assignment only if no later transfer, sale, disposal, or other lifecycle action changed the asset.
General ledger posting
| Condition | Debit | Credit | Result |
|---|---|---|---|
| Normal asset transfer | - | - | No GL entry because value does not change. |
Other module and document effects
| Area | Effect | Conditional/no effect |
|---|---|---|
| Asset register | Updates location/custodian and preserves prior assignment history. | Cost, accumulated depreciation, NBV, inventory, customer/vendor, and cash remain unchanged. |
| Lifecycle | Later transfer/sale/disposal sees new assignment. | Later activity can block unpost. |
Core acceptance scenarios
- Given a Draft transfer and assets still in the recorded source location, when the custodian clicks Post, then asset location/custody history and transfer status update together.
- Given a later lifecycle action changed the asset after transfer, when the operation is attempted, then Unpost 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.
Transfer requires Draft, date, source and different target location/custody, and eligible assets not locked by another lifecycle operation. Post updates asset location/custody history and marks the transfer Posted; it creates no value change unless the configured transfer type requires accounting. Unpost requires the current asset state still matches the posted transfer target, restores prior assignment, and marks Unposted. Later transfers or disposal/sale 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) | TransferListComponent | List/navigation | Permission: AppFixedAssetsPermissions.AssetsTransfer_List; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/assets-transfer-routing.module.ts:20 |
| (module default) | TransferMainComponent | List/navigation | Permission: AppFixedAssetsPermissions.AssetsTransfer_List; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/assets-transfer-routing.module.ts:12 |
| add | TransferAddComponent | Add | Permission: AppFixedAssetsPermissions.AssetsTransfer_Add; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/assets-transfer-routing.module.ts:30 |
| assets-transfer | Lazy-loaded route | List/navigation | No route-level permission/guard found in source window | Explicit frontend feature rule | apps/fixedAssets/src/app/remote-entry/transactions/transactions-routing.module.ts:62 |
| edit/:id | TransferEditComponent | Edit | Permission: AppFixedAssetsPermissions.AssetsTransfer_Edit; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/assets-transfer-routing.module.ts:40 |
| sequence | SequenceComponent | Sequence | No route-level permission/guard found in source window | Explicit frontend feature rule | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/assets-transfer-routing.module.ts:60 |
| view/:id | TransferViewComponent | View/detail | Permission: AppFixedAssetsPermissions.AssetsTransfer_View; Guards: PolicyAuthGuard | Explicit frontend feature rule | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/assets-transfer-routing.module.ts:50 |
Observed frontend fields and validation
| Component | Control | Observed frontend rule | Component source |
|---|---|---|---|
| TransferHeaderComponent | activationDate | Required in component validator | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/component/transfer-header/transfer-header.component.ts |
| TransferHeaderComponent | defaultToLocationId | Visible control; optional or API-dependent | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/component/transfer-header/transfer-header.component.ts |
| TransferHeaderComponent | description | Visible control; optional or API-dependent | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/component/transfer-header/transfer-header.component.ts |
| TransferHeaderComponent | referenceNo | Visible control; optional or API-dependent | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/component/transfer-header/transfer-header.component.ts |
| TransferHeaderComponent | transferDate | Required in component validator | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/component/transfer-header/transfer-header.component.ts |
Observed frontend actions
| Component | Click action/handler | Business review requirement | Component source |
|---|---|---|---|
| TransferBodyComponent | onDeleteRow | 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/fixedAssets/src/app/remote-entry/transactions/assets-transfer/component/transfer-body/transfer-body.component.ts |
| TransferBodyComponent | openDialog | Open 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-transfer/component/transfer-body/transfer-body.component.ts |
| TransferHeaderComponent | openAttachments | Document the enabled state, permission, input context, resulting API or navigation effect, success refresh, and concrete failure behavior for this handler. | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/component/transfer-header/transfer-header.component.ts |
| TransferListComponent | 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/fixedAssets/src/app/remote-entry/transactions/assets-transfer/pages/assets-transfer-list/assets-transfer-list.component.ts |
| TransferListComponent | 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/fixedAssets/src/app/remote-entry/transactions/assets-transfer/pages/assets-transfer-list/assets-transfer-list.component.ts |
| TransferListComponent | 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/fixedAssets/src/app/remote-entry/transactions/assets-transfer/pages/assets-transfer-list/assets-transfer-list.component.ts |
| TransferListComponent | onPrintOut | 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/fixedAssets/src/app/remote-entry/transactions/assets-transfer/pages/assets-transfer-list/assets-transfer-list.component.ts |
| TransferListComponent | onUnPost | 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/fixedAssets/src/app/remote-entry/transactions/assets-transfer/pages/assets-transfer-list/assets-transfer-list.component.ts |
| TransferListComponent | 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/fixedAssets/src/app/remote-entry/transactions/assets-transfer/pages/assets-transfer-list/assets-transfer-list.component.ts |
| TransferViewComponent | openAttachments | Document the enabled state, permission, input context, resulting API or navigation effect, success refresh, and concrete failure behavior for this handler. | apps/fixedAssets/src/app/remote-entry/transactions/assets-transfer/pages/assets-transfer-view/assets-transfer-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: AppFixedAssetsPermissions.AssetsTransfer_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: AppFixedAssetsPermissions.AssetsTransfer_Add; Guards: PolicyAuthGuard
- assets-transfer - 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
- 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: AppFixedAssetsPermissions.AssetsTransfer_Edit; Guards: PolicyAuthGuard
- 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
- 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: AppFixedAssetsPermissions.AssetsTransfer_View; Guards: PolicyAuthGuard