Skip to content

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_item records 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):

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: SENT locks the payment; GL posting occurs only after bank confirmation (ACKNOWLEDGED or PAID).

2. Process Overview

mermaid
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

  1. Payment item creation — When a worksheet transitions to Approved, createPaymentItemsFromPayouts converts each cash_receipt_payout record into a payment_item. Initial payment_execution_status_cd is WAITING if the payment date is future-dated or do_not_send_ind = true; otherwise PENDING. See Settlements Procedures: Create Payment Items from Payouts.

  2. Hold management (WAITING → PENDING) — Users can manually release a hold by clearing do_not_send_ind, or the item transitions automatically when its payment_date arrives. Future-dated payments remain in WAITING until their date.

  3. 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).

  4. Selection and processing — The user selects one or more PENDING items using checkboxes and clicks "Process." Items that are returned or already SENT cannot be selected.

  5. Locking — The processor immediately sets all selected items to PROCESSING in a bulk update, preventing concurrent processing.

  6. Schema determination — For each item, the processor reads the bank_id from the payee's bank account and looks up the PAYMENT_REQUEST_SCHEMA code attribute. This determines which bank adapter to use: CNB_EASI_LINK, ISO20022_PAIN001, BOFA_CASHPRO, or JPM_GLOBAL_PAY.

  7. Execution record creation — An outbound_payment_execution record is created with execution_status_cd = 'CREATED', capturing the adapter schema, service level (WIRE or ACH), amount, and currency.

  8. 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_payload before transmission — ensuring it is preserved even if the bank call fails.

  9. 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 the bank_reference_id is stored. The payment is now locked. On failure: execution_status_cd = 'FAILED', payment item reverts to PENDING for retry.

  10. Status polling — For adapters that support polling (CNB EASI Link), the PaymentStatusPollerService periodically polls the bank API using the stored bank_reference_id. When the bank confirms completion, payment_item.payment_execution_status_cd advances to PAID and the execution record to ACKNOWLEDGED.

  11. 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 sets posting_status_cd = 'P' and posting_dt. Payments that were cancelled before being sent receive posting_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

OperationFoundation DocPurpose in This Workflow
getPaymentItemsWithOriginSettlements Queries: Get Payment Items with Origin DataLoads all payment items for the Payments list page, enriched with payee name, deal name, bank details, origin classification, and latest execution summary
getPaymentItemWithOriginByIdSettlements Queries: Get Single Payment Item with Origin DataLoads a single payment item for the Execution Details drawer
getExecutionsByPaymentItemIdSettlements Queries: Get Executions by Payment Item IDLoads the full execution history for a payment item shown in the drawer's Processing Timeline
getFailedExecutionsSettlements Queries: Get Failed ExecutionsRetrieves all failed executions eligible for retry (used by the status poller and admin views)
gatherPaymentDataSettlements Queries: Gather Payment Data for Bank TransmissionCollects the source account, beneficiary bank details, service level, and bank_id needed to build the bank payload
getUnpostedItemsUpToDateSettlements Queries: Get Unposted Payment Items Up to DateFeeds the GL posting batch — retrieves all posting_status_cd = 'U' items up to a cutoff date
getSumPaymentsByClientMetricSettlements Queries: Get Sum Payments by ClientDashboard metric: total uncleared payment amounts grouped by client

4.2 Procedures Used

OperationFoundation DocTrigger in This Workflow
createPaymentItemsFromPayoutsSettlements Procedures: Create Payment Items from PayoutsTriggered automatically when a worksheet is approved (Settled → Approved); converts cash_receipt_payout records into payment_item records
processPendingItemsSettlements Procedures: Process Pending Payment ItemsTriggered when user clicks "Process" on the Payments page for selected PENDING items
retryExecutionSettlements Procedures: Retry Failed ExecutionTriggered when user clicks "Retry" on a FAILED execution in the Execution Details drawer
pollPendingPaymentsSettlements Procedures: Poll Payment StatusTriggered by a scheduled background job; polls SENT executions for bank confirmation
updateDoNotSendFlagTODO: Document in foundation/procedures/settlements.mdTriggered when user toggles the "Do Not Send" switch on a single payment item row
bulkUpdateDoNotSendFlagTODO: Document in foundation/procedures/settlements.mdTriggered when user clicks "Mark Do Not Send" or "Clear Do Not Send" bulk actions
resetItemsPostedOnOrAfterDateSettlements Queries: Reset Posted Payment ItemsUsed 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_item records exist with payment_execution_status_cd = 'PENDING'
  • Selected items are not returned (return_reason_cd IS NULL and returned_dt IS NULL)
  • Selected items have not already been sent (latestExecutionStatus != 'SENT')
  • Each selected item's payee bank account must have a bank_id configured with a matching PAYMENT_REQUEST_SCHEMA code attribute

Procedure reference: Settlements Procedures: Process Pending Payment Items

Steps:

  1. User selects one or more payment item rows using checkboxes on the Payments list.
  2. User clicks the "Process" button (enabled only when at least one valid item is selected).
  3. All selected items are immediately locked to payment_execution_status_cd = 'PROCESSING'.
  4. 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.
  5. Successful transmissions: payment_item.payment_execution_status_cd = 'SENT', execution SENT, bank_reference_id stored.
  6. Failed transmissions: execution FAILED, payment_item reverts to PENDING.
  7. 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_id populated.
  • Failed: payment_item.payment_execution_status_cd = 'PENDING', outbound_payment_execution.execution_status_cd = 'FAILED', outbound_payment_execution.error_message populated.

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_execution record exists with execution_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:

  1. User clicks on a row in the Payments list to open the Execution Details drawer.
  2. User sees the Processing Timeline listing all execution attempts, with the most recent failure shown first.
  3. User clicks the "Retry" button on the failed execution entry.
  4. A new execution record is created; the original failed execution is unchanged.
  5. The payment item goes through the full processing flow again (lock → gather → schema → payload → transmit).
  6. The drawer and page refresh with the new execution result.

Postconditions:

  • A new outbound_payment_execution record exists for the same payment_item_id.
  • On success: payment_item.payment_execution_status_cd = 'SENT', new execution execution_status_cd = 'SENT'.
  • On failure: another outbound_payment_execution with execution_status_cd = 'FAILED'; payment_item remains PENDING.

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 NULL and payment_item.returned_dt IS NULL)
  • The payment item has not already been sent (payment_item.payment_execution_status_cd not in PROCESSING, SENT, ACKNOWLEDGED, PAID)

Procedure reference: TODO: Document in foundation/procedures/settlements.md

Steps:

  1. User locates the payment item row in the Payments list.
  2. User toggles the "Do Not Send" switch on that row.
  3. The UI optimistically updates the switch state.
  4. payment_item.do_not_send_ind is updated in the database.
  5. If the update fails, the switch reverts to its previous state and a toast error is shown.

Postconditions:

  • payment_item.do_not_send_ind reflects the new value.
  • The flag is consulted at processing time. Setting do_not_send_ind = true on a PENDING item does not immediately change payment_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:

  1. User selects one or more items using checkboxes.
  2. Two bulk action buttons appear in the page header: "Mark Do Not Send" and "Clear Do Not Send."
  3. User clicks the desired action.
  4. Items that are returned or already sent are automatically excluded from the update.
  5. payment_item.do_not_send_ind is updated for all valid selected items in a single bulk UPDATE.
  6. Selection is cleared after the operation. A toast notification shows how many items were updated.

Postconditions:

  • payment_item.do_not_send_ind is 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_item record exists (any status)

Procedure reference: Settlements Queries: Get Executions by Payment Item ID

Steps:

  1. User clicks any row in the Payments list.
  2. An Execution Details drawer opens.
  3. The drawer loads all outbound_payment_execution records for the selected payment_item_id via getExecutionHistory.
  4. The Processing Timeline section lists all executions in reverse chronological order.
  5. User can click "Request Payload" to view the full outbound_payment_execution.generated_payload (JSON or XML).
  6. User can click "Response" to view the http_response_code, bank_reference_id, and error_message.
  7. A link to the linked cash worksheet appears if cashReceiptWorksheetId is 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

ActionCASH_MANAGERCASH_PROCESSORSETTLEMENT_APPROVERIT
View Payments listYesYes
Process payment items (transmit to bank)YesYes
Retry failed executionYesYes
Toggle Do Not Send (single item)YesYes
Bulk Mark / Clear Do Not SendYesYes
View execution details and payloadYesYes
Override payment execution status (admin/test)Yes

Field-level restrictions:

  • The outbound_payment_execution.generated_payload field (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

SourceData ProvidedMechanism
Worksheet Approval (Settlements Workflow)cash_receipt_payout records converted into payment_item records at the T → A worksheet transitionAutomatic 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_dateFK lookup: cash_receipt_payout.participant_settlement_item_id → participant_settlement_item
Bank Account Configurationbank_account.bank_id and PAYMENT_REQUEST_SCHEMA code attribute on the bank record, driving adapter selectionFK lookup at processing time via code_attribute
Party Bank Account Configurationparty_bank_account.preferred_payment_method (WIRE or ACH) determining service level in the bank payloadFK lookup at processing time

7.2 Downstream

ConsumerData ConsumedMechanism
GL Posting Batchpayment_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 StatusWhen 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

SystemDirectionProtocolNotes
City National Bank (CNB) EASI LinkOutboundJSON over HTTPSCnbEasiAdapter. 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 PaymentsOutboundISO 20022 pain.001.001.03 XML over HTTPSJpmGlobalPayAdapter. 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 CashProOutboundISO 20022 pain.001.001.03 XML over HTTPSBofaCashProAdapter. Selected when PAYMENT_REQUEST_SCHEMA = 'BOFA_CASHPRO'. Extends the base ISO 20022 adapter with BofA CashPro extensions.
Generic ISO 20022OutboundISO 20022 pain.001.001.03 XML over HTTPSIso20022Adapter. 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:

Header Region

Displays the page title, a live count of pending and sent payments, and bulk action controls.

Field / ColumnSourceEditable?Condition
Pending countComputed: count of items where !isReturned && latestExecutionStatus != 'SENT' && latestExecutionStatus != 'PROCESSING'NoAlways visible when data is loaded
Sent countComputed: count of items where latestExecutionStatus = 'SENT'NoAlways visible when data is loaded
Refresh buttonAlways visible
Mark Do Not Send buttonVisible only when selectedIds.size > 0
Clear Do Not Send buttonVisible only when selectedIds.size > 0
Process buttonAlways visible; enabled when selectedIds.size > 0 and not currently processing

Payments Grid

Tabular list of all payment items.

Field / ColumnSourceEditable?Condition
Select checkboxYesDisabled when isReturned = true or latestExecutionStatus = 'SENT'
Payment Datepayment_item.payment_dateNoAlways visible
OriginComputed originType: SETTLEMENT when payment_item_type_cd = 'S' and settlement linked; CLIENT_LEDGER when 'L'; REFUND when 'R'; OTHER otherwiseNoAlways visible
Typepayment_item.payment_item_type_cd — displayed as: S = Settlement, L = Loan/Advance, R = Refund, P = PassthroughNoAlways visible
Descriptionpayment_item.payment_item_name or payment_item.payment_item_comment (whichever is present first)NoAlways visible
Payment Partyparty.display_name via payment_item.payment_party_idNoShows "Unknown" when null
Amountpayment_item.payment_item_amt formatted as currency with payment_item.payment_item_currency_cdNoAlways visible
Bankcode_master.codeMaster_desc for payment_item → bank_account.bank_id → code_master[BANK]NoShown as when bank_id is null
Bank Accountbank_account.bank_account_name and masked bank_account.bank_account_no (last 4 digits)NoShows "No account" when payment_party_bank_id is null
Dealdeal.deal_name via payment_item.deal_idNoShown as when deal_id is null
Clientparty.display_name via payment_item.client_idNoShown as when client_id is null
Do Not Sendpayment_item.do_not_send_ind displayed as a toggle switchYesDisabled when isReturned = true or latestExecutionStatus = 'SENT'
Statusoutbound_payment_execution.execution_status_cd from latest execution, falling back to payment_item.payment_execution_status_cd; rendered as a status badgeNo"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 > 0 and isProcessing = 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:

Header Region

Identifies the payment being inspected.

Field / ColumnSourceEditable?Condition
Drawer titleStatic: "Payment Details"NoAlways visible
Drawer subtitlepaymentPartyName — formatted paymentItemAmt / paymentItemCurrencyCdNoAlways visible

Payment Summary Region

Shows the current state and key attributes of the selected payment item.

Field / ColumnSourceEditable?Condition
Status badgeDerived from latestExecutionStatus or paymentExecutionStatusCd; overridden to "Returned" when isReturned = trueNoAlways visible
Amountpayment_item.payment_item_amt + payment_item.payment_item_currency_cdNoAlways visible
Typepayment_item.payment_item_type_cd labelNoAlways visible
OriginoriginType labelNoAlways visible
Payment Datepayment_item.payment_dateNoAlways visible
Descriptionpayment_item.payment_item_name or payment_item.payment_item_commentNoAlways visible
Return warning panelpayment_item.return_reason_cd and payment_item.returned_dtNoVisible only when isReturned = true

Processing Timeline Region

Full execution history for the payment item, rendered as a vertical list of execution cards.

Field / ColumnSourceEditable?Condition
Execution number labelComputed: totalCount - index (most recent = highest number)NoPer execution card
Execution status badgeoutbound_payment_execution.execution_status_cdNoPer execution card
Bank Profileoutbound_payment_execution.bank_profile_nameNoPer execution card
Format / Schemaoutbound_payment_execution.payment_schema or combined payload_format + service_levelNoPer execution card
HTTP Codeoutbound_payment_execution.http_response_codeNoPer execution card; shown in red for non-2xx codes
Bank Referenceoutbound_payment_execution.bank_reference_idNoShown as when null
Created timestampoutbound_payment_execution.created_dtNoPer execution card
Amount snapshotoutbound_payment_execution.payment_amount + payment_currencyNoPer execution card
Error message paneloutbound_payment_execution.error_messageNoVisible only when error_message is not null
Retry buttonVisible only when execution_status_cd = 'FAILED'; disabled while retry in progress
Request Payload buttonOpens modal displaying outbound_payment_execution.generated_payload formatted as JSON or XMLAlways visible per execution card
Response buttonOpens modal displaying http_response_code, bank_reference_id, and error_message or success confirmationAlways 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 getExecutionHistory is 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 / ColumnSourceEditable?Condition
Nameparty.display_name via payment_item.payment_party_idNoShows when null
Bankbank_account.bank_account_name via payment_item.payment_party_bank_idNoShows when null
AccountMasked bank_account.bank_account_no (last 4 digits)NoShown only when bank_account_no is present
Routingbank_account.bank_account_routing_noNoShown only when routing number is present

Reference Data Region

Business context attributes for the payment.

Field / ColumnSourceEditable?Condition
Clientparty.display_name via payment_item.client_idNoVisible only when client_id is not null
Loanoutparty.display_name via payment_item.contracted_party_idNoVisible only when contracted_party_id is not null
Dealdeal.deal_name via payment_item.deal_idNoVisible only when deal_id is not null
Buyerparty.display_name via payment_item.buyer_idNoVisible only when buyer_id is not null
Settlement IDparticipant_settlement.participant_settlement_idNoVisible only when settlementId is not null
Commentpayment_item.payment_item_commentNoVisible only when comment is not null
View Worksheet linkcash_receipt_payout.cash_receipt_worksheet_id rendered as a navigation link to /worksheets/{id}NoVisible only when cashReceiptWorksheetId is not null

9. Additional Diagrams

Payment Item Execution Status State Machine

mermaid
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 payment

Outbound Payment Execution Status State Machine

mermaid
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 REVERSED

NOTE

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

mermaid
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

mermaid
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 needed

10. Cross-References

DocumentRelationship
Settlements Data ModelDefines 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 QueriesSpecifies all data retrieval operations: getPaymentItemsWithOrigin, getExecutionsByPaymentItemId, gatherPaymentData, getUnpostedItemsUpToDate, and others used by this workflow
Settlements ProceduresSpecifies all mutation operations: createPaymentItemsFromPayouts, processPendingItems, retryExecution, and status polling procedures
Settlements WorkflowUpstream: settlement creation and worksheet approval that generates payment_item records. Settlements divide PAY; this workflow sends the resulting payment items to banks
Worksheets WorkflowUpstream: worksheet approval (T → A) triggers createPaymentItemsFromPayouts. Worksheet return voids unlocked payment items and seals locked ones as historical context

11. Gherkin Scenarios

gherkin
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"

Confidential. For internal use only.