diff --git a/README.rst b/README.rst index 8189860..21523cd 100644 --- a/README.rst +++ b/README.rst @@ -45,6 +45,7 @@ Example: "IBAN": "NL50BANK1234567890", "BIC": "BANKNL2A", "batch": True, + # "split_transactions": True, # optional, split transactions into different payments when batch booking is disabled. Default is 'True'. See https://github.com/raphaelm/python-sepaxml/issues/53 "creditor_id": "DE26ZZZ00000000000", # supplied by your bank or financial authority "currency": "EUR", # ISO 4217 # "instrument": "B2B", # - default is CORE (B2C) @@ -121,6 +122,7 @@ Example: "IBAN": "NL50BANK1234567890", "BIC": "BANKNL2A", "batch": True, + # "split_transactions": True, # optional, split transactions into different payments when batch booking is disabled. Default is 'True'. See https://github.com/raphaelm/python-sepaxml/issues/53 # For non-SEPA transfers, set "domestic" to True, necessary e.g. for CH/LI "currency": "EUR", # ISO 4217 "address": { diff --git a/sepaxml/debit.py b/sepaxml/debit.py index 0950643..aa5223d 100644 --- a/sepaxml/debit.py +++ b/sepaxml/debit.py @@ -99,7 +99,7 @@ def add_payment(self, payment): self.check_payment(payment) # Get the CstmrDrctDbtInitnNode - if not self._config['batch']: + if self._transaction_splitting: # Start building the non batch payment PmtInf_nodes = self._create_PmtInf_node() PmtInf_nodes['PmtInfIdNode'].text = make_id(self._config['name']) @@ -159,10 +159,10 @@ def add_payment(self, payment): payment['endtoend_id'] = make_id(self._config['name']) TX_nodes['EndToEndIdNode'].text = payment['endtoend_id'][:35] - if self._config['batch']: - self._add_batch(TX_nodes, payment) - else: + if self._transaction_splitting: self._add_non_batch(TX_nodes, PmtInf_nodes) + else: + self._add_batch(TX_nodes, payment) def _create_header(self): """ @@ -464,7 +464,7 @@ def _finalize_batch(self): PmtInf_nodes = self._create_PmtInf_node() PmtInf_nodes['PmtInfIdNode'].text = make_id(self._config['name']) PmtInf_nodes['PmtMtdNode'].text = "DD" - PmtInf_nodes['BtchBookgNode'].text = "true" + PmtInf_nodes['BtchBookgNode'].text = str(self._config['batch']).lower() PmtInf_nodes['Cd_SvcLvl_Node'].text = "SEPA" PmtInf_nodes['Cd_LclInstrm_Node'].text = self._config['instrument'] PmtInf_nodes['SeqTpNode'].text = batch_meta_split[0] diff --git a/sepaxml/shared.py b/sepaxml/shared.py index 6bdff06..679baae 100644 --- a/sepaxml/shared.py +++ b/sepaxml/shared.py @@ -54,6 +54,8 @@ def __init__(self, config, schema, clean=True): if self._config.get('msg_id'): self.msg_id = self._config['msg_id'][:35] + self._transaction_splitting = self._config.get('split_transactions', True) and not self._config['batch'] + self._prepare_document() self._create_header() diff --git a/sepaxml/transfer.py b/sepaxml/transfer.py index 669632f..0670f12 100644 --- a/sepaxml/transfer.py +++ b/sepaxml/transfer.py @@ -96,7 +96,7 @@ def add_payment(self, payment): payment['description'] = unidecode(payment['description'])[:140] # Get the CstmrDrctDbtInitnNode - if not self._config['batch']: + if self._transaction_splitting: # Start building the non batch payment PmtInf_nodes = self._create_PmtInf_node() PmtInf_nodes['PmtInfIdNode'].text = make_id(self._config['name']) @@ -160,10 +160,10 @@ def add_payment(self, payment): TX_nodes['IBAN_CdtrAcct_Node'].text = payment['IBAN'] TX_nodes['UstrdNode'].text = payment['description'] - if self._config['batch']: - self._add_batch(TX_nodes, payment) - else: + if self._transaction_splitting: self._add_non_batch(TX_nodes, PmtInf_nodes) + else: + self._add_batch(TX_nodes, payment) def _create_header(self): """ @@ -397,7 +397,7 @@ def _finalize_batch(self): PmtInf_nodes = self._create_PmtInf_node() PmtInf_nodes['PmtInfIdNode'].text = make_id(self._config['name']) PmtInf_nodes['PmtMtdNode'].text = "TRF" - PmtInf_nodes['BtchBookgNode'].text = "true" + PmtInf_nodes['BtchBookgNode'].text = str(self._config['batch']).lower() if not self._config.get('domestic', False): PmtInf_nodes['Cd_SvcLvl_Node'].text = "SEPA" diff --git a/tests/debit/test_non_batch.py b/tests/debit/test_non_batch.py index 30327d6..3fc926d 100644 --- a/tests/debit/test_non_batch.py +++ b/tests/debit/test_non_batch.py @@ -24,8 +24,8 @@ def sdd(): 20012017014921-ba2dab283fdd 2017-01-20T13:49:21 - 2 - 60.12 + 3 + 103.33 TestCreditor @@ -122,7 +122,7 @@ def sdd(): CORE - RCUR + FRST 2017-01-20 @@ -181,6 +181,78 @@ def sdd(): + + TestCreditor-d547a1b3882f + DD + false + 1 + 43.21 + + + SEPA + + + CORE + + RCUR + + 2017-01-20 + + TestCreditor + + + + NL50BANK1234567890 + + + + + BANKNL2A + + + SLEV + + + + + DE26ZZZ00000000000 + + SEPA + + + + + + + + TestCreditor-7e989083e265 + + 43.21 + + + 1234 + 2017-01-20 + false + + + + + BANKNL2A + + + + Test du Test 2 + + + + NL50BANK1234567890 + + + + Test transaction3 + + + """ @@ -203,15 +275,27 @@ def test_two_debits(sdd): "IBAN": "NL50BANK1234567890", "BIC": "BANKNL2A", "amount": 5000, - "type": "RCUR", + "type": "FRST", "collection_date": datetime.date.today(), "mandate_id": "1234", "mandate_date": datetime.date.today(), "description": "Test transaction2" } + payment3 = { + "name": "Test du Test 2", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "amount": 4321, + "type": "RCUR", + "collection_date": datetime.date.today(), + "mandate_id": "1234", + "mandate_date": datetime.date.today(), + "description": "Test transaction3" + } sdd.add_payment(payment1) sdd.add_payment(payment2) + sdd.add_payment(payment3) xmlout = sdd.export() xmlpretty = validate_xml(xmlout, "pain.008.001.02") assert clean_ids(xmlpretty.strip()) == clean_ids(SAMPLE_RESULT.strip()) diff --git a/tests/debit/test_non_batch_non_split.py b/tests/debit/test_non_batch_non_split.py new file mode 100644 index 0000000..77e3259 --- /dev/null +++ b/tests/debit/test_non_batch_non_split.py @@ -0,0 +1,259 @@ +import datetime + +import pytest + +from sepaxml import SepaDD +from tests.utils import clean_ids, validate_xml + + +@pytest.fixture +def sdd(): + return SepaDD({ + "name": "TestCreditor", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "batch": False, + "split_transactions": False, + "creditor_id": "DE26ZZZ00000000000", + "currency": "EUR" + }, schema="pain.008.001.02") + + +SAMPLE_RESULT = b""" + + + + 20012017014921-ba2dab283fdd + 2017-01-20T13:49:21 + 3 + 103.33 + + TestCreditor + + + + DE26ZZZ00000000000 + + + + + + + TestCreditor-ecd6a2f680ce + DD + false + 2 + 60.12 + + + SEPA + + + CORE + + FRST + + 2017-01-20 + + TestCreditor + + + + NL50BANK1234567890 + + + + + BANKNL2A + + + SLEV + + + + + DE26ZZZ00000000000 + + SEPA + + + + + + + + TestCreditor-4431989789fb + + 10.12 + + + 1234 + 2017-01-20 + false + + + + + BANKNL2A + + + + Test von Testenstein + + + + NL50BANK1234567890 + + + + Test transaction1 + + + + + TestCreditor-7e989083e265 + + 50.00 + + + 1234 + 2017-01-20 + false + + + + + BANKNL2A + + + + Test du Test + + + + NL50BANK1234567890 + + + + Test transaction2 + + + + + TestCreditor-d547a1b3882f + DD + false + 1 + 43.21 + + + SEPA + + + CORE + + RCUR + + 2017-01-20 + + TestCreditor + + + + NL50BANK1234567890 + + + + + BANKNL2A + + + SLEV + + + + + DE26ZZZ00000000000 + + SEPA + + + + + + + + TestCreditor-7e989083e265 + + 43.21 + + + 1234 + 2017-01-20 + false + + + + + BANKNL2A + + + + Test du Test 2 + + + + NL50BANK1234567890 + + + + Test transaction3 + + + + + +""" + + +def test_two_debits(sdd): + payment1 = { + "name": "Test von Testenstein", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "amount": 1012, + "type": "FRST", + "collection_date": datetime.date.today(), + "mandate_id": "1234", + "mandate_date": datetime.date.today(), + "description": "Test transaction1" + } + payment2 = { + "name": "Test du Test", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "amount": 5000, + "type": "FRST", + "collection_date": datetime.date.today(), + "mandate_id": "1234", + "mandate_date": datetime.date.today(), + "description": "Test transaction2" + } + payment3 = { + "name": "Test du Test 2", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "amount": 4321, + "type": "RCUR", + "collection_date": datetime.date.today(), + "mandate_id": "1234", + "mandate_date": datetime.date.today(), + "description": "Test transaction3" + } + + sdd.add_payment(payment1) + sdd.add_payment(payment2) + sdd.add_payment(payment3) + xmlout = sdd.export() + xmlpretty = validate_xml(xmlout, "pain.008.001.02") + assert clean_ids(xmlpretty.strip()) == clean_ids(SAMPLE_RESULT.strip()) diff --git a/tests/transfer/test_non_batch.py b/tests/transfer/test_non_batch.py index 72473a4..95b6395 100644 --- a/tests/transfer/test_non_batch.py +++ b/tests/transfer/test_non_batch.py @@ -160,7 +160,6 @@ def test_two_debits(strf): strf.add_payment(payment2) xmlout = strf.export() xmlpretty = validate_xml(xmlout, "pain.001.001.03") - print(xmlpretty.decode()) assert clean_ids(xmlpretty.strip()) == clean_ids(SAMPLE_RESULT.strip()) diff --git a/tests/transfer/test_non_batch_non_split.py b/tests/transfer/test_non_batch_non_split.py new file mode 100644 index 0000000..fd28c89 --- /dev/null +++ b/tests/transfer/test_non_batch_non_split.py @@ -0,0 +1,166 @@ +import datetime + +import pytest + +from sepaxml import SepaTransfer +from tests.utils import clean_ids, validate_xml + + +@pytest.fixture +def strf(): + return SepaTransfer({ + "name": "TestCreditor", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "batch": False, + "split_transactions": False, + "currency": "EUR" + }, schema="pain.001.001.03") + + +SAMPLE_RESULT = b""" + + + + 20180724041334-4db42f0dd97e + 2018-07-24T16:13:34 + 2 + 20.24 + + TestCreditor + + + + TestCreditor-8748725a0019 + TRF + false + 2 + 20.24 + + + SEPA + + + 2018-07-24 + + TestCreditor + + + + NL50BANK1234567890 + + + + + BANKNL2A + + + SLEV + + + ebd75e7e649375d91b33dc11ae44c0e1 + + + 10.12 + + + + BANKNL2A + + + + Test von Testenstein + + + + NL50BANK1234567890 + + + + Test transaction1 + + + + + af755a40cb692551ed9f9d55f7179525 + + + 10.12 + + + + BANKNL2A + + + + Test von Testenstein + + + + NL50BANK1234567890 + + + + Test transaction1 + + + + + +""" + + +def test_two_debits(strf): + payment1 = { + "endtoend_id": "ebd75e7e649375d91b33dc11ae44c0e1", + "name": "Test von Testenstein", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "amount": 1012, + "execution_date": datetime.date.today(), + "description": "Test transaction1" + } + payment2 = { + "name": "Test von Testenstein", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "amount": 1012, + "execution_date": datetime.date.today(), + "description": "Test transaction1", + "endtoend_id": "af755a40cb692551ed9f9d55f7179525" + } + + strf.add_payment(payment1) + strf.add_payment(payment2) + xmlout = strf.export() + xmlpretty = validate_xml(xmlout, "pain.001.001.03") + assert clean_ids(xmlpretty.strip()) == clean_ids(SAMPLE_RESULT.strip()) + + +def test_sepa_address(strf): + config = { + "name": "TestCreditor", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "batch": False, + "currency": "EUR", + "address": { + "country": "DE", + "lines": ["Line 1", "Line 2"], + }, + } + payment1 = { + "endtoend_id": "ebd75e7e649375d91b33dc11ae44c0e1", + "name": "Test von Testenstein", + "IBAN": "NL50BANK1234567890", + "BIC": "BANKNL2A", + "amount": 1012, + "execution_date": datetime.date.today(), + "description": "Test transaction1", + "address": { + "country": "DE", + "lines": ["Line 1", "Line 2"], + }, + } + strf = SepaTransfer(config) + strf.add_payment(payment1)