statement-parser-fab
Version:
Parse bank and credit card statements. Updated fork with FAB (First Abu Dhabi Bank) support and maintained dependencies.
140 lines (139 loc) • 5.82 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.usaaBankAccountStatementParser = void 0;
const augment_vir_1 = require("augment-vir");
const date_1 = require("../../augments/date");
const statement_parser_1 = require("../statement-parser");
var State;
(function (State) {
State["PageHeader"] = "header";
State["StatementPeriod"] = "statement-period";
State["DepositHeaders"] = "deposit-headers";
State["Deposit"] = "deposit";
State["DebitHeaders"] = "debit-headers";
State["Debit"] = "debit";
State["Filler"] = "filler";
State["End"] = "end";
})(State || (State = {}));
var ParsingTriggers;
(function (ParsingTriggers) {
ParsingTriggers["OtherDebits"] = "other debits";
})(ParsingTriggers || (ParsingTriggers = {}));
const otherDebitsRegExp = /^\s+other debits$/;
const accountSummaryRegExp = /^\s+account balance summary$/i;
const accountNumberHeaderRegExp = /account number\s+account type\s+statement period/i;
const depositsRegExp = /^\s+deposits and other credits$/i;
const fromRegExp = /^\s{2,}FROM\s+/;
exports.usaaBankAccountStatementParser = (0, statement_parser_1.createStatementParser)({
action: performStateAction,
next: nextState,
initialState: State.PageHeader,
endState: State.End,
parserKeywords: [
...(0, augment_vir_1.getEnumTypedValues)(ParsingTriggers),
otherDebitsRegExp,
accountSummaryRegExp,
accountNumberHeaderRegExp,
depositsRegExp,
fromRegExp,
],
});
const validTransactionLineRegex = /(?:^\d{1,2}\/\d{1,2}\s+|^\s{2,})/;
function performStateAction(currentState, line, output, parserOptions) {
if (currentState === State.StatementPeriod && line !== '') {
const [, accountSuffix, startDateString, endDateString,] = (0, augment_vir_1.safeMatch)(line, /([\d-]+)\s+.+?(\d{1,2}\/\d{1,2}\/\d{1,2}).+?(\d{1,2}\/\d{1,2}\/\d{1,2})/);
if (accountSuffix && startDateString && endDateString) {
output.accountSuffix = accountSuffix.replace(/-/g, '').slice(-4);
if (!output.accountSuffix.match(/\d+/)) {
throw new Error(`Invalid account suffix: "${output.accountSuffix}"`);
}
output.startDate = (0, augment_vir_1.createDateFromSlashFormat)(startDateString, parserOptions.yearPrefix);
output.endDate = (0, augment_vir_1.createDateFromSlashFormat)(endDateString, parserOptions.yearPrefix);
}
else {
throw new Error(`Start and end date were not found in line for "${State.StatementPeriod}" state: "${line}"`);
}
}
else if ((currentState === State.Debit || currentState === State.Deposit) &&
line.match(validTransactionLineRegex)) {
const array = currentState === State.Debit ? output.expenses : output.incomes;
const [, dateString, amountString, descriptionString,] = (0, augment_vir_1.safeMatch)(line, /^(\d{1,2}\/\d{1,2})\s+((?:\d+|,|\.)+)\s+(.*)$/);
const currentDebit = array[array.length - 1];
if (dateString && amountString && descriptionString) {
if (!output.startDate || !output.endDate) {
throw new Error(`Missing start/end date: ${JSON.stringify({
start: output.startDate,
end: output.endDate,
})}`);
}
// start line of debit
const parts = dateString.split('/');
const date = (0, date_1.dateWithinRange)(output.startDate, output.endDate, Number(parts[0]), Number(parts[1]));
array.push({
date: date,
amount: Number((0, augment_vir_1.removeCommasFromNumberString)(amountString)),
description: (0, augment_vir_1.collapseSpaces)(descriptionString).trim(),
from: undefined,
originalText: [line],
});
}
else if (currentDebit) {
/*
* Assume that the current line is the last line for the current debit.
* "from" is always the last line, so shift the current "from" to "method" since it wasn't the last line.
*/
currentDebit.description += '\n' + (0, augment_vir_1.collapseSpaces)(line).trim();
if (line.match(fromRegExp)) {
currentDebit.from = (0, augment_vir_1.collapseSpaces)(line.replace(fromRegExp, '')).trim();
}
currentDebit.originalText.push(line);
}
}
return output;
}
function nextState(currentState, line) {
line = line.toLowerCase();
if (line.match(accountSummaryRegExp)) {
return State.End;
}
switch (currentState) {
case State.PageHeader:
if (line.match(accountNumberHeaderRegExp)) {
return State.StatementPeriod;
}
else if (line.match(depositsRegExp)) {
return State.DepositHeaders;
}
break;
case State.StatementPeriod:
if (line !== '') {
return State.PageHeader;
}
break;
case State.DepositHeaders:
return State.Deposit;
case State.Deposit:
if (line === '') {
return State.Filler;
}
else if (line.match(otherDebitsRegExp)) {
return State.DebitHeaders;
}
break;
case State.DebitHeaders:
return State.Debit;
case State.Debit:
if (line === '') {
return State.Filler;
}
break;
case State.Filler:
if (line.match(otherDebitsRegExp)) {
return State.DebitHeaders;
}
break;
case State.End:
break;
}
return currentState;
}