diff --git a/Services/BankStatementProcessor/bankStatementProcessor-HDFC.js b/Services/BankStatementProcessor/bankStatementProcessor-HDFC.js new file mode 100644 index 0000000..ee509c3 --- /dev/null +++ b/Services/BankStatementProcessor/bankStatementProcessor-HDFC.js @@ -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; \ No newline at end of file