Mortgage servicing engine¶
| ID | MOD-116 |
| System | SD05 |
| Repo | bank-credit |
| Build status | Deployed |
| Deployed | Yes |
| Last commit | 19b0610 |
Purpose¶
Manages the full post-drawdown lifecycle of a residential mortgage: scheduled repayment execution, fixed rate period management (including expiry notifications and rate elections), early repayment and break cost calculation, discharge processing, and arrears detection and escalation. This is the operational engine that runs a mortgage from settlement to payoff.
Compliance rationale¶
CCCFA (NZ) and NCCP (AU) require that customers receive adequate notice before their interest rate changes on a credit contract. ASIC RG 274 requires the bank to notify customers of significant product changes. This module enforces those obligations by driving the fixed rate expiry notification sequence at 90, 60, and 30 days before the expiry date.
Break cost disclosure before early repayment is mandatory under responsible lending obligations in both jurisdictions. A customer cannot submit an early repayment request through any channel until the break cost has been calculated, disclosed, and explicitly accepted.
Arrears early escalation to hardship is required under CON-008 and the industry banking codes in both NZ and AU. This module detects missed repayments at day 1 and drives a structured escalation sequence that routes accounts to the financial hardship workflow before any collections action is taken.
Commercial rationale¶
Fixed rate roll-off is one of the highest-value customer engagement moments in retail banking. A customer whose fixed rate expires without proactive contact will typically reprice with a competitor. The notification and election workflow in this module creates structured touchpoints at 90, 60, and 30 days that give the bank three opportunities to retain the customer before expiry.
Break cost transparency builds trust and reduces disputes. Customers who receive a clear, upfront break cost calculation before they refinance are significantly less likely to raise a complaint or seek external dispute resolution.
Fixed rate lifecycle¶
State machine: VARIABLE → FIXED → EXPIRING → EXPIRED → VARIABLE | FIXED (re-fixed)
VARIABLE: Default state for accounts on a variable rate, or accounts that have reverted after fixed period expiry.
FIXED: Entered on drawdown (fixed rate election) or when a customer successfully elects a new fixed term. The credit.mortgage_rate_periods record is created with rate_type = 'fixed', end_date set to the expiry date, and disclosed_at set when disclosure was delivered.
EXPIRING: Entered automatically when 90 days remain before end_date. The first expiry notification (expiry_90d) is dispatched via MOD-063. Subsequent notifications are sent at 60 days (expiry_60d) and 30 days (expiry_30d). The account remains in EXPIRING until the customer makes an election or the end date is reached.
EXPIRED: If no election is made by the end_date, the account reverts to the prevailing variable rate automatically. A expired_revert notification is sent. No penalty applies for automatic reversion. The credit.mortgage_rate_periods record for the fixed period is updated with status = 'expired' and a new variable rate period record is created.
Rate election: Customer (via app) or agent (via back office) elects variable rate or a new fixed term. MOD-050 enforces delivery and acknowledgement of the required disclosure before the election is accepted. On acceptance, a new credit.mortgage_rate_periods record is created with elected_at and disclosed_at set. The new rate takes effect on the current period's end_date.
Break cost calculation¶
Applies when a fixed rate loan is repaid in full or refinanced before the fixed term expires.
break_cost = max(0, (contract_rate − reinvestment_rate) × outstanding_balance × remaining_fixed_days / 365)
contract_rate is the rate on the active credit.mortgage_rate_periods record. reinvestment_rate is the wholesale swap rate for the remaining fixed term, sourced from MOD-085 at the time of calculation. remaining_fixed_days is the number of calendar days between the requested repayment date and end_date.
The break cost calculation result is stored in credit.break_cost_disclosures. The customer must acknowledge and accept the disclosed amount (accepted_at must be set) before the early repayment transaction is submitted to MOD-001 for posting. Break cost is posted as a separate ledger entry by MOD-001.
Data model¶
-- credit.mortgage_rate_periods
CREATE TABLE credit.mortgage_rate_periods (
period_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
loan_id UUID NOT NULL REFERENCES credit.loans(loan_id),
rate_type TEXT NOT NULL CHECK (rate_type IN ('variable','fixed')),
rate_pct NUMERIC(8,5) NOT NULL,
start_date DATE NOT NULL,
end_date DATE, -- null for variable; set for fixed
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','expired','superseded')),
elected_at TIMESTAMPTZ,
disclosed_at TIMESTAMPTZ, -- must be set before rate election accepted
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- credit.mortgage_notifications
CREATE TABLE credit.mortgage_notifications (
notification_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
loan_id UUID NOT NULL,
notification_type TEXT NOT NULL CHECK (notification_type IN ('expiry_90d','expiry_60d','expiry_30d','expired_revert','rate_elected','break_cost_disclosure','discharge_initiated','arrears_day1','arrears_day7','arrears_day30')),
sent_at TIMESTAMPTZ,
acknowledged_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- credit.break_cost_disclosures
CREATE TABLE credit.break_cost_disclosures (
disclosure_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
loan_id UUID NOT NULL,
disclosed_at TIMESTAMPTZ NOT NULL,
contract_rate NUMERIC(8,5) NOT NULL,
reinvestment_rate NUMERIC(8,5) NOT NULL,
outstanding_balance NUMERIC(18,2) NOT NULL,
remaining_days INT NOT NULL,
break_cost_amount NUMERIC(18,2) NOT NULL,
accepted_at TIMESTAMPTZ, -- set when customer confirms
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Arrears detection and escalation¶
At end of day, MOD-005 accrual output is compared against the scheduled repayment due for each active mortgage account. If a repayment is missed by end of business on the due date, the event bank.credit.repayment_missed is emitted and the escalation sequence begins:
Day 1: Notification of missed repayment dispatched via MOD-063 (arrears_day1). Record inserted into credit.mortgage_notifications.
Day 7: Hardship flag set on the account record in MOD-007 (customer profile). Back-office alert dispatched via MOD-063 (arrears_day7). Account is routed to the financial hardship queue for proactive outreach. No collections action is initiated until the hardship assessment is complete.
Day 30: Account escalated to MOD-065 (credit servicing and collections) for formal collections workflow. Notification dispatched (arrears_day30). All transitions are logged to credit.mortgage_notifications.
This sequence satisfies CON-008 by ensuring hardship routing precedes any collections action. The day thresholds are configurable per product to allow for code-free adjustment if regulatory guidance changes.
Requirements satisfied¶
FR-525 — System shall manage the fixed rate lifecycle state machine for each mortgage account, including automatic reversion to variable rate on expiry and dispatch of notifications at 90, 60, and 30 days before the fixed rate end date.
FR-526 — System shall calculate break cost using the formula max(0, (contract_rate − reinvestment_rate) × outstanding_balance × remaining_fixed_days / 365), store the result in credit.break_cost_disclosures, and enforce customer acceptance before posting the early repayment transaction.
FR-527 — System shall process loan discharge requests by calculating any applicable break cost, confirming zero arrears via MOD-065, posting the final repayment via MOD-001, and triggering security discharge in MOD-115.
FR-528 — System shall detect missed repayments at end of day and drive the arrears escalation sequence: notification at day 1, hardship flag at day 7, escalation to MOD-065 at day 30.
Module dependencies¶
Depends on¶
| Module | Title | Required? | Contract | Reason |
|---|---|---|---|---|
| MOD-001 | Double-entry posting engine | Required | — | All mortgage repayments and interest postings are made via the double-entry engine. |
| MOD-005 | Daily accrual calculator | Optional | — | Daily interest accrual — v1 reads outstanding_principal directly from credit.loan_accounts; MOD-005 will keep it current once built. |
| MOD-006 | Rate change propagation | Optional | — | Variable rate propagation — out of v1 scope; MOD-116 v1 handles only fixed rate elections and manual variable rate updates. |
| MOD-063 | Notification orchestration | Optional | — | Notification orchestration — v1 uses audit-only stub (credit.mortgage_notifications + SNS to MOD-076); live customer notifications delivered when MOD-063 ships. |
| MOD-112 | Amortisation schedule engine | Optional | — | Amortisation schedule engine — v1 reuses MOD-065's credit.repayment_schedules tables; MOD-112 ownership clarification handoff filed. |
| MOD-115 | Property security and LVR management | Required | — | Security status and LVR data is required for discharge processing and for rate tier determination. |
| MOD-065 | Credit servicing & collections | Required | — | Arrears and hardship cases are escalated to the credit servicing and collections module for management; MOD-116 subscribes to bank.credit.arrears_triggered and filters for MORTGAGE product_type. |
| MOD-085 | Market rates ingestion & normalisation | Optional | contract/events/ |
Swap curve reinvestment rate for break cost calculation — v1 uses locked static defaults; MOD-085 swap rate write-back replaces the static lookup 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 | AUTO |
Fixed rate expiry notification at 90/60/30 days ensures customers have adequate time to make an informed rate election before reversion to variable. |
| CON-004 | Product Disclosure & Sales Practice Policy | AUTO |
Rate election disclosure is enforced before the customer's rate election is accepted — customer cannot elect without confirming they have received the disclosure. |
| CON-005 | Fee & Pricing Transparency Policy | GATE |
Break cost calculation is disclosed to the customer before any early repayment of a fixed rate loan is processed. |
| CON-008 | Financial Hardship Policy | ALERT |
Arrears escalation triggers a hardship flag, routing the account to the financial hardship workflow before collections action. |
| PAY-001 | Payment Operations Policy | AUTO |
Scheduled repayments are initiated as automatic payments via the payment engine on their due date. |
Capabilities satisfied¶
(No capabilities mapped)
Part of SD05 — Credit Decisioning & Loan Platform
Compiled 2026-05-22 from source/entities/modules/MOD-116.yaml