Credit data model — the six tables (Task 10.2)
Status: ✅ Complete
Plan reference: Task 10.2 in the Credit module implementation plan (operations workspace root)
The Credit module persists three financing products (Murabaha, Ijarah, Diminishing Musharakah) across six tables. This page documents the model: tables, key columns, enums, and the relationships between them. For the API surface see credit API; for the repayment math see credit engine.
Money math. As with the finance module, money columns are
NUMERIC(20,4)in the DB and handled withdecimal.jsin code — never JavaScriptnumber.
Entity relationships
credit_facilities — master record
The aggregate root. One row per financing deal.
| Column | Type / values | Notes |
|---|---|---|
facilityRef | string CF-YYYYMMDD-XXXX | Human-facing reference, unique |
customerId | FK | The financed customer |
productType | murabaha | ijarah | diminishing_musharakah | Selects the repayment engine |
assetCost | NUMERIC(20,4) | Purchase cost of the asset |
downPayment | NUMERIC(20,4) | Customer's upfront contribution |
facilityAmount | NUMERIC(20,4) | assetCost − downPayment; the financed principal |
rate | NUMERIC | Annual profit/rent rate (DM/Ijarah). Not used to derive Murabaha profit |
term | int + periods/year | Number of periods and frequency |
status | lifecycle enum (below) | Drives the state machine |
fixedTotalProfit | NUMERIC(20,4), Murabaha only | Flat profit locked at signing. Required for Murabaha; never rate-derived |
nonAccrual | boolean | When true, payments recognise principal only |
nonAccrualAt | timestamp | When non-accrual was flagged |
nonAccrualReason | text | Why the facility was impaired |
activity | JSONB | Append-only audit trail of lifecycle events |
status lifecycle enum: draft → pending_review → shariah_review → approved → active → completed, plus rejected (from pending_review/shariah_review) and cancelled. See the architecture state machine.
credit_assets — the financed asset
The asset record that backs the VR-001 "no sale before ownership" gate.
| Column | Type / values | Notes |
|---|---|---|
facilityId | FK | One asset per facility |
description | text | What is being financed |
cost | NUMERIC(20,4) | Asset cost |
ownershipStatus | pending_acquisition | owned_by_ethica | co_owned | transferred_to_customer | Gate input |
acquiredAt | timestamp | Set when Ethica buys the asset |
transferredAt | timestamp | Set when ownership passes to the customer |
Activation gate (VR-001): a facility can only be activated when its asset is owned_by_ethica (Murabaha/Ijarah) or co_owned (Diminishing Musharakah). The asset typically ends at transferred_to_customer on completion.
credit_ownership_ledger — per-period co-ownership
Records the Ethica/customer ownership split per period (most meaningful for DM, where Ethica's share declines to zero).
| Column | Type | Notes |
|---|---|---|
facilityId | FK | |
period | int | Schedule period this row describes |
ethicaOwnershipPct | NUMERIC | Ethica's remaining share after this period |
customerOwnershipPct | NUMERIC | Customer's accumulated share (100 − ethica) |
For the DM golden example: P1 → customer 6.67% / Ethica 93.33%, winding down to P15 → customer 100% / Ethica 0%.
credit_schedule_lines — the repayment plan
One row per scheduled period, generated by the engine.
| Column | Type / values | Notes |
|---|---|---|
facilityId | FK | |
period | int | 1..N |
paymentDate | date | Due date |
beginningBalance | NUMERIC(20,4) | Outstanding at start of period |
principalPortion | NUMERIC(20,4) | Buy-out / cost recovery for the period |
profitPortion | NUMERIC(20,4) | Rent (DM/Ijarah) or profit (Murabaha) for the period |
totalPayment | NUMERIC(20,4) | principalPortion + profitPortion |
endingBalance | NUMERIC(20,4) | Outstanding at end of period |
ethicaOwnershipPct / customerOwnershipPct | NUMERIC | Ownership at this period |
status | pending | paid | partially_paid | overdue | |
paidAmount | NUMERIC(20,4) | Cumulative amount applied to this line |
Residual closing: the engine closes rounding on the final period so Σ principalPortion === facilityAmount/cost exactly, and (Murabaha) Σ profitPortion === fixedTotalProfit exactly.
credit_payments — payments received
| Column | Type | Notes |
|---|---|---|
facilityId | FK | |
scheduleLineId | FK (nullable) | Line(s) the payment is applied to |
amount | NUMERIC(20,4) | Amount received |
extraPayment | boolean | True if above the period's due amount |
paymentDate | date | |
reference | string | Operator-supplied reference |
journalEntryId | FK → journal_entries | The GL journal this payment posted |
Every payment that recognises income posts through the finance PostingEngine; the resulting journal id is stored here for traceability.
credit_posting_account_map — CFO-configurable GL mapping
Resolves which GL account each posting leg hits. Indexed by product type × event type × side.
| Column | Type | Notes |
|---|---|---|
productType | product enum | murabaha / ijarah / diminishing_musharakah |
eventType | enum | e.g. origination, payment, non_accrual_payment |
side | debit | credit | Which side of the journal |
glAccount | account code | The resolved GL account |
Fail-closed: if the engine cannot resolve an account for a (productType, eventType, side), the posting is rejected — accounts are never hardcoded in the handler. This keeps the chart of accounts under CFO control without code changes.
Files
- Models —
ethica-api/src/credit/models/*.model.ts - Posting account map —
ethica-api/src/credit/models/credit-posting-account-map.model.ts
Related
- Credit API — endpoints and request/response shapes.
- Credit engine — schedule generation and the worked example.
- Credit architecture — lifecycle and posting flow diagrams.