Skip to content
Open
Show file tree
Hide file tree
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
215 changes: 215 additions & 0 deletions Services/BankStatementProcessor/bankStatementProcessor-HDFC.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/**
* Generated by Gemini AI - Attempt 1
* 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,
};

let headerRowIndex = -1;
let keyMapping = {};
const transactions = [];
let voucher_number = 1;

// Bank Details Extraction
for (let i = 0; i < Math.min(15, rawData.length); i++) {
const row = rawData[i];
for (const key in row) {
if (row.hasOwnProperty(key)) {
const value = row[key];
if (typeof value === 'string') {
const lowerValue = value.toLowerCase();
if (lowerValue.includes("bank name")) {
const adjacentKey = Object.keys(row).find(k => k !== key && row[k] != null);
if (adjacentKey) {
bank_details.bank_name = row[adjacentKey] || null;
}
} else if (lowerValue.includes("account holder name") || lowerValue.includes("account name")) {
const adjacentKey = Object.keys(row).find(k => k !== key && row[k] != null);
if (adjacentKey) {
bank_details.account_holder_name = row[adjacentKey] || null;
}
} else if (lowerValue.includes("account no") || lowerValue.includes("account number")) {
const adjacentKey = Object.keys(row).find(k => k !== key && row[k] != null);
if (adjacentKey) {
bank_details.account_no = row[adjacentKey] || null;
}
} else if (lowerValue.includes("ifsc") || lowerValue.includes("ifs")) {
const adjacentKey = Object.keys(row).find(k => k !== key && row[k] != null);
if (adjacentKey) {
bank_details.ifsc = row[adjacentKey] || null;
} else {
bank_details.ifsc = value
}
} else if (lowerValue.includes("branch name")) {
const adjacentKey = Object.keys(row).find(k => k !== key && row[k] != null);
if (adjacentKey) {
bank_details.branch_name = row[adjacentKey] || null;
}
} else if (lowerValue.includes("branch code")) {
const adjacentKey = Object.keys(row).find(k => k !== key && row[k] != null);
if (adjacentKey) {
bank_details.branch_code = row[adjacentKey] || null;
}
} else if (lowerValue.includes("address")) {
const adjacentKey = Object.keys(row).find(k => k !== key && row[k] != null);
if (adjacentKey && typeof row[adjacentKey] === 'string') {
bank_details.address = (bank_details.address || '') + (row[adjacentKey] || '');
} else if (typeof value === 'string') {
bank_details.address = (bank_details.address || '') + value;
}
}
}
}
}
}

// Header Row Identification & Key Mapping
for (let i = 0; i < rawData.length; i++) {
const row = rawData[i];
let hasDate = false;
let hasNarration = false;
let hasDebit = false;
let hasCredit = false;
let hasBalance = false;

for (const key in row) {
if (row.hasOwnProperty(key) && typeof row[key] === 'string') {
const lowerValue = row[key].toLowerCase();
if (lowerValue.includes("date")) {
keyMapping.dateKey = key;
hasDate = true;
} else if (lowerValue.includes("narration") || lowerValue.includes("description") || lowerValue.includes("details")) {
keyMapping.narrationKey = key;
hasNarration = true;
} else if (lowerValue.includes("debit") || lowerValue.includes("withdrawal")) {
keyMapping.debitKey = key;
hasDebit = true;
} else if (lowerValue.includes("credit") || lowerValue.includes("deposit")) {
keyMapping.creditKey = key;
hasCredit = true;
} else if (lowerValue.includes("balance") || lowerValue.includes("closing balance")) {
keyMapping.balanceKey = key;
hasBalance = true;
}
}
}

if (hasDate && hasNarration && (hasDebit || hasCredit) && hasBalance) {
headerRowIndex = i;
break;
}
}

// Transaction Processing
if (headerRowIndex !== -1) {
for (let i = headerRowIndex + 1; i < rawData.length; i++) {
const row = rawData[i];
if (!row || Object.keys(row).length === 0) {
continue; // Skip empty rows
}
let date = row[keyMapping.dateKey] || null;
let desc = row[keyMapping.narrationKey] ? (typeof row[keyMapping.narrationKey] === 'string' ? row[keyMapping.narrationKey].trim() : String(row[keyMapping.narrationKey])) : null;
let debit = row[keyMapping.debitKey] || null;
let credit = row[keyMapping.creditKey] || null;
let balance = row[keyMapping.balanceKey] || null;

if (!date && !desc && !debit && !credit && !balance) continue;

let amount = null;
let type = null;

if (debit && !isNaN(Number(debit))) {
amount = Math.abs(Number(debit.toString().replace(/[^0-9.-]+/g,"")));
type = "withdrawal";
} else if (credit && !isNaN(Number(credit))) {
amount = Math.abs(Number(credit.toString().replace(/[^0-9.-]+/g,"")));
type = "deposit";
}
if (!amount && amount !== 0) continue;

if (balance && !isNaN(Number(balance))) {
balance = Number(balance.toString().replace(/[^0-9.-]+/g,""));
} else {
continue
}

if (date) {
try {
if (typeof date === 'number') {
// Excel date serial number
const excelEpoch = new Date(Date.UTC(1899, 11, 31));
const javascriptDate = new Date(excelEpoch.getTime() + (date * 24 * 60 * 60 * 1000));
const year = javascriptDate.getFullYear();
const month = String(javascriptDate.getMonth() + 1).padStart(2, '0');
const day = String(javascriptDate.getDate()).padStart(2, '0');
date = `${year}-${month}-${day}`;
} else {
const dateFormats = [
/^\d{2}[./-]\d{2}[./-]\d{4}$/,
/^\d{4}[./-]\d{2}[./-]\d{2}$/,
/^\d{2}-\w{3}-\d{4}$/i
];

if (dateFormats[0].test(date)) {
const parts = date.split(/[-/.]/);
const day = parts[0];
const month = parts[1];
const year = parts[2];
date = `${year}-${month}-${day}`;
} else if (dateFormats[1].test(date)) {
const parts = date.split(/[-/.]/);
const year = parts[0];
const month = parts[1];
const day = parts[2];
date = `${year}-${month}-${day}`;
}
else if (dateFormats[2].test(date)) {
const parts = date.split('-');
const day = parts[0];
const month = parts[1];
const year = parts[2];
const monthNumber = new Date(Date.parse(month +" 1, 2000")).getMonth() + 1
date = `${year}-${String(monthNumber).padStart(2,'0')}-${day}`;
} else {
date = null
}
}
}
catch (e) {
date = null
}
}
if (!date) continue;
const transaction = {
date: date,
voucher_number: voucher_number++,
amount: amount,
desc: desc,
from: null,
to: null,
type: type,
balance: balance
};
transactions.push(transaction);
}
}

return {
bank_details: bank_details,
transactions: transactions
};
}

module.exports = processBankStatement;
1 change: 1 addition & 0 deletions src/BANK.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const BANK = {
BOB: "bob",
IOB: "iob",
HDFC: "hdfc",
}

module.exports = BANK;