diff --git a/src/app/npa/npa-data.ts b/src/app/npa/npa-data.ts index 60dc67d56..0f0a79921 100644 --- a/src/app/npa/npa-data.ts +++ b/src/app/npa/npa-data.ts @@ -967,6 +967,7 @@ export const REPORT_META: ReportMeta[] = [ { id: 'probable', group: 'classification', label: 'Probable NPA' }, { id: 'sma', group: 'classification', label: 'SMA Report' }, { id: 'recovery-dashboard', group: 'recovery-mgmt', label: 'Recovery Management Dashboard' }, + { id: 'cc-recovery', group: 'recovery-mgmt', label: 'Cash Credit (CC) Recovery Management' }, { id: 'daywise-overdue', group: 'recovery-mgmt', label: 'Day-wise Overdue Report' }, { id: 'daywise-overdrawn', group: 'recovery-mgmt', label: 'Day-wise Overdrawn Report' }, { id: 'agri-gold-value', group: 'recovery-mgmt', label: 'Agriculture / Gold Market Value Report' }, diff --git a/src/app/npa/npa-reports/npa-reports.component.html b/src/app/npa/npa-reports/npa-reports.component.html index 9211bbdc8..9ad416d85 100644 --- a/src/app/npa/npa-reports/npa-reports.component.html +++ b/src/app/npa/npa-reports/npa-reports.component.html @@ -1400,6 +1400,184 @@ } + + @if (activeReport === 'cc-recovery') { +
+
+ Cash Credit (CC) Recovery Management — Drawing Power & Overdrawn-Day Tracking +
+

+ Cash Credit is a revolving working-capital facility secured against stock / receivables. Unlike a Term Loan, it + isn't checked against a fixed instalment — it is checked against its Drawing Power (DP), and + tracked by how many continuous Days Overdrawn ("Out of Order") it has run — crossing 90 days + turns it NPA. +

+
+
+ +
Drawing Power (DP)
+

+ The maximum amount the borrower may actually draw against the sanctioned CC limit — fixed from the value of + stock / book-debts declared in the latest stock statement. Can be lower than the sanctioned limit. +

+
+
+ +
Out of Order
+

+ A CC account turns "Out of Order" once its outstanding continuously stays above the limit/DP, or adequate + credits stop coming in, for 90 days — the CC-specific overdue trigger, in place of a missed EMI. +

+
+
+ +
Days Overdrawn
+

+ The running count of continuous days an account has stayed Out of Order — the number a recovery desk + actually watches day to day, instead of a classification label. A rising count is the early-warning sign. +

+
+
+ +
NPA (Non-Performing Asset)
+

+ Once Out of Order crosses 90 days, the CC account slips to NPA — Sub-Standard, then Doubtful-1/2/3, then + Loss Asset as it ages further. +

+
+
+
+ +
+
+
+ + Total CC Accounts + {{ ccAccounts.length }} + ₹{{ ccTotalSanctioned | number: '1.0-0' }} sanctioned +
+
+ + Overdrawn (1–90 Days) + {{ ccOverdrawnCount }} + Out of Order, not yet NPA +
+
+ + NPA (90+ Days Overdrawn) + {{ ccNpaCount }} + ₹{{ ccTotalOutstanding | number: '1.0-0' }} total outstanding +
+
+ + Interest Paid (This Cycle) + ₹{{ ccInterestPaidThisCycle | number: '1.0-0' }} + of ₹{{ ccInterestDueThisCycle | number: '1.0-0' }} due +
+
+ + Total Recovered + ₹{{ ccTotalRecovered | number: '1.0-0' }} + {{ ccRecoveryPct | number: '1.1-1' }}% of ₹{{ ccTotalSanctioned | number: '1.0-0' }} sanctioned +
+
+
+ +
+
+ Cash Credit Accounts — Limit, DP & Classification ({{ ccOutOfOrderCount }} of {{ ccAccounts.length }} Out of + Order) +
+
+ + + + + + + + + + + + + + + + + + + + + @for (a of paged(ccAccounts); track a.accountNo) { + + + + + + + + + + + + + + + + + } @empty { + + + + } + +
Account NoCustomerBranchSanctioned LimitDrawing Power (DP)OutstandingPending to RecoverExcess Over DPInterest Due (This Cycle)Interest Paid (This Cycle)Total RecoveredDays OverdrawnStatusRecovery Officer
+ {{ a.accountNo }} + {{ a.customerName }}{{ a.branchName }}₹{{ a.sanctionedAmount | number: '1.0-0' }}₹{{ a.drawingPower | number: '1.0-0' }}₹{{ a.outstandingBalance | number: '1.0-0' }}₹{{ a.overdueAmount | number: '1.0-0' }}₹{{ excessOverDp(a) | number: '1.0-0' }}₹{{ expectedThisMonth(a) | number: '1.0-0' }} + ₹{{ actualThisMonth(a) | number: '1.0-0' }} + ₹{{ recovered(a) | number: '1.0-0' }}{{ isNpaStageOf(a) || overdrawnBreachOf(a) ? a.overdueDays : 0 }} + + {{ isNpaStageOf(a) ? 'NPA' : overdrawnBreachOf(a) ? 'Overdrawn' : 'Regular' }} + + {{ a.recoveryOfficer }}
No Cash Credit accounts in this branch.
+
+ @if (ccAccounts.length > pageSize) { +
+ Showing {{ rangeStart(ccAccounts) }}–{{ rangeEnd(ccAccounts) }} of {{ ccAccounts.length }} +
+ + Page {{ currentPage }} of {{ pageCount(ccAccounts) }} + +
+
+ } +
+ } + @if (activeReport === 'agri-gold-value') {
diff --git a/src/app/npa/npa-reports/npa-reports.component.ts b/src/app/npa/npa-reports/npa-reports.component.ts index f9f62f9cd..678eac397 100644 --- a/src/app/npa/npa-reports/npa-reports.component.ts +++ b/src/app/npa/npa-reports/npa-reports.component.ts @@ -660,6 +660,69 @@ export class NpaReportsComponent implements OnInit, OnDestroy { return this.overdrawnAccounts.filter((a) => this.overdrawnBreachOf(a)).length; } + // -------------------- Cash Credit (CC) Recovery Management -------------------- + /** Pure Cash Credit (MSME) accounts only — product code '01' — kept separate from the combined + * Overdrawn (CC/OD/KCC) bucket so CC's own Drawing Power / Out-of-Order / SMA-NPA position can + * be reviewed on its own, the way a recovery desk actually works a CC book. */ + get ccAccounts(): LoanAccount[] { + return this.allAccounts + .filter((a) => a.productCode === '01') + .sort((a, b) => this.excessOverDp(b) - this.excessOverDp(a)); + } + + /** Any CC account that's irregular — either drawn beyond its Drawing Power right now, or already + * NPA-staged from its overdrawn history (an NPA account doesn't "go Regular" just because it's + * briefly back within DP — RBI upgrade norms require full clearance first). Covers the same + * population the 1–90-day Overdrawn and 90+-day NPA buckets below split in two. */ + get ccOutOfOrderCount(): number { + return this.ccAccounts.filter((a) => this.overdrawnBreachOf(a) || this.isNpaStageOf(a)).length; + } + + /** Out of Order for 1–90 continuous days — the pre-NPA monitoring window a recovery desk tracks + * by plain day-count, not by an SMA-0/1/2 label (that split only matters for regulatory + * reporting, not the day-to-day collection screen). */ + get ccOverdrawnCount(): number { + return this.ccAccounts.filter((a) => this.overdrawnBreachOf(a) && !this.isNpaStageOf(a)).length; + } + + /** Out of Order for more than 90 continuous days — the point RBI norms classify a CC/OD account + * as NPA. */ + get ccNpaCount(): number { + return this.ccAccounts.filter((a) => this.isNpaStageOf(a)).length; + } + + get ccTotalSanctioned(): number { + return this.ccAccounts.reduce((s, a) => s + a.sanctionedAmount, 0); + } + + get ccTotalOutstanding(): number { + return this.ccAccounts.reduce((s, a) => s + a.outstandingBalance, 0); + } + + /** A Cash Credit account has no fixed EMI — each cycle's "due" amount is interest servicing on + * the drawn balance, so this total doubles as "how much interest was CC customers due to pay + * this cycle". */ + get ccInterestDueThisCycle(): number { + return this.ccAccounts.reduce((s, a) => s + this.expectedThisMonth(a), 0); + } + + /** How much of that interest the CC book has actually paid this cycle — the answer to "which + * customers have paid how much interest", summed across the Cash Credit book. */ + get ccInterestPaidThisCycle(): number { + return this.ccAccounts.reduce((s, a) => s + this.actualThisMonth(a), 0); + } + + /** Lifetime cash recovered against the CC book (recorded recovery transactions — cash deposits, + * cheques, NEFT/RTGS, auction/SARFAESI, OTS) — the answer to "loan diya kitna, recover kitna + * hua", measured against what was actually sanctioned. */ + get ccTotalRecovered(): number { + return this.ccAccounts.reduce((s, a) => s + this.recovered(a), 0); + } + + get ccRecoveryPct(): number { + return this.ccTotalSanctioned ? (this.ccTotalRecovered / this.ccTotalSanctioned) * 100 : 0; + } + // -------------------- Agriculture / Gold Market Value Report -------------------- get agriGoldAccounts(): LoanAccount[] { return this.allAccounts @@ -1617,6 +1680,63 @@ export class NpaReportsComponent implements OnInit, OnDestroy { } ]; + case 'cc-recovery': + return [ + { + sheetName: 'Cash Credit Recovery', + title: 'Cash Credit (CC) Recovery Management — Drawing Power & Overdrawn-Day Tracking', + subtitle, + headers: [ + 'Account No', + 'Customer', + 'Branch', + 'Sanctioned Limit', + 'Drawing Power', + 'Outstanding', + 'Pending to Recover', + 'Excess Over DP', + 'Interest Due (This Cycle)', + 'Interest Paid (This Cycle)', + 'Total Recovered', + 'Days Overdrawn', + 'Status', + 'Recovery Officer' + ], + widths: [ + 16, + 22, + 16, + 16, + 16, + 16, + 16, + 16, + 18, + 18, + 18, + 14, + 14, + 20 + ], + rows: this.ccAccounts.map((a) => [ + a.accountNo, + a.customerName, + a.branchName, + a.sanctionedAmount, + a.drawingPower, + a.outstandingBalance, + a.overdueAmount, + this.excessOverDp(a), + this.expectedThisMonth(a), + this.actualThisMonth(a), + this.recovered(a), + this.isNpaStageOf(a) || this.overdrawnBreachOf(a) ? a.overdueDays : 0, + this.isNpaStageOf(a) ? 'NPA' : this.overdrawnBreachOf(a) ? 'Overdrawn' : 'Regular', + a.recoveryOfficer + ]) + } + ]; + case 'daywise-overdrawn': return [ { diff --git a/src/app/npa/npa-shared.scss b/src/app/npa/npa-shared.scss index 419e908f0..f66d106c2 100644 --- a/src/app/npa/npa-shared.scss +++ b/src/app/npa/npa-shared.scss @@ -94,6 +94,165 @@ grid-template-columns: repeat(3, 1fr); } +// ----------------------------------------------------------------------- +// Cash Credit Recovery Management — a glossary strip explaining DP / Out of +// Order / SMA / NPA to a reader meeting the terms for the first time (e.g. a +// stakeholder demo), plus tone-coloured accents for that screen's KPI row. +// ----------------------------------------------------------------------- +.npa-glossary { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); + gap: 14px; + margin-top: 18px; +} + +.npa-glossary-card { + padding: 16px 16px 16px 18px; + border-radius: 12px; + background: #fff; + border: 1px solid #e2e8f0; + border-left: 4px solid #64748b; + transition: box-shadow 0.15s ease; + + &:hover { + box-shadow: 0 4px 14px rgb(15 23 42 / 8%); + } + + &__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border-radius: 999px; + background: #f1f5f9; + color: #475569; + font-size: 14px; + margin-bottom: 10px; + } + + &__term { + font-size: 12px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.05em; + color: #0f172a; + margin-bottom: 6px; + } + + &__desc { + font-size: 12.5px; + line-height: 1.55; + color: #475569; + margin: 0; + } + + &--dp { + border-left-color: #2563eb; + + .npa-glossary-card__icon { + background: #dbeafe; + color: #2563eb; + } + } + + &--ooo { + border-left-color: #d97706; + + .npa-glossary-card__icon { + background: #fef3c7; + color: #b45309; + } + } + + &--sma { + border-left-color: #ca8a04; + + .npa-glossary-card__icon { + background: #fef9c3; + color: #854d0e; + } + } + + &--npa { + border-left-color: #dc2626; + + .npa-glossary-card__icon { + background: #fee2e2; + color: #dc2626; + } + } +} + +// Compact gap variant for a KPI row that otherwise uses npa-kpis--ratio's 4-wide grid — used by +// the CC Recovery Management summary row, which packs more (smaller) tiles than the main header. +.npa-kpis--compact { + gap: 8px; +} + +// Tone-coloured KPI tile for the CC summary row — same .inv-kpi tile, with a left accent + icon +// chip so "Out of Order"/"SMA"/"NPA" read as a severity strip rather than four identical boxes. +// Sized smaller than the default .inv-kpi tile since this row packs 6 of them. +.npa-kpi--accent { + padding: 10px 12px 10px 14px; + border-left: 4px solid var(--npa-kpi-accent, #64748b); + + .inv-kpi__label { + font-size: 9px; + } + + .inv-kpi__value { + font-size: 16px; + } + + .inv-kpi__sub { + font-size: 10px; + } + + .npa-kpi__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border-radius: 999px; + background: var(--npa-kpi-accent-soft, #f1f5f9); + color: var(--npa-kpi-accent, #475569); + font-size: 10px; + margin-bottom: 4px; + } + + &--neutral { + --npa-kpi-accent: #0f4f3c; + --npa-kpi-accent-soft: #dcfce7; + } + + &--warn { + --npa-kpi-accent: #d97706; + --npa-kpi-accent-soft: #fef3c7; + } + + &--sma { + --npa-kpi-accent: #ca8a04; + --npa-kpi-accent-soft: #fef9c3; + } + + &--npa { + --npa-kpi-accent: #dc2626; + --npa-kpi-accent-soft: #fee2e2; + } + + &--info { + --npa-kpi-accent: #2563eb; + --npa-kpi-accent-soft: #dbeafe; + } + + &--positive { + --npa-kpi-accent: #15803d; + --npa-kpi-accent-soft: #dcfce7; + } +} + // Account Detail header — title/badge on the left, the Recovery Journey trigger pinned right. .npa-detail-header { display: flex; diff --git a/src/environments/.env.ts b/src/environments/.env.ts index 5cf0d7b56..c7a2f2020 100644 --- a/src/environments/.env.ts +++ b/src/environments/.env.ts @@ -9,8 +9,8 @@ /* tslint:disable */ export default { 'mifos_x': { - 'version': '261012', - 'hash': '6b0f218b' + 'version': '261019', + 'hash': '1b70fb453' }, 'allow_switching_backend_instance': true };