diff --git a/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.ts b/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.ts index 4328be591..e02b48b3f 100644 --- a/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.ts +++ b/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.ts @@ -238,6 +238,42 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { } } + /** + * Resets transient address-tab state for a brand-new client. + * + * This app's RouteReusableStrategy (core/route/route-reusable-strategy.ts) keeps this SAME + * component instance alive across repeat visits to /clients/create, so `ngOnInit` never + * re-runs for a second "create new client" session — the previous client's address forms, + * selected proof-of-address chip and uploaded files would otherwise leak into the new one. + * Called by CreateClientComponent.resetForNewClient() whenever a fresh create session begins. + */ + resetForNewClient(): void { + this.permanentForm.reset({ country: 'India' }); + this.currentForm.reset({ country: 'India' }); + this.officeForm.reset({ country: 'India' }); + this.correspondenceForm.reset({ country: 'India' }); + this.activeTab = 0; + this.sameAsPermanent = false; + this.pinStatus = { + permanent: { loading: false, found: false, error: false, message: '' }, + current: { loading: false, found: false, error: false, message: '' }, + office: { loading: false, found: false, error: false, message: '' }, + correspondence: { loading: false, found: false, error: false, message: '' } + }; + this.localityOptions = { permanent: [], current: [], office: [], correspondence: [] }; + this.selectedAddrProof = null; + this.addrFiles = { front: null, back: null }; + this.addrMergedFile = null; + this.addrMergedPreview = null; + this.existingAddressProofIdentifierId = null; + this.existingAddressProofDocuments = []; + this.addressLoaded = false; + // Re-fetch rather than re-filter `addressProofs` in place — if the previous session's + // isMinor filtering already reduced it (e.g. down to Aadhaar/Passport only), filtering + // that reduced list again can never recover the items it already removed. + this.loadProofTypes(); + } + private loadProofTypes(): void { this.loadingProofs = true; this.clientsService.getKycProofTypes().subscribe({ diff --git a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html index 60c70538c..efce2ddb7 100644 --- a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html +++ b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.html @@ -161,6 +161,17 @@ } } + @if (aadhaarOtpSent && aadhaarVerifyStatus !== 'VERIFIED') { + + } @if (aadhaarVerifyStatus === 'VERIFIED') { Verified } diff --git a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.scss b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.scss index 2b53e5ecc..b4f30d859 100644 --- a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.scss +++ b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.scss @@ -266,6 +266,23 @@ gap: 6px; } +.change-number-btn { + font-size: 12px !important; + font-weight: 600 !important; + padding: 0 14px !important; + height: 36px !important; + border-radius: 6px !important; + display: flex; + align-items: center; + gap: 6px; + + mat-icon { + font-size: 16px; + width: 16px; + height: 16px; + } +} + .verify-chip { display: inline-flex; align-items: center; diff --git a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts index c3cf4edf8..78ad94746 100644 --- a/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts +++ b/src/app/clients/client-stepper/client-kyc-step/client-kyc-step.component.ts @@ -462,15 +462,16 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { 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' - ]; + /** RBI-accepted OVDs for a minor client — mirrors the backend's minor branch in + * ClientKycApiResource.getRequiredDocuments(). Voter ID, Driving Licence, PAN and NREGA + * all require adult eligibility; National Population Register Letter is deliberately + * excluded too (not offered as a minor OVD in this system). */ + private static readonly MINOR_ELIGIBLE_DOC_CODES = new Set([ + 'AADHAAR', + 'PASSPORT', + 'BIRTH_CERTIFICATE', + 'SCHOOL_ID_CARD' + ]); /** Birth Certificate / School ID Card are minor-specific OVDs — not relevant once the * client is an adult, so they're hidden the other direction. */ @@ -481,27 +482,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { private filterDocTypesForMinor(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_ELIGIBLE_DOC_CODES.has((d.code || '').toUpperCase())); } return docs.filter((d) => !ClientKycStepComponent.MINOR_ONLY_DOC_CODES.has((d.code || '').toUpperCase())); } ngOnInit(): void { - // Seed defaults immediately so the picker has options before clientId is set - // (template API requires clientId; user can still pick a type pre-save). - if (!this.kycDocumentTypes.length) { - this.kycDocumentTypes = this.filterDocTypesForMinor( - this.defaultDocumentTypes.map((d) => ({ - name: d.name, - code: d.code, - mandatory: false - })) - ); - this.rebuildKycDocPicker(); - } + this.seedDefaultDocTypes(); this.loadExistingKycIfNeeded(); this.loadKycTemplate(); if (this.isNonIndividual && this.constitutionMeta) { @@ -509,6 +496,92 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { } } + /** Seed the document-type picker from defaults, filtered by the current isMinor input. + * Only seeds when the list is empty — the template API (loadKycTemplate) takes over once + * a clientId is available. */ + private seedDefaultDocTypes(): void { + if (this.kycDocumentTypes.length) return; + this.kycDocumentTypes = this.filterDocTypesForMinor( + this.defaultDocumentTypes.map((d) => ({ + name: d.name, + code: d.code, + mandatory: false + })) + ); + this.rebuildKycDocPicker(); + } + + /** + * Resets transient KYC-tab state for a brand-new client. + * + * This app's RouteReusableStrategy (core/route/route-reusable-strategy.ts) keeps this SAME + * component instance alive across repeat visits to /clients/create, so `ngOnInit` never + * re-runs for a second "create new client" session. Without this, the document-type picker + * (and any PAN/Aadhaar verification state, uploaded photo/signature) would otherwise leak + * from whatever client was created in the previous session into the new one. Called by + * CreateClientComponent.resetForNewClient() whenever a fresh create session begins. + */ + resetForNewClient(): void { + this.kycForm.reset({ + hasPan: true, + riskCategory: 1, + amlCheckStatus: 'CLEAR', + bannedEntityMatch: 'NO', + willfulDefaulter: 'NO', + ecgcSalFlag: 'NO', + dormantFlag: 'ACTIVE', + accountStatus: 'ACTIVE', + form60Type: 'FORM_60', + form15Type: '15G' + }); + this.kycId = ''; + this.kycLoaded = false; + this.kycStatus = 0; + this.kycTemplate = null; + this.kycTemplateLoaded = false; + this.kycDocumentTypes = []; + this.selectedKycDoc = null; + this.kycDocFiles = { front: null, back: null }; + this.kycDocMergedFile = null; + this.kycDocMergedPreview = null; + this.existingKycIdentifierId = null; + this.existingKycDocuments = []; + this.photoSigTouched = false; + this.panVerifyStatus = 'NONE'; + this.panVerifying = false; + this.panVerifyError = ''; + this.panVerifyName = ''; + this.panVerifiedOn = ''; + this.aadhaarVerifyStatus = 'NONE'; + this.aadhaarVerifying = false; + this.aadhaarVerifyError = ''; + this.aadhaarVerifyName = ''; + this.aadhaarVisible = false; + this.aadhaarOtpSent = false; + this.resetOtpDigits(); + this.aadhaarOtpReferenceId = ''; + this.panAadhaarSeedingStatus = ''; + this.entPanVerifyStatus = 'NONE'; + this.entPanVerifying = false; + this.entPanVerifyName = ''; + this.entPanVerifiedOn = ''; + this.gstinVerifyStatus = 'NONE'; + this.gstinVerifying = false; + this.gstinVerifyName = ''; + this.gstinVerifiedOn = ''; + this.gstinVerifyState = ''; + this.cinVerifyStatus = 'NONE'; + this.cinVerifying = false; + this.cinVerifiedOn = ''; + this.cinCompanyName = ''; + this.photographFile = null; + this.photographPreview = null; + this.signatureFile = null; + this.signaturePreview = null; + this.existingSignatureDocId = ''; + this.seedDefaultDocTypes(); + } + ngOnDestroy(): void { if (this.cinProgressTimer) clearInterval(this.cinProgressTimer); if (this.resendTimer) clearInterval(this.resendTimer); @@ -1375,6 +1448,21 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { ); } + /** Lets the user go back and fix a wrong Aadhaar number after an OTP was already sent — + * unlocks the (readonly-while-OTP-pending) input and discards the in-flight OTP session, + * since it was requested for the now-being-corrected number. */ + changeAadhaarNumber(): void { + this.aadhaarOtpSent = false; + this.aadhaarVerifyError = ''; + this.aadhaarOtpReferenceId = ''; + this.resetOtpDigits(); + this.resendCountdown = 0; + if (this.resendTimer) { + clearInterval(this.resendTimer); + this.resendTimer = null; + } + } + /** Verify Aadhaar OTP */ verifyAadhaarOtp() { if (!this.aadhaarOtp || !this.clientId || !this.kycId) return; diff --git a/src/app/clients/create-client/create-client.component.ts b/src/app/clients/create-client/create-client.component.ts index 8a0e00863..8d8286acb 100644 --- a/src/app/clients/create-client/create-client.component.ts +++ b/src/app/clients/create-client/create-client.component.ts @@ -139,6 +139,8 @@ export class CreateClientComponent { this.validationError = ''; this.prefillData = null; this.clientGeneralStep?.resetForNewClient(); + this.clientKycStep?.resetForNewClient(); + this.clientIndiaAddressStep?.resetForNewClient(); } /** Check if a tab is in read-only mode (completed tab navigated back to) */ diff --git a/src/environments/.env.ts b/src/environments/.env.ts index 373b7c4a3..77c6490a2 100644 --- a/src/environments/.env.ts +++ b/src/environments/.env.ts @@ -10,7 +10,7 @@ export default { 'mifos_x': { 'version': '260728', - 'hash': 'a9c60083' + 'hash': '2e4187c3' }, 'allow_switching_backend_instance': true };