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
7 changes: 7 additions & 0 deletions BANK.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const BANK = {
BOB: "bob",
IOB: "iob",
HDFC: "hdfc",
}

module.exports = BANK;
198 changes: 198 additions & 0 deletions Services/BankStatementProcessor/bankStatementProcessor-HDFC.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/**
* Generated by Gemini AI - Attempt 3
* 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 = {};
let voucher_number = 1;

// Helper function to format dates to 'YYYY-MM-DD'
function formatDate(dateString) {
try {
if (!dateString) return null;

let date;

// Attempt to parse various date formats
if (typeof dateString === 'number') {
date = new Date((dateString - 25569) * 86400 * 1000); // Excel serial date
}
else if (typeof dateString === 'string') {
date = new Date(dateString); // Attempt standard parsing
if(isNaN(date)){ // if standard parsing fails.
const dateParts = dateString.split(/[-/]/);
if (dateParts.length === 3) {
const day = parseInt(dateParts[0], 10);
const month = parseInt(dateParts[1], 10) - 1;
const year = parseInt(dateParts[2], 10);
date = new Date(year, month, day);
}
}
} else {
return null;
}


if (isNaN(date)) return null;

const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
} catch (e) {
console.error("Error formatting date:", dateString, e);
return null;
}
}

// Helper function to convert to number, handling null/undefined/string values.
function convertToNumber(value) {
if (value === null || value === undefined) {
return 0;
}
const num = Number(value);
return isNaN(num) ? 0 : num;
}


// 1. Extract Bank Details
for (let i = 0; i < Math.min(15, rawData.length); i++) { // Check first 15 rows
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() + " BANK";
}
if (!bank_details.account_holder_name && value.toUpperCase().includes('MR') || value.toUpperCase().includes('MS')) {
bank_details.account_holder_name = value;
}

if (!bank_details.address && (value.toUpperCase().includes('ADDRESS') || value.toUpperCase().includes('ADDR')))
{
bank_details.address=value;
}
if(!bank_details.city && value.toUpperCase().includes('CITY'))
{
bank_details.city=value;
}
if (!bank_details.branch_name && value.toUpperCase().includes('ACCOUNT BRANCH')) {
bank_details.branch_name = value;
}
}
if (!bank_details.ifsc) {
const ifscKeywords = ['IFSC', 'ifsc', 'IFSC Code', 'ifsc Code','RTGS/NEFT IFSC'];
if(key.toLowerCase().includes('ifsc') || ifscKeywords.some(keyword => row[key] === keyword || (typeof row[key] === 'string' && row[key]?.toUpperCase().includes(keyword.toUpperCase())))) {
if(typeof value === 'string' && value.length > 0){
bank_details.ifsc = value;
}else if (typeof value === 'string' && value.length==0 && typeof row[key] === 'string'){
bank_details.ifsc = row[key];
} else if(typeof row[key] === 'string'){
bank_details.ifsc = row[key];
}
}

}
if(!bank_details.account_no && (key.toLowerCase().includes('account no') || key.toLowerCase().includes('account number')))
{
if(typeof value === 'string' || typeof value === 'number'){
bank_details.account_no = value
}
}
}
}

// 2. Identify Header Row and Keys
for (let i = 0; i < rawData.length; i++) {
const row = rawData[i];
const headerValues = Object.values(row).filter(value => typeof value === 'string');
if (
headerValues.includes("Date") &&
(headerValues.includes("Narration") || headerValues.includes("Narration or Details") || headerValues.includes("Narration/Description")) &&
(headerValues.includes("Withdrawal Amt.") || headerValues.includes("Withdrawal Amt. or Debit") ||headerValues.includes("Withdrawal")) &&
(headerValues.includes("Deposit Amt.") || headerValues.includes("Deposit Amt. or Credit")|| headerValues.includes("Deposit")) &&
(headerValues.includes("Closing Balance") || headerValues.includes("Closing Balance or Balance") || headerValues.includes("Balance"))
) {
headerRowIndex = i;
headerKeys = row;
break;
}
}

if (headerRowIndex === -1) {
console.warn("Could not find header row.");
return { bank_details, transactions }; // Return empty results if header not found
}

const dateKey = Object.keys(headerKeys).find(key => headerKeys[key] === "Date");
const narrationKey = Object.keys(headerKeys).find(key => headerKeys[key] === "Narration" || headerKeys[key] === "Narration or Details" || headerKeys[key] === "Narration/Description");
const withdrawalKey = Object.keys(headerKeys).find(key => headerKeys[key] === "Withdrawal Amt." || headerKeys[key] === "Withdrawal Amt. or Debit" || headerKeys[key] === "Withdrawal");
const depositKey = Object.keys(headerKeys).find(key => headerKeys[key] === "Deposit Amt." || headerKeys[key] === "Deposit Amt. or Credit" || headerKeys[key] === "Deposit");
const balanceKey = Object.keys(headerKeys).find(key => headerKeys[key] === "Closing Balance" || headerKeys[key] === "Closing Balance or Balance" || headerKeys[key] === "Balance");

// 3. Process Transactions
for (let i = headerRowIndex + 1; i < rawData.length; i++) {
const row = rawData[i];
if (!row) continue; // Skip empty rows

const dateValue = row[dateKey];
const desc = row[narrationKey] || "";
let amount = 0;
let type = null;

const withdrawalAmount = convertToNumber(row[withdrawalKey]);
const depositAmount = convertToNumber(row[depositKey]);

if (withdrawalAmount) {
type = "withdrawal";
amount = withdrawalAmount;
} else if (depositAmount) {
type = "deposit";
amount = depositAmount;
}

const balance = convertToNumber(row[balanceKey]);

const transaction = {
date: formatDate(dateValue),
voucher_number: voucher_number++,
amount: amount,
desc: desc,
from: null,
to: null,
type: type,
balance: balance
};

transactions.push(transaction);
}

const openingBalanceRow = rawData.find(row => Object.values(row).some(val => typeof val === 'string' && val.toLowerCase().includes("opening balance")));

if(openingBalanceRow){
for (const key in openingBalanceRow){
if(typeof openingBalanceRow[key] === 'number'){
bank_details.opening_balance = openingBalanceRow[key];
break;
}
}
}
return { bank_details, transactions };
}

module.exports = processBankStatement;