Payment
Introduction
The Payment entity represents payment records for both accounts receivable (customer payments) and accounts payable (vendor payments). Unlike charges that represent what is owed, payments represent actual money received or disbursed.
Payments are applied to one or more AccountingTransactions (invoices, bills, credit memos) through the AccountingTransactionPayment junction table, which tracks how much of the payment amount is allocated to each transaction. This design supports scenarios like:
- Partial payments on invoices
- Single payment covering multiple invoices
- Unapplied payments (prepayments held as credit)
- Payment application adjustments
The Payment entity is intentionally simple, serving primarily as a container for payment metadata (date, amount, method, reference number). The complex logic for payment application and balance tracking resides in the AccountingTransaction entity's RecalculateAmountDue() method.
Entity Structure
Properties
| Property Name | Type | Required | Description |
|---|---|---|---|
| PaymentId | int | Yes | Primary key - unique payment identifier |
| PaymentStatus | PaymentStatus | Yes | Current status (Posted, Void) |
| OrganizationId | int | Yes | Organization owning this payment (multi-tenancy) |
| DivisionId | int | Yes | Division for data partitioning |
| Division | Division | No | Navigation property to division |
| ApplyToContactId | int | Yes | Customer (payment received) or Vendor (payment made) |
| ApplyToContact | Contact | No | Navigation property to customer/vendor |
| AmountReceived | decimal | Yes | Total payment amount |
| CurrencyId | int | Yes | Currency for this payment |
| Currency | Currency | No | Navigation property to currency |
| PaymentDate | DateTime | Yes | Date payment was received/made |
| CheckNumber | string | Yes | Check number, transaction ID, or reference number |
| Memo | string | Yes | Payment memo or notes |
| AccountingAccountId | int | Yes | GL account for payment deposit/withdrawal |
| AccountingAccount | AccountingAccount | No | Navigation property to GL account |
| CustomValues | Dictionary<string, object?> | No | Extensible custom properties dictionary |
| Created | DateTime | Yes | Creation timestamp (inherited from AuditableEntity) |
| CreatedBy | string | Yes | User ID who created the payment (inherited) |
| LastModified | DateTime | Yes | Last modification timestamp (inherited) |
| LastModifiedBy | string | Yes | User ID who last modified (inherited) |
PaymentStatus Enum
| Value | Description | When Used |
|---|---|---|
| Posted | Active payment | Normal status for all valid payments |
| Void | Cancelled payment | Payment has been voided/reversed |
Note: Voided payments are excluded from balance calculations in AccountingTransaction.RecalculateAmountDue().
Relationships (Navigation Properties)
| Relationship | Type | Description |
|---|---|---|
| AccountingTransactions | ICollection<AccountingTransaction> | Transactions this payment is applied to |
| AccountingTransactionPayments | ICollection<AccountingTransactionPayment> | Junction table tracking amount applied to each transaction |
| Division | Division | Parent division |
| ApplyToContact | Contact | Customer or vendor |
| Currency | Currency | Currency for amounts |
| AccountingAccount | AccountingAccount | GL account for deposit/withdrawal |
| Organization | Organization | Parent organization |
YAML Configuration
Recording a Customer Payment (Check)
# Create payment record for customer check
- type: createEntity
entityName: Payment
properties:
organizationId: ${organizationId}
divisionId: ${divisionId}
applyToContactId: ${customerId}
amountReceived: 5000.00
currencyId: ${usdCurrencyId}
paymentDate: ${currentDate}
checkNumber: "12345"
memo: "Payment for Invoice INV-2025-001"
accountingAccountId: ${unDepositedFundsAccountId}
paymentStatus: Posted
customValues:
paymentMethod: "Check"
depositBatch: "BATCH-2025-001"
receivedBy: "Jane Smith"
resultVariable: customerPayment
# Apply payment to invoice
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${invoiceId}
parameters:
accountingTransactionPayments:
- paymentId: ${customerPayment.paymentId}
amountApplied: 5000.00
# Invoice automatically transitions to Paid status when fully paid
Recording a Vendor Payment (ACH)
# Create payment record for vendor ACH transfer
- type: createEntity
entityName: Payment
properties:
organizationId: ${organizationId}
divisionId: ${divisionId}
applyToContactId: ${vendorId}
amountReceived: 2500.00
currencyId: ${usdCurrencyId}
paymentDate: ${currentDate}
checkNumber: "ACH-${timestamp}" # Use for transaction reference
memo: "Payment for carrier invoice CARR-INV-789"
accountingAccountId: ${operatingAccountId}
paymentStatus: Posted
customValues:
paymentMethod: "ACH"
achTransactionId: "TXN-987654321"
bankConfirmation: "CONF-ABC123"
approvedBy: "Finance Manager"
resultVariable: vendorPayment
# Apply to vendor bill
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${billId}
parameters:
accountingTransactionPayments:
- paymentId: ${vendorPayment.paymentId}
amountApplied: 2500.00
Credit Card Payment
# Record credit card payment
- type: createEntity
entityName: Payment
properties:
organizationId: ${organizationId}
divisionId: ${divisionId}
applyToContactId: ${customerId}
amountReceived: 1250.00
currencyId: ${usdCurrencyId}
paymentDate: ${currentDate}
checkNumber: "CC-${last4digits}" # E.g., "CC-1234"
memo: "Credit card payment - ${cardType}"
accountingAccountId: ${merchantAccountId}
paymentStatus: Posted
customValues:
paymentMethod: "CreditCard"
cardType: "Visa"
last4Digits: "1234"
authorizationCode: "AUTH-456789"
transactionId: "TXN-CC-987654"
processorFee: 37.50 # 3% processing fee
resultVariable: creditCardPayment
Single Payment Applied to Multiple Invoices
# Customer pays $10,000 to cover 3 invoices
- type: createEntity
entityName: Payment
properties:
organizationId: ${organizationId}
divisionId: ${divisionId}
applyToContactId: ${customerId}
amountReceived: 10000.00
currencyId: ${usdCurrencyId}
paymentDate: ${currentDate}
checkNumber: "54321"
memo: "Payment for multiple invoices"
accountingAccountId: ${unDepositedFundsAccountId}
paymentStatus: Posted
resultVariable: bulkPayment
# Apply to first invoice ($4,000)
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${invoice1Id}
parameters:
accountingTransactionPayments:
- paymentId: ${bulkPayment.paymentId}
amountApplied: 4000.00
# Apply to second invoice ($3,500)
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${invoice2Id}
parameters:
accountingTransactionPayments:
- paymentId: ${bulkPayment.paymentId}
amountApplied: 3500.00
# Apply to third invoice ($2,500)
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${invoice3Id}
parameters:
accountingTransactionPayments:
- paymentId: ${bulkPayment.paymentId}
amountApplied: 2500.00
# Total applied: $4,000 + $3,500 + $2,500 = $10,000 (fully applied)
Partial Payment on Large Invoice
# Customer makes first installment payment
- type: createEntity
entityName: Payment
properties:
organizationId: ${organizationId}
divisionId: ${divisionId}
applyToContactId: ${customerId}
amountReceived: 5000.00
currencyId: ${usdCurrencyId}
paymentDate: ${currentDate}
checkNumber: "78901"
memo: "First installment - $15,000 invoice"
accountingAccountId: ${unDepositedFundsAccountId}
paymentStatus: Posted
customValues:
paymentPlan: "3 installments"
installmentNumber: 1
resultVariable: installment1
# Apply to invoice (invoice total: $15,000)
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${largeInvoiceId}
parameters:
accountingTransactionPayments:
- paymentId: ${installment1.paymentId}
amountApplied: 5000.00
# Invoice remains Open, amountDue = $10,000, amountPaid = $5,000
Prepayment (Unapplied Payment)
# Customer sends payment before invoice is created
- type: createEntity
entityName: Payment
properties:
organizationId: ${organizationId}
divisionId: ${divisionId}
applyToContactId: ${customerId}
amountReceived: 3000.00
currencyId: ${usdCurrencyId}
paymentDate: ${currentDate}
checkNumber: "99999"
memo: "Prepayment - deposit for future shipments"
accountingAccountId: ${unDepositedFundsAccountId}
paymentStatus: Posted
customValues:
paymentType: "Prepayment"
notes: "Hold as credit for future invoices"
resultVariable: prepayment
# Payment exists but is not applied to any transaction yet
# Will be applied when invoices are created later
Voiding a Payment
# Void a payment (e.g., check bounced, payment error)
- type: executeMethod
entityName: Payment
method: ChangePaymentStatus
filter:
paymentId: ${paymentId}
parameters:
paymentStatus: Void
# System behavior:
# 1. Payment marked as Void
# 2. AccountingTransaction.RecalculateAmountDue() excludes voided payments
# 3. Invoices automatically revert to Open status if payment was their only payment
# 4. Balance due increases back to original amount
Updating Payment Properties
# Update payment reference number
- type: executeMethod
entityName: Payment
method: ChangeCheckNumber
filter:
paymentId: ${paymentId}
parameters:
checkNumber: "CORRECTED-12345"
# Update payment date
- type: executeMethod
entityName: Payment
method: ChangePaymentDate
filter:
paymentId: ${paymentId}
parameters:
paymentDate: "2025-01-15"
# Update payment amount
- type: executeMethod
entityName: Payment
method: ChangeAmountReceived
filter:
paymentId: ${paymentId}
parameters:
amountReceived: 5500.00
# Update custom values
- type: executeMethod
entityName: Payment
method: ChangeCustomValues
filter:
paymentId: ${paymentId}
parameters:
customValues:
depositDate: "2025-01-16"
depositBatch: "BATCH-2025-002"
bankReconciled: true
reconciliationDate: "2025-01-20"
Querying Payment with Relationships
# Query payment with complete data
- type: queryEntity
entityName: Payment
filter:
paymentId: ${paymentId}
include:
- ApplyToContact
- Division
- Currency
- AccountingAccount
- AccountingTransactionPayments.AccountingTransaction
- AccountingTransactions
resultVariable: fullPayment
Key Methods
Property Updates
ChangeAmountReceived(decimal)- Update payment amountChangePaymentDate(DateTime)- Update payment dateChangeCheckNumber(string)- Update reference numberChangeMemo(string)- Update memo/notesChangePaymentStatus(PaymentStatus)- Change status (Posted, Void)
Contact and Account Relationships
ChangeApplyToContactId(int)- Change customer/vendorChangeApplyToContact(Contact)- Set customer/vendor objectChangeAccountingAccountId(int)- Change GL accountChangeAccountingAccount(AccountingAccount)- Set GL account object
Currency and Division
ChangeCurrencyId(int)- Change currencyChangeCurrency(Currency)- Set currency objectChangeDivisionId(int)- Change divisionChangeDivision(Division)- Set division object
Transaction Application
ChangeAccountingTransactions(ICollection<AccountingTransaction>)- Set transaction collectionChangeAccountingTransactionPayments(ICollection<AccountingTransactionPayment>)- Set payment applicationsAddAccountingTransactionPayments(AccountingTransactionPayment)- Add single payment application
Extensibility
ChangeCustomValues(Dictionary<string, object?>?)- Update custom properties (overwrites existing)
Note: Payment application logic is handled through AccountingTransaction.ChangeAccountingTransactionPayments(), not directly on Payment.
Payment Application Workflow
The Payment entity is designed to work closely with AccountingTransaction:
1. Create Payment Record
- type: createEntity
entityName: Payment
properties:
applyToContactId: ${customerId}
amountReceived: 5000.00
# ... other properties
resultVariable: payment
2. Apply to Transaction(s)
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${invoiceId}
parameters:
accountingTransactionPayments:
- paymentId: ${payment.paymentId}
amountApplied: 5000.00
3. Automatic Balance Recalculation
When ChangeAccountingTransactionPayments() is called:
- AccountingTransaction calls
RecalculateAmountDue() - Sums all payment applications (excluding Void payments)
- Updates
AmountPaidandAmountDue - Transitions status to Paid if
AmountDue <= 0 - Cascades status to associated charges
4. Voiding Payments
- type: executeMethod
entityName: Payment
method: ChangePaymentStatus
filter:
paymentId: ${payment.paymentId}
parameters:
paymentStatus: Void
When payment is voided:
- Next time
RecalculateAmountDue()runs, voided payments are excluded - Invoice balance increases (payment no longer counts)
- Invoice may transition back to Open status
Use Cases
1. Daily Payment Processing
# Morning batch: Process customer checks
- type: createEntity
entityName: Payment
properties:
applyToContactId: ${customer1Id}
amountReceived: 2500.00
paymentDate: ${currentDate}
checkNumber: "1001"
accountingAccountId: ${unDepositedFundsAccountId}
paymentStatus: Posted
customValues:
depositBatch: "BATCH-${currentDate}"
receivedBy: ${currentUserName}
resultVariable: check1
# Apply to customer's oldest invoice
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${oldestInvoiceId}
parameters:
accountingTransactionPayments:
- paymentId: ${check1.paymentId}
amountApplied: 2500.00
# Repeat for all checks in the batch...
# End of day: Update batch status
- type: executeMethod
entityName: Payment
method: ChangeCustomValues
filter:
customValues.depositBatch: "BATCH-${currentDate}"
parameters:
customValues:
batchStatus: "Deposited"
depositedDate: ${currentDate}
depositSlipNumber: "SLIP-12345"
2. Vendor Bill Payment with Discount
# Vendor offers 2% discount for payment within 10 days
# Bill amount: $10,000
# Discount: $200 (2%)
# Payment amount: $9,800
# Create payment for discounted amount
- type: createEntity
entityName: Payment
properties:
applyToContactId: ${vendorId}
amountReceived: 9800.00
paymentDate: ${currentDate}
checkNumber: "CHECK-5001"
accountingAccountId: ${operatingAccountId}
paymentStatus: Posted
customValues:
discountTaken: 200.00
discountPercent: 0.02
earlyPaymentDiscount: true
resultVariable: discountedPayment
# Apply full payment amount
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${billId}
parameters:
accountingTransactionPayments:
- paymentId: ${discountedPayment.paymentId}
amountApplied: 9800.00
# Create credit memo for discount
- type: createEntity
entityName: AccountingTransaction
properties:
accountingTransactionType: CreditMemo
applyToContactID: ${vendorId}
transactionNumber: "CM-DISCOUNT-${sequence}"
transactionDate: ${currentDate}
resultVariable: discountCredit
# Add discount charge (negative)
- type: executeMethod
entityName: AccountingTransaction
method: AddCharges
filter:
accountingTransactionId: ${discountCredit.accountingTransactionId}
parameters:
charges:
- chargeTypeId: ${earlyPaymentDiscountTypeId}
amount: -200.00
description: "2% early payment discount"
# Apply credit memo to bill
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${billId}
parameters:
accountingTransactionPayments:
- paymentId: ${discountedPayment.paymentId}
amountApplied: 9800.00
- accountingTransactionId: ${discountCredit.accountingTransactionId}
amountApplied: 200.00
# Bill is now fully paid: $9,800 + $200 = $10,000
3. Payment Plan Management
# Setup: Invoice for $30,000, 3 monthly installments
# Month 1: First payment
- type: createEntity
entityName: Payment
properties:
applyToContactId: ${customerId}
amountReceived: 10000.00
paymentDate: "2025-01-15"
checkNumber: "INSTALL-1"
accountingAccountId: ${unDepositedFundsAccountId}
paymentStatus: Posted
customValues:
paymentPlanId: "PLAN-2025-001"
installmentNumber: 1
totalInstallments: 3
resultVariable: payment1
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${invoiceId}
parameters:
accountingTransactionPayments:
- paymentId: ${payment1.paymentId}
amountApplied: 10000.00
# Balance: $20,000 remaining
# Month 2: Second payment
- type: createEntity
entityName: Payment
properties:
applyToContactId: ${customerId}
amountReceived: 10000.00
paymentDate: "2025-02-15"
checkNumber: "INSTALL-2"
accountingAccountId: ${unDepositedFundsAccountId}
paymentStatus: Posted
customValues:
paymentPlanId: "PLAN-2025-001"
installmentNumber: 2
totalInstallments: 3
resultVariable: payment2
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${invoiceId}
parameters:
accountingTransactionPayments:
- paymentId: ${payment1.paymentId}
amountApplied: 10000.00
- paymentId: ${payment2.paymentId}
amountApplied: 10000.00
# Balance: $10,000 remaining
# Month 3: Final payment
- type: createEntity
entityName: Payment
properties:
applyToContactId: ${customerId}
amountReceived: 10000.00
paymentDate: "2025-03-15"
checkNumber: "INSTALL-3"
accountingAccountId: ${unDepositedFundsAccountId}
paymentStatus: Posted
customValues:
paymentPlanId: "PLAN-2025-001"
installmentNumber: 3
totalInstallments: 3
paymentPlanCompleted: true
resultVariable: payment3
- type: executeMethod
entityName: AccountingTransaction
method: ChangeAccountingTransactionPayments
filter:
accountingTransactionId: ${invoiceId}
parameters:
accountingTransactionPayments:
- paymentId: ${payment1.paymentId}
amountApplied: 10000.00
- paymentId: ${payment2.paymentId}
amountApplied: 10000.00
- paymentId: ${payment3.paymentId}
amountApplied: 10000.00
# Invoice automatically transitions to Paid, balance: $0
Best Practices
1. CheckNumber Field Usage
- Not just for checks: Use for any payment reference (ACH transaction ID, wire reference, credit card auth code)
- Store meaningful identifiers: "CHECK-12345", "ACH-TXN-987654", "CC-VISA-1234"
- Include in payment memo for easy reconciliation
2. Payment vs Payment Application
- Payment: The money received/disbursed (single record per check, ACH, etc.)
- PaymentApplication: How that money is allocated to invoices/bills (AccountingTransactionPayment junction)
- One payment can apply to multiple transactions
- Multiple payments can apply to one transaction
3. Unapplied Payments
- Payments can exist without being applied to any transaction (prepayments, deposits)
- Track unapplied amounts in custom values or through reporting
- Apply when invoices are created later
4. Payment Status Management
- Use Posted for all valid payments
- Use Void for cancelled/reversed payments
- Don't delete payments - voiding preserves audit trail
- Voided payments are excluded from balance calculations automatically
5. Custom Values for Payment Details
- Store payment method (Check, ACH, Wire, CreditCard, Cash)
- Track batch information for deposit reconciliation
- Record authorization codes, transaction IDs for electronic payments
- Store processing fees, bank references, reconciliation status
6. GL Account Selection
- Accounts Receivable: Use "Undeposited Funds" or "Cash Clearing" account initially
- Update to "Bank Account" when deposited
- Accounts Payable: Use "Operating Account" or specific bank account
- Track payment method in custom values, not through different GL accounts
7. Payment Application Order
- Apply payments to oldest invoices first (FIFO) unless customer specifies
- Document application logic in Memo field
- Use custom values to track application preferences
8. Partial Payments
- System fully supports partial payments through AccountingTransactionPayment
- Invoice remains Open until fully paid
- Each application tracked separately with amountApplied
9. Payment Reversal
- Void the original payment (ChangePaymentStatus to Void)
- Create new payment if correction is needed
- Document reason in Memo field
- Use custom values to link reversed payment to original
10. Currency Handling
- Payment currency should match invoice currency for proper application
- Multi-currency payments require conversion logic in application layer
- Store exchange rate information in custom values
11. Date Tracking
- PaymentDate: When payment was received/made
- Store deposit date in custom values
- Track reconciliation date separately
- Use audit fields (Created, LastModified) for system tracking
12. Batch Processing
- Use custom values to group payments by batch (depositBatch, processingBatch)
- Enable batch reconciliation and reporting
- Update batch status as payments progress through workflow
Related Topics
- AccountingTransaction Entity - Invoices and bills receiving payments
- AccountingTransactionPayment Entity - Junction table for payment application
- Contact Entity - Customers and vendors
- AccountingAccount Entity - GL accounts for payment deposit/withdrawal
- Currency Entity - Currency definitions
- Division Entity - Organizational divisions
- Entity System Overview - Aggregate root patterns and architecture