Skip to main content

Accounting Transaction

Introduction

The AccountingTransaction entity is a polymorphic aggregate root that represents all financial documents in the system - invoices (accounts receivable), bills (accounts payable), and credit memos. Unlike most entities that represent a single concept, AccountingTransaction uses the AccountingTransactionType enum to differentiate between these three document types, each serving a distinct business purpose.

AccountingTransaction provides intelligent payment tracking with automatic recalculation of balances, status transitions, and charge management. It supports draft mode for work-in-progress documents, payment term integration for automatic due date calculation, job linkage for consolidated billing, and custom values for extensibility. The entity automatically transitions between Open and Paid statuses based on payment application, cascading status changes to associated charges.

Entity Structure

Properties

Property NameTypeRequiredDescription
AccountingTransactionIdintYesPrimary key - unique transaction identifier
AccountingTransactionTypeAccountingTransactionTypeYesType of transaction (Invoice, Bill, CreditMemo)
AccountingTransactionStatusAccountingTransactionStatusYesCurrent status (Open, Paid, Void)
OrganizationIdintYesOrganization owning this transaction (multi-tenancy)
DivisionIdintYesDivision for data partitioning
DivisionDivisionNoNavigation property to division
TransactionNumberstringYesHuman-readable transaction number (e.g., INV-2025-001)
TransactionDateDateTimeYesDate transaction was created/posted
DueDateDateTimeYesPayment due date (calculated from payment terms)
PaidDateDateTime?NoDate transaction was fully paid (set automatically)
ApplyToContactIDint?NoCustomer (Invoice) or Vendor (Bill) contact
ApplyToContactContactNoNavigation property to customer/vendor
BillToContactAddressIdint?NoBilling address for this transaction
BillToContactAddressContactAddressNoNavigation property to billing address
AccountIdint?NoGL account for posting
AccountAccountingAccountNoNavigation property to GL account
PaymentTermsIdint?NoPayment terms for this transaction
PaymentTermPaymentTermNoNavigation property to payment terms
AmountDuedecimalYesCurrent balance due (Total charges - Total payments)
AmountPaiddecimalYesTotal amount paid against this transaction
PaidAsPaidAsYesPrepaid or Collect (freight payment method)
Notestring?NoInternal notes or comments
IsDraftboolYesWhether transaction is draft (default: false)
CustomValuesDictionary<string, object?>NoExtensible custom properties dictionary
CreatedDateTimeYesCreation timestamp (inherited from AuditableEntity)
CreatedBystringYesUser ID who created the transaction (inherited)
LastModifiedDateTimeYesLast modification timestamp (inherited)
LastModifiedBystringYesUser ID who last modified (inherited)

AccountingTransactionType Enum

Determines the nature and purpose of the financial document:

ValueDescriptionBusiness PurposeAffects
InvoiceAccounts Receivable documentBill customer for services renderedIncreases customer AR balance
BillAccounts Payable documentRecord vendor charges to payIncreases vendor AP balance
CreditMemoAccounts Receivable adjustmentReduce customer balance (refund, discount, correction)Decreases customer AR balance

AccountingTransactionStatus Enum

Represents the payment lifecycle:

ValueDescriptionTransition Rules
OpenHas outstanding balance dueInitial status when charges exist; automatic transition from Paid when new charges added
PaidFully paid, no balance dueAutomatic transition when AmountDue <= 0 after payment application
VoidCancelled transactionManual void; AmountDue and AmountPaid set to 0

Relationships (Navigation Properties)

RelationshipTypeDescription
ChargesICollection<Charge>Charges included in this transaction
AccountingTransactionChargesICollection<AccountingTransactionCharges>Junction table for transaction-charge relationships
PaymentsICollection<Payment>Payments applied to this transaction
AccountingTransactionPaymentsICollection<AccountingTransactionPayment>Junction table for transaction-payment relationships with amount applied
JobsICollection<Job>Jobs associated with this transaction
JobAccountingTransactionsICollection<JobAccountingTransaction>Junction table for job-transaction relationships
DivisionDivisionParent division
AccountAccountingAccountGL account for posting
PaymentTermPaymentTermPayment terms
ApplyToContactContactCustomer (Invoice) or Vendor (Bill)
BillToContactAddressContactAddressBilling address
OrganizationOrganizationParent organization

YAML Configuration

Creating an Invoice

# Create customer invoice with charges
- task: AccountingTransaction/Generate@1
name: createCustomerInvoice
inputs:
organizationId: ${organizationId}
accountingTransaction:
accountingTransactionType: Invoice
accountingTransactionStatus: Open
divisionId: ${divisionId}
transactionNumber: "INV-2025-${autoIncrement}"
transactionDate: ${currentDate}
applyToContactID: ${customerId}
billToContactAddressId: ${billingAddressId}
paymentTermsId: ${net30TermId}
paidAs: Prepaid
note: "Freight charges for January 2025 shipments"
isDraft: false
customValues:
invoicePeriod: "2025-01"
salesRep: "John Doe"
customerPO: "PO-12345"
charges:
- chargeTypeId: ${freightChargeTypeId}
amount: 1500.00
quantity: 1
description: "Ocean freight Shanghai to Los Angeles"
- chargeTypeId: ${fuelSurchargeTypeId}
amount: 225.00
quantity: 1
description: "Fuel surcharge (15%)"
- chargeTypeId: ${customsChargeTypeId}
amount: 350.00
quantity: 1
description: "Customs clearance fee"
outputs:
- accountingTransaction: invoice

Creating a Bill (Accounts Payable)

# Create vendor bill with charges
- task: AccountingTransaction/Generate@1
name: createCarrierBill
inputs:
organizationId: ${organizationId}
accountingTransaction:
accountingTransactionType: Bill
accountingTransactionStatus: Open
divisionId: ${divisionId}
transactionNumber: "BILL-2025-${autoIncrement}"
transactionDate: ${currentDate}
applyToContactID: ${carrierId}
paymentTermsId: ${net15TermId}
paidAs: Collect
note: "Carrier charges for shipment XYZ-123"
customValues:
carrierInvoiceNumber: "CARR-INV-789"
proNumber: "PRO-456123"
charges:
- chargeTypeId: ${lineHaulChargeTypeId}
amount: 1200.00
description: "Line haul charges"
- chargeTypeId: ${fuelSurchargeTypeId}
amount: 180.00
description: "Fuel surcharge"
outputs:
- accountingTransaction: carrierBill

Creating a Credit Memo

# Create credit memo for damaged goods
- task: AccountingTransaction/Generate@1
name: createCreditMemo
inputs:
organizationId: ${organizationId}
accountingTransaction:
accountingTransactionType: CreditMemo
accountingTransactionStatus: Open
divisionId: ${divisionId}
transactionNumber: "CM-2025-${autoIncrement}"
transactionDate: ${currentDate}
applyToContactID: ${customerId}
paymentTermsId: ${net30TermId}
note: "Credit for damaged merchandise - claim #DMG-2025-001"
customValues:
originalInvoice: "INV-2025-123"
claimNumber: "DMG-2025-001"
approvedBy: "Operations Manager"
charges:
- chargeTypeId: ${creditAdjustmentTypeId}
amount: -500.00
description: "Credit for damaged goods"
outputs:
- accountingTransaction: creditMemo

Applying Payments to Invoice

# Create payment record
- task: Payment/Create@1
name: createPayment
inputs:
organizationId: ${organizationId}
payment:
divisionId: ${divisionId}
paymentNumber: "PMT-2025-${autoIncrement}"
paymentDate: ${currentDate}
paymentAmount: 2075.00
paymentMethodId: ${checkPaymentMethodId}
applyToContactId: ${customerId}
note: "Check #12345"
customValues:
checkNumber: "12345"
depositDate: ${currentDate}
outputs:
- payment: payment

# Apply payment to invoice
- task: AccountingTransaction/Update@1
name: applyPaymentToInvoice
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${invoice.accountingTransactionId}
accountingTransaction:
accountingTransactionPayments:
- paymentId: ${payment.paymentId}
amountApplied: 2075.00 # Full invoice amount

# Invoice status automatically updated to "Paid"
# amountPaid = 2075.00, amountDue = 0.00, paidDate set automatically

Partial Payment Application

# Apply partial payments to large invoice
- task: AccountingTransaction/Update@1
name: applyPartialPayments
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${largeInvoice.accountingTransactionId}
accountingTransaction:
accountingTransactionPayments:
- paymentId: ${payment1.paymentId}
amountApplied: 1000.00
- paymentId: ${payment2.paymentId}
amountApplied: 500.00
# If invoice total is $2500, amountDue will be $1000, status remains Open

Linking Transaction to Jobs

# Link invoice to multiple jobs (consolidated billing)
- task: AccountingTransaction/Update@1
name: linkInvoiceToJobs
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${invoice.accountingTransactionId}
accountingTransaction:
jobAccountingTransactions:
- jobId: ${job1Id}
- jobId: ${job2Id}
- jobId: ${job3Id}

Draft Invoice Workflow

# Create draft invoice for review
- task: AccountingTransaction/Generate@1
name: createDraftInvoice
inputs:
organizationId: ${organizationId}
accountingTransaction:
accountingTransactionType: Invoice
accountingTransactionStatus: Open
divisionId: ${divisionId}
transactionNumber: "DRAFT-INV-${timestamp}"
transactionDate: ${currentDate}
applyToContactID: ${customerId}
isDraft: true
note: "Draft invoice - pending review"
charges: ${pendingCharges}
outputs:
- accountingTransaction: draftInvoice

# Later: Finalize the invoice
- task: AccountingTransaction/Update@1
name: finalizeInvoice
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${draftInvoice.accountingTransactionId}
accountingTransaction:
isDraft: false
transactionNumber: "INV-2025-${finalSequence}"

Querying Transaction with All Relationships

# Query invoice with complete data using GraphQL
- task: Query/GraphQL
name: getInvoiceWithRelationships
inputs:
organizationId: ${organizationId}
query: |
query GetInvoice($invoiceId: ID!) {
accountingTransaction(id: $invoiceId) {
accountingTransactionId
transactionNumber
transactionDate
accountingTransactionType
accountingTransactionStatus
amountDue
amountPaid
charges { description amount chargeType { name } }
accountingTransactionCharges { charge { description amount } }
payments { paymentNumber paymentAmount paymentDate }
accountingTransactionPayments { payment { paymentNumber } amountApplied }
applyToContact { name contactAddresses { addressLine cityName } }
billToContactAddress { addressLine cityName stateCode }
division { divisionName }
paymentTerm { termName }
account { name accountNumber }
jobAccountingTransactions { job { jobNumber } }
}
}
variables:
invoiceId: ${invoiceId}
outputs:
- response.accountingTransaction: fullInvoice

Voiding a Transaction

# Void an invoice
- task: AccountingTransaction/Update@1
name: voidInvoice
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${invoiceId}
accountingTransaction:
accountingTransactionStatus: Void
# System automatically sets AmountDue = 0, AmountPaid = 0 during recalculation

Updating Transaction Properties

# Update invoice properties in a single call
- task: AccountingTransaction/Update@1
name: updateInvoiceProperties
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${invoiceId}
accountingTransaction:
dueDate: "2025-02-15"
applyToContactID: ${newCustomerId}
customValues:
exportLicense: "EXP-2025-789"
shipmentReference: "SHIP-ABC-123"
approvalStatus: "approved"
approvedBy: "Finance Manager"
approvalDate: ${currentDate}

Key Methods

The AccountingTransaction entity provides comprehensive methods for managing the aggregate:

Charge Management

  • AddCharge(Charge) - Add single charge to transaction
  • AddCharges(List<Charge>) - Add multiple charges, set status to Posted, trigger recalculation
  • ChangeCharges(IEnumerable<Charge>) - Replace charge collection, trigger recalculation
  • AddAccountingTransactionCharge(int) - Create junction entry for charge

Payment Management

  • ChangeAccountingTransactionPayments(ICollection<AccountingTransactionPayment>) - Set payment applications, trigger recalculation
  • RemoveAccountingTransactionPayments(AccountingTransactionPayment) - Remove payment application, trigger recalculation
  • RecalculateAmountDue() - Core method: Recalculate balance, auto-transition status, cascade charge statuses

Job Linkage

  • ChangeJobId(Guid?) - Link single job to transaction (prevents duplicates)
  • ChangeJobIds(IEnumerable<Guid>?) - Link multiple jobs to transaction

Property Updates (Basic Information)

  • ChangeTransactionNumber(string) - Update transaction number
  • ChangeTransactionDate(DateTime) - Update transaction date (UTC conversion)
  • ChangeDueDate(DateTime?) - Update due date (UTC conversion)
  • ChangePaidDate(DateTime?) - Update paid date (usually set automatically)
  • ChangeNote(string?) - Update notes

Type and Status Management

  • ChangeAccountingTransactionType(AccountingTransactionType) - Change type (Invoice, Bill, CreditMemo)
  • ChangeAccountingTransactionStatus(AccountingTransactionStatus) - Change status (Open, Paid, Void)
  • ChangeIsDraft(bool) - Toggle draft mode

Financial Settings

  • ChangeAmountDue(decimal) - Manually set amount due (usually calculated automatically)
  • ChangeAmountPaid(decimal) - Manually set amount paid (usually calculated automatically)
  • ChangePaidAs(PaidAs) - Set prepaid or collect

Contact and Account Relationships

  • ChangeApplyToContactID(int?) - Set customer (Invoice) or vendor (Bill)
  • ChangeApplyToContact(Contact?) - Set contact object, recalculate due date
  • ChangeBillToContactAddressId(int?) - Set billing address
  • ChangeBillToContactAddress(ContactAddress?) - Set billing address object
  • ChangeAccountId(int?) - Set GL account
  • ChangeAccount(AccountingAccount?) - Set GL account object

Payment Terms

  • ChangePaymentTermsId(int?) - Set payment terms
  • ChangePaymentTerms(PaymentTerm?) - Set payment term object, recalculate due date

Organizational Structure

  • ChangeDivisionId(int) - Change division
  • ChangeDivision(Division) - Set division object

Extensibility

  • ChangeCustomValues(Dictionary<string, object?>?) - Update custom properties (merges with existing)

Automatic Calculation Intelligence

RecalculateAmountDue() - The Heart of Payment Tracking

This method is automatically triggered when charges or payments change. It implements sophisticated business logic:

Calculation Logic:

  1. Sum Total Charges: Sum all charges excluding voided ones
  2. Sum Total Payments: Sum all payment applications excluding voided payments
  3. Calculate Balance: AmountDue = TotalCharges - TotalPayments
  4. Update AmountPaid: Set to total payment applications

Automatic Status Transitions:

  • Transition to Paid: When AmountDue <= 0 and payments exist:

    • Set AccountingTransactionStatus = Paid
    • Set PaidDate to latest payment date (if not already set)
    • Cascade charge statuses to Paid
  • Transition to Open: When AmountDue > 0 and charges exist:

    • Set AccountingTransactionStatus = Open
    • Cascade charge statuses to Posted
    • Clear PaidDate
  • Void Handling: When AccountingTransactionStatus = Void:

    • Set AmountDue = 0
    • Set AmountPaid = 0

Charge Status Validation in AddCharges():

  • Rejects charges with status Paid or Void
  • Sets charge status to Posted when added
  • Automatically recalculates balance

SetDueDate() - Intelligent Due Date Calculation

Automatically calculates due date when payment terms or contact changes:

Calculation Priority:

  1. Use transaction's PaymentTerm NetDueDays if set
  2. Fall back to ApplyToContact's PaymentTerm NetDueDays
  3. Default to 0 days (due immediately)

Formula: DueDate = TransactionDate + NetDueDays

Use Cases

1. Monthly Customer Invoicing

# Create invoice for monthly recurring customer with charges and jobs
- task: AccountingTransaction/Generate@1
name: createMonthlyInvoice
inputs:
organizationId: ${organizationId}
accountingTransaction:
accountingTransactionType: Invoice
accountingTransactionStatus: Open
divisionId: ${divisionId}
transactionNumber: "INV-MONTHLY-${customerId}-${month}"
transactionDate: ${monthEndDate}
applyToContactID: ${customerId}
paymentTermsId: ${net30TermId}
note: "Monthly freight charges - ${monthYear}"
customValues:
invoicePeriod: ${monthYear}
billingType: "monthly-recurring"
charges: ${monthCharges} # List of all charges for the month
jobAccountingTransactions: ${completedJobIds}
outputs:
- accountingTransaction: monthlyInvoice

2. Carrier Bill Processing with Payment

# Create carrier bill with charges
- task: AccountingTransaction/Generate@1
name: createCarrierBill
inputs:
organizationId: ${organizationId}
accountingTransaction:
accountingTransactionType: Bill
accountingTransactionStatus: Open
divisionId: ${divisionId}
transactionNumber: "BILL-${carrierInvoiceNumber}"
transactionDate: ${carrierInvoiceDate}
applyToContactID: ${carrierId}
paymentTermsId: ${quickPayTermId}
customValues:
carrierInvoiceNumber: ${carrierInvoiceNumber}
carrierName: "ABC Freight Lines"
receivedDate: ${currentDate}
approvalRequired: true
charges: ${carrierChargeList}
outputs:
- accountingTransaction: carrierBill

# After approval, create and apply payment
- task: Payment/Create@1
name: payCarrierBill
inputs:
organizationId: ${organizationId}
payment:
divisionId: ${divisionId}
paymentNumber: "CHECK-${checkNumber}"
paymentDate: ${paymentDate}
paymentAmount: ${carrierBill.amountDue}
paymentMethodId: ${achPaymentMethodId}
applyToContactId: ${carrierId}
customValues:
achTransactionId: "ACH-${transactionId}"
outputs:
- payment: carrierPayment

# Apply payment to bill
- task: AccountingTransaction/Update@1
name: applyPaymentToBill
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${carrierBill.accountingTransactionId}
accountingTransaction:
accountingTransactionPayments:
- paymentId: ${carrierPayment.paymentId}
amountApplied: ${carrierBill.amountDue}
# Bill status automatically transitions to Paid

3. Progressive Payment on Large Invoice

# Create large project invoice with all charges
- task: AccountingTransaction/Generate@1
name: createProjectInvoice
inputs:
organizationId: ${organizationId}
accountingTransaction:
accountingTransactionType: Invoice
accountingTransactionStatus: Open
divisionId: ${divisionId}
transactionNumber: "INV-PROJECT-${projectId}"
transactionDate: ${currentDate}
applyToContactID: ${customerId}
paymentTermsId: ${net45TermId}
note: "Project shipment - Factory relocation"
customValues:
projectId: ${projectId}
totalShipments: 50
paymentSchedule: "Progressive - 3 installments"
charges: ${allProjectCharges} # Total: $150,000
outputs:
- accountingTransaction: projectInvoice

# Apply first payment (deposit - 33%)
- task: AccountingTransaction/Update@1
name: applyDepositPayment
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${projectInvoice.accountingTransactionId}
accountingTransaction:
accountingTransactionPayments:
- paymentId: ${payment1.paymentId}
amountApplied: 50000.00
# Invoice status remains Open, amountDue = $100,000

# Apply second payment (mid-project - 33%)
- task: AccountingTransaction/Update@1
name: applyMidProjectPayment
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${projectInvoice.accountingTransactionId}
accountingTransaction:
accountingTransactionPayments:
- paymentId: ${payment1.paymentId}
amountApplied: 50000.00
- paymentId: ${payment2.paymentId}
amountApplied: 50000.00
# Invoice status remains Open, amountDue = $50,000

# Apply final payment (completion - 34%)
- task: AccountingTransaction/Update@1
name: applyFinalPayment
inputs:
organizationId: ${organizationId}
accountingTransactionId: ${projectInvoice.accountingTransactionId}
accountingTransaction:
accountingTransactionPayments:
- paymentId: ${payment1.paymentId}
amountApplied: 50000.00
- paymentId: ${payment2.paymentId}
amountApplied: 50000.00
- paymentId: ${payment3.paymentId}
amountApplied: 50000.00
# Invoice status automatically transitions to Paid, amountDue = $0

Best Practices

1. Transaction Type Usage

  • Use Invoice for billing customers (accounts receivable)
  • Use Bill for recording vendor charges (accounts payable)
  • Use CreditMemo for customer refunds, discounts, or billing corrections
  • Never change AccountingTransactionType after creation - create new transaction instead

2. Automatic vs Manual Recalculation

  • Trust automatic recalculation - RecalculateAmountDue() is triggered when charges or payments change
  • Avoid manually setting AmountDue or AmountPaid unless you have specific business logic
  • Status transitions happen automatically - don't manually set to Paid

3. Payment Term Integration

  • Set PaymentTermsId on transaction OR rely on contact's default payment term
  • Due date is calculated automatically when payment term changes
  • Override due date with ChangeDueDate() only when business rules require it

4. Draft Invoices

  • Use isDraft: true for work-in-progress invoices
  • Draft invoices can have incomplete data or placeholder transaction numbers
  • Finalize with ChangeIsDraft(false) and update TransactionNumber before sending to customer

5. Charge Status Cascading

  • When transaction is Paid, all charges cascade to Paid status
  • When transaction returns to Open (new charges added), charges cascade to Posted
  • Trust the automatic cascading - don't manually manage charge statuses

6. Partial Payments

  • Apply multiple payments to a single invoice using AccountingTransactionPayment junction
  • Specify amountApplied for each payment (can be less than full payment amount)
  • System tracks balance automatically - transaction stays Open until fully paid

7. Job Consolidation

  • Link invoices to multiple jobs for consolidated billing using ChangeJobIds()
  • Enables reporting by job or by invoice
  • Master bill scenarios: single invoice, multiple jobs

8. Custom Values Strategy

  • Store industry-specific fields (export license, shipment reference, claim number) in CustomValues
  • Track approval workflow status in CustomValues
  • Link to external system identifiers (ERP invoice ID, carrier invoice number)

9. Void vs Delete

  • Use ChangeAccountingTransactionStatus(Void) instead of deleting transactions
  • Voided transactions remain in system for audit trail
  • Voided transactions set AmountDue and AmountPaid to 0 automatically
  • Create reversing entries (CreditMemo) for customer-facing corrections instead of voiding

10. Billing Address Management

  • Set BillToContactAddressId to specific address for this transaction
  • Falls back to contact's default billing address if not specified
  • Supports scenarios where customer has multiple locations with different billing addresses

11. GL Account Integration

  • Set AccountId for automatic GL posting
  • Different transaction types typically post to different accounts (AR for Invoice, AP for Bill)
  • AccountId can be set based on transaction type, charge type, or business rules

12. Contact Relationship

  • ApplyToContactID is the customer for Invoices, vendor for Bills
  • Contact's payment term is used for due date calculation if transaction has no PaymentTermId
  • Changing ApplyToContact triggers due date recalculation