Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions Services/BankStatementProcessor/bankStatementProcessor-HDFC.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/**
* Generated by Gemini AI - Attempt 5
* Processes bank statement data from Excel
* @param {Array} rawData - Array of objects from bank statement Excel
* @returns {Object} Processed bank statement data
*/
function processBankStatement(rawData) {
const bank_details = {
bank_name: null,
opening_balance: 0,
ifsc: null,
address: null,
city: null,
account_no: null,
account_holder_name: null,
branch_name: null,
branch_code: null
};
const transactions = [];
let headerRowIndex = -1;
let headerKeys = {};

// Helper function to extract a date in YYYY-MM-DD format
function formatDate(dateString) {
if (!dateString) return null;

const dateFormats = [
{ regex: /(\d{2})\/(\d{2})\/(\d{2})/, format: (m) => `20${m[3]}-${m[2]}-${m[1]}` }, // DD/MM/YY
{ regex: /(\d{2})\/(\d{2})\/(\d{4})/, format: (m) => `${m[3]}-${m[2]}-${m[1]}` }, // DD/MM/YYYY
{ regex: /(\d{4})-(\d{2})-(\d{2})/, format: (m) => `${m[1]}-${m[2]}-${m[3]}` }, // YYYY-MM-DD
{ regex: /(\d{2})-(\w{3})-(\d{2})/, format: (m) => { // DD-MON-YY
const monthMap = {
'JAN': '01', 'FEB': '02', 'MAR': '03', 'APR': '04', 'MAY': '05', 'JUN': '06',
'JUL': '07', 'AUG': '08', 'SEP': '09', 'OCT': '10', 'NOV': '11', 'DEC': '12'
};
return `20${m[3]}-${monthMap[m[2].toUpperCase()] || '01'}-${m[1]}`;
}},
{ regex: /(\d{2})-(\w{3})-(\d{4})/, format: (m) => { // DD-MON-YYYY
const monthMap = {
'JAN': '01', 'FEB': '02', 'MAR': '03', 'APR': '04', 'MAY': '05', 'JUN': '06',
'JUL': '07', 'AUG': '08', 'SEP': '09', 'OCT': '10', 'NOV': '11', 'DEC': '12'
};
return `${m[3]}-${monthMap[m[2].toUpperCase()] || '01'}-${m[1]}`;
}}
];

for (const format of dateFormats) {
const match = dateString.match(format.regex);
if (match) {
return format.format(match);
}
}
console.warn(`Could not parse date: ${dateString}`);
return null;
}

// 1. Extract Bank Details
for (let i = 0; i < Math.min(15, rawData.length); i++) {
const row = rawData[i];
for (const key in row) {
const value = row[key];
if (typeof value === 'string') {
if (!bank_details.bank_name && value.includes("BANK")) {
bank_details.bank_name = value.split("BANK")[0].trim();
}
if (!bank_details.account_holder_name && value.includes("MR") ) {
bank_details.account_holder_name = value.trim();
}

if (!bank_details.address && (value.toLowerCase().includes("address") || value.toLowerCase().includes("add:")) ) {
bank_details.address = (bank_details.address || "") + " " +value.replace(/address:|address/gi, '').trim();
}

if (!bank_details.branch_name && value.toLowerCase().includes("account branch")) {
bank_details.branch_name = value.replace(/Account Branch :/gi, '').trim();
}
if (!bank_details.city && value.toLowerCase().includes("city")) {
bank_details.city = value.replace(/City :/gi, '').trim();
}

if (!bank_details.ifsc) {
const ifscRegex = /(IFSC|IFSC Code|IFSC Code:|RTGS\/NEFT IFSC|RTGS\/NEFT IFSC :)\s*:?\s*([A-Za-z]{4}[0-9]{6,7})/i;
const ifscMatch = value.match(ifscRegex);
if (ifscMatch && ifscMatch[2]) {
bank_details.ifsc = ifscMatch[2].trim();
}
}
if (!bank_details.account_no && value.toLowerCase().includes("account no")) {
bank_details.account_no = value.replace(/Account No :|Account No:/gi, '').trim().split(' ')[0];
}
if (!bank_details.opening_balance && value.toLowerCase().includes("opening balance")) {

try {
bank_details.opening_balance = parseFloat(value.replace(/Opening Balance:|Opening Balance/gi, '').trim())
} catch(e) {
bank_details.opening_balance = 0;
}

}
}
}
if(bank_details.ifsc){
break;
}
}

if (!bank_details.ifsc) {
for (let i = 0; i < Math.min(15, rawData.length); i++) {
const row = rawData[i];
for (const key in row) {
const value = row[key];
if (typeof value === 'string') {
if (!bank_details.ifsc) {
const ifscRegex = /(IFSC|IFSC Code|IFSC Code:|RTGS\/NEFT IFSC|RTGS\/NEFT IFSC :)\s*:?\s*([A-Za-z]{4}[0-9]{6,7})/i;
const ifscMatch = value.match(ifscRegex);
if (ifscMatch && ifscMatch[2]) {
bank_details.ifsc = ifscMatch[2].trim();
break;
}
}

}
}
if(bank_details.ifsc){
break;
}

}
}
// 2. & 3. Find Header Row and Process Transactions
for (let i = 0; i < rawData.length; i++) {
const row = rawData[i];
let isHeaderRow = false;

for (const key in row) {
const value = row[key];
if (typeof value === 'string' &&
(value.toLowerCase().includes("date") ||
value.toLowerCase().includes("narration") ||
value.toLowerCase().includes("details") ||
value.toLowerCase().includes("withdrawal") ||
value.toLowerCase().includes("debit") ||
value.toLowerCase().includes("deposit") ||
value.toLowerCase().includes("credit") ||
value.toLowerCase().includes("balance"))) {
isHeaderRow = true;
break;
}
}

if (isHeaderRow) {
headerRowIndex = i;
for (const key in row) {
const value = row[key];
if (typeof value === 'string') {
if (value.toLowerCase().includes("date")) {
headerKeys.date = key;
} else if (value.toLowerCase().includes("narration") || value.toLowerCase().includes("details")) {
headerKeys.narration = key;
} else if (value.toLowerCase().includes("withdrawal") || value.toLowerCase().includes("debit")) {
headerKeys.withdrawal = key;
} else if (value.toLowerCase().includes("deposit") || value.toLowerCase().includes("credit")) {
headerKeys.deposit = key;
} else if (value.toLowerCase().includes("balance")) {
headerKeys.balance = key;
}

}
}
break;
}
}

if (headerRowIndex !== -1) {
let voucher_number = 1;
for (let i = headerRowIndex + 1; i < rawData.length; i++) {
const row = rawData[i];
if (!row) continue;

const dateString = row[headerKeys.date];
const date = formatDate(dateString);
const desc = row[headerKeys.narration] || '';
let amount = null;
let type = null;

if (headerKeys.withdrawal && row[headerKeys.withdrawal] !== null && row[headerKeys.withdrawal] !== undefined) {
try {
amount = parseFloat(row[headerKeys.withdrawal]);
} catch (e) {
amount = null;
}
if (!isNaN(amount)) {
type = "withdrawal";
} else {
amount = null;
}

}
if (headerKeys.deposit && row[headerKeys.deposit] !== null && row[headerKeys.deposit] !== undefined) {
try {
amount = parseFloat(row[headerKeys.deposit]);
} catch (e) {
amount = null;
}
if (!isNaN(amount)) {
type = "deposit";
} else {
amount = null;
}
}
let balance = null;
if(headerKeys.balance){
try {
balance = parseFloat(row[headerKeys.balance]);
} catch (e) {
balance = null;
}
}

if(date && (type === "withdrawal" || type === "deposit") && amount !== null){
transactions.push({
date,
voucher_number: voucher_number++,
amount,
desc,
from: null,
to: null,
type,
balance
});
}
}
}
if (!bank_details.ifsc) {
bank_details.ifsc = null
}

return { bank_details, transactions };
}

module.exports = processBankStatement;