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;
229 changes: 229 additions & 0 deletions Services/BankStatementProcessor/bankStatementProcessor-HDFC.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/**
* Generated by Gemini AI - Attempt 2
* 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 date as YYYY-MM-DD
function formatDate(dateString) {
if (!dateString) return null;
try {
const dateParts = dateString.split(/[-/]/);
if (dateParts.length === 3) {
const day = parseInt(dateParts[0]);
const month = parseInt(dateParts[1]);
const year = parseInt(dateParts[2]);

if (isNaN(day) || isNaN(month) || isNaN(year)) {
return null;
}

const formattedYear = (year < 100) ? (year < 50 ? `20${year}` : `19${year}`) : year;

return `${formattedYear}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
}
return null;
} catch (error) {
return null;
}
}

// Helper function to safely convert to number
function safeNumber(value) {
if (value === null || value === undefined) {
return null;
}
const num = Number(value);
return isNaN(num) ? null : num;
}

// 1. Extract bank details
for (let i = 0; i < Math.min(15, rawData.length); i++) {
const row = rawData[i];
for (const key in row) {
if (row[key]) {
const value = row[key].toString().trim();

if (!bank_details.bank_name && value.toUpperCase().includes("BANK")) {
bank_details.bank_name = value.split(" ")[0];
}

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")) {
bank_details.address = value.replace(/Address\s*:\s*/i, '');
}

if (!bank_details.city && value.toUpperCase().includes("CITY")) {
bank_details.city = value.replace(/City\s*:\s*/i, '');
}

if (!bank_details.branch_name && value.toUpperCase().includes("Account Branch")) {
bank_details.branch_name = value.replace(/Account Branch\s*:\s*/i, '');
}

if (!bank_details.account_no && value.toUpperCase().includes("ACCOUNT NO")) {
bank_details.account_no = value.replace(/Account No\s*: */i, '').split(" ")[0];
}

const ifscKeywords = ['IFSC', 'ifsc', 'Ifsc', 'RTGS/NEFT IFSC','NEFT/RTGS IFSC', 'Neft/Rtgs IFSC', 'rtgs/neft ifsc'];
if (ifscKeywords.some(keyword => value.includes(keyword))) {
const ifscValue = value.replace(new RegExp(ifscKeywords.join('|'), 'gi'), '').replace(/:/g, '').trim().split(" ")[0];
bank_details.ifsc = ifscValue;
if (!bank_details.address && value.toUpperCase().includes("ADDRESS")) {
bank_details.address = value.replace(/Address\s*:\s*/i, '');
}
}
}
}
}

// 2. Identify header row and keys
for (let i = 0; i < rawData.length; i++) {
const row = rawData[i];
let hasDate = false;
let hasNarration = false;
let hasWithdrawal = false;
let hasDeposit = false;
let hasBalance = false;

for (const key in row) {
if (row[key]) {
const value = row[key].toString().trim().toUpperCase();
if (value === "DATE") hasDate = true;
else if (value === "NARRATION" || value === "NARRATION OR DETAILS" || value === "DETAILS") hasNarration = true;
else if (value === "WITHDRAWAL AMT." || value === "WITHDRAWAL AMT. OR DEBIT" || value === "WITHDRAWAL" || value === "DEBIT") hasWithdrawal = true;
else if (value === "DEPOSIT AMT." || value === "DEPOSIT AMT. OR CREDIT" || value === "DEPOSIT" || value === "CREDIT") hasDeposit = true;
else if (value === "CLOSING BALANCE" || value === "CLOSING BALANCE OR BALANCE" || value === "BALANCE") hasBalance = true;
}
}

if (hasDate && hasNarration && (hasWithdrawal || hasDeposit) && hasBalance) {
headerRowIndex = i;
headerKeys = row;
break;
}
}

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

const dateKey = Object.keys(headerKeys).find(key => headerKeys[key] && headerKeys[key].toString().trim().toUpperCase() === "DATE");
const narrationKey = Object.keys(headerKeys).find(key => headerKeys[key] && (headerKeys[key].toString().trim().toUpperCase() === "NARRATION" || headerKeys[key].toString().trim().toUpperCase() === "NARRATION OR DETAILS" || headerKeys[key].toString().trim().toUpperCase() === "DETAILS"));
const withdrawalKey = Object.keys(headerKeys).find(key => headerKeys[key] && (headerKeys[key].toString().trim().toUpperCase() === "WITHDRAWAL AMT." || headerKeys[key].toString().trim().toUpperCase() === "WITHDRAWAL AMT. OR DEBIT" || headerKeys[key].toString().trim().toUpperCase() === "WITHDRAWAL" || headerKeys[key].toString().trim().toUpperCase() === "DEBIT"));
const depositKey = Object.keys(headerKeys).find(key => headerKeys[key] && (headerKeys[key].toString().trim().toUpperCase() === "DEPOSIT AMT." || headerKeys[key].toString().trim().toUpperCase() === "DEPOSIT AMT. OR CREDIT" || headerKeys[key].toString().trim().toUpperCase() === "DEPOSIT" || headerKeys[key].toString().trim().toUpperCase() === "CREDIT"));
const balanceKey = Object.keys(headerKeys).find(key => headerKeys[key] && (headerKeys[key].toString().trim().toUpperCase() === "CLOSING BALANCE" || headerKeys[key].toString().trim().toUpperCase() === "CLOSING BALANCE OR BALANCE" || headerKeys[key].toString().trim().toUpperCase() === "BALANCE"));

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

const dateValue = row[dateKey];
const narration = row[narrationKey] ? row[narrationKey].toString().trim() : null;
const withdrawalAmount = safeNumber(row[withdrawalKey]);
const depositAmount = safeNumber(row[depositKey]);
const balance = safeNumber(row[balanceKey]);

if (!dateValue && !narration && !withdrawalAmount && !depositAmount && !balance) continue; //Skip empty row

const date = formatDate(dateValue);

let type = null;
let amount = null;

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

if (amount === null) {
continue; // Skip if amount is null
}

const transaction = {
date,
voucher_number: voucher_number++,
amount: amount !== null ? Number(amount) : null,
desc: narration,
from: null,
to: null,
type,
balance
};

transactions.push(transaction);
}

// Try to extract opening balance from statement
let openingBalanceFound = false;
for (let i = 0; i < rawData.length; i++) {
const row = rawData[i];
for (const key in row) {
if(row[key]){
const value = row[key].toString().trim().toUpperCase();

if (value.includes('OPENING BALANCE')){

let value_open=rawData[i][Object.keys(rawData[i])[0]]

if (typeof value_open === 'number'){
bank_details.opening_balance = value_open;
openingBalanceFound = true;
break;
}

}
}
}
if (openingBalanceFound) break;
}

let addressString = '';
for (let i = 0; i < Math.min(10, rawData.length); i++) {
const row = rawData[i];
for (const key in row) {
if (row[key] && typeof row[key] === 'string') {
const value = row[key].trim();
if (!bank_details.address && value.toUpperCase().includes("ADDRESS")) {
addressString += value.replace(/Address\s*:\s*/i, '');
}

if (!bank_details.city && value.toUpperCase().includes("CITY")) {
addressString += value.replace(/City\s*:\s*/i, '');
}
}
}
}
if (!bank_details.address && addressString != ''){
bank_details.address = addressString
}

return { bank_details, transactions };
}

module.exports = processBankStatement;