Skip to main content

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 NameTypeRequiredDescription
PaymentIdintYesPrimary key - unique payment identifier
PaymentStatusPaymentStatusYesCurrent status (Posted, Void)
OrganizationIdintYesOrganization owning this payment (multi-tenancy)
DivisionIdintYesDivision for data partitioning
DivisionDivisionNoNavigation property to division
ApplyToContactIdintYesCustomer (payment received) or Vendor (payment made)
ApplyToContactContactNoNavigation property to customer/vendor
AmountReceiveddecimalYesTotal payment amount
CurrencyIdintYesCurrency for this payment
CurrencyCurrencyNoNavigation property to currency
PaymentDateDateTimeYesDate payment was received/made
CheckNumberstringYesCheck number, transaction ID, or reference number
MemostringYesPayment memo or notes
AccountingAccountIdintYesGL account for payment deposit/withdrawal
AccountingAccountAccountingAccountNoNavigation property to GL account
CustomValuesDictionary<string, object?>NoExtensible custom properties dictionary
CreatedDateTimeYesCreation timestamp (inherited from AuditableEntity)
CreatedBystringYesUser ID who created the payment (inherited)
LastModifiedDateTimeYesLast modification timestamp (inherited)
LastModifiedBystringYesUser ID who last modified (inherited)

PaymentStatus Enum

ValueDescriptionWhen Used
PostedActive paymentNormal status for all valid payments
VoidCancelled paymentPayment has been voided/reversed

Note: Voided payments are excluded from balance calculations in AccountingTransaction.RecalculateAmountDue().

Relationships (Navigation Properties)

RelationshipTypeDescription
AccountingTransactionsICollection<AccountingTransaction>Transactions this payment is applied to
AccountingTransactionPaymentsICollection<AccountingTransactionPayment>Junction table tracking amount applied to each transaction
DivisionDivisionParent division
ApplyToContactContactCustomer or vendor
CurrencyCurrencyCurrency for amounts
AccountingAccountAccountingAccountGL account for deposit/withdrawal
OrganizationOrganizationParent 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 amount
  • ChangePaymentDate(DateTime) - Update payment date
  • ChangeCheckNumber(string) - Update reference number
  • ChangeMemo(string) - Update memo/notes
  • ChangePaymentStatus(PaymentStatus) - Change status (Posted, Void)

Contact and Account Relationships

  • ChangeApplyToContactId(int) - Change customer/vendor
  • ChangeApplyToContact(Contact) - Set customer/vendor object
  • ChangeAccountingAccountId(int) - Change GL account
  • ChangeAccountingAccount(AccountingAccount) - Set GL account object

Currency and Division

  • ChangeCurrencyId(int) - Change currency
  • ChangeCurrency(Currency) - Set currency object
  • ChangeDivisionId(int) - Change division
  • ChangeDivision(Division) - Set division object

Transaction Application

  • ChangeAccountingTransactions(ICollection<AccountingTransaction>) - Set transaction collection
  • ChangeAccountingTransactionPayments(ICollection<AccountingTransactionPayment>) - Set payment applications
  • AddAccountingTransactionPayments(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 AmountPaid and AmountDue
  • 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