feat(loan-origination): tabbed CIC report dialog with real PDF parsing via pdfjs-dist
Tests / testRigor (push) Canceled after 0s
build-run / Run Lint, Build and Deploy (push) Canceled after 0s
Publish Image in Docker Hub / build (push) Canceled after 0s
Playwright E2E Tests / Run Playwright Tests (push) Canceled after 0s

Extracts score, accounts, DPD payment history and enquiries directly from an
uploaded bureau PDF (position-aware parsing, so blank DPD months are handled
correctly) instead of always showing a simulated report, falling back to the
simulated analysis when a file can't be read.
This commit is contained in:
2026-08-13 18:05:22 +05:30
parent 997e8920e4
commit 8d67a22f18
11 changed files with 2526 additions and 434 deletions
@@ -29,7 +29,12 @@ import {
summarizeBankStatement
} from './bank-statement-analysis-dialog/bank-statement-analysis.util';
import { CicReportAnalysisDialogComponent } from './cic-report-analysis-dialog/cic-report-analysis-dialog.component';
import { generateCicReportAnalysis } from './cic-report-analysis-dialog/cic-report-analysis.util';
import {
CicReportAnalysis,
CIC_KNOWN_BUREAUS,
generateCicReportAnalysis
} from './cic-report-analysis-dialog/cic-report-analysis.util';
import { parseCicPdfReport } from './cic-report-analysis-dialog/cic-pdf-parser.util';
import { FieldVisitDetailsDialogComponent } from './field-visit-details-dialog/field-visit-details-dialog.component';
import {
ApprovalField,
@@ -2050,24 +2055,51 @@ I confirm that:
}
// -------------------- CIC Upload --------------------
onCicFileSelected(event: Event, a: LoanApplication): void {
async onCicFileSelected(event: Event, a: LoanApplication): Promise<void> {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) {
return;
}
let parsed: CicReportAnalysis | null = null;
if (file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf')) {
parsed = await parseCicPdfReport(file);
}
if (parsed) {
const knownBureau = (CIC_KNOWN_BUREAUS as readonly string[]).includes(parsed.bureau)
? parsed.bureau
: a.cicBureau;
a.cicBureau = knownBureau as LoanApplication['cicBureau'];
a.cibilScore = parsed.score;
a.cicParsedAnalysis = parsed;
a.cicAnalysisSource = 'PARSED';
} else {
a.cicParsedAnalysis = generateCicReportAnalysis(a);
a.cicAnalysisSource = 'SIMULATED';
}
uploadCicReport(a, file.name, this.currentUserName);
input.value = '';
this.openCicReportAnalysis(a);
}
/** No real bureau API or PDF parser sits behind this mock pipeline, so the analysis shown here
* is generated (deterministically, seeded off the application) from the cibilScore the "Check
* Credit Score" step already pulled -- not read from the actual uploaded file. Auto-opens right
* after upload, and reopenable afterwards via the "View Analysis" pill next to the Report row. */
/** Reopens whatever analysis is already on the application -- the real parsed one if the last
* upload actually yielded one, otherwise the simulated fallback -- generating and caching the
* simulated version on first view rather than a fresh (and pointlessly different) one every
* time this is clicked. */
openCicReportAnalysis(a: LoanApplication): void {
if (!a.cicParsedAnalysis) {
a.cicParsedAnalysis = generateCicReportAnalysis(a);
a.cicAnalysisSource = 'SIMULATED';
}
this.dialog.open(CicReportAnalysisDialogComponent, {
data: { fileName: a.cicReportFileName, analysis: generateCicReportAnalysis(a) },
data: {
fileName: a.cicReportFileName,
analysis: a.cicParsedAnalysis as CicReportAnalysis,
wasParsedFromFile: a.cicAnalysisSource === 'PARSED'
},
width: '960px',
maxWidth: '95vw'
});