feat: add KYC document renewal alerts, Aadhaar/PAN exceptions, and AML recurring alerts UI; remove old journal AML marking
This commit is contained in:
+57
-12
@@ -33,6 +33,7 @@ import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicke
|
||||
/** Custom Services */
|
||||
import { TasksService } from '../../tasks.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { AlertService } from 'app/core/alert/alert.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { KeyValuePipe } from '@angular/common';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
@@ -74,6 +75,7 @@ export class ClientApprovalComponent implements OnInit {
|
||||
private dateUtils = inject(Dates);
|
||||
private settingsService = inject(SettingsService);
|
||||
private tasksService = inject(TasksService);
|
||||
private alertService = inject(AlertService);
|
||||
|
||||
/** Current page of pending clients (from the server), grouped by office for display */
|
||||
groupedClients: any = {};
|
||||
@@ -89,7 +91,7 @@ export class ClientApprovalComponent implements OnInit {
|
||||
displayedColumns: string[] = [
|
||||
'select',
|
||||
'name',
|
||||
'clientId',
|
||||
'cifNumber',
|
||||
'staff'
|
||||
];
|
||||
|
||||
@@ -221,11 +223,15 @@ export class ClientApprovalComponent implements OnInit {
|
||||
const data = {
|
||||
title: 'Enter Clients Activation Date',
|
||||
layout: { addButtonText: 'Confirm' },
|
||||
formfields: formfields
|
||||
formfields: formfields,
|
||||
// The date is pre-filled with today, so the form is already valid on open. Without this the
|
||||
// dialog stays pristine and Confirm is disabled until the user needlessly re-picks the date.
|
||||
pristine: false
|
||||
};
|
||||
const clientApprovalDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||
clientApprovalDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.data) {
|
||||
// Cancel / the X button close with no value, so response is undefined there.
|
||||
if (response?.data) {
|
||||
this.bulkClientApproval(response.data);
|
||||
}
|
||||
});
|
||||
@@ -240,31 +246,70 @@ export class ClientApprovalComponent implements OnInit {
|
||||
activationDate,
|
||||
locale
|
||||
};
|
||||
const selectedAccounts = this.selection.selected.length;
|
||||
const listSelectedAccounts = this.selection.selected;
|
||||
let activatedAccounts = 0;
|
||||
this.batchRequests = [];
|
||||
let reqId = 1;
|
||||
// Keep the client each request belongs to, so a rejection can name who it was for.
|
||||
const clientsByRequestId = new Map<number, any>();
|
||||
listSelectedAccounts.forEach((element: any) => {
|
||||
const url = 'clients/' + element.id + '?command=activate';
|
||||
const bodyData = JSON.stringify(formData);
|
||||
const batchData = { requestId: reqId++, relativeUrl: url, method: 'POST', body: bodyData };
|
||||
clientsByRequestId.set(batchData.requestId, element);
|
||||
this.batchRequests.push(batchData);
|
||||
});
|
||||
this.tasksService.submitBatchData(this.batchRequests).subscribe((response: any) => {
|
||||
response.forEach((responseEle: any) => {
|
||||
if (responseEle.statusCode === '200') {
|
||||
let activatedAccounts = 0;
|
||||
const failures: string[] = [];
|
||||
|
||||
(response ?? []).forEach((responseEle: any) => {
|
||||
// The batch API returns statusCode as a number, so the old `=== '200'` string comparison
|
||||
// never matched: successes were never counted (no refresh) and rejections were dropped
|
||||
// entirely, which made a failed approval look like nothing had happened at all.
|
||||
if (Number(responseEle.statusCode) === 200) {
|
||||
activatedAccounts++;
|
||||
responseEle.body = JSON.parse(responseEle.body);
|
||||
if (selectedAccounts === activatedAccounts) {
|
||||
this.selection.clear();
|
||||
this.reload();
|
||||
}
|
||||
} else {
|
||||
const client = clientsByRequestId.get(Number(responseEle.requestId));
|
||||
const name = client?.displayName ?? `Client ${client?.id ?? ''}`.trim();
|
||||
failures.push(`${name}: ${this.batchErrorMessage(responseEle)}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (failures.length) {
|
||||
this.alertService.alert({
|
||||
type: 'Client Approval Failed',
|
||||
severity: 'error',
|
||||
message:
|
||||
(activatedAccounts ? `${activatedAccounts} approved. ` : '') +
|
||||
`${failures.length} could not be approved — ${failures.join('; ')}`
|
||||
});
|
||||
}
|
||||
|
||||
// Refresh whenever anything changed, rather than only when every single one succeeded —
|
||||
// a partial batch previously left the approved clients sitting in the list.
|
||||
if (activatedAccounts) {
|
||||
this.selection.clear();
|
||||
this.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Pulls the human-readable reason out of a failed batch entry's JSON body. */
|
||||
private batchErrorMessage(responseEle: any): string {
|
||||
try {
|
||||
const body = typeof responseEle.body === 'string' ? JSON.parse(responseEle.body) : responseEle.body;
|
||||
return (
|
||||
body?.errors?.[0]?.defaultUserMessage ||
|
||||
body?.errors?.[0]?.developerMessage ||
|
||||
body?.defaultUserMessage ||
|
||||
body?.developerMessage ||
|
||||
`request failed with status ${responseEle.statusCode}`
|
||||
);
|
||||
} catch {
|
||||
return `request failed with status ${responseEle.statusCode}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refetches the current page after a bulk approval.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user