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 Name | Type | Required | Description |
|---|---|---|---|
| AccountingTransactionId | int | Yes | Primary key - unique transaction identifier |
| AccountingTransactionType | AccountingTransactionType | Yes | Type of transaction (Invoice, Bill, CreditMemo) |
| AccountingTransactionStatus | AccountingTransactionStatus | Yes | Current status (Open, Paid, Void) |
| OrganizationId | int | Yes | Organization owning this transaction (multi-tenancy) |
| DivisionId | int | Yes | Division for data partitioning |
| Division | Division | No | Navigation property to division |
| TransactionNumber | string | Yes | Human-readable transaction number (e.g., INV-2025-001) |
| TransactionDate | DateTime | Yes | Date transaction was created/posted |
| DueDate | DateTime | Yes | Payment due date (calculated from payment terms) |
| PaidDate | DateTime? | No | Date transaction was fully paid (set automatically) |
| ApplyToContactID | int? | No | Customer (Invoice) or Vendor (Bill) contact |
| ApplyToContact | Contact | No | Navigation property to customer/vendor |
| BillToContactAddressId | int? | No | Billing address for this transaction |
| BillToContactAddress | ContactAddress | No | Navigation property to billing address |
| AccountId | int? | No | GL account for posting |
| Account | AccountingAccount | No | Navigation property to GL account |
| PaymentTermsId | int? | No | Payment terms for this transaction |
| PaymentTerm | PaymentTerm | No | Navigation property to payment terms |
| AmountDue | decimal | Yes | Current balance due (Total charges - Total payments) |
| AmountPaid | decimal | Yes | Total amount paid against this transaction |
| PaidAs | PaidAs | Yes | Prepaid or Collect (freight payment method) |
| Note | string? | No | Internal notes or comments |
| IsDraft | bool | Yes | Whether transaction is draft (default: false) |
| 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 transaction (inherited) |
| LastModified | DateTime | Yes | Last modification timestamp (inherited) |
| LastModifiedBy | string | Yes | User ID who last modified (inherited) |
AccountingTransactionType Enum
Determines the nature and purpose of the financial document:
| Value | Description | Business Purpose | Affects |
|---|---|---|---|
| Invoice | Accounts Receivable document | Bill customer for services rendered | Increases customer AR balance |
| Bill | Accounts Payable document | Record vendor charges to pay | Increases vendor AP balance |
| CreditMemo | Accounts Receivable adjustment | Reduce customer balance (refund, discount, correction) | Decreases customer AR balance |
AccountingTransactionStatus Enum
Represents the payment lifecycle:
| Value | Description | Transition Rules |
|---|---|---|
| Open | Has outstanding balance due | Initial status when charges exist; automatic transition from Paid when new charges added |
| Paid | Fully paid, no balance due | Automatic transition when AmountDue <= 0 after payment application |
| Void | Cancelled transaction | Manual void; AmountDue and AmountPaid set to 0 |
Relationships (Navigation Properties)
| Relationship | Type | Description |
|---|---|---|
| Charges | ICollection<Charge> | Charges included in this transaction |
| AccountingTransactionCharges | ICollection<AccountingTransactionCharges> | Junction table for transaction-charge relationships |
| Payments | ICollection<Payment> | Payments applied to this transaction |
| AccountingTransactionPayments | ICollection<AccountingTransactionPayment> | Junction table for transaction-payment relationships with amount applied |
| Jobs | ICollection<Job> | Jobs associated with this transaction |
| JobAccountingTransactions | ICollection<JobAccountingTransaction> | Junction table for job-transaction relationships |
| Division | Division | Parent division |
| Account | AccountingAccount | GL account for posting |
| PaymentTerm | PaymentTerm | Payment terms |
| ApplyToContact | Contact | Customer (Invoice) or Vendor (Bill) |
| BillToContactAddress | ContactAddress | Billing address |
| Organization | Organization | Parent 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 transactionAddCharges(List<Charge>)- Add multiple charges, set status to Posted, trigger recalculationChangeCharges(IEnumerable<Charge>)- Replace charge collection, trigger recalculationAddAccountingTransactionCharge(int)- Create junction entry for charge
Payment Management
ChangeAccountingTransactionPayments(ICollection<AccountingTransactionPayment>)- Set payment applications, trigger recalculationRemoveAccountingTransactionPayments(AccountingTransactionPayment)- Remove payment application, trigger recalculationRecalculateAmountDue()- 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 numberChangeTransactionDate(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 dateChangeBillToContactAddressId(int?)- Set billing addressChangeBillToContactAddress(ContactAddress?)- Set billing address objectChangeAccountId(int?)- Set GL accountChangeAccount(AccountingAccount?)- Set GL account object
Payment Terms
ChangePaymentTermsId(int?)- Set payment termsChangePaymentTerms(PaymentTerm?)- Set payment term object, recalculate due date
Organizational Structure
ChangeDivisionId(int)- Change divisionChangeDivision(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:
- Sum Total Charges: Sum all charges excluding voided ones
- Sum Total Payments: Sum all payment applications excluding voided payments
- Calculate Balance:
AmountDue = TotalCharges - TotalPayments - Update AmountPaid: Set to total payment applications
Automatic Status Transitions:
-
Transition to Paid: When
AmountDue <= 0and payments exist:- Set
AccountingTransactionStatus = Paid - Set
PaidDateto latest payment date (if not already set) - Cascade charge statuses to
Paid
- Set
-
Transition to Open: When
AmountDue > 0and charges exist:- Set
AccountingTransactionStatus = Open - Cascade charge statuses to
Posted - Clear
PaidDate
- Set
-
Void Handling: When
AccountingTransactionStatus = Void:- Set
AmountDue = 0 - Set
AmountPaid = 0
- Set
Charge Status Validation in AddCharges():
- Rejects charges with status
PaidorVoid - Sets charge status to
Postedwhen added - Automatically recalculates balance
SetDueDate() - Intelligent Due Date Calculation
Automatically calculates due date when payment terms or contact changes:
Calculation Priority:
- Use transaction's PaymentTerm NetDueDays if set
- Fall back to ApplyToContact's PaymentTerm NetDueDays
- 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: truefor 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
amountAppliedfor 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
Related Topics
- Charge Entity - Individual charges on transactions
- AccountingTransactionCharge Entity - Junction table for transaction-charge relationships
- Payment Entity - Payment records
- AccountingTransactionPayment Entity - Junction table for payment application with amount
- JobAccountingTransaction Entity - Junction table for job-transaction relationships
- Contact Entity - Customers and vendors
- PaymentTerm Entity - Payment terms (Net 30, etc.)
- AccountingAccount Entity - GL accounts for posting
- Job Entity - Jobs for consolidated billing
- Division Entity - Organizational divisions
- Entity System Overview - Aggregate root patterns and architecture