diff --git a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html
index f139711f3..65d2d97da 100644
--- a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html
+++ b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html
@@ -1321,6 +1321,11 @@
kycForm.get('kycDocumentNumber')?.hasError('pattern') && kycForm.get('kycDocumentNumber')?.touched
) {
Only letters, numbers, hyphens and slashes allowed
+ } @else if (
+ kycForm.get('kycDocumentNumber')?.hasError('aadhaarLast4Mismatch') &&
+ kycForm.get('kycDocumentNumber')?.touched
+ ) {
+ Last 4 digits do not match the Aadhaar number entered above
}
diff --git a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts
index 00d2afccd..cfca4fb8c 100644
--- a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts
+++ b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts
@@ -8,7 +8,14 @@
import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges, inject } from '@angular/core';
import { forkJoin, catchError, of } from 'rxjs';
-import { UntypedFormBuilder, UntypedFormGroup, Validators, FormsModule } from '@angular/forms';
+import {
+ UntypedFormBuilder,
+ UntypedFormGroup,
+ Validators,
+ FormsModule,
+ AbstractControl,
+ ValidationErrors
+} from '@angular/forms';
import {
MatDialog,
MAT_DIALOG_DATA,
@@ -567,6 +574,28 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
/** Existing signature document ID (for re-upload/delete) */
existingSignatureDocId: string = '';
+ /**
+ * Cross-field check for the OVD "Document Number" field (kycDocumentNumber): when Aadhaar Card
+ * is the selected KYC document, its value must identify the same Aadhaar as the aadhaarNumber
+ * field captured earlier in this step - the last 4 digits are what's compared, since
+ * aadhaarNumber may hold either the raw 12-digit number or a masked value (masking only ever
+ * obscures the first 8 digits - see aadhaarMasked in the kycData getter below - so the last 4
+ * characters are always the real digits either way). Not applicable to any other document type,
+ * where kycDocumentNumber is unrelated to Aadhaar entirely.
+ */
+ private aadhaarLast4MatchValidator = (control: AbstractControl): ValidationErrors | null => {
+ if (this.selectedKycDoc?.code !== 'AADHAAR') {
+ return null;
+ }
+ const docNumber = (control.value || '').toString().trim();
+ const aadhaarValue = (control.parent?.get('aadhaarNumber')?.value || '').toString().trim();
+ if (docNumber.length < 4 || aadhaarValue.length < 4) {
+ // Required/pattern validators already flag an empty or too-short value on their own.
+ return null;
+ }
+ return docNumber.slice(-4) === aadhaarValue.slice(-4) ? null : { aadhaarLast4Mismatch: true };
+ };
+
/** KYC Form */
kycForm: UntypedFormGroup = this.formBuilder.group({
hasPan: [true],
@@ -616,7 +645,8 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
[
Validators.required,
Validators.pattern(/^[A-Za-z0-9\-\/\s]+$/),
- Validators.maxLength(30)
+ Validators.maxLength(30),
+ this.aadhaarLast4MatchValidator
]
],
kycDocIssueDate: [''],
@@ -739,6 +769,14 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
this.kycForm.get('ecgcSalFlag')?.disable();
this.kycForm.get('dormantFlag')?.disable();
}
+
+ // aadhaarLast4MatchValidator (on kycDocumentNumber) reads this control's value directly rather
+ // than through its own valueChanges, so a re-check has to be triggered explicitly whenever the
+ // Aadhaar field itself changes - otherwise fixing aadhaarNumber after kycDocumentNumber was
+ // already entered would leave a stale mismatch error in place.
+ this.kycForm.get('aadhaarNumber')?.valueChanges.subscribe(() => {
+ this.kycForm.get('kycDocumentNumber')?.updateValueAndValidity({ onlySelf: true });
+ });
}
/** Seed the document-type picker from defaults (entity RBI docs vs. individual OVDs,
@@ -1009,6 +1047,9 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
// Reset uploads + merged preview when document type changes
this.kycDocFiles = { front: null, back: null };
this.kycDocMergedPreview = null;
+ // aadhaarLast4MatchValidator only applies while Aadhaar Card is selected - re-check so switching
+ // to/away from it immediately picks up (or clears) the mismatch error.
+ this.kycForm.get('kycDocumentNumber')?.updateValueAndValidity({ onlySelf: true });
}
isKycDocSelected(proof: ProofType): boolean {
@@ -1189,6 +1230,9 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
// Sync chip-picker selection with the prefilled document name
if (data.kycDocumentName) {
this.selectedKycDoc = this.findKycDocProofByName(data.kycDocumentName);
+ // patchValue above ran kycDocumentNumber's validators before selectedKycDoc was known,
+ // so aadhaarLast4MatchValidator needs a fresh check now that it is.
+ this.kycForm.get('kycDocumentNumber')?.updateValueAndValidity({ onlySelf: true });
}
// Set verification statuses from existing data
if (data.panVerified) {