feat(clients, alm): add CIN/MCA verification & authorized signatories tab; refactor ALM report print and export
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
import { toProperNameCase } from './name-case.util';
|
||||
|
||||
describe('toProperNameCase', () => {
|
||||
it('raises a lower-case first letter on every word', () => {
|
||||
expect(toProperNameCase('keshav Singh sisodiya')).toBe('Keshav Singh Sisodiya');
|
||||
});
|
||||
|
||||
it('leaves the rest of each word exactly as typed', () => {
|
||||
expect(toProperNameCase('lincpay lTD')).toBe('Lincpay LTD');
|
||||
expect(toProperNameCase('lincpay pvt ltd')).toBe('Lincpay Pvt Ltd');
|
||||
});
|
||||
|
||||
it('does not rewrite a name that is already correct', () => {
|
||||
expect(toProperNameCase('Technoludium Solutions Private Limited')).toBe('Technoludium Solutions Private Limited');
|
||||
});
|
||||
|
||||
it('leaves an upper-case name shouted rather than title-casing it', () => {
|
||||
expect(toProperNameCase('TECHNOLUDIUM SOLUTIONS')).toBe('TECHNOLUDIUM SOLUTIONS');
|
||||
expect(toProperNameCase('KESHAV singh')).toBe('KESHAV Singh');
|
||||
});
|
||||
|
||||
it('capitalises after a hyphen', () => {
|
||||
expect(toProperNameCase('jean-marie traders')).toBe('Jean-Marie Traders');
|
||||
});
|
||||
|
||||
it('capitalises name prefixes after an apostrophe but not possessives', () => {
|
||||
expect(toProperNameCase("o'brien d'souza")).toBe("O'Brien D'Souza");
|
||||
expect(toProperNameCase("lincpay's agency")).toBe("Lincpay's Agency");
|
||||
});
|
||||
|
||||
it('leaves punctuation and non-letters untouched', () => {
|
||||
expect(toProperNameCase('abc pvt. ltd.')).toBe('Abc Pvt. Ltd.');
|
||||
expect(toProperNameCase('tata & sons')).toBe('Tata & Sons');
|
||||
expect(toProperNameCase('3m india')).toBe('3m India');
|
||||
});
|
||||
|
||||
it('trims and collapses whitespace', () => {
|
||||
expect(toProperNameCase(' lincpay holdings ')).toBe('Lincpay Holdings');
|
||||
});
|
||||
|
||||
it('returns an empty string for blank or non-string input', () => {
|
||||
expect(toProperNameCase('')).toBe('');
|
||||
expect(toProperNameCase(' ')).toBe('');
|
||||
expect(toProperNameCase(null)).toBe('');
|
||||
expect(toProperNameCase(undefined)).toBe('');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Casing for client names: the name is stored exactly as the officer typed it, except that a word
|
||||
* starting with a lower-case letter has that first letter raised. Nothing else is touched.
|
||||
*
|
||||
* 'keshav Singh sisodiya' → 'Keshav Singh Sisodiya'
|
||||
* 'lincpay lTD' → 'Lincpay LTD'
|
||||
* 'Technoludium Solutions Private Limited' → unchanged
|
||||
* 'TECHNOLUDIUM SOLUTIONS' → unchanged
|
||||
*
|
||||
* Deliberately NOT full title-casing. Lower-casing the rest of each word would rewrite an entity
|
||||
* name the officer entered as it appears on the certificate — 'TECHNOLUDIUM' becoming
|
||||
* 'Technoludium', 'Ltd' becoming a forced 'LTD'. That name is matched character-for-character
|
||||
* against the PAN holder's registered name during KYC verification, so the typed spelling is the
|
||||
* thing to preserve; only an unintended lower-case first letter is corrected.
|
||||
*/
|
||||
|
||||
/** Characters after which a new word starts: 'jean-marie' → 'Jean-Marie'. */
|
||||
const SEGMENT_STARTERS = new Set([
|
||||
'-',
|
||||
"'",
|
||||
'’'
|
||||
]);
|
||||
|
||||
/**
|
||||
* Raises the first letter of each word in a single space-delimited token, leaving every other
|
||||
* character as typed.
|
||||
*/
|
||||
function capitalizeWord(word: string): string {
|
||||
let result = '';
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
const previous = i === 0 ? '' : word[i - 1];
|
||||
let startsWord = i === 0 || SEGMENT_STARTERS.has(previous);
|
||||
if (previous === "'" || previous === '’') {
|
||||
// 'o'brien' / 'd'souza' capitalise after the apostrophe, but a possessive
|
||||
// ('lincpay's') must not — only a single leading letter marks the name prefix.
|
||||
startsWord = i === 2;
|
||||
}
|
||||
result += startsWord ? word[i].toUpperCase() : word[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the casing rule above to a person or entity name.
|
||||
* Trims and collapses runs of whitespace; non-string input yields ''.
|
||||
*/
|
||||
export function toProperNameCase(value: string | null | undefined): string {
|
||||
if (typeof value !== 'string') {
|
||||
return '';
|
||||
}
|
||||
const collapsed = value.trim().replace(/\s+/g, ' ');
|
||||
if (!collapsed) {
|
||||
return '';
|
||||
}
|
||||
return collapsed.split(' ').map(capitalizeWord).join(' ');
|
||||
}
|
||||
Reference in New Issue
Block a user