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
205 changes: 205 additions & 0 deletions Services/BankStatementProcessor/bankStatementProcessor-HDFC.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/**
* 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 format dates
const formatDate = (dateString) => {
try {
if (!dateString) return null;

const dateFormats = [
{ format: 'DD/MM/YY', regex: /(\d{2})\/(\d{2})\/(\d{2})/ },
{ format: 'DD-MM-YY', regex: /(\d{2})-(\d{2})-(\d{2})/ },
{ format: 'DD/MM/YYYY', regex: /(\d{2})\/(\d{2})\/(\d{4})/ },
{ format: 'DD-MM-YYYY', regex: /(\d{2})-(\d{2})-(\d{4})/ },
{ format: 'MM/DD/YY', regex: /(\d{2})\/(\d{2})\/(\d{2})/ },
{ format: 'MM-DD-YY', regex: /(\d{2})-(\d{2})-(\d{2})/ },
{ format: 'MM/DD/YYYY', regex: /(\d{2})\/(\d{2})\/(\d{4})/ },
{ format: 'MM-DD-YYYY', regex: /(\d{2})-(\d{2})-(\d{4})/ },
{ format: 'YYYY-MM-DD', regex: /(\d{4})-(\d{2})-(\d{2})/ },
{ format: 'YYYY/MM/DD', regex: /(\d{4})\/(\d{2})\/(\d{2})/ }

];

for (const dateFormat of dateFormats) {
const match = dateString.match(dateFormat.regex);
if (match) {
let day, month, year;
if (dateFormat.format.startsWith('DD')) {
day = parseInt(match[1], 10);
month = parseInt(match[2], 10);
year = parseInt(match[3], 10);
} else if (dateFormat.format.startsWith('MM')) {
month = parseInt(match[1], 10);
day = parseInt(match[2], 10);
year = parseInt(match[3], 10);
} else if(dateFormat.format.startsWith('YYYY')){
year = parseInt(match[1],10);
month = parseInt(match[2],10);
day = parseInt(match[3],10);
}

if (year < 100) {
year += 2000; // Adjust two-digit years
}
return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
}
}

return null; // Could not parse date
} catch (error) {
return null; // Handle parsing errors
}
};

//Find the 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("VINOD")) {
bank_details.account_holder_name = value;
}
if (!bank_details.account_no && value.toUpperCase().includes("ACCOUNT NO")) {
bank_details.account_no = value.split(':')[1].trim().split(' ')[0];
}
if (!bank_details.address && value.toUpperCase().includes("ADDRESS")) {
bank_details.address = value.split(':')[1].trim();
}
if (!bank_details.branch_code && value.toUpperCase().includes("BRANCH CODE")) {
bank_details.branch_code = value.split(':')[1].trim();
}
if (!bank_details.branch_name && value.toUpperCase().includes("ACCOUNT BRANCH")) {
bank_details.branch_name = value.split(':')[1].trim();
}
if (!bank_details.ifsc) {
if (value.toUpperCase().includes("IFSC") || value.toUpperCase().includes("IFS CODE") || value.toUpperCase().includes("RTGS/NEFT IFSC")) {
const ifscValue = value.split(':')[1]?.trim() || value.split('IFSC CODE')?.trim() || value.split("RTGS/NEFT IFSC")?.[1].trim();
if (ifscValue && ifscValue.length === 11) {
bank_details.ifsc = ifscValue.split(' ')[0].trim();
}
}
}

}
}
}

// 1. b. Identify the transaction header row
const headerKeywords = ["Date", "Narration", "Details", "Withdrawal", "Deposit", "Closing Balance", "Balance"];
for (let i = 0; i < rawData.length; i++) {
const row = rawData[i];
let foundHeader = true;
let matchedCount = 0;

for (const key in row) {
if (row[key]) {
const value = row[key].toString().trim();
if (headerKeywords.some(keyword => value.toLowerCase().includes(keyword.toLowerCase()))) {
matchedCount++;
}
}
}

if (matchedCount >= 3) { //Adjust threshold as needed
headerRowIndex = i;
break;
}
}

// 1. c. Identify the actual keys corresponding to header values.
if (headerRowIndex !== -1) {
const headerRow = rawData[headerRowIndex];
for (const key in headerRow) {
if (headerRow[key]) {
const value = headerRow[key].toString().trim();
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("closing balance") || value.toLowerCase().includes("balance")) {
headerKeys.balance = key;
}
}
}
}

// 1. d. Process the rows *after* the header row as transactions.
if (headerRowIndex !== -1) {
let voucher_number = 1;
for (let i = headerRowIndex + 1; i < rawData.length; i++) {
const row = rawData[i];
if (!row || typeof row !== 'object') continue;

const date = formatDate(row[headerKeys.date]);
const desc = row[headerKeys.narration] ? row[headerKeys.narration].toString().trim() : null;
let amount = 0;
let type = null;

if (headerKeys.withdrawal && row[headerKeys.withdrawal] !== null && row[headerKeys.withdrawal] !== undefined) {
amount = Number(row[headerKeys.withdrawal]);
if (!isNaN(amount)) {
type = "withdrawal";
}
}

if (headerKeys.deposit && row[headerKeys.deposit] !== null && row[headerKeys.deposit] !== undefined) {
const depositAmount = Number(row[headerKeys.deposit]);
if (!isNaN(depositAmount)) {
amount = depositAmount;
type = "deposit";
}
}

const balance = row[headerKeys.balance] !== null && row[headerKeys.balance] !== undefined ? Number(row[headerKeys.balance]) : null;

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

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;