feat: add KYC document renewal alerts, Aadhaar/PAN exceptions, and AML recurring alerts UI; remove old journal AML marking
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
Tests / testRigor (push) Canceled after 0s

This commit is contained in:
2026-08-11 11:11:18 +05:30
parent 01e86173c1
commit 7b895a0e2b
77 changed files with 10046 additions and 1130 deletions
@@ -1021,6 +1021,9 @@ export class EditClientComponent {
* Mirrors identities-tab.createIdentifierWithDocs.
*/
private saveAddressProof(clientId: string) {
if (this.clientIndiaAddressStep?.existingAddressProofIdentifierId) {
return;
}
const payload = this.clientIndiaAddressStep?.addressProofPayload;
if (!payload?.proofId) return;
@@ -1036,6 +1039,9 @@ export class EditClientComponent {
next: (res: any) => {
const identifierId = res?.resourceId;
if (!identifierId) return;
if (this.clientIndiaAddressStep) {
this.clientIndiaAddressStep.existingAddressProofIdentifierId = identifierId.toString();
}
for (const entry of payload.files) {
const formData = new FormData();
formData.append('name', `${proofName} — ${entry.slot}`);
@@ -1056,6 +1062,21 @@ export class EditClientComponent {
if (!payload?.proofId || !payload?.files?.length) return;
const proofName = payload.proof?.name || 'KYC Document';
const uploadFiles = (identifierId: string) => {
for (const entry of payload.files) {
const formData = new FormData();
formData.append('name', `${proofName} — ${entry.slot}`);
formData.append('file', entry.file);
this.clientsService.uploadClientIdentifierDocument(identifierId, formData).subscribe({ error: () => {} });
}
};
if (this.clientKycStep?.existingKycIdentifierId) {
uploadFiles(this.clientKycStep.existingKycIdentifierId);
return;
}
// The KYC step's chip-picker carries `id` as a doc-type CODE string (e.g. "AADHAAR").
// The backend ClientIdentifierRequest.documentTypeId is a Long, so a string proofId
// trips Jackson → HttpMessageNotReadableException → 400. Resolve to numeric CodeValue
@@ -1075,12 +1096,10 @@ export class EditClientComponent {
next: (res: any) => {
const identifierId = res?.resourceId;
if (!identifierId) return;
for (const entry of payload.files) {
const formData = new FormData();
formData.append('name', `${proofName} — ${entry.slot}`);
formData.append('file', entry.file);
this.clientsService.uploadClientIdentifierDocument(identifierId, formData).subscribe({ error: () => {} });
if (this.clientKycStep) {
this.clientKycStep.existingKycIdentifierId = identifierId.toString();
}
uploadFiles(identifierId.toString());
},
error: () => {}
});
@@ -1098,8 +1117,45 @@ export class EditClientComponent {
return this.clientsService.getKycProofTypes('id').pipe(
map((res: any) => {
const list: any[] = res?.idProofs || [];
const targetName = (proofName || '').trim().toLowerCase();
const match = list.find((p: any) => (p?.name || '').trim().toLowerCase() === targetName);
const cleanStr = (s: string) => (s || '').toLowerCase().replace(/[^a-z0-9]/g, '');
const target = cleanStr(proofName || proofId);
// 1. Try exact code/name match
let match = list.find((p: any) => {
const pcode = cleanStr(p.code);
const pname = cleanStr(p.name);
return pcode === target || pname === target;
});
// 2. Try loose contains/includes match
if (!match) {
match = list.find((p: any) => {
const pcode = cleanStr(p.code);
const pname = cleanStr(p.name);
return pcode.includes(target) || pname.includes(target) || target.includes(pcode) || target.includes(pname);
});
}
// 3. Specific fallbacks for common keywords
if (!match && target.includes('voter')) {
match = list.find((p: any) => {
const name = cleanStr(p.name);
return name.includes('voter') || name.includes('epic');
});
}
if (!match && target.includes('aadhaar')) {
match = list.find((p: any) => cleanStr(p.name).includes('aadhaar'));
}
if (!match && target.includes('passport')) {
match = list.find((p: any) => cleanStr(p.name).includes('passport'));
}
if (!match && target.includes('driving')) {
match = list.find((p: any) => cleanStr(p.name).includes('drive') || cleanStr(p.name).includes('licen'));
}
if (!match && target.includes('pan')) {
match = list.find((p: any) => cleanStr(p.name).includes('pan'));
}
return (match?.id ?? null) as number | null;
})
);