Skip to content

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: VARIABLEFIXEDEXPIRINGEXPIREDVARIABLE | 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