diff --git a/CHANGELOG b/CHANGELOG
index 74bcdab2ce..f0b27a373e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,5 @@
+* Add pain.001.003.03 and pain.008.003.02 flavors
+
Version 3.4.0 - 2014-10-20
* Bug fixes (see mercurial logs for details)
* Add mandate report
diff --git a/payment.py b/payment.py
index dea5935e56..92c370a18b 100644
--- a/payment.py
+++ b/payment.py
@@ -2,6 +2,7 @@
#this repository contains the full copyright notices and license terms.
import datetime
import os
+import unicodedata
from itertools import groupby
from io import BytesIO
@@ -43,6 +44,7 @@ class Journal:
(None, ''),
('pain.001.001.03', 'pain.001.001.03'),
('pain.001.001.05', 'pain.001.001.05'),
+ ('pain.001.003.03', 'pain.001.003.03'),
], 'Payable Flavor', states={
'required': Eval('process_method') == 'sepa',
'invisible': Eval('process_method') != 'sepa',
@@ -53,6 +55,7 @@ class Journal:
(None, ''),
('pain.008.001.02', 'pain.008.001.02'),
('pain.008.001.04', 'pain.008.001.04'),
+ ('pain.008.003.02', 'pain.008.003.02'),
], 'Receivable Flavor', states={
'required': Eval('process_method') == 'sepa',
'invisible': Eval('process_method') != 'sepa',
@@ -156,7 +159,8 @@ class Group:
if not self.sepa_messages:
self.sepa_messages = ()
message = tmpl.generate(group=self,
- datetime=datetime).filter(remove_comment).render()
+ datetime=datetime, normalize=unicodedata.normalize,
+ ).filter(remove_comment).render()
message = Message(message=message, type='in', state='waiting')
self.sepa_messages += (message,)
diff --git a/template/base.003.xml b/template/base.003.xml
new file mode 100644
index 0000000000..39555e0587
--- /dev/null
+++ b/template/base.003.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ ${normalize('NFKD', party.name).encode('ascii', 'replace')[:70]}
+
+
+
+ ${PostalAddress(address)}
+
+
+
+
+
+
+ ${party.sepa_creditor_identifier_used}
+
+ SEPA
+
+
+
+
+
+
+
+
+
+ ${normalize('NFKD', address.country.code).encode('ascii', 'replace')}
+ ${normalize('NFKD', address.street).encode('ascii', 'replace')[:70]}
+ ${normalize('NFKD', address.streetbis).encode('ascii', 'replace')[:70]}
+
+
+
+
+ ${account_number.compact_iban}
+
+
+ ${account_number.account.currency.code}
+
+
+
+
+
+ ${bank.bic}
+
+ NOTPROVIDED
+
+
+
+
diff --git a/template/pain.001.003.03.xml b/template/pain.001.003.03.xml
new file mode 100644
index 0000000000..a54207760b
--- /dev/null
+++ b/template/pain.001.003.03.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+ ${group.reference[:35]}
+ ${datetime.datetime.now().isoformat()}
+ ${len(group.payments)}
+ ${sum(p.amount for p in group.payments)}
+
+ ${PartyIdentification(group.sepa_initiating_party, with_address=False)}
+
+
+
+ ${group.sepa_group_payment_id(key)[:35]}
+ TRF
+ ${'true' if group.journal.sepa_batch_booking else 'false'}
+ ${len(payments)}
+ ${sum(p.amount for p in payments)}
+
+
+
+ SEPA
+
+
+
+ ${key['date'].isoformat()}
+
+ ${PartyIdentification(group.company.party)}
+
+
+ ${Account(group.journal.sepa_bank_account_number)}
+
+
+ ${FinancialInstitution(group.journal.sepa_bank_account_number.account.bank, only_bic=True)}
+
+
+ SLEV
+
+
+ ${payment.sepa_instruction_id}
+ ${payment.sepa_end_to_end_id}
+
+
+
+ ${payment.amount}
+
+
+
+
+ ${FinancialInstitution(payment.sepa_bank_account_number.account.bank, only_bic=True)}
+
+
+ ${PartyIdentification(payment.party)}
+
+
+ ${Account(payment.sepa_bank_account_number, with_ccy=False)}
+
+
+
+
+ ${normalize('NFKD', payment.sepa_remittance_information).encode('ascii', 'replace')[:140]}
+
+
+
+
+
diff --git a/template/pain.008.003.02.xml b/template/pain.008.003.02.xml
new file mode 100644
index 0000000000..21d70c832d
--- /dev/null
+++ b/template/pain.008.003.02.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+ ${group.reference[:35]}
+ ${datetime.datetime.now().isoformat()}
+ ${len(group.payments)}
+ ${sum(p.amount for p in group.payments)}
+
+ ${PartyIdentification(group.sepa_initiating_party, with_address=False)}
+
+
+
+ ${group.sepa_group_payment_id(key)[:35]}
+ DD
+ ${'true' if group.journal.sepa_batch_booking else 'false'}
+ ${len(payments)}
+ ${sum(p.amount for p in payments)}
+
+
+ SEPA
+
+
+ ${key['scheme']}
+
+ ${key['sequence_type']}
+
+
+ ${key['date'].isoformat()}
+
+ ${PartyIdentification(group.company.party, id=False)}
+
+
+ ${Account(group.journal.sepa_bank_account_number)}
+
+
+ ${FinancialInstitution(group.journal.sepa_bank_account_number.account.bank, only_bic=True)}
+
+
+ SLEV
+
+ ${PartyIdentification(group.company.party, with_name=False, with_address=False)}
+
+
+
+ ${payment.sepa_instruction_id}
+ ${payment.sepa_end_to_end_id}
+
+ ${payment.amount}
+
+
+
+ ${mandate.identification}
+ ${mandate.signature_date.isoformat()}
+
+
+
+
+
+
+
+
+ ${FinancialInstitution(payment.sepa_bank_account_number.account.bank, only_bic=True)}
+
+
+ ${PartyIdentification(payment.party)}
+
+
+ ${Account(payment.sepa_bank_account_number, with_ccy=False)}
+
+
+
+
+ ${normalize('NFKD', payment.sepa_remittance_information).encode('ascii', 'replace')[:140]}
+
+
+
+
+
+
diff --git a/tests/pain.001.003.03.xsd b/tests/pain.001.003.03.xsd
new file mode 100644
index 0000000000..a90ed99a1e
--- /dev/null
+++ b/tests/pain.001.003.03.xsd
@@ -0,0 +1,474 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ If used, it is recommended to be used at ‘Payment Information’ level and not at ‘Credit Transfer Transaction Information’ level.
+
+
+
+
+
+ It is recommended that this element be specified at ‘Payment Information’ level.
+
+
+
+
+ This data element may be present either at ‘Payment Information’ or at ‘Credit Transfer Transaction Information’ level.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ If a Creditor Reference contains a check digit, the receiving bank is not required to validate this.
+If the receiving bank validates the check digit and if this validation fails, the bank may continue its processing and send the transaction to the next party in the chain.
+RF Creditor Reference may be used (ISO 11649).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Either ‘BIC or BEI’ or one
+occurrence of ‘Other’ is allowed.
+
+
+
+
+ Either ‘Date and Place of Birth’ or one occurrence of ‘Other’ is allowed.
+
+
+
+
+
+
+
+
+
+ ‘Name’ is limited to 70 characters
+in length.
+
+
+
+
+
+
+
+
+
+ ‘Name’ is limited to 70 characters
+in length.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Only ‘TRF’ is allowed.
+
+
+
+
+ If present and contains ‘true’, batch booking is requested. If present and contains ‘false’, booking per transaction is requested. If element is not present, pre-agreed customer-to-bank conditions apply.
+
+
+
+
+
+
+ If used, it is recommended to be used only at ‘Payment Information’ level and not at Credit Transfer Transaction Information’ level.
+When Instruction Priority is to be used, ‘Payment Type Information’ must be present at ‘Payment Information’ level.
+
+
+
+
+
+
+
+
+ This data element may be present either at ‘Payment Information’ or at ‘Credit Transfer Transaction Information’ level.
+
+
+
+
+ It is recommended that this element be specified at ‘Payment Information’ level.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ If present, pre-agreed customer-to-bank conditions apply.
+
+
+
+
+
+ Depending on the agreement between the Originator and the Originator Bank, ‘Category Purpose’ may be forwarded to the Beneficiary Bank.
+
+
+
+
+
+
+
+
+
+ Depending on the agreement between the Originator and the Originator Bank, ‘Category Purpose’ may be forwarded to the Beneficiary Bank.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Only codes from the ISO 20022 ExternalPurposeCode list are allowed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ When present, the receiving bank is not obliged to validate the the reference information.
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/pain.008.003.02.xsd b/tests/pain.008.003.02.xsd
new file mode 100644
index 0000000000..99992b5e2b
--- /dev/null
+++ b/tests/pain.008.003.02.xsd
@@ -0,0 +1,614 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Mandatory if changes occur in ‘Mandate Identification’, otherwise not to be used.
+
+
+
+
+ Mandatory if changes occur in 'Creditor Scheme Identification', otherwise not to be used.
+
+
+
+
+ To be used only for changes of accounts within the same bank.
+
+
+
+
+ To use 'Identification’ under 'Other' under 'Financial Institution Identifier with code ‘SMNDA’ to indicate same mandate with new Debtor Agent. To be used with the ‘FRST’ indicator in the ‘Sequence Type’.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ If a Creditor Reference contains a check digit, the receiving bank is not required to validate this.
+If the receiving bank validates the check digit and if this validation fails, the bank may continue its processing and send the transaction to the next party in the chain.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ It is recommended that all transactions within the same ‘Payment Information’ block have the same ‘Creditor Scheme Identification’.
+This data element must be present at either ‘Payment Information’ or ‘Direct Debit
+Transaction’ level.
+
+
+
+
+
+
+
+
+
+
+ It is recommended that this element be specified at ‘Payment Information’ level.
+
+
+
+
+
+ This data element may be present either at ‘Payment Information’ or at ‘Direct Debit Transaction Information’ level.
+
+
+
+
+
+
+
+ Mandatory if provided by the debtor in the mandate.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Mandatory if 'Amendment Indicator' is 'TRUE'
+The reason code from the Rulebook is indicated using one of the following message subelements.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Either ‘BIC or BEI’ or one
+occurrence of ‘Other’ is allowed.
+
+
+
+
+ Either ‘Date and Place of Birth’ or one occurrence of ‘Other’ is allowed
+
+
+
+
+
+
+
+
+
+ Private Identification is used to identify either an organisation or a private
+person.
+
+
+
+
+
+
+
+
+ ‘Name’ is limited to 70 characters in length.
+
+
+
+
+
+
+
+
+
+ ‘Name’ is limited to 70 characters in length.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ If present the new’ Name’ must be specified under ‘Creditor’. ‘Name’ is limited to 70 characters in length.
+
+
+
+
+
+
+
+
+
+ ‘Name’ is limited to 70 characters in length.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ If present and contains ‘true’, batch booking is requested. If present and contains ‘false’, booking per transaction is requested. If element is not present, pre-agreed customer-to-bank conditions apply.
+
+
+
+
+
+
+
+
+
+
+
+ This data element may be present either at ‘Payment Information’ or at ‘Direct Debit Transaction Information’ level.
+
+
+
+
+ It is recommended that this element be specified at ‘Payment Information’ level.
+
+
+
+
+ It is recommended that all transactions within the same ‘Payment Information’ block have the same ‘Creditor Scheme Identification’.
+This data element must be present at either ‘Payment Information’ or ‘Direct Debit
+Transaction’ level.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Only ‘B2B’, 'CORE' or 'COR1' is allowed. The mixing of different Local Instrument values is not allowed in the same message.
+
+
+
+
+ If 'Amendment Indicator' is 'true' and 'Original Debtor Agent' is set to 'SMNDA' this message element must indicate 'FRST'
+
+
+
+
+ Depending on the agreement between the Creditor and the Creditor Bank, ‘Category Purpose’ may be forwarded to the Debtor Bank.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Only one occurrence of ‘Other’ is allowed, and no other sub-elements are allowed.
+Identification must be used with an identifier described in General Message Element Specifications, Chapter 1.5.2 of the Implementation Guide.
+Scheme Name’ under ‘Other’ must specify ‘SEPA’ under ‘Proprietary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Only codes from the ISO 20022 ExternalPurposeCode list are allowed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ When present, the receiving bank is not obliged to validate the reference information.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/test_account_payment_sepa.py b/tests/test_account_payment_sepa.py
index 9b1658a07d..7a186b4f52 100644
--- a/tests/test_account_payment_sepa.py
+++ b/tests/test_account_payment_sepa.py
@@ -16,6 +16,7 @@ from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.modules.account_payment_sepa.payment import CAMT054
+from trytond.pool import Pool
class AccountPaymentSepaTestCase(unittest.TestCase):
@@ -47,6 +48,9 @@ class AccountPaymentSepaTestCase(unittest.TestCase):
test_depends()
def setup_environment(self):
+ pool = Pool()
+ Address = pool.get('party.address')
+
company, = self.company.search([
('rec_name', '=', 'Dunder Mifflin'),
])
@@ -64,6 +68,9 @@ class AccountPaymentSepaTestCase(unittest.TestCase):
bank = self.bank(party=bank_party, bic='BICODEBBXXX')
bank.save()
customer = self.party(name='Customer')
+ address = Address(street='street', streetbis='street bis',
+ zip='1234', city='City')
+ customer.addresses = [address]
customer.save()
return {
'company': company,
@@ -75,12 +82,14 @@ class AccountPaymentSepaTestCase(unittest.TestCase):
return self.bank_account.create([{
'bank': bank,
'owners': [('add', [company.party])],
+ 'currency': company.currency.id,
'numbers': [('create', [{
'type': 'iban',
'number': 'ES8200000000000000000000',
}])]}, {
'bank': bank,
'owners': [('add', [customer])],
+ 'currency': company.currency.id,
'numbers': [('create', [{
'type': 'iban',
'number': 'ES3600000000050000000001',
@@ -155,6 +164,10 @@ class AccountPaymentSepaTestCase(unittest.TestCase):
'Test pain001.001.05 xsd validation'
self.validate_file('pain.001.001.05', 'payable')
+ def test_pain001_003_03(self):
+ 'Test pain001.003.03 xsd validation'
+ self.validate_file('pain.001.003.03', 'payable')
+
def test_pain008_001_02(self):
'Test pain008.001.02 xsd validation'
self.validate_file('pain.008.001.02', 'receivable')
@@ -163,6 +176,10 @@ class AccountPaymentSepaTestCase(unittest.TestCase):
'Test pain008.001.04 xsd validation'
self.validate_file('pain.008.001.04', 'receivable')
+ def test_pain008_003_02(self):
+ 'Test pain008.003.02 xsd validation'
+ self.validate_file('pain.008.003.02', 'receivable')
+
def test_sepa_mandate_sequence(self):
'Test SEPA mandate sequence'
Configuration = POOL.get('account.configuration')