minor change on client
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 688 KiB |
+86
-1
@@ -466,6 +466,51 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets transient session state back to a blank-client baseline.
|
||||||
|
*
|
||||||
|
* This app's RouteReusableStrategy (core/route/route-reusable-strategy.ts) keeps the SAME
|
||||||
|
* component instance alive across repeat visits to /clients/create — `ngOnInit`/the
|
||||||
|
* constructor never re-run for a second "create new client" session. Without this, computed
|
||||||
|
* state like `isMinor`, the linked guardian, or a prior sanctions/OTP verification would leak
|
||||||
|
* from whatever client was created in the previous session into the new one. Called by
|
||||||
|
* CreateClientComponent whenever `route.data` re-emits (which happens on every navigation,
|
||||||
|
* even a reused one).
|
||||||
|
*/
|
||||||
|
resetForNewClient(): void {
|
||||||
|
this.createClientForm.reset();
|
||||||
|
// legalFormId's raw FormControl default is '' — the buildDependencies() subscription
|
||||||
|
// treats any non-PERSON value (including '') as "non-individual" and adds fullname /
|
||||||
|
// clientNonPersonDetails. Patch it back to the same PERSON default buildDependencies()
|
||||||
|
// itself applies on first construction, so a reset lands on a blank Individual client
|
||||||
|
// instead of stuck with leftover Entity-only controls.
|
||||||
|
this.createClientForm.get('legalFormId')?.setValue(LegalFormId.PERSON);
|
||||||
|
this.isMinor = false;
|
||||||
|
this.calculatedAgeDetail = null;
|
||||||
|
this.ageBreakdown = { years: 0, months: 0, days: 0 };
|
||||||
|
this.selectedGuardian = null;
|
||||||
|
this.guardianSearchText = '';
|
||||||
|
this.guardianSearchResults = [];
|
||||||
|
this.guardianExpanded = false;
|
||||||
|
if (this.createClientForm.get('guardianDetails')) {
|
||||||
|
this.createClientForm.removeControl('guardianDetails');
|
||||||
|
}
|
||||||
|
// Verification state must not carry over — a previous client's cleared sanctions
|
||||||
|
// screening or verified OTP must not silently apply to a different new client.
|
||||||
|
this.mobileVerified = false;
|
||||||
|
this.dipLookupLogId = null;
|
||||||
|
this.dipTimestamp = null;
|
||||||
|
this.alternateMobileVerified = false;
|
||||||
|
this.alternateDipLookupLogId = null;
|
||||||
|
this.alternateDipTimestamp = null;
|
||||||
|
this.mobileNewOtpId = null;
|
||||||
|
this.emailNewOtpId = null;
|
||||||
|
this.sanctionsVerified = false;
|
||||||
|
this.sanctionsBlocked = false;
|
||||||
|
this.screeningLogId = null;
|
||||||
|
this.verifiedName = '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pre-fills the General step form from a manually-entered family member
|
* Pre-fills the General step form from a manually-entered family member
|
||||||
* when the user clicks "Create as Client" on the family members tab.
|
* when the user clicks "Create as Client" on the family members tab.
|
||||||
@@ -521,7 +566,11 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
mobileNo: data.mobileNo,
|
mobileNo: data.mobileNo,
|
||||||
alternateMobileNo: data.alternateMobileNo || '',
|
alternateMobileNo: data.alternateMobileNo || '',
|
||||||
emailAddress: data.emailAddress,
|
emailAddress: data.emailAddress,
|
||||||
dateOfBirth: data.dateOfBirth ? new Date(data.dateOfBirth) : '',
|
dateOfBirth: data.dateOfBirth
|
||||||
|
? Array.isArray(data.dateOfBirth)
|
||||||
|
? new Date(data.dateOfBirth[0], data.dateOfBirth[1] - 1, data.dateOfBirth[2])
|
||||||
|
: new Date(data.dateOfBirth)
|
||||||
|
: '',
|
||||||
occupationCategoryId: data.occupationCategory?.id || data.occupationCategoryId || '',
|
occupationCategoryId: data.occupationCategory?.id || data.occupationCategoryId || '',
|
||||||
occupationId: data.occupation?.id || data.occupationId || '',
|
occupationId: data.occupation?.id || data.occupationId || '',
|
||||||
monthlyIncome: data.monthlyIncome || '',
|
monthlyIncome: data.monthlyIncome || '',
|
||||||
@@ -599,6 +648,42 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore linked guardian for minor clients. The `guardianDetails` control group above was
|
||||||
|
// just created synchronously by the dateOfBirth subscription (patched a few lines up), which
|
||||||
|
// also resets `selectedGuardian` to null — so this has to run after that patch, not before it.
|
||||||
|
// If a guardian is linked in the database (or the client is marked minor), we force minor state
|
||||||
|
// and explicitly ensure the form control exists so the guardian details load correctly.
|
||||||
|
if ((data.isMinor || this.isMinor || data.guardianClientId) && data.guardianClientId) {
|
||||||
|
this.isMinor = true;
|
||||||
|
if (!this.createClientForm.get('guardianDetails')) {
|
||||||
|
this.createClientForm.addControl(
|
||||||
|
'guardianDetails',
|
||||||
|
this.formBuilder.group({
|
||||||
|
guardianClientId: [
|
||||||
|
'',
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
guardianRelation: [
|
||||||
|
'',
|
||||||
|
Validators.required
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.createClientForm.get('guardianDetails')?.patchValue({
|
||||||
|
guardianClientId: data.guardianClientId,
|
||||||
|
guardianRelation: data.guardianRelation || ''
|
||||||
|
});
|
||||||
|
this.selectedGuardian = {
|
||||||
|
id: data.guardianClientId,
|
||||||
|
displayName: data.guardianClientDisplayName || '',
|
||||||
|
_loading: true
|
||||||
|
};
|
||||||
|
this.fetchClientFullDetails(data.guardianClientId, this.selectedGuardian);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+32
-1
@@ -43,6 +43,9 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges {
|
|||||||
@Input() clientId: string = '';
|
@Input() clientId: string = '';
|
||||||
/** Read-only mode — disables form editing until user clicks edit icon */
|
/** Read-only mode — disables form editing until user clicks edit icon */
|
||||||
@Input() readOnly = false;
|
@Input() readOnly = false;
|
||||||
|
/** Minor clients can't hold utility bills, bank statements or an adult-only OVD in their own
|
||||||
|
* name — restricts the address-proof picker to Aadhaar/Passport when true. */
|
||||||
|
@Input() isMinor = false;
|
||||||
|
|
||||||
activeTab = 0;
|
activeTab = 0;
|
||||||
sameAsPermanent = false;
|
sameAsPermanent = false;
|
||||||
@@ -240,7 +243,9 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges {
|
|||||||
this.clientsService.getKycProofTypes().subscribe({
|
this.clientsService.getKycProofTypes().subscribe({
|
||||||
next: (res: any) => {
|
next: (res: any) => {
|
||||||
const activeFilter = (p: any) => p?.active !== false;
|
const activeFilter = (p: any) => p?.active !== false;
|
||||||
this.addressProofs = (res?.addressProofs || []).filter(activeFilter).map(normalizeProofType);
|
this.addressProofs = this.filterAddressProofsForMinor(
|
||||||
|
(res?.addressProofs || []).filter(activeFilter).map(normalizeProofType)
|
||||||
|
);
|
||||||
this.addressGroups = groupProofsByCategory(this.addressProofs);
|
this.addressGroups = groupProofsByCategory(this.addressProofs);
|
||||||
this.loadingProofs = false;
|
this.loadingProofs = false;
|
||||||
},
|
},
|
||||||
@@ -250,6 +255,21 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Minors don't hold utility bills, bank statements, rent agreements, property tax receipts,
|
||||||
|
* employer letters or pension orders in their own name, and Voter ID/Driving Licence both
|
||||||
|
* require adult age — Aadhaar and Passport are the only OVDs left for proof of address. */
|
||||||
|
private static readonly MINOR_ELIGIBLE_ADDRESS_PROOF_CODES = new Set([
|
||||||
|
'AADHAAR',
|
||||||
|
'PASSPORT'
|
||||||
|
]);
|
||||||
|
|
||||||
|
private filterAddressProofsForMinor(proofs: ProofType[]): ProofType[] {
|
||||||
|
if (!this.isMinor) return proofs;
|
||||||
|
return proofs.filter((p) =>
|
||||||
|
ClientIndiaAddressStepComponent.MINOR_ELIGIBLE_ADDRESS_PROOF_CODES.has((p.code || '').toUpperCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
selectAddrProof(proof: ProofType): void {
|
selectAddrProof(proof: ProofType): void {
|
||||||
this.selectedAddrProof = proof;
|
this.selectedAddrProof = proof;
|
||||||
this.permanentForm.get('proofId')?.setValue(proof.id);
|
this.permanentForm.get('proofId')?.setValue(proof.id);
|
||||||
@@ -355,6 +375,17 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (changes['isMinor'] && !changes['isMinor'].firstChange && this.addressProofs.length) {
|
||||||
|
const filtered = this.filterAddressProofsForMinor(this.addressProofs);
|
||||||
|
if (filtered.length !== this.addressProofs.length) {
|
||||||
|
this.addressProofs = filtered;
|
||||||
|
this.addressGroups = groupProofsByCategory(this.addressProofs);
|
||||||
|
if (this.selectedAddrProof && !filtered.some((p) => p.id === this.selectedAddrProof?.id)) {
|
||||||
|
this.selectedAddrProof = null;
|
||||||
|
this.permanentForm.get('proofId')?.setValue('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,33 +28,36 @@
|
|||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<!-- ══ INDIVIDUAL KYC ══ -->
|
<!-- ══ INDIVIDUAL KYC ══ -->
|
||||||
@if (!isNonIndividual) {
|
@if (!isNonIndividual) {
|
||||||
@if (readOnly) {
|
<!-- Minors don't hold a PAN card — skip the PAN checkbox/section entirely -->
|
||||||
<!-- In view mode the mat-checkbox renders as a faded grey square
|
@if (!isMinor) {
|
||||||
that reads as "blurry". Show a clean Yes/No pill instead. -->
|
@if (readOnly) {
|
||||||
<div class="form-field checkbox-field full-width">
|
<!-- In view mode the mat-checkbox renders as a faded grey square
|
||||||
<div class="ro-flag">
|
that reads as "blurry". Show a clean Yes/No pill instead. -->
|
||||||
<span class="ro-flag__label">Client has PAN Card</span>
|
<div class="form-field checkbox-field full-width">
|
||||||
@if (kycForm.get('hasPan')?.value) {
|
<div class="ro-flag">
|
||||||
<span class="ro-flag__chip ro-flag__chip--yes">
|
<span class="ro-flag__label">Client has PAN Card</span>
|
||||||
<mat-icon>check_circle</mat-icon>
|
@if (kycForm.get('hasPan')?.value) {
|
||||||
Yes
|
<span class="ro-flag__chip ro-flag__chip--yes">
|
||||||
</span>
|
<mat-icon>check_circle</mat-icon>
|
||||||
} @else {
|
Yes
|
||||||
<span class="ro-flag__chip ro-flag__chip--no">
|
</span>
|
||||||
<mat-icon>cancel</mat-icon>
|
} @else {
|
||||||
No
|
<span class="ro-flag__chip ro-flag__chip--no">
|
||||||
</span>
|
<mat-icon>cancel</mat-icon>
|
||||||
}
|
No
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
} @else {
|
||||||
} @else {
|
<div class="form-field checkbox-field full-width">
|
||||||
<div class="form-field checkbox-field full-width">
|
<mat-checkbox formControlName="hasPan">Client has PAN Card</mat-checkbox>
|
||||||
<mat-checkbox formControlName="hasPan">Client has PAN Card</mat-checkbox>
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Individual PAN with inline verify -->
|
<!-- Individual PAN with inline verify -->
|
||||||
@if (kycForm.get('hasPan')?.value) {
|
@if (!isMinor && kycForm.get('hasPan')?.value) {
|
||||||
<div class="form-field verify-field">
|
<div class="form-field verify-field">
|
||||||
<mat-form-field appearance="outline" class="verify-input">
|
<mat-form-field appearance="outline" class="verify-input">
|
||||||
<mat-label>PAN Number</mat-label>
|
<mat-label>PAN Number</mat-label>
|
||||||
@@ -1266,11 +1269,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ═══ SECTION: Form 60/61 ═══ -->
|
<!-- ═══ SECTION: Form 97/98 ═══ -->
|
||||||
<div class="section-block">
|
<div class="section-block">
|
||||||
<h4 class="section-title">
|
<h4 class="section-title">
|
||||||
<fa-icon icon="file-invoice" class="section-icon"></fa-icon>
|
<fa-icon icon="file-invoice" class="section-icon"></fa-icon>
|
||||||
Form 60 / 61
|
Form 97 / 98
|
||||||
@if (!kycForm.get('hasPan')?.value) {
|
@if (!kycForm.get('hasPan')?.value) {
|
||||||
<span class="kyc-badge required-badge">Required — No PAN</span>
|
<span class="kyc-badge required-badge">Required — No PAN</span>
|
||||||
}
|
}
|
||||||
@@ -1278,14 +1281,14 @@
|
|||||||
@if (!kycForm.get('hasPan')?.value) {
|
@if (!kycForm.get('hasPan')?.value) {
|
||||||
<div class="kyc-info-hint warning-hint">
|
<div class="kyc-info-hint warning-hint">
|
||||||
<mat-icon class="kyc-info-hint-icon">warning</mat-icon>
|
<mat-icon class="kyc-info-hint-icon">warning</mat-icon>
|
||||||
<span>Client does not have PAN. Form 60/61 is mandatory.</span>
|
<span>Client does not have PAN. Form 97/98 is mandatory.</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<mat-form-field appearance="outline" class="form-field">
|
<mat-form-field appearance="outline" class="form-field">
|
||||||
<mat-label>Form Type</mat-label>
|
<mat-label>Form Type</mat-label>
|
||||||
<mat-select formControlName="form60Type">
|
<mat-select formControlName="form60Type">
|
||||||
<mat-option value="FORM_60">Form 60</mat-option>
|
<mat-option value="FORM_60">Form 97</mat-option>
|
||||||
<mat-option value="FORM_61">Form 61</mat-option>
|
<mat-option value="FORM_61">Form 98</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field appearance="outline" class="form-field" (click)="form60DatePicker.open()">
|
<mat-form-field appearance="outline" class="form-field" (click)="form60DatePicker.open()">
|
||||||
@@ -1296,26 +1299,26 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<p class="form60-not-needed">Client has PAN — Form 60/61 not required.</p>
|
<p class="form60-not-needed">Client has PAN — Form 97/98 not required.</p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ═══ SECTION: Form 15G / 15H ═══ -->
|
<!-- ═══ SECTION: Form 121 ═══ -->
|
||||||
<div class="section-block">
|
<div class="section-block">
|
||||||
<h4 class="section-title">
|
<h4 class="section-title">
|
||||||
<fa-icon icon="receipt" class="section-icon"></fa-icon>
|
<fa-icon icon="receipt" class="section-icon"></fa-icon>
|
||||||
Form 15G / 15H
|
Form 121
|
||||||
</h4>
|
</h4>
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<div class="form-field checkbox-field">
|
<div class="form-field checkbox-field">
|
||||||
<mat-checkbox formControlName="form15Applicable">Form 15G/15H Applicable</mat-checkbox>
|
<mat-checkbox formControlName="form15Applicable">Form 121 Applicable</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@if (kycForm.get('form15Applicable')?.value) {
|
@if (kycForm.get('form15Applicable')?.value) {
|
||||||
<mat-form-field appearance="outline" class="form-field">
|
<mat-form-field appearance="outline" class="form-field">
|
||||||
<mat-label>Form Type</mat-label>
|
<mat-label>Form Type</mat-label>
|
||||||
<mat-select formControlName="form15Type">
|
<mat-select formControlName="form15Type">
|
||||||
<mat-option value="15G">15G (Below 60 years)</mat-option>
|
<mat-option value="15G">Form 121 (Below 60 years)</mat-option>
|
||||||
<mat-option value="15H">15H (Senior Citizen 60+)</mat-option>
|
<mat-option value="15H">Form 121 (Senior Citizen 60+)</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field appearance="outline" class="form-field">
|
<mat-form-field appearance="outline" class="form-field">
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
/** Set by parent after client is created */
|
/** Set by parent after client is created */
|
||||||
@Input() clientId: string = '';
|
@Input() clientId: string = '';
|
||||||
@Input() isNonIndividual = false;
|
@Input() isNonIndividual = false;
|
||||||
|
/** Minor clients don't hold a PAN card — hides the PAN section on the individual KYC form */
|
||||||
|
@Input() isMinor = false;
|
||||||
/** Constitution metadata (panLabel, showCin, etc.) passed from general step */
|
/** Constitution metadata (panLabel, showCin, etc.) passed from general step */
|
||||||
@Input() constitutionMeta: any = null;
|
@Input() constitutionMeta: any = null;
|
||||||
/** Entity full name (non-individual) — used for enterprise PAN verification */
|
/** Entity full name (non-individual) — used for enterprise PAN verification */
|
||||||
@@ -163,7 +165,9 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
{ name: 'Driving Licence', code: 'DRIVING_LICENSE' },
|
{ name: 'Driving Licence', code: 'DRIVING_LICENSE' },
|
||||||
{ name: 'Voter ID', code: 'VOTER_ID' },
|
{ name: 'Voter ID', code: 'VOTER_ID' },
|
||||||
{ name: 'NREGA Job Card', code: 'NREGA_JOB_CARD' },
|
{ name: 'NREGA Job Card', code: 'NREGA_JOB_CARD' },
|
||||||
{ name: 'National Population Register Letter', code: 'NPR_LETTER' }
|
{ name: 'National Population Register Letter', code: 'NPR_LETTER' },
|
||||||
|
{ name: 'Birth Certificate', code: 'BIRTH_CERTIFICATE' },
|
||||||
|
{ name: 'School ID Card', code: 'SCHOOL_ID_CARD' }
|
||||||
];
|
];
|
||||||
/** Constitution-based KYC document types for non-individual clients */
|
/** Constitution-based KYC document types for non-individual clients */
|
||||||
private constitutionDocTypes: Record<string, { name: string; mandatory: boolean }[]> = {
|
private constitutionDocTypes: Record<string, { name: string; mandatory: boolean }[]> = {
|
||||||
@@ -458,15 +462,44 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
form15FY: ['']
|
form15FY: ['']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** RBI OVDs a minor cannot hold: Voter ID and Driving Licence require adult age,
|
||||||
|
* PAN and NREGA job cards are issued to adults. Aadhaar, Passport and NPR letters
|
||||||
|
* are unaffected by age and stay available. */
|
||||||
|
private static readonly MINOR_INELIGIBLE_DOC_KEYWORDS = [
|
||||||
|
'pan',
|
||||||
|
'driving',
|
||||||
|
'voter',
|
||||||
|
'nrega'
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Birth Certificate / School ID Card are minor-specific OVDs — not relevant once the
|
||||||
|
* client is an adult, so they're hidden the other direction. */
|
||||||
|
private static readonly MINOR_ONLY_DOC_CODES = new Set([
|
||||||
|
'BIRTH_CERTIFICATE',
|
||||||
|
'SCHOOL_ID_CARD'
|
||||||
|
]);
|
||||||
|
|
||||||
|
private filterDocTypesForMinor<T extends { name: string; code?: string }>(docs: T[]): T[] {
|
||||||
|
if (this.isMinor) {
|
||||||
|
return docs.filter((d) => {
|
||||||
|
const text = `${d.name || ''} ${d.code || ''}`.toLowerCase();
|
||||||
|
return !ClientKycStepComponent.MINOR_INELIGIBLE_DOC_KEYWORDS.some((k) => text.includes(k));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return docs.filter((d) => !ClientKycStepComponent.MINOR_ONLY_DOC_CODES.has((d.code || '').toUpperCase()));
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Seed defaults immediately so the picker has options before clientId is set
|
// Seed defaults immediately so the picker has options before clientId is set
|
||||||
// (template API requires clientId; user can still pick a type pre-save).
|
// (template API requires clientId; user can still pick a type pre-save).
|
||||||
if (!this.kycDocumentTypes.length) {
|
if (!this.kycDocumentTypes.length) {
|
||||||
this.kycDocumentTypes = this.defaultDocumentTypes.map((d) => ({
|
this.kycDocumentTypes = this.filterDocTypesForMinor(
|
||||||
name: d.name,
|
this.defaultDocumentTypes.map((d) => ({
|
||||||
code: d.code,
|
name: d.name,
|
||||||
mandatory: false
|
code: d.code,
|
||||||
}));
|
mandatory: false
|
||||||
|
}))
|
||||||
|
);
|
||||||
this.rebuildKycDocPicker();
|
this.rebuildKycDocPicker();
|
||||||
}
|
}
|
||||||
this.loadExistingKycIfNeeded();
|
this.loadExistingKycIfNeeded();
|
||||||
@@ -496,6 +529,21 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
if (changes['constitutionMeta'] && this.isNonIndividual) {
|
if (changes['constitutionMeta'] && this.isNonIndividual) {
|
||||||
this.updateDocTypesForConstitution();
|
this.updateDocTypesForConstitution();
|
||||||
}
|
}
|
||||||
|
if (changes['isMinor']) {
|
||||||
|
if (this.isMinor) {
|
||||||
|
this.kycForm.get('hasPan')?.setValue(false);
|
||||||
|
this.kycForm.get('panNumber')?.setValue('');
|
||||||
|
}
|
||||||
|
const filtered = this.filterDocTypesForMinor(this.kycDocumentTypes);
|
||||||
|
if (filtered.length !== this.kycDocumentTypes.length) {
|
||||||
|
this.kycDocumentTypes = filtered;
|
||||||
|
const currentName = this.kycForm.get('kycDocumentName')?.value;
|
||||||
|
if (currentName && !filtered.some((d) => d.name === currentName)) {
|
||||||
|
this.kycForm.get('kycDocumentName')?.setValue('');
|
||||||
|
}
|
||||||
|
this.rebuildKycDocPicker();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update KYC document types based on selected constitution type */
|
/** Update KYC document types based on selected constitution type */
|
||||||
@@ -874,11 +922,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.kycTemplate = template;
|
this.kycTemplate = template;
|
||||||
this.kycTemplateLoaded = true;
|
this.kycTemplateLoaded = true;
|
||||||
if (template?.requiredDocuments?.length > 0) {
|
if (template?.requiredDocuments?.length > 0) {
|
||||||
this.kycDocumentTypes = template.requiredDocuments.map((doc: any) => ({
|
this.kycDocumentTypes = this.filterDocTypesForMinor(
|
||||||
name: doc.name,
|
template.requiredDocuments.map((doc: any) => ({
|
||||||
code: doc.code || doc.name,
|
name: doc.name,
|
||||||
mandatory: doc.mandatory ?? false
|
code: doc.code || doc.name,
|
||||||
}));
|
mandatory: doc.mandatory ?? false
|
||||||
|
}))
|
||||||
|
);
|
||||||
// Update kycDocumentName validator based on mandatory docs
|
// Update kycDocumentName validator based on mandatory docs
|
||||||
const hasMandatory = this.kycDocumentTypes.some((d) => d.mandatory);
|
const hasMandatory = this.kycDocumentTypes.some((d) => d.mandatory);
|
||||||
if (hasMandatory) {
|
if (hasMandatory) {
|
||||||
@@ -891,11 +941,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
error: () => {
|
error: () => {
|
||||||
// Fallback to defaults if template API unavailable
|
// Fallback to defaults if template API unavailable
|
||||||
this.kycTemplateLoaded = true;
|
this.kycTemplateLoaded = true;
|
||||||
this.kycDocumentTypes = this.defaultDocumentTypes.map((d) => ({
|
this.kycDocumentTypes = this.filterDocTypesForMinor(
|
||||||
name: d.name,
|
this.defaultDocumentTypes.map((d) => ({
|
||||||
code: d.code,
|
name: d.name,
|
||||||
mandatory: false
|
code: d.code,
|
||||||
}));
|
mandatory: false
|
||||||
|
}))
|
||||||
|
);
|
||||||
this.rebuildKycDocPicker();
|
this.rebuildKycDocPicker();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'form60',
|
path: 'form60',
|
||||||
component: Form60TabComponent,
|
component: Form60TabComponent,
|
||||||
data: { title: 'Form 60', breadcrumb: 'Form 60', routeParamBreadcrumb: false }
|
data: { title: 'Form 97', breadcrumb: 'Form 97', routeParamBreadcrumb: false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'documents',
|
path: 'documents',
|
||||||
|
|||||||
@@ -19,14 +19,14 @@
|
|||||||
<div class="f60-pan-ok__icon-wrap">
|
<div class="f60-pan-ok__icon-wrap">
|
||||||
<mat-icon class="f60-pan-ok__icon">verified</mat-icon>
|
<mat-icon class="f60-pan-ok__icon">verified</mat-icon>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="f60-pan-ok__title">Form 60 Not Required</h3>
|
<h3 class="f60-pan-ok__title">Form 97 Not Required</h3>
|
||||||
<p class="f60-pan-ok__desc">
|
<p class="f60-pan-ok__desc">
|
||||||
PAN <strong>{{ panNumber }}</strong> is attached to this client.
|
PAN <strong>{{ panNumber }}</strong> is attached to this client.
|
||||||
@if (panVerified) {
|
@if (panVerified) {
|
||||||
<mat-icon class="f60-pan-ok__tick">check_circle</mat-icon> <span class="f60-pan-ok__verified">Verified</span>
|
<mat-icon class="f60-pan-ok__tick">check_circle</mat-icon> <span class="f60-pan-ok__verified">Verified</span>
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
<p class="f60-pan-ok__rule">As per Income Tax Rule 114B, Form 60 is only required when PAN is not available.</p>
|
<p class="f60-pan-ok__rule">As per Income Tax Rule 114B, Form 97 is only required when PAN is not available.</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="f60-warning">
|
<div class="f60-warning">
|
||||||
<mat-icon class="f60-warning__icon">warning_amber</mat-icon>
|
<mat-icon class="f60-warning__icon">warning_amber</mat-icon>
|
||||||
<div class="f60-warning__text">
|
<div class="f60-warning__text">
|
||||||
<strong>PAN not provided</strong> — Form 60 declaration is mandatory as per Income Tax Rule 114B.
|
<strong>PAN not provided</strong> — Form 97 declaration is mandatory as per Income Tax Rule 114B.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<div class="f60-card">
|
<div class="f60-card">
|
||||||
<div class="f60-card__header">
|
<div class="f60-card__header">
|
||||||
<mat-icon class="f60-card__header-icon">description</mat-icon>
|
<mat-icon class="f60-card__header-icon">description</mat-icon>
|
||||||
<h3>Form 60 Declaration</h3>
|
<h3>Form 97 Declaration</h3>
|
||||||
<span class="f60-card__status">Submitted</span>
|
<span class="f60-card__status">Submitted</span>
|
||||||
<button mat-stroked-button color="primary" (click)="toggleEditMode()" class="f60-card__edit">
|
<button mat-stroked-button color="primary" (click)="toggleEditMode()" class="f60-card__edit">
|
||||||
<mat-icon>edit</mat-icon> Edit
|
<mat-icon>edit</mat-icon> Edit
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
<div class="f60-card">
|
<div class="f60-card">
|
||||||
<div class="f60-card__header">
|
<div class="f60-card__header">
|
||||||
<mat-icon class="f60-card__header-icon">edit_note</mat-icon>
|
<mat-icon class="f60-card__header-icon">edit_note</mat-icon>
|
||||||
<h3>Form 60 Declaration</h3>
|
<h3>Form 97 Declaration</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="f60-card__body">
|
<div class="f60-card__body">
|
||||||
<form [formGroup]="form60Form" (ngSubmit)="submit()">
|
<form [formGroup]="form60Form" (ngSubmit)="submit()">
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
<button type="button" mat-stroked-button (click)="toggleEditMode()">Cancel</button>
|
<button type="button" mat-stroked-button (click)="toggleEditMode()">Cancel</button>
|
||||||
}
|
}
|
||||||
<button type="submit" mat-flat-button color="primary" [disabled]="!form60Form.valid">
|
<button type="submit" mat-flat-button color="primary" [disabled]="!form60Form.valid">
|
||||||
<mat-icon>check</mat-icon> Submit Form 60
|
<mat-icon>check</mat-icon> Submit Form 97
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -224,14 +224,14 @@ export class Form60TabComponent implements OnInit, OnDestroy {
|
|||||||
this.isEditMode = false;
|
this.isEditMode = false;
|
||||||
this.alertService.alert({
|
this.alertService.alert({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: 'Form 60 submitted successfully.'
|
message: 'Form 97 submitted successfully.'
|
||||||
});
|
});
|
||||||
this.loadForm60Data();
|
this.loadForm60Data();
|
||||||
},
|
},
|
||||||
error: (err: any) => {
|
error: (err: any) => {
|
||||||
this.alertService.alert({
|
this.alertService.alert({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: err?.error?.developerMessage || err?.error?.defaultUserMessage || 'Failed to submit Form 60.'
|
message: err?.error?.developerMessage || err?.error?.defaultUserMessage || 'Failed to submit Form 97.'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
<mifosx-client-kyc-step
|
<mifosx-client-kyc-step
|
||||||
#clientKyc
|
#clientKyc
|
||||||
[isNonIndividual]="legalFormType === 2"
|
[isNonIndividual]="legalFormType === 2"
|
||||||
|
[isMinor]="clientGeneralStep?.isMinor || false"
|
||||||
[clientId]="createdClientId"
|
[clientId]="createdClientId"
|
||||||
[constitutionMeta]="clientGeneralStep?.selectedConstitution"
|
[constitutionMeta]="clientGeneralStep?.selectedConstitution"
|
||||||
[entityName]="clientGeneralStep?.createClientForm?.get('fullname')?.value || ''"
|
[entityName]="clientGeneralStep?.createClientForm?.get('fullname')?.value || ''"
|
||||||
@@ -146,6 +147,7 @@
|
|||||||
}
|
}
|
||||||
<mifosx-client-india-address-step
|
<mifosx-client-india-address-step
|
||||||
#clientIndiaAddress
|
#clientIndiaAddress
|
||||||
|
[isMinor]="clientGeneralStep?.isMinor || false"
|
||||||
[readOnly]="isTabReadOnly(addressTabIdx)"
|
[readOnly]="isTabReadOnly(addressTabIdx)"
|
||||||
></mifosx-client-india-address-step>
|
></mifosx-client-india-address-step>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -111,6 +111,12 @@ export class CreateClientComponent {
|
|||||||
this.route.data.subscribe((data: { clientTemplate: any; clientAddressFieldConfig: any }) => {
|
this.route.data.subscribe((data: { clientTemplate: any; clientAddressFieldConfig: any }) => {
|
||||||
this.clientTemplate = data.clientTemplate;
|
this.clientTemplate = data.clientTemplate;
|
||||||
this.clientAddressFieldConfig = data.clientAddressFieldConfig;
|
this.clientAddressFieldConfig = data.clientAddressFieldConfig;
|
||||||
|
// This app's RouteReusableStrategy keeps this SAME component instance alive across
|
||||||
|
// repeat visits to /clients/create, so ngOnInit/constructor won't re-run for a second
|
||||||
|
// "create new client" session — reset transient state here instead, since `route.data`
|
||||||
|
// re-emits on every navigation regardless of whether the component was reused. Must run
|
||||||
|
// BEFORE setDatatables(), which reads legalFormType to filter clientTemplate.datatables.
|
||||||
|
this.resetForNewClient();
|
||||||
this.setDatatables();
|
this.setDatatables();
|
||||||
});
|
});
|
||||||
// Read pre-fill data set by "Create as Client" from a manual family member
|
// Read pre-fill data set by "Create as Client" from a manual family member
|
||||||
@@ -121,6 +127,20 @@ export class CreateClientComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resets session state that could otherwise leak between clients when the route is reused. */
|
||||||
|
private resetForNewClient(): void {
|
||||||
|
this.legalFormType = 1;
|
||||||
|
this.selectedTabIndex = 0;
|
||||||
|
this.createdClientId = '';
|
||||||
|
this.savingClient = false;
|
||||||
|
this.saveProgress = 0;
|
||||||
|
this.tabEditMode = {};
|
||||||
|
this.highestTabReached = 0;
|
||||||
|
this.validationError = '';
|
||||||
|
this.prefillData = null;
|
||||||
|
this.clientGeneralStep?.resetForNewClient();
|
||||||
|
}
|
||||||
|
|
||||||
/** Check if a tab is in read-only mode (completed tab navigated back to) */
|
/** Check if a tab is in read-only mode (completed tab navigated back to) */
|
||||||
isTabReadOnly(tabIndex: number): boolean {
|
isTabReadOnly(tabIndex: number): boolean {
|
||||||
// Only tabs that have been passed (below highest reached) AND not explicitly set to edit mode
|
// Only tabs that have been passed (below highest reached) AND not explicitly set to edit mode
|
||||||
|
|||||||
@@ -149,6 +149,7 @@
|
|||||||
<mifosx-client-kyc-step
|
<mifosx-client-kyc-step
|
||||||
#clientKyc
|
#clientKyc
|
||||||
[isNonIndividual]="legalFormType === 2"
|
[isNonIndividual]="legalFormType === 2"
|
||||||
|
[isMinor]="clientGeneralStep?.isMinor || false"
|
||||||
[clientId]="clientId"
|
[clientId]="clientId"
|
||||||
[readOnly]="isTabReadOnly(1)"
|
[readOnly]="isTabReadOnly(1)"
|
||||||
></mifosx-client-kyc-step>
|
></mifosx-client-kyc-step>
|
||||||
@@ -206,6 +207,7 @@
|
|||||||
<mifosx-client-india-address-step
|
<mifosx-client-india-address-step
|
||||||
#clientIndiaAddress
|
#clientIndiaAddress
|
||||||
[clientId]="clientId"
|
[clientId]="clientId"
|
||||||
|
[isMinor]="clientGeneralStep?.isMinor || false"
|
||||||
[readOnly]="isTabReadOnly(addressTabIdx)"
|
[readOnly]="isTabReadOnly(addressTabIdx)"
|
||||||
></mifosx-client-india-address-step>
|
></mifosx-client-india-address-step>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -70,6 +70,19 @@ export const PROOF_META: Record<string, Partial<ProofType>> = {
|
|||||||
documentKeyPlaceholder: 'NPR reference number',
|
documentKeyPlaceholder: 'NPR reference number',
|
||||||
info: 'NPR letter — upload full document.'
|
info: 'NPR letter — upload full document.'
|
||||||
},
|
},
|
||||||
|
BIRTH_CERTIFICATE: {
|
||||||
|
sides: 1,
|
||||||
|
documentKeyLabel: 'Birth certificate registration number',
|
||||||
|
documentKeyPlaceholder: 'Registration / certificate number',
|
||||||
|
category: 'Government-issued ID',
|
||||||
|
info: 'Birth certificate issued by the Municipal Corporation / Registrar of Births & Deaths — accepted proof of identity for minor clients as per RBI KYC norms.'
|
||||||
|
},
|
||||||
|
SCHOOL_ID_CARD: {
|
||||||
|
sides: 1,
|
||||||
|
documentKeyLabel: 'School ID / admission number',
|
||||||
|
documentKeyPlaceholder: 'Student ID number',
|
||||||
|
info: 'School identity card bearing photograph — accepted proof of identity for minor clients as per RBI KYC norms.'
|
||||||
|
},
|
||||||
UTILITY_BILL: {
|
UTILITY_BILL: {
|
||||||
sides: 1,
|
sides: 1,
|
||||||
category: 'Utility bills',
|
category: 'Utility bills',
|
||||||
|
|||||||
@@ -1468,13 +1468,13 @@
|
|||||||
Balance Certificate
|
Balance Certificate
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item routerLink="/letter-mgmt/form-60" tabindex="0">
|
<button mat-menu-item routerLink="/letter-mgmt/form-60" tabindex="0">
|
||||||
<fa-icon icon="file-signature" class="menu-item-icon" style="color: #15803d"></fa-icon> Form 60
|
<fa-icon icon="file-signature" class="menu-item-icon" style="color: #15803d"></fa-icon> Form 97
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item tabindex="0">
|
<button mat-menu-item tabindex="0">
|
||||||
<fa-icon icon="file-signature" class="menu-item-icon" style="color: #15803d"></fa-icon> Form 61
|
<fa-icon icon="file-signature" class="menu-item-icon" style="color: #15803d"></fa-icon> Form 98
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item routerLink="/letter-mgmt/form-15gh" tabindex="0">
|
<button mat-menu-item routerLink="/letter-mgmt/form-15gh" tabindex="0">
|
||||||
<fa-icon icon="file-signature" class="menu-item-icon" style="color: #15803d"></fa-icon> Form 15G and H
|
<fa-icon icon="file-signature" class="menu-item-icon" style="color: #15803d"></fa-icon> Form 121
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item routerLink="/letter-mgmt/hypothecation-certificate" tabindex="0">
|
<button mat-menu-item routerLink="/letter-mgmt/hypothecation-certificate" tabindex="0">
|
||||||
<fa-icon icon="file-alt" class="menu-item-icon" style="color: #15803d"></fa-icon>
|
<fa-icon icon="file-alt" class="menu-item-icon" style="color: #15803d"></fa-icon>
|
||||||
|
|||||||
@@ -969,7 +969,7 @@ export function buildForm60Pdf(r: Form60Record): jsPDF {
|
|||||||
|
|
||||||
let y = paintLetterhead(
|
let y = paintLetterhead(
|
||||||
c,
|
c,
|
||||||
'Form 60 Declaration',
|
'Form 97 Declaration',
|
||||||
'CERTIFICATE REFERENCE',
|
'CERTIFICATE REFERENCE',
|
||||||
`F60-${r.customerId}`,
|
`F60-${r.customerId}`,
|
||||||
'PAN Status',
|
'PAN Status',
|
||||||
|
|||||||
+3
-3
@@ -85,7 +85,7 @@
|
|||||||
} @else if (isBalance && balance) {
|
} @else if (isBalance && balance) {
|
||||||
<span class="cpd-stage-pill">{{ balance.asOnDate | date: 'd MMM y' }}</span>
|
<span class="cpd-stage-pill">{{ balance.asOnDate | date: 'd MMM y' }}</span>
|
||||||
} @else if (isForm60 && form60) {
|
} @else if (isForm60 && form60) {
|
||||||
<span class="cpd-stage-pill cpd-stage-pill--amber">No PAN — Form 60</span>
|
<span class="cpd-stage-pill cpd-stage-pill--amber">No PAN — Form 97</span>
|
||||||
} @else if (isForm15gh && form15gh) {
|
} @else if (isForm15gh && form15gh) {
|
||||||
<span class="cpd-stage-pill cpd-stage-pill--teal">Form {{ form15gh.kind }}</span>
|
<span class="cpd-stage-pill cpd-stage-pill--teal">Form {{ form15gh.kind }}</span>
|
||||||
} @else if (isHypothecation && hypothecation) {
|
} @else if (isHypothecation && hypothecation) {
|
||||||
@@ -313,7 +313,7 @@
|
|||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- ── FORM 60 ── -->
|
<!-- ── FORM 97 ── -->
|
||||||
@if (isForm60 && form60) {
|
@if (isForm60 && form60) {
|
||||||
<p class="cpd-para">
|
<p class="cpd-para">
|
||||||
Declaration under <strong>Section 114B</strong> of the Income-tax Rules, 1962, to be made by a person who does
|
Declaration under <strong>Section 114B</strong> of the Income-tax Rules, 1962, to be made by a person who does
|
||||||
@@ -367,7 +367,7 @@
|
|||||||
<tr class="cpd-summary-total">
|
<tr class="cpd-summary-total">
|
||||||
<td colspan="3">PAN STATUS</td>
|
<td colspan="3">PAN STATUS</td>
|
||||||
<td>
|
<td>
|
||||||
<strong>NOT ALLOTTED — Form 60 declared</strong>
|
<strong>NOT ALLOTTED — Form 97 declared</strong>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
+2
-2
@@ -151,9 +151,9 @@ export class CertificatePreviewDialogComponent {
|
|||||||
case 'balance':
|
case 'balance':
|
||||||
return 'Balance Certificate';
|
return 'Balance Certificate';
|
||||||
case 'form60':
|
case 'form60':
|
||||||
return 'Form 60 Declaration';
|
return 'Form 97 Declaration';
|
||||||
case 'form15gh':
|
case 'form15gh':
|
||||||
return this.form15gh?.kind === '15H' ? 'Form 15H Declaration' : 'Form 15G Declaration';
|
return 'Form 121 Declaration';
|
||||||
case 'hypothecation':
|
case 'hypothecation':
|
||||||
return 'Hypothecation Certificate';
|
return 'Hypothecation Certificate';
|
||||||
case 'noc':
|
case 'noc':
|
||||||
|
|||||||
@@ -11,11 +11,8 @@
|
|||||||
<div class="cert-head-l">
|
<div class="cert-head-l">
|
||||||
<fa-icon icon="file-signature" class="cert-head-ic"></fa-icon>
|
<fa-icon icon="file-signature" class="cert-head-ic"></fa-icon>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="cert-head-t">Form 15G & 15H</h2>
|
<h2 class="cert-head-t">Form 121</h2>
|
||||||
<div class="cert-head-s">
|
<div class="cert-head-s">TDS-waiver declarations for FD / RD interest income — Form 121.</div>
|
||||||
TDS-waiver declarations for FD / RD interest income —
|
|
||||||
<strong>15G</strong> for individuals below 60 years, <strong>15H</strong> for senior citizens (60+).
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,14 +23,14 @@
|
|||||||
<fa-icon icon="user" class="cert-kpi-ic"></fa-icon>
|
<fa-icon icon="user" class="cert-kpi-ic"></fa-icon>
|
||||||
<div>
|
<div>
|
||||||
<div class="cert-kpi-n">{{ count15G() | number }}</div>
|
<div class="cert-kpi-n">{{ count15G() | number }}</div>
|
||||||
<div class="cert-kpi-l">Form 15G (under 60)</div>
|
<div class="cert-kpi-l">Form 121 (under 60)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cert-kpi cert-kpi--amber">
|
<div class="cert-kpi cert-kpi--amber">
|
||||||
<fa-icon icon="user-tie" class="cert-kpi-ic"></fa-icon>
|
<fa-icon icon="user-tie" class="cert-kpi-ic"></fa-icon>
|
||||||
<div>
|
<div>
|
||||||
<div class="cert-kpi-n">{{ count15H() | number }}</div>
|
<div class="cert-kpi-n">{{ count15H() | number }}</div>
|
||||||
<div class="cert-kpi-l">Form 15H (60+)</div>
|
<div class="cert-kpi-l">Form 121 (60+)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cert-kpi cert-kpi--green">
|
<div class="cert-kpi cert-kpi--green">
|
||||||
@@ -63,11 +60,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cert-filter">
|
<div class="cert-filter">
|
||||||
<label>Form</label>
|
<label>Form Type</label>
|
||||||
<select [(ngModel)]="kindFilter">
|
<select [(ngModel)]="kindFilter">
|
||||||
<option value="">Both forms</option>
|
<option value="">All Form 121</option>
|
||||||
<option value="15G">15G — Under 60</option>
|
<option value="15G">Form 121 (Below 60 years)</option>
|
||||||
<option value="15H">15H — Senior citizens (60+)</option>
|
<option value="15H">Form 121 (Senior Citizen 60+)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -92,7 +89,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Customer · PAN</th>
|
<th>Customer · PAN</th>
|
||||||
<th>Form</th>
|
<th>Form Type</th>
|
||||||
<th>Age</th>
|
<th>Age</th>
|
||||||
<th>Branch</th>
|
<th>Branch</th>
|
||||||
<th class="num">Deposit Principal</th>
|
<th class="num">Deposit Principal</th>
|
||||||
@@ -115,7 +112,7 @@
|
|||||||
[class.cert-pill--blue]="formKind(r) === '15G'"
|
[class.cert-pill--blue]="formKind(r) === '15G'"
|
||||||
[class.cert-pill--amber]="formKind(r) === '15H'"
|
[class.cert-pill--amber]="formKind(r) === '15H'"
|
||||||
>
|
>
|
||||||
Form {{ formKind(r) }}
|
Form 121 ({{ formKind(r) }})
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="small">{{ age(r.dob) }} yrs</td>
|
<td class="small">{{ age(r.dob) }} yrs</td>
|
||||||
|
|||||||
@@ -146,9 +146,9 @@ export class Form15ghComponent implements OnInit {
|
|||||||
const kind = this.formKind(r);
|
const kind = this.formKind(r);
|
||||||
this.dscSign.promptThenDownload(
|
this.dscSign.promptThenDownload(
|
||||||
{
|
{
|
||||||
title: `Form ${kind} Declaration`,
|
title: `Form 121 (${kind}) Declaration`,
|
||||||
reference: `${r.customerId} · ${r.customerName} · ${r.fy}`,
|
reference: `${r.customerId} · ${r.customerName} · ${r.fy}`,
|
||||||
filename: `Form-${kind}-${r.customerId}-${r.fy.replace(/\s+/g, '')}.pdf`
|
filename: `Form121-${r.customerId}-${r.fy.replace(/\s+/g, '')}.pdf`
|
||||||
},
|
},
|
||||||
(dsc) => downloadForm15ghPdf(r, kind, dsc)
|
(dsc) => downloadForm15ghPdf(r, kind, dsc)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="cert-head-l">
|
<div class="cert-head-l">
|
||||||
<fa-icon icon="file-signature" class="cert-head-ic"></fa-icon>
|
<fa-icon icon="file-signature" class="cert-head-ic"></fa-icon>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="cert-head-t">Form 60 Declaration</h2>
|
<h2 class="cert-head-t">Form 97 Declaration</h2>
|
||||||
<div class="cert-head-s">
|
<div class="cert-head-s">
|
||||||
Income Tax Rule 114B — required for any customer transacting above the threshold who does
|
Income Tax Rule 114B — required for any customer transacting above the threshold who does
|
||||||
<strong>not</strong> have a PAN on file.
|
<strong>not</strong> have a PAN on file.
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<fa-icon icon="check-circle" class="cert-kpi-ic"></fa-icon>
|
<fa-icon icon="check-circle" class="cert-kpi-ic"></fa-icon>
|
||||||
<div>
|
<div>
|
||||||
<div class="cert-kpi-n">{{ totalFiled() | number }}</div>
|
<div class="cert-kpi-n">{{ totalFiled() | number }}</div>
|
||||||
<div class="cert-kpi-l">Form 60 Filed</div>
|
<div class="cert-kpi-l">Form 97 Filed</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cert-kpi cert-kpi--amber">
|
<div class="cert-kpi cert-kpi--amber">
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
<div class="cert-filter">
|
<div class="cert-filter">
|
||||||
<label>PAN status</label>
|
<label>PAN status</label>
|
||||||
<select [(ngModel)]="panFilter">
|
<select [(ngModel)]="panFilter">
|
||||||
<option value="MISSING">Without PAN (Form 60 applicable)</option>
|
<option value="MISSING">Without PAN (Form 97 applicable)</option>
|
||||||
<option value="ON_FILE">With PAN on file</option>
|
<option value="ON_FILE">With PAN on file</option>
|
||||||
<option value="">All customers</option>
|
<option value="">All customers</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
<th>Trigger Transaction</th>
|
<th>Trigger Transaction</th>
|
||||||
<th class="num">Amount</th>
|
<th class="num">Amount</th>
|
||||||
<th>PAN Status</th>
|
<th>PAN Status</th>
|
||||||
<th>Form 60</th>
|
<th>Form 97</th>
|
||||||
<th class="col-actions">Actions</th>
|
<th class="col-actions">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
class="cert-icon-btn primary"
|
class="cert-icon-btn primary"
|
||||||
[disabled]="r.hasPan"
|
[disabled]="r.hasPan"
|
||||||
(click)="viewCertificate(r)"
|
(click)="viewCertificate(r)"
|
||||||
matTooltip="View / Print Form 60"
|
matTooltip="View / Print Form 97"
|
||||||
>
|
>
|
||||||
<fa-icon icon="eye"></fa-icon>
|
<fa-icon icon="eye"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export class Form60Component implements OnInit {
|
|||||||
|
|
||||||
viewCertificate(r: Form60Record): void {
|
viewCertificate(r: Form60Record): void {
|
||||||
if (r.hasPan) {
|
if (r.hasPan) {
|
||||||
this.snack.open(`${r.customerName} already has a PAN on file — Form 60 is not applicable.`, 'OK', {
|
this.snack.open(`${r.customerName} already has a PAN on file — Form 97 is not applicable.`, 'OK', {
|
||||||
duration: 3200
|
duration: 3200
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -139,14 +139,14 @@ export class Form60Component implements OnInit {
|
|||||||
|
|
||||||
downloadCertificate(r: Form60Record): void {
|
downloadCertificate(r: Form60Record): void {
|
||||||
if (r.hasPan) {
|
if (r.hasPan) {
|
||||||
this.snack.open(`${r.customerName} already has a PAN on file — Form 60 is not applicable.`, 'OK', {
|
this.snack.open(`${r.customerName} already has a PAN on file — Form 97 is not applicable.`, 'OK', {
|
||||||
duration: 3200
|
duration: 3200
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.dscSign.promptThenDownload(
|
this.dscSign.promptThenDownload(
|
||||||
{
|
{
|
||||||
title: 'Form 60 Declaration',
|
title: 'Form 97 Declaration',
|
||||||
reference: `${r.customerId} · ${r.customerName}`,
|
reference: `${r.customerId} · ${r.customerName}`,
|
||||||
filename: `Form60-${r.customerId}.pdf`
|
filename: `Form60-${r.customerId}.pdf`
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'letter-mgmt/form-60',
|
path: 'letter-mgmt/form-60',
|
||||||
component: Form60Component,
|
component: Form60Component,
|
||||||
data: { title: 'Form 60', breadcrumb: 'Form 60' }
|
data: { title: 'Form 97', breadcrumb: 'Form 97' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'letter-mgmt/form-15gh',
|
path: 'letter-mgmt/form-15gh',
|
||||||
component: Form15ghComponent,
|
component: Form15ghComponent,
|
||||||
data: { title: 'Form 15G & 15H', breadcrumb: 'Form 15G/H' }
|
data: { title: 'Form 121', breadcrumb: 'Form 121' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'letter-mgmt/hypothecation-certificate',
|
path: 'letter-mgmt/hypothecation-certificate',
|
||||||
|
|||||||
@@ -2084,8 +2084,8 @@
|
|||||||
"Floating Rates": "Floating Rates",
|
"Floating Rates": "Floating Rates",
|
||||||
"Floating rate": "Floating Rate",
|
"Floating rate": "Floating Rate",
|
||||||
"Follow": "Follow",
|
"Follow": "Follow",
|
||||||
"Form 60": "Form 60",
|
"Form 60": "Form 97",
|
||||||
"Form 60 Declaration": "Form 60 Declaration",
|
"Form 60 Declaration": "Form 97 Declaration",
|
||||||
"Frequency": "Frequency",
|
"Frequency": "Frequency",
|
||||||
"Frequency Interval": "Frequency Interval",
|
"Frequency Interval": "Frequency Interval",
|
||||||
"Frequency Interval for compounding": "Frequency Interval for compounding",
|
"Frequency Interval for compounding": "Frequency Interval for compounding",
|
||||||
@@ -3983,7 +3983,7 @@
|
|||||||
"Organization available languages": "Organization can select a language here from the available languages here.",
|
"Organization available languages": "Organization can select a language here from the available languages here.",
|
||||||
"Original Schedule": "Original Schedule",
|
"Original Schedule": "Original Schedule",
|
||||||
"Overdue Charges": "Overdue Charges",
|
"Overdue Charges": "Overdue Charges",
|
||||||
"PAN not provided. Please fill Form 60 as per Income Tax Rule 114B.": "PAN not provided. Please fill Form 60 as per Income Tax Rule 114B.",
|
"PAN not provided. Please fill Form 60 as per Income Tax Rule 114B.": "PAN not provided. Please fill Form 97 as per Income Tax Rule 114B.",
|
||||||
"Password Preferences": "Password Preferences",
|
"Password Preferences": "Password Preferences",
|
||||||
"Passwords": "Passwords",
|
"Passwords": "Passwords",
|
||||||
"Pay Charge": "Pay Charge",
|
"Pay Charge": "Pay Charge",
|
||||||
|
|||||||
@@ -9,10 +9,9 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
export default {
|
export default {
|
||||||
'mifos_x': {
|
'mifos_x': {
|
||||||
'version': '260723',
|
'version': '260728',
|
||||||
'hash': 'ca85bae4'
|
'hash': 'a9c60083'
|
||||||
},
|
},
|
||||||
'allow_switching_backend_instance': true
|
'allow_switching_backend_instance': true
|
||||||
};
|
};
|
||||||
/* tslint:enable */
|
/* tslint:enable */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user