From 353e7be31f51e137232f687ee34579a53ddbd52b Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Sat, 11 Nov 2023 13:28:48 -0800 Subject: [PATCH] Support ofx INVSTMTMSGSRSV1 (#1876) * Support ofx INVSTMTMSGSRSV1 * Release notes --- .../components/modals/ImportTransactions.js | 26 +++++----- .../loot-core/src/server/accounts/ofx2json.ts | 52 ++++++++++++++++--- upcoming-release-notes/1876.md | 6 +++ 3 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 upcoming-release-notes/1876.md diff --git a/packages/desktop-client/src/components/modals/ImportTransactions.js b/packages/desktop-client/src/components/modals/ImportTransactions.js index 39fd2e4a109..11665ea71e8 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactions.js +++ b/packages/desktop-client/src/components/modals/ImportTransactions.js @@ -767,11 +767,11 @@ export default function ImportTransactions({ modalProps, options }) { useEffect(() => { const fileType = getFileType(options.filename); - const parseOptions = getParseOptions( - fileType, - { delimiter, hasHeaderRow }, - { fallbackMissingPayeeToMemo }, - ); + const parseOptions = getParseOptions(fileType, { + delimiter, + hasHeaderRow, + fallbackMissingPayeeToMemo, + }); parse(options.filename, parseOptions); }, [parseTransactions, options.filename]); @@ -819,11 +819,11 @@ export default function ImportTransactions({ modalProps, options }) { }); const fileType = getFileType(res[0]); - const parseOptions = getParseOptions( - fileType, - { delimiter, hasHeaderRow }, - { fallbackMissingPayeeToMemo }, - ); + const parseOptions = getParseOptions(fileType, { + delimiter, + hasHeaderRow, + fallbackMissingPayeeToMemo, + }); parse(res[0], parseOptions); } @@ -1192,12 +1192,12 @@ export default function ImportTransactions({ modalProps, options }) { ); } -function getParseOptions(fileType, csvOptions, ofxOptions) { +function getParseOptions(fileType, options = {}) { if (fileType === 'csv') { - const { delimiter, hasHeaderRow } = csvOptions; + const { delimiter, hasHeaderRow } = options; return { delimiter, hasHeaderRow }; } else if (isOfxFile(fileType)) { - const { fallbackMissingPayeeToMemo } = ofxOptions; + const { fallbackMissingPayeeToMemo } = options; return { fallbackMissingPayeeToMemo }; } return {}; diff --git a/packages/loot-core/src/server/accounts/ofx2json.ts b/packages/loot-core/src/server/accounts/ofx2json.ts index 36199bc57d7..ad43b099ef1 100644 --- a/packages/loot-core/src/server/accounts/ofx2json.ts +++ b/packages/loot-core/src/server/accounts/ofx2json.ts @@ -34,14 +34,54 @@ async function parseXml(content) { function getStmtTrn(data) { const ofx = data?.['OFX']; - const isCc = ofx?.['CREDITCARDMSGSRSV1'] != null; - const msg = isCc ? ofx?.['CREDITCARDMSGSRSV1'] : ofx?.['BANKMSGSRSV1']; - const stmtTrnRs = msg?.[`${isCc ? 'CC' : ''}STMTTRNRS`]; - const stmtRs = stmtTrnRs?.[`${isCc ? 'CC' : ''}STMTRS`]; - const bankTranList = stmtRs?.['BANKTRANLIST']; + if (ofx?.['CREDITCARDMSGSRSV1'] != null) { + return getCcStmtTrn(ofx); + } else if (ofx?.['INVSTMTMSGSRSV1'] != null) { + return getInvStmtTrn(ofx); + } else { + return getBankStmtTrn(ofx); + } +} + +function getBankStmtTrn(ofx) { + const msg = ofx?.['BANKMSGSRSV1']; + const stmtTrnRs = msg?.['STMTTRNRS']; + const stmtRs = stmtTrnRs?.['STMTRS']; + const tranList = stmtRs?.['BANKTRANLIST']; + // Could be an array or a single object. + // xml2js serializes single item to an object and multiple to an array. + const stmtTrn = tranList?.['STMTTRN']; + + if (!Array.isArray(stmtTrn)) { + return [stmtTrn]; + } + return stmtTrn; +} + +function getCcStmtTrn(ofx) { + const msg = ofx?.['CREDITCARDMSGSRSV1']; + const stmtTrnRs = msg?.['CCSTMTTRNRS']; + const stmtRs = stmtTrnRs?.['CCSTMTRS']; + const tranList = stmtRs?.['BANKTRANLIST']; // Could be an array or a single object. // xml2js serializes single item to an object and multiple to an array. - const stmtTrn = bankTranList?.['STMTTRN']; + const stmtTrn = tranList?.['STMTTRN']; + + if (!Array.isArray(stmtTrn)) { + return [stmtTrn]; + } + return stmtTrn; +} + +function getInvStmtTrn(ofx) { + const msg = ofx?.['INVSTMTMSGSRSV1']; + const stmtTrnRs = msg?.['INVSTMTTRNRS']; + const stmtRs = stmtTrnRs?.['INVSTMTRS']; + const tranList = stmtRs?.['INVTRANLIST']; + // Could be an array or a single object. + // xml2js serializes single item to an object and multiple to an array. + const stmtTrn = tranList?.['INVBANKTRAN']?.flatMap(t => t?.['STMTTRN']); + if (!Array.isArray(stmtTrn)) { return [stmtTrn]; } diff --git a/upcoming-release-notes/1876.md b/upcoming-release-notes/1876.md new file mode 100644 index 00000000000..55735971182 --- /dev/null +++ b/upcoming-release-notes/1876.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [joel-jeremy] +--- + +Support import of ofx transactions of type INVSTMTMSGSRSV1