- 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)
159 lines
3.7 KiB
TypeScript
159 lines
3.7 KiB
TypeScript
/**
|
|
* 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,
|
|
};
|
|
}
|