From ec4cd8a19e38a6b58c16fb062be4bdba6c14b5d0 Mon Sep 17 00:00:00 2001 From: Daniele Nicolodi Date: Wed, 7 Jan 2026 21:40:51 +0100 Subject: [PATCH 1/2] examples/importers/ofx: Add importer test --- .github/workflows/test.yaml | 1 + examples/importers/ofx.py | 6 +++ examples/tests/ofx/ofxdownload.ofx | 11 ++++++ examples/tests/ofx/ofxdownload.ofx.beancount | 41 ++++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 examples/tests/ofx/ofxdownload.ofx create mode 100644 examples/tests/ofx/ofxdownload.ofx.beancount diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7e80f9c0..c6021dd5 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -62,6 +62,7 @@ jobs: cd examples python importers/acme.py test tests/acme --verbose python importers/csvbank.py test tests/csvbank --verbose + python importers/ofx.py test tests/ofx --verbose python importers/utrade.py test tests/utrade --verbose - name: Run example ledger.import run: | diff --git a/examples/importers/ofx.py b/examples/importers/ofx.py index 553b0312..d7d3e2a0 100644 --- a/examples/importers/ofx.py +++ b/examples/importers/ofx.py @@ -36,6 +36,7 @@ import beangulp from beangulp import mimetypes +from beangulp.testing import main class BalanceType(enum.Enum): @@ -322,3 +323,8 @@ def build_transaction(stmttrn, flag, account, currency): return data.Transaction( fileloc, date, flag, payee, narration, data.EMPTY_SET, data.EMPTY_SET, [posting] ) + + +if __name__ == "__main__": + importer = Importer("379700001111222", "Liabilities:US:CreditCard", "bofa") + main(importer) diff --git a/examples/tests/ofx/ofxdownload.ofx b/examples/tests/ofx/ofxdownload.ofx new file mode 100644 index 00000000..6fd34775 --- /dev/null +++ b/examples/tests/ofx/ofxdownload.ofx @@ -0,0 +1,11 @@ +OFXHEADER:100 +DATA:OFXSGML +VERSION:102 +SECURITY:NONE +ENCODING:USASCII +CHARSET:1252 +COMPRESSION:NONE +OLDFILEUID:NONE +NEWFILEUID:NONE + +0INFOLogin successful20140112083600.212[-7:MST]ENGAMEX3101FMPWeb310120140112083600exampleuser00INFOUSD379700001111222falsedownload90Days379700001111222trueBe308f58246398a74c52504a8b06d5f0520140112050000.000[-7:MST]20140112050000.000[-7:MST]DEBIT20131201000000.000[-7:MST]-9.99320133350353869664320133350353869664SPOTIFY USA 287701309012600720879 WWW.SPOTIFY.COMDEBIT20131202000000.000[-7:MST]-61.71320133360368356592320133360368356592GOAT TOWN 1200000549NEW YORK 071000163 2126873641DEBIT20131202000000.000[-7:MST]-17.75320133360368356593320133360368356593AMC VILLAGE 7 #2110 NEW YORK 12010365699 212-982-2116DEBIT20131205000000.000[-7:MST]-42.4320133390419136515320133390419136515CAFE MOGADOR 0048 NEW YORK 969881 212-677-2226DEBIT20131209000000.000[-7:MST]-34.38320133430477621377320133430477621377UNION MARKET - HOUSNEW YORK 15827 GROCERY STOREDEBIT20131209000000.000[-7:MST]-13.5320133430477621378320133430477621378SUNSHINE CINEMA 5429NEW YORK 208001222 2122607289DEBIT20131211000000.000[-7:MST]-21.96320133450009270816320133450009270816UNION MARKET - HOUSNEW YORK 13775 GROCERY STOREDEBIT20131211000000.000[-7:MST]-14.47320133450009270817320133450009270817WHOLEFDS HOU 10236 02124201320042002720272124201320DEBIT20131214000000.000[-7:MST]-56.43320133480059384557320133480059384557MACY'S / #003 HERALD S / NEW YORK 00307963916 MACY'SDEBIT20131215000000.000[-7:MST]-13320133490073491495320133490073491495WHOLEFDS HOU 10236 02124201320042402720282124201320DEBIT20131216000000.000[-7:MST]-60.23320133500088330906320133500088330906PAPYRUS #2302 000002NEW YORK 14252302002 2162527300DEBIT20131216000000.000[-7:MST]-7320133500088330907320133500088330907LES CAFES 400 LAFAYENEW YORK 10156320131 2157761076-2356.3820131218050000.000[-7:MST]falsefalsefalse diff --git a/examples/tests/ofx/ofxdownload.ofx.beancount b/examples/tests/ofx/ofxdownload.ofx.beancount new file mode 100644 index 00000000..62688d46 --- /dev/null +++ b/examples/tests/ofx/ofxdownload.ofx.beancount @@ -0,0 +1,41 @@ +;; Account: Liabilities:US:CreditCard +;; Date: 2013-12-18 +;; Name: bofa.ofx + +2013-12-01 * "SPOTIFY USA 28770130901 / 2600720879 WWW.SPOTIFY.COM" + Liabilities:US:CreditCard -9.99 USD + +2013-12-02 * "GOAT TOWN 1200000549NEW YORK / 071000163 2126873641" + Liabilities:US:CreditCard -61.71 USD + +2013-12-02 * "AMC VILLAGE 7 #2110 NEW YORK / 12010365699 212-982-2116" + Liabilities:US:CreditCard -17.75 USD + +2013-12-05 * "CAFE MOGADOR 0048 NEW YORK / 969881 212-677-2226" + Liabilities:US:CreditCard -42.4 USD + +2013-12-09 * "UNION MARKET - HOUSNEW YORK / 15827 GROCERY STORE" + Liabilities:US:CreditCard -34.38 USD + +2013-12-09 * "SUNSHINE CINEMA 5429NEW YORK / 208001222 2122607289" + Liabilities:US:CreditCard -13.5 USD + +2013-12-11 * "UNION MARKET - HOUSNEW YORK / 13775 GROCERY STORE" + Liabilities:US:CreditCard -21.96 USD + +2013-12-11 * "WHOLEFDS HOU 10236 02124201320 / 042002720272124201320" + Liabilities:US:CreditCard -14.47 USD + +2013-12-14 * "MACY'S / #003 HERALD S / NEW YORK / 00307963916 MACY'S" + Liabilities:US:CreditCard -56.43 USD + +2013-12-15 * "WHOLEFDS HOU 10236 02124201320 / 042402720282124201320" + Liabilities:US:CreditCard -13 USD + +2013-12-16 * "PAPYRUS #2302 000002NEW YORK / 14252302002 2162527300" + Liabilities:US:CreditCard -60.23 USD + +2013-12-16 * "LES CAFES 400 LAFAYENEW YORK / 10156320131 2157761076" + Liabilities:US:CreditCard -7 USD + +2013-12-19 balance Liabilities:US:CreditCard -2356.38 USD From c59fbe06fcdf14fd63a4f59ab242c9e71dd97547 Mon Sep 17 00:00:00 2001 From: Wil Clouser Date: Mon, 5 Jan 2026 18:43:05 -0800 Subject: [PATCH 2/2] examples/import: Harden against unexpected input data This code escapes its demonstrative purpose and is copied into user setups. Fix the import hook to be robust against narration containing multiple slashes. Splitting on the last slash is arbitrary but good as any other choice. --- examples/Downloads/ofxdownload.ofx | 2 +- examples/import.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/Downloads/ofxdownload.ofx b/examples/Downloads/ofxdownload.ofx index f8e6a0ec..6fd34775 100644 --- a/examples/Downloads/ofxdownload.ofx +++ b/examples/Downloads/ofxdownload.ofx @@ -8,4 +8,4 @@ COMPRESSION:NONE OLDFILEUID:NONE NEWFILEUID:NONE -0INFOLogin successful20140112083600.212[-7:MST]ENGAMEX3101FMPWeb310120140112083600exampleuser00INFOUSD379700001111222falsedownload90Days379700001111222trueBe308f58246398a74c52504a8b06d5f0520140112050000.000[-7:MST]20140112050000.000[-7:MST]DEBIT20131201000000.000[-7:MST]-9.99320133350353869664320133350353869664SPOTIFY USA 287701309012600720879 WWW.SPOTIFY.COMDEBIT20131202000000.000[-7:MST]-61.71320133360368356592320133360368356592GOAT TOWN 1200000549NEW YORK 071000163 2126873641DEBIT20131202000000.000[-7:MST]-17.75320133360368356593320133360368356593AMC VILLAGE 7 #2110 NEW YORK 12010365699 212-982-2116DEBIT20131205000000.000[-7:MST]-42.4320133390419136515320133390419136515CAFE MOGADOR 0048 NEW YORK 969881 212-677-2226DEBIT20131209000000.000[-7:MST]-34.38320133430477621377320133430477621377UNION MARKET - HOUSNEW YORK 15827 GROCERY STOREDEBIT20131209000000.000[-7:MST]-13.5320133430477621378320133430477621378SUNSHINE CINEMA 5429NEW YORK 208001222 2122607289DEBIT20131211000000.000[-7:MST]-21.96320133450009270816320133450009270816UNION MARKET - HOUSNEW YORK 13775 GROCERY STOREDEBIT20131211000000.000[-7:MST]-14.47320133450009270817320133450009270817WHOLEFDS HOU 10236 02124201320042002720272124201320DEBIT20131214000000.000[-7:MST]-56.43320133480059384557320133480059384557MACY'S #003 HERALD SNEW YORK 00307963916 MACY'SDEBIT20131215000000.000[-7:MST]-13320133490073491495320133490073491495WHOLEFDS HOU 10236 02124201320042402720282124201320DEBIT20131216000000.000[-7:MST]-60.23320133500088330906320133500088330906PAPYRUS #2302 000002NEW YORK 14252302002 2162527300DEBIT20131216000000.000[-7:MST]-7320133500088330907320133500088330907LES CAFES 400 LAFAYENEW YORK 10156320131 2157761076-2356.3820131218050000.000[-7:MST]falsefalsefalse +0INFOLogin successful20140112083600.212[-7:MST]ENGAMEX3101FMPWeb310120140112083600exampleuser00INFOUSD379700001111222falsedownload90Days379700001111222trueBe308f58246398a74c52504a8b06d5f0520140112050000.000[-7:MST]20140112050000.000[-7:MST]DEBIT20131201000000.000[-7:MST]-9.99320133350353869664320133350353869664SPOTIFY USA 287701309012600720879 WWW.SPOTIFY.COMDEBIT20131202000000.000[-7:MST]-61.71320133360368356592320133360368356592GOAT TOWN 1200000549NEW YORK 071000163 2126873641DEBIT20131202000000.000[-7:MST]-17.75320133360368356593320133360368356593AMC VILLAGE 7 #2110 NEW YORK 12010365699 212-982-2116DEBIT20131205000000.000[-7:MST]-42.4320133390419136515320133390419136515CAFE MOGADOR 0048 NEW YORK 969881 212-677-2226DEBIT20131209000000.000[-7:MST]-34.38320133430477621377320133430477621377UNION MARKET - HOUSNEW YORK 15827 GROCERY STOREDEBIT20131209000000.000[-7:MST]-13.5320133430477621378320133430477621378SUNSHINE CINEMA 5429NEW YORK 208001222 2122607289DEBIT20131211000000.000[-7:MST]-21.96320133450009270816320133450009270816UNION MARKET - HOUSNEW YORK 13775 GROCERY STOREDEBIT20131211000000.000[-7:MST]-14.47320133450009270817320133450009270817WHOLEFDS HOU 10236 02124201320042002720272124201320DEBIT20131214000000.000[-7:MST]-56.43320133480059384557320133480059384557MACY'S / #003 HERALD S / NEW YORK 00307963916 MACY'SDEBIT20131215000000.000[-7:MST]-13320133490073491495320133490073491495WHOLEFDS HOU 10236 02124201320042402720282124201320DEBIT20131216000000.000[-7:MST]-60.23320133500088330906320133500088330906PAPYRUS #2302 000002NEW YORK 14252302002 2162527300DEBIT20131216000000.000[-7:MST]-7320133500088330907320133500088330907LES CAFES 400 LAFAYENEW YORK 10156320131 2157761076-2356.3820131218050000.000[-7:MST]falsefalsefalse diff --git a/examples/import.py b/examples/import.py index ec847e74..42bab371 100755 --- a/examples/import.py +++ b/examples/import.py @@ -37,12 +37,14 @@ def clean_up_descriptions(extracted_entries): clean_entries = [] for entry in extracted_entries: if isinstance(entry, data.Transaction): - if entry.narration and " / " in entry.narration: - left_part, _ = entry.narration.split(" / ") - entry = entry._replace(narration=left_part) - if entry.payee and " / " in entry.payee: - left_part, _ = entry.payee.split(" / ") - entry = entry._replace(payee=left_part) + if entry.narration: + narration, sep, _ = entry.narration.rpartition(" / ") + if sep: + entry = entry._replace(narration=narration) + if entry.payee: + payee, sep, _ = entry.payee.rpartition(" / ") + if sep: + entry = entry._replace(payee=payee) clean_entries.append(entry) return clean_entries