Merge remote-tracking branch 'origin/uate' into uate

This commit is contained in:
2026-07-28 10:53:26 +05:30
23 changed files with 305 additions and 101 deletions
@@ -61,6 +61,8 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
/** Set by parent after client is created */
@Input() clientId: string = '';
@Input() isNonIndividual = false;
/** Minor clients don't hold a PAN card — hides the PAN section on the individual KYC form */
@Input() isMinor = false;
/** Constitution metadata (panLabel, showCin, etc.) passed from general step */
@Input() constitutionMeta: any = null;
/** Entity full name (non-individual) — used for enterprise PAN verification */
@@ -163,7 +165,9 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
{ name: 'Driving Licence', code: 'DRIVING_LICENSE' },
{ name: 'Voter ID', code: 'VOTER_ID' },
{ name: 'NREGA Job Card', code: 'NREGA_JOB_CARD' },
{ name: 'National Population Register Letter', code: 'NPR_LETTER' }
{ name: 'National Population Register Letter', code: 'NPR_LETTER' },
{ name: 'Birth Certificate', code: 'BIRTH_CERTIFICATE' },
{ name: 'School ID Card', code: 'SCHOOL_ID_CARD' }
];
/** Constitution-based KYC document types for non-individual clients */
private constitutionDocTypes: Record<string, { name: string; mandatory: boolean }[]> = {
@@ -458,15 +462,44 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
form15FY: ['']
});
/** RBI OVDs a minor cannot hold: Voter ID and Driving Licence require adult age,
* PAN and NREGA job cards are issued to adults. Aadhaar, Passport and NPR letters
* are unaffected by age and stay available. */
private static readonly MINOR_INELIGIBLE_DOC_KEYWORDS = [
'pan',
'driving',
'voter',
'nrega'
];
/** Birth Certificate / School ID Card are minor-specific OVDs — not relevant once the
* client is an adult, so they're hidden the other direction. */
private static readonly MINOR_ONLY_DOC_CODES = new Set([
'BIRTH_CERTIFICATE',
'SCHOOL_ID_CARD'
]);
private filterDocTypesForMinor<T extends { name: string; code?: string }>(docs: T[]): T[] {
if (this.isMinor) {
return docs.filter((d) => {
const text = `${d.name || ''} ${d.code || ''}`.toLowerCase();
return !ClientKycStepComponent.MINOR_INELIGIBLE_DOC_KEYWORDS.some((k) => text.includes(k));
});
}
return docs.filter((d) => !ClientKycStepComponent.MINOR_ONLY_DOC_CODES.has((d.code || '').toUpperCase()));
}
ngOnInit(): void {
// Seed defaults immediately so the picker has options before clientId is set
// (template API requires clientId; user can still pick a type pre-save).
if (!this.kycDocumentTypes.length) {
this.kycDocumentTypes = this.defaultDocumentTypes.map((d) => ({
name: d.name,
code: d.code,
mandatory: false
}));
this.kycDocumentTypes = this.filterDocTypesForMinor(
this.defaultDocumentTypes.map((d) => ({
name: d.name,
code: d.code,
mandatory: false
}))
);
this.rebuildKycDocPicker();
}
this.loadExistingKycIfNeeded();
@@ -496,6 +529,21 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
if (changes['constitutionMeta'] && this.isNonIndividual) {
this.updateDocTypesForConstitution();
}
if (changes['isMinor']) {
if (this.isMinor) {
this.kycForm.get('hasPan')?.setValue(false);
this.kycForm.get('panNumber')?.setValue('');
}
const filtered = this.filterDocTypesForMinor(this.kycDocumentTypes);
if (filtered.length !== this.kycDocumentTypes.length) {
this.kycDocumentTypes = filtered;
const currentName = this.kycForm.get('kycDocumentName')?.value;
if (currentName && !filtered.some((d) => d.name === currentName)) {
this.kycForm.get('kycDocumentName')?.setValue('');
}
this.rebuildKycDocPicker();
}
}
}
/** Update KYC document types based on selected constitution type */
@@ -879,11 +927,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
this.kycTemplate = template;
this.kycTemplateLoaded = true;
if (template?.requiredDocuments?.length > 0) {
this.kycDocumentTypes = template.requiredDocuments.map((doc: any) => ({
name: doc.name,
code: doc.code || doc.name,
mandatory: doc.mandatory ?? false
}));
this.kycDocumentTypes = this.filterDocTypesForMinor(
template.requiredDocuments.map((doc: any) => ({
name: doc.name,
code: doc.code || doc.name,
mandatory: doc.mandatory ?? false
}))
);
// Update kycDocumentName validator based on mandatory docs
const hasMandatory = this.kycDocumentTypes.some((d) => d.mandatory);
if (hasMandatory) {
@@ -896,11 +946,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
error: () => {
// Fallback to defaults if template API unavailable
this.kycTemplateLoaded = true;
this.kycDocumentTypes = this.defaultDocumentTypes.map((d) => ({
name: d.name,
code: d.code,
mandatory: false
}));
this.kycDocumentTypes = this.filterDocTypesForMinor(
this.defaultDocumentTypes.map((d) => ({
name: d.name,
code: d.code,
mandatory: false
}))
);
this.rebuildKycDocPicker();
}
});