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 with decimal.js in code — never JavaScript number.

Entity relationships

credit_facilities — master record

The aggregate root. One row per financing deal.

ColumnType / valuesNotes
facilityRefstring CF-YYYYMMDD-XXXXHuman-facing reference, unique
customerIdFKThe financed customer
productTypemurabaha | ijarah | diminishing_musharakahSelects the repayment engine
assetCostNUMERIC(20,4)Purchase cost of the asset
downPaymentNUMERIC(20,4)Customer's upfront contribution
facilityAmountNUMERIC(20,4)assetCost − downPayment; the financed principal
rateNUMERICAnnual profit/rent rate (DM/Ijarah). Not used to derive Murabaha profit
termint + periods/yearNumber of periods and frequency
statuslifecycle enum (below)Drives the state machine
fixedTotalProfitNUMERIC(20,4), Murabaha onlyFlat profit locked at signing. Required for Murabaha; never rate-derived
nonAccrualbooleanWhen true, payments recognise principal only
nonAccrualAttimestampWhen non-accrual was flagged
nonAccrualReasontextWhy the facility was impaired
activityJSONBAppend-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.

ColumnType / valuesNotes
facilityIdFKOne asset per facility
descriptiontextWhat is being financed
costNUMERIC(20,4)Asset cost
ownershipStatuspending_acquisition | owned_by_ethica | co_owned | transferred_to_customerGate input
acquiredAttimestampSet when Ethica buys the asset
transferredAttimestampSet 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).

ColumnTypeNotes
facilityIdFK
periodintSchedule period this row describes
ethicaOwnershipPctNUMERICEthica's remaining share after this period
customerOwnershipPctNUMERICCustomer'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.

ColumnType / valuesNotes
facilityIdFK
periodint1..N
paymentDatedateDue date
beginningBalanceNUMERIC(20,4)Outstanding at start of period
principalPortionNUMERIC(20,4)Buy-out / cost recovery for the period
profitPortionNUMERIC(20,4)Rent (DM/Ijarah) or profit (Murabaha) for the period
totalPaymentNUMERIC(20,4)principalPortion + profitPortion
endingBalanceNUMERIC(20,4)Outstanding at end of period
ethicaOwnershipPct / customerOwnershipPctNUMERICOwnership at this period
statuspending | paid | partially_paid | overdue
paidAmountNUMERIC(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

ColumnTypeNotes
facilityIdFK
scheduleLineIdFK (nullable)Line(s) the payment is applied to
amountNUMERIC(20,4)Amount received
extraPaymentbooleanTrue if above the period's due amount
paymentDatedate
referencestringOperator-supplied reference
journalEntryIdFK → journal_entriesThe 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.

ColumnTypeNotes
productTypeproduct enummurabaha / ijarah / diminishing_musharakah
eventTypeenume.g. origination, payment, non_accrual_payment
sidedebit | creditWhich side of the journal
glAccountaccount codeThe 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