././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1631966929.381493 febelfin-coda-0.2.0/0000755000175000017500000000000000000000000012240 5ustar00cedced././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966909.0 febelfin-coda-0.2.0/.hgtags0000644000175000017500000000013600000000000013516 0ustar00cedcedb61708f8591a1c32aa28747d0c7313404b4f88ac 0.1.0 c615e651a0f1b767ff54981fd1b51d39e38bb517 0.2.0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966849.0 febelfin-coda-0.2.0/CHANGELOG0000644000175000017500000000035000000000000013450 0ustar00cedcedVersion 0.2.0 - 2021-09-18 * Remove support for Python older than 3.5 * Add support for Python 3.9 * Add support for Python 3.8 * Add support for Python 3.7 * Add support for Python 3.6 Version 0.1.0 - 2017-09-22 * Initial release ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966849.0 febelfin-coda-0.2.0/COPYRIGHT0000644000175000017500000000277700000000000013550 0ustar00cedcedCopyright (c) 2017-2021, Cédric Krier Copyright (c) 2017-2021, B2CK All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966543.0 febelfin-coda-0.2.0/MANIFEST.in0000644000175000017500000000011100000000000013767 0ustar00cedcedinclude COPYRIGHT include README include CHANGELOG include coda/CODA.txt ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1631966929.381493 febelfin-coda-0.2.0/PKG-INFO0000644000175000017500000000473200000000000013343 0ustar00cedcedMetadata-Version: 1.2 Name: febelfin-coda Version: 0.2.0 Summary: A module to parse CODA files Home-page: https://coda.b2ck.com/ Author: B2CK Author-email: info@b2ck.com License: BSD Description: febelfin-coda ============= febelfin-coda is a parser for `CODA files`_. .. _CODA files: https://www.febelfin.be/sites/default/files/2019-04/standard-coda-2.6-en.pdf Nutshell -------- Import:: >>> import os >>> from coda import CODA Instantiate:: >>> coda = CODA('coda/CODA.txt') The statements:: >>> len(coda.statements) 1 >>> statement, = coda.statements >>> statement.account '435000000080' >>> statement.account_currency 'EUR' >>> statement.old_balance Decimal('0') >>> statement.old_balance_date datetime.date(2006, 12, 6) >>> statement.new_balance Decimal('9405296.99') >>> statement.new_balance_date datetime.date(2006, 12, 7) The transactions:: >>> len(statement.moves) 59 >>> move = statement.moves[0] >>> move.value_date datetime.date(2006, 12, 6) >>> move.entry_date datetime.date(2006, 12, 6) >>> move.amount Decimal('-2578.25') >>> move.bank_reference 'EPIB00048 AWIUBTKAPUO' >>> move.transaction_code '00799000' >>> move.communication "BORDEREAU DE DECOMPTE AVANCES 015 NUMERO D'OPERATION 495953" To report issues please visit the `coda bugtracker`_. .. _coda bugtracker: https://coda.b2ck.com/ Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Topic :: Office/Business Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Utilities Requires-Python: >=3.5 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1560353422.0 febelfin-coda-0.2.0/README0000644000175000017500000000235100000000000013121 0ustar00cedcedfebelfin-coda ============= febelfin-coda is a parser for `CODA files`_. .. _CODA files: https://www.febelfin.be/sites/default/files/2019-04/standard-coda-2.6-en.pdf Nutshell -------- Import:: >>> import os >>> from coda import CODA Instantiate:: >>> coda = CODA('coda/CODA.txt') The statements:: >>> len(coda.statements) 1 >>> statement, = coda.statements >>> statement.account '435000000080' >>> statement.account_currency 'EUR' >>> statement.old_balance Decimal('0') >>> statement.old_balance_date datetime.date(2006, 12, 6) >>> statement.new_balance Decimal('9405296.99') >>> statement.new_balance_date datetime.date(2006, 12, 7) The transactions:: >>> len(statement.moves) 59 >>> move = statement.moves[0] >>> move.value_date datetime.date(2006, 12, 6) >>> move.entry_date datetime.date(2006, 12, 6) >>> move.amount Decimal('-2578.25') >>> move.bank_reference 'EPIB00048 AWIUBTKAPUO' >>> move.transaction_code '00799000' >>> move.communication "BORDEREAU DE DECOMPTE AVANCES 015 NUMERO D'OPERATION 495953" To report issues please visit the `coda bugtracker`_. .. _coda bugtracker: https://coda.b2ck.com/ ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1631966929.381493 febelfin-coda-0.2.0/coda/0000755000175000017500000000000000000000000013146 5ustar00cedced././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1534592110.0 febelfin-coda-0.2.0/coda/CODA.txt0000644000175000017500000010200600000000000014414 0ustar00cedced0000006120672505 00099449 Testgebruiker21 KREDBEBB 00630366277 00000 2 10001435000000080 EUR0BE 0000000000000000061206Testgebruiker21 KBC-Bedrijfsrekening 001 2100010000EPIB00048 AWIUBTKAPUO1000000002578250061206007990000BORDEREAU DE DECOMPTE AVANCES 015 NUMERO D'OPERATI06120600111 0 2200010000ON 495953 0 0 2100020000INID00243 AWIVVDRNTSO0000000000011210061206307870000AFREKENINGSBORDEREL VOORSCHOTTEN 014 OPERATIENUMMER 06120600111 0 2200020000 491820 0 0 2100020001INID00243 AWIVVDRNTSO0000000000011210061206807870020 06120600110 0 2100030000OL9456574JBBNEUBCRCL10000000001075000061206341500000/INV/2061260 06120600111 0 2200030000 00843246306A16481 0 2300030000LU037050522702273100 Olgerdin Egill Skallagrims 0 1 3100030001OL9456574JBBNEUBCRCL1341500001001Olgerdin Egill Skallagrims 1 0 3200030001Grjothalsi 7 11110 Reykjavik 0 0 2100030002OL9456574JBBNEUBCRCL10000000001075000061206841501001105000000001075000000000001075000000100000000EUR 06120600111 0 2200030002 000000001075000 0 0 2100040000OL7254378 BCCHRSTGKOS0000000000030860061206343870000GEDEELTELIJKE TERUGGAVE KOSTEN :CHEQ 290906-EUR755,8006120600111 0 2200040000 NB2206092900135 H206102500000180 0 2100040001OL7254378 BCCHRSTGKOS0000000000023000061206843870150 06120600100 0 2100040002OL7254378 BCCHRSTGKOS0000000000002500061206843870040 06120600100 0 2100040003OL7254378 BCCHRSTGKOS00000000000053600612068438701111060000000000053600000000000255000021000000002000000006120600111 0 220004000300005360 0 0 2100050000OL7252793 BCCHRSXTKOS1000000000010610061206343370000CHARGES CHEQUE GBP 1400 BRITISH HEART FOUNDATION 06120600111 0 2200050000 H506080300000100 0 2100050001OL7252793 BCCHRSXTKOS1000000000008770061206843370150 06120600100 0 2100050002OL7252793 BCCHRSXTKOS10000000000018400612068433701111060000000000018400000000000087700021000000002000000006120600111 0 220005000200001840 0 0 2100060000OL4754532 BELCTOCOMFG0000000002500000061206001500000RISTOURNE COMMERCIALE FRAIS D'ENTREE CBC FONDS REVERS06120600101 0 2200060000E SPREAD 1 CREGBEBB 1 0 2300060000000000000000 CBC BANQUE S.A. 0 1 3100060001OL4754532 BELCTOCOMFG01500001001CBC BANQUE S.A 0 0 2100070000IJUF00068 BHKDGLDTBIC1000000128000000061206101130000 06120600111 0 2200070000 CASHTPF0132 0 0 2100070001IHMI00001 TBOGOVOVERS1000000004000000061206501130000 06120600101 0 2200070001 CASHTPF0132 1 0 2300070001191038188256 ECONOLER S.A. 0 1 3100070002IHMI00001 TBOGOVOVERS501130001001ECONOLER S.A. 1 0 3200070002AV. DE HAVESKERCKE 46 1190 BRUXELLES 0 0 2100070003IHMI00002 TBOGOVOVERS1000000027000000061206501130000 06120600101 0 2200070003 CASHTPF0132 1 0 2300070003068226750863 T.P.F. S.A. 0 1 3100070004IHMI00001 TBOGOVOVERS501130001001T.P.F. S.A. 1 0 3200070004AV. DE HAVESKERCKE 46 1190 BRUXELLES 0 0 2100070005IHMI00003 TBOGOVOVERS1000000097000000061206501130000 06120600111 0 2200070005 CASHTPF0132 1 0 2300070005191039349226 TPF CONSULTING 0 1 3100070006IHMI00001 TBOGOVOVERS501130001001TPF CONSULTING 1 0 3200070006AV. DE HAVESKERCKE 46 1190 BRUXELLES 0 0 2100080000IQQQ00412 BHKDGLDTBLO1000000002782570061206101050000 06120600110 0 2100080001IPTE00001 TBOSOCOVERS1000000000747690061206501050000311006 BEZOLDIGING 06120600101 0 2200080001 951 0010000000038 1 0 2300080001393064679386 JANSSENS TUUR 0 1 3100080002IPTE00001 TBOSOCOVERS501050001001JANSSENS TUUR 1 0 3200080002DR COCHEZSTRAAT 27 9470 DENDERLEEUW 0 0 2100080003IPTE00002 TBOSOCOVERS1000000000565640061206501050000311006 BEZOLDIGING 06120600101 0 2200080003 952 0010000000041 1 0 2300080003001101815623 MONTEO CARLOS 0 1 3100080004IPTE00002 TBOSOCOVERS501050001001MONTEO CARLOS 1 0 3200080004RUE J LAHAYE 288 BTE 131 1090 JETTE 0 0 2100080005IPTE00003 TBOSOCOVERS1000000000588630061206501050000311006 BEZOLDIGING 06120600101 0 2200080005 953 0010000000042 1 0 2300080005429012832102 VANBUITEN GUIDO 0 1 3100080006IPTE00003 TBOSOCOVERS501050001001VANBUITEN GUIDO 1 0 3200080006GANZEWEG 11 1730 MOLLEM 0 0 2100080007IPTE00004 TBOSOCOVERS1000000000880610061206501050000311006 BEZOLDIGING 06120600101 0 2200080007 954 0010000000043 1 0 2300080007001125786444 FLIPKENS MARIA 0 1 3100080008IPTE00004 TBOSOCOVERS501050001001FLIPKENS MARIA 1 0 3200080008HULSTSTRAAT 9 9620 ZOTTEGEM 0 0 2100090000IHBA0005X BKTBBNPOSKC000000000009500006120600450000111196023984130095830111060095850111068MIRANO CONTINEN06120600101 0 2200090000TBRUXELLES 0 0 2100100000IHIG00001HBKTCOLBETHL000000000016317006120600468000111195202570100000002909060000000111060POLYCLINIQUE 06120600101 0 2200100000 BRUXELLES 0 0 2100110000OL4743165 BKTCTOKMBTW0000000000065610061206001500000TUSSENKOMST KOSTEN TANKKAARTEN - DOSSIER BET20061013606120600101 0 220011000071 0 0 2100120000NBHK00001 BKTHRSBECLG00000000001800000612060049900011137372171950661206000000000000029060600004 06120600101 0 2200120000 000000000000000000000000000 1 0 2300120000666000000180 0 0 2100130000OL4713253 BKTHRSCEXTR1000000000104230061206004490000BANKSYSDOSSIER D0500285 06120600101 0 2200130000 SR 631472 1 0 2300130000001106249937 0 0 2100140000CXQZA0AAL BKTKOSKOBFG1000000000002270061206304370000FRAIS CARTE BANCAIRE 199-9141991-24 3213 06120600110 0 2100140001CXQZA0AAL BKTKOSKOBFG1000000000001880061206804370060 06120600100 0 2100140002CXQZA0AAL BKTKOSKOBFG10000000000003900612068043701111060000000000003900000000000018800021000000002000000006120600111 0 220014000200000000 0 0 2100150000OL1DBB0YHGBKTOTBBEPOS10000000000217000612060040200011131910317041110214172454663076602110614287CORA LA LO06120600101 0 2200150000UVIERELA LOUVIER000000000000000000000000000 0 0 2100160000OL1DE8LYE BKTOTBBEPRO10000000001000000612060040500011131913140862650511000000000000002110600000BANQUE BRA06120600101 0 2200160000BANT BRUXELLES 00000000000000000000000000 000000000000 0 0 2100170000OL4769382BBKTTBKTBCLG0000000000008500061206004990000 1232 R0760043 06120600100 0 2100180000IVEV00021 BKTUBBBECLG10000000003000000612060040400011131919283001670926925000700867831100614020BPVF 06120600101 0 2200180000 000000000300000000100000000EUR0000000 1 0 2300180000 00000 0 0 2100190000VOPB03739 BNKHRSRIDRO1000000000004410061206335010000CORRECTION REFUND OF DEBIT INTEREST AS AT 30/06120600111 0 220019000009/2006 FOR ACCOUNT 191-0510341-87 IN EUR 000000000000000 0 0 2100190001VOPB03739 BNKHRSRIDRO1000000000004410061206835010020 06120600110 0 2100200000IGQG000014BNKHRSRNTAG0000000000001260061206335990000CALCUL COMPLEMENTAIRE DES INTERETS AU 31/10/2006 D06120600111 0 2200200000U COMPTE 191-0179633-52 EN EUR 0 0 2100200001IGQG000014BNKHRSRNTAG0000000000001260061206835990010 06120600110 0 2100210000OL7253698 BWIHRSDIHRS0000000000160500061206047990000H20609190000018 06120600101 0 2200210000 NB32060825001581 0 2300210000192210170245 0 0 2100220000OL7253991 BWIHRSDIHRS1000000049000000061206047490000H50610050000013 06120600100 0 2100230000OL4766446 CHQCORCCLKT1000000000000980061206003490000HERSTELLING CHEQUE NR 538428 GEBOEKT OP 070706 VOOR 106120600101 0 2200230000376 EUR IPV 1376,98 EUR 73073338A4EQ3 0 0 2100240000OL4718947 CHQFAKHAMAC0000000000006050061206003990000TERUGGAVE KOSTEN AANMAAK BANKCHEQUE NR 644701 06120600100 0 2100250000OL47991625DOMHRSINVCD0000000000091000061206005990000ONUITV GEDOM TERUGBET DOMNR 738-3021430-96 ONBESTAAND06120600101 0 2200250000 : MED :700400186181 REF2152029619 DD23/8 0 0 2100260000OL4760675 DOMHRSINVCD1000000000495790061206005030000TEGENBOEKING DOMICILIERING DOMNR 740-3223388-58 REEDS06120600101 0 2200260000 BETAALD VIA OVERSCHR. 0 0 2100270000DWES00006 EMSITAKPIEO00000120646986700612063119900001795EO763445 06120600110 0 2100270001DWES00006 EMSITAKPIEO0000012004200000061206811991000 06120600100 0 2100270002DWES00006 EMSITAKPIEO0000000060498670061206811990020 06120600110 0 2100280000EVCP00474 EMSITKKPIEG1000002501250000061206311030000 06120600110 1 3100280001EVCP00474 EMSITKKPIEG311030001010EO767357 727973443458 02XS0271580481 N00000000000 1 0 32002800010 0001EUR000001000500000100000000PEMBRIDGE SQ CDO CL F 06 VAR 070117 1795 00000000 1 0 3300280001 20102006intekening 000002500000000 0 0 2100280002EVCP00474 EMSITKKPIEG1000002501250000061206811031000 06120600110 0 2100290000IVEE00331 EMSITKKPIEG1000000010678030061206311010000 06120600110 1 3100290001IVEE00331 EMSITKKPIEG311010001010EO768512 725135970661 02SE0000459539 N00000000000 1 0 32002900010 0001EUR000001029000000100000000ZWEDEN 98 EURO 05,00 280109 1795 00000000 1 0 3300290001 30102006achat 000000010000000 0 0 2100290002IVEE00331 EMSITKKPIEG1000000010290000061206811011000 06120600100 0 2100290003IVEE00331 EMSITKKPIEG1000000000007470061206811014270 06120600100 0 2100290004IVEE00331 EMSITKKPIEG1000000000380560061206811010020 06120600110 0 2100300000ZTCU00147 EMSVKAKPUEO10000003027107500612063114900001795EO764997 06120600110 0 2100300001ZTCU00147 EMSVKAKPUEO1000000300000000061206811491000 06120600100 0 2100300002ZTCU00147 EMSVKAKPUEO1000000002710750061206811490010 06120600110 0 2100310000OL7040394 KGDABDVCBDG0000000001475940061206009990000Uw storting beschadigde deviezen 53213471890 06120600100 0 2100320000BVGB00596LKKTOVSOVKUG10000000001208500612060040300011245526145483651201 1420 06120600101 0 2300320000666000000483 0 0 2100330000OL4798969 KRDHRSFEESO00000000093445000612060139900008300759-81 GUTENBERG SA CORRECTION COMMI06120600101 0 2200330000TMENT FEE 2E TRIMESTRE 2006 0 0 2100340000AFTP00004 NCPHRSRNTAG0000000000000030061206335500000196-0285402-83 EUR 06120600110 0 2100340001AFTP00004 NCPHRSRNTAG0000000000000030061206835500020 06120600110 0 2100350000UWCCA0AFR OTFFAKABONF1000000000024200061206380020000KOSTEN ELEKTRONISCHE BERICHTGEVING via KBC@Isab06120600111 0 2200350000el VAN 01/07/2006 TOT 30/09/2006 0 0 2100350001UWCCA0AFR OTFFAKABONF1000000000020000061206880330060 06120600100 0 2100350002UWCCA0AFR OTFFAKABONF10000000000042000612068803301111060000000000042000000000000200000021000000002000000006120600111 0 220035000200000000 0 0 2100360000IGYV00026 TK1TBNINNIG1000000000279500061206013010000PAIEMENT CREDIT 728-0379193-58 06120600101 0 2300360000728037919358 0 0 2100370000OL4729981 TMNHRSTOTUO1000000056003590061206030490000ANNULATION PLACEMENT 004018282 06120600101 0 2300370000728051142478 002WR2 CARNIPOR SA 0 1 3100370001OL4729981 TMNHRSTOTUO030490001001CARNIPOR SA 1 0 3200370001RUE DE LA STATION 47 4852HOMBOURG 0 0 2100380000OL9483338IUBOBPOTRFCO1000000000321040061206341010000AUTOROUTE SANEF FAKT NO F0337604812 06120600111 0 2200380000FAKT DECEMBER 2005 SANEF NORD 832856027A00011 0 2300380000 AUTOROUTE BANQUE 0 1 3100380001OL9483338IUBOBPOTRFCO341010001001AUTOROUTE BANQUE 1 0 3200380001AUTOROUTE SANEF NORD 59023 LILLE FR 0 0 2100380002OL9483338IUBOBPOTRFCO1000000000321040061206841011001105000000000321040000000000321040000100000000EUR 06120600111 0 2200380002 000000000321040 0 0 2100390000OL9001989 UBOHRSINTEG000000000013050006120604149000000224476304A0005 06120600101 0 2200390000 00352036300B8687 0 0 2100400000OL9433561JBBOEUBCRECL0000000002448000061206341500000C.0609/717, 06120600111 0 2200400000 019938863058283A KREDNL2XXXX00843246305A02961 0 2300400000NL34KRED0633083542 HOLLANDSE INVESTERINGSMAATSCHAPPIJ 0 1 3100400001OL9433561JBBOEUBCRECL341500001001HOLLANDSE INVESTERINGSMAATSCHAPPIJ AFDELING NOORD-BRABANT 1 0 3200400001POSTBUS 998 5600 EINDHOVEN 0 0 2100400002OL9433561JBBOEUBCRECL0000000002448000061206841501001105000000002448000000000002448000000100000000EUR 06120600111 0 2200400002 000000002448000 0 0 2100410000OL9433967KBBOKOSDIVKS1000000000010890061206341370000 06120600111 0 2200410000 00843246305A02960 0 2100410001OL9433967KBBOKOSDIVKS1000000000009000061206841370130 06120600100 0 2100410002OL9433967KBBOKOSDIVKS10000000000018900612068413701111060000000000018900000000000090000021000000002000000006120600111 0 220041000200001890 0 0 2100420000OL9442776JBBOTRFCRECL0000000006730240061206341500000INVOICE NO.. 2006/66992 06120600111 0 2200420000 7906ULA302330 NDEANOKKXXX00843246305A00241 0 2300420000NO1160060431712 TITANIA A/S 0 1 3100420001OL9442776JBBOTRFCRECL341500001001TITANIA A/S 1 0 3200420001 4380 HAUGE I DALANE 0 0 2100420002OL9442776JBBOTRFCRECL0000000006730240061206841501001105000000006730240000000056059600000832950600NOK 06120600111 0 2200420002 000000006730240 0 0 2100430000IJUF01356 BHKDGLPOSKV0000000001705250061206204500000OPERATIONS TERMINAL DE PAIEMENT 06120600110 0 2100430001IHBC0001H BKTBBNPOSG10000000000600000061206604500001114935304718600683531100611018ETS STEYLEMANS BRUXELL06120600101 0 2200430001ES 0507775191760001 0 0 2100430002IHBC0001I BKTBBNPOSG10000000001080000061206604500001114935304718600683631100611028ETS STEYLEMANS BRUXELL06120600101 0 2200430002ES 0507775191760001 0 0 2100430003IHBC0001J BKTBBNPOSG10000000000025250061206604500001114935304718600683731100612048ETS STEYLEMANS BRUXELL06120600111 0 2200430003ES 0503797159140026 0 0 2100440000IJUF01359 BHKDGLPOSKV0000000000180000061206204500000OPERATIONS TERMINAL DE PAIEMENT 06120600110 0 2100440001IHBC0001X BKTBBNPOSG2000000000017300006120660450000111190649479270021833110060021863110068TIME SQUARE 06120600101 0 2200440001 MONS 0 0 2100440002IHBC0001Y BKTBBNPOSG2000000000000700006120660450000111190649479270021873110060021873110068TIME SQUARE 06120600111 0 2200440002 MONS 0 0 2100450000IHBA00004 BKTBBNPOSKZ0000000000642200061206004500001114901704113200287431100617188VIATOR LESSINE06120600101 0 2200450000S 260583 0 0 2100460000AUYN00001GBKTUBNBEPOS10000000000367200612060040600011137291975120230119102778900456712100612189HURKMANS P06120600101 0 2200460000ETROL.HASSELT 000000000000000000000000000 0300006 1 0 2300460000335055422210 01224 0 0 2100470000IGNV03125 BODASMAKBBG1000000006509350061206311010000179206278287 06120600110 1 3100470001IGNV03125 BODASMAKBBG31101000101006278287 729134436660 02BE0003790079 P00000100000 1 0 32004700010EUR0001EUR000000645000000100000000BARCO 1792 00000000E 1 0 3300470001uronext Bruxelles 31102006achat 000000000000000 0 0 2100470002IGNV03125 BODASMAKBBG1000000006450000061206811011000 06120600100 0 2100470003IGNV03125 BODASMAKBBG1000000000010970061206811014270 06120600100 0 2100470004IGNV03125 BODASMAKBBG1000000000048380061206811014260 06120600110 0 2100480000ITARA0AG64DOMDCDDID021000000000021940061206005030001107740316202802031106436769 1001987517 1006120600101 0 22004800000202239951 0020061023 0 0 2100490000OL7040373EKGDSTNSTABG00000000008100000612060045300011157294582459985245100058901894704100613480410065864906120600101 0 22004900002000000000810000 KBC BANK TIELT TIELT 0000000020 1 0 2300490000 20 0 1 3100490001OL7040373EKGDSTNSTABG0045300010070000001010000000000000010000 0 1 3100490002OL7040373EKGDSTNSTABG0045300010070000001100000000000000100000 0 1 3100490003OL7040373EKGDSTNSTABG0045300010070000001200000000000000200000 0 1 3100490004OL7040373EKGDSTNSTABG0045300010070000001500000000000000500000 0 0 2100500000BVGB00596LKKTOVSOVKUG10000000001208500612060040300011245526145483651201 1420 06120600101 0 2300500000666000000483 0 0 2100510000IHAB005Y4 OSICOUAFREG0000000000139200061206311520000 06120600110 1 3100510001IHAB005Y4 OSICOUAFREG3115200010110000018067684729165843038 02US92343V1044 000008500000 1 0 3200510001VERIZON COMMUNICATIONS ACTION USD000000004050001000000000080850coupon 01 1 0 33005100011106000078284800EUR000000000000000 0 0 2100510002IHAB005Y4 OSICOUAFREG0000000000269500061206811521000OSCP000000018067684 06120600100 0 2100510003IHAB005Y4 OSICOUAFREG1000000000080850061206811524290 06120600100 0 2100510004IHAB005Y4 OSICOUAFREG10000000000471600612068115205111060000000000471600000000001886400025000000002000000006120600101 0 220051000400047160 0 0 2100510005IHAB005Y4 OSICOUAFREG1000000000001890061206811520140 06120600100 0 2100510006IHAB005Y4 OSICOUAFREG10000000000004000612068115201111060000000000004000000000000018900021000000002000000006120600111 0 220051000600000400 0 0 2100520000IHFF00001BOVSBBNONTVA0000000010239930061206001500000R:0298001 24/10 AX 9411001225 T: 9 L:NA 06120600101 0 2200520000BRT: 10502,50 C: 262,57 A: 0,00 D:311006 1 0 2300520000310159253878 AMERICAN EXPRESS 0 1 3100520001IHFF00001BOVSBBNONTVA001500001001AMERICAN EXPRESS 1 0 3200520001BD.SOUVERAIN/VORSTLAAN 100 1170BRUSSELS/BRUXELLES 0 0 2100530000IKKUZ0AAAAOVSBBNONTVA0000000000817560061206001500001101269021157996 06120600101 0 2300530000370121620105 LA CROIX D OR SPRL 0 1 3100530001IKKUZ0AAAAOVSBBNONTVA001500001001LA CROIX D OR SPRL 1 0 3200530001RUE FELIX MAIGRET 7 7030 ST-SYMPHORIEN 0 0 2100540000IQQRZ0ASR TBOGOVOVERS000000038725882006120600199000086047442,86047472,86047438,86047447,86047452,8604746106120600101 0 2200540000 0002261314 34000112 1 0 2300540000685576703767 CARGILL NV 0 1 3100540001IQQRZ0ASR TBOGOVOVERS001990001001CARGILL NV 1 0 3200540001PRINS ALBERTLAAN 12 8870 IZEGEM 0 0 2100550000FTBV00082ATBOSOCOVERS0000000000159590061206001500001101702600521948 06120600101 0 2300550000440040179189 VERZ.KAS ARBEIDSON.SECUREX 0 1 3100550001FTBV00082ATBOSOCOVERS001500001001VERZ.KAS ARBEIDSON.SECUREX 1 0 3200550001VERENIGDE-NATIESLAAN 1 9000 GENT 0 0 2100560000OL7496335 TTKTTKTTAUT10000000005000000612060040400011131921033602120312200526500000002110600001CBC BANQUE06120600101 0 2200560000 ENGHIENGHIEN 000000000500000000100000000EUR000000000000 0 0 2100570000OL9400612IUBOBENTRFCB1000000023846320061206341010000 06120600111 0 2200570000 0000000000005584 KREDGB2XXXX00352036318C12421 0 230057000003056891 DUFERCO SP.STEELS EUR.SA 0 1 3100570001OL9400612IUBOBENTRFCB341010001001DUFERCO SP.STEELS EUR.SA . 1 0 3200570001 NL 0 1 3100570002OL9400612IUBOBENTRFCB341010001004GARANTIBANK INTERNATIONAL NV 0 0 2100570003OL9400612IUBOBENTRFCB1000000023846320061206841011001105000000023846320000000023846320000100000000GBP 06120600111 0 2200570003 000000035259970 0 0 2100580000OL9416622IUBOEUBTRFCS1000000000001960061206341010000LISTE DIFFUSION ISAAC DEVIS 1300129844D DU 13/10/06 06120600111 0 2200580000 KREDFRPPXXX00843246304A23251 0 23005800000000000000000278004000106589380301 AB CONNECT SARL 0 1 3100580001OL9416622IUBOEUBTRFCS341010001001AB CONNECT SARL 1 0 320058000121 RUE DES VERTUS 13005 MARSEILLE FR 0 1 3100580002OL9416622IUBOEUBTRFCS341010001004SOCIETE GENERALE 0 0 2100580003OL9416622IUBOEUBTRFCS1000000000001960061206841011001105000000000001960000000000001960000100000000EUR 06120600111 0 2200580003 000000000001960 0 0 2100590000OL9462125KUBOKOSDIVKS1000000000034800061206341370000 06120600111 0 2200590000 00843246304A23250 0 2100590001OL9462125KUBOKOSDIVKS1000000000022310061206841370150 06120600100 0 2100590002OL9462125KUBOKOSDIVKS1000000000006450061206841370130 06120600100 0 2100590003OL9462125KUBOKOSDIVKS10000000000060400612068413701111060000000000060400000000000287600021000000002000000006120600111 0 220059000300006040 0 0 8001435000000080 EUR0BE 0000009405296990071206 0 9 000260000003085871600000012491168590 2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966883.0 febelfin-coda-0.2.0/coda/__init__.py0000644000175000017500000003667700000000000015302 0ustar00cedced# This file is part of febelfin-coda. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. """a parser for CODA files """ import io from collections import defaultdict from datetime import datetime from decimal import Decimal __version__ = '0.2.0' __all__ = ['CODA', 'Statement', 'Move', 'Information', 'FreeCommunication'] class CODA(object): def __init__(self, name, encoding='windows-1252'): self.statements = [] if isinstance(name, (bytes, str)): with io.open(name, encoding=encoding, mode='r') as f: self._parse(f) else: self._parse(name) def _parse(self, f): statement = None total_credit, total_debit = 0, 0 move = None information = None i = 0 for record in f: type_ = record[0] if type_ == '0': statement = Statement() self.statements.append(statement) self._parse_statement(record, statement, HEADER) if statement.version != 2: raise ValueError( "Unsupported version %d" % statement.version) elif type_ == '1': self._parse_statement(record, statement, OLD_BALANCE) elif type_ == '2': article = record[1] if article == '1': move = Move() self._parse_move(record, article, move) if article == '1': transaction_type = move.transaction_type if transaction_type in {'0', '1', '2', '3'}: statement.moves.append(move) if move.amount > 0: total_credit += move.amount else: total_debit -= move.amount elif transaction_type in {'5', '6', '7', '8'}: parent = statement.moves[-1] assert parent.sequence == move.sequence parent.moves.append(move) elif transaction_type == '9': parent = statement.moves[-1].moves[-1] assert parent.sequence == move.sequence parent.append(move) else: raise ValueError('Unknown type: %s' % transaction_type) elif type_ == '3': article = record[1] if article == '1': information = Information() self._parse_information(record, article, information) if article == '1': key = information.bank_reference statement.informations[key].append(information) elif type_ == '4': free_communication = FreeCommunication() self._parse_free_communication(record, free_communication) self.free_communications.append(free_communication) elif type_ == '8': self._parse_statement(record, statement, NEW_BALANCE) elif type_ == '9': self._parse_statement(record, statement, TRAILER) if (statement.new_balance - statement.old_balance != statement.total_credit - statement.total_debit): raise ValueError("Wrong balance") if statement.total_credit != total_credit: raise ValueError("Wrong total credit") if statement.total_debit != total_debit: raise ValueError("Wrong total debit") if statement.number_records != i - 1: raise ValueError("Wrong number of records") statement = None total_credit, total_debit = 0, 0 i = 0 i += 1 def _parse_statement(self, record, statement, desc): for name, (slice_, parser) in desc.items(): value = parser(record[slice_]) if getattr(statement, name) is not None: assert getattr(statement, name) == value, (name, value, getattr(statement, name), statement) setattr(statement, name, value) def _parse_move(self, record, article, move): for name, (slice_, parser) in MOVE_COMMON.items(): value = parser(record[slice_]) if getattr(move, name) is not None: assert getattr(move, name) == value else: setattr(move, name, value) for name, (slice_, parser) in MOVE[article].items(): value = parser(record[slice_]) if getattr(move, name): value = getattr(move, name) + value setattr(move, name, value) def _parse_information(self, record, article, information): for name, (slice_, parser) in INFORMATION_COMMON.items(): value = parser(record[slice_]) if getattr(information, name) is not None: assert getattr(information, name) == value else: setattr(information, name, value) for name, (slice_, parser) in INFORMATION[article].items(): value = parser(record[slice_]) if getattr(information, name): value = getattr(information, name) + value setattr(information, name, value) def _parse_free_communication(self, record, free_communication): for name, (slice_, parser) in FREE_COMMUNICATION.items(): value = parser(record[slice_]) setattr(free_communication, name, value) def _date(value): return datetime.strptime(value, '%d%m%y').date() def _string(value): return value.rstrip() def _amount(value): sign = value[0:1] amount = Decimal(value[1:]) if sign == '1': amount *= -1 return amount / 1000 HEADER = { 'creation_date': (slice(5, 11), _date), 'bank_id': (slice(11, 14), int), 'duplicate': (slice(16, 17), lambda c: c == 'D'), 'file_reference': (slice(24, 34), _string), 'address': (slice(34, 60), _string), 'bic': (slice(60, 71), _string), 'company_id': (slice(71, 82), str), 'reference': (slice(88, 104), _string), 'related_reference': (slice(105, 120), _string), 'version': (slice(127, 128), int), } TRAILER = { 'number_records': (slice(16, 22), int), 'total_debit': (slice(22, 37), _amount), 'total_credit': (slice(37, 52), _amount), } OLD_BALANCE = { '_account_structure': (slice(1, 2), str), 'old_sequence': (slice(2, 5), str), '_account_currency': (slice(5, 42), str), 'old_balance': (slice(43, 58), _amount), 'old_balance_date': (slice(58, 64), _date), 'account_holder_name': (slice(64, 90), _string), 'account_description': (slice(90, 125), _string), 'coda_sequence': (slice(125, 128), str), } NEW_BALANCE = { 'new_sequence': (slice(1, 4), str), '_account_currency': (slice(4, 41), str), 'new_balance': (slice(41, 57), _amount), 'new_balance_date': (slice(57, 63), _date), } class _SlotsNone(object): def __init__(self, *args, **kwargs): super(_SlotsNone, self).__init__(*args, **kwargs) for name in self.__slots__: setattr(self, name, None) class _Moves(object): __slots__ = ('moves',) def __init__(self, *args, **kwargs): super(_Moves, self).__init__(*args, **kwargs) self.moves = [] def find_move(self, sequence, detail_sequence='0000'): for move in self.moves: if move.sequence == sequence: if move.detail_sequence == detail_sequence: return move found = move.find_move(sequence, detail_sequence) if found: return found @property def all_moves(self): for move in self.moves: yield move for move in move.all_moves: yield move class Statement(_SlotsNone, _Moves): __slots__ = (list(HEADER.keys()) + list(TRAILER.keys()) + list(OLD_BALANCE.keys()) + list(NEW_BALANCE.keys()) + ['informations', 'free_communications']) def __init__(self, *args, **kwargs): super(Statement, self).__init__(*args, **kwargs) self.informations = defaultdict(list) self.free_communications = [] def __str__(self): if self.old_sequence != self.new_sequence: return ' - '.join([self.old_sequence, self.new_sequence]) else: return self.old_sequence @property def account(self): return _string(self._account_currency[{ '0': slice(0, 12), '1': slice(0, 34), '2': slice(0, 31), '3': slice(0, 34), }[self._account_structure]]) @property def account_currency(self): return self._account_currency[{ '0': slice(13, 16), '1': slice(34, 37), '2': slice(34, 37), '3': slice(34, 37), }[self._account_structure]] @property def account_country(self): if self._account_structure == '0': return self._account_currency[17:19] class _TransactionMixin(object): @property def transaction_type(self): return self.transaction_code[0] @property def transaction_family(self): return self.transaction_code[1:3] @property def transaction_transaction(self): return self.transaction_code[3:5] @property def transaction_category(self): return self.transaction_code[5:8] MOVE_COMMON = { 'sequence': (slice(2, 6), str), 'detail_sequence': (slice(6, 10), str), } MOVE = { '1': { 'bank_reference': (slice(10, 31), str), 'amount': (slice(31, 47), _amount), 'value_date': (slice(47, 53), _date), 'transaction_code': (slice(53, 61), str), '_communication': (slice(61, 115), str), 'entry_date': (slice(115, 121), _date), 'statement_number': (slice(121, 124), str), }, '2': { '_communication': (slice(10, 63), str), 'customer_reference': (slice(63, 98), _string), 'counterparty_bic': (slice(98, 109), _string), 'r_transaction': (slice(112, 113), _string), 'r_reason': (slice(113, 117), _string), 'category_purpose': (slice(117, 121), _string), 'purpose': (slice(121, 125), _string), }, '3': { 'counterparty_account': (slice(10, 47), _string), 'counterparty_name': (slice(47, 82), _string), '_communication': (slice(82, 125), str), }, } class Move(_SlotsNone, _Moves, _TransactionMixin): __slots__ = sum( (list(m.keys()) for m in MOVE.values()), list(MOVE_COMMON.keys())) def __str__(self): return self.sequence + self.detail_sequence @property def communication(self): type_ = self.communication_type if type_ is None: return _string(self._communication[1:]) elif type_ == '100': # TODO ISO-11649 return self._communication[4:] elif type_ in {'101', '102', '103'}: return _string(self._communication[4:16]) elif type_ == '105': return _string(self._communication[49:61]) elif type_ in {'106', '108', '111', '113', '114', '115', '121', '122', '123', '124', '125', '126'}: return elif type_ == '127': return self._communication[83:145] else: return self._communication[2:] # TODO add parser communication @property def communication_type(self): type_ = self._communication[0] if type_ == '1': return self._communication[1:4] INFORMATION_COMMON = { 'sequence': (slice(2, 6), str), 'detail_sequence': (slice(6, 10), str), } INFORMATION = { '1': { 'bank_reference': (slice(10, 31), str), 'transaction_code': (slice(31, 39), str), '_communication': (slice(39, 113), str), }, '2': { '_communication': (slice(10, 115), str), }, '3': { '_communication': (slice(10, 100), str), }, } class Information(_SlotsNone, _TransactionMixin): __slots__ = sum( (list(m.keys()) for m in INFORMATION.values()), list(INFORMATION_COMMON.keys())) def __str__(self): return self.sequence + self.detail_sequence @property def communication_type(self): type_ = self._communication[0] if type_ == '1': return self._communication[1:4] @property def name(self): if self.communication_type in {'001', '008', '009'}: return _string(self._communication[4:74]) else: raise AttributeError @property def street(self): if self.communication_type == '001': return _string(self._communication[74:109]) else: raise AttributeError @property def locality(self): if self.communication_type == '001': return _string(self._communication[109:144]) else: raise AttributeError @property def code_id(self): if self.communication_type == '001': return _string(self._communication[144:179]) elif self.communication_type in {'008', '009'}: return _string(self._communication[74:109]) else: raise AttributeError @property def communication(self): if self.communication_type == '002': return self._communication[4:] else: raise AttributeError @property def counterparty_banker(self): if self.communication_type == '004': return _string(self._communication[4:]) else: raise AttributeError @property def correspondent_data(self): if self.communication_type == '005': return self._communication[4:] else: raise AttributeError @property def description(self): if self.communication_type == '006': return self._communication[4:34] else: raise AttributeError @property def currency(self): if self.communication_type == '006': return self._communication[34:37] else: raise AttributeError @property def amount(self): if self.communication_type == '006': return _amount( self._communication[53] + self._communication[37:52]) else: raise AttributeError @property def category(self): if self.communication_type == '006': return self._communication[53:56] else: raise AttributeError @property def coin_number(self): if self.communication_type == '007': return int(self._communication[4:11]) else: raise AttributeError @property def coin(self): if self.communication_type == '007': return Decimal(self._communication[11:17]) / 1000 else: raise AttributeError @property def total_amount(self): if self.communication_type == '007': return Decimal(self._communication[17:32]) / 1000 else: raise AttributeError # TODO communication type: 010 and 011 FREE_COMMUNICATION = { 'sequence': (slice(2, 6), str), 'detail_sequence': (slice(6, 10), str), 'text': (slice(32, 112), _string), } class FreeCommunication(_SlotsNone): __slots__ = list(FREE_COMMUNICATION.keys()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966569.0 febelfin-coda-0.2.0/coda/test.py0000644000175000017500000002653100000000000014506 0ustar00cedced#!/usr/bin/env python # This file is part of febelfin-coda. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. """Test MT940 """ import doctest import os import unittest import sys from datetime import date from decimal import Decimal from coda import CODA here = os.path.dirname(__file__) readme = os.path.normpath(os.path.join(here, '..', 'README')) class TestCODA(unittest.TestCase): def setUp(self): self.coda = CODA(os.path.join(here, 'CODA.txt')) @property def statement(self): return self.coda.statements[0] @property def move(self): return self.statement.moves[0] def get_move(self, sequence, detail_sequence): return self.statement.find_move(sequence, detail_sequence) @property def information(self): return self.statement.informations['OL9456574JBBNEUBCRCL1'][0] def get_information(self, sequence, detail_sequence): for informations in self.statement.informations.values(): for information in informations: if (information.sequence == sequence and information.detail_sequence == detail_sequence): return information def test_number_statements(self): self.assertEqual(len(self.coda.statements), 1) def test_statement_str(self): self.assertEqual(str(self.statement), '001') def test_statement_creation_date(self): self.assertEqual(self.statement.creation_date, date(2006, 12, 6)) def test_statement_bank_id(self): self.assertEqual(self.statement.bank_id, 725) def test_statement_duplicate(self): self.assertEqual(self.statement.duplicate, False) def test_statement_file_reference(self): self.assertEqual(self.statement.file_reference, '00099449') def test_statement_address(self): self.assertEqual(self.statement.address, 'Testgebruiker21') def test_statement_bic(self): self.assertEqual(self.statement.bic, 'KREDBEBB') def test_statement_company_id(self): self.assertEqual(self.statement.company_id, '00630366277') def test_statement_version(self): self.assertEqual(self.statement.version, 2) def test_statement_old_sequence(self): self.assertEqual(self.statement.old_sequence, '001') def test_statement_new_sequence(self): self.assertEqual(self.statement.new_sequence, '001') def test_statement_account(self): self.assertEqual(self.statement.account, '435000000080') def test_statement_account_currency(self): self.assertEqual(self.statement.account_currency, 'EUR') def test_statement_account_country(self): self.assertEqual(self.statement.account_country, 'BE') def test_statement_old_balance(self): self.assertEqual(self.statement.old_balance, Decimal('0.000')) def test_statement_old_balance_date(self): self.assertEqual(self.statement.old_balance_date, date(2006, 12, 6)) def test_statement_new_balance(self): self.assertEqual(self.statement.new_balance, Decimal('9405296.990')) def test_statement_new_balance_date(self): self.assertEqual(self.statement.new_balance_date, date(2006, 12, 7)) def test_statement_account_holder_name(self): self.assertEqual(self.statement.account_holder_name, 'Testgebruiker21') def test_statement_account_description(self): self.assertEqual( self.statement.account_description, 'KBC-Bedrijfsrekening') def test_statement_coda_sequence(self): self.assertEqual(self.statement.coda_sequence, '001') def test_statement_number_records(self): self.assertEqual(self.statement.number_records, 260) def test_statement_total_debit(self): amount = sum(m.amount for m in self.statement.moves if m.amount < 0) self.assertEqual(self.statement.total_debit, -amount) def test_statement_total_credit(self): amount = sum(m.amount for m in self.statement.moves if m.amount > 0) self.assertEqual(self.statement.total_credit, amount) def test_statement_find(self): move = self.statement.find_move('0041', '0001') self.assertEqual(move.sequence, '0041') self.assertEqual(move.detail_sequence, '0001') def test_moves(self): self.assertEqual(len(list(self.statement.moves)), 59) def test_move_str(self): self.assertEqual(str(self.move), '00010000') def test_move_sequence(self): self.assertEqual(self.move.sequence, '0001') def test_move_detail_sequence(self): self.assertEqual(self.move.detail_sequence, '0000') def test_move_bank_reference(self): self.assertEqual(self.move.bank_reference, 'EPIB00048 AWIUBTKAPUO') def test_move_amount(self): self.assertEqual(self.move.amount, Decimal('-2578.25')) def test_move_value_date(self): self.assertEqual(self.move.value_date, date(2006, 12, 6)) def test_move_transaction_code(self): self.assertEqual(self.move.transaction_code, '00799000') def test_move_transaction_type(self): self.assertEqual(self.move.transaction_type, '0') def test_move_transaction_family(self): self.assertEqual(self.move.transaction_family, '07') def test_move_transaction_transaction(self): self.assertEqual(self.move.transaction_transaction, '99') def test_move_transaction_category(self): self.assertEqual(self.move.transaction_category, '000') def test_move_communication_type(self): self.assertEqual(self.move.communication_type, None) def test_move_communication(self): self.assertEqual( self.move.communication, "BORDEREAU DE DECOMPTE AVANCES 015 NUMERO D'OPERATION 495953") def test_move_communication_101(self): move = self.get_move('0053', '0000') self.assertEqual(move.communication_type, '101') self.assertEqual(move.communication, '269021157996') def test_move_communication_105(self): move = self.get_move('0003', '0002') self.assertEqual(move.communication_type, '105') self.assertEqual(move.communication, '') def test_move_communication_106(self): move = self.get_move('0004', '0003') self.assertEqual(move.communication_type, '106') self.assertEqual(move.communication, None) def test_move_communication_111(self): move = self.get_move('0009', '0000') self.assertEqual(move.communication_type, '111') self.assertEqual(move.communication, None) def test_move_communication_113(self): move = self.get_move('0012', '0000') self.assertEqual(move.communication_type, '113') self.assertEqual(move.communication, None) def test_move_communication_114(self): move = self.get_move('0043', '0001') self.assertEqual(move.communication_type, '114') self.assertEqual(move.communication, None) def test_move_communication_115(self): move = self.get_move('0049', '0000') self.assertEqual(move.communication_type, '115') self.assertEqual(move.communication, None) def test_move_entry_date(self): self.assertEqual(self.move.entry_date, date(2006, 12, 6)) def test_move_statement_number(self): self.assertEqual(self.move.statement_number, '001') def test_move_customer_reference(self): move = self.get_move('0004', '0000') self.assertEqual(move.customer_reference, 'NB2206092900135') def test_move_counterparty_bic(self): move = self.get_move('0006', '0000') self.assertEqual(move.counterparty_bic, 'CREGBEBB') def test_move_r_transaction(self): move = self.get_move('0003', '0000') self.assertEqual(move.r_transaction, '4') def test_move_r_reason(self): move = self.get_move('0003', '0000') self.assertEqual(move.r_reason, '3246') def test_move_category_purpose(self): move = self.get_move('0003', '0000') self.assertEqual(move.category_purpose, '306A') def test_move_purpose(self): move = self.get_move('0003', '0000') self.assertEqual(move.purpose, '1648') def test_move_counterparty_account(self): move = self.get_move('0003', '0000') self.assertEqual(move.counterparty_account, 'LU037050522702273100') def test_move_counterparty_name(self): move = self.get_move('0003', '0000') self.assertEqual(move.counterparty_name, ' Olgerdin Egill Skallagrims') def test_informations(self): self.assertEqual(len(self.statement.informations), 22) def test_information_str(self): self.assertEqual(str(self.information), '00030001') def test_information_sequence(self): self.assertEqual(self.information.sequence, '0003') def test_information_detail_sequence(self): self.assertEqual(self.information.detail_sequence, '0001') def test_information_bank_reference(self): self.assertEqual( self.information.bank_reference, 'OL9456574JBBNEUBCRCL1') def test_information_transaction_code(self): self.assertEqual(self.information.transaction_code, '34150000') def test_information_communitcation_type(self): self.assertEqual(self.information.communication_type, '001') def test_information_communitcation_name(self): self.assertEqual(self.information.name, 'Olgerdin Egill Skallagrims') def test_information_communitcation_street(self): self.assertEqual(self.information.street, 'Grjothalsi 7') def test_information_communitcation_locality(self): self.assertEqual(self.information.locality, '11110 Reykjavik') def test_information_communitcation_code_id(self): self.assertEqual(self.information.code_id, '') def test_information_counterparty_banker(self): information = self.get_information('0058', '0002') self.assertEqual(information.counterparty_banker, 'SOCIETE GENERALE') def test_information_coin_number(self): information = self.get_information('0049', '0001') self.assertEqual(information.coin_number, 1) def test_information_coin(self): information = self.get_information('0049', '0001') self.assertEqual(information.coin, Decimal('10')) def test_information_total_amount(self): information = self.get_information('0049', '0001') self.assertEqual(information.total_amount, Decimal('10')) def test_sum_moves(self): amount = sum(m.amount for m in self.statement.moves) self.assertEqual( amount, self.statement.new_balance - self.statement.old_balance) def test_sum_details(self): move = self.get_move('0002', '0000') amount = sum(m.amount for m in move.moves) self.assertEqual(amount, move.amount) def test_suite(): suite = additional_tests() loader = unittest.TestLoader() suite.addTests(loader.loadTestsFromTestCase(TestCODA)) return suite def additional_tests(): suite = unittest.TestSuite() if os.path.isfile(readme): suite.addTest(doctest.DocFileSuite(readme, module_relative=False)) return suite def main(): suite = test_suite() runner = unittest.TextTestRunner() return runner.run(suite) if __name__ == '__main__': sys.path.insert(0, os.path.dirname(os.path.dirname( os.path.dirname(os.path.abspath(__file__))))) sys.exit(not main().wasSuccessful()) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1631966929.381493 febelfin-coda-0.2.0/febelfin_coda.egg-info/0000755000175000017500000000000000000000000016472 5ustar00cedced././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966928.0 febelfin-coda-0.2.0/febelfin_coda.egg-info/PKG-INFO0000644000175000017500000000473200000000000017575 0ustar00cedcedMetadata-Version: 1.2 Name: febelfin-coda Version: 0.2.0 Summary: A module to parse CODA files Home-page: https://coda.b2ck.com/ Author: B2CK Author-email: info@b2ck.com License: BSD Description: febelfin-coda ============= febelfin-coda is a parser for `CODA files`_. .. _CODA files: https://www.febelfin.be/sites/default/files/2019-04/standard-coda-2.6-en.pdf Nutshell -------- Import:: >>> import os >>> from coda import CODA Instantiate:: >>> coda = CODA('coda/CODA.txt') The statements:: >>> len(coda.statements) 1 >>> statement, = coda.statements >>> statement.account '435000000080' >>> statement.account_currency 'EUR' >>> statement.old_balance Decimal('0') >>> statement.old_balance_date datetime.date(2006, 12, 6) >>> statement.new_balance Decimal('9405296.99') >>> statement.new_balance_date datetime.date(2006, 12, 7) The transactions:: >>> len(statement.moves) 59 >>> move = statement.moves[0] >>> move.value_date datetime.date(2006, 12, 6) >>> move.entry_date datetime.date(2006, 12, 6) >>> move.amount Decimal('-2578.25') >>> move.bank_reference 'EPIB00048 AWIUBTKAPUO' >>> move.transaction_code '00799000' >>> move.communication "BORDEREAU DE DECOMPTE AVANCES 015 NUMERO D'OPERATION 495953" To report issues please visit the `coda bugtracker`_. .. _coda bugtracker: https://coda.b2ck.com/ Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Topic :: Office/Business Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Utilities Requires-Python: >=3.5 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966929.0 febelfin-coda-0.2.0/febelfin_coda.egg-info/SOURCES.txt0000644000175000017500000000037700000000000020365 0ustar00cedced.hgtags CHANGELOG COPYRIGHT MANIFEST.in README setup.py tox.ini coda/CODA.txt coda/__init__.py coda/test.py febelfin_coda.egg-info/PKG-INFO febelfin_coda.egg-info/SOURCES.txt febelfin_coda.egg-info/dependency_links.txt febelfin_coda.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966928.0 febelfin-coda-0.2.0/febelfin_coda.egg-info/dependency_links.txt0000644000175000017500000000000100000000000022540 0ustar00cedced ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966928.0 febelfin-coda-0.2.0/febelfin_coda.egg-info/top_level.txt0000644000175000017500000000000500000000000021217 0ustar00cedcedcoda ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1631966929.381493 febelfin-coda-0.2.0/setup.cfg0000644000175000017500000000004600000000000014061 0ustar00cedced[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631966597.0 febelfin-coda-0.2.0/setup.py0000644000175000017500000000272300000000000013756 0ustar00cedced#!/usr/bin/env python # This file is part of febelfin-coda. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import os import re import codecs from setuptools import setup, find_packages def read(fname): return codecs.open( os.path.join(os.path.dirname(__file__), fname), 'r', 'utf-8').read() def get_version(): init = read(os.path.join('coda', '__init__.py')) return re.search("__version__ = '([0-9.]*)'", init).group(1) setup(name='febelfin-coda', version=get_version(), author='B2CK', author_email='info@b2ck.com', url='https://coda.b2ck.com/', description='A module to parse CODA files', long_description=read('README'), packages=find_packages(), package_data={ 'coda': ['CODA.txt'], }, python_requires='>=3.5', classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Topic :: Office/Business', 'Topic :: Software Development :: Libraries', 'Topic :: Utilities', ], license='BSD', test_suite='coda.test', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1631396795.0 febelfin-coda-0.2.0/tox.ini0000644000175000017500000000053700000000000013560 0ustar00cedced# Tox (http://tox.testrun.org/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py35, py36, py37, py38, py39, pypy3 [testenv] commands = {envpython} setup.py test deps =