Skip to content

Overdraft management engine

ID MOD-117
System SD05
Repo bank-credit
Build status Deployed
Deployed Yes
Last commit 19b0610

Purpose

Manages the full lifecycle of a revolving credit limit attached to a transaction account (PRD-019 Linked Transaction Overdraft). Responsibilities include: maintaining the approved limit record, tracking the drawn balance, calculating daily interest on any negative balance, posting monthly interest and facility fee charges, enforcing payment declines at the limit boundary, monitoring for financial hardship indicators, and handling the unarranged overdraft edge case.

MOD-117 operates as a sub-ledger on top of the core balance engine (MOD-003). It does not hold balances directly — all monetary postings flow through MOD-001 (double-entry posting engine). MOD-117 is the authoritative source for facility terms, accrual records, and the facility event log.


Compliance rationale

Under the NZ Credit Contracts and Consumer Finance Act 2003 (CCCFA) and the AU National Consumer Credit Protection Act 2009 (NCCP), a linked overdraft is a continuing credit contract. This creates several ongoing obligations that MOD-117 is designed to operationalise:

Responsible lending at origination and limit increase. The bank must perform a creditworthiness and affordability assessment before granting or increasing any limit. MOD-117 enforces this as a hard gate: no facility record can be created and no limit can be increased without a completed assessment reference from MOD-027 and MOD-028.

Persistent overdraft use as a hardship signal. Both CCCFA and NCCP require lenders to act proactively when a borrower shows signs of financial difficulty. A customer who is consistently at or near their overdraft limit for an extended period is exhibiting that signal. MOD-117 monitors consecutive days in a drawn state and emits a hardship flag at the 60-day threshold, triggering the proactive hardship conversation required by CON-008 and CRE-007.

Unarranged overdraft notification. The NZ and AU Banking Codes require prompt notification when a customer enters an unarranged overdraft (a negative balance where no overdraft facility has been granted). MOD-117 detects this condition and triggers an immediate customer notification via MOD-063.

Disclosure before activation. CON-005 requires that the limit, interest rate, and fee are disclosed before the facility is activated. MOD-117 will not create a facility record until MOD-050 confirms that the initial credit disclosure has been delivered and acknowledged.


Commercial rationale

Linked overdrafts are a high-margin retail credit product with low origination cost relative to other credit products: no security assessment, no title search, and minimal ongoing servicing overhead. The facility charges daily interest on the drawn balance, and daily interest income on even modest utilisation rates is material at scale.

The retention effect is significant. Customers with an overdraft attached to their transaction account have measurably lower churn than those without, because the overdraft increases the friction of switching to a competing bank (the customer would lose the pre-approved credit line and face a new application elsewhere). The overdraft is therefore both a revenue product and a retention mechanism for the core transaction account relationship.


Data model

-- credit.overdraft_facilities
CREATE TABLE credit.overdraft_facilities (
  facility_id        UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  account_id         UUID NOT NULL REFERENCES core.accounts(account_id),
  approved_limit     NUMERIC(18,2) NOT NULL,
  current_limit      NUMERIC(18,2) NOT NULL,  -- may differ if partial reduction pending
  interest_rate_pct  NUMERIC(8,5) NOT NULL,
  facility_fee       NUMERIC(18,2) NOT NULL,
  status             TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','suspended','closed')),
  review_date        DATE NOT NULL,
  activated_at       TIMESTAMPTZ NOT NULL,
  closed_at          TIMESTAMPTZ,
  last_assessment_id UUID,
  created_at         TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- credit.overdraft_daily_accruals
CREATE TABLE credit.overdraft_daily_accruals (
  accrual_id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  facility_id        UUID NOT NULL REFERENCES credit.overdraft_facilities(facility_id),
  accrual_date       DATE NOT NULL,
  drawn_balance      NUMERIC(18,2) NOT NULL,
  daily_interest     NUMERIC(18,6) NOT NULL,
  posted             BOOLEAN NOT NULL DEFAULT false,
  created_at         TIMESTAMPTZ NOT NULL DEFAULT now(),
  UNIQUE (facility_id, accrual_date)
);

-- credit.overdraft_events
CREATE TABLE credit.overdraft_events (
  event_id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  facility_id        UUID NOT NULL REFERENCES credit.overdraft_facilities(facility_id),
  event_type         TEXT NOT NULL CHECK (event_type IN ('limit_set','limit_increased','limit_reduced','limit_suspended','interest_charged','hardship_flag','utilisation_alert','review_due','closed')),
  event_data         JSONB,
  created_at         TIMESTAMPTZ NOT NULL DEFAULT now()
);

overdraft_facilities is the master record for each facility. current_limit may be lower than approved_limit during a partial reduction notice period. last_assessment_id links to the most recent affordability assessment record in the credit assessment service.

overdraft_daily_accruals stores one row per facility per day on which a negative balance was recorded. The posted flag distinguishes accruals that have been included in a monthly charge from those still pending. The unique constraint on (facility_id, accrual_date) prevents duplicate accrual runs.

overdraft_events is an append-only audit log of all significant lifecycle events on the facility. event_data carries structured JSON payload appropriate to each event type (e.g. old and new limit values for limit_reduced, consecutive days count for hardship_flag).


Key operations

1. Available balance

MOD-003 calls get_available_balance(account_id) when computing the balance available for a payment or display. MOD-117 returns ledger_balance + current_limit if an active facility exists for the account, or ledger_balance if no active facility exists. This combined figure is what MOD-021 (payment limit and velocity controller) uses to gate payment execution: a payment that would take the resulting balance below zero minus the limit is declined.

2. Daily interest accrual

A scheduled job runs at end of each calendar day. For each active facility where the associated account's ledger balance is negative:

daily_interest = abs(ledger_balance) × (interest_rate_pct / 100 / 365)

A row is inserted into overdraft_daily_accruals with posted = false. If the balance is zero or positive, no accrual row is created for that day. MOD-005 (daily accrual calculator) provides the end-of-day balance snapshot; MOD-117 owns the accrual record.

The consecutive-days-drawn counter is updated on each daily run. If the counter reaches 60, the hardship detection flow (see below) is triggered.

3. Monthly interest charge

On the last calendar day of each month, MOD-117 runs the monthly close job for all active facilities:

  1. Select all overdraft_daily_accruals rows for the facility where posted = false.
  2. Sum daily_interest across all selected rows.
  3. Post a single debit entry to the account via MOD-001 for the summed amount, with description identifying the period.
  4. Mark all selected accrual rows posted = true.
  5. Insert a interest_charged event into overdraft_events.
  6. Emit bank.credit.overdraft_interest_charged domain event.

4. Monthly facility fee

On the same monthly close run: check whether any overdraft_daily_accruals row exists for the facility in the calendar month (i.e. whether the balance was ever negative). If yes, post a facility fee debit via MOD-001 using the fee amount from overdraft_facilities.facility_fee. If no rows exist (balance was positive throughout the month), the fee is waived: a fee waiver event is logged in MOD-110 and an interest_charged event with zero fee amount is recorded for audit completeness.

5. Hardship detection

The daily accrual job tracks the number of consecutive calendar days on which a negative balance was recorded. When this count reaches 60:

  1. MOD-117 inserts a hardship_flag event into overdraft_events with the consecutive-days count in event_data.
  2. MOD-007 sets the hardship_review_pending flag on the account.
  3. MOD-063 dispatches an alert to the back-office operations queue and a customer-facing notification advising them to contact the bank if they are experiencing financial difficulty.
  4. The consecutive-days counter is not reset until the balance returns to positive and remains positive for at least 5 days, preventing repeated alerts on minor balance oscillations.

The hardship flag does not automatically restrict the account. It creates a task for a human review. The outcome of that review may result in a hardship arrangement under CON-008, at which point MOD-007 applies the appropriate account restrictions.

6. Unarranged overdraft

An unarranged overdraft occurs when an account with no active overdraft facility records a negative balance. This is an edge case that can arise from timing issues in payment settlement or fee debits.

On detection (MOD-007 observes a negative balance on an account with no active facility):

  1. MOD-007 sets the unarranged_overdraft state on the account.
  2. MOD-063 dispatches an immediate customer notification.
  3. MOD-117 creates a flag record for manual operations review.
  4. If the bank's product rules permit an unarranged overdraft fee (configured in MOD-110), the fee is posted via MOD-001.
  5. The account is not blocked from incoming credits. The balance is expected to return to positive when the next credit arrives.

If the balance remains negative beyond a configurable threshold period (default 5 business days), the operations queue receives an escalation alert.


FRs satisfied

FR Description
FR-529 System shall maintain an approved overdraft limit per transaction account and enforce that no payment or debit causes the balance to fall below the negative of that limit.
FR-530 System shall calculate available balance as ledger balance plus undrawn overdraft limit for all accounts with an active overdraft facility, and expose this figure to balance display and payment validation.
FR-531 System shall accrue overdraft interest daily on any negative ledger balance and post a single aggregated interest charge debit on the last calendar day of each month.
FR-532 System shall monitor consecutive days with a negative balance per facility and emit a financial hardship flag when the threshold of 60 consecutive days is reached.

Module dependencies

Depends on

Module Title Required? Contract Reason
MOD-001 Double-entry posting engine Required Monthly interest charges and facility fees are posted as ledger entries via the double-entry posting engine. v1 uses ADJUSTMENT posting_type pending ACCRUAL type addition to MOD-001 request enum.
MOD-065 Credit servicing & collections Required credit.loan_accounts must exist (MOD-065 owns the table) before MOD-117 can create paired OVERDRAFT product_type rows on facility creation.
MOD-027 Affordability calculator Required Affordability assessment (credit.affordability_assessments) is a hard gate before any facility is created or limit increased — CRE-002 GATE.
MOD-003 Real-time balance engine Optional v1 stub — current_drawn_balance column on overdraft_facilities mirrors the drawn balance; update-drawn-balance Function URL replaces the real-time feed until MOD-003 ships and a bank.core.balance_updated SQS consumer takes over.
MOD-005 Daily accrual calculator Optional v1 owns its own daily accrual job following MOD-005 conventions; MOD-005 integration is a v2 enhancement once the shared accrual engine is built.
MOD-007 Account state machine Optional v1 stub — hardship flag emits event + writes to overdraft_events audit log; MOD-007 account state machine integration wires in v2 when MOD-007 ships.
MOD-050 Disclosure enforcement module Optional v1 stub — disclosure gate is enforced via caller-asserted disclosure_acknowledged field; MOD-050 delivery confirmation replaces the stub in v2.
MOD-063 Notification orchestration Optional v1 stub — hardship and unarranged-overdraft alerts use audit-only mode + SNS; live notifications delivered when MOD-063 ships.
MOD-110 Fee engine Optional v1 stub — fee waiver outcome emits bank.credit.overdraft_fee_assessed with waived=true; MOD-110 fee waiver record API wires in v2.

Required by

(No modules in this wiki currently declare a dependency on this module.)


Policies satisfied

Policy Title Mode How
CRE-002 Responsible Lending Policy GATE Overdraft limit cannot be set or increased without a completed affordability assessment satisfying responsible lending obligations.
CON-005 Fee & Pricing Transparency Policy GATE The current overdraft limit, interest rate, and monthly facility fee are disclosed to the customer before any limit is activated.
CON-008 Financial Hardship Policy ALERT Customers who are drawn on their overdraft for more than 60 consecutive days are flagged for financial hardship review.
PAY-001 Payment Operations Policy GATE Outgoing payments that would exceed the combined available balance (credit + overdraft limit) are declined before execution.
CRE-001 Credit Risk Management Policy CALC Drawn overdraft balances contribute to credit exposure reporting and concentration risk calculations.

Capabilities satisfied

(No capabilities mapped)


Part of SD05 — Credit Decisioning & Loan Platform Compiled 2026-05-22 from source/entities/modules/MOD-117.yaml