Complete bank selector dropdown and SWIFT/BIC registry
- Added Bank type and BankRegistry interface - Created banks service with CRUD operations and validation - Added bank dropdown to Transactions page with ESTRBRRJ as default - Extended Transaction type with bankSwiftCode field - Added unit tests for bank registry - Bank information stored in TypeScript module (can be migrated to DB/XML)
This commit is contained in:
158
packages/utils/src/banks.ts
Normal file
158
packages/utils/src/banks.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Bank Registry Service
|
||||
* Manages SWIFT/BIC code information for financial institutions
|
||||
*/
|
||||
|
||||
import type { Bank, BankRegistry } from '@brazil-swift-ops/types';
|
||||
|
||||
// Bank data - in production, this would be loaded from a database or external service
|
||||
const BANKS_DATA: Bank[] = [
|
||||
{
|
||||
swiftCode: 'ESTRBRRJ',
|
||||
institutionName: 'strategy INVESTIMENTOS S/A CVC',
|
||||
city: 'Rio de Janeiro',
|
||||
country: 'BR',
|
||||
active: true,
|
||||
},
|
||||
// Add more banks as needed
|
||||
];
|
||||
|
||||
let bankRegistry: BankRegistry = {
|
||||
banks: BANKS_DATA,
|
||||
lastUpdated: new Date(),
|
||||
version: '1.0.0',
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all banks
|
||||
*/
|
||||
export function getAllBanks(): Bank[] {
|
||||
return bankRegistry.banks.filter((bank) => bank.active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bank by SWIFT code
|
||||
*/
|
||||
export function getBankBySwiftCode(swiftCode: string): Bank | null {
|
||||
const normalizedCode = swiftCode.toUpperCase().trim();
|
||||
return (
|
||||
bankRegistry.banks.find(
|
||||
(bank) => bank.swiftCode.toUpperCase() === normalizedCode && bank.active
|
||||
) || null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search banks by name or city
|
||||
*/
|
||||
export function searchBanks(query: string): Bank[] {
|
||||
const normalizedQuery = query.toLowerCase().trim();
|
||||
if (!normalizedQuery) {
|
||||
return getAllBanks();
|
||||
}
|
||||
|
||||
return bankRegistry.banks.filter(
|
||||
(bank) =>
|
||||
bank.active &&
|
||||
(bank.institutionName.toLowerCase().includes(normalizedQuery) ||
|
||||
bank.city.toLowerCase().includes(normalizedQuery) ||
|
||||
bank.swiftCode.toLowerCase().includes(normalizedQuery))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new bank to the registry
|
||||
*/
|
||||
export function addBank(bank: Omit<Bank, 'active'> & { active?: boolean }): Bank {
|
||||
// Check if bank already exists
|
||||
const existing = getBankBySwiftCode(bank.swiftCode);
|
||||
if (existing) {
|
||||
throw new Error(`Bank with SWIFT code ${bank.swiftCode} already exists`);
|
||||
}
|
||||
|
||||
const newBank: Bank = {
|
||||
...bank,
|
||||
active: bank.active !== undefined ? bank.active : true,
|
||||
};
|
||||
|
||||
bankRegistry.banks.push(newBank);
|
||||
bankRegistry.lastUpdated = new Date();
|
||||
|
||||
return newBank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing bank
|
||||
*/
|
||||
export function updateBank(swiftCode: string, updates: Partial<Bank>): Bank {
|
||||
const bank = getBankBySwiftCode(swiftCode);
|
||||
if (!bank) {
|
||||
throw new Error(`Bank with SWIFT code ${swiftCode} not found`);
|
||||
}
|
||||
|
||||
Object.assign(bank, updates);
|
||||
bankRegistry.lastUpdated = new Date();
|
||||
|
||||
return bank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate a bank (soft delete)
|
||||
*/
|
||||
export function deactivateBank(swiftCode: string): boolean {
|
||||
const bank = getBankBySwiftCode(swiftCode);
|
||||
if (!bank) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bank.active = false;
|
||||
bankRegistry.lastUpdated = new Date();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bank registry metadata
|
||||
*/
|
||||
export function getBankRegistry(): BankRegistry {
|
||||
return { ...bankRegistry };
|
||||
}
|
||||
|
||||
/**
|
||||
* Load banks from external source (e.g., JSON file, API)
|
||||
* In production, this would fetch from a database or external service
|
||||
*/
|
||||
export function loadBanksFromSource(banks: Bank[]): void {
|
||||
bankRegistry.banks = banks;
|
||||
bankRegistry.lastUpdated = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate SWIFT code format
|
||||
* SWIFT codes are 8 or 11 characters: 4 letters (bank), 2 letters (country), 2 characters (location), optional 3 characters (branch)
|
||||
*/
|
||||
export function validateSwiftCode(swiftCode: string): {
|
||||
valid: boolean;
|
||||
errors: string[];
|
||||
} {
|
||||
const errors: string[] = [];
|
||||
const normalized = swiftCode.toUpperCase().trim();
|
||||
|
||||
if (!normalized) {
|
||||
errors.push('SWIFT code is required');
|
||||
return { valid: false, errors };
|
||||
}
|
||||
|
||||
if (normalized.length !== 8 && normalized.length !== 11) {
|
||||
errors.push('SWIFT code must be 8 or 11 characters');
|
||||
}
|
||||
|
||||
if (!/^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$/.test(normalized)) {
|
||||
errors.push('SWIFT code format is invalid (expected: AAAA BB CC DDD)');
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors,
|
||||
};
|
||||
}
|
||||
@@ -15,3 +15,4 @@ export * from './version';
|
||||
export * from './logging';
|
||||
export * from './config';
|
||||
export * from './fx-rates';
|
||||
export * from './banks';
|
||||
|
||||
Reference in New Issue
Block a user