Skip to content

BI-37: Opening Balance Management — All Business Scenarios

Key Definitions

credit_balance — Money the family has "on their account" with the school. Like a wallet. The school owes them this money. It can be used to reduce a future bill, or refunded back to the family as cash. Lives on the Debtor record (not per-term) — it's always available regardless of which billing cycle is active.

opening_balance — Debt a family carries over from a previous billing term. The family still owes this from before. Lives on the Billing Profile (per debtor per term).

billing_profile — One record per family per billing cycle (term). This is where opening_balance lives. Each term has its own.

credit_note — An accounting document that says "the school owes the family this amount." It is the record for a refund or billing adjustment. Creating a credit note means money is leaving the school — either by reducing what the family owes on an invoice, or by returning cash.

carry-forward — The process of moving unpaid debts from one term to the next. Only opening_balance is carried forward (calculated from unpaid invoices). Credit balance does not need carrying forward because it already lives on the debtor.


How credit_balance changes

credit_balance lives on the Debtor record. It is not tied to any specific billing cycle.

Event credit_balance goes... Why
Family overpays on a payment UP Extra money is now sitting on their account
A bill is generated that uses their credit DOWN Credit was applied as a discount on the invoice
Cash refund is processed DOWN Money returned to family
Credit note not applied to invoice UP School acknowledges it owes the family, money is parked on their account
Void a payment that caused an overpayment DOWN Reversing the overpayment — as if it never happened
Admin manually adjusts UP or DOWN Manual correction

How opening_balance changes

opening_balance lives on the Billing Profile (per debtor per term).

Event opening_balance goes... Why
Admin sets it manually or imports via CSV SET to a value Admin provided the number
Carry-forward calculates it from unpaid invoices SET to calculated value System computed it
A bill is generated that includes it TO ZERO Consumed — added as a line item on the new invoice

PART 1: PAYMENT SCENARIOS

P1: Payment exactly covers the invoices

Family has Invoice A with $1,000 outstanding. Admin records a payment of $1,000 and allocates all of it to Invoice A.

  • Invoice A: fully paid
  • credit_balance: no change

P2: Payment is more than what's allocated (overpayment)

Family has Invoice A with $1,000 outstanding. Admin records $1,200 and allocates $1,000 to Invoice A. $200 is left over.

  • Invoice A: fully paid
  • $200 automatically added to credit_balance on the Debtor record
  • Audit log: "credit_balance +$200.00 from payment PAY-FAM001-0005"

No edge case — the debtor record always exists, so the overpayment always has somewhere to land.

P3: Partial payment

Family owes $1,000 on Invoice A. Admin records $600, allocates all to Invoice A.

  • Invoice A: partially paid, $400 outstanding
  • credit_balance: no change

P4: Multiple invoices with overpayment

Invoice A ($500) and Invoice B ($300) outstanding. Payment $1,000: $500 to A, $300 to B.

  • Both invoices: fully paid
  • credit_balance: +$200

P5: Adding allocations to a payment that had an overpayment

Payment $1,200: $1,000 allocated to Invoice A, $200 went to credit_balance. Admin now allocates $200 of that payment to Invoice B.

  • credit_balance -$200 (the unallocated portion is now allocated — reversed from the debtor)
  • Invoice B: reduced by $200
  • Payment is now fully allocated ($1,000 to A, $200 to B)
  • Audit log: "credit_balance -$200.00 — reallocated from payment PAY-FAM001-0005 to INV-002"

P6: Adding allocations when the credit from that payment was already consumed

Payment $1,200: $1,000 to Invoice A, $200 to credit_balance. A bill generation consumed that $200 credit (credit_balance = $0 on the debtor). Admin now tries to allocate $200 from this payment to Invoice B.

  • BLOCKED. The $200 unallocated amount from this payment was already consumed as a credit line item on a generated invoice.
  • Allowing the allocation would double-spend: Invoice B gets $200 from the payment AND the generated invoice already got $200 from the credit.
  • Admin must void the consuming transaction first, or record a new payment.
  • Message: "Cannot allocate — the unallocated amount from this payment ($200) was consumed by credit application. Void the consuming transaction first, or record a new payment."

This is the same pattern as VP3 (void blocked when credit consumed) — triggered by adding allocations instead of voiding.


PART 2: REFUND SCENARIOS

The rule: When refunding a payment, the system takes from credit_balance first (the unallocated money from THAT specific payment), then reverses allocations if needed.

Important: A refund only reverses what that specific payment did. It does NOT touch credit from other payments.

R1: Full refund — payment had an overpayment

Payment was $1,200: $1,000 allocated to Invoice A, $200 went to credit_balance. Admin refunds $1,200.

  1. credit_balance -$200 (the unallocated portion from this payment)
  2. Reverse $1,000 from Invoice A → Invoice A back to $1,000 outstanding
  3. Payment status → refunded
  4. Credit note created for $1,200

R2: Partial refund — amount ≤ credit_balance from this payment

Same payment as above. Admin refunds $150.

  1. credit_balance -$150 (fully covered by the unallocated portion)
  2. Invoice A: untouched, still fully paid
  3. Payment status: still applied, amount_refunded = $150
  4. Credit note created for $150
  5. Remaining credit from this payment = $50

R3: Partial refund — amount > credit_balance from this payment

Same payment. Admin refunds $500.

  1. credit_balance -$200 (exhaust the unallocated portion)
  2. Remaining $300 reversed from Invoice A → Invoice A now $300 outstanding
  3. Payment status: still applied, amount_refunded = $500
  4. Credit note created for $500

R4: Full refund — no overpayment (payment was fully allocated)

Payment was $1,000, all allocated to Invoice A. credit_balance contribution = $0. Admin refunds $1,000.

  1. credit_balance: nothing to deduct
  2. Reverse full $1,000 from Invoice A → back to $1,000 outstanding
  3. Payment status → refunded
  4. Credit note created for $1,000

R5: Refund when credit_balance was already consumed by a new invoice

Payment was $1,200: $1,000 to Invoice A, $200 to credit_balance. A new invoice consumed that $200 credit (credit_balance = $0). Admin wants to refund $200.

  1. credit_balance is $0 — the credit from this payment was already used
  2. $200 reversed from Invoice A allocations → Invoice A now $200 outstanding
  3. Credit note created for $200

The new invoice that consumed the credit is unaffected.

R6: Second refund on same payment

Payment $1,200, first refund of $300 already done. Admin refunds another $200.

  • Same logic: check remaining credit from this payment, then reverse allocations
  • amount_refunded becomes $500
  • Second credit note created for $200

R7: Refund exceeds remaining refundable amount

Payment $1,000, already refunded $800. Admin tries to refund $300 (only $200 left).

  • Rejected. Cannot refund more than remaining.

R8: Refund a voided payment

  • Not allowed. Voided payments were already fully reversed.

R9: Refund when credit_balance has credit from OTHER payments too

credit_balance = $500 (from multiple overpayments over time). Admin refunds Payment X ($1,000, fully allocated, $0 contribution to credit). Admin refunds $300.

  • The $500 credit came from other payments, NOT Payment X
  • Only Payment X's allocations are reversed — $300 from Invoice A
  • credit_balance: no change (not touched — it belongs to other payments)
  • Credit note created for $300

PART 3: VOID PAYMENT SCENARIOS

Voiding = "this payment was entered by mistake — it never happened." No credit note. Internal correction only.

VP1: Void a fully allocated payment

Payment $1,000 to Invoice A. Admin voids.

  • Invoice A back to $1,000 outstanding
  • credit_balance: no change
  • Payment status → voided

VP2: Void a payment that had an overpayment

Payment $1,200: $1,000 to Invoice A, $200 to credit_balance. Admin voids.

  • Invoice A back to $1,000 outstanding
  • credit_balance -$200 (reverse the overpayment sweep)
  • Payment status → voided

VP3: Void when credit_balance from this payment was already consumed

Payment $1,200: $1,000 to Invoice A, $200 to credit_balance. New invoice consumed the $200 credit (credit_balance = $0). Admin tries to void.

  • BLOCKED. The $200 from this payment has been consumed. Voiding would make credit_balance -$200 which is invalid.
  • Admin must first void the invoice that consumed the credit, then come back.
  • Message: "Cannot void — credit from this payment was consumed by invoice generation. Void the consuming invoice first."

VP4: Void a partially refunded payment

  • Not allowed. A refund already happened in reality (money was returned). Cannot pretend the payment never existed.

PART 4: CREDIT NOTE SCENARIOS

Credit notes are created in two ways:

  1. Automatically — as part of a refund (Part 2). The credit note is just the receipt.
  2. Manually — admin creates one directly for billing adjustments.

CN1: Credit note applied to a specific invoice

Invoice A: $2,000 outstanding. School overcharged $300. Admin creates a credit note for $300 applied to Invoice A.

  • Credit note created ($300, immediately closed)
  • Invoice A outstanding: $2,000 → $1,700
  • credit_balance: no change (credit went directly to the invoice)

CN2: Credit note NOT applied to an invoice

Family paid everything already. School discovers a $300 overcharge. Admin creates a credit note for $300 with no invoice to apply it to.

  • Credit note created ($300, immediately closed)
  • credit_balance: +$300 (money is owed to the family, parked on their account)
  • That $300 will later be: consumed by next invoice, carried forward, or refunded as cash

Why UP? The credit note acknowledges "we owe you." The money hasn't left yet — it's sitting as credit. It goes DOWN later when we refund it or apply it to an invoice.

CN3: Credit note via the refund flow

This is automatic — created by the refund endpoint. The credit_balance changes are handled by the refund logic (Part 2), not by the credit note itself. The credit note here is just the accounting record.


PART 5: SETTING OPENING BALANCES

OB1: Manual entry

Admin opens Term 2 → Billing Profiles → FAM001 → sets opening_balance = $1,200.

To set credit_balance, admin goes to the Debtor record for FAM001 and sets credit_balance = $50.

  • Values saved. Audit logged.
  • Lock rule (opening_balance): Only works if no transaction has been generated yet for this profile.
  • credit_balance: Can be adjusted on the debtor at any time (audit logged).

OB2: Bulk import (CSV)

debtor_code, opening_balance, credit_balance
FAM001, 1200.00, 50.00
FAM002, 0.00, 0.00
FAM003, 850.00, 120.00

System validates all debtor codes. opening_balance is written to the billing profile (lock rule applies). credit_balance is written to the Debtor record. Logs everything.

When would you use this? When migrating from another system (like NetSuite) or when the finance team prepared the numbers in a spreadsheet.

OB3: Carry-forward

Admin clicks "Carry Forward from Term 1" on Term 2 and selects Term 1 as the source.

For each family in both terms:

  1. Sum unpaid invoices from Term 1 → opening_balance on Term 2's billing profile
  2. Mark Term 1's unpaid invoices as carried_forward (unpayable, closed)
  3. Log everything

credit_balance is not carried forward — it already lives on the debtor record and persists across terms automatically.

Skipped families: If a family is in Term 1 but not Term 2, they're skipped and reported. Their Term 1 invoices stay as-is.

OB4: Carry-forward overwrites existing values

If Term 2 profiles already had manual opening_balance values, carry-forward overwrites them (with a warning). Old values are in the audit log.


PART 6: GENERATING BILLS

TG1: include_opening_balance = ON

Line Item Amount
Tuition Term 2 +$5,000
Sibling Discount -$500
Opening Balance (Term 1) +$1,200
Credit Applied -$300
TOTAL $5,400

After: opening_balance on billing profile → $0 (consumed), credit_balance on debtor → $0 (consumed)

TG2: include_opening_balance = OFF

Normal invoice without balance line items. opening_balance and credit_balance stay on the profile untouched.

TG3: Credit > invoice total

Fees = $500, debtor's credit_balance = $800. Only $500 of credit applied. Invoice total = $0. Remaining $300 stays on the debtor's credit_balance. We do NOT generate negative invoices.

TG4: Opening balance and credit on same invoice — NOT netted

Both appear as separate line items. Opening Balance +$1,200, Credit Applied -$300. Transparent to the family — they see both.

TG5: Void generated transactions and regenerate

When voiding generated transactions:

  • opening_balance on billing profile restored to pre-generation value
  • credit_balance on debtor restored to pre-generation value
  • Profile reset to draft, ready for regeneration

PART 7: CARRY-FORWARD SPECIAL SITUATIONS

CF1: Reversing a carry-forward

Admin carried forward to Term 2 by mistake.

  • Term 2: opening_balance → $0 (or to pre-carry-forward values if manually set before)
  • Term 1: carried_forward invoices → un-marked (back to sent/overdue/partially_paid)
  • credit_balance: not affected (it lives on the debtor, not the profile)
  • Only allowed if Term 2 has NOT generated transactions yet

CF2: Family only in source term

FAM005 has $800 unpaid in Term 1 but no profile in Term 2. Skipped. Term 1 invoices untouched.

CF3: Mix of paid and unpaid

3 paid, 2 unpaid ($600 total). Only the 2 unpaid invoices contribute to opening_balance. Only they get marked carried_forward. Paid invoices are untouched.

CF4: Credit_balance and carry-forward

credit_balance lives on the debtor, not on the billing profile. It does not participate in carry-forward at all. Whatever credit a family has accumulated (from overpayments, unapplied credit notes, etc.) is always available on their debtor record regardless of which term is active.

CF5: Can someone pay a carried-forward invoice?

No. carried_forward = closed + unpayable. System rejects payment attempts. The debt has moved to the new term.


PART 8: BILLING CYCLE DELETION

BD1: Delete a draft cycle that received carry-forward

Deleting Term 2 (draft). Term 1 had invoices carried forward to it.

  • Term 2 profiles deleted (with their opening_balance values)
  • credit_balance on debtors: not affected (it lives on the debtor, not the profile)
  • Term 1 carried_forward invoices: un-marked (back to previous status)
  • Without this, the debt disappears — it left Term 1 but Term 2 no longer exists

BD2: Delete a cycle that was carried forward FROM

Term 1 was the source. Admin wants to delete Term 1.

  • Blocked. Deleting would destroy the source of Term 2's opening balances.
  • Admin must reverse carry-forward on Term 2 first, then delete Term 1.

BD3: Only draft cycles can be deleted

No change to this existing rule.


PART 9: DEBTOR BALANCE VIEW

Total owed = Sum of outstanding on all unpaid transactions − credit_balance (from Debtor record)

  • If positive: the family owes the school
  • If zero or negative: the family is in credit or square

Do NOT add opening_balance on top of outstanding — that would double-count once it's been consumed into an invoice.

credit_balance is always available from the debtor record. No need to look up which billing profile it belongs to.


SUMMARY TABLE

Scenario opening_balance (profile) credit_balance (debtor) Transactions
Overpayment UP Invoice paid
Reallocate overpayment to invoice DOWN Invoice reduced
Reallocate overpayment (credit consumed) BLOCKED Must void consuming txn first
Refund from credit DOWN Credit note created
Refund exceeding credit DOWN to 0 Credit note + invoices reversed
Void payment (overpayment) DOWN Allocations reversed
Void payment (credit consumed) BLOCKED Must resolve first
Credit note → invoice unchanged Invoice reduced
Credit note → no invoice UP Credit on account
Manual balance entry SET SET None
CSV import SET SET None
Carry-forward SET not affected Source invoices carried_forward
Reverse carry-forward TO ZERO not affected Invoices un-marked
Bill generated (ON) TO ZERO DOWN/ZERO Balance line items added
Bill generated (OFF) unchanged unchanged Normal items only
Void generated txns RESTORED RESTORED Voided, profile reset
Delete cycle (target of CF) deleted not affected Source invoices un-marked

TRACKING NOTE

The refund logic (R1–R9) requires knowing how much of the current credit_balance came from a specific payment. This is tracked via the balance_audit_log — each credit_balance increase records the source payment ID so the system can determine exactly how much credit from Payment X remains available.