Files
cbsnexora/src/app/clients/shared/entity-kyc-documents.ts
T
keshav 5581d4180d
Playwright E2E Tests / Run Playwright Tests (push) Canceled after 0s
Tests / testRigor (push) Canceled after 0s
build-run / Run Lint, Build and Deploy (push) Canceled after 0s
Publish Image in Docker Hub / build (push) Canceled after 0s
feat(clients, alm): add CIN/MCA verification & authorized signatories tab; refactor ALM report print and export
2026-08-13 15:05:16 +05:30

139 lines
5.8 KiB
TypeScript

/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/** Shape the KYC document-type picker works with. */
export interface KycDocumentType {
name: string;
code: string;
mandatory: boolean;
category?: string;
}
export const ENTITY_KYC_CATEGORY = 'Entity KYC documents';
/**
* The KYC documents every legal entity must produce, whatever its constitution — RBI Master
* Direction on KYC, Annexure I. Codes match PROOF_META in kyc-proof-types.ts, which is what
* groups them under "Entity KYC documents" and supplies each chip's banner text.
*
* This is the single source for the entity list so a non-individual client sees the same
* documents wherever the picker is rendered — the create stepper, the edit stepper, before a
* constitution has been chosen and after, and once the backend KYC template has loaded.
*/
export const ENTITY_KYC_DOCUMENT_TYPES: { name: string; code: string }[] = [
{ name: 'Certificate of Incorporation / Registration Certificate', code: 'CERT_INCORPORATION' },
{ name: 'PAN Card of Entity', code: 'ENTITY_PAN' },
{ name: 'Memorandum & Articles of Association / Partnership Deed / Trust Deed', code: 'CONSTITUTIVE_DOC' },
{ name: 'Board Resolution / Authorization Letter', code: 'BOARD_RESOLUTION' },
{ name: 'KYC of Authorized Signatory / Power of Attorney Holder', code: 'AUTH_SIGNATORY_KYC' },
{ name: 'GST Registration Certificate', code: 'GST_CERTIFICATE' }
];
/**
* Codes the backend KYC template uses for documents the entity list already covers
* (ClientKycApiResource.getRequiredDocuments). Folding them onto the entity codes keeps one
* chip per document instead of "PAN Card of Company" sitting next to "PAN Card of Entity".
*/
const ENTITY_DOC_CODE_ALIASES: Record<string, string> = {
CERT_INCORPORATION: 'CERT_INCORPORATION',
COI: 'CERT_INCORPORATION',
REG_CERT: 'CERT_INCORPORATION',
ENTITY_PAN: 'ENTITY_PAN',
PAN: 'ENTITY_PAN',
CONSTITUTIVE_DOC: 'CONSTITUTIVE_DOC',
MOA: 'CONSTITUTIVE_DOC',
AOA: 'CONSTITUTIVE_DOC',
PARTNERSHIP_DEED: 'CONSTITUTIVE_DOC',
TRUST_DEED: 'CONSTITUTIVE_DOC',
BOARD_RESOLUTION: 'BOARD_RESOLUTION',
AUTH_SIGNATORY_KYC: 'AUTH_SIGNATORY_KYC',
SIGNATORY_KYC: 'AUTH_SIGNATORY_KYC',
GST_CERTIFICATE: 'GST_CERTIFICATE',
GST_CERT: 'GST_CERTIFICATE'
};
/**
* The per-constitution lists carry no codes of their own (code === name), so those entries are
* matched on the label instead. Patterns are anchored deliberately: a document that merely
* mentions another one ("Proof of Business Existence (GST Certificate / Shop License)") is a
* distinct requirement and has to survive as its own chip rather than vanish into GST.
*/
const ENTITY_DOC_NAME_PATTERNS: { pattern: RegExp; code: string }[] = [
{ pattern: /^certificate of incorporation\b/, code: 'CERT_INCORPORATION' },
{ pattern: /^registration certificate\b/, code: 'CERT_INCORPORATION' },
{
pattern: /^(memorandum|articles of association|partnership deed|trust deed|llp agreement|by-?laws)\b/,
code: 'CONSTITUTIVE_DOC'
},
{ pattern: /^pan\b/, code: 'ENTITY_PAN' },
{ pattern: /^(board resolution|designated partners resolution|resolution authorizing)\b/, code: 'BOARD_RESOLUTION' },
{ pattern: /^(kyc of|authorized signatory kyc|proprietor'?s kyc)\b/, code: 'AUTH_SIGNATORY_KYC' },
{ pattern: /^gst (registration )?certificate\b/, code: 'GST_CERTIFICATE' }
];
/** The entity document this one duplicates, or null when it is a requirement of its own. */
function resolveEntityDocCode(doc: { name?: string; code?: string }): string | null {
const code = (doc.code || '').toUpperCase().trim();
// A constitution list stores the label in `code`, so only treat it as a code when it is one.
if (code && !code.includes(' ') && ENTITY_DOC_CODE_ALIASES[code]) {
return ENTITY_DOC_CODE_ALIASES[code];
}
const name = (doc.name || '').toLowerCase().trim();
return ENTITY_DOC_NAME_PATTERNS.find((p) => p.pattern.test(name))?.code || null;
}
/**
* Puts the RBI entity documents in front of whatever list a caller has (per-constitution docs,
* or the backend KYC template) instead of letting that list replace them.
*
* Without this the officer sees a different set of documents on every screen for the same
* client: the entity list until a constitution is picked, the constitution's own list after,
* and the backend template's list once the client exists and is reopened for edit. Anything in
* `extra` that isn't already one of the six stays, grouped under `extraCategory` — those are
* genuine constitution-specific requirements (Karta declaration, Udyam certificate, government
* notification) and dropping them would lose real requirements.
*
* A mandatory flag on a duplicate carries over to the entity chip it folds into, so the
* "at least one mandatory document" validator still sees it.
*/
export function withEntityKycDocuments(
extra: { name: string; code?: string; mandatory?: boolean }[] = [],
extraCategory?: string | null
): KycDocumentType[] {
const mandatoryCodes = new Set<string>();
const leftovers: KycDocumentType[] = [];
extra.forEach((doc) => {
const entityCode = resolveEntityDocCode(doc);
if (entityCode) {
if (doc.mandatory) {
mandatoryCodes.add(entityCode);
}
return;
}
leftovers.push({
name: doc.name,
code: doc.code || doc.name,
mandatory: !!doc.mandatory,
category: extraCategory ? `${extraCategory} documents` : 'Additional documents'
});
});
const base: KycDocumentType[] = ENTITY_KYC_DOCUMENT_TYPES.map((d) => ({
name: d.name,
code: d.code,
mandatory: mandatoryCodes.has(d.code),
category: ENTITY_KYC_CATEGORY
}));
return [
...base,
...leftovers
];
}