fix: draft the policy-supported amount, not an invented lending rule

The Approve File draft computed
    min(requested, max_eligible, 25% of the lowest turnover source)
which produced 7,00,000 on a 15,00,000 request.

That 25% rule is nowhere in HDFC-CP-2026.08. It was invented here. Inventing a
lending rule inside a draft credit note is worse than leaving the field empty:
it reads as policy to whoever signs it, and nobody could say where the number
came from. It also contradicted every demo script, which uses 12,00,000 — a
figure with no derivation either.

Clause 7.1 is the only rule that applies: a file may not be offered above the
computed maximum eligible without a documented deviation. So the draft is now
min(requested, computed ceiling) and nothing cleverer.

Whether to cap below that is a JUDGEMENT, which is what the credit manager is
for. The draft remarks now state the fact that bears on it — the lowest of the
three declared turnover figures — and invite him to size against it, without
choosing the number for him.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yashas 2026-08-18 11:11:23 +05:30
parent e745cc5649
commit 8a3ec901b6

View File

@ -51,31 +51,40 @@ export function prefillFor(
switch (activity) {
case ACT.CM_APPROVE: {
// The cap: the most the policy can defend. Where three turnover figures
// disagree, size the loan so the unanswered question stops mattering —
// lend against the LOWEST figure rather than the one we hope is true.
// The draft amount is the POLICY-SUPPORTED figure and nothing cleverer:
// the requested amount, or the computed ceiling if that is lower.
// Clause 7.1 is the only rule here — a file may not be offered above the
// computed maximum eligible without a documented deviation.
//
// An earlier version of this also capped at "25% of the lowest declared
// turnover source", which produced 7,00,000 on a 15,00,000 request. That
// rule is nowhere in HDFC-CP-2026.08. It was invented here, and inventing
// a lending rule inside a draft credit note is worse than leaving the
// field blank: it reads as policy to whoever signs it, and no one would
// be able to say where the number came from.
//
// WHETHER TO CAP IS A JUDGEMENT, and judgement is what the credit manager
// is for. So the draft states the fact that bears on it — the lowest of
// the three turnover figures — and leaves the number to him.
const suggested = maxElig > 0 ? Math.min(requested, maxElig) : requested;
const sources = [itr, gst, bank].filter((v): v is number => v !== null && v > 0);
const lowest = sources.length >= 2 ? Math.min(...sources) : null;
const capByEligibility = maxElig > 0 ? Math.min(requested, maxElig) : requested;
// A quarter of the lowest declared turnover is a conventional working
// ceiling for an unsecured facility of this shape.
const capByLowestSource = lowest !== null ? Math.round((lowest * 0.25) / 100000) * 100000 : null;
const suggested =
capByLowestSource !== null
? Math.min(capByEligibility, capByLowestSource)
: capByEligibility;
const note =
flag === 'income_corroboration' && variance !== null
? `Turnover corroboration spread of ${pct(variance)} noted and accepted. The gap is consistent with exempt or zero-rated sales absent from the ITR computation; GST filings and bank credits agree once that is allowed for. To be re-tested at the next review.`
? `Turnover corroboration spread of ${pct(variance)} noted. The gap is consistent with exempt or zero-rated sales absent from the ITR computation; GST filings and bank credits agree once that is allowed for. Accepted on that basis, to be re-tested at the next review.`
: flag
? `Deviation on ${flag.replace(/_/g, ' ')} noted and accepted at this exposure.`
: '';
const capNote =
lowest !== null && flag === 'income_corroboration'
? ` The lowest of the three declared turnover figures is ${amt(lowest)}; consider whether the facility should be sized against that rather than the requested amount, so that the outstanding corroboration question does not put it at risk.`
: '';
const remarks =
suggested < requested
? `Business verified as genuine — ${str(row.business_vintage_months)} months vintage, GST and Udyam registered, bureau ${str(row.bureau_score)} with no adverse history. Exposure capped at ${amt(suggested)} against ${amt(requested)} requested: an amount serviceable even on the lowest of the three declared turnover figures, so the outstanding corroboration question does not put the facility at risk. Revisit the limit after two further GST filing cycles.`
: `Business verified as genuine — ${str(row.business_vintage_months)} months vintage, GST and Udyam registered, bureau ${str(row.bureau_score)}. Proceeding at the requested amount, which sits inside the computed eligibility of ${amt(maxElig)}.`;
`Business verified as genuine — ${str(row.business_vintage_months)} months vintage, GST and Udyam registered, bureau ${str(row.bureau_score)} with no adverse history. Computed eligibility is ${amt(maxElig)} against ${amt(requested)} requested.${capNote}`;
return { cm_approved_amount: suggested, cm_deviation_note: note, cm_remarks: remarks, maker: me };
}