UNPKG

israeli-bank-scrapers

Version:

Provide scrapers for all major Israeli banks and credit card companies

83 lines (80 loc) 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.filterOldTransactions = filterOldTransactions; exports.fixInstallments = fixInstallments; exports.getRawTransaction = getRawTransaction; exports.sortTransactionsByDate = sortTransactionsByDate; var _moment = _interopRequireDefault(require("moment")); var _transactions = require("../transactions"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function isNormalTransaction(txn) { return txn && txn.type === _transactions.TransactionTypes.Normal; } function isInstallmentTransaction(txn) { return txn && txn.type === _transactions.TransactionTypes.Installments; } function isNonInitialInstallmentTransaction(txn) { return isInstallmentTransaction(txn) && !!txn.installments && txn.installments.number > 1; } function isInitialInstallmentTransaction(txn) { return isInstallmentTransaction(txn) && !!txn.installments && txn.installments.number === 1; } function fixInstallments(txns) { return txns.map(txn => { const clonedTxn = { ...txn }; if (isInstallmentTransaction(clonedTxn) && isNonInitialInstallmentTransaction(clonedTxn) && clonedTxn.installments) { const dateMoment = (0, _moment.default)(clonedTxn.date); const actualDateMoment = dateMoment.add(clonedTxn.installments.number - 1, 'month'); clonedTxn.date = actualDateMoment.toISOString(); } return clonedTxn; }); } function sortTransactionsByDate(txns) { return [...txns].sort((a, b) => a.date < b.date ? -1 : a.date > b.date ? 1 : 0); } function filterOldTransactions(txns, startMoment, combineInstallments) { return txns.filter(txn => { const combineNeededAndInitialOrNormal = combineInstallments && (isNormalTransaction(txn) || isInitialInstallmentTransaction(txn)); return !combineInstallments && startMoment.isSameOrBefore(txn.date) || combineNeededAndInitialOrNormal && startMoment.isSameOrBefore(txn.date); }); } /** * Recursively remove null, undefined, empty string, and empty array values from objects and arrays. */ function removeEmptyValues(value) { if (Array.isArray(value)) { return value.map(item => removeEmptyValues(item)); } if (value && typeof value === 'object') { const entries = Object.entries(value).filter(([, v]) => { if (v === null || v === undefined || v === '') return false; if (Array.isArray(v) && v.length === 0) return false; return true; }).map(([k, v]) => [k, removeEmptyValues(v)]); return Object.fromEntries(entries); } return value; } /** * Add/extend raw transaction data with new raw data. * - Cleans the data to remove null/undefined/empty-string keys. * - When called with one argument: returns cleaned data (common case for setting new raw transaction). * - When called with two arguments and transaction has rawTransaction: extends existing raw transaction. */ function getRawTransaction(data, transaction) { const current = transaction?.rawTransaction; const cleaned = removeEmptyValues(data); if (!current) { return cleaned; } if (Array.isArray(current)) { return [...current, cleaned]; } return [current, cleaned]; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbW9tZW50IiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfdHJhbnNhY3Rpb25zIiwiZSIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiaXNOb3JtYWxUcmFuc2FjdGlvbiIsInR4biIsInR5cGUiLCJUcmFuc2FjdGlvblR5cGVzIiwiTm9ybWFsIiwiaXNJbnN0YWxsbWVudFRyYW5zYWN0aW9uIiwiSW5zdGFsbG1lbnRzIiwiaXNOb25Jbml0aWFsSW5zdGFsbG1lbnRUcmFuc2FjdGlvbiIsImluc3RhbGxtZW50cyIsIm51bWJlciIsImlzSW5pdGlhbEluc3RhbGxtZW50VHJhbnNhY3Rpb24iLCJmaXhJbnN0YWxsbWVudHMiLCJ0eG5zIiwibWFwIiwiY2xvbmVkVHhuIiwiZGF0ZU1vbWVudCIsIm1vbWVudCIsImRhdGUiLCJhY3R1YWxEYXRlTW9tZW50IiwiYWRkIiwidG9JU09TdHJpbmciLCJzb3J0VHJhbnNhY3Rpb25zQnlEYXRlIiwic29ydCIsImEiLCJiIiwiZmlsdGVyT2xkVHJhbnNhY3Rpb25zIiwic3RhcnRNb21lbnQiLCJjb21iaW5lSW5zdGFsbG1lbnRzIiwiZmlsdGVyIiwiY29tYmluZU5lZWRlZEFuZEluaXRpYWxPck5vcm1hbCIsImlzU2FtZU9yQmVmb3JlIiwicmVtb3ZlRW1wdHlWYWx1ZXMiLCJ2YWx1ZSIsIkFycmF5IiwiaXNBcnJheSIsIml0ZW0iLCJlbnRyaWVzIiwiT2JqZWN0IiwidiIsInVuZGVmaW5lZCIsImxlbmd0aCIsImsiLCJmcm9tRW50cmllcyIsImdldFJhd1RyYW5zYWN0aW9uIiwiZGF0YSIsInRyYW5zYWN0aW9uIiwiY3VycmVudCIsInJhd1RyYW5zYWN0aW9uIiwiY2xlYW5lZCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9oZWxwZXJzL3RyYW5zYWN0aW9ucy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgbW9tZW50LCB7IHR5cGUgTW9tZW50IH0gZnJvbSAnbW9tZW50JztcbmltcG9ydCB7IFRyYW5zYWN0aW9uVHlwZXMsIHR5cGUgVHJhbnNhY3Rpb24gfSBmcm9tICcuLi90cmFuc2FjdGlvbnMnO1xuXG5mdW5jdGlvbiBpc05vcm1hbFRyYW5zYWN0aW9uKHR4bjogYW55KTogYm9vbGVhbiB7XG4gIHJldHVybiB0eG4gJiYgdHhuLnR5cGUgPT09IFRyYW5zYWN0aW9uVHlwZXMuTm9ybWFsO1xufVxuXG5mdW5jdGlvbiBpc0luc3RhbGxtZW50VHJhbnNhY3Rpb24odHhuOiBhbnkpOiBib29sZWFuIHtcbiAgcmV0dXJuIHR4biAmJiB0eG4udHlwZSA9PT0gVHJhbnNhY3Rpb25UeXBlcy5JbnN0YWxsbWVudHM7XG59XG5cbmZ1bmN0aW9uIGlzTm9uSW5pdGlhbEluc3RhbGxtZW50VHJhbnNhY3Rpb24odHhuOiBUcmFuc2FjdGlvbik6IGJvb2xlYW4ge1xuICByZXR1cm4gaXNJbnN0YWxsbWVudFRyYW5zYWN0aW9uKHR4bikgJiYgISF0eG4uaW5zdGFsbG1lbnRzICYmIHR4bi5pbnN0YWxsbWVudHMubnVtYmVyID4gMTtcbn1cblxuZnVuY3Rpb24gaXNJbml0aWFsSW5zdGFsbG1lbnRUcmFuc2FjdGlvbih0eG46IFRyYW5zYWN0aW9uKTogYm9vbGVhbiB7XG4gIHJldHVybiBpc0luc3RhbGxtZW50VHJhbnNhY3Rpb24odHhuKSAmJiAhIXR4bi5pbnN0YWxsbWVudHMgJiYgdHhuLmluc3RhbGxtZW50cy5udW1iZXIgPT09IDE7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmaXhJbnN0YWxsbWVudHModHhuczogVHJhbnNhY3Rpb25bXSk6IFRyYW5zYWN0aW9uW10ge1xuICByZXR1cm4gdHhucy5tYXAoKHR4bjogVHJhbnNhY3Rpb24pID0+IHtcbiAgICBjb25zdCBjbG9uZWRUeG4gPSB7IC4uLnR4biB9O1xuXG4gICAgaWYgKFxuICAgICAgaXNJbnN0YWxsbWVudFRyYW5zYWN0aW9uKGNsb25lZFR4bikgJiZcbiAgICAgIGlzTm9uSW5pdGlhbEluc3RhbGxtZW50VHJhbnNhY3Rpb24oY2xvbmVkVHhuKSAmJlxuICAgICAgY2xvbmVkVHhuLmluc3RhbGxtZW50c1xuICAgICkge1xuICAgICAgY29uc3QgZGF0ZU1vbWVudCA9IG1vbWVudChjbG9uZWRUeG4uZGF0ZSk7XG4gICAgICBjb25zdCBhY3R1YWxEYXRlTW9tZW50ID0gZGF0ZU1vbWVudC5hZGQoY2xvbmVkVHhuLmluc3RhbGxtZW50cy5udW1iZXIgLSAxLCAnbW9udGgnKTtcbiAgICAgIGNsb25lZFR4bi5kYXRlID0gYWN0dWFsRGF0ZU1vbWVudC50b0lTT1N0cmluZygpO1xuICAgIH1cbiAgICByZXR1cm4gY2xvbmVkVHhuO1xuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNvcnRUcmFuc2FjdGlvbnNCeURhdGUodHhuczogVHJhbnNhY3Rpb25bXSkge1xuICByZXR1cm4gWy4uLnR4bnNdLnNvcnQoKGEsIGIpID0+IChhLmRhdGUgPCBiLmRhdGUgPyAtMSA6IGEuZGF0ZSA+IGIuZGF0ZSA/IDEgOiAwKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJPbGRUcmFuc2FjdGlvbnModHhuczogVHJhbnNhY3Rpb25bXSwgc3RhcnRNb21lbnQ6IE1vbWVudCwgY29tYmluZUluc3RhbGxtZW50czogYm9vbGVhbikge1xuICByZXR1cm4gdHhucy5maWx0ZXIodHhuID0+IHtcbiAgICBjb25zdCBjb21iaW5lTmVlZGVkQW5kSW5pdGlhbE9yTm9ybWFsID1cbiAgICAgIGNvbWJpbmVJbnN0YWxsbWVudHMgJiYgKGlzTm9ybWFsVHJhbnNhY3Rpb24odHhuKSB8fCBpc0luaXRpYWxJbnN0YWxsbWVudFRyYW5zYWN0aW9uKHR4bikpO1xuICAgIHJldHVybiAoXG4gICAgICAoIWNvbWJpbmVJbnN0YWxsbWVudHMgJiYgc3RhcnRNb21lbnQuaXNTYW1lT3JCZWZvcmUodHhuLmRhdGUpKSB8fFxuICAgICAgKGNvbWJpbmVOZWVkZWRBbmRJbml0aWFsT3JOb3JtYWwgJiYgc3RhcnRNb21lbnQuaXNTYW1lT3JCZWZvcmUodHhuLmRhdGUpKVxuICAgICk7XG4gIH0pO1xufVxuXG4vKipcbiAqIFJlY3Vyc2l2ZWx5IHJlbW92ZSBudWxsLCB1bmRlZmluZWQsIGVtcHR5IHN0cmluZywgYW5kIGVtcHR5IGFycmF5IHZhbHVlcyBmcm9tIG9iamVjdHMgYW5kIGFycmF5cy5cbiAqL1xuZnVuY3Rpb24gcmVtb3ZlRW1wdHlWYWx1ZXM8VD4odmFsdWU6IFQpOiBUIHtcbiAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgcmV0dXJuIHZhbHVlLm1hcChpdGVtID0+IHJlbW92ZUVtcHR5VmFsdWVzKGl0ZW0pKSBhcyB1bmtub3duIGFzIFQ7XG4gIH1cblxuICBpZiAodmFsdWUgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgIGNvbnN0IGVudHJpZXMgPSBPYmplY3QuZW50cmllcyh2YWx1ZSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilcbiAgICAgIC5maWx0ZXIoKFssIHZdKSA9PiB7XG4gICAgICAgIGlmICh2ID09PSBudWxsIHx8IHYgPT09IHVuZGVmaW5lZCB8fCB2ID09PSAnJykgcmV0dXJuIGZhbHNlO1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheSh2KSAmJiB2Lmxlbmd0aCA9PT0gMCkgcmV0dXJuIGZhbHNlO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0pXG4gICAgICAubWFwKChbaywgdl0pID0+IFtrLCByZW1vdmVFbXB0eVZhbHVlcyh2KV0pO1xuXG4gICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhlbnRyaWVzKSBhcyB1bmtub3duIGFzIFQ7XG4gIH1cblxuICByZXR1cm4gdmFsdWU7XG59XG5cbi8qKlxuICogQWRkL2V4dGVuZCByYXcgdHJhbnNhY3Rpb24gZGF0YSB3aXRoIG5ldyByYXcgZGF0YS5cbiAqIC0gQ2xlYW5zIHRoZSBkYXRhIHRvIHJlbW92ZSBudWxsL3VuZGVmaW5lZC9lbXB0eS1zdHJpbmcga2V5cy5cbiAqIC0gV2hlbiBjYWxsZWQgd2l0aCBvbmUgYXJndW1lbnQ6IHJldHVybnMgY2xlYW5lZCBkYXRhIChjb21tb24gY2FzZSBmb3Igc2V0dGluZyBuZXcgcmF3IHRyYW5zYWN0aW9uKS5cbiAqIC0gV2hlbiBjYWxsZWQgd2l0aCB0d28gYXJndW1lbnRzIGFuZCB0cmFuc2FjdGlvbiBoYXMgcmF3VHJhbnNhY3Rpb246IGV4dGVuZHMgZXhpc3RpbmcgcmF3IHRyYW5zYWN0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0UmF3VHJhbnNhY3Rpb24oZGF0YTogdW5rbm93biwgdHJhbnNhY3Rpb24/OiB7IHJhd1RyYW5zYWN0aW9uPzogdW5rbm93biB9KTogdW5rbm93biB7XG4gIGNvbnN0IGN1cnJlbnQgPSB0cmFuc2FjdGlvbj8ucmF3VHJhbnNhY3Rpb247XG4gIGNvbnN0IGNsZWFuZWQgPSByZW1vdmVFbXB0eVZhbHVlcyhkYXRhKTtcblxuICBpZiAoIWN1cnJlbnQpIHtcbiAgICByZXR1cm4gY2xlYW5lZDtcbiAgfVxuXG4gIGlmIChBcnJheS5pc0FycmF5KGN1cnJlbnQpKSB7XG4gICAgcmV0dXJuIFsuLi5jdXJyZW50LCBjbGVhbmVkXTtcbiAgfVxuXG4gIHJldHVybiBbY3VycmVudCwgY2xlYW5lZF07XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBLElBQUFBLE9BQUEsR0FBQUMsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLGFBQUEsR0FBQUQsT0FBQTtBQUFxRSxTQUFBRCx1QkFBQUcsQ0FBQSxXQUFBQSxDQUFBLElBQUFBLENBQUEsQ0FBQUMsVUFBQSxHQUFBRCxDQUFBLEtBQUFFLE9BQUEsRUFBQUYsQ0FBQTtBQUVyRSxTQUFTRyxtQkFBbUJBLENBQUNDLEdBQVEsRUFBVztFQUM5QyxPQUFPQSxHQUFHLElBQUlBLEdBQUcsQ0FBQ0MsSUFBSSxLQUFLQyw4QkFBZ0IsQ0FBQ0MsTUFBTTtBQUNwRDtBQUVBLFNBQVNDLHdCQUF3QkEsQ0FBQ0osR0FBUSxFQUFXO0VBQ25ELE9BQU9BLEdBQUcsSUFBSUEsR0FBRyxDQUFDQyxJQUFJLEtBQUtDLDhCQUFnQixDQUFD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