Skip to content

Payment Out Complete Walkthrough — Example-Based Documentation

Overview

This document demonstrates the complete lifecycle of a Payment Out transaction using real payload examples, showing exactly which tables and columns are affected during Add (Draft), Post, and Unpost phases.

A Payment Out records money sent from a bank account or treasury to a vendor (or other payee). It can optionally:

  • Include a payment method commission (e.g. bank transfer fee, cheque commission) with optional VAT on that commission
  • Be linked to a source document (e.g. a POS session or fund transfer)
  • Be issued via cheque with a future due date

Unlike Payment In, Payment Out does not auto-reconcile purchase invoices at creation time — reconciliation is handled separately through the Purchase Invoice Reconciliation flow.


Example Payload — Vendor Payment (Bank Transfer)

json
{
  "paymentOutDate": "2026-01-28T17:00:00",
  "description": "Payment to supplier for PI-2026-0001",
  "currencyId": 4,
  "rate": 1.0,
  "paymentHub": "Bank",
  "paymentHubDetailId": 12,
  "glAccountId": null,
  "paymentOutDetails": [
    {
      "paidBy": "Vendor",
      "paidByDetailsId": 201,
      "paymentMethodId": 5,
      "amount": 1000.00,
      "currencyId": 4,
      "rate": 1.0,
      "costCenters": [],
      "methodDetails": {
        "chequeNumber": null,
        "chequeDueDate": null,
        "bankReference": "TRF-20260128-002",
        "commissionType": null,
        "commissionValue": 0,
        "allowVat": false,
        "vatRatio": 0
      }
    }
  ]
}

Pre-Save Calculations

Detail-Level:
  LocalAmount      = Amount × Rate          = 1000 × 1.0 = 1000
  CommissionAmount = 0  (no commission)
  VatAmount        = 0  (no VAT on commission)
  NetAmount        = Amount + CommissionAmount + VatAmount = 1000 + 0 + 0 = 1000
  NetLocalAmount   = NetAmount × Rate       = 1000 × 1.0 = 1000

  Note: For Payment Out, NetAmount = Amount + Commission + VAT
  (commission is an additional cost on top of what the vendor receives)

Header Totals:
  Amount         = Sum of detail amounts          = 1000
  LocalAmount    = Sum of detail local amounts    = 1000
  NetAmount      = Sum of detail net amounts      = 1000
  NetLocalAmount = Sum of detail net local amounts = 1000

Phase 1: Add Payment Out (Draft)

Handler: AddPaymentOutCommandHandler

Action: Creates a draft payment-out record with no GL or bank impact.

Validations Performed

CheckRuleResult
Payment date in open periodResolved via GetCurrentPeriodQuery(PaymentOutDate)✅ Jan 2026 open
Currency requiredCurrencyId must be provided✅ SAR (4) provided
Rate requiredRate > 0✅ 1.0 provided
Payment hubMust be Bank or Treasury✅ Bank
PaymentHubDetailId requiredMust be provided — resolves to BankAccountId (hub=Bank) or TreasuryId (hub=Treasury)✅ 12 provided
Details not emptyAt least one detail line✅ 1 detail
Detail paid-byPaidBy must be Customer, Vendor, or Other✅ Vendor
Paid-by entityPaidByDetailsId must reference valid vendor✅ Vendor 201
Payment methodMust reference valid PaymentMethod✅ Method 5 (Transfer)
Amount > 0Each detail amount must be positive✅ 1000 > 0
Bank balance sufficientBank/treasury current balance ≥ total amount✅ Balance OK
Cheque fieldsIf PaymentMethodType = Check, ChequeNumber + ChequeDueDate required✅ Not cheque
Cost center sumIf account requires cost centers, percentages must sum to 100%✅ No cost centers required

Table 1: PaymentOutHeader (INSERT)

Operation: INSERT new record

Column NameValueSourceNotes
Id[Auto-Generated GUID]Systeme.g., b2c3d4e5-2345-6789-bcde-f01234567890
Code"PO-2026-0001"Sequence ServiceGenerated by UpdateLastSequenceCommand
PaymentOutDate2026-01-28 17:00:00PayloadTransaction date
Description"Payment to supplier for PI-2026-0001"PayloadOptional notes
Status"Draft"SetAsDraft()TransactionStatus enum
PaymentHub"Bank"PayloadPaymentPlace enum: Bank or Treasury
PaymentHubDetailId12PayloadHolds BankAccountId when PaymentHub = Bank; holds TreasuryId when PaymentHub = Treasury
CurrencyId4PayloadSaudi Riyal
Rate1.0PayloadExchange rate to base currency
Amount1000.00CalculatedSum of detail amounts
LocalAmount1000.00Amount × RateBase currency amount
NetAmount1000.00Amount + Commission + VATTotal disbursed (including fees)
NetLocalAmount1000.00NetAmount × RateBase currency net
GlAccountIdNULLPayloadOverride GL account for header (optional)
FinancialYearPeriodId[Integer DB ID]GetCurrentPeriodQuery(PaymentOutDate)Resolved from date — payload string is for display only
JournalIdNULLDraft stateSet on post
JournalCodeNULLDraft stateSet on post
SourceDocumentTypeNULLNot linkedSet for POS / system-generated payments
SourceDocumentIdNULLNot linkedReference to source transaction
SourceDocumentCodeNULLNot linkedSource transaction code
UnpostedReasonNULLNot yet unpostedReason text stored on unpost
UnpostedByNULLNot yet unpostedUser who unposted
UnpostedOnNULLNot yet unpostedTimestamp of unpost
WorkflowStatusNULLNo workflow yetRequestStatus enum
POSSessionIdNULLNot POSPOS session reference
BranchId[Current Branch]ISecurityServiceBranch context
CompanyId[Current Company]ISecurityServiceCompany context
CreatedDate[Current Timestamp]AuditAuto-set
CreatedBy[Current User ID]ISecurityServiceAuthenticated user
TenantId[Tenant GUID]ISecurityServiceMulti-tenancy isolation
IsDeletedfalseDefaultSoft delete flag

Table 2: PaymentOutDetail (INSERT)

Operation: INSERT one record per detail line

Column NameValueSourceNotes
Id[Auto-Generated GUID]SystemDetail line ID
PaymentOutId[Header GUID]FKParent payment-out
PaidBy"Vendor"PayloadPaidBy enum: Customer / Vendor / Other
PaidByDetailsId201PayloadVendor ID (FK to Vendor table)
VendorName"ABC Supplier"ResolvedVendor display name
VendorNameAr"مورد ABC"ResolvedArabic vendor name
CustomerIdNULLNot customerSet when PaidBy = Customer
GlAccountIdNULLOptionalOverride GL account for this line
PaymentMethodId5PayloadReferences PaymentMethod table
Amount1000.00PayloadIn foreign currency
LocalAmount1000.00Amount × RateBase currency
NetAmount1000.00Amount + Commission + VATTotal leaving bank for this line
NetLocalAmount1000.00NetAmount × RateBase currency net
Rate1.0PayloadExchange rate
CurrencyId4PayloadSaudi Riyal
NotesNULLPayloadOptional line notes
IsVatLinefalsePayloadNot a VAT line
HasVatfalsePayloadNo VAT on this line
EntityBalanceNULLResolvedVendor's current outstanding balance
CreatedDate[Current Timestamp]AuditAuto-set
CreatedBy[Current User ID]ISecurityServiceAuthenticated user
TenantId[Tenant GUID]ISecurityServiceMulti-tenancy

Table 3: PaymentOutMethodDetail (INSERT)

Operation: INSERT one record per detail (stores payment method specifics)

Column NameValueSourceNotes
Id[Auto-Generated GUID]SystemMethod detail ID
PaymentOutDetailId[Detail GUID]FKParent detail line
PaymentMethodId5PayloadPaymentMethod reference
ChequeNumberNULLNot chequePopulated for Check type
ChequeDueDateNULLNot chequePopulated for Check type
BankReference"TRF-20260128-002"PayloadBank transfer reference
CommissionAmount0Calculated0 — no commission configured
VatAmount0Calculated0 — AllowVat = false
CommissionTypeNULLNo commissionAmount or Percent
CommissionValue0PayloadCommission percentage or fixed amount
CommissionAccountIdNULLNo commissionGL account for commission expense
AllowVatfalsePayloadVAT on commission disabled
VatRatio0Payload0% VAT on commission
VatAccountIdNULLNo VATGL account for commission VAT
CreatedDate[Current Timestamp]AuditAuto-set

Table 4: PaymentOutDetailCostCenter (INSERT)

Operation: NONE — no cost centers in this example

When cost centers are required (account's CostCenterConfig = Mandatory):

Column NameValueSourceNotes
Id[Auto-Generated GUID]SystemAllocation ID
PaymentOutDetailId[Detail GUID]FKParent detail
CostCenterId[e.g. 87]PayloadCost center reference
Percentage100.0PayloadAllocation percentage
Amount1000.00Detail.NetLocalAmount × Percentage/100Allocated amount

Summary of Add Phase (Draft)

Tables Affected:

  • ✅ PaymentOutHeader — 1 INSERT
  • ✅ PaymentOutDetail — 1 INSERT
  • ✅ PaymentOutMethodDetail — 1 INSERT
  • ❌ PaymentOutDetailCostCenter — 0 (no cost centers in this example)
  • ❌ GL / Bank / Treasury — NO impact (draft only)

Phase 2: Post Payment Out

Handler: PostPaymentOutCommandHandler

Action: Finalizes the payment, creates a Journal Entry, records a Bank Transaction, and updates the vendor's financial position.

Prerequisites Check

CheckRule
Status = DraftCannot post already-posted payment
GL account for vendorVendor must have Payable Account configured
GL account for payment methodPaymentMethod must have a GL account (bank clearing or cash account)
GL account for commission (if any)CommissionAccountId must be set
GL account for commission VAT (if any)VatAccountId must be set when AllowVat = true
Open financial periodPaymentOutDate period must be open
Sufficient balanceBank/treasury current balance ≥ NetAmount (total including commission)
Accounting module accessUser must have access to create journal entries

Table 1: PaymentOutHeader (UPDATE)

Operation: UPDATE existing record

Column NameBefore (Draft)After (Posted)Notes
Status"Draft""Posted"TransactionStatus enum
JournalIdNULL[Journal GUID]Linked journal entry
JournalCodeNULL"JE-2026-0130"Journal entry code
UpdatedDateNULL[Current Timestamp]Last update timestamp
UpdatedByNULL[Current User ID]User who posted

Table 2: JournalEntry (INSERT — PaymentOut Journal)

Operation: INSERT via IAccountingPublicApi.AddJournal()

Type: PaymentOut

Column NameValueNotes
Id[Auto-Generated GUID]Journal ID
Code"JE-2026-0130"Sequence code
JournalDate2026-01-28 17:00:00From PaymentOutDate
Type"PaymentOut"JournalEntryType enum
SourceDocument"PaymentOut"SourceDocument enum
SourceDocumentId[PaymentOutHeader GUID]Link to payment
SourceDocumentCode"PO-2026-0001"Payment code
Status"Posted"Immediately posted
TotalDebit1000.00Sum of debit lines
TotalCredit1000.00Sum of credit lines (balanced)
FinancialYearPeriodId[Period ID]From payment period
Description"Payment to supplier for PI-2026-0001"From payment description

Table 3: JournalEntryLine (INSERT × 2 — PaymentOut Journal)

Line 1: Accounts Payable (Debit)

Vendor's outstanding balance reduced:

ColumnValueNotes
AccountId[Vendor.PayableAccountId]AP account for vendor 201
AccountCode"2010"e.g. Accounts Payable
DebitAmount1000.00Clears vendor balance
CreditAmount0No credit
DebitAmountLocal1000.00× Rate (1.0)
CurrencyId4SAR
CurrencyRate1.0Base currency
CostCenterIdNULLNo cost center for AP

Line 2: Bank Account (Credit)

Money leaves the bank:

ColumnValueNotes
AccountId[BankAccount.GLAccountId]GL account of bank account 12
AccountCode"1020"e.g. Current Bank Account
DebitAmount0No debit
CreditAmount1000.00Full amount leaving bank
CreditAmountLocal1000.00× Rate (1.0)
CurrencyId4SAR
CurrencyRate1.0Base currency
CostCenterIdNULLNo cost center for bank
Journal JE-2026-0130 — Payment Out

  DR  2010  Accounts Payable (Vendor)  1,000.00
      CR  1020  Bank Account                   1,000.00
                                      ---------  ---------
      Total                           1,000.00   1,000.00

Table 4: BankTransactionHeader (INSERT)

Operation: INSERT — records this payment in the bank's transaction ledger

Column NameValueNotes
Id[Auto-Generated GUID]Transaction ID
BankAccountId12Sending bank account
TransactionDate2026-01-28From PaymentOutDate
TransactionType"Out"Money leaving the bank
Amount1000.00Payment amount
LocalAmount1000.00Base currency
SourceDocumentType"PaymentOut"Reference type
SourceDocumentId[PaymentOutHeader GUID]Link to payment
SourceDocumentCode"PO-2026-0001"Payment code
JournalId[Journal GUID]Link to journal
Status"Posted"Always posted
CreatedDate[Current Timestamp]Audit

Table 5: BankTransactionDetail (INSERT)

Operation: INSERT — one per payment detail line

Column NameValueNotes
BankTransactionHeaderId[Header GUID]Parent transaction
PaymentOutDetailId[Detail GUID]Link to payment detail
Amount1000.00Detail amount
LocalAmount1000.00Base currency
PaidBy"Vendor"Payee type
PaidByDetailsId201Vendor ID

Summary of Post Phase

Tables Affected:

  • ✅ PaymentOutHeader — 1 UPDATE (status, journal IDs)
  • ✅ JournalEntry — 1 INSERT (PaymentOut journal, immediately Posted)
  • ✅ JournalEntryLine — 2 INSERTs (AP DR + Bank CR)
  • ✅ BankTransactionHeader — 1 INSERT
  • ✅ BankTransactionDetail — 1 INSERT

GL Impact:

Account 2010 (Accounts Payable)  DR  1,000.00
Account 1020 (Bank Account)          CR  1,000.00

Phase 3: Unpost Payment Out

Handler: UnpostPaymentOutCommandHandler

Action: Fully reverses a posted payment — removes the journal and bank transaction, returns to Draft. Requires a reason to be provided.

Prerequisites Check

CheckRule
Status = PostedCannot unpost a Draft payment
Reason providedUnpostPaymentOutCommand.Reason must not be empty
Open financial periodPaymentOutDate period must still be open
Journal reversibleThe linked journal must not already be reversed

Tables Affected

TableOperationNotes
PaymentOutHeaderUPDATEStatus → Draft, JournalId/JournalCode → NULL, UnpostedReason/UnpostedBy/UnpostedOn set
JournalEntryReversedJournal reversed via ReversePaymentOutJournalAsync
BankTransactionHeaderSoft DELETEBank transaction removed
BankTransactionDetailSoft DELETEBank detail removed

Table: PaymentOutHeader (UPDATE on Unpost)

ColumnBefore (Posted)After (Unposted)Notes
Status"Posted""Draft"Reset to Draft
JournalId[GUID]NULLCleared
JournalCode"JE-2026-0130"NULLCleared
UnpostedReasonNULL"Correction needed"Reason stored permanently
UnpostedByNULL[Current User ID]Who unposted
UnpostedOnNULL[Current Timestamp]When unposted

Note: Unlike other draft resets, the unpost reason fields are permanently stored for audit trail purposes even after the payment is re-posted.


Payment Out with Commission (Cheque with Bank Fee)

When a payment method has a commission configured (e.g. 2% cheque processing fee + 15% VAT), the journal gets additional lines and more money leaves the bank than what the vendor receives.

Example

json
{
  "paymentOutDetails": [
    {
      "paidBy": "Vendor",
      "paidByDetailsId": 201,
      "paymentMethodId": 3,
      "amount": 1000.00,
      "methodDetails": {
        "chequeNumber": "CHQ-002",
        "chequeDueDate": "2026-02-15",
        "commissionType": "Percent",
        "commissionValue": 2.0,
        "commissionAccountId": 6010,
        "allowVat": true,
        "vatRatio": 15.0,
        "vatAccountId": 2030
      }
    }
  ]
}

Commission Calculations

CommissionAmount = Amount × CommissionValue / 100 = 1000 × 2%  = 20.00
VatAmount        = CommissionAmount × VatRatio / 100 = 20 × 15% = 3.00
NetAmount        = Amount + CommissionAmount + VatAmount = 1000 + 20 + 3 = 1023.00

Vendor receives:  1000.00 SAR
Bank deducts:     1023.00 SAR  (1000 vendor + 20 commission + 3 VAT)

Updated PaymentOutDetail

ColumnValue
Amount1000.00
NetAmount1023.00
LocalAmount1000.00
NetLocalAmount1023.00

Journal Entry Lines (4 lines instead of 2)

JE-2026-0131 — Payment Out (with Commission)

  DR  2010  Accounts Payable          1,000.00   ← clears vendor balance
  DR  6010  Commission Expense           20.00   ← bank cheque commission
  DR  2030  VAT Input (Commission)        3.00   ← VAT on commission
      CR  1020  Bank Account                  1,023.00   ← total leaving bank
                                      ---------  ---------
      Total                           1,023.00   1,023.00

BankTransactionHeader Amount

The bank transaction records the full NetAmount (1023.00), not just the vendor amount:

ColumnValue
Amount1023.00
LocalAmount1023.00

Payment Out via Treasury (Cash)

When PaymentHub = Treasury, the flow is identical to Bank but creates a TreasuryTransaction instead of a BankTransaction.

Key Differences

PropertyBankTreasury
PaymentHubBankTreasury
PaymentHubDetailIdBankAccountIdTreasuryId
Balance checkBank account balanceTreasury current balance
Transaction createdBankTransactionHeader/DetailTreasuryTransactionHeader/Detail
GL credit accountBankAccount.GLAccountIdTreasury.GLAccountId

Journal Entry Lines (Treasury Cash)

JE-2026-0132 — Cash Payment Out (Treasury)

  DR  2010  Accounts Payable          1,000.00
      CR  1001  Petty Cash / Treasury          1,000.00

Paying a Customer (Refund / Debit Note)

When PaidBy = Customer, money is paid out to a customer (e.g. a refund or rebate).

The GL credit line uses the Customer's Receivable Account instead of a Vendor Payable Account:

JE-2026-0133 — Customer Refund Payment Out

  DR  1010  Accounts Receivable (Customer)  500.00   ← reduces customer balance
      CR  1020  Bank Account                        500.00

When PaidBy = Other, the GL account is taken from the detail's GlAccountId field (a manually specified account).


Paying with Cheque (Post-Dated)

Cheques have a ChequeDueDate that may be in the future. The journal is created at posting time (today), not at cheque due date.

json
"methodDetails": {
  "chequeNumber": "CHQ-003",
  "chequeDueDate": "2026-03-01",
  "bankReference": null
}

The cheque details are stored in PaymentOutMethodDetail for reference. The bank transaction and journal are created immediately when posted — the ERP does not automatically defer posting to the cheque due date.


Multi-Currency Payment Out

When paying a vendor in a foreign currency (e.g. USD), amounts are stored in both currencies.

Example — USD Payment at 3.75 SAR/USD

json
{
  "currencyId": 1,
  "rate": 3.75,
  "paymentOutDetails": [
    {
      "amount": 1000.00,
      "currencyId": 1,
      "rate": 3.75
    }
  ]
}

Stored Values

ColumnHeaderDetail
Amount1000.00 USD1000.00 USD
LocalAmount3750.00 SAR3750.00 SAR
NetAmount1000.00 USD1000.00 USD
NetLocalAmount3750.00 SAR3750.00 SAR

Journal Entry Lines

JE-2026-0134 — Payment Out (USD)

  DR  2010  Accounts Payable          3,750.00 SAR   (= 1000 USD × 3.75)
      CR  1020  Bank Account                  3,750.00 SAR

All GL postings use local (base) currency amounts. Foreign amounts are stored for reference only.


Complete Transaction Flow Summary

1. User submits Add Payment Out request

2. AddPaymentOutCommandHandler.Handle()

3. Validate: payment date, hub, currency, vendor, payment method,
             commission fields, cheque fields, cost center sums

4. GetCurrentPeriodQuery(PaymentOutDate) → FinancialYearPeriodId

5. Load payment methods with commission data

6. Calculate detail amounts:
   - LocalAmount    = 1000 × 1.0 = 1000
   - CommissionAmount = 0
   - VatAmount       = 0
   - NetAmount       = 1000
   - NetLocalAmount  = 1000

7. Validate bank/treasury balance ≥ NetAmount

8. Generate sequence code → "PO-2026-0001"

9. INSERT PaymentOutHeader (Status = Draft)
   INSERT PaymentOutDetail × 1
   INSERT PaymentOutMethodDetail × 1

10. SaveChanges → committed

11. Return PaymentOutHeaderId

--- DRAFT PHASE COMPLETE ---

12. User clicks "Post"

13. PostPaymentOutCommandHandler.Handle()

14. ValidatePreConditions():
    Draft status, Vendor GL account set, open period, balance sufficient

15. CanPostJournal(): User has Accounting module access

16. BuildJournalCommand():
    Header line:    CR  Bank/Treasury GL   1000  (money leaves bank)
    Detail line:    DR  Vendor AP GL       1000  (clears payable)
    (no commission lines — commission = 0)

17. Call IAccountingPublicApi.AddJournal() → JournalCreatedDto

18. InsertIntoEntityTransactions():
    INSERT BankTransactionHeader (Type = Out, Amount = 1000)
    INSERT BankTransactionDetail × 1

19. UPDATE PaymentOutHeader:
    Status = Posted
    JournalId = [GUID]
    JournalCode = "JE-2026-0130"

20. SaveChanges → committed

21. Return success

--- POST PHASE COMPLETE ---

22. (If correction needed) User clicks "Unpost" + provides reason

23. UnpostPaymentOutCommandHandler.Handle()

24. Validate: Posted, reason not empty, open period, journal reversible

25. Call IAccountingPublicApi.ReversePaymentOutJournalAsync()
    → Reverses JournalEntry (swaps DR/CR lines)

26. SoftDeleteLiquidityTransactions():
    Soft DELETE BankTransactionHeader
    Soft DELETE BankTransactionDetail

27. PaymentOutHeader.Unpost():
    Status = Draft
    JournalId = NULL
    JournalCode = NULL
    UnpostedReason = "Correction needed"
    UnpostedBy = [Current User ID]
    UnpostedOn = [Current Timestamp]

28. SaveChanges → committed

--- UNPOST PHASE COMPLETE ---

Database Transactions

Add Phase

sql
BEGIN TRANSACTION

  INSERT INTO PaymentOutHeader VALUES (...)       -- Status = Draft
  INSERT INTO PaymentOutDetail VALUES (...)       -- 1 detail
  INSERT INTO PaymentOutMethodDetail VALUES (...)  -- Method detail

COMMIT TRANSACTION

Duration: ~40–80ms

Post Phase

sql
BEGIN TRANSACTION

  -- Step 1: Create journal (via Accounting public API)
  INSERT INTO JournalEntry VALUES (...)          -- Type = PaymentOut, Posted
  INSERT INTO JournalEntryLine VALUES (...)      -- 2 lines (AP DR + Bank CR)

  -- Step 2: Create bank transaction
  INSERT INTO BankTransactionHeader VALUES (...)
  INSERT INTO BankTransactionDetail VALUES (...)

  -- Step 3: Update payment header
  UPDATE PaymentOutHeader
    SET Status = 'Posted',
        JournalId = @JournalId,
        JournalCode = @JournalCode,
        UpdatedDate = GETUTCDATE(),
        UpdatedBy = @UserId
    WHERE Id = @PaymentOutId

COMMIT TRANSACTION

Duration: ~100–300ms

Unpost Phase

sql
BEGIN TRANSACTION

  -- Step 1: Reverse journal (via Accounting public API)
  -- Creates reversed journal entry with swapped DR/CR

  -- Step 2: Soft delete bank transaction
  UPDATE BankTransactionHeader SET IsDeleted = 1 WHERE SourceDocumentId = @PaymentOutId
  UPDATE BankTransactionDetail  SET IsDeleted = 1 WHERE ...

  -- Step 3: Reset payment header
  UPDATE PaymentOutHeader
    SET Status = 'Draft',
        JournalId = NULL,
        JournalCode = NULL,
        UnpostedReason = @Reason,
        UnpostedBy = @UserId,
        UnpostedOn = GETUTCDATE()
    WHERE Id = @PaymentOutId

COMMIT TRANSACTION

Duration: ~100–250ms


Error Scenarios

Add / Edit Validation Failures

Error ConditionError MessageResolution
Closed financial period"Payment date must be in an open financial period"Change date or open period
Missing PaymentHubDetailId"PaymentHubDetailId is required"Provide the BankAccountId (hub=Bank) or TreasuryId (hub=Treasury)
Missing cheque data"Cheque number and due date are required for cheque payments"Fill cheque fields
Insufficient bank balance"Insufficient balance in bank account"Reduce amount or use different account
Cost center sum ≠ 100%"Cost center allocations must total 100%"Adjust percentages
Amount ≤ 0"Payment amount must be greater than zero"Correct amount
Details empty"At least one payment detail is required"Add a detail line

Post Validation Failures

Error ConditionError MessageResolution
Not Draft"Payment must be in Draft status to post"Already posted
Missing vendor GL account"Vendor payable account not configured"Configure vendor accounting
Missing payment method GL"Payment method GL account not configured"Configure method account
Missing commission GL"Commission account required for payment method with commission"Set CommissionAccountId
Insufficient balance at post time"Insufficient balance to complete payment"Top up bank account
No Accounting module access"User does not have access to create journal entries"Check user permissions
Closed period"Cannot post in a closed financial period"Reopen period

Unpost Validation Failures

Error ConditionError MessageResolution
Not Posted"Payment must be Posted to unpost"Nothing to unpost
Missing reason"Reason is required to unpost"Provide UnpostReason
Closed period"Payment period is closed — cannot unpost"Reopen period
Journal already reversed"Journal entry has already been reversed"Cannot reverse twice

Payment Method Types Reference

TypeCommissionDataChequeFieldsNotes
CashOptionalNoPhysical cash disbursed
CheckOptionalRequired (ChequeNumber + ChequeDueDate)Post-dated cheques; journal created at post time
VisaOptionalNoCard payment, commission = merchant fee
MasterCardOptionalNoCard payment
SPAN / MadaOptionalNoLocal debit network
TransferOptionalNoBank wire transfer; BankReference used

Key Interfaces Used

InterfaceUsage
IAppsUnitOfWorkRepository access for all ERP entities
ISecurityServiceCurrent user, tenant, branch, company context
IClockServiceTestable timestamp provider
IGeneralSettingsPublicApiSequence generation, branch/company context, financial period
IAccountingPublicApiCreates and reverses Journal Entries
IPaymentOutProcessingServiceEncapsulates payment creation, posting, journal building, commission logic, balance validation

Payment In vs Payment Out — Side-by-Side

PropertyPayment InPayment Out
DirectionMoney receivedMoney sent
Typical counterpartyCustomerVendor
Bank lineDR BankCR Bank
AR/AP lineCR Accounts ReceivableDR Accounts Payable
NetAmount formulaAmount − Commission − VATAmount + Commission + VAT
Commission GL directionDR Commission, CR BankDR Commission, DR Bank total
Advanced paymentCustomer Advance (liability)Vendor Advance (asset)
Auto-reconcilesSales Invoices (on post)No — reconciled separately
Unpost requires reasonNoYes — UnpostedReason stored permanently
Balance check on addNoYes — bank/treasury balance must be sufficient
Journal typePaymentInPaymentOut

Document Version: 1.0
Namespace: AppsPortal.Application.Finance.PaymentOut
Example Date: 2026-01-28
Currency: Saudi Riyal (SAR)
Total Payment: 1,000.00 SAR

Internal Documentation — Microtec