Payments Workflow
1. Executive Summary
Purpose
The Payments workflow manages the outbound payment execution lifecycle — from the moment payment items are created at worksheet approval through bank transmission, status confirmation, and GL posting. When a worksheet is approved, settlement payouts are converted into payment_item records that represent the funds owed to each payee (artist, manager, lawyer, business manager, etc.). This workflow covers how those items are selected, transmitted to banks through the appropriate adapter, monitored for confirmation, and eventually posted to the General Ledger. It is the final step in the cash-application chain: money leaves UTA's bank accounts and reaches the client's party.
Scope
Covered:
- Viewing all
payment_itemrecords with their execution status, payee details, and origin classification - Selecting and processing payment items through bank adapters (CNB EASI Link JSON, ISO 20022 XML, BofA CashPro, JPM Global Pay)
- Managing the "Do Not Send" hold flag on individual and bulk payment items
- Viewing execution history and bank transmission payloads for each payment item
- Retrying failed bank transmission executions
- Status polling — propagating bank confirmations back to
payment_item.payment_execution_status_cd - GL posting lifecycle tracked via
payment_item.posting_status_cd
Not covered (documented separately):
- Settlement creation and the division of PAY among deal parties — see Settlements Workflow
- Worksheet lifecycle and the approval that triggers payment item creation — see Worksheets Workflow
- Bank statement ingestion and inbound cash receipts — see Bank Ingestion Workflow
Key Objectives
- Ensure every approved PAY amount reaches its intended payee's bank account accurately and on record.
- Provide a complete, immutable audit trail of every bank transmission attempt, including the exact payload sent.
- Support retry logic for failed transmissions without losing the original failure record.
- Enforce the two-stage payment lifecycle:
SENTlocks the payment; GL posting occurs only after bank confirmation (ACKNOWLEDGEDorPAID).
2. Process Overview
flowchart TD
A["Worksheet Approved (T → A)"] --> B["Payment Items Created\npayment_item.payment_execution_status_cd\n= PENDING or WAITING"]
B --> C{payment_date in future\nor do_not_send_ind = true?}
C -->|Yes| D["WAITING\n(held from processing)"]
C -->|No| E["PENDING\n(ready for processing)"]
D --> F["Date arrives or hold released\n→ PENDING"]
F --> G["User selects items\non Payments page"]
E --> G
G --> H["User clicks Process\nor Retry"]
H --> I["Lock: payment_execution_status_cd\n= PROCESSING"]
I --> J["Gather payment data\nfrom payment_item + bank_account\n+ party_bank_account"]
J --> K["Determine payment schema\nfrom bank code_attribute\nPAYMENT_REQUEST_SCHEMA"]
K --> L["Create execution record\noutbound_payment_execution\nstatus = CREATED"]
L --> M["Build payload\nadapter generates JSON or XML"]
M --> N["Transmit to bank API"]
N --> O{Bank response}
O -->|HTTP 2xx| P["execution = SENT\npayment_item = SENT\nLOCKED"]
O -->|HTTP 4xx/5xx| Q["execution = FAILED\npayment_item reverts to PENDING"]
Q --> R["User may Retry\nfrom execution drawer"]
R --> I
P --> S["Status Polling\npoll bank for confirmation"]
S --> T{Bank status}
T -->|COMPLETED| U["execution = ACKNOWLEDGED\npayment_item = PAID"]
T -->|FAILED/REVERSED| V["execution = FAILED\npayment_item = FAILED"]
U --> W["GL Posting batch\nposting_status_cd = P\nposting_dt set"]Walkthrough
Payment item creation — When a worksheet transitions to Approved,
createPaymentItemsFromPayoutsconverts eachcash_receipt_payoutrecord into apayment_item. Initialpayment_execution_status_cdisWAITINGif the payment date is future-dated ordo_not_send_ind = true; otherwisePENDING. See Settlements Procedures: Create Payment Items from Payouts.Hold management (WAITING → PENDING) — Users can manually release a hold by clearing
do_not_send_ind, or the item transitions automatically when itspayment_datearrives. Future-dated payments remain inWAITINGuntil their date.Payments list review — A user with the SETTLEMENT_APPROVER or IT role navigates to the Payments management page (
/cash-management/payments). All payment items are displayed with their execution status, payee, amount, bank, deal, and origin (settlement, client ledger, refund, or other).Selection and processing — The user selects one or more
PENDINGitems using checkboxes and clicks "Process." Items that are returned or alreadySENTcannot be selected.Locking — The processor immediately sets all selected items to
PROCESSINGin a bulk update, preventing concurrent processing.Schema determination — For each item, the processor reads the
bank_idfrom the payee's bank account and looks up thePAYMENT_REQUEST_SCHEMAcode attribute. This determines which bank adapter to use:CNB_EASI_LINK,ISO20022_PAIN001,BOFA_CASHPRO, orJPM_GLOBAL_PAY.Execution record creation — An
outbound_payment_executionrecord is created withexecution_status_cd = 'CREATED', capturing the adapter schema, service level (WIRE or ACH), amount, and currency.Payload build and save — The selected adapter generates the bank-specific payload (JSON for CNB EASI Link, XML for ISO 20022 variants). The payload is saved to
outbound_payment_execution.generated_payloadbefore transmission — ensuring it is preserved even if the bank call fails.Bank transmission — The payload is sent to the bank API. On HTTP 2xx:
execution_status_cd = 'SENT',payment_item.payment_execution_status_cd = 'SENT', and thebank_reference_idis stored. The payment is now locked. On failure:execution_status_cd = 'FAILED', payment item reverts toPENDINGfor retry.Status polling — For adapters that support polling (CNB EASI Link), the
PaymentStatusPollerServiceperiodically polls the bank API using the storedbank_reference_id. When the bank confirms completion,payment_item.payment_execution_status_cdadvances toPAIDand the execution record toACKNOWLEDGED.GL posting — A batch process picks up payment items in unposted GL posting status (
posting_status_cd = 'U') where the bank has confirmed completion (payment_execution_status_cd = 'ACKNOWLEDGED'or'PAID'). It setsposting_status_cd = 'P'andposting_dt. Payments that were cancelled before being sent receiveposting_status_cd = 'X'(Skipped) — no GL entry is created.
3. Business Rules
3.1 Payment Items Are Created at Worksheet Approval
Business rule: Payment items are not created when a settlement is saved or when a worksheet is settled. They are created only when the worksheet transitions from Settled (T) to Approved (A). Each cash_receipt_payout with payment_item_id IS NULL generates exactly one payment_item. Payouts with zero amounts are skipped.
Foundation reference: Settlements Procedures: Create Payment Items from Payouts
3.2 Initial Status Is WAITING or PENDING
Business rule: A newly created payment item starts in WAITING when its payment_date is in the future or do_not_send_ind = true. Otherwise it starts in PENDING and is immediately eligible for bank transmission.
Foundation reference: Settlements Data Model: Payment Item Execution Status Transitions
Workflow context: Items in WAITING status appear in the Payments list but their checkboxes are disabled — they cannot be selected for processing until the hold is released.
3.3 Adapter Selection Is Schema-Driven, Not Bank-Driven
Business rule: The bank adapter used for transmission is determined by looking up the PAYMENT_REQUEST_SCHEMA code attribute on the beneficiary's bank (keyed by bank_account.bank_id), not by the bank's identity directly. This means any bank can adopt any payment schema without requiring code changes.
Foundation reference: Settlements Procedures: Process Pending Payment Items — Step 3
Workflow context: If a payment item's payee bank account does not have a bank_id configured, processing will fail immediately with an error message. The item reverts to PENDING and can be retried after the bank account is corrected.
3.4 Payload Is Saved Before Transmission
Business rule: The bank-specific payload is saved to outbound_payment_execution.generated_payload immediately after it is built and before the HTTP call is attempted. This ensures the payload exists for audit and debugging even if the transmission fails.
Foundation reference: Settlements Procedures: Process Pending Payment Items — Step 5
Workflow context: Users can view the saved request payload from the Execution Details drawer on the Payments page.
3.5 SENT Status Locks the Payment
Business rule: Once payment_item.payment_execution_status_cd reaches SENT, the payment is locked. It cannot be reversed, voided, or modified. The linked participant_settlement, its items, and the associated PAY applications are also locked as a unit. Statuses PROCESSING, SENT, ACKNOWLEDGED, and PAID are all considered locked.
Foundation reference: Settlements Data Model: Locked Statuses
Workflow context: Locked payment items have their checkboxes and "Do Not Send" toggle disabled on the Payments list. A "Returned" badge is shown instead of the normal status badge for items that were cancelled during a worksheet return.
3.6 Failure Reverts to PENDING, Not FAILED
Business rule: When a bank API call fails (HTTP error, timeout, or rejection), the outbound_payment_execution record is set to FAILED, but the payment_item.payment_execution_status_cd reverts to PENDING — not FAILED. This allows the user to immediately retry without any manual status correction. The FAILED status on payment_item is reserved for bank-reported failures received through status polling after the payment was initially accepted.
Foundation reference: Settlements Procedures: Process Pending Payment Items — Step 6
Workflow context: After a failed transmission, the item reappears in the Payments list as PENDING and can be selected and processed again.
3.7 Retry Creates a New Execution Record
Business rule: Retrying a failed execution does not modify the failed outbound_payment_execution record. A new outbound_payment_execution record is created for the same payment_item_id. This preserves a complete, immutable audit trail of every transmission attempt.
Foundation reference: Settlements Procedures: Retry Failed Execution
Workflow context: The Execution Details drawer shows all execution attempts for a payment item in reverse chronological order. Only the latest failed execution shows a "Retry" button.
3.8 GL Posting Requires Bank Confirmation
Business rule: GL posting for outbound payments occurs only after the bank confirms completion (payment_execution_status_cd = 'ACKNOWLEDGED' or 'PAID'). The GL posting batch selects items with posting_status_cd = 'U' that have reached a confirmed terminal status.
Foundation reference: Settlements Data Model: GL Posting Status
3.9 Cancelled Payments Skip GL Posting
Business rule: Payment items cancelled during a worksheet return (before being sent) have posting_status_cd = 'X' (Skipped). No GL entry is created, and no GL reversal is posted. You cannot reverse a GL entry that was never posted.
Foundation reference: Settlements Data Model: GL Posting Status — Transition to Skipped
3.10 Do Not Send Holds Payment Without Cancellation
Business rule: Setting do_not_send_ind = true on a payment item holds it in WAITING status without cancelling it. The payment remains associated with its settlement and can be released at any time by clearing the flag. This is different from cancellation — the payment item is not voided and its settlement is not affected.
Foundation reference: Settlements Data Model: payment_item.do_not_send_ind
Workflow context: The "Do Not Send" toggle appears as an inline switch on each row in the Payments list. Bulk "Mark Do Not Send" and "Clear Do Not Send" buttons appear in the page header when items are selected.
3.11 Service Level (WIRE vs ACH) Is Determined by Payee's Preferred Payment Method
Business rule: The service level for bank transmission is derived from party_bank_account.preferred_payment_method for the payee's bank account. If preferred_payment_method = 'WIRE', the payment is transmitted as a wire transfer (ISO 20022 service level URGP); otherwise it defaults to ACH (NURG). This affects both the payload format and the bank API routing.
Foundation reference: Settlements Queries: Gather Payment Data for Bank Transmission
4. Data Access & Operations References
4.1 Queries Used
| Operation | Foundation Doc | Purpose in This Workflow |
|---|---|---|
getPaymentItemsWithOrigin | Settlements Queries: Get Payment Items with Origin Data | Loads all payment items for the Payments list page, enriched with payee name, deal name, bank details, origin classification, and latest execution summary |
getPaymentItemWithOriginById | Settlements Queries: Get Single Payment Item with Origin Data | Loads a single payment item for the Execution Details drawer |
getExecutionsByPaymentItemId | Settlements Queries: Get Executions by Payment Item ID | Loads the full execution history for a payment item shown in the drawer's Processing Timeline |
getFailedExecutions | Settlements Queries: Get Failed Executions | Retrieves all failed executions eligible for retry (used by the status poller and admin views) |
gatherPaymentData | Settlements Queries: Gather Payment Data for Bank Transmission | Collects the source account, beneficiary bank details, service level, and bank_id needed to build the bank payload |
getUnpostedItemsUpToDate | Settlements Queries: Get Unposted Payment Items Up to Date | Feeds the GL posting batch — retrieves all posting_status_cd = 'U' items up to a cutoff date |
getSumPaymentsByClientMetric | Settlements Queries: Get Sum Payments by Client | Dashboard metric: total uncleared payment amounts grouped by client |
4.2 Procedures Used
| Operation | Foundation Doc | Trigger in This Workflow |
|---|---|---|
createPaymentItemsFromPayouts | Settlements Procedures: Create Payment Items from Payouts | Triggered automatically when a worksheet is approved (Settled → Approved); converts cash_receipt_payout records into payment_item records |
processPendingItems | Settlements Procedures: Process Pending Payment Items | Triggered when user clicks "Process" on the Payments page for selected PENDING items |
retryExecution | Settlements Procedures: Retry Failed Execution | Triggered when user clicks "Retry" on a FAILED execution in the Execution Details drawer |
pollPendingPayments | Settlements Procedures: Poll Payment Status | Triggered by a scheduled background job; polls SENT executions for bank confirmation |
updateDoNotSendFlag | TODO: Document in foundation/procedures/settlements.md | Triggered when user toggles the "Do Not Send" switch on a single payment item row |
bulkUpdateDoNotSendFlag | TODO: Document in foundation/procedures/settlements.md | Triggered when user clicks "Mark Do Not Send" or "Clear Do Not Send" bulk actions |
resetItemsPostedOnOrAfterDate | Settlements Queries: Reset Posted Payment Items | Used by administrators to roll back GL posting for a date range (admin/recovery operation) |
5. Key User Actions
5.1 Process Payment Items
Preconditions:
- One or more
payment_itemrecords exist withpayment_execution_status_cd = 'PENDING' - Selected items are not returned (
return_reason_cd IS NULLandreturned_dt IS NULL) - Selected items have not already been sent (
latestExecutionStatus != 'SENT') - Each selected item's payee bank account must have a
bank_idconfigured with a matchingPAYMENT_REQUEST_SCHEMAcode attribute
Procedure reference: Settlements Procedures: Process Pending Payment Items
Steps:
- User selects one or more payment item rows using checkboxes on the Payments list.
- User clicks the "Process" button (enabled only when at least one valid item is selected).
- All selected items are immediately locked to
payment_execution_status_cd = 'PROCESSING'. - For each item: payment data is gathered, the payment schema is determined, an execution record is created, the payload is built and saved, and the payload is transmitted to the bank API.
- Successful transmissions:
payment_item.payment_execution_status_cd = 'SENT', executionSENT,bank_reference_idstored. - Failed transmissions: execution
FAILED,payment_itemreverts toPENDING. - The page refreshes to show the updated statuses. A toast notification reports the count of successes and failures.
Postconditions:
- Successfully processed:
payment_item.payment_execution_status_cd = 'SENT',outbound_payment_execution.execution_status_cd = 'SENT',outbound_payment_execution.bank_reference_idpopulated. - Failed:
payment_item.payment_execution_status_cd = 'PENDING',outbound_payment_execution.execution_status_cd = 'FAILED',outbound_payment_execution.error_messagepopulated.
UI trigger: "Process" button in the page header. Visible always. Enabled when at least one item is selected and not already sent or returned.
5.2 Retry a Failed Execution
Preconditions:
- An
outbound_payment_executionrecord exists withexecution_status_cd = 'FAILED' - The associated
payment_item.payment_execution_status_cd = 'PENDING'(automatically reverted on failure) - The payee bank account configuration is still valid
Procedure reference: Settlements Procedures: Retry Failed Execution
Steps:
- User clicks on a row in the Payments list to open the Execution Details drawer.
- User sees the Processing Timeline listing all execution attempts, with the most recent failure shown first.
- User clicks the "Retry" button on the failed execution entry.
- A new execution record is created; the original failed execution is unchanged.
- The payment item goes through the full processing flow again (lock → gather → schema → payload → transmit).
- The drawer and page refresh with the new execution result.
Postconditions:
- A new
outbound_payment_executionrecord exists for the samepayment_item_id. - On success:
payment_item.payment_execution_status_cd = 'SENT', new executionexecution_status_cd = 'SENT'. - On failure: another
outbound_payment_executionwithexecution_status_cd = 'FAILED';payment_itemremainsPENDING.
UI trigger: "Retry" button inside an execution entry in the Processing Timeline section of the Execution Details drawer. Visible only when outbound_payment_execution.execution_status_cd = 'FAILED'.
5.3 Toggle Do Not Send (Single Item)
Preconditions:
- The payment item is not returned (
payment_item.return_reason_cd IS NULLandpayment_item.returned_dt IS NULL) - The payment item has not already been sent (
payment_item.payment_execution_status_cdnot inPROCESSING,SENT,ACKNOWLEDGED,PAID)
Procedure reference: TODO: Document in foundation/procedures/settlements.md
Steps:
- User locates the payment item row in the Payments list.
- User toggles the "Do Not Send" switch on that row.
- The UI optimistically updates the switch state.
payment_item.do_not_send_indis updated in the database.- If the update fails, the switch reverts to its previous state and a toast error is shown.
Postconditions:
payment_item.do_not_send_indreflects the new value.- The flag is consulted at processing time. Setting
do_not_send_ind = trueon aPENDINGitem does not immediately changepayment_execution_status_cd; however, the item will not be successfully processed until the flag is cleared.
UI trigger: Toggle switch in the "Do Not Send" column of the Payments list. Visible always. Disabled when isReturned = true or latestExecutionStatus = 'SENT'.
5.4 Bulk Toggle Do Not Send
Preconditions:
- One or more payment items are selected via checkboxes
- Selected items are not returned and not already sent
Procedure reference: TODO: Document in foundation/procedures/settlements.md
Steps:
- User selects one or more items using checkboxes.
- Two bulk action buttons appear in the page header: "Mark Do Not Send" and "Clear Do Not Send."
- User clicks the desired action.
- Items that are returned or already sent are automatically excluded from the update.
payment_item.do_not_send_indis updated for all valid selected items in a single bulk UPDATE.- Selection is cleared after the operation. A toast notification shows how many items were updated.
Postconditions:
payment_item.do_not_send_indis updated for all qualifying selected items.
UI trigger: "Mark Do Not Send" and "Clear Do Not Send" buttons in the page header. Visible only when selectedIds.size > 0.
5.5 View Execution Details and Payload
Preconditions:
- A
payment_itemrecord exists (any status)
Procedure reference: Settlements Queries: Get Executions by Payment Item ID
Steps:
- User clicks any row in the Payments list.
- An Execution Details drawer opens.
- The drawer loads all
outbound_payment_executionrecords for the selectedpayment_item_idviagetExecutionHistory. - The Processing Timeline section lists all executions in reverse chronological order.
- User can click "Request Payload" to view the full
outbound_payment_execution.generated_payload(JSON or XML). - User can click "Response" to view the
http_response_code,bank_reference_id, anderror_message. - A link to the linked cash worksheet appears if
cashReceiptWorksheetIdis present.
Postconditions:
- No data is changed. This is a read-only view.
UI trigger: Clicking any row in the Payments list opens the Execution Details drawer. Always enabled.
6. Permissions & Role-Based Access
| Action | CASH_MANAGER | CASH_PROCESSOR | SETTLEMENT_APPROVER | IT |
|---|---|---|---|---|
| View Payments list | — | — | Yes | Yes |
| Process payment items (transmit to bank) | — | — | Yes | Yes |
| Retry failed execution | — | — | Yes | Yes |
| Toggle Do Not Send (single item) | — | — | Yes | Yes |
| Bulk Mark / Clear Do Not Send | — | — | Yes | Yes |
| View execution details and payload | — | — | Yes | Yes |
| Override payment execution status (admin/test) | — | — | — | Yes |
Field-level restrictions:
- The
outbound_payment_execution.generated_payloadfield (the full bank payload) is displayed in the Execution Details drawer and is visible to any user who can access the Payments page. In production, this field may warrant additional access controls due to the sensitive banking data it contains. - The status override screen (
/actions/outbound-payments) is an IT-only tool that bypasses the bank adapter pipeline entirely. It exists for testing and must not be accessible to production users.
IMPORTANT
The status override screen under /actions/outbound-payments is a PoC testing tool that allows direct payment_item.payment_execution_status_cd overrides, bypassing the bank API entirely. This must not be shipped as a production feature.
7. Integration Points
7.1 Upstream
| Source | Data Provided | Mechanism |
|---|---|---|
| Worksheet Approval (Settlements Workflow) | cash_receipt_payout records converted into payment_item records at the T → A worksheet transition | Automatic call to createPaymentItemsFromPayouts during worksheet approval |
| Settlement Creation (Settlements Workflow) | participant_settlement_item records providing payee identity, bank account, commission amount, do_not_send_ind, and payment_date | FK lookup: cash_receipt_payout.participant_settlement_item_id → participant_settlement_item |
| Bank Account Configuration | bank_account.bank_id and PAYMENT_REQUEST_SCHEMA code attribute on the bank record, driving adapter selection | FK lookup at processing time via code_attribute |
| Party Bank Account Configuration | party_bank_account.preferred_payment_method (WIRE or ACH) determining service level in the bank payload | FK lookup at processing time |
7.2 Downstream
| Consumer | Data Consumed | Mechanism |
|---|---|---|
| GL Posting Batch | payment_item records with posting_status_cd = 'U' where bank has confirmed completion (payment_execution_status_cd = 'ACKNOWLEDGED' or 'PAID') | Batch job queries getUnpostedItemsUpToDate and updates posting_status_cd = 'P', posting_dt |
| Worksheet Return (Worksheets Workflow) | payment_item.payment_execution_status_cd to determine whether a settlement and its applications are locked (cannot be voided) or free (can be cancelled) | FK lookup: participant_settlement_item.payment_item_id → payment_item |
| Settlement Paid Status | When all payment_item records linked to a settlement reach PAID, the participant_settlement.participant_settlement_status_cd may transition to 'P' | Propagated by the status polling flow |
7.3 External Integrations
| System | Direction | Protocol | Notes |
|---|---|---|---|
| City National Bank (CNB) EASI Link | Outbound | JSON over HTTPS | CnbEasiAdapter. Selected when PAYMENT_REQUEST_SCHEMA = 'CNB_EASI_LINK'. Supports status polling via GET /StatusInquiry/{id}. Batch up to 500 payments. CNB status codes: 0=Processed, 5=Received, 10=Processed w/changes, 31=Scheduled, 50=Failed, 65=Denied. Service level: URGP (Wire), NURG (ACH). |
| J.P. Morgan Global Payments | Outbound | ISO 20022 pain.001.001.03 XML over HTTPS | JpmGlobalPayAdapter. Selected when PAYMENT_REQUEST_SCHEMA = 'JPM_GLOBAL_PAY'. Extends the base ISO 20022 adapter with JPM-specific headers and OFAC-compliance address requirements. |
| Bank of America CashPro | Outbound | ISO 20022 pain.001.001.03 XML over HTTPS | BofaCashProAdapter. Selected when PAYMENT_REQUEST_SCHEMA = 'BOFA_CASHPRO'. Extends the base ISO 20022 adapter with BofA CashPro extensions. |
| Generic ISO 20022 | Outbound | ISO 20022 pain.001.001.03 XML over HTTPS | Iso20022Adapter. Selected when PAYMENT_REQUEST_SCHEMA = 'ISO20022_PAIN001'. Base fallback for banks using the standard format. ACH SEC codes: CCD for organizations, PPD for individuals. |
NOTE
Adapter selection is driven by the PAYMENT_REQUEST_SCHEMA code attribute on the bank record (keyed by bank_account.bank_id), not by bank identity. Any bank can adopt any schema without changing code. The outbound_payment_execution.bank_profile_id column is a legacy field retained for backward compatibility — it is no longer used for adapter selection or routing.
8. Functional Screen Requirements
8.1 Payments List (/cash-management/payments)
Route: /cash-management/payments
Data loading:
getPaymentItemsWithOrigin— Settlements Queries: Get Payment Items with Origin Data: loads allpayment_itemrecords enriched with payee, bank, deal, origin classification, and latestoutbound_payment_executionsummary, ordered bypayment_item.created_dtdescending.
Header Region
Displays the page title, a live count of pending and sent payments, and bulk action controls.
| Field / Column | Source | Editable? | Condition |
|---|---|---|---|
| Pending count | Computed: count of items where !isReturned && latestExecutionStatus != 'SENT' && latestExecutionStatus != 'PROCESSING' | No | Always visible when data is loaded |
| Sent count | Computed: count of items where latestExecutionStatus = 'SENT' | No | Always visible when data is loaded |
| Refresh button | — | — | Always visible |
| Mark Do Not Send button | — | — | Visible only when selectedIds.size > 0 |
| Clear Do Not Send button | — | — | Visible only when selectedIds.size > 0 |
| Process button | — | — | Always visible; enabled when selectedIds.size > 0 and not currently processing |
Payments Grid
Tabular list of all payment items.
| Field / Column | Source | Editable? | Condition |
|---|---|---|---|
| Select checkbox | — | Yes | Disabled when isReturned = true or latestExecutionStatus = 'SENT' |
| Payment Date | payment_item.payment_date | No | Always visible |
| Origin | Computed originType: SETTLEMENT when payment_item_type_cd = 'S' and settlement linked; CLIENT_LEDGER when 'L'; REFUND when 'R'; OTHER otherwise | No | Always visible |
| Type | payment_item.payment_item_type_cd — displayed as: S = Settlement, L = Loan/Advance, R = Refund, P = Passthrough | No | Always visible |
| Description | payment_item.payment_item_name or payment_item.payment_item_comment (whichever is present first) | No | Always visible |
| Payment Party | party.display_name via payment_item.payment_party_id | No | Shows "Unknown" when null |
| Amount | payment_item.payment_item_amt formatted as currency with payment_item.payment_item_currency_cd | No | Always visible |
| Bank | code_master.codeMaster_desc for payment_item → bank_account.bank_id → code_master[BANK] | No | Shown as — when bank_id is null |
| Bank Account | bank_account.bank_account_name and masked bank_account.bank_account_no (last 4 digits) | No | Shows "No account" when payment_party_bank_id is null |
| Deal | deal.deal_name via payment_item.deal_id | No | Shown as — when deal_id is null |
| Client | party.display_name via payment_item.client_id | No | Shown as — when client_id is null |
| Do Not Send | payment_item.do_not_send_ind displayed as a toggle switch | Yes | Disabled when isReturned = true or latestExecutionStatus = 'SENT' |
| Status | outbound_payment_execution.execution_status_cd from latest execution, falling back to payment_item.payment_execution_status_cd; rendered as a status badge | No | "Returned" badge when isReturned = true overrides all other status values |
Grid features:
- Sortable columns: Payment Date, Origin, Type, Description, Payment Party, Amount, Bank, Bank Account, Deal, Client
- Filters: None — all items loaded on page entry
- Row selection: Multi-select via checkboxes. Select-all checkbox in header selects only non-returned, non-sent items. Shows indeterminate state when some but not all selectable items are checked.
- Pagination: None in PoC — all records loaded
Conditional display:
- Bulk action buttons ("Mark Do Not Send", "Clear Do Not Send") visible only when
selectedIds.size > 0 - "Process" button enabled only when
selectedIds.size > 0andisProcessing = false - Empty state card ("No payment items found") shown when the payments array is empty after loading
8.2 Execution Details Drawer
Route: Opens over /cash-management/payments when any payment row is clicked; no dedicated route
Data loading:
getExecutionHistory(paymentItemId)— Settlements Queries: Get Executions by Payment Item ID: loads alloutbound_payment_executionrecords for the selected payment item, ordered bycreated_dtdescending, after the drawer opens.
Header Region
Identifies the payment being inspected.
| Field / Column | Source | Editable? | Condition |
|---|---|---|---|
| Drawer title | Static: "Payment Details" | No | Always visible |
| Drawer subtitle | paymentPartyName — formatted paymentItemAmt / paymentItemCurrencyCd | No | Always visible |
Payment Summary Region
Shows the current state and key attributes of the selected payment item.
| Field / Column | Source | Editable? | Condition |
|---|---|---|---|
| Status badge | Derived from latestExecutionStatus or paymentExecutionStatusCd; overridden to "Returned" when isReturned = true | No | Always visible |
| Amount | payment_item.payment_item_amt + payment_item.payment_item_currency_cd | No | Always visible |
| Type | payment_item.payment_item_type_cd label | No | Always visible |
| Origin | originType label | No | Always visible |
| Payment Date | payment_item.payment_date | No | Always visible |
| Description | payment_item.payment_item_name or payment_item.payment_item_comment | No | Always visible |
| Return warning panel | payment_item.return_reason_cd and payment_item.returned_dt | No | Visible only when isReturned = true |
Processing Timeline Region
Full execution history for the payment item, rendered as a vertical list of execution cards.
| Field / Column | Source | Editable? | Condition |
|---|---|---|---|
| Execution number label | Computed: totalCount - index (most recent = highest number) | No | Per execution card |
| Execution status badge | outbound_payment_execution.execution_status_cd | No | Per execution card |
| Bank Profile | outbound_payment_execution.bank_profile_name | No | Per execution card |
| Format / Schema | outbound_payment_execution.payment_schema or combined payload_format + service_level | No | Per execution card |
| HTTP Code | outbound_payment_execution.http_response_code | No | Per execution card; shown in red for non-2xx codes |
| Bank Reference | outbound_payment_execution.bank_reference_id | No | Shown as — when null |
| Created timestamp | outbound_payment_execution.created_dt | No | Per execution card |
| Amount snapshot | outbound_payment_execution.payment_amount + payment_currency | No | Per execution card |
| Error message panel | outbound_payment_execution.error_message | No | Visible only when error_message is not null |
| Retry button | — | — | Visible only when execution_status_cd = 'FAILED'; disabled while retry in progress |
| Request Payload button | Opens modal displaying outbound_payment_execution.generated_payload formatted as JSON or XML | — | Always visible per execution card |
| Response button | Opens modal displaying http_response_code, bank_reference_id, and error_message or success confirmation | — | Always visible per execution card |
Grid features:
- Not a tabular grid — execution cards are rendered as a vertical list
- Sortable columns: N/A
- Filters: None
- Pagination: None — all executions shown
Conditional display:
- Empty state ("No processing history — This payment has not been sent to a bank yet") shown when
executions.length === 0 - Loading spinner shown while
getExecutionHistoryis in progress - Retry button on a card is visible only when
outbound_payment_execution.execution_status_cd = 'FAILED'
**PoC Artifact:** A "Test Mode" collapsible info panel exists in the Processing Timeline region showing "magic cent value" testing instructions (e.g., `.99` triggers a 400 bank rejection, `.50` triggers a 500 gateway timeout). This is a PoC testing aid and must be removed before production.
Payment Party Region
Shows the payee's identity and bank routing details.
| Field / Column | Source | Editable? | Condition |
|---|---|---|---|
| Name | party.display_name via payment_item.payment_party_id | No | Shows — when null |
| Bank | bank_account.bank_account_name via payment_item.payment_party_bank_id | No | Shows — when null |
| Account | Masked bank_account.bank_account_no (last 4 digits) | No | Shown only when bank_account_no is present |
| Routing | bank_account.bank_account_routing_no | No | Shown only when routing number is present |
Reference Data Region
Business context attributes for the payment.
| Field / Column | Source | Editable? | Condition |
|---|---|---|---|
| Client | party.display_name via payment_item.client_id | No | Visible only when client_id is not null |
| Loanout | party.display_name via payment_item.contracted_party_id | No | Visible only when contracted_party_id is not null |
| Deal | deal.deal_name via payment_item.deal_id | No | Visible only when deal_id is not null |
| Buyer | party.display_name via payment_item.buyer_id | No | Visible only when buyer_id is not null |
| Settlement ID | participant_settlement.participant_settlement_id | No | Visible only when settlementId is not null |
| Comment | payment_item.payment_item_comment | No | Visible only when comment is not null |
| View Worksheet link | cash_receipt_payout.cash_receipt_worksheet_id rendered as a navigation link to /worksheets/{id} | No | Visible only when cashReceiptWorksheetId is not null |
9. Additional Diagrams
Payment Item Execution Status State Machine
stateDiagram-v2
[*] --> WAITING : Future date or do_not_send_ind=true
[*] --> PENDING : Ready immediately
WAITING --> PENDING : Date arrives or hold released
PENDING --> PROCESSING : Processor locks item
PROCESSING --> SENT : Bank API call succeeds (LOCKED)
PROCESSING --> PENDING : Bank API call fails (reverts for retry)
SENT --> ACKNOWLEDGED : Bank confirms via poll (LOCKED)
SENT --> FAILED : Bank reports failure via poll
ACKNOWLEDGED --> PAID : Bank confirms funds transferred (LOCKED)
FAILED --> PENDING : User retries
PENDING --> CANCELLED : Worksheet return voids paymentOutbound Payment Execution Status State Machine
stateDiagram-v2
[*] --> CREATED : Execution record initialized
CREATED --> SENT : Bank API call succeeds
CREATED --> FAILED : Bank API call fails
SENT --> ACKNOWLEDGED : Status poll returns COMPLETED
SENT --> FAILED : Status poll returns FAILED or REVERSEDNOTE
FAILED on outbound_payment_execution is terminal — a retry creates a new execution record and the failed record is preserved unchanged. In contrast, after a transmission failure, payment_item.payment_execution_status_cd reverts to PENDING (not FAILED) to allow immediate retry. The FAILED status on payment_item is set only by status polling after the bank rejects a previously-accepted payment.
Adapter Selection Logic
flowchart LR
A["payment_item.payment_party_bank_id"] --> B["bank_account.bank_id"]
B --> C["code_attribute lookup\nattribute = PAYMENT_REQUEST_SCHEMA"]
C --> D{schema value}
D -->|CNB_EASI_LINK| E["CnbEasiAdapter\nJSON payload"]
D -->|ISO20022_PAIN001| F["Iso20022Adapter\npain.001.001.03 XML"]
D -->|BOFA_CASHPRO| G["BofaCashProAdapter\nISO 20022 + BofA extensions"]
D -->|JPM_GLOBAL_PAY| H["JpmGlobalPayAdapter\nISO 20022 + JPM extensions"]GL Posting Status Lifecycle
stateDiagram-v2
[*] --> U : Payment item created (Unposted)
U --> P : GL batch posts after bank confirmation (ACKNOWLEDGED or PAID)
U --> X : Payment cancelled before being sent — Skipped, no GL entry needed10. Cross-References
| Document | Relationship |
|---|---|
| Settlements Data Model | Defines payment_item, outbound_payment_execution, participant_settlement, and participant_settlement_item tables used throughout this workflow; status lifecycle diagrams for all five status machines |
| Settlements Queries | Specifies all data retrieval operations: getPaymentItemsWithOrigin, getExecutionsByPaymentItemId, gatherPaymentData, getUnpostedItemsUpToDate, and others used by this workflow |
| Settlements Procedures | Specifies all mutation operations: createPaymentItemsFromPayouts, processPendingItems, retryExecution, and status polling procedures |
| Settlements Workflow | Upstream: settlement creation and worksheet approval that generates payment_item records. Settlements divide PAY; this workflow sends the resulting payment items to banks |
| Worksheets Workflow | Upstream: worksheet approval (T → A) triggers createPaymentItemsFromPayouts. Worksheet return voids unlocked payment items and seals locked ones as historical context |
11. Gherkin Scenarios
Feature: Payments - Outbound Payment Processing
Scenario: Successfully process a pending payment item via CNB EASI Link
Given a worksheet has been approved for client "Taylor Marks"
And a payment_item exists with payment_item_id = 1042
And payment_item.payment_execution_status_cd = 'PENDING'
And payment_item.payment_item_amt = '8500.00' and payment_item.payment_item_currency_cd = 'USD'
And payment_item.payment_party_bank_id links to a bank_account with bank_id = 'CNB'
And the CNB bank has code_attribute PAYMENT_REQUEST_SCHEMA = 'CNB_EASI_LINK'
And party_bank_account.preferred_payment_method = 'WIRE'
When the user selects payment_item 1042 and clicks "Process"
Then payment_item.payment_execution_status_cd is set to 'PROCESSING'
And an outbound_payment_execution record is created with execution_status_cd = 'CREATED'
And outbound_payment_execution.generated_payload is saved as valid CNB EASI Link JSON with serviceLevel = 'URGP'
And the HTTP call to the bank API returns 200 with a bank_reference_id
And outbound_payment_execution.execution_status_cd is updated to 'SENT'
And outbound_payment_execution.bank_reference_id is populated
And payment_item.payment_execution_status_cd is updated to 'SENT'
And the Payments list shows a "Sent" status badge for payment_item 1042
Scenario: Process fails due to bank rejection and payment reverts to PENDING
Given a payment_item exists with payment_item_id = 2033
And payment_item.payment_execution_status_cd = 'PENDING'
And the bank API returns HTTP 400 for this payment
When the user selects payment_item 2033 and clicks "Process"
Then payment_item.payment_execution_status_cd is set to 'PROCESSING' initially
And an outbound_payment_execution record is created with execution_status_cd = 'CREATED'
And outbound_payment_execution.generated_payload is saved before the API call
And the bank API returns HTTP 400
And outbound_payment_execution.execution_status_cd is updated to 'FAILED'
And outbound_payment_execution.error_message contains the bank rejection reason
And payment_item.payment_execution_status_cd reverts to 'PENDING'
And the Payments list shows a "Pending" status badge for payment_item 2033
And the user can select and process payment_item 2033 again
Scenario: Retry a failed execution creates a new execution record
Given payment_item 3017 has payment_execution_status_cd = 'PENDING'
And an outbound_payment_execution exists with execution_status_cd = 'FAILED' for payment_item 3017
And the bank API will return HTTP 200 on the next attempt
When the user opens the Execution Details drawer for payment_item 3017
And the user clicks "Retry" on the failed execution entry
Then a new outbound_payment_execution record is created for payment_item_id = 3017
And the original FAILED execution record is unchanged
And the new execution transitions execution_status_cd: CREATED → SENT
And payment_item.payment_execution_status_cd is updated to 'SENT'
And the Processing Timeline in the drawer shows two execution entries
Scenario: Future-dated payment starts in WAITING and cannot be selected for processing
Given a worksheet is approved on 2026-03-02
And a cash_receipt_payout has payment_date = '2026-04-01' and do_not_send_ind = false
When createPaymentItemsFromPayouts is called for the worksheet
Then a payment_item is created with payment_execution_status_cd = 'WAITING'
And payment_item.do_not_send_ind = false
And on the Payments list the checkbox for this item is disabled
And this item is excluded from the pending count in the page header
Scenario: Clearing do_not_send on a held payment makes it eligible for processing
Given payment_item 4008 has do_not_send_ind = true
And payment_item.payment_execution_status_cd = 'WAITING'
When the user toggles the "Do Not Send" switch to off for payment_item 4008
Then payment_item.do_not_send_ind is set to false
And the item checkbox becomes enabled on the Payments list
Scenario: Locked payment item cannot be selected or modified
Given payment_item 5099 has payment_execution_status_cd = 'SENT'
When the Payments list loads
Then the checkbox for payment_item 5099 is disabled
And the "Do Not Send" toggle for payment_item 5099 is disabled
And payment_item 5099 is excluded when the user clicks "Select All"
Scenario: Bulk mark payments as Do Not Send
Given three payment_items exist with payment_item_ids 6001, 6002, 6003
And all have payment_execution_status_cd = 'PENDING' and do_not_send_ind = false
When the user selects all three and clicks "Mark Do Not Send"
Then payment_item.do_not_send_ind = true for payment_item_ids 6001, 6002, and 6003
And a toast notification shows "3 payment(s) updated"
And the selection is cleared
Scenario: Bank confirmation via status polling advances payment to PAID
Given payment_item 7055 has payment_execution_status_cd = 'SENT'
And outbound_payment_execution exists with execution_status_cd = 'SENT' for payment_item 7055
And outbound_payment_execution.bank_reference_id = 'REF-7055-CNB'
And outbound_payment_execution.payment_schema = 'CNB_EASI_LINK'
When the PaymentStatusPollerService polls the CNB bank for reference REF-7055-CNB
And the bank returns CNB status code 0 (Processed)
Then outbound_payment_execution.execution_status_cd is updated to 'ACKNOWLEDGED'
And outbound_payment_execution.status_history records this poll result
And payment_item.payment_execution_status_cd is updated to 'PAID'
Scenario: Cancelled payment receives Skipped GL posting status
Given payment_item 8012 has payment_execution_status_cd = 'PENDING'
And payment_item.posting_status_cd = 'U'
When a worksheet return is initiated and payment_item 8012 is voided
Then payment_item.payment_execution_status_cd is set to 'CANCELLED'
And payment_item.return_reason_cd = 'WORKSHEET_RETURN'
And payment_item.posting_status_cd is set to 'X'
And the GL posting batch does not include payment_item 8012
Scenario: GL posting occurs only after bank confirmation
Given payment_item 9025 has payment_execution_status_cd = 'PAID'
And payment_item.posting_status_cd = 'U'
When the GL posting batch runs with a cutoff_date on or after payment_item.created_dt
Then payment_item.posting_status_cd is set to 'P'
And payment_item.posting_dt is populated with the current date
And payment_item 9025 is not returned by the next run of getUnpostedItemsUpToDate
Scenario: Missing bank_id on payee account causes processing to fail gracefully
Given payment_item 1099 has payment_execution_status_cd = 'PENDING'
And payment_item.payment_party_bank_id links to a bank_account with bank_id = NULL
When the user selects payment_item 1099 and clicks "Process"
Then payment_item.payment_execution_status_cd is set to 'PROCESSING' initially
And processing fails with error "does not have a valid bank_id on the payee's bank account"
And payment_item.payment_execution_status_cd reverts to 'PENDING'
And a partial-success toast shows "0 succeeded, 1 failed"