SourceModule enum extension (Phase 0 Task 0.1)
Status: ✅ Complete
Plan reference: Task 0.1 in 2026-04-30-FINANCE_MODULE_IMPLEMENTATION_PLAN.md (operations workspace root)
What it is
SourceModule is a string-literal union used on every JournalEntry. It tells consumers (reports, reconciliation, posting handlers) which kind of business event produced the entry. Phase 0 extended the enum so every posting flow that Phases 1–3 will introduce has a dedicated tag.
Where it lives
- API (authoritative) —
ethica-api/src/models/journal-entry.model.ts:18-35 - ERP (mirror) —
ethica-erp/types/finance.ts:10-27
Both must list the same values in the same order. The mirror is a manual copy because the ERP is built from a different package boundary; there is no shared types package today.
The values
| Value | Posted by |
|---|---|
manual | Direct journal entry created via Finance → Accounting transactions |
trade | Legacy generic trade postings (predates equity/sukuk split) |
subscription | Investment subscription approval (investments.service.ts) |
redemption | Redemption pricing journal (Phase 1.D) |
nav_adjustment | NAV publish (Phase 1.C) |
fx_revaluation | Daily FX revaluation cron (Phase 2.C) |
purification | Haram-income allocation to purification reserve (Phase 2.A) |
management_fee | Daily management/trustee/custody fee accrual (Phase 2.D) |
payroll | Salary run integration (Phase 3.A) |
dividend | Equity dividend receipt with halal/haram split (Phase 2.D) |
sukuk_income | Sukuk profit accrual + receipt (Phase 2.D) |
mudaraba_profit | Mudaraba/Wakala profit distribution (Phase 3.A) |
equity_trade | Equity buy/sell posting (Phase 1.D) |
zakat | Zakat expense + payable + payment (Phase 2.B) |
depreciation | Monthly fixed-asset depreciation (Phase 3.A) |
redemption_payment | Payout leg of a redemption (Phase 1.D) |
month_end_close | Month-end orchestrator postings (Phase 2.E) |
Design rationale
- Why a string-literal union, not a DB enum? Sequelize-typescript treats this as
DataType.STRING(32). Adding a value requires no migration. The trade-off is no DB-level constraint, but our posting engine validates against the union at write time. - Why include all future values now? So Phases 1–3 add posting handlers without touching the central model file repeatedly. Each value is harmless until a handler emits it.
- Why mirror manually in ERP? Avoids a shared package dependency. A drift test on first invocation of a handler will catch missed mirror updates.
Edge cases
- Switch statements over
SourceModule(search:grep -rn "SourceModule" ethica-api/src/ ethica-erp/) currently rely on string equality — no exhaustiveswitchexists yet, so adding a value silently passes. Phase 1.B will introduce an exhaustiveSourceModuleHandlerregistry that compile-fails on missing handlers.
Tests
- Compile-time: TypeScript narrows correctly across both packages.
- Runtime: existing journal-entry unit tests already exercise
manual,subscription,trade. New values are exercised by the handlers that emit them.