Files
cbsnexora/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.ts
T
AdityaandClaude Opus 4.6 b890d24bb7 feat: non-individual entity UI improvements — hide individual-only sections, fix legal form display, send extended fields to API
- Hide Family Members tab/section for non-individual entities in client view, create flow, and preview
- Hide Financial & Classification section for non-individual entities in personal data and preview
- Fix legal form column in clients list — fetch legalForm from client API for correct display
- Add person/business icon before client name in list rows
- Remove unsupportedNpd filter — send authorizedSignatory, additionalInfo, dateOfRegistration to API
- Add registration number format hint (mat-hint) based on constitution type
- Add spacing between verification row and constitution type dropdowns
- Reduce table cell padding and prevent action buttons from being clipped
- Add missing MPL-2.0 license headers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 15:10:51 +05:30

312 lines
9.8 KiB
TypeScript

/**
* Copyright since 2025 Mifos Initiative
*/
import { Component, Input, OnInit, OnChanges, SimpleChanges, inject } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MatCheckbox } from '@angular/material/checkbox';
import { MatIcon } from '@angular/material/icon';
import { MatTooltip } from '@angular/material/tooltip';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatSlideToggle } from '@angular/material/slide-toggle';
import { ClientsService } from '../../clients.service';
import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
export interface CategoryMeta {
id: string;
label: string;
tag: string;
tagColor: string;
tagBg: string;
desc: string;
icon: string;
}
@Component({
selector: 'mifosx-client-entity-kyc-step',
templateUrl: './client-entity-kyc-step.component.html',
styleUrls: ['./client-entity-kyc-step.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent,
MatCheckbox,
MatIcon,
MatTooltip,
MatProgressSpinner,
MatSlideToggle
]
})
export class ClientEntityKycStepComponent implements OnInit, OnChanges {
private fb = inject(UntypedFormBuilder);
private clientService = inject(ClientsService);
private settingsService = inject(SettingsService);
private dateUtils = inject(Dates);
@Input() clientId: string = '';
@Input() constitutionType: string = '';
/** Read-only mode — disables form editing until user clicks edit icon */
@Input() readOnly = false;
today = new Date();
/** Category metadata */
categories: CategoryMeta[] = [
{ id: 'msme', label: 'MSME', tag: 'Udyam / MSME', tagColor: '#0A5C6B', tagBg: '#E6F4F6', desc: 'Micro, Small & Medium Enterprise — Ministry of MSME / Udyam Registration', icon: 'cogs' },
{ id: 'startup', label: 'Startup India Registered', tag: 'DPIIT', tagColor: '#1A5FAB', tagBg: '#EAF2FB', desc: 'DPIIT recognition under Startup India initiative', icon: 'rocket' },
{ id: 'mudra', label: 'Mudra Borrower', tag: 'PMMY', tagColor: '#8A5A00', tagBg: '#FEF3DC', desc: 'Pradhan Mantri Mudra Yojana — SIDBI / Mudra scheme', icon: 'hand-holding-usd' },
{ id: 'eou', label: 'Export Oriented Unit', tag: 'EOU', tagColor: '#1B7A4A', tagBg: '#E8F5EE', desc: 'Customs-registered EOU under DGFT / Ministry of Commerce', icon: 'ship' },
{ id: 'importexport', label: 'Import Export Business', tag: 'DGFT / IEC', tagColor: '#5B3A8A', tagBg: '#F0EBF8', desc: 'Entity with valid DGFT Importer Exporter Code', icon: 'exchange-alt' },
{ id: 'psl', label: 'Priority Sector Borrower', tag: 'RBI PSL', tagColor: '#7B1F1F', tagBg: '#FCEAEA', desc: 'Eligible under RBI Priority Sector Lending guidelines', icon: 'bullseye' },
{ id: 'nps', label: 'Non-Priority Sector', tag: 'General', tagColor: '#444441', tagBg: '#F1EFE8', desc: 'Standard corporate — no special priority sector classification', icon: 'building' }
];
/** Toggle & expand state per category */
enabled: Record<string, boolean> = {};
expanded: Record<string, boolean> = {};
/** Forms per category */
msmeForm!: UntypedFormGroup;
startupForm!: UntypedFormGroup;
mudraForm!: UntypedFormGroup;
eouForm!: UntypedFormGroup;
importexportForm!: UntypedFormGroup;
pslForm!: UntypedFormGroup;
npsForm!: UntypedFormGroup;
/** Saving state */
saving: Record<string, boolean> = {};
saved: Record<string, boolean> = {};
ngOnInit() {
this.initForms();
if (this.clientId) {
this.loadExisting();
}
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['readOnly']) {
const forms = [this.msmeForm, this.startupForm, this.mudraForm, this.eouForm, this.importexportForm, this.pslForm, this.npsForm];
for (const form of forms) {
if (!form) continue;
if (this.readOnly) {
form.disable({ emitEvent: false });
} else {
form.enable({ emitEvent: false });
}
}
}
}
private initForms() {
this.msmeForm = this.fb.group({
udyamRegistrationNumber: [''],
registrationDate: [''],
registrationStatus: [''],
msmeClassification: [''],
enterpriseType: [''],
nicActivityCode: [''],
annualTurnover: [''],
investmentPlantMachinery: [''],
financialYearAssessment: [''],
socialCategoryOwner: [''],
genderPrimaryOwner: [''],
noOfEmployees: [''],
womenLedEnterprise: [false],
divyangPwdOwned: [false]
});
this.startupForm = this.fb.group({
dpiitRecognitionNumber: [''],
recognitionDate: [''],
certificateExpiryDate: [''],
entityStage: [''],
sectorAsDpiit: [''],
nicIndustryCode: [''],
taxExemption80iac: [false],
sidbiFund: [false],
selfCertification: [false],
fastTrackPatent: [false],
govtTenderExemption: [false],
creditGuaranteeCgss: [false],
pslLoanLimit: [''],
pslSubCategory: ['']
});
this.mudraForm = this.fb.group({
mudraCategory: [''],
mudraLoanAccountNo: [''],
lendingInstitutionName: [''],
sanctionedAmount: [''],
disbursementDate: [''],
loanPurpose: [''],
mudraCardNumber: [''],
currentOutstanding: [''],
repaymentStatus: [''],
npaFlag: [false]
});
this.eouForm = this.fb.group({
eouRegistrationNumber: [''],
registeredDcZone: [''],
bondLutNumber: [''],
validityFrom: [''],
validityTo: [''],
gstinEou: [''],
annualExportObligation: [''],
nfeTarget: [''],
softexFilingStatus: [''],
brcSubmissionStatus: [''],
adCategoryBankCode: [''],
femaDeclarationDate: ['']
});
this.importexportForm = this.fb.group({
iecCode: [''],
iecIssueDate: [''],
iecStatus: [''],
tradeType: [''],
primaryHsCode: [''],
keyTradingCountries: [''],
adCategoryBankCode: [''],
ecgcPolicyNumber: [''],
ecgcPolicyExpiry: [''],
eefcAccountRequired: [false],
pcfcFacilityRequired: [false],
femaDeclarationObtained: [false]
});
this.pslForm = this.fb.group({
pslCategory: [''],
pslSubCategory: [''],
pslLoanPurposeCode: [''],
pslEligibilityBasis: [''],
fiddReportingCode: [''],
pslSanctionedLimit: [''],
msmeClassification: [''],
udyamRegistrationNumber: [''],
udyamRegistrationDate: [''],
annualTurnover: [''],
investmentPlantMachinery: [''],
weakerSectionFlag: [false],
scStBorrowerFlag: [false],
womenEntrepreneurFlag: [false],
minorityCommunityFlag: [false]
});
this.npsForm = this.fb.group({
industrySector: [''],
nicCode: [''],
industryDescription: [''],
annualRevenueBand: [''],
riskCategory: [''],
customerRiskScore: [''],
amlRiskClassification: [''],
pepFlag: [false],
amlWatchlistMatch: [false],
adverseMediaFlag: [false]
});
}
getForm(categoryId: string): UntypedFormGroup {
switch (categoryId) {
case 'msme': return this.msmeForm;
case 'startup': return this.startupForm;
case 'mudra': return this.mudraForm;
case 'eou': return this.eouForm;
case 'importexport': return this.importexportForm;
case 'psl': return this.pslForm;
case 'nps': return this.npsForm;
default: return this.fb.group({});
}
}
toggleCategory(id: string) {
this.enabled[id] = !this.enabled[id];
if (this.enabled[id]) {
this.expanded[id] = true;
}
}
toggleExpand(id: string) {
this.expanded[id] = !this.expanded[id];
}
get enabledCategories(): CategoryMeta[] {
return this.categories.filter(c => this.enabled[c.id]);
}
get enabledCount(): number {
return this.enabledCategories.length;
}
removeCategory(id: string) {
this.enabled[id] = false;
this.expanded[id] = false;
}
/** Save a single category */
saveCategory(categoryId: string) {
if (!this.clientId) return;
this.saving[categoryId] = true;
const form = this.getForm(categoryId);
const payload: any = { ...form.value, enabled: true, locale: 'en', dateFormat: this.settingsService.dateFormat };
// Format date fields
for (const key of Object.keys(payload)) {
if (payload[key] instanceof Date) {
payload[key] = this.dateUtils.formatDate(payload[key], this.settingsService.dateFormat);
}
}
this.clientService.saveEntityClassification(this.clientId, categoryId, payload).subscribe({
next: () => {
this.saving[categoryId] = false;
this.saved[categoryId] = true;
setTimeout(() => this.saved[categoryId] = false, 3000);
},
error: () => {
this.saving[categoryId] = false;
}
});
}
/** Load existing classifications */
private loadExisting() {
this.clientService.getEntityClassifications(this.clientId).subscribe({
next: (data: any) => {
if (!data) return;
for (const cat of this.categories) {
const catData = data[cat.id];
if (catData && catData.enabled) {
this.enabled[cat.id] = true;
this.getForm(cat.id).patchValue(catData);
}
}
}
});
}
/** Get all classification data for parent */
get classificationData(): any {
const result: any = {};
for (const cat of this.categories) {
if (this.enabled[cat.id]) {
result[cat.id] = { ...this.getForm(cat.id).value, enabled: true };
}
}
return result;
}
get hasClassificationData(): boolean {
return this.enabledCount > 0;
}
// Keep old getter for backward compatibility
get entityKycData(): any { return this.classificationData; }
get hasEntityKycData(): boolean { return this.hasClassificationData; }
}