feat(loan-origination): add full Loan Origination Sanctioning workflow module

New standalone module modelling the pre-disbursement pipeline: Channel intake
(Branch/BC/Self-Apply) -> KYC/OVD & e-KYC fingerprint -> CIC/Credit Bureau
check -> Field Investigation -> Personal Discussion & declaration -> Loan
Committee -> Risk Approval -> Legal Approval -> Customer Signing -> Cheque/ACH
-> Post-Disbursement Check -> Disbursement, per RBI KYC Master Direction, the
mandatory CIC check, and co-operative-bank share-membership rules.

Includes a 12-stage stepper with real approve/reject actions and a date-wise
audit trail, CIC upload + credit-score check, Field Inquiry push + Agent
Report upload, Committee "Reports Required" / Risk "Verification Checklist",
Head-Office-vs-branch scoping, a Pipeline screen (filters, New Application
intake, Channel-wise/BC-wise/Share-Member reports, Disbursement Target vs
Actual, Export to Excel), and a full EMI repayment schedule once disbursed.
This commit is contained in:
2026-08-02 14:22:32 +05:30
parent b4608cbbe4
commit 5cd9998cfc
10 changed files with 4474 additions and 0 deletions
@@ -0,0 +1,322 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// Loan Origination-specific styling layered on top of ../cashier/cashier-shared.scss (pg-*,
// cctr-*) and ../npa/npa-shared.scss (npa-timeline-* — reused as-is for the application's stage
// history/audit trail, since it's the same generic "date-led event log" primitive already built
// for the NPA Recovery Journey popup, not NPA-specific).
// EMI Repayment Schedule can run to 240 rows (a 20-year Housing Loan) — scrolls within a fixed
// height with the header pinned, rather than pushing the rest of the page far down.
.loan-emi-table-scroll {
max-height: 420px;
overflow-y: auto;
thead th {
position: sticky;
top: 0;
z-index: 1;
background: #fff;
}
}
.loan-badge {
display: inline-flex;
align-items: center;
padding: 3px 10px;
border-radius: 999px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
white-space: nowrap;
&--progress {
background: #eff6ff;
color: #1d4ed8;
border: 1px solid #bfdbfe;
}
&--hold {
background: #fffbeb;
color: #b45309;
border: 1px solid #fde68a;
}
&--rejected {
background: #fef2f2;
color: #dc2626;
border: 1px solid #fecaca;
}
&--disbursed {
background: #f0fdf4;
color: #15803d;
border: 1px solid #bbf7d0;
}
}
.pg-btn--success {
background: linear-gradient(135deg, #22c55e, #15803d);
color: #fff;
&:disabled {
opacity: 0.45;
cursor: not-allowed;
}
}
.pg-btn--danger {
background: #fff;
border-color: #fecaca;
color: #dc2626;
&:hover {
background: #fef2f2;
}
&:disabled {
opacity: 0.45;
cursor: not-allowed;
}
}
.pg-btn--warn {
background: #fff;
border-color: #fde68a;
color: #b45309;
&:hover {
background: #fffbeb;
}
}
// ---------------------------------------------------------------------------
// Stage stepper — the 12-stage sanctioning pipeline, drawn as a connected row of
// dots so an application's progress reads at a glance.
// ---------------------------------------------------------------------------
.loan-stepper {
display: flex;
align-items: flex-start;
overflow-x: auto;
padding: 22px 18px 16px;
}
.loan-step {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
min-width: 90px;
position: relative;
&__connector {
position: absolute;
top: 15px;
left: -50%;
width: 100%;
height: 2px;
background: #e2e8f0;
z-index: 0;
}
&:first-child &__connector {
display: none;
}
&__dot {
position: relative;
z-index: 1;
width: 32px;
height: 32px;
border-radius: 999px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 700;
background: #f1f5f9;
color: #94a3b8;
border: 2px solid #e2e8f0;
}
&__label {
margin-top: 8px;
font-size: 10.5px;
text-align: center;
color: #64748b;
font-weight: 600;
line-height: 1.3;
max-width: 92px;
}
&--done {
.loan-step__dot {
background: #15803d;
border-color: #15803d;
color: #fff;
}
.loan-step__connector {
background: #86efac;
}
.loan-step__label {
color: #15803d;
}
}
&--current {
.loan-step__dot {
background: #4338ca;
border-color: #4338ca;
color: #fff;
box-shadow: 0 0 0 4px rgb(67 56 202 / 15%);
}
.loan-step__label {
color: #4338ca;
font-weight: 800;
}
}
&--rejected {
.loan-step__dot {
background: #dc2626;
border-color: #dc2626;
color: #fff;
}
.loan-step__label {
color: #dc2626;
}
}
&--hold {
.loan-step__dot {
background: #d97706;
border-color: #d97706;
color: #fff;
}
.loan-step__label {
color: #d97706;
}
}
}
// ---------------------------------------------------------------------------
// Action card — the current stage's checklist/inputs and Approve/Reject buttons.
// ---------------------------------------------------------------------------
.loan-action-card {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 16px 18px;
margin: 0 16px 16px;
}
.loan-checklist {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 14px;
}
.loan-checklist-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 13px;
color: #1e293b;
// stylelint-disable-next-line selector-no-qualifying-type
input[type='checkbox'] {
width: 16px;
height: 16px;
accent-color: #4338ca;
}
}
.loan-action-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
margin-top: 12px;
}
.loan-kv-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 12px 20px;
padding: 18px;
}
.loan-kv-field {
display: flex;
flex-direction: column;
gap: 3px;
dt {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #64748b;
}
dd {
margin: 0;
font-size: 14px;
font-weight: 600;
color: #0d1b2a;
font-variant-numeric: tabular-nums;
}
}
.loan-approval-summary {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 12px;
padding: 4px 18px 18px;
}
.loan-approval-tile {
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 4px;
&__role {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #64748b;
}
&__decision {
font-size: 13px;
font-weight: 700;
}
&--approved .loan-approval-tile__decision {
color: #15803d;
}
&--rejected .loan-approval-tile__decision {
color: #dc2626;
}
&--pending .loan-approval-tile__decision {
color: #94a3b8;
}
}