kyc risk and AML & Compliance Checks automate on client creation
This commit is contained in:
+51
-4
@@ -2126,7 +2126,15 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
|
||||
return generalDetails;
|
||||
}
|
||||
|
||||
get calculatedRisk(): { category: number; reason: string } {
|
||||
get calculatedRisk(): {
|
||||
category: number;
|
||||
reason: string;
|
||||
amlCheckStatus?: string;
|
||||
bannedEntityMatch?: string;
|
||||
willfulDefaulter?: string;
|
||||
ecgcSalFlag?: string;
|
||||
dormantFlag?: string;
|
||||
} {
|
||||
const legalFormId = this.createClientForm.get('legalFormId')?.value || 1; // 1 = Person, 2 = Entity
|
||||
const occCatId = this.createClientForm.get('occupationCategoryCvId')?.value;
|
||||
const occId = this.createClientForm.get('occupationCvId')?.value;
|
||||
@@ -2143,6 +2151,21 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
|
||||
// Evaluate rules from DB (ordered by orderPosition ASC)
|
||||
if (this.riskRules && this.riskRules.length > 0) {
|
||||
for (const rule of this.riskRules) {
|
||||
// Skip transaction/AML rules that have no KYC criteria
|
||||
if (
|
||||
rule.legalFormId == null &&
|
||||
rule.occupationCategoryCvId == null &&
|
||||
rule.occupationCvId == null &&
|
||||
rule.minMonthlyIncome == null &&
|
||||
rule.maxMonthlyIncome == null &&
|
||||
rule.minAnnualIncome == null &&
|
||||
rule.maxAnnualIncome == null &&
|
||||
rule.minAnnualTurnover == null &&
|
||||
rule.maxAnnualTurnover == null
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Match legal form
|
||||
if (rule.legalFormId != null && rule.legalFormId !== legalFormId) {
|
||||
continue;
|
||||
@@ -2183,15 +2206,39 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
|
||||
}
|
||||
|
||||
// All matched!
|
||||
return { category: rule.riskCategory, reason: rule.reason };
|
||||
return {
|
||||
category: rule.riskCategory,
|
||||
reason: rule.reason,
|
||||
amlCheckStatus: rule.amlCheckStatus || (rule.riskCategory === 3 ? 'FLAGGED' : 'CLEAR'),
|
||||
bannedEntityMatch: rule.bannedEntityMatch || 'NO',
|
||||
willfulDefaulter: rule.willfulDefaulter || 'NO',
|
||||
ecgcSalFlag: rule.ecgcSalFlag || 'NO',
|
||||
dormantFlag: rule.dormantFlag || 'ACTIVE'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Default Fallbacks
|
||||
if (legalFormId !== LegalFormId.PERSON) {
|
||||
return { category: 2, reason: 'Non-Individual / Entity profile (Default Medium Risk)' };
|
||||
return {
|
||||
category: 2,
|
||||
reason: 'Non-Individual / Entity profile (Default Medium Risk)',
|
||||
amlCheckStatus: 'CLEAR',
|
||||
bannedEntityMatch: 'NO',
|
||||
willfulDefaulter: 'NO',
|
||||
ecgcSalFlag: 'NO',
|
||||
dormantFlag: 'ACTIVE'
|
||||
};
|
||||
}
|
||||
return { category: 1, reason: 'Default Low Risk profile (Salary / Service / Agriculture / Others)' };
|
||||
return {
|
||||
category: 1,
|
||||
reason: 'Default Low Risk profile (Salary / Service / Agriculture / Others)',
|
||||
amlCheckStatus: 'CLEAR',
|
||||
bannedEntityMatch: 'NO',
|
||||
willfulDefaulter: 'NO',
|
||||
ecgcSalFlag: 'NO',
|
||||
dormantFlag: 'ACTIVE'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1251,6 +1251,12 @@
|
||||
</h4>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="checkbox-field override-checkbox-wrapper full-width">
|
||||
<mat-checkbox formControlName="allowAmlOverride" [disabled]="readOnly">
|
||||
Override Auto-calculated AML & Compliance Checks
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>AML Check Status</mat-label>
|
||||
<mat-select required formControlName="amlCheckStatus">
|
||||
|
||||
@@ -70,7 +70,15 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() entityName: string = '';
|
||||
/** Read-only mode — disables form editing until user clicks edit icon */
|
||||
@Input() readOnly = false;
|
||||
@Input() calculatedRisk: { category: number; reason: string } | null = null;
|
||||
@Input() calculatedRisk: {
|
||||
category: number;
|
||||
reason: string;
|
||||
amlCheckStatus?: string;
|
||||
bannedEntityMatch?: string;
|
||||
willfulDefaulter?: string;
|
||||
ecgcSalFlag?: string;
|
||||
dormantFlag?: string;
|
||||
} | null = null;
|
||||
|
||||
today = new Date();
|
||||
kycId: string = '';
|
||||
@@ -457,6 +465,7 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
kycDocExpiryDate: [''],
|
||||
riskCategory: [1],
|
||||
allowRiskOverride: [false],
|
||||
allowAmlOverride: [false],
|
||||
remarks: [''],
|
||||
amlCheckStatus: [
|
||||
'CLEAR',
|
||||
@@ -534,9 +543,35 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
});
|
||||
|
||||
this.kycForm.get('allowAmlOverride')?.valueChanges.subscribe((allowed: boolean) => {
|
||||
const amlControls = [
|
||||
this.kycForm.get('amlCheckStatus'),
|
||||
this.kycForm.get('bannedEntityMatch'),
|
||||
this.kycForm.get('willfulDefaulter'),
|
||||
this.kycForm.get('ecgcSalFlag'),
|
||||
this.kycForm.get('dormantFlag')
|
||||
];
|
||||
if (allowed) {
|
||||
if (!this.readOnly) {
|
||||
amlControls.forEach((ctrl) => ctrl?.enable());
|
||||
}
|
||||
} else {
|
||||
amlControls.forEach((ctrl) => ctrl?.disable());
|
||||
this.applyAutoCalculatedAmlFlags();
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.kycForm.get('allowRiskOverride')?.value) {
|
||||
this.kycForm.get('riskCategory')?.disable();
|
||||
}
|
||||
|
||||
if (!this.kycForm.get('allowAmlOverride')?.value) {
|
||||
this.kycForm.get('amlCheckStatus')?.disable();
|
||||
this.kycForm.get('bannedEntityMatch')?.disable();
|
||||
this.kycForm.get('willfulDefaulter')?.disable();
|
||||
this.kycForm.get('ecgcSalFlag')?.disable();
|
||||
this.kycForm.get('dormantFlag')?.disable();
|
||||
}
|
||||
}
|
||||
|
||||
/** Seed the document-type picker from defaults (entity RBI docs vs. individual OVDs,
|
||||
@@ -644,6 +679,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
if (!this.kycForm.get('allowRiskOverride')?.value) {
|
||||
this.kycForm.get('riskCategory')?.disable({ emitEvent: false });
|
||||
}
|
||||
if (!this.kycForm.get('allowAmlOverride')?.value) {
|
||||
this.kycForm.get('amlCheckStatus')?.disable({ emitEvent: false });
|
||||
this.kycForm.get('bannedEntityMatch')?.disable({ emitEvent: false });
|
||||
this.kycForm.get('willfulDefaulter')?.disable({ emitEvent: false });
|
||||
this.kycForm.get('ecgcSalFlag')?.disable({ emitEvent: false });
|
||||
this.kycForm.get('dormantFlag')?.disable({ emitEvent: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changes['calculatedRisk']) {
|
||||
@@ -656,6 +698,11 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
riskControl.setValue(newRisk.category);
|
||||
riskControl.markAsDirty();
|
||||
}
|
||||
|
||||
const allowAmlOverride = this.kycForm.get('allowAmlOverride')?.value;
|
||||
if (!allowAmlOverride) {
|
||||
this.applyAutoCalculatedAmlFlags(newRisk);
|
||||
}
|
||||
} else {
|
||||
this.riskCalculationReason = '';
|
||||
}
|
||||
@@ -870,6 +917,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
// Risk & Remarks
|
||||
riskCategory: data.riskCategory || 1,
|
||||
allowRiskOverride: this.calculatedRisk ? this.calculatedRisk.category !== (data.riskCategory || 1) : false,
|
||||
allowAmlOverride: this.calculatedRisk
|
||||
? this.calculatedRisk.amlCheckStatus !== (data.amlCheckStatus || 'CLEAR') ||
|
||||
this.calculatedRisk.bannedEntityMatch !== (data.bannedEntityMatch || 'NO') ||
|
||||
this.calculatedRisk.willfulDefaulter !== (data.willfulDefaulter || 'NO') ||
|
||||
this.calculatedRisk.ecgcSalFlag !== (data.ecgcSalFlag || 'NO') ||
|
||||
this.calculatedRisk.dormantFlag !== (data.dormantFlag || 'ACTIVE')
|
||||
: false,
|
||||
remarks: data.remarks || '',
|
||||
hasPan: !!data.panNumber,
|
||||
// AML & Compliance
|
||||
@@ -1809,6 +1863,21 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
v.form15Applicable
|
||||
);
|
||||
}
|
||||
|
||||
private applyAutoCalculatedAmlFlags(risk = this.calculatedRisk): void {
|
||||
if (!risk) return;
|
||||
this.kycForm.get('amlCheckStatus')?.setValue(risk.amlCheckStatus || 'CLEAR');
|
||||
this.kycForm.get('bannedEntityMatch')?.setValue(risk.bannedEntityMatch || 'NO');
|
||||
this.kycForm.get('willfulDefaulter')?.setValue(risk.willfulDefaulter || 'NO');
|
||||
this.kycForm.get('ecgcSalFlag')?.setValue(risk.ecgcSalFlag || 'NO');
|
||||
this.kycForm.get('dormantFlag')?.setValue(risk.dormantFlag || 'ACTIVE');
|
||||
|
||||
this.kycForm.get('amlCheckStatus')?.markAsDirty();
|
||||
this.kycForm.get('bannedEntityMatch')?.markAsDirty();
|
||||
this.kycForm.get('willfulDefaulter')?.markAsDirty();
|
||||
this.kycForm.get('ecgcSalFlag')?.markAsDirty();
|
||||
this.kycForm.get('dormantFlag')?.markAsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user