diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 9b43b18..5acc08b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.8", "3.9", "3.10", "3.11"]
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v2
diff --git a/Pipfile b/Pipfile
index 2284bd1..3fbaad5 100644
--- a/Pipfile
+++ b/Pipfile
@@ -12,8 +12,10 @@ mypy = "*"
types-mock = "*"
exceptiongroup = {markers="python_version < '3.11'"}
tomli = {markers="python_version < '3.11'"}
+types-appdirs = "*"
[packages]
ofxstatement = {editable = true,path = "."}
exceptiongroup = "*"
-importlib_metadata = {version = ">=3.8", markers="python_version < '3.10'"}
\ No newline at end of file
+importlib_metadata = {version = ">=3.8", markers="python_version < '3.10'"}
+zipp = {version = ">=3.8", markers="python_version < '3.10'"}
diff --git a/Pipfile.lock b/Pipfile.lock
index f56bba1..08a7e97 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "9eff26da7c0885e82355bd0e9d77d60f3ebee69507ba41b063a5a5599ccc6f6f"
+ "sha256": "d01ad0c4b47d2fff1cffed79da795c043f9e96086adac9594cc73053658fa6ed"
},
"pipfile-spec": 6,
"requires": {},
@@ -31,13 +31,8 @@
"version": "==1.2.0"
},
"importlib-metadata": {
- "hashes": [
- "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e",
- "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"
- ],
- "index": "pypi",
"markers": "python_version < '3.10'",
- "version": "==7.0.1"
+ "version": "==3.8"
},
"ofxstatement": {
"editable": true,
@@ -45,42 +40,43 @@
},
"zipp": {
"hashes": [
- "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31",
- "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"
+ "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b",
+ "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"
],
- "markers": "python_version < '3.10' and python_version >= '3.8'",
- "version": "==3.17.0"
+ "index": "pypi",
+ "markers": "python_version >= '3.8'",
+ "version": "==3.18.1"
}
},
"develop": {
"black": {
"hashes": [
- "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50",
- "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f",
- "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e",
- "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec",
- "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055",
- "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3",
- "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5",
- "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54",
- "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b",
- "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e",
- "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e",
- "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba",
- "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea",
- "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59",
- "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d",
- "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0",
- "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9",
- "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a",
- "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e",
- "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba",
- "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2",
- "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"
+ "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f",
+ "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93",
+ "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11",
+ "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0",
+ "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9",
+ "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5",
+ "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213",
+ "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d",
+ "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7",
+ "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837",
+ "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f",
+ "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395",
+ "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995",
+ "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f",
+ "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597",
+ "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959",
+ "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5",
+ "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb",
+ "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4",
+ "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7",
+ "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd",
+ "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
- "version": "==23.12.1"
+ "version": "==24.3.0"
},
"click": {
"hashes": [
@@ -95,61 +91,61 @@
"toml"
],
"hashes": [
- "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca",
- "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471",
- "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a",
- "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058",
- "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85",
- "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143",
- "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446",
- "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590",
- "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a",
- "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105",
- "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9",
- "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a",
- "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac",
- "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25",
- "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2",
- "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450",
- "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932",
- "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba",
- "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137",
- "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae",
- "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614",
- "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70",
- "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e",
- "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505",
- "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870",
- "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc",
- "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451",
- "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7",
- "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e",
- "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566",
- "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5",
- "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26",
- "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2",
- "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42",
- "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555",
- "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43",
- "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed",
- "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa",
- "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516",
- "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952",
- "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd",
- "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09",
- "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c",
- "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f",
- "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6",
- "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1",
- "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0",
- "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e",
- "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9",
- "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9",
- "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e",
- "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"
+ "sha256:006d220ba2e1a45f1de083d5022d4955abb0aedd78904cd5a779b955b019ec73",
+ "sha256:06fe398145a2e91edaf1ab4eee66149c6776c6b25b136f4a86fcbbb09512fd10",
+ "sha256:175f56572f25e1e1201d2b3e07b71ca4d201bf0b9cb8fad3f1dfae6a4188de86",
+ "sha256:18cac867950943fe93d6cd56a67eb7dcd2d4a781a40f4c1e25d6f1ed98721a55",
+ "sha256:1a5ee18e3a8d766075ce9314ed1cb695414bae67df6a4b0805f5137d93d6f1cb",
+ "sha256:20a875bfd8c282985c4720c32aa05056f77a68e6d8bbc5fe8632c5860ee0b49b",
+ "sha256:2412e98e70f16243be41d20836abd5f3f32edef07cbf8f407f1b6e1ceae783ac",
+ "sha256:2599972b21911111114100d362aea9e70a88b258400672626efa2b9e2179609c",
+ "sha256:2ed37e16cf35c8d6e0b430254574b8edd242a367a1b1531bd1adc99c6a5e00fe",
+ "sha256:32b4ab7e6c924f945cbae5392832e93e4ceb81483fd6dc4aa8fb1a97b9d3e0e1",
+ "sha256:34423abbaad70fea9d0164add189eabaea679068ebdf693baa5c02d03e7db244",
+ "sha256:3507427d83fa961cbd73f11140f4a5ce84208d31756f7238d6257b2d3d868405",
+ "sha256:3733545eb294e5ad274abe131d1e7e7de4ba17a144505c12feca48803fea5f64",
+ "sha256:3ff5bdb08d8938d336ce4088ca1a1e4b6c8cd3bef8bb3a4c0eb2f37406e49643",
+ "sha256:3ff7f92ae5a456101ca8f48387fd3c56eb96353588e686286f50633a611afc95",
+ "sha256:42a9e754aa250fe61f0f99986399cec086d7e7a01dd82fd863a20af34cbce962",
+ "sha256:51593a1f05c39332f623d64d910445fdec3d2ac2d96b37ce7f331882d5678ddf",
+ "sha256:5b11f9c6587668e495cc7365f85c93bed34c3a81f9f08b0920b87a89acc13469",
+ "sha256:69f1665165ba2fe7614e2f0c1aed71e14d83510bf67e2ee13df467d1c08bf1e8",
+ "sha256:78cdcbf7b9cb83fe047ee09298e25b1cd1636824067166dc97ad0543b079d22f",
+ "sha256:7df95fdd1432a5d2675ce630fef5f239939e2b3610fe2f2b5bf21fa505256fa3",
+ "sha256:81a5fb41b0d24447a47543b749adc34d45a2cf77b48ca74e5bf3de60a7bd9edc",
+ "sha256:840456cb1067dc350af9080298c7c2cfdddcedc1cb1e0b30dceecdaf7be1a2d3",
+ "sha256:8562ca91e8c40864942615b1d0b12289d3e745e6b2da901d133f52f2d510a1e3",
+ "sha256:861d75402269ffda0b33af94694b8e0703563116b04c681b1832903fac8fd647",
+ "sha256:8b98c89db1b150d851a7840142d60d01d07677a18f0f46836e691c38134ed18b",
+ "sha256:a178b7b1ac0f1530bb28d2e51f88c0bab3e5949835851a60dda80bff6052510c",
+ "sha256:a8ddbd158e069dded57738ea69b9744525181e99974c899b39f75b2b29a624e2",
+ "sha256:ac4bab32f396b03ebecfcf2971668da9275b3bb5f81b3b6ba96622f4ef3f6e17",
+ "sha256:ac9e95cefcf044c98d4e2c829cd0669918585755dd9a92e28a1a7012322d0a95",
+ "sha256:adbdfcda2469d188d79771d5696dc54fab98a16d2ef7e0875013b5f56a251047",
+ "sha256:b3c8bbb95a699c80a167478478efe5e09ad31680931ec280bf2087905e3b95ec",
+ "sha256:b3f2b1eb229f23c82898eedfc3296137cf1f16bb145ceab3edfd17cbde273fb7",
+ "sha256:b4ae777bebaed89e3a7e80c4a03fac434a98a8abb5251b2a957d38fe3fd30088",
+ "sha256:b953275d4edfab6cc0ed7139fa773dfb89e81fee1569a932f6020ce7c6da0e8f",
+ "sha256:bf54c3e089179d9d23900e3efc86d46e4431188d9a657f345410eecdd0151f50",
+ "sha256:bf711d517e21fb5bc429f5c4308fbc430a8585ff2a43e88540264ae87871e36a",
+ "sha256:c00e54f0bd258ab25e7f731ca1d5144b0bf7bec0051abccd2bdcff65fa3262c9",
+ "sha256:c11ca2df2206a4e3e4c4567f52594637392ed05d7c7fb73b4ea1c658ba560265",
+ "sha256:c5f9683be6a5b19cd776ee4e2f2ffb411424819c69afab6b2db3a0a364ec6642",
+ "sha256:cf89ab85027427d351f1de918aff4b43f4eb5f33aff6835ed30322a86ac29c9e",
+ "sha256:d1b750a8409bec61caa7824bfd64a8074b6d2d420433f64c161a8335796c7c6b",
+ "sha256:d779a48fac416387dd5673fc5b2d6bd903ed903faaa3247dc1865c65eaa5a93e",
+ "sha256:d9a1ef0f173e1a19738f154fb3644f90d0ada56fe6c9b422f992b04266c55d5a",
+ "sha256:ddb79414c15c6f03f56cc68fa06994f047cf20207c31b5dad3f6bab54a0f66ef",
+ "sha256:ef00d31b7569ed3cb2036f26565f1984b9fc08541731ce01012b02a4c238bf03",
+ "sha256:f40ac873045db4fd98a6f40387d242bde2708a3f8167bd967ccd43ad46394ba2",
+ "sha256:f593a4a90118d99014517c2679e04a4ef5aee2d81aa05c26c734d271065efcb6",
+ "sha256:f5df76c58977bc35a49515b2fbba84a1d952ff0ec784a4070334dfbec28a2def",
+ "sha256:f72cdd2586f9a769570d4b5714a3837b3a59a53b096bb954f1811f6a0afad305",
+ "sha256:f8e845d894e39fb53834da826078f6dc1a933b32b1478cf437007367efaf6f6a",
+ "sha256:fe6e43c8b510719b48af7db9631b5fbac910ade4bd90e6378c85ac5ac706382c"
],
"markers": "python_version >= '3.8'",
- "version": "==7.4.0"
+ "version": "==7.4.2"
},
"exceptiongroup": {
"hashes": [
@@ -179,37 +175,37 @@
},
"mypy": {
"hashes": [
- "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6",
- "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d",
- "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02",
- "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d",
- "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3",
- "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3",
- "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3",
- "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66",
- "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259",
- "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835",
- "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd",
- "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d",
- "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8",
- "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07",
- "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b",
- "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e",
- "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6",
- "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae",
- "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9",
- "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d",
- "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a",
- "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592",
- "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218",
- "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817",
- "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4",
- "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410",
- "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"
+ "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6",
+ "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913",
+ "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129",
+ "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc",
+ "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974",
+ "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374",
+ "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150",
+ "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03",
+ "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9",
+ "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02",
+ "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89",
+ "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2",
+ "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d",
+ "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3",
+ "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612",
+ "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e",
+ "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3",
+ "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e",
+ "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd",
+ "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04",
+ "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed",
+ "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185",
+ "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf",
+ "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b",
+ "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4",
+ "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f",
+ "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
- "version": "==1.8.0"
+ "version": "==1.9.0"
},
"mypy-extensions": {
"hashes": [
@@ -221,11 +217,11 @@
},
"packaging": {
"hashes": [
- "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
- "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
+ "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
+ "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"
],
"markers": "python_version >= '3.7'",
- "version": "==23.2"
+ "version": "==24.0"
},
"pathspec": {
"hashes": [
@@ -237,28 +233,28 @@
},
"platformdirs": {
"hashes": [
- "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380",
- "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"
+ "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068",
+ "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"
],
"markers": "python_version >= '3.8'",
- "version": "==4.1.0"
+ "version": "==4.2.0"
},
"pluggy": {
"hashes": [
- "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12",
- "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"
+ "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981",
+ "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"
],
"markers": "python_version >= '3.8'",
- "version": "==1.3.0"
+ "version": "==1.4.0"
},
"pytest": {
"hashes": [
- "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280",
- "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"
+ "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7",
+ "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"
],
"index": "pypi",
- "markers": "python_version >= '3.7'",
- "version": "==7.4.4"
+ "markers": "python_version >= '3.8'",
+ "version": "==8.1.1"
},
"pytest-cov": {
"hashes": [
@@ -278,22 +274,30 @@
"markers": "python_version < '3.11'",
"version": "==2.0.1"
},
+ "types-appdirs": {
+ "hashes": [
+ "sha256:337c750e423c40911d389359b4edabe5bbc2cdd5cd0bd0518b71d2839646273b",
+ "sha256:83268da64585361bfa291f8f506a209276212a0497bd37f0512a939b3d69ff14"
+ ],
+ "index": "pypi",
+ "version": "==1.4.3.5"
+ },
"types-mock": {
"hashes": [
- "sha256:13ca379d5710ccb3f18f69ade5b08881874cb83383d8fb49b1d4dac9d5c5d090",
- "sha256:3d116955495935b0bcba14954b38d97e507cd43eca3e3700fc1b8e4f5c6bf2c7"
+ "sha256:0769cb376dfc75b45215619f17a9fd6333d771cc29ce4a38937f060b1e45530f",
+ "sha256:7472797986d83016f96fde7f73577d129b0cd8a8d0b783487a7be330d57ba431"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
- "version": "==5.1.0.20240106"
+ "version": "==5.1.0.20240311"
},
"typing-extensions": {
"hashes": [
- "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783",
- "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"
+ "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475",
+ "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"
],
"markers": "python_version >= '3.8'",
- "version": "==4.9.0"
+ "version": "==4.10.0"
}
}
}
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..6b889f4
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,39 @@
+[build-system]
+requires = ["setuptools>=61.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "ofxstatement"
+version = "0.9.2.dev0"
+authors = [
+ { name="Andrey Lebedev", email="andrey@lebedev.lt" },
+]
+description = "Tool to convert proprietary bank statement to OFX format, suitable for importing to GnuCash"
+readme = "README.rst"
+requires-python = ">=3.9"
+license = { file="LICENSE.txt" }
+classifiers = [
+ "Development Status :: 3 - Alpha",
+ "Programming Language :: Python :: 3",
+ "Natural Language :: English",
+ "Topic :: Office/Business :: Financial :: Accounting",
+ "Topic :: Utilities",
+ "Environment :: Console",
+ "Operating System :: OS Independent",
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+]
+keywords = ["ofx", "banking", "statement"]
+dependencies = [
+ "appdirs>=1.3.0",
+ "importlib_metadata>=3.8;python_version<'3.10'",
+ "zipp;python_version<'3.10'"
+]
+
+[project.urls]
+Homepage = "https://github.com/kedder/ofxstatement"
+
+[project.scripts]
+ofxstatement = "ofxstatement.tool:run"
+
+[tool.setuptools.package-data]
+ofxstatement = ["tests/samples"]
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index c25c1ad..561057d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,51 +1,10 @@
-[metadata]
-name = ofxstatement
-version = 0.9.2.dev0
-description = Tool to convert proprietary bank statement to OFX format, suitable for importing to GnuCash
-long_description = file: README.rst
-long_description_content_type = text/x-rst
-url = https://github.com/kedder/ofxstatement
-author = Andrey Lebedev
-author_email = andrey@lebedev.lt
-classifiers =
- Development Status :: 3 - Alpha
- Programming Language :: Python :: 3
- Natural Language :: English
- Topic :: Office/Business :: Financial :: Accounting
- Topic :: Utilities
- Environment :: Console
- Operating System :: OS Independent
- License :: OSI Approved :: GNU General Public License v3 (GPLv3)
-keywords = ofx banking statement
-project_urls =
-
[options]
package_dir = =src
packages = find:
-python_requires = >=3.8, <4
-install_requires =
- appdirs>=1.3.0
- importlib_metadata>=3.8;python_version<'3.10'
-data_files =
+namespace_packages =
+ ofxstatement
+ ofxstatement.plugins
+
[options.packages.find]
where = src
-
-[options.entry_points]
-console_scripts =
- ofxstatement=ofxstatement.tool:run
-
-[options.extras_require]
-dev =
- black
- mypy
-test =
- mock
- pytest
- pytest-coverage
-
-[mypy]
-ignore_missing_imports = True
-
-[pycodestyle]
-max-line-length=88
diff --git a/src/ofxstatement/__init__.py b/src/ofxstatement/__init__.py
index e69de29..5284146 100644
--- a/src/ofxstatement/__init__.py
+++ b/src/ofxstatement/__init__.py
@@ -0,0 +1 @@
+__import__("pkg_resources").declare_namespace(__name__)
diff --git a/src/ofxstatement/ofx.py b/src/ofxstatement/ofx.py
index 888c7d5..8b034c6 100644
--- a/src/ofxstatement/ofx.py
+++ b/src/ofxstatement/ofx.py
@@ -155,6 +155,8 @@ def buildInvestTransactionList(self) -> None:
for security_id in dict.fromkeys(
map(lambda x: x.security_id, self.statement.invest_lines)
):
+ if security_id is None:
+ continue
tb.start("STOCKINFO", {})
tb.start("SECINFO", {})
tb.start("SECID", {})
@@ -199,12 +201,21 @@ def buildInvestTransactionList(self) -> None:
tb.end("INVSTMTMSGSRSV1")
def buildInvestTransaction(self, line: InvestStatementLine) -> None:
- # invest transactions must always have trntype and trntype_detailed
- if line.trntype is None or line.trntype_detailed is None:
+ # invest transactions must always have trntype
+ if line.trntype is None:
return
tb = self.tb
+ if line.trntype == "INVBANKTRAN":
+ tb.start(line.trntype, {})
+ bankTran = StatementLine(line.id, line.date, line.memo, line.amount)
+ bankTran.trntype = line.trntype_detailed
+ self.buildBankTransaction(bankTran)
+ self.buildText("SUBACCTFUND", "OTHER")
+ tb.end(line.trntype)
+ return
+
tran_type_detailed_tag_name = None
inner_tran_type_tag_name = None
if line.trntype.startswith("BUY"):
@@ -213,6 +224,10 @@ def buildInvestTransaction(self, line: InvestStatementLine) -> None:
elif line.trntype.startswith("SELL"):
tran_type_detailed_tag_name = "SELLTYPE"
inner_tran_type_tag_name = "INVSELL"
+ elif line.trntype == "TRANSFER":
+ # Transfer transactions don't have details or an envelope
+ tran_type_detailed_tag_name = None
+ inner_tran_type_tag_name = None
else:
tran_type_detailed_tag_name = "INCOMETYPE"
inner_tran_type_tag_name = (
@@ -220,7 +235,8 @@ def buildInvestTransaction(self, line: InvestStatementLine) -> None:
)
tb.start(line.trntype, {})
- self.buildText(tran_type_detailed_tag_name, line.trntype_detailed, False)
+ if tran_type_detailed_tag_name:
+ self.buildText(tran_type_detailed_tag_name, line.trntype_detailed, False)
if inner_tran_type_tag_name:
tb.start(inner_tran_type_tag_name, {})
@@ -266,12 +282,7 @@ def buildInvestTransaction(self, line: InvestStatementLine) -> None:
precision=self.invest_transactions_float_precision,
)
- self.buildAmount(
- "TOTAL",
- line.amount,
- False,
- precision=self.invest_transactions_float_precision,
- )
+ self.buildAmount("TOTAL", line.amount)
if inner_tran_type_tag_name:
tb.end(inner_tran_type_tag_name)
diff --git a/src/ofxstatement/parser.py b/src/ofxstatement/parser.py
index e045cf5..1ef47d6 100644
--- a/src/ofxstatement/parser.py
+++ b/src/ofxstatement/parser.py
@@ -1,10 +1,10 @@
-from typing import Dict, Optional, Any, Iterable, List, TextIO, TypeVar, Generic
+from typing import Dict, Optional, Any, Iterable, List, TextIO, TypeVar, Generic, Union
from abc import abstractmethod
import csv
from decimal import Decimal, Decimal as D
from datetime import datetime
-from ofxstatement.statement import Statement, StatementLine
+from ofxstatement.statement import Statement, StatementLine, InvestStatementLine
LT = TypeVar("LT")
@@ -46,7 +46,7 @@ def parse(self) -> Statement:
stmt_line = self.parse_record(line)
if stmt_line:
stmt_line.assert_valid()
- self.statement.lines.append(stmt_line)
+ self.statement.lines.append(stmt_line) # type: ignore
return self.statement
def split_records(self) -> Iterable[LT]: # pragma: no cover
@@ -57,6 +57,12 @@ def parse_record(self, line: LT) -> Optional[StatementLine]: # pragma: no cover
"""Parse given transaction line and return StatementLine object"""
raise NotImplementedError
+ def parse_invest_record(
+ self, line: LT
+ ) -> Optional[InvestStatementLine]: # pragma: no cover
+ """Parse given investement transaction line and return InvetStatementLine object"""
+ raise NotImplementedError
+
def parse_value(self, value: Optional[str], field: str) -> Any:
tp = StatementLine.__annotations__.get(field)
if value is None:
diff --git a/src/ofxstatement/plugin.py b/src/ofxstatement/plugin.py
index 9eaf532..ea81f34 100644
--- a/src/ofxstatement/plugin.py
+++ b/src/ofxstatement/plugin.py
@@ -2,6 +2,7 @@
Plugins are objects that configures and coordinates conversion machinery.
"""
+
from typing import List, Tuple, Type
from collections.abc import MutableMapping
import sys
@@ -21,7 +22,7 @@ def get_plugin(name: str, ui: UI, settings: MutableMapping) -> "Plugin":
raise PluginNotRegistered(name)
if len(plugins) > 1:
raise PluginNameConflict(plugins)
- plugin = plugins[0].load() # type: ignore[index] # index requires a int but class expects a string
+ plugin = plugins[name].load()
return plugin(ui, settings)
diff --git a/src/ofxstatement/plugins/__init__.py b/src/ofxstatement/plugins/__init__.py
index e69de29..5284146 100644
--- a/src/ofxstatement/plugins/__init__.py
+++ b/src/ofxstatement/plugins/__init__.py
@@ -0,0 +1 @@
+__import__("pkg_resources").declare_namespace(__name__)
diff --git a/src/ofxstatement/statement.py b/src/ofxstatement/statement.py
index 922cd87..859b8b7 100644
--- a/src/ofxstatement/statement.py
+++ b/src/ofxstatement/statement.py
@@ -1,4 +1,5 @@
"""Statement model"""
+
from typing import List, Optional
from datetime import datetime
from decimal import Decimal as D
@@ -31,9 +32,11 @@
INVEST_TRANSACTION_TYPES = [
"BUYSTOCK",
"BUYDEBT",
+ "INCOME",
+ "INVBANKTRAN",
"SELLSTOCK",
"SELLDEBT",
- "INCOME",
+ "TRANSFER",
]
INVEST_TRANSACTION_TYPES_DETAILED = [
@@ -43,6 +46,17 @@
"SELLSHORT", # open short sale
"DIV", # only for INCOME
"INTEREST", # only for INCOME
+ "CGLONG", # only for INCOME
+ "CGSHORT", # only for INCOME
+]
+
+INVBANKTRAN_TYPES_DETAILED = [
+ "INT",
+ "XFER",
+ "DEBIT",
+ "CREDIT",
+ "SRVCHG",
+ "OTHER",
]
ACCOUNT_TYPE = [
@@ -274,19 +288,39 @@ def assert_valid(self) -> None:
INVEST_TRANSACTION_TYPES,
)
- assert (
- self.trntype_detailed in INVEST_TRANSACTION_TYPES_DETAILED
- ), "trntype_detailed %s is not valid, must be one of %s" % (
- self.trntype_detailed,
- INVEST_TRANSACTION_TYPES_DETAILED,
- )
+ if self.trntype == "INVBANKTRAN":
+ assert self.trntype_detailed in INVBANKTRAN_TYPES_DETAILED, (
+ "trntype_detailed %s is not valid for INVBANKTRAN, must be one of %s"
+ % (
+ self.trntype_detailed,
+ INVBANKTRAN_TYPES_DETAILED,
+ )
+ )
+ elif self.trntype == "TRANSFER":
+ assert (
+ self.trntype_detailed is None
+ ), f"trntype_detailed '{self.trntype_detailed}' should be empty for TRANSFERS"
+ else:
+ assert (
+ self.trntype_detailed in INVEST_TRANSACTION_TYPES_DETAILED
+ ), "trntype_detailed %s is not valid, must be one of %s" % (
+ self.trntype_detailed,
+ INVEST_TRANSACTION_TYPES_DETAILED,
+ )
assert self.id
- assert self.security_id
- assert self.amount
-
- assert self.trntype == "INCOME" or self.units
- assert self.trntype == "INCOME" or self.unit_price
+ assert self.date
+ assert self.trntype == "TRANSFER" or self.amount
+ assert self.trntype == "INVBANKTRAN" or self.security_id
+
+ if self.trntype == "INVBANKTRAN":
+ pass
+ elif self.trntype == "INCOME":
+ assert self.security_id
+ else:
+ assert self.security_id
+ assert self.units
+ assert self.trntype == "TRANSFER" or self.unit_price
class BankAccount(Printable):
diff --git a/src/ofxstatement/tests/test_ofx_invest.py b/src/ofxstatement/tests/test_ofx_invest.py
index 433a5af..82ba15b 100644
--- a/src/ofxstatement/tests/test_ofx_invest.py
+++ b/src/ofxstatement/tests/test_ofx_invest.py
@@ -88,7 +88,7 @@
1.24000
138.28000
3.00000
- -416.08000
+ -416.08
@@ -108,7 +108,7 @@
0.28000
225.63000
-5.00000
- 1127.87000
+ 1127.87
@@ -125,8 +125,33 @@
OTHER
OTHER
0.50000
- 0.79000
+ 0.79
+
+
+ INT
+ 20210102
+ 0.45
+ 6
+ Bank Interest
+
+ OTHER
+
+
+
+ 7
+ 20210103
+ Journaled Shares
+
+
+ MSFT
+ TICKER
+
+ OTHER
+ OTHER
+ 225.63000
+ 4.00000
+
@@ -190,6 +215,22 @@ def test_ofxWriter(self) -> None:
invest_line.assert_valid()
statement.invest_lines.append(invest_line)
+ invest_line = InvestStatementLine(
+ "6", datetime(2021, 1, 2), "Bank Interest", "INVBANKTRAN", "INT"
+ )
+ invest_line.amount = Decimal("0.45")
+ invest_line.assert_valid()
+ statement.invest_lines.append(invest_line)
+
+ invest_line = InvestStatementLine(
+ "7", datetime(2021, 1, 3), "Journaled Shares", "TRANSFER"
+ )
+ invest_line.security_id = "MSFT"
+ invest_line.units = Decimal("4")
+ invest_line.unit_price = Decimal("225.63")
+ invest_line.assert_valid()
+ statement.invest_lines.append(invest_line)
+
# Create writer:
writer = ofx.OfxWriter(statement)
diff --git a/src/ofxstatement/tests/test_plugin.py b/src/ofxstatement/tests/test_plugin.py
index 34618d1..2d156de 100644
--- a/src/ofxstatement/tests/test_plugin.py
+++ b/src/ofxstatement/tests/test_plugin.py
@@ -2,6 +2,13 @@
import mock
+import sys
+
+if sys.version_info < (3, 10):
+ from importlib_metadata import EntryPoints
+else:
+ from importlib.metadata import EntryPoints
+
from ofxstatement import plugin
@@ -13,7 +20,9 @@ def get_parser(self):
ep = mock.Mock()
ep.load.return_value = SamplePlugin
- ep_patch = mock.patch("ofxstatement.plugin.entry_points", return_value=[ep])
+ ep_patch = mock.patch(
+ "ofxstatement.plugin.entry_points", return_value=EntryPoints([ep])
+ )
with ep_patch:
p = plugin.get_plugin("sample", mock.Mock("UI"), mock.Mock("Settings"))
self.assertIsInstance(p, SamplePlugin)
@@ -21,7 +30,9 @@ def get_parser(self):
def test_get_plugin_conflict(self) -> None:
ep = mock.Mock()
- ep_patch = mock.patch("ofxstatement.plugin.entry_points", return_value=[ep, ep])
+ ep_patch = mock.patch(
+ "ofxstatement.plugin.entry_points", return_value=EntryPoints([ep, ep])
+ )
with ep_patch:
with self.assertRaises(plugin.PluginNameConflict):
plugin.get_plugin("conflicting", mock.Mock("UI"), mock.Mock("Settings"))
diff --git a/src/ofxstatement/tests/test_statement.py b/src/ofxstatement/tests/test_statement.py
index 730bc94..cb63320 100644
--- a/src/ofxstatement/tests/test_statement.py
+++ b/src/ofxstatement/tests/test_statement.py
@@ -50,3 +50,88 @@ def test_generate_unique_transaction_id(self) -> None:
self.assertTrue(tid2.endswith("-1"))
self.assertEqual(len(txnids), 2)
+
+ def test_transfer_line_validation(self) -> None:
+ line = statement.InvestStatementLine("id", datetime(2020, 3, 25))
+ line.trntype = "TRANSFER"
+ line.security_id = "ABC"
+ line.units = Decimal(2)
+ line.assert_valid()
+ with self.assertRaises(AssertionError):
+ line.security_id = None
+ line.assert_valid()
+ line.security_id = "ABC"
+ with self.assertRaises(AssertionError):
+ line.units = None
+ line.assert_valid()
+ line.units = Decimal(2)
+ with self.assertRaises(AssertionError):
+ line.trntype_detailed = "DETAIL"
+ line.assert_valid()
+
+ def test_invbank_line_validation(self) -> None:
+ line = statement.InvestStatementLine("id", datetime(2020, 3, 25))
+ line.trntype = "INVBANKTRAN"
+ line.trntype_detailed = "INT"
+ line.amount = Decimal(1)
+ line.assert_valid()
+ with self.assertRaises(AssertionError):
+ line.amount = None
+ line.assert_valid()
+ line.amount = Decimal(1)
+ with self.assertRaises(AssertionError):
+ line.trntype_detailed = "BLAH"
+ line.assert_valid()
+
+ def test_income_line_validation(self) -> None:
+ line = statement.InvestStatementLine("id", datetime(2020, 3, 25))
+ line.trntype = "INCOME"
+ line.trntype_detailed = "INTEREST"
+ line.amount = Decimal(1)
+ line.security_id = "AAPL"
+ line.assert_valid()
+ with self.assertRaises(AssertionError):
+ line.amount = None
+ line.assert_valid()
+ line.amount = Decimal(1)
+ with self.assertRaises(AssertionError):
+ line.trntype_detailed = "BLAH"
+ line.assert_valid()
+ line.trntype_detailed = "INTEREST"
+ with self.assertRaises(AssertionError):
+ line.security_id = None
+ line.assert_valid()
+
+ def test_buy_line_validation(self) -> None:
+ line = statement.InvestStatementLine("id", datetime(2020, 3, 25))
+ line.trntype = "BUYSTOCK"
+ line.trntype_detailed = "BUY"
+ line.amount = Decimal(1)
+ line.security_id = "AAPL"
+ line.units = Decimal(3)
+ line.unit_price = Decimal(1.1)
+ line.assert_valid()
+
+ with self.assertRaises(AssertionError):
+ line.amount = None
+ line.assert_valid()
+ line.amount = Decimal(1)
+
+ with self.assertRaises(AssertionError):
+ line.trntype_detailed = "BLAH"
+ line.assert_valid()
+ line.trntype_detailed = "INTEREST"
+
+ with self.assertRaises(AssertionError):
+ line.security_id = None
+ line.assert_valid()
+ line.security_id = "AAPL"
+
+ with self.assertRaises(AssertionError):
+ line.units = None
+ line.assert_valid()
+ line.units = Decimal(3)
+
+ with self.assertRaises(AssertionError):
+ line.unit_price = None
+ line.assert_valid()
diff --git a/src/ofxstatement/tool.py b/src/ofxstatement/tool.py
index 90bd0b6..4fdfc7d 100644
--- a/src/ofxstatement/tool.py
+++ b/src/ofxstatement/tool.py
@@ -1,5 +1,6 @@
"""Command line tool for converting statements to OFX format
"""
+
import os
import argparse
import shlex