kyc exception at client creation time and some tds work
This commit is contained in:
@@ -954,6 +954,75 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
|
||||
Validators.required
|
||||
]
|
||||
});
|
||||
|
||||
this.linkMonthlyAnnual('monthlyIncome', 'annualIncome');
|
||||
this.linkMonthlyAnnual('monthlyTurnover', 'annualTurnover');
|
||||
this.linkIncomeToTurnover();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps a Monthly/Annual pair of amount fields in sync at a 12x ratio — typing into either
|
||||
* one auto-fills the other. Uses { emitEvent: false } on the write side so the two
|
||||
* subscriptions don't trigger each other back and forth.
|
||||
*/
|
||||
/** Rounds to at most 2 decimals without padding whole numbers with a trailing ".00". */
|
||||
private round2(n: number): string {
|
||||
return (Math.round(n * 100) / 100).toString();
|
||||
}
|
||||
|
||||
private linkMonthlyAnnual(monthlyControlName: string, annualControlName: string): void {
|
||||
const monthly = this.createClientForm.get(monthlyControlName);
|
||||
const annual = this.createClientForm.get(annualControlName);
|
||||
if (!monthly || !annual) {
|
||||
return;
|
||||
}
|
||||
|
||||
monthly.valueChanges.subscribe((value) => {
|
||||
if (this.suppressMonthlyAnnualLink) {
|
||||
return;
|
||||
}
|
||||
const num = parseFloat(value);
|
||||
annual.setValue(!isNaN(num) && num >= 0 ? this.round2(num * 12) : '', { emitEvent: false });
|
||||
});
|
||||
|
||||
annual.valueChanges.subscribe((value) => {
|
||||
if (this.suppressMonthlyAnnualLink) {
|
||||
return;
|
||||
}
|
||||
const num = parseFloat(value);
|
||||
monthly.setValue(!isNaN(num) && num >= 0 ? this.round2(num / 12) : '', { emitEvent: false });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-calculates turnover based on income (doubling monthly income for monthly turnover,
|
||||
* and annual income for annual/yearly turnover).
|
||||
*/
|
||||
private linkIncomeToTurnover(): void {
|
||||
const monthlyIncome = this.createClientForm.get('monthlyIncome');
|
||||
const monthlyTurnover = this.createClientForm.get('monthlyTurnover');
|
||||
const annualIncome = this.createClientForm.get('annualIncome');
|
||||
const annualTurnover = this.createClientForm.get('annualTurnover');
|
||||
|
||||
if (monthlyIncome && monthlyTurnover) {
|
||||
monthlyIncome.valueChanges.subscribe((value) => {
|
||||
if (this.suppressMonthlyAnnualLink) {
|
||||
return;
|
||||
}
|
||||
const num = parseFloat(value);
|
||||
monthlyTurnover.setValue(!isNaN(num) && num >= 0 ? this.round2(num * 2) : '', { emitEvent: true });
|
||||
});
|
||||
}
|
||||
|
||||
if (annualIncome && annualTurnover) {
|
||||
annualIncome.valueChanges.subscribe((value) => {
|
||||
if (this.suppressMonthlyAnnualLink) {
|
||||
return;
|
||||
}
|
||||
const num = parseFloat(value);
|
||||
annualTurnover.setValue(!isNaN(num) && num >= 0 ? this.round2(num * 2) : '', { emitEvent: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -723,6 +723,33 @@
|
||||
<span>PAN verification requires client to be created first. Complete General tab and click Next to save.</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- KYC Exception Flag -->
|
||||
<div class="exception-flag-box" style="margin-top: 20px; padding: 16px; border: 1px solid #f59e0b; background: #fffbeb; border-radius: 8px; clear: both;">
|
||||
<h5 style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600; color: #b45309; display: flex; align-items: center; gap: 8px;">
|
||||
<fa-icon icon="exclamation-triangle"></fa-icon>
|
||||
KYC Exception Flag
|
||||
</h5>
|
||||
<div style="display: flex; align-items: center; gap: 20px; flex-wrap: wrap;">
|
||||
<mat-checkbox formControlName="isException">Mark as KYC Exception</mat-checkbox>
|
||||
|
||||
@if (kycForm.get('isException')?.value) {
|
||||
<mat-form-field appearance="outline" style="flex: 1; min-width: 280px; margin-bottom: -1.25em;">
|
||||
<mat-label>Exception Reasons</mat-label>
|
||||
<mat-select formControlName="exceptionReason" multiple>
|
||||
@if (!isNonIndividual) {
|
||||
<mat-option value="PAN Card Verification Failed">PAN Card Verification Failed</mat-option>
|
||||
<mat-option value="Aadhaar Verification Failed">Aadhaar Verification Failed</mat-option>
|
||||
} @else {
|
||||
<mat-option value="Enterprise PAN Verification Failed">Enterprise PAN Verification Failed</mat-option>
|
||||
<mat-option value="GSTIN Verification Failed">GSTIN Verification Failed</mat-option>
|
||||
<mat-option value="CIN Verification Failed">CIN Verification Failed</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: KYC Document Management ═══ -->
|
||||
@@ -1410,6 +1437,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ═══ SECTION: Photograph & Signature ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title">
|
||||
|
||||
@@ -29,6 +29,7 @@ import { ClientsService } from '../../clients.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { ProofType, normalizeProofType, groupProofsByCategory } from '../../shared/kyc-proof-types';
|
||||
import { deriveKycExceptionFields } from '../../kyc-exception.util';
|
||||
import { KycUploadHelperService } from '../../shared/kyc-upload-helper.service';
|
||||
import { CaptureImageDialogComponent } from '../../clients-view/custom-dialogs/capture-image-dialog/capture-image-dialog.component';
|
||||
import { DrawSignatureDialogComponent } from '../../clients-view/custom-dialogs/draw-signature-dialog/draw-signature-dialog.component';
|
||||
@@ -274,6 +275,14 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
riskCalculationReason = '';
|
||||
showRiskRulesTable = false;
|
||||
|
||||
/**
|
||||
* Exception state as last read back from the server. Needed to tell "this client never had an
|
||||
* exception" from "this client had one and has now cleared it" — only the latter should be
|
||||
* stamped resolved and surface in the Resolved tab.
|
||||
*/
|
||||
persistedIsException = false;
|
||||
persistedExceptionReason: string | null = null;
|
||||
|
||||
/** PAN verification */
|
||||
panVerifyStatus: 'NONE' | 'VERIFIED' | 'FAILED' = 'NONE';
|
||||
panVerifying = false;
|
||||
@@ -468,6 +477,8 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
allowRiskOverride: [false],
|
||||
allowAmlOverride: [false],
|
||||
remarks: [''],
|
||||
isException: [false],
|
||||
exceptionReason: [[]],
|
||||
amlCheckStatus: [
|
||||
'CLEAR',
|
||||
Validators.required
|
||||
@@ -610,8 +621,12 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
dormantFlag: 'ACTIVE',
|
||||
accountStatus: 'ACTIVE',
|
||||
form60Type: 'FORM_60',
|
||||
form15Type: '15G'
|
||||
form15Type: '15G',
|
||||
isException: false,
|
||||
exceptionReason: []
|
||||
});
|
||||
this.persistedIsException = false;
|
||||
this.persistedExceptionReason = null;
|
||||
this.kycId = '';
|
||||
this.kycLoaded = false;
|
||||
this.kycStatus = 0;
|
||||
@@ -942,8 +957,12 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
form60Date: parseDate(data.form60Date),
|
||||
form15Applicable: data.form15Applicable || false,
|
||||
form15Type: data.form15Type || '15G',
|
||||
form15FY: data.form15FY || ''
|
||||
form15FY: data.form15FY || '',
|
||||
isException: data.isException || false,
|
||||
exceptionReason: data.exceptionReason ? data.exceptionReason.split(', ') : []
|
||||
});
|
||||
this.persistedIsException = data.isException === true;
|
||||
this.persistedExceptionReason = data.exceptionReason || null;
|
||||
// Sync chip-picker selection with the prefilled document name
|
||||
if (data.kycDocumentName) {
|
||||
this.selectedKycDoc = this.kycDocProofs.find((p) => p.name === data.kycDocumentName) || null;
|
||||
@@ -1843,7 +1862,21 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
form15Applicable: v.form15Applicable || false,
|
||||
form15Type: v.form15Type || null,
|
||||
form15FY: v.form15FY || null,
|
||||
remarks: v.remarks || null
|
||||
remarks: v.remarks || null,
|
||||
// Exception state is DERIVED from verification status, not from a checkbox — an unverified
|
||||
// or blank PAN/Aadhaar is an exception by definition, so the officer cannot forget to flag
|
||||
// it. See deriveKycExceptionFields() for the raise / narrow / close rules.
|
||||
...deriveKycExceptionFields({
|
||||
panVerified: this.isNonIndividual ? this.entPanVerifyStatus === 'VERIFIED' : this.panVerifyStatus === 'VERIFIED',
|
||||
aadhaarVerified: this.aadhaarVerifyStatus === 'VERIFIED',
|
||||
// Non-individual clients submit a dummy masked Aadhaar, so checking it would saddle every
|
||||
// company with a permanent exception.
|
||||
aadhaarApplicable: !this.isNonIndividual,
|
||||
hadException: this.persistedIsException,
|
||||
previousReason: this.persistedExceptionReason,
|
||||
manualReasons: Array.isArray(v.exceptionReason) ? v.exceptionReason : [],
|
||||
todayFormatted: this.dateUtils.formatDate(new Date(), dateFormat)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1856,7 +1889,8 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
||||
v.enterprisePan ||
|
||||
v.cinNumber ||
|
||||
v.kycDocumentName ||
|
||||
v.form15Applicable
|
||||
v.form15Applicable ||
|
||||
v.isException
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user