Skip to content
Open
13 changes: 13 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Example:
import datetime, uuid

config = {
# "msg_id": "", # If your bank require a specific format for MsgId, you can optionnaly set it up here. Default will be randomly generated with timestamp followed by a random value.
"name": "Test von Testenstein",
"IBAN": "NL50BANK1234567890",
"BIC": "BANKNL2A",
Expand All @@ -60,6 +61,15 @@ Example:
"country_subdivision": None,
"lines": ["Line 1", "Line 2"],
},
# "initiating_party": "John Doe", # optional name of the initiator of the payment, required by some banks. default to ''name'
# "initiating_party_id": "DE26ZZZ00000000002", # optional, supplied by your bank or financial authority. default to 'creditor_id'
"ultimate_creditor": {
# The ultimate_creditor and all of its fields are optional but in some financial institution they are required
"name": "Real Creditor",
"BIC_or_BEI": "REALNL2A",
"id": "12345678900001", # can be a local official id or the creditor_id
"id_scheme_name": "SIRET", # proprietary scheme of the id provided (i.e. SEPA, SIRET...)
},
}
sepa = SepaDD(config, schema="pain.008.001.02", clean=True)

Expand Down Expand Up @@ -87,6 +97,8 @@ Example:
"country_subdivision": None,
"lines": ["Line 1", "Line 2"],
},
# "initiating_party": "John Doe", # optional name of the initiator of the payment, required by some banks. default to ''name'
# "initiating_party_id": "DE26ZZZ00000000002", # optional, supplied by your bank or financial authority. default to 'creditor_id'
}
sepa.add_payment(payment)

Expand All @@ -104,6 +116,7 @@ Example:
import datetime, uuid

config = {
# "msg_id": "", # If your bank require a specific message_id format, you can set it up here. Default will be randomly generated with timestamp followed by a random value.
"name": "Test von Testenstein",
"IBAN": "NL50BANK1234567890",
"BIC": "BANKNL2A",
Expand Down
57 changes: 56 additions & 1 deletion sepaxml/debit.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def add_payment(self, payment):

TX_nodes['MndtIdNode'].text = payment['mandate_id']
TX_nodes['DtOfSgntrNode'].text = payment['mandate_date']
TX_nodes['AmdmntIndNode'].text = 'false'
if bic:
TX_nodes['BIC_DbtrAgt_Node'].text = payment['BIC']
else:
Expand All @@ -156,7 +157,7 @@ def add_payment(self, payment):
TX_nodes['UstrdNode'].text = payment['description']
if not payment.get('endtoend_id', ''):
payment['endtoend_id'] = make_id(self._config['name'])
TX_nodes['EndToEndIdNode'].text = payment['endtoend_id']
TX_nodes['EndToEndIdNode'].text = payment['endtoend_id'][:35]

if self._config['batch']:
self._add_batch(TX_nodes, payment)
Expand Down Expand Up @@ -188,7 +189,11 @@ def _create_header(self):
MsgId_node.text = self.msg_id
CreDtTm_node.text = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
Nm_node.text = self._config['name']
if 'initiating_party' in self._config and self._config['initiating_party']:
Nm_node.text = self._config['initiating_party']
Id_node.text = self._config['creditor_id']
if 'initiating_party_id' in self._config and self._config['initiating_party_id']:
Id_node.text = self._config['initiating_party_id']

# Append the nodes
Othr_node.append(Id_node)
Expand Down Expand Up @@ -239,6 +244,20 @@ def _create_PmtInf_node(self):
else:
ED['Othr_CdtrAgt_Node'] = ET.Element("Othr")
ED['Id_CdtrAgt_Node'] = ET.Element("Id")
if 'ultimate_creditor' in self._config:
ED['UltmtCdtrNode'] = ET.Element("UltmtCdtr")
if 'name' in self._config['ultimate_creditor']:
ED['Nm_UltmtCdtr_Node'] = ET.Element("Nm")
ED['Id_UltmtCdtr_Node'] = ET.Element("Id")
ED['OrgId_Id_UltmtCdtr_Node'] = ET.Element("OrgId")
if 'BIC_or_BEI' in self._config['ultimate_creditor']:
ED['BICOrBEI_OrgId_Id_UltmtCdtr_Node'] = ET.Element("BICOrBEI")
if 'id' in self._config['ultimate_creditor']:
ED['Othr_OrgId_Id_UltmtCdtr_Node'] = ET.Element("Othr")
ED['Id_Othr_OrgId_Id_UltmtCdtr_Node'] = ET.Element("Id")
if 'id_scheme_name' in self._config['ultimate_creditor']:
ED['SchmeNm_Othr_OrgId_Id_UltmtCdtr_Node'] = ET.Element("SchmeNm")
ED['Prtry_SchmeNm_Othr_OrgId_Id_UltmtCdtr_Node'] = ET.Element("Prtry")
ED['ChrgBrNode'] = ET.Element("ChrgBr")
ED['CdtrSchmeIdNode'] = ET.Element("CdtrSchmeId")
ED['Id_CdtrSchmeId_Node'] = ET.Element("Id")
Expand All @@ -263,6 +282,7 @@ def _create_TX_node(self, bic=True):
ED['MndtRltdInfNode'] = ET.Element("MndtRltdInf")
ED['MndtIdNode'] = ET.Element("MndtId")
ED['DtOfSgntrNode'] = ET.Element("DtOfSgntr")
ED['AmdmntIndNode'] = ET.Element("AmdmntInd")
ED['DbtrAgtNode'] = ET.Element("DbtrAgt")
ED['FinInstnId_DbtrAgt_Node'] = ET.Element("FinInstnId")
if bic:
Expand Down Expand Up @@ -342,6 +362,7 @@ def _add_non_batch(self, TX_nodes, PmtInf_nodes):

TX_nodes['MndtRltdInfNode'].append(TX_nodes['MndtIdNode'])
TX_nodes['MndtRltdInfNode'].append(TX_nodes['DtOfSgntrNode'])
TX_nodes['MndtRltdInfNode'].append(TX_nodes['AmdmntIndNode'])
TX_nodes['DrctDbtTxNode'].append(TX_nodes['MndtRltdInfNode'])
TX_nodes['DrctDbtTxInfNode'].append(TX_nodes['DrctDbtTxNode'])

Expand Down Expand Up @@ -383,6 +404,7 @@ def _add_batch(self, TX_nodes, payment):

TX_nodes['MndtRltdInfNode'].append(TX_nodes['MndtIdNode'])
TX_nodes['MndtRltdInfNode'].append(TX_nodes['DtOfSgntrNode'])
TX_nodes['MndtRltdInfNode'].append(TX_nodes['AmdmntIndNode'])
TX_nodes['DrctDbtTxNode'].append(TX_nodes['MndtRltdInfNode'])
TX_nodes['DrctDbtTxInfNode'].append(TX_nodes['DrctDbtTxNode'])

Expand Down Expand Up @@ -472,6 +494,16 @@ def _finalize_batch(self):
PmtInf_nodes['NbOfTxsNode'].text = str(len(batch_nodes))
PmtInf_nodes['CtrlSumNode'].text = int_to_decimal_str(self._batch_totals[batch_meta])

if 'ultimate_creditor' in self._config:
if 'name' in self._config['ultimate_creditor']:
PmtInf_nodes['Nm_UltmtCdtr_Node'].text = self._config['ultimate_creditor']['name']
if 'BIC_or_BEI' in self._config['ultimate_creditor']:
PmtInf_nodes['BICOrBEI_OrgId_Id_UltmtCdtr_Node'].text = self._config['ultimate_creditor']['BIC_or_BEI']
if 'id' in self._config['ultimate_creditor']:
PmtInf_nodes['Id_Othr_OrgId_Id_UltmtCdtr_Node'].text = self._config['ultimate_creditor']['id']
if 'id_scheme_name' in self._config['ultimate_creditor']:
PmtInf_nodes['Prtry_SchmeNm_Othr_OrgId_Id_UltmtCdtr_Node'].text = self._config['ultimate_creditor']['id_scheme_name']

PmtInf_nodes['PmtInfNode'].append(PmtInf_nodes['PmtInfIdNode'])
PmtInf_nodes['PmtInfNode'].append(PmtInf_nodes['PmtMtdNode'])
PmtInf_nodes['PmtInfNode'].append(PmtInf_nodes['BtchBookgNode'])
Expand Down Expand Up @@ -511,6 +543,29 @@ def _finalize_batch(self):
PmtInf_nodes['FinInstnId_CdtrAgt_Node'])
PmtInf_nodes['PmtInfNode'].append(PmtInf_nodes['CdtrAgtNode'])

if 'ultimate_creditor' in self._config:
if 'BIC_or_BEI' in self._config['ultimate_creditor']:
PmtInf_nodes['OrgId_Id_UltmtCdtr_Node'].append(
PmtInf_nodes['BICOrBEI_OrgId_Id_UltmtCdtr_Node'])
PmtInf_nodes['Id_UltmtCdtr_Node'].append(
PmtInf_nodes['OrgId_Id_UltmtCdtr_Node'])
if 'id' in self._config['ultimate_creditor']:
PmtInf_nodes['Othr_OrgId_Id_UltmtCdtr_Node'].append(
PmtInf_nodes['Id_Othr_OrgId_Id_UltmtCdtr_Node'])
if 'id_scheme_name' in self._config['ultimate_creditor']:
PmtInf_nodes['SchmeNm_Othr_OrgId_Id_UltmtCdtr_Node'].append(
PmtInf_nodes['Prtry_SchmeNm_Othr_OrgId_Id_UltmtCdtr_Node'])
PmtInf_nodes['Othr_OrgId_Id_UltmtCdtr_Node'].append(
PmtInf_nodes['SchmeNm_Othr_OrgId_Id_UltmtCdtr_Node'])
PmtInf_nodes['OrgId_Id_UltmtCdtr_Node'].append(
PmtInf_nodes['Othr_OrgId_Id_UltmtCdtr_Node'])
if 'name' in self._config['ultimate_creditor']:
PmtInf_nodes['UltmtCdtrNode'].append(
PmtInf_nodes['Nm_UltmtCdtr_Node'])
PmtInf_nodes['UltmtCdtrNode'].append(
PmtInf_nodes['Id_UltmtCdtr_Node'])
PmtInf_nodes['PmtInfNode'].append(PmtInf_nodes['UltmtCdtrNode'])

PmtInf_nodes['PmtInfNode'].append(PmtInf_nodes['ChrgBrNode'])

PmtInf_nodes['OthrNode'].append(PmtInf_nodes['Id_Othr_Node'])
Expand Down
5 changes: 4 additions & 1 deletion sepaxml/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ def __init__(self, config, schema, clean=True):

self._config['name'] = unidecode(self._config['name'])[:70]

if self._config.get('msg_id'):
self.msg_id = self._config['msg_id'][:35]

self._prepare_document()
self._create_header()

Expand Down Expand Up @@ -114,7 +117,7 @@ def export(self, validate=True, pretty_print=False):
if pretty_print:
from xml.dom import minidom
out_minidom = minidom.parseString(out)
out = out_minidom.toprettyxml(encoding="utf-8")
out = out_minidom.toprettyxml(encoding="UTF-8")

if validate:
try_valid_xml(out, self.schema)
Expand Down
13 changes: 13 additions & 0 deletions sepaxml/transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,27 @@ def _create_header(self):
CtrlSum_node = ET.Element("CtrlSum")
InitgPty_node = ET.Element("InitgPty")
Nm_node = ET.Element("Nm")
SupId_node = ET.Element("Id")
OrgId_node = ET.Element("OrgId")
Othr_node = ET.Element("Othr")
Id_node = ET.Element("Id")

# Add data to some header nodes.
MsgId_node.text = self.msg_id
CreDtTm_node.text = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
Nm_node.text = self._config['name']
if 'initiating_party' in self._config and self._config['initiating_party']:
Nm_node.text = self._config['initiating_party']
if 'initiating_party_id' in self._config and self._config['initiating_party_id']:
Id_node.text = self._config['initiating_party_id']

# Append the nodes
Othr_node.append(Id_node)
OrgId_node.append(Othr_node)
SupId_node.append(OrgId_node)
InitgPty_node.append(Nm_node)
if 'initiating_party_id' in self._config:
InitgPty_node.append(SupId_node)
GrpHdr_node.append(MsgId_node)
GrpHdr_node.append(CreDtTm_node)
GrpHdr_node.append(NbOfTxs_node)
Expand Down
2 changes: 2 additions & 0 deletions tests/debit/test_00800102.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def sdd():
<MndtRltdInf>
<MndtId>1234</MndtId>
<DtOfSgntr>2017-01-20</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
Expand Down Expand Up @@ -160,6 +161,7 @@ def sdd():
<MndtRltdInf>
<MndtId>1234</MndtId>
<DtOfSgntr>2017-01-20</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
Expand Down
2 changes: 2 additions & 0 deletions tests/debit/test_00800108.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def sdd():
<MndtRltdInf>
<MndtId>1234</MndtId>
<DtOfSgntr>2017-01-20</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
Expand Down Expand Up @@ -160,6 +161,7 @@ def sdd():
<MndtRltdInf>
<MndtId>1234</MndtId>
<DtOfSgntr>2017-01-20</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
Expand Down
2 changes: 2 additions & 0 deletions tests/debit/test_00800109.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def sdd():
<MndtRltdInf>
<MndtId>1234</MndtId>
<DtOfSgntr>2017-01-20</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
Expand Down Expand Up @@ -160,6 +161,7 @@ def sdd():
<MndtRltdInf>
<MndtId>1234</MndtId>
<DtOfSgntr>2017-01-20</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
Expand Down
2 changes: 2 additions & 0 deletions tests/debit/test_00800110.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def sdd():
<MndtRltdInf>
<MndtId>1234</MndtId>
<DtOfSgntr>2017-01-20</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
Expand Down Expand Up @@ -160,6 +161,7 @@ def sdd():
<MndtRltdInf>
<MndtId>1234</MndtId>
<DtOfSgntr>2017-01-20</DtOfSgntr>
<AmdmntInd>false</AmdmntInd>
</MndtRltdInf>
</DrctDbtTx>
<DbtrAgt>
Expand Down
Loading