Settlements Procedures
1. Executive Summary
This document describes every data-mutation procedure in the settlements and payments domain. Each procedure is presented as a step-by-step sequence of database operations at the table and field level, independent of any specific technology stack.
The procedures fall into six functional groups:
- Settlement Lifecycle — Creating settlements from PAY applications, populating deal-party defaults, saving/updating/deleting settlement headers and items.
- Payout Creation — Generating
cash_receipt_payoutrecords from settlement items, bridging settlements to the payment layer. - Payment Item Creation — Converting payout records into
payment_itemrecords at worksheet approval, with initial status determination and back-linking. - Outbound Payment Execution — Transmitting payment items to banks through the adapter pattern, recording execution attempts, and handling success/failure.
- Payment Status Polling — Querying bank APIs for status updates on sent payments and propagating terminal statuses back to
payment_item. - Void & Cancellation — Voiding unlocked payment items during settlement edits, settlement deletion, and worksheet returns.
Data model reference: Settlements Data Model
Queries reference: Settlements Queries
Tables mutated: participant_settlement, participant_settlement_item, payment_item, outbound_payment_execution, cash_receipt_application, cash_receipt_payout
2. Key Procedures
2.1 Settlement Lifecycle
2.1.1 Compute Settlement Defaults
Operation: computeSettlementDefaults
Trigger: User selects one or more REV receivable rows on an Applied worksheet and clicks "Create Settlement," causing the system to pre-populate the settlement form.
Input Parameters:
revBillingItemDetailIds:int[](required) — IDs of the selected REVbilling_item_detailrecordsapplicationIds:int[](optional) —cash_receipt_applicationIDs scoped to the current worksheet; when provided, restricts amount aggregations to this worksheet only
Calculates the pre-populated values shown when a user starts creating a new settlement from selected receivables. This procedure is read-only — it does not mutate any tables.
Step 1. Resolve Source Details
- Source:
billing_item_detailjoined tobilling_item,revenue_item, anddealfor all rows matchingrevBillingItemDetailIds. - Action: SELECT (read-only).
- Logic:
- Validate that all rows share the same
revenue_item_id. If any row differs, reject with error: "All selected receivables must belong to the same Revenue Item." - Extract
deal_id,currency_cd,deal_name, andrevenue_item_namefrom the first row. - Collect
billing_item_idvalues assource_billing_item_ids.
- Validate that all rows share the same
Step 2. Find Sibling PAY Details
- Source:
billing_item_detailfiltered bybilling_item_id IN source_billing_item_idsandbilling_item_detail_type_cd = 'PAY'. - Action: SELECT (read-only).
- Logic:
- Sum
billing_item_detail_total_amtacross all matching rows to producetotal_pay_amt. - Collect the resulting
billing_item_detail_idvalues aspay_detail_ids.
- Sum
Step 3. Sum REV Gross
- Source:
billing_item_detailfiltered bybilling_item_detail_id IN revBillingItemDetailIds. - Action: SELECT (read-only).
- Logic:
- Sum
billing_item_detail_gross_amtto producetotal_rev_amt.
- Sum
Step 4. Sum REV Applied
- Source:
cash_receipt_applicationfiltered bybilling_item_detail_id IN revBillingItemDetailIds. - Action: SELECT (read-only).
- Logic:
- When
applicationIdsis provided, additionally filter bycash_receipt_application_id IN applicationIds. - Sum
cash_receipt_amt_appliedto producetotal_rev_applied.
- When
Step 5. Sum PAY Applied
- Source:
cash_receipt_applicationfiltered bybilling_item_detail_id IN pay_detail_ids. - Action: SELECT (read-only).
- Logic:
- When
applicationIdsis provided, additionally filter bycash_receipt_application_id IN applicationIds. - Sum
cash_receipt_amt_appliedto producetotal_pay_applied.
- When
Step 6. Sum Deductions Split by REV and PAY
- Source:
cash_receipt_application_deductionjoined tocash_receipt_application. - Action: SELECT (read-only).
- Logic:
- Identify REV application IDs:
cash_receipt_applicationwherebilling_item_detail_id IN revBillingItemDetailIds(and scoped byapplicationIdsif provided). - Identify PAY application IDs:
cash_receipt_applicationwherebilling_item_detail_id IN pay_detail_ids(and scoped byapplicationIdsif provided). - Sum
deduction_amt_appliedfor each set separately, producingrev_deduction_appliedandpay_deduction_applied.
- Identify REV application IDs:
Step 7. Load Deal Parties
- Source:
deal_partyjoined toparty,party_bank_account(whereactive_ind = true), andbank_accountfor the resolveddeal_id. - Action: SELECT (read-only).
- Logic:
- For each deal party, return:
party_id,display_name,party_role_type_cd,deal_party_commission_flat_ind,deal_party_commission_perc,deal_party_commission_amt,bank_account_id, and a display-friendly bank account name.
- For each deal party, return:
Side-effects:
- None. This procedure is read-only.
Postconditions:
- Caller receives an aggregated defaults object containing deal name, revenue item name,
total_pay_amt,total_rev_amt,total_rev_applied,total_pay_applied,rev_deduction_applied,pay_deduction_applied,pay_detail_ids, currency, and the deal party list with commission defaults and bank accounts.
2.1.2 Create Settlement
Operation: createSettlement
Trigger: User completes the settlement form and clicks Save, after selecting one or more PAY receivable applications on an Applied worksheet.
Input Parameters:
header: Object —participant_settlement_overrided_ind,participant_settlement_commentitems: Object[] — one per payee, each containingpayment_party_id,payment_party_bank_id,participant_settlement_commission_flat_ind,participant_settlment_commission_perc,participant_settlement_commission_amt,calc_level_cd,participant_settlement_item_comment,payment_date,do_not_send_indapplicationIds:int[]— thecash_receipt_applicationrecords being settledactorId: String — identifier for the user performing the operation
Creates a new participant_settlement header and one or more participant_settlement_item rows, then links the relevant cash_receipt_application records, and auto-creates payout records.
**PoC Artifact:** The PoC performs the settlement-total validation at the server-action layer before calling the service. Production should enforce this validation in the service or at the database level.
Step 1. Validate Settlement Total
- Source:
cash_receipt_applicationjoined tobilling_item_detailforapplicationIdswherebilling_item_detail_type_cd = 'PAY'. - Action: SELECT (validation, read-only).
- Logic:
- Sum
cash_receipt_amt_appliedacross matching rows to producepay_applied_amt. - Sum
participant_settlement_commission_amtacross all input items to produceinput_total. - Assert
|input_total - pay_applied_amt| <= 0.01. If not satisfied, reject with error.
- Sum
Step 2. Insert Settlement Header
- Source: Input header fields.
- Action: INSERT into
participant_settlement. - Logic:
- Set
participant_settlement_status_cd='D'(Draft). - Set
participant_settlement_overrided_ind= from input. - Set
participant_settlement_comment= from input. - Set
created_by=actorId,created_dt= current timestamp. - Set
updated_by=actorId,updated_dt= current timestamp. - Capture the returned
participant_settlement_id.
- Set
Step 3. Insert Settlement Items
- Source: Input items array.
- Action: INSERT into
participant_settlement_itemfor each item whereparticipant_settlement_commission_amt != 0. - Logic:
- Set
participant_settlement_id= from Step 2. - Set
payment_party_id= payee party ID from input. - Set
payment_party_bank_id= payee bank account ID from input. - Set
participant_settlement_commission_flat_ind= from input (true= flat,false= percentage). - Set
participant_settlment_commission_perc= from input (decimal(7,4)). - Set
participant_settlement_commission_amt= from input (decimal(15,2)). - Set
calc_level_cd= from input (default'DNI'if not specified). - Set
participant_settlement_item_comment= from input. - Set
payment_date= from input. - Set
do_not_send_ind= from input (defaultfalse). - Set
created_by/updated_by=actorId, timestamps = current. - Items with
participant_settlement_commission_amt = 0are skipped entirely.
- Set
Step 4. Link Applications
- Source:
cash_receipt_applicationrows matchingcash_receipt_application_id IN applicationIds. - Action: UPDATE
cash_receipt_application. - Logic:
- Set
participant_settlement_id=participant_settlement_idfrom Step 2. - Set
updated_by=actorId,updated_dt= current timestamp.
- Set
Step 5. Auto-Create Payouts
- Source: Settlement items created in Step 3.
- Action: Invoke the payout-creation procedure (
createPayoutsForWorksheet, section 2.2.1). This is idempotent — items that already have payouts are skipped. - Logic:
- Payouts are created immediately after saving the settlement.
- Items with zero
participant_settlement_commission_amtare skipped.
Side-effects:
cash_receipt_payoutrecords of type'S'are created for each non-zero settlement item that does not already have a payout.cash_receipt_application.participant_settlement_idis populated for all linked application records.
Postconditions:
participant_settlementrecord exists withparticipant_settlement_status_cd = 'D'.participant_settlement_itemrecords exist for each non-zero payee.- All
applicationIdsnow haveparticipant_settlement_idset to the new settlement. cash_receipt_payoutrecords exist for each qualifying settlement item.- Returns the new
participant_settlement_id.
2.1.3 Update Settlement
Operation: updateSettlement
Trigger: User modifies an existing settlement on an Applied worksheet and clicks Save.
Input Parameters:
settlementId: Integer — theparticipant_settlement_idto updateheader: Object — updated header fieldsitems: Object[] — updated items array (one per payee)applicationIds:int[]— updated set of linkedcash_receipt_applicationIDsactorId: String — identifier for the user performing the operation
Updates an existing settlement header and items. Handles party additions, removals, and amount changes while preserving locked items.
Step 1. Update Header
- Source: Input header fields.
- Action: UPDATE
participant_settlementwhereparticipant_settlement_id = settlementId. - Logic:
- Set
participant_settlement_overrided_ind,participant_settlement_comment= from input. - Set
updated_by=actorId,updated_dt= current timestamp.
- Set
Step 2. Identify Locked Items
- Source:
payment_itemwhereparticipant_settlement_item_idmatches existing items on this settlement. - Action: SELECT (read-only).
- Logic:
- Exclude voided payment items (
payment_item_posting_status_cd = 'X'). - Build a set of locked
participant_settlement_item_idvalues — those linked to payment items withpayment_execution_status_cd IN ('PROCESSING', 'SENT', 'ACKNOWLEDGED', 'PAID').
- Exclude voided payment items (
Step 3. Process Each Incoming Item
- Source: Input items array matched against existing
participant_settlement_itemrecords bypayment_party_id. - Action: UPDATE, INSERT, or skip per item based on lock state and amount.
- Logic:
- If an existing item for that party is locked: skip it entirely (preserve as-is).
- If an existing item for that party is unlocked:
- If the new amount is zero: mark for deletion (handled in Step 5).
- If the amount changed and the item has a linked
payment_item_id: void the old payment item by settingpayment_item_posting_status_cd = 'X',payment_execution_status_cd = 'CANCELLED',return_reason_cd = 'SETTLEMENT_EDIT',returned_dt = now(). Then clearpayment_item_idon both the settlement item and its correspondingcash_receipt_payout. - Update the settlement item fields:
payment_party_bank_id,participant_settlement_commission_flat_ind,participant_settlment_commission_perc,participant_settlement_commission_amt,calc_level_cd,participant_settlement_item_comment,payment_date. - Also update the corresponding
cash_receipt_payoutrecord fields:payment_item_amt,payment_item_name,payment_date.
- If no existing item for that party: INSERT a new
participant_settlement_item(skip if amount is zero).
Step 4. Re-Link Applications
- Source:
cash_receipt_applicationcurrently linked to this settlement plus the newapplicationIds. - Action: UPDATE
cash_receipt_application. - Logic:
- First, clear
participant_settlement_idon allcash_receipt_applicationrows currently linked to this settlement (set toNULL). - Then, set
participant_settlement_id=settlementIdfor all rows inapplicationIds.
- First, clear
Step 5. Delete Removed Items
- Source: Existing
participant_settlement_itemrecords whosepayment_party_idis not in the incoming items set and whoseparticipant_settlement_item_idis not locked. - Action: UPDATE
payment_item, DELETEcash_receipt_payout, DELETEparticipant_settlement_item. - Logic:
- If linked to a
payment_item: void the payment item using the same pattern as Step 3. - Delete the corresponding
cash_receipt_payoutrow. - Delete the
participant_settlement_itemrow.
- If linked to a
Side-effects:
- Voided payment items have
payment_execution_status_cd = 'CANCELLED',payment_item_posting_status_cd = 'X',return_reason_cd = 'SETTLEMENT_EDIT'. cash_receipt_application.participant_settlement_idis updated to reflect the current application linkage.
Postconditions:
participant_settlementheader reflects the updated values.- Locked items are unchanged.
- Unlocked items reflect the new amounts, party, and bank assignments.
- Removed unlocked items and their payouts have been deleted.
2.2 Payout Creation
2.2.1 Create Payouts from Settlement Items
Operation: createPayoutsForWorksheet
Trigger: Called automatically after settlement save (section 2.1.2), during the worksheet Settle transition (Applied P → Settled T), or at worksheet approval if previously skipped. This procedure is idempotent.
Input Parameters:
worksheetId: Integer — thecash_receipt_worksheet_idto processactorId: String — identifier for the user performing the operation
Generates cash_receipt_payout records of type 'S' from settlement items. This bridges the settlement to the payment layer and is idempotent — it skips items that already have payout records and items with zero amounts.
Step 1. Load Settlements for the Worksheet
- Source:
cash_receipt_applicationwherecash_receipt_worksheet_id = worksheetIdjoined toparticipant_settlementviaparticipant_settlement_id. - Action: SELECT (read-only).
- Logic:
- Collect all distinct
participant_settlement_idvalues linked to this worksheet.
- Collect all distinct
Step 2. Load Existing Payouts
- Source:
cash_receipt_payoutwherecash_receipt_worksheet_id = worksheetId. - Action: SELECT (read-only).
- Logic:
- Build a set of
participant_settlement_item_idvalues that already have payout records. These will be skipped in Step 4.
- Build a set of
Step 3. Load Full Settlement with Deal Context
- Source:
participant_settlement_itemfor each settlement found in Step 1. Also querycash_receipt_applicationjoined tobilling_item_detailjoined tobilling_itemviaparticipant_settlement_idto extract deal context. - Action: SELECT (read-only).
- Logic:
- For each settlement, load its full item list.
- Extract
deal_id,buyer_id,uta_entity_id,department_idfrom the first matchingbilling_itemrecord.
Step 4. Filter Items Needing Payouts
- Source: Settlement items from Step 3.
- Action: SELECT (read-only, filtering).
- Logic:
- Exclude items whose
participant_settlement_item_idis already in the existing payout set (Step 2). - Exclude items where
participant_settlement_commission_amt = 0.
- Exclude items whose
Step 5. Insert Payout Records
- Source: Qualifying settlement items from Step 4.
- Action: INSERT into
cash_receipt_payoutfor each item. - Logic:
- Set
cash_receipt_worksheet_id=worksheetId. - Set
payout_party_id=participant_settlement_item.payment_party_id. - Set
payment_party_bank_id=participant_settlement_item.payment_party_bank_id. - Set
participant_settlement_item_id= the settlement item'sparticipant_settlement_item_id. - Set
deal_id= from billing item context (Step 3). - Set
buyer_id= from billing item context. - Set
uta_entity_id= from billing item context. - Set
department_id= from billing item context. - Set
payment_item_amt=participant_settlement_item.participant_settlement_commission_amt. - Set
payment_item_name=participant_settlement_item.participant_settlement_item_commentor a system default. - Set
payment_item_type_cd='S'(Settlement). - Set
payment_item_currency_cd='USD'(or from billing item if available). - Set
payment_date=participant_settlement_item.payment_date. - Set
do_not_send_ind=participant_settlement_item.do_not_send_ind. - Set
payment_item_id=NULL(populated at worksheet approval). - Set
created_by/updated_by=actorId, timestamps = current.
- Set
Side-effects:
cash_receipt_payoutrecords of type'S'are created for each qualifying settlement item.
Postconditions:
- Every non-zero settlement item on the worksheet that did not already have a payout now has a corresponding
cash_receipt_payoutrecord withpayment_item_id = NULL.
2.2.2 Create Passthrough Payment Payout
Operation: createPassthroughPayout
Trigger: User adds a passthrough payout entry on the worksheet Payouts tab.
Input Parameters:
worksheetId: Integer — thecash_receipt_worksheet_idpayoutPartyId: Integer —party.party_idof the payeepaymentPartyBankId: Integer —bank_account.bank_account_idfor the payeedealId: Integer (optional) —deal.deal_idbuyerId: Integer (optional) —party.party_idof the buyerutaEntityId: Integer (optional) —uta_entity.uta_entity_iddepartmentId: Integer (optional) —department.department_idpaymentItemName: String — descriptive name for the payoutpaymentItemAmt: String (decimal) — amount to pay (decimal(15,2))paymentItemTypeCd: String —'P'(Passthrough)paymentItemCurrencyCd: String — ISO currency codepaymentDate: Date (optional) — requested payment datedoNotSendInd: Boolean — whether to hold from transmissionactorId: String — identifier for the user performing the operation
Creates a direct payment payout record that bypasses the settlement process. Passthrough payments go from the worksheet to payment without dividing among deal parties via a settlement.
Step 1. Insert Payout Record
- Source: Input fields.
- Action: INSERT into
cash_receipt_payout. - Logic:
- Set
cash_receipt_worksheet_id=worksheetId. - Set
payout_party_id=payoutPartyId. - Set
payment_party_bank_id=paymentPartyBankId. - Set
deal_id,buyer_id,uta_entity_id,department_id= from input (may beNULL). - Set
payment_item_name= from input. - Set
payment_item_amt= from input. - Set
payment_item_type_cd='P'. - Set
payment_item_currency_cd= from input (default'USD'). - Set
payment_date= from input. - Set
do_not_send_ind= from input (defaultfalse). - Set
participant_settlement_item_id=NULL(no settlement linkage). - Set
payment_item_id=NULL(populated at worksheet approval). - Set
created_by/updated_by=actorId, timestamps = current.
- Set
Side-effects:
- None beyond the inserted payout record.
Postconditions:
- A
cash_receipt_payoutrecord withpayment_item_type_cd = 'P'andpayment_item_id = NULLexists for the worksheet. - The payout will be converted to a
payment_itemwhen the worksheet is approved (section 2.3.1).
2.2.3 Create VAT Pass-Through Payout
Operation: createVatPassthroughPayout
Trigger: The tax engine determines that UK VAT applies to the artist fee on a worksheet receivable.
Input Parameters:
worksheetId: Integer — thecash_receipt_worksheet_idbillingItemId: Integer —billing_item.billing_item_idfor context lookupclientPartyId: Integer —party.party_idof the clientvatAmt: String (decimal) — VAT amount to pass throughcurrencyCd: String — ISO currency codeactorId: String — identifier for the user performing the operation
Creates a VAT-specific pass-through payout (type 'V') for UK Artist Fee VAT scenarios. This is an idempotent operation.
Step 1. Check for Existing VAT Payout
- Source:
cash_receipt_payoutwherecash_receipt_worksheet_id = worksheetIdandpayment_item_type_cd = 'V'. - Action: SELECT (read-only).
- Logic:
- If any rows exist, return without creating a new payout (idempotent guard).
Step 2. Look Up Billing Item Context
- Source:
billing_itemwherebilling_item_id = billingItemId. - Action: SELECT (read-only).
- Logic:
- Extract
deal_id,buyer_id,uta_entity_id,department_idfor use in the payout record.
- Extract
Step 3. Insert VAT Payout
- Source: Input fields and billing item context from Step 2.
- Action: INSERT into
cash_receipt_payout. - Logic:
- Set
payment_item_type_cd='V'. - Set
payment_item_name='UK VAT Pass-Through on Artist Fee'. - Set
payment_item_amt=vatAmt. - Set
payment_item_currency_cd=currencyCd. - Set
deal_id,buyer_id,uta_entity_id,department_id= from billing item context. - Set
payout_party_id=clientPartyId. - Set all other fields following the passthrough pattern (section 2.2.2).
- Set
Side-effects:
- None beyond the inserted payout record if none already existed.
Postconditions:
- At most one
cash_receipt_payoutrecord withpayment_item_type_cd = 'V'exists for this worksheet.
2.3 Payment Item Creation
2.3.1 Create Payment Items from Payouts
Operation: createPaymentItemsFromPayouts
Trigger: Called during worksheet approval (Settled T → Approved A), after settlements are approved, converting payout records into outbound payment requests.
Input Parameters:
worksheetId: Integer — thecash_receipt_worksheet_idto processactorId: String — identifier for the user performing the operation
Converts cash_receipt_payout records into payment_item records at worksheet approval. This is where outbound payment requests are materialized. Only payouts with payment_item_id IS NULL are processed.
Step 1. Load Payouts Needing Payment Items
- Source:
cash_receipt_payoutjoined toparticipant_settlement_item,participant_settlement,cash_receipt_application,billing_item_detail,billing_item,cash_receipt_worksheet,cash_receipt_split, andcash_receiptwherecash_receipt_worksheet_id = worksheetIdandpayment_item_id IS NULL. - Action: SELECT (read-only).
- Logic:
- Deduplicate by
cash_receipt_payout_id— the settlement-to-application join may produce multiple rows per payout. - Collect:
payment_item_type_cd,payment_item_name,payout_party_id,participant_settlement_item_id,payment_item_amt,payment_item_currency_cd,payment_date,payment_party_bank_id(payout),payment_party_bank_id(settlement item, as fallback),participant_settlement_item_comment,deal_id(payout and billing item),buyer_id(payout and billing item),uta_entity_id(payout and billing item),department_id(payout and billing item),client_id(billing item),contracted_party_id(billing item),bank_account_idfromcash_receipt(UTA source account),do_not_send_ind.
- Deduplicate by
Step 2. Determine Initial Execution Status
- Source:
payment_dateanddo_not_send_indfrom each payout. - Action: Computation (no database write).
- Logic:
- If
payment_dateis in the future ORdo_not_send_ind = true: setinitial_status = 'WAITING'. - Otherwise: set
initial_status = 'PENDING'.
- If
Step 3. Insert Payment Item
- Source: Payout data from Step 1 and status from Step 2.
- Action: INSERT into
payment_item. - Logic:
- Set
payment_item_type_cd=cash_receipt_payout.payment_item_type_cd(default'P'if null). - Set
payment_item_name=cash_receipt_payout.payment_item_name(default'Commission Payment'if null). - Set
payment_party_id=cash_receipt_payout.payout_party_id. - Set
payment_party_bank_id= payout'spayment_party_bank_idfirst; fall back to settlement item'spayment_party_bank_id. - Set
participant_settlement_item_id=cash_receipt_payout.participant_settlement_item_id. - Set
payment_item_amt=cash_receipt_payout.payment_item_amt. - Set
payment_item_currency_cd=cash_receipt_payout.payment_item_currency_cd(default'USD'). - Set
payment_item_comment=participant_settlement_item.participant_settlement_item_comment. - Set
payment_item_posting_status_cd='U'(Unposted). - Set
payment_execution_status_cd=initial_statusfrom Step 2. - Set
payment_clearing_status_ind=true. - Set
payment_date=cash_receipt_payout.payment_date. - Set
deal_id= payout'sdeal_idfirst, then billing item'sdeal_id. - Set
client_id= billing item'sclient_idfirst; fall back topayout_party_id(for passthroughs where payout party is the client). - Set
buyer_id= payout'sbuyer_idfirst, then billing item's. - Set
contracted_party_id= billing item'scontracted_party_id. - Set
department_id= payout'sdepartment_idfirst, then billing item's. - Set
uta_entity_id= payout'suta_entity_idfirst, then billing item's. - Set
source_account_id=cash_receipt.bank_account_id(UTA's receiving bank account). - Set
do_not_send_ind=cash_receipt_payout.do_not_send_ind. - Set
created_by/updated_by=actorId. - Capture the returned
payment_item_id.
- Set
IMPORTANT
The field priority for deal_id, buyer_id, uta_entity_id, and department_id is: payout fields first, then billing item fields. This ensures passthrough payments (which may not have billing item linkage) are populated correctly from the payout record.
Step 4. Back-Link Payout
- Source: The
cash_receipt_payoutrecord processed in Step 3. - Action: UPDATE
cash_receipt_payout. - Logic:
- Set
payment_item_id=payment_item_idfrom Step 3. - Set
updated_by=actorId,updated_dt= current timestamp.
- Set
Step 5. Back-Link Settlement Item
- Source: The
participant_settlement_itemlinked to this payout. - Action: UPDATE
participant_settlement_item. - Logic:
- Set
payment_item_id=payment_item_idfrom Step 3. - Set
updated_by=actorId,updated_dt= current timestamp.
- Set
Side-effects:
cash_receipt_payout.payment_item_idis populated for each processed payout.participant_settlement_item.payment_item_idis populated for each linked settlement item.
Postconditions:
- One
payment_itemrecord exists for each qualifying payout (non-zero, previously unlinked). - Each new payment item has
payment_execution_status_cd = 'WAITING'(future-dated or do-not-send) or'PENDING'(ready for processing). - Each new payment item has
payment_item_posting_status_cd = 'U'(GL not yet posted).
2.4 Settlement Status Transitions
2.4.1 Submit Settlements for Worksheet (Applied → Settled)
Operation: submitSettlementsForWorksheet
Trigger: Worksheet transitions from Applied (P) to Settled (T). Called by the worksheet settle procedure.
Input Parameters:
worksheetId: Integer — thecash_receipt_worksheet_idactorId: String — identifier for the user performing the operation
Ensures payouts exist for all settlement items and transitions all settlements on the worksheet to Settled status.
Step 1. Create Payouts (Idempotent)
- Source: All settlements linked to this worksheet.
- Action: Invoke
createPayoutsForWorksheet(section 2.2.1). - Logic:
- Creates payout records for any settlement items that do not yet have them.
- Skips items that already have payouts and items with zero amounts.
Step 2. Update Settlement Statuses to Settled
- Source: All
participant_settlementrecords linked to this worksheet viacash_receipt_application.participant_settlement_id. - Action: UPDATE
participant_settlement. - Logic:
- Set
participant_settlement_status_cd='T'(Settled). - Set
updated_by=actorId,updated_dt= current timestamp.
- Set
Side-effects:
- All payout records that were missing are now created.
Postconditions:
- All
participant_settlementrecords on this worksheet haveparticipant_settlement_status_cd = 'T'. - All settlement items have corresponding
cash_receipt_payoutrecords.
2.4.2 Approve Settlements for Worksheet (Settled → Approved)
Operation: approveAllSettlementsForWorksheet
Trigger: Worksheet transitions to Approved (A). Called by the worksheet approval procedure after settling.
Input Parameters:
worksheetId: Integer — thecash_receipt_worksheet_idactorId: String — identifier for the user performing the operation
Transitions all settlements on the worksheet to Approved status. Idempotent — settlements already in Approved status are skipped.
Step 1. Load Settlements
- Source:
participant_settlementrecords linked to this worksheet. - Action: SELECT (read-only).
- Logic:
- Find all
participant_settlement_idvalues for this worksheet viacash_receipt_application.participant_settlement_id.
- Find all
Step 2. Update Each Settlement Status
- Source: Each
participant_settlementrecord from Step 1. - Action: UPDATE
participant_settlementfor each non-Approved settlement. - Logic:
- If
participant_settlement_status_cd = 'A'already: skip (idempotent). - Otherwise: set
participant_settlement_status_cd='A'(Approved). - Set
updated_by=actorId,updated_dt= current timestamp.
- If
Side-effects:
- None beyond the status update. Payment items are created in a separate step (section 2.3.1).
Postconditions:
- All
participant_settlementrecords on this worksheet haveparticipant_settlement_status_cd = 'A'.
2.4.3 Return Settlements (Worksheet Return)
Operation: returnSettlementsForWorksheet
Trigger: Worksheet return procedure is initiated for an Approved worksheet.
Input Parameters:
worksheetId: Integer — thecash_receipt_worksheet_idbeing returnedactorId: String — identifier for the user performing the operation
Sets all settlements on the worksheet to Returned status as part of the three-document return model.
Step 1. Load Settlements
- Source:
participant_settlementrecords linked to this worksheet. - Action: SELECT (read-only).
- Logic:
- Find all
participant_settlement_idvalues for this worksheet.
- Find all
Step 2. Update Settlement Statuses to Returned
- Source: Each
participant_settlementfrom Step 1. - Action: UPDATE
participant_settlement. - Logic:
- Set
participant_settlement_status_cd='R'(Returned). - Set
returned_dt= current timestamp. - Set
returned_by_user_id=actorId. - Set
updated_by=actorId,updated_dt= current timestamp.
- Set
Side-effects:
- All settlements on the worksheet are sealed as Returned.
Postconditions:
- All
participant_settlementrecords on this worksheet haveparticipant_settlement_status_cd = 'R'. - Returned settlements are read-only historical records.
2.5 Outbound Payment Execution
2.5.1 Process Pending Payment Items
Operation: processPendingItems
Trigger: User selects payment items on the Payments page and clicks "Process," or a scheduled job picks up PENDING items.
Input Parameters:
paymentItemIds:int[]— IDs ofpayment_itemrecords to processactorId: String — identifier for the user performing the operation
Transmits pending payment items to banks through the adapter pattern. This is the core outbound payment execution flow.
Step 1. Lock Payment Items
- Source:
payment_itemrecords matchingpaymentItemIds. - Action: UPDATE
payment_item. - Logic:
- Set
payment_execution_status_cd='PROCESSING'for all input IDs in a single bulk update. - Set
updated_by=actorId,updated_dt= current timestamp.
- Set
Step 2. Gather Payment Data
- Source:
payment_itemjoined toparty(beneficiary) for each payment item. - Action: SELECT (read-only).
- Logic:
- Query
bank_accountforsource_account_id(UTA's account / debtor information). - Query
bank_accountforpayment_party_bank_id(beneficiary's bank account). Thebank_idon this record drives adapter selection. - Query
party_bank_accountforpreferred_payment_methodon the beneficiary's bank account. If'WIRE', use wire; otherwise default to'ACH'.
- Query
Step 3. Determine Payment Schema
- Source:
bank_account.bank_idfrom the beneficiary's bank account (Step 2). - Action: SELECT from
code_attribute(read-only). - Logic:
- Query
code_attributewherecode_master_type = 'BANK',code = bank_id,attribute = 'PAYMENT_REQUEST_SCHEMA'. - The
valuecolumn determines which adapter to use:CNB_EASI_LINK,ISO20022_PAIN001,BOFA_CASHPRO, orJPM_GLOBAL_PAY. - If no matching code attribute is found, reject with error.
- Query
IMPORTANT
Adapter selection is driven by the PAYMENT_REQUEST_SCHEMA code attribute on the bank, not by bank identity. This decouples the adapter from specific banks — any bank can use any schema.
Step 4. Create Execution Record
- Source: Payment data from Step 2 and schema from Step 3.
- Action: INSERT into
outbound_payment_execution. - Logic:
- Set
outbound_payment_execution_id= auto-generated UUID. - Set
payment_item_id= current payment item ID. - Set
bank_profile_id=0(legacy field, no longer used for routing). - Set
bank_profile_name= human-readable bank name derived frombank_id. - Set
execution_status_cd='CREATED'. - Set
payload_format='JSON'or'XML'(from adapter). - Set
payment_schema= schema value from Step 3. - Set
requested_execution_date=payment_item.payment_dateor today's date. - Set
payment_amount=payment_item.payment_item_amt. - Set
payment_currency=payment_item.payment_item_currency_cd. - Set
service_level='WIRE'or'ACH'from Step 2. - Set
created_by/updated_by=actorId.
- Set
Step 5. Build and Save Payload
- Source: Payment data from Step 2.
- Action: UPDATE
outbound_payment_execution.generated_payload. - Logic:
- Invoke the adapter's
buildPayloadmethod with the gathered payment data. The adapter produces the bank-specific format (JSON for EASI Link, XML for ISO 20022 variants). - Save the generated payload to
outbound_payment_execution.generated_payloadbefore transmission. This ensures the payload is preserved even if transmission fails.
- Invoke the adapter's
Step 6. Transmit to Bank
- Source: Payload from Step 5.
- Action: External HTTP call (bank API). UPDATE
outbound_payment_executionandpayment_itembased on result. - Logic:
- Send the payload to the bank API endpoint.
- On success (HTTP 2xx):
- Update
outbound_payment_execution: setexecution_status_cd = 'SENT',bank_reference_id= from bank response,http_response_code= HTTP code. - Update
payment_item: setpayment_execution_status_cd = 'SENT'. The payment is now locked — it cannot be reversed, voided, or modified.
- Update
- On failure (HTTP 4xx/5xx or error):
- Update
outbound_payment_execution: setexecution_status_cd = 'FAILED',http_response_code= HTTP code,error_message= error details. - Revert
payment_item: setpayment_execution_status_cd = 'PENDING'(allows retry without manual intervention).
- Update
WARNING
On failure, the payment item is reverted to PENDING, not FAILED. This allows immediate retry. The FAILED status on payment_item is reserved for cases where the bank confirms failure after initial acceptance, received via status polling.
Side-effects:
outbound_payment_executionrecord is created with the full payload and result.- Payment items that succeed are locked at
payment_execution_status_cd = 'SENT'. - Payment items that fail revert to
payment_execution_status_cd = 'PENDING'for retry.
Postconditions:
- Each successfully processed payment item has
payment_execution_status_cd = 'SENT'and a linkedoutbound_payment_executionwithexecution_status_cd = 'SENT'and abank_reference_id. - Each failed payment item has
payment_execution_status_cd = 'PENDING'and a linkedoutbound_payment_executionwithexecution_status_cd = 'FAILED'.
2.5.2 Retry Failed Execution
Operation: retryExecution
Trigger: User clicks "Retry" on a failed execution on the Payments page.
Input Parameters:
executionId: UUID — theoutbound_payment_execution_idto retryactorId: String — identifier for the user performing the operation
Retries a failed outbound payment execution by reprocessing the original payment item. Creates a new execution record rather than modifying the failed one, preserving the full audit trail.
Step 1. Load Failed Execution
- Source:
outbound_payment_executionwhereoutbound_payment_execution_id = executionId. - Action: SELECT (read-only).
- Logic:
- Validate that
execution_status_cd = 'FAILED'. If not, reject with error. - Validate that
payment_item_idis not null. If null, reject with error.
- Validate that
Step 2. Reprocess Payment Item
- Source:
payment_item_idfrom Step 1. - Action: Invoke
processPendingItems(section 2.5.1) with[execution.payment_item_id]. - Logic:
- A new
outbound_payment_executionrecord is created for the same payment item. - The original failed execution record is never modified (audit trail preserved).
- A new
NOTE
Execution records are immutable once terminal (ACKNOWLEDGED or FAILED). Retries always create new execution records, providing a complete history of all transmission attempts for a given payment item.
Side-effects:
- A new
outbound_payment_executionrecord is created for the samepayment_item_id. - The failed execution record is not modified.
Postconditions:
- A new
outbound_payment_executionexists for the payment item. Its outcome depends on the bank response (same as section 2.5.1).
2.6 Payment Status Polling
2.6.1 Poll Payment Status
Operation: pollPendingPayments
Trigger: Scheduled polling job runs periodically, or user clicks "Check Status" for a single execution.
Input Parameters:
executionId: UUID (optional) — when provided, polls only the specified execution; when omitted, polls all SENT executionsactorId: String — identifier for the user performing the operation
Queries bank APIs for status updates on SENT payment items and propagates terminal statuses back to payment_item.
Step 1. Load SENT Executions
- Source:
outbound_payment_executionwhereexecution_status_cd = 'SENT'. - Action: SELECT (read-only).
- Logic:
- When
executionIdis provided, filter to that single record. - When omitted, load all executions in
SENTstatus.
- When
Step 2. Check Adapter Polling Capability
- Source:
outbound_payment_execution.payment_schemafor each execution. - Action: Computation (no database operation).
- Logic:
- If the adapter for
payment_schemadoes not support status polling (statusPolling = false): skip this execution. - If
bank_reference_idis null: skip this execution.
- If the adapter for
Step 3. Build and Send Status Request
- Source:
outbound_payment_execution.bank_reference_idfor each eligible execution. - Action: External HTTP call (bank status API).
- Logic:
- Use the adapter's
buildStatusRequestmethod with thebank_reference_id. - Send the status inquiry to the bank's status endpoint.
- Parse the bank response using the adapter's
parseStatusResponsemethod.
- Use the adapter's
Step 4. Update Execution Record
- Source: Bank response from Step 3.
- Action: UPDATE
outbound_payment_execution. - Logic:
- Map bank-specific status to standardized execution status:
COMPLETED→'ACKNOWLEDGED';FAILEDorREVERSED→'FAILED';PENDINGorPROCESSING→ remain'SENT'. - Set
execution_status_cd= mapped status. - Set
poll_count= incremented by 1. - Set
last_polled_at= current timestamp. - Append to
status_history(JSONB):{ pollNumber, bankStatus, mappedStatus, timestamp, details }.
- Map bank-specific status to standardized execution status:
Step 5. Propagate Terminal Status to Payment Item
- Source: The status change computed in Step 4.
- Action: UPDATE
payment_itemwhen a terminal status is reached. - Logic:
- If the mapped status changed and reached a terminal state:
- Bank status
COMPLETED: setpayment_item.payment_execution_status_cd = 'PAID'. - Bank status
FAILEDorREVERSED: setpayment_item.payment_execution_status_cd = 'FAILED'. - Bank status
PENDINGorPROCESSING: no change to payment item (remains'SENT').
- Bank status
- If the mapped status changed and reached a terminal state:
**PoC Artifact:** The bank transport layer in the PoC is a stub that uses "magic numbers" (specific cent values like `.50`, `.99`) to simulate different bank behaviors. Production will replace the transport layer stub with actual HTTP clients for each bank.
Side-effects:
outbound_payment_execution.poll_count,last_polled_at,status_history, andexecution_status_cdare updated for each polled execution.payment_item.payment_execution_status_cdis updated when the execution reachesACKNOWLEDGEDorFAILED.
Postconditions:
- Each polled execution has an updated
status_historyentry reflecting the latest poll. - Payment items whose bank status reached a terminal state (
PAIDorFAILED) have theirpayment_execution_status_cdupdated accordingly.
2.7 Void & Cancellation
2.7.1 Void Payment Items on Worksheet Return
Operation: voidPaymentItemsForWorksheetReturn
Trigger: Worksheet return procedure initiates for an Approved worksheet.
Input Parameters:
worksheetId: Integer — thecash_receipt_worksheet_idbeing returnedactorId: String — identifier for the user performing the operation
When a worksheet is returned from Approved status, unlocked payment items are voided and locked ones are preserved as read-only context on the replacement draft.
Step 1. Identify All Payment Items for the Worksheet
- Source:
cash_receipt_payoutwherecash_receipt_worksheet_id = worksheetIdandpayment_item_id IS NOT NULL. - Action: SELECT (read-only).
- Logic:
- Collect all
payment_item_idvalues linked to payouts on this worksheet.
- Collect all
Step 2. Classify Each Payment Item
- Source:
payment_itemrecords from Step 1. - Action: SELECT (read-only).
- Logic:
- Locked:
payment_execution_status_cd IN ('PROCESSING', 'SENT', 'ACKNOWLEDGED', 'PAID')— preserve as-is. These are copied to the replacement draft as read-only context. - Unlocked: All other statuses (
WAITING,PENDING,FAILED,CANCELLED,NULL) — void these.
- Locked:
Step 3. Void Unlocked Payment Items
- Source: Unlocked payment items from Step 2.
- Action: UPDATE
payment_itemfor each unlocked item. - Logic:
- Set
payment_item_posting_status_cd='X'(Skipped — payment was never posted to GL, so no GL reversal needed). - Set
payment_execution_status_cd='CANCELLED'. - Set
return_reason_cd='WORKSHEET_RETURN'. - Set
returned_dt= current timestamp. - Set
returned_by_user_id=actorId.
- Set
WARNING
Voided payment items must have payment_item_posting_status_cd = 'X' (Skipped), not 'U' (Unposted). Setting them to 'X' prevents the GL posting job from ever picking them up. These payments were never sent, so no GL entry was ever posted, and no GL reversal is needed.
Side-effects:
- Settlement statuses are set to Returned by the separate
returnSettlementsForWorksheetprocedure (section 2.4.3).
Postconditions:
- Unlocked payment items have
payment_execution_status_cd = 'CANCELLED'andpayment_item_posting_status_cd = 'X'. - Locked payment items are unchanged.
- The worksheet return three-document model continues: original sealed as Returned, reversal worksheet created, replacement draft created with only locked items as read-only context.
2.7.2 Delete Settlement
Operation: deleteSettlement
Trigger: User clicks "Delete" on a settlement in the worksheet.
Input Parameters:
settlementId: Integer — theparticipant_settlement_idto deleteactorId: String — identifier for the user performing the operation
Removes a settlement and all its dependent records. Blocked if any linked payment items are locked.
IMPORTANT
Steps 3 through 8 must execute in this order to respect foreign key constraints. Payment items must be voided before settlement items are deleted. Payout records must be deleted before settlement items (FK dependency). Applications must be unlinked before the settlement header is deleted.
Step 1. Load Settlement Items
- Source:
participant_settlement_itemwhereparticipant_settlement_id = settlementId. - Action: SELECT (read-only).
- Logic:
- Collect all
participant_settlement_item_idvalues.
- Collect all
Step 2. Check for Locked Payments
- Source:
payment_itemwhereparticipant_settlement_item_idmatches the collected item IDs. - Action: SELECT (read-only).
- Logic:
- Exclude voided payment items (
payment_item_posting_status_cd = 'X'). - If any non-voided payment item has
payment_execution_status_cd IN ('PROCESSING', 'SENT', 'ACKNOWLEDGED', 'PAID'): abort with error "Cannot delete settlement with locked payment items."
- Exclude voided payment items (
Step 3. Void Unlocked Payment Items
- Source: Non-voided payment items linked to this settlement's items.
- Action: UPDATE
payment_item. - Logic:
- Set
payment_item_posting_status_cd='X'(Skipped). - Set
payment_execution_status_cd='CANCELLED'. - Set
return_reason_cd='SETTLEMENT_DELETE'. - Set
returned_dt= current timestamp.
- Set
Step 4. Clear Payment Item Links on Settlement Items
- Source:
participant_settlement_itemrecords for this settlement. - Action: UPDATE
participant_settlement_item. - Logic:
- Set
payment_item_id=NULLfor all items.
- Set
Step 5. Delete Payout Records
- Source:
cash_receipt_payoutwhereparticipant_settlement_item_idmatches each settlement item ID. - Action: DELETE from
cash_receipt_payout.
Step 6. Unlink Applications
- Source:
cash_receipt_applicationwhereparticipant_settlement_id = settlementId. - Action: UPDATE
cash_receipt_application. - Logic:
- Set
participant_settlement_id=NULL.
- Set
Step 7. Delete Settlement Items
- Source:
participant_settlement_itemwhereparticipant_settlement_id = settlementId. - Action: DELETE from
participant_settlement_item.
Step 8. Delete Settlement Header
- Source:
participant_settlementwhereparticipant_settlement_id = settlementId. - Action: DELETE from
participant_settlement.
Side-effects:
- All unlocked payment items linked to this settlement are voided with
payment_execution_status_cd = 'CANCELLED'. cash_receipt_application.participant_settlement_idis cleared for all previously linked applications.
Postconditions:
- No
participant_settlement,participant_settlement_item, orcash_receipt_payoutrecords exist for this settlement. - Previously linked
cash_receipt_applicationrecords haveparticipant_settlement_id = NULL.
2.7.3 Update Payment Item Do-Not-Send Flag
Operation: updateDoNotSendFlag
Trigger: User toggles the do-not-send flag on an individual payment item or in bulk on the Payments page or worksheet payout grid.
Input Parameters:
paymentItemIds:int[]— one or morepayment_item_idvalues to updatedoNotSendInd: Boolean — the new flag valueactorId: String — identifier for the user performing the operation
Toggles the do_not_send_ind flag on one or more payment items to hold or release them from bank transmission.
Step 1. Update Payment Item Flag
- Source:
payment_itemrecords matchingpaymentItemIds. - Action: UPDATE
payment_item. - Logic:
- Set
do_not_send_ind=doNotSendInd. - Set
updated_by=actorId,updated_dt= current timestamp.
- Set
NOTE
Setting do_not_send_ind = true on a PENDING payment item does not automatically change its status to WAITING. The status transition occurs when the payment processor checks eligibility. Similarly, setting the flag to false on a WAITING item does not auto-transition to PENDING — this requires a separate eligibility check.
Side-effects:
- None beyond the flag update.
Postconditions:
payment_item.do_not_send_indreflects the new value for all specified payment items.
3. Business Rules & Logic
3.1 Locked Payment Rule
Business rule: Once a payment item has been transmitted to the bank, it cannot be reversed, voided, or modified. A single locked payment item locks its entire dependency chain.
Data-level enforcement:
- Read:
payment_item.payment_execution_status_cd— locked statuses are'PROCESSING','SENT','ACKNOWLEDGED','PAID'. - Guard: If any payment item in the chain is in a locked status, reject the modification, void, or reversal with an appropriate error.
- Write: The locked dependency chain includes: the
participant_settlement_itemlinked viaparticipant_settlement_item_id; theparticipant_settlementheader (if any item within it is locked, the entire settlement is locked); thecash_receipt_applicationrecords linked viaparticipant_settlement_id; thecash_receipt_payoutrecord linked viaparticipant_settlement_item_id; and the corresponding REV application (bilateral locking). - Statuses considered unlocked:
'WAITING','PENDING','FAILED','CANCELLED', andNULL.
3.2 Two-Stage Payment Lifecycle
Business rule: GL posting for payments only occurs after the bank confirms the payment has been processed. A payment that was never sent must never post a GL entry.
Data-level enforcement:
- Read:
payment_item.payment_execution_status_cdandpayment_item.payment_item_posting_status_cd. - Guard: GL posting (transition of
payment_item_posting_status_cdfrom'U'to'P') is only permitted whenpayment_execution_status_cdis'ACKNOWLEDGED'or'PAID'. - Write: If a payment item is voided before being sent (cancelled at
'PENDING'or'WAITING'), setpayment_item_posting_status_cd = 'X'(Skipped). Setting to'X'prevents the GL posting job from picking it up. Do not set it to'P'(Posted) — posting a reversal for a never-posted transaction creates a GL imbalance.
3.3 Settlement Must Cover All PAY Applications
Business rule: A worksheet cannot transition to Settled unless every PAY application has a settlement and all settlement payouts balance against total PAY applied.
Data-level enforcement:
- Read: Query all
cash_receipt_applicationon the worksheet wherebilling_item_detail.billing_item_detail_type_cd = 'PAY'. Check that each has a non-nullparticipant_settlement_id. - Guard: If any PAY application has
participant_settlement_id = NULL, reject the Settle transition. - Guard: Sum all
cash_receipt_payout.payment_item_amtwherepayment_item_type_cd = 'S'for the worksheet. Sum allcash_receipt_application.cash_receipt_amt_appliedfor PAY applications. Assert|payout_total - pay_applied_total| <= 0.005. If not satisfied, reject the Settle transition.
3.4 Commission Calculation
Business rule: Settlement item amounts can be entered as a flat dollar value or calculated as a percentage of the applicable base amount. The base amount depends on whether deductions are included.
Data-level enforcement:
- Read:
participant_settlement_item.participant_settlement_commission_flat_indandparticipant_settlement_item.calc_level_cd. - Write (percentage mode):
participant_settlement_commission_flat_ind = false. Computeparticipant_settlement_commission_amt = base_amount * (participant_settlment_commission_perc / 100).- If
calc_level_cd = 'DNI'(Do Not Ignore Deductions):base_amount= PAY applied amount minus PAY deductions (net). This is the default. - If
calc_level_cd = 'IGN'(Ignore Deductions):base_amount= PAY applied amount before deductions (gross).
- If
- Write (flat mode):
participant_settlement_commission_flat_ind = true.participant_settlement_commission_amt= directly entered value;participant_settlment_commission_percis informational only.
3.5 Same Revenue Item Constraint
Business rule: All receivables selected for a single settlement must belong to the same revenue item (and therefore the same deal).
Data-level enforcement:
- Read:
billing_item.revenue_item_idfor eachbilling_item_detail_idin the input. - Guard: If any resolved
revenue_item_iddiffers from the others, reject the settlement creation with error: "All selected receivables must belong to the same Revenue Item." - This ensures deal party information is consistent across all items in the settlement.
3.6 Adapter Selection (Schema-Based)
Business rule: The outbound payment adapter is determined by a code attribute on the bank, not by hard-coded bank identity.
Data-level enforcement:
- Read: Resolve
payment_item.payment_party_bank_id→bank_account.bank_id. - Read: Query
code_attributewherecode_master_type = 'BANK',code = bank_id,attribute = 'PAYMENT_REQUEST_SCHEMA'. Thevaluecolumn is one of:CNB_EASI_LINK,ISO20022_PAIN001,BOFA_CASHPRO,JPM_GLOBAL_PAY. - Guard: If no matching code attribute is found, reject with error.
- This design means new banks can be onboarded by configuring a code attribute rather than writing new adapter code, as long as the bank supports one of the existing schemas.
3.7 Service Level Determination
Business rule: Whether a payment is sent as WIRE or ACH is determined by the payee's bank account preference.
Data-level enforcement:
- Read:
payment_item.payment_party_bank_id→party_bank_account.preferred_payment_method. - Write: If
preferred_payment_method = 'WIRE', use wire transfer; otherwise (includingNULL), default to'ACH'. - The service level maps to bank-specific payload codes:
WIRE=URGP(both CNB EASI Link and ISO 20022);ACH=NURG+CCD(orPPDfor individuals).
3.8 Zero-Amount Handling
Business rule: Zero-amount settlements items, payouts, and payment items must not be created or carried forward.
Data-level enforcement:
- Write (settlement items): Items with
participant_settlement_commission_amt = 0are skipped during insert. During update, an existing item whose amount changes to zero is treated as a removal and deleted. - Write (payouts): Payouts with
payment_item_amt = 0are not created during payout generation. - Write (payment items): Payment items are not created for zero-amount payouts.
4. Field Mapping & Transformation
4.1 Settlement Item to Payout (participant_settlement_item → cash_receipt_payout)
When a cash_receipt_payout record of type 'S' is created from a participant_settlement_item:
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
| — | cash_receipt_payout.cash_receipt_worksheet_id | From worksheet context |
participant_settlement_item.payment_party_id | cash_receipt_payout.payout_party_id | Copied as-is |
participant_settlement_item.payment_party_bank_id | cash_receipt_payout.payment_party_bank_id | Copied as-is |
participant_settlement_item.participant_settlement_item_id | cash_receipt_payout.participant_settlement_item_id | Copied as-is |
billing_item.deal_id (from linked application) | cash_receipt_payout.deal_id | Copied from billing item context |
billing_item.buyer_id | cash_receipt_payout.buyer_id | Copied from billing item context |
billing_item.uta_entity_id | cash_receipt_payout.uta_entity_id | Copied from billing item context |
billing_item.department_id | cash_receipt_payout.department_id | Copied from billing item context |
participant_settlement_item.participant_settlement_item_comment | cash_receipt_payout.payment_item_name | Copied as-is (or system default) |
| — | cash_receipt_payout.payment_item_type_cd | Defaulted to 'S' |
participant_settlement_item.participant_settlement_commission_amt | cash_receipt_payout.payment_item_amt | Copied as-is |
| — | cash_receipt_payout.payment_item_currency_cd | Defaulted to 'USD' (or from billing item) |
participant_settlement_item.payment_date | cash_receipt_payout.payment_date | Copied as-is |
participant_settlement_item.do_not_send_ind | cash_receipt_payout.do_not_send_ind | Copied as-is |
| — | cash_receipt_payout.payment_item_id | Defaulted to NULL (populated at approval) |
4.2 Payout to Payment Item (cash_receipt_payout → payment_item)
When a payment_item is created from a cash_receipt_payout at worksheet approval:
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
cash_receipt_payout.payment_item_type_cd | payment_item.payment_item_type_cd | Copied as-is (default 'P' if null) |
cash_receipt_payout.payment_item_name | payment_item.payment_item_name | Copied as-is (default 'Commission Payment' if null) |
cash_receipt_payout.payout_party_id | payment_item.payment_party_id | Copied as-is |
cash_receipt_payout.payment_party_bank_id (then participant_settlement_item.payment_party_bank_id) | payment_item.payment_party_bank_id | Payout field first; settlement item as fallback |
cash_receipt_payout.participant_settlement_item_id | payment_item.participant_settlement_item_id | Copied as-is |
cash_receipt_payout.payment_item_amt | payment_item.payment_item_amt | Copied as-is |
cash_receipt_payout.payment_item_currency_cd | payment_item.payment_item_currency_cd | Copied as-is (default 'USD') |
participant_settlement_item.participant_settlement_item_comment | payment_item.payment_item_comment | From settlement item |
| — | payment_item.payment_item_posting_status_cd | Defaulted to 'U' (Unposted) |
| — | payment_item.payment_execution_status_cd | 'WAITING' if future date or do_not_send_ind = true; else 'PENDING' |
| — | payment_item.payment_clearing_status_ind | Defaulted to true |
cash_receipt_payout.payment_date | payment_item.payment_date | Copied as-is |
cash_receipt_payout.deal_id (then billing_item.deal_id) | payment_item.deal_id | Payout field first; billing item as fallback |
billing_item.client_id (then cash_receipt_payout.payout_party_id) | payment_item.client_id | Billing item first; payout party as fallback for passthroughs |
cash_receipt_payout.buyer_id (then billing_item.buyer_id) | payment_item.buyer_id | Payout field first; billing item as fallback |
billing_item.contracted_party_id | payment_item.contracted_party_id | From billing item only |
cash_receipt_payout.department_id (then billing_item.department_id) | payment_item.department_id | Payout field first; billing item as fallback |
cash_receipt_payout.uta_entity_id (then billing_item.uta_entity_id) | payment_item.uta_entity_id | Payout field first; billing item as fallback |
cash_receipt.bank_account_id | payment_item.source_account_id | From the receipt's bank account (UTA's receiving account) |
cash_receipt_payout.do_not_send_ind | payment_item.do_not_send_ind | Copied as-is |
4.3 Payment Item to Outbound Execution (payment_item → outbound_payment_execution)
When an outbound_payment_execution record is created for a payment item:
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
payment_item.payment_item_id | outbound_payment_execution.payment_item_id | Copied as-is |
| — | outbound_payment_execution.bank_profile_id | Defaulted to 0 (legacy field, not used for routing) |
Lookup from bank_account.bank_id | outbound_payment_execution.bank_profile_name | Human-readable bank name from lookup |
| — | outbound_payment_execution.execution_status_cd | Defaulted to 'CREATED' |
| From adapter | outbound_payment_execution.payload_format | 'JSON' or 'XML' depending on adapter |
From code_attribute lookup | outbound_payment_execution.payment_schema | CNB_EASI_LINK, ISO20022_PAIN001, BOFA_CASHPRO, or JPM_GLOBAL_PAY |
payment_item.payment_date (or current date) | outbound_payment_execution.requested_execution_date | Copied as-is; defaulted to today if null |
payment_item.payment_item_amt | outbound_payment_execution.payment_amount | Snapshot at time of execution |
payment_item.payment_item_currency_cd | outbound_payment_execution.payment_currency | Snapshot at time of execution |
party_bank_account.preferred_payment_method | outbound_payment_execution.service_level | 'WIRE' or 'ACH' |
| Built by adapter before transmission | outbound_payment_execution.generated_payload | Saved before transport is attempted |
NOTE
The outbound_payment_execution table stores snapshots of amount, currency, and execution date at the time of transmission. These snapshots do not change if the parent payment_item is later modified, providing a reliable audit trail of what was actually sent to the bank.
5. Cross-References
| Document | Relationship |
|---|---|
| Settlements Data Model | Table definitions, field types, status lifecycle diagrams for participant_settlement, participant_settlement_item, payment_item, and outbound_payment_execution. Code master values for PARTICIPANT_SETTLEMENT_STATUS_CD, PAYMENT_ITEM_TYPE_CD, POSTING_STATUS_CD, CALC_LEVEL_CD. Decimal precision rules. |
| Settlements Queries | Read-only queries used to populate settlement forms, payment item lists, execution history, and lock-status checks. |
| Worksheets Data Model | Worksheet status transitions drive when settlement procedures are invoked. Draft → Applied creates context for settlement creation. Applied → Settled triggers section 2.4.1. Settled → Approved triggers section 2.4.2 and then section 2.3.1. Worksheet return triggers section 2.7.1 and section 2.4.3. cash_receipt_application.participant_settlement_id links applications to settlements. cash_receipt_payout bridges the worksheet to payment items. |
| Billing Items Data Model | billing_item and billing_item_detail provide receivable context for settlements. The deal_id, client_id, buyer_id, uta_entity_id, and department_id from billing_item propagate through the payout to the payment item. billing_item_deduction and cash_receipt_application_deduction affect commission calculations via calc_level_cd. |
| Parties Data Model | deal_party provides default commission percentages and party assignments when computing settlement defaults. party_bank_account.preferred_payment_method determines WIRE vs ACH. bank_account.bank_id drives adapter selection via code_attribute. |
| Accounting Data Model | GL posting occurs only after a payment item reaches ACKNOWLEDGED or PAID status (Stage 2). payment_item.payment_item_posting_status_cd and posting_dt track this. Cancelled payments use payment_item_posting_status_cd = 'X' to indicate no GL entry was ever posted. |