Finance — Core concepts
Core objects
| Concept | Meaning |
|---|---|
| Billing | Aggregate that groups charge obligations from booking/subscription; owns the payment option, split context, and data to generate invoices. |
| BillingPeriod | Financial period or stage used to group lines, especially the subscription billing schedule. |
| Invoice | Receivable document sent to the customer/account; has header, lines, tax/discount, status, and a scheduler that processes it. |
| InvoiceLine | Specific fee line: session, add-on, discount, adjustment, or booking-related metadata. |
| CreditNote | Document that reverses an invoice when money is reduced, cancelled, or the booking is edited after the invoice has been issued/paid. |
| FinanceTransaction | Projection for current-state queries on invoice/credit-note/payment; keeps list/search fast and stable. |
| InvoiceScheduler | Job/scheduler that issues or processes invoices at the right time. |
| Preview Billing Difference | Read-only computation for booking edits: computes the expected invoice/credit-note without persisting. |
| Accounting Provider | Accounting provider such as Xero; receives invoice/credit-note/payment sync from AIMY. |
Billing vs Invoice
Billing is the charging context; Invoice is the document the customer sees, finance operates on. One billing can create multiple invoices when the split rule splits by org, attendee, category, or period.
Invoice vs FinanceTransaction
Invoice is the original business record. FinanceTransaction is a projection/read model for search, list, booking number sync, allocation, and aggregate state. Drift between the two → the source of truth is still the original record; the projection needs refresh or reconcile.
Credit note
A credit note appears when edit/cancel reduces the financial obligation after the invoice has been issued or paid. It is not "deleting the invoice"; it is an adjustment document that keeps the financial history auditable.
Billing generation — how documents are generated
Billing holds the charging obligation; billing generation is the pipeline that turns a confirmed booking/enrollment into documents (Invoice, CreditNote). It separates 3 layers (invariant INV-BILLING-CORE-001):
| Layer | What it does |
|---|---|
| Prepare | Gathers booking/order/line/extra/discount/split into a standard input. |
| Calculation core | Computes invoice, line, tax, discount, split group, total, credit-note impact — in memory only, no persistence. |
| Persistence | Saves invoice/credit note, finance log, scheduler, SaveChangesAsync. |
Preview Billing Diff and the real confirm share this same core → the projected output matches the real output. Backbone rules (each rule includes an example):
- Confirm gate — confirm only finalizes after billing regeneration succeeds; billing fail → confirm fail, no side effect recorded (INV-CONFIRM-BILLING-001).
Real-world example: A parent confirms a Pay Now booking but the DD config is broken → the booking does not go to
Approved, returns failure → admin fixes the config and confirms again. - Frequency override — Pay Now →
One_Off, Direct Debit →WeeklyviaResolveBillingFrequencyOverride; the real path and preview share this helper.Real-world example: A customer chooses Direct Debit at checkout → the invoice splits weekly so DD collects the right period.
- Deposit — confirming with a deposit still generates an invoice for the full amount; settlement sits outside billing generation (INV-DEP-015).
Real-world example: A customer puts down a 20% deposit → the invoice records the full amount; the deposit is handled by Payment, the invoice is not changed.
- Approve / Skip subscription — approve generates an invoice via
UpdateBillingByIdAsync; skip with an invoice already Paid/Sent/Synced issues a credit note, otherwise regenerates.Real-world example: Admin approves the May period → creates TermBooking + invoice + Approved log; skipping an already-charged period → a credit note reverses it.
- Price line mandatory — subscription generation needs
SubscriptionPriceId+ ≥1 active price line; missing → fail before persist, no fallback from the schedule amount (INV-SUB-BM-04).Real-world example: A Weekly plan without price lines → generate fails with structured validation; after adding them the invoice appears, an empty invoice is never generated.
Invoice email
Invoice email is the channel that sends invoices to customers through a Notification template, available regardless of accounting provider (Xero or AIMY Accounting) (INV-INVOICE-EMAIL-001).
- Composer — opens before scheduling; shows invoice count, recipient, From, Email Reply To, Subject, Body; Subject/Body editable before Send (INV-INVOICE-EMAIL-002).
- Single & batch — batch across sites → each site has its own editor based on the site's template + Email Reply To; cancel/close composer → nothing scheduled.
- No PDF attachment — the email does not attach the invoice PDF; there is no "Include Invoice PDF" control, legacy payloads cannot restore the attachment (INV-INVOICE-EMAIL-003).
- Placeholders —
,,; links HTTPS, use opaque guid/token, do not expose numeric id (INV-INVOICE-EMAIL-005). - Customer link — View/Download require authenticate + authorize by account/BU; unauthorized returns not-found/forbidden, no metadata leak (INV-INVOICE-LINK-001).
Real-world example: Admin opens invoice AIN-0042 Approved in BU Xero, clicks Email → the composer shows the site template, admin edits Subject/Body then Send → the email goes out without PDF; the customer must log in to open the View/Download link.
Invoice reminders
Reminders are automatic debt notices sent through the same Invoice Email pipeline, configured per business unit.
| Rule | Content |
|---|---|
| Limit | ≤3 active rules/BU; add/edit/delete; disable keeps the rule, blocks new runs |
| Rule types | OnInvoiceDate (no offset), BeforeDueDate(N), AfterDueDate(N) with offset 0–365 |
| Duplicate target | 2 rules cannot share an effective target (including 0 days before/0 days after) |
| Target date | Invoice.Date; DueDate ∓ N by BU local calendar/timezone; no backfill before enable date |
| Eligibility | active, same BU, AmountDue > 0, not in DD collection flow, LockTypeId null |
| Template | rendered from the site's Invoice Email template at send time; no separate sender/subject/attachment stored |
| Idempotent | 1 logical send per (invoice, rule); Invoice.Sent only true after the NotificationJob is created |
Real-world example: Admin creates rule
AfterDueDate(7); invoice AIN-0031 still owes, not in the DD collection flow → the evaluator runs on due+7 by BU time → sends a reminder with the current Invoice Email template, records 1 history entry on Account + Invoice Transaction.
Invoice fully-paid notification
Invoice_Fully_Paid is a specialized EntityEvent published when an invoice moves to FinanceStatus.Paid (INV-INVOICE-PAID-NOTIF-001).
- Trigger — every path that sets
Invoice.StatusId = Paid(payment or credit-note allocation makingAmountDue = 0) publishes the event after commit, only on a real transition. - Exceptions — an invoice with
TotalAmount = 0marked Paid because nothing is collected → not published; an invoice imported/synced already Paid → not published. - Hint — the event carries
NotificationEventTypeId = PaymentSuccessConfirmation(810); the worker creates the NotificationJob via the hint path, not by callingCreateNotificationJobAsyncdirectly (INV-INVOICE-PAID-NOTIF-002). - Template — default from NotificationOption, site override via NotificationSetting by
OrgId; email disabled at the site → not sent (INV-INVOICE-PAID-NOTIF-003). - Recipient — primary contact of the account; missing email → job completes without delivery, no throw (INV-INVOICE-PAID-NOTIF-004).
Real-world example: A parent pays the remaining balance of invoice AIN-0027 →
AmountDue = 0→ the transaction commits then publishesInvoice_Fully_Paid→ worker creates a PaymentSuccessConfirmation job; the confirmation email goes to the primary contact if the site enables the email channel.
Invoice viewed tracking
Invoice.Viewed (boolean) stores whether the customer has successfully accessed the invoice (INV-INVOICE-VIEW-001).
- Default
false; only settruewhen the customer is authenticated + authorized and View/Download PDF succeeds (INV-INVOICE-VIEW-002). - Monotonic — not reset by resend email, edit, paid, or sync; repeated access is idempotent.
- Not set — admin opening Invoice Details, email schedule/delivery, unauthorized request, or PDF failure.
- Display — Invoice Details API returns
Viewed; Booking Manager only shows theViewedtag when the state is true (INV-INVOICE-VIEW-003).
Real-world example: Customer opens the View Invoice link AIN-0012 for the first time → the system sets
Viewed = true→ admin sees theViewedtag; resending email does not remove the tag.
Invoice lock & tag "Processing"
An invoice in a payment processing flow has LockTypeId != null and is marked "processing" in Invoice Manager.
- List — shows a
Processingtag below the invoice number whenLockTypeId != null; does not replace or mutate status (INV-INV-MGR-LOCK-001). - Hover — tooltip shows the exact content
Payment in processing(INV-INV-MGR-LOCK-002). - Details — title shows
Payment in processingafter Invoice Status;Add PaymentandApply Creditvisible but disabled (INV-INV-MGR-LOCK-003/005). - Display-only — the tag creates no new invoice status and does not change the payment/lock lifecycle (INV-INV-MGR-LOCK-004).
Real-world example: A payment is processing, invoice AIN-0090 has
LockTypeId != null→ the list shows the Processing tag below the invoice number; details showAdd Payment/Apply Creditdisabled while the invoice status stays unchanged.
Ownership summary
| Owner | Owns |
|---|---|
| Billing | Payment option on billing, split context, data prepared for invoice generation. |
| Invoice | Header/line/status/lock of the invoice. |
| CreditNote | Header/line/allocation of the credit note. |
| FinanceTransaction | Projection/search state, can refresh from the source. |
| Payment | Collects money and provider transaction; does not own the invoice lifecycle. |