changeset e84edb3e2966 in modules/account_payment_sepa:default details: https://hg.tryton.org/modules/account_payment_sepa?cmd=changeset;node=e84edb3e2966 description: Add initiator id on journal
Each country/bank has its own requirement about the ID to use on the initiator party so we add an option on the journal to define which identifier to use. issue8063 review284021004 diffstat: CHANGELOG | 1 + __init__.py | 26 +++++++++++++------------- doc/index.rst | 6 ++++++ exceptions.py | 7 +++++++ message.xml | 3 +++ party.py | 40 ++++++++++++++++++++++++++++++++++++++-- party.xml | 6 ++++++ payment.py | 24 +++++++++++++++++++++--- template/base.003.xml | 42 +++++++++++++++++++++++++++++++----------- template/base.xml | 42 +++++++++++++++++++++++++++++++----------- template/pain.001.001.03.xml | 2 +- template/pain.001.001.05.xml | 2 +- template/pain.001.003.03.xml | 2 +- template/pain.008.001.02.xml | 6 +++--- template/pain.008.001.04.xml | 6 +++--- template/pain.008.003.02.xml | 6 +++--- view/party_identifier_form.xml | 9 +++++++++ view/payment_journal_form.xml | 4 ++++ 18 files changed, 182 insertions(+), 52 deletions(-) diffs (491 lines): diff -r d93b59d8aa87 -r e84edb3e2966 CHANGELOG --- a/CHANGELOG Sat Sep 28 23:59:08 2019 +0200 +++ b/CHANGELOG Tue Oct 08 18:24:45 2019 +0200 @@ -1,3 +1,4 @@ +* Add initiator identifiers on journal * Allow to store SEPA message content on the file system Version 5.2.0 - 2019-05-06 diff -r d93b59d8aa87 -r e84edb3e2966 __init__.py --- a/__init__.py Sat Sep 28 23:59:08 2019 +0200 +++ b/__init__.py Tue Oct 08 18:24:45 2019 +0200 @@ -1,23 +1,23 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. from trytond.pool import Pool -from .payment import * -from .party import * -from .account import * +from . import payment +from . import party +from . import account def register(): Pool.register( - Journal, - Group, - Mandate, - Payment, - Message, - Party, - PartyIdentifier, - Configuration, - ConfigurationSepaMandateSequence, + payment.Journal, + payment.Group, + payment.Mandate, + payment.Payment, + payment.Message, + party.Party, + party.PartyIdentifier, + account.Configuration, + account.ConfigurationSepaMandateSequence, module='account_payment_sepa', type_='model') Pool.register( - MandateReport, + payment.MandateReport, module='account_payment_sepa', type_='report') diff -r d93b59d8aa87 -r e84edb3e2966 doc/index.rst --- a/doc/index.rst Sat Sep 28 23:59:08 2019 +0200 +++ b/doc/index.rst Tue Oct 08 18:24:45 2019 +0200 @@ -14,9 +14,15 @@ - Payable Flavor: - pain.001.001.03 - pain.001.001.05 + - pain.001.003.05 - Receivable Flavor: - pain.008.001.02 - pain.008.001.04 + - pain.008.003.02 +- Payable/Receivable Initiator Identifier: + - SEPA Creditor Identifier + - Belgian Enterprise Number + - Spanish VAT Number - Batch Booking. - Charge Bearer: - Debtor diff -r d93b59d8aa87 -r e84edb3e2966 exceptions.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exceptions.py Tue Oct 08 18:24:45 2019 +0200 @@ -0,0 +1,7 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. +from trytond.exceptions import UserError + + +class PartyIdentificationdError(UserError): + pass diff -r d93b59d8aa87 -r e84edb3e2966 message.xml --- a/message.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/message.xml Tue Oct 08 18:24:45 2019 +0200 @@ -18,5 +18,8 @@ <record model="ir.message" id="msg_party_invalid_sepa"> <field name="text">The SEPA creditor identifier "%(code)s" for party "%(party)s" is not valid.</field> </record> + <record model="ir.message" id="msg_party_no_id"> + <field name="text">The party "%(party)s" is missing a "%(type)s" identifier.</field> + </record> </data> </tryton> diff -r d93b59d8aa87 -r e84edb3e2966 party.py --- a/party.py Sat Sep 28 23:59:08 2019 +0200 +++ b/party.py Tue Oct 08 18:24:45 2019 +0200 @@ -10,10 +10,10 @@ from trytond.pool import PoolMeta, Pool from trytond.model import fields from trytond.transaction import Transaction +from trytond.pyson import Eval from trytond.modules.party.exceptions import InvalidIdentifierCode - -__all__ = ['Party', 'PartyIdentifier'] +from .exceptions import PartyIdentificationdError class Party(metaclass=PoolMeta): @@ -51,10 +51,31 @@ if identifier.type == 'sepa': return identifier.code + def get_sepa_identifier(self, name): + pool = Pool() + Identifier = pool.get('party.identifier') + for identifier in self.identifiers: + if identifier.type == name: + return identifier.sepa_identifier + else: + selection = Identifier.fields_get(['type'])['type']['selection'] + type = dict(selection).get(name, name) + raise PartyIdentificationdError( + gettext('account_payment_sepa.msg_party_no_id', + party=self.rec_name, + type=type)) + class PartyIdentifier(metaclass=PoolMeta): __name__ = 'party.identifier' + sepa_es_suffix = fields.Char( + "SEPA Suffix", size=3, + states={ + 'invisible': Eval('type') != 'es_nif', + }, + depends=['type']) + @classmethod def __setup__(cls): super(PartyIdentifier, cls).__setup__() @@ -84,3 +105,18 @@ except stdnum.exceptions.ValidationError: pass return code + + @property + def sepa_identifier(self): + identifier = { + 'Type': 'OrgId', + 'Id': self.code, + } + if self.type == 'sepa': + identifier['Type'] = 'PrvtId' + identifier['SchmeNm'] = {'Prtry': 'SEPA'} + elif self.type == 'be_vat': + identifier['Issr'] = 'KBO-BCE' + elif self.type == 'es_nif': + identifier['Id'] += self.sepa_es_suffix or '000' + return identifier diff -r d93b59d8aa87 -r e84edb3e2966 party.xml --- a/party.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/party.xml Tue Oct 08 18:24:45 2019 +0200 @@ -3,6 +3,12 @@ this repository contains the full copyright notices and license terms. --> <tryton> <data> + <record model="ir.ui.view" id="party_identifier_view_form"> + <field name="model">party.identifier</field> + <field name="inherit" ref="party.identifier_form"/> + <field name="name">party_identifier_form</field> + </record> + <record model="ir.action.act_window" id="act_mandate_form2"> <field name="name">Mandates</field> <field name="res_model">account.payment.sepa.mandate</field> diff -r d93b59d8aa87 -r e84edb3e2966 payment.py --- a/payment.py Sat Sep 28 23:59:08 2019 +0200 +++ b/payment.py Tue Oct 08 18:24:45 2019 +0200 @@ -27,9 +27,6 @@ from .sepa_handler import CAMT054 -__all__ = ['Journal', 'Group', 'Payment', 'Mandate', 'Message', - 'MandateReport'] - # XXX fix: https://genshi.edgewall.org/ticket/582 from genshi.template.astutil import ASTCodeGenerator, ASTTransformer if not hasattr(ASTCodeGenerator, 'visit_NameConstant'): @@ -55,6 +52,13 @@ file_id = None store_prefix = None +INITIATOR_IDS = [ + (None, ''), + ('sepa', "SEPA Creditor Identifier"), + ('be_vat', "Belgian Enterprise Number"), + ('es_nif', "Spanish VAT Number"), + ] + class Journal(metaclass=PoolMeta): __name__ = 'account.payment.journal' @@ -92,6 +96,20 @@ }, translate=False, depends=['process_method']) + sepa_payable_initiator_id = fields.Selection( + INITIATOR_IDS, "SEPA Payable Initiator Identifier", + states={ + 'invisible': Eval('process_method') != 'sepa', + }, + depends=['process_method'], + help="The identifier used for the initiating party.") + sepa_receivable_initiator_id = fields.Selection( + INITIATOR_IDS, "SEPA Receivable Initiator Identifier", + states={ + 'invisible': Eval('process_method') != 'sepa', + }, + depends=['process_method'], + help="The identifier used for the initiating party.") sepa_batch_booking = fields.Boolean('Batch Booking', states={ 'invisible': Eval('process_method') != 'sepa', }, diff -r d93b59d8aa87 -r e84edb3e2966 template/base.003.xml --- a/template/base.003.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/template/base.003.xml Tue Oct 08 18:24:45 2019 +0200 @@ -3,7 +3,7 @@ this repository contains the full copyright notices and license terms. --> <py:strip xmlns:py="http://genshi.edgewall.org/"> <!-- only ASCII --> - <py:def function="PartyIdentification(party, id=True, with_name=True, with_address=True)"> + <py:def function="PartyIdentification(party, id=None, with_name=True, with_address=True)"> <!-- EPC limits to 70 instead of 140 --> <Nm py:if="with_name">${normalize('NFKD', party.name).encode('ascii', 'replace')[:70]}</Nm> <py:if test="with_address"> @@ -13,16 +13,36 @@ </PstlAdr> </py:with> </py:if> - <Id py:if="id and party.sepa_creditor_identifier_used"> - <PrvtId> - <Othr> - <Id>${party.sepa_creditor_identifier_used}</Id> - <SchmeNm> - <Prtry>SEPA</Prtry> - </SchmeNm> - <!-- Issr --> - </Othr> - </PrvtId> + <Id py:if="id"> + <py:with vars="identifier = party.get_sepa_identifier(id)"> + <OrgId py:if="identifier['Type'] == 'OrgId'"> + <BICOrBEI py:if="identifier.get('BICOrBEI')">${identifier['BICOrBEI']}</BICOrBEI> + <Othr py:if="identifier.get('Id')"> + <Id>${identifier['Id']}</Id> + <SchmeNm py:if="identifier.get('SchmeNm')"> + <Cd py:if="identifier['SchmeNm'].get('Cd')">${identifier['SchmeNm']['Cd']}</Cd> + <Prtry py:if="identifier['SchmeNm'].get('Prtry')">${identifier['SchmeNm']['Cd']}</Prtry> + </SchmeNm> + <Issr py:if="identifier.get('Issr')">${identifier['Issr']}</Issr> + </Othr> + </OrgId> + <PrvtId py:if="identifier['Type'] == 'PrvtId'"> + <DtAndPlcOfBirth py:if="identifier.get('DtAndPlcOfBirth')"> + <BirthDt>${identifier['DtAndPlcOfBirth']['BirthDt'].isoformat()}</BirthDt> + <PrvcOfBirth py:if="identifier['DtAndPlcOfBirth'].get('PrvcOfBirth')">${identifier['DtAndPlcOfBirth']['PrvcOfBirth']}</PrvcOfBirth> + <CityOfBirth>${identifier['DtAndPlcOfBirth']['CityOfBirth']}</CityOfBirth> + <CtryOfBirth>${identifier['DtAndPlcOfBirth']['CtryOfBirth']}</CtryOfBirth> + </DtAndPlcOfBirth> + <Othr py:if="identifier.get('Id')"> + <Id>${identifier['Id']}</Id> + <SchmeNm py:if="identifier.get('SchmeNm')"> + <Cd py:if="identifier['SchmeNm'].get('Cd')">${identifier['SchmeNm']['Cd']}</Cd> + <Prtry py:if="identifier['SchmeNm'].get('Prtry')">${identifier['SchmeNm']['Prtry']}</Prtry> + </SchmeNm> + <Issr py:if="identifier.get('Issr')">${identifier['Issr']}</Issr> + </Othr> + </PrvtId> + </py:with> </Id> <!-- CtryOfRes --> <!-- CtctDtls --> diff -r d93b59d8aa87 -r e84edb3e2966 template/base.xml --- a/template/base.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/template/base.xml Tue Oct 08 18:24:45 2019 +0200 @@ -2,7 +2,7 @@ <!-- This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. --> <py:strip xmlns:py="http://genshi.edgewall.org/"> - <py:def function="PartyIdentification(party, id=True)"> + <py:def function="PartyIdentification(party, id=None)"> <!-- EPC limits to 70 instead of 140 --> <Nm>${party.name[:70]}</Nm> <py:with vars="address = party.address_get()"> @@ -10,16 +10,36 @@ ${PostalAddress(address)} </PstlAdr> </py:with> - <Id py:if="id and party.sepa_creditor_identifier_used"> - <PrvtId> - <Othr> - <Id>${party.sepa_creditor_identifier_used}</Id> - <SchmeNm> - <Prtry>SEPA</Prtry> - </SchmeNm> - <!-- Issr --> - </Othr> - </PrvtId> + <Id py:if="id"> + <py:with vars="identifier = party.get_sepa_identifier(id)"> + <OrgId py:if="identifier['Type'] == 'OrgId'"> + <BICOrBEI py:if="identifier.get('BICOrBEI')">${identifier['BICOrBEI']}</BICOrBEI> + <Othr py:if="identifier.get('Id')"> + <Id>${identifier['Id']}</Id> + <SchmeNm py:if="identifier.get('SchmeNm')"> + <Cd py:if="identifier['SchmeNm'].get('Cd')">${identifier['SchmeNm']['Cd']}</Cd> + <Prtry py:if="identifier['SchmeNm'].get('Prtry')">${identifier['SchmeNm']['Cd']}</Prtry> + </SchmeNm> + <Issr py:if="identifier.get('Issr')">${identifier['Issr']}</Issr> + </Othr> + </OrgId> + <PrvtId py:if="identifier['Type'] == 'PrvtId'"> + <DtAndPlcOfBirth py:if="identifier.get('DtAndPlcOfBirth')"> + <BirthDt>${identifier['DtAndPlcOfBirth']['BirthDt'].isoformat()}</BirthDt> + <PrvcOfBirth py:if="identifier['DtAndPlcOfBirth'].get('PrvcOfBirth')">${identifier['DtAndPlcOfBirth']['PrvcOfBirth']}</PrvcOfBirth> + <CityOfBirth>${identifier['DtAndPlcOfBirth']['CityOfBirth']}</CityOfBirth> + <CtryOfBirth>${identifier['DtAndPlcOfBirth']['CtryOfBirth']}</CtryOfBirth> + </DtAndPlcOfBirth> + <Othr py:if="identifier.get('Id')"> + <Id>${identifier['Id']}</Id> + <SchmeNm py:if="identifier.get('SchmeNm')"> + <Cd py:if="identifier['SchmeNm'].get('Cd')">${identifier['SchmeNm']['Cd']}</Cd> + <Prtry py:if="identifier['SchmeNm'].get('Prtry')">${identifier['SchmeNm']['Prtry']}</Prtry> + </SchmeNm> + <Issr py:if="identifier.get('Issr')">${identifier['Issr']}</Issr> + </Othr> + </PrvtId> + </py:with> </Id> <!-- CtryOfRes --> <!-- CtctDtls --> diff -r d93b59d8aa87 -r e84edb3e2966 template/pain.001.001.03.xml --- a/template/pain.001.001.03.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/template/pain.001.001.03.xml Tue Oct 08 18:24:45 2019 +0200 @@ -36,7 +36,7 @@ <!-- PmtTpInf --> <!-- ReqdColltnDt --> <InitgPty> - ${PartyIdentification(group.sepa_initiating_party)} + ${PartyIdentification(group.sepa_initiating_party, id=group.journal.sepa_payable_initiator_id)} </InitgPty> <!-- FwdgAgt --> </GrpHdr> diff -r d93b59d8aa87 -r e84edb3e2966 template/pain.001.001.05.xml --- a/template/pain.001.001.05.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/template/pain.001.001.05.xml Tue Oct 08 18:24:45 2019 +0200 @@ -17,7 +17,7 @@ <!-- PmtTpInf --> <!-- ReqdColltnDt --> <InitgPty> - ${PartyIdentification(group.sepa_initiating_party)} + ${PartyIdentification(group.sepa_initiating_party, id=group.journal.sepa_payable_initiator_id)} </InitgPty> <!-- FwdgAgt --> </GrpHdr> diff -r d93b59d8aa87 -r e84edb3e2966 template/pain.001.003.03.xml --- a/template/pain.001.003.03.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/template/pain.001.003.03.xml Tue Oct 08 18:24:45 2019 +0200 @@ -17,7 +17,7 @@ <NbOfTxs>${sum(len(payments) for _, payments in group.sepa_payments)}</NbOfTxs> <CtrlSum>${sum(p.amount for p in group.payments)}</CtrlSum> <InitgPty> - ${PartyIdentification(group.sepa_initiating_party, with_address=False)} + ${PartyIdentification(group.sepa_initiating_party, id=group.journal.sepa_payable_initiator_id, with_address=False)} </InitgPty> </GrpHdr> <PmtInf py:for="key, payments in group.sepa_payments"> diff -r d93b59d8aa87 -r e84edb3e2966 template/pain.008.001.02.xml --- a/template/pain.008.001.02.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/template/pain.008.001.02.xml Tue Oct 08 18:24:45 2019 +0200 @@ -36,7 +36,7 @@ <!-- PmtTpInf --> <!-- ReqdColltnDt --> <InitgPty> - ${PartyIdentification(group.sepa_initiating_party)} + ${PartyIdentification(group.sepa_initiating_party, id=group.journal.sepa_receivable_initiator_id)} </InitgPty> <!-- FwdgAgt --> </GrpHdr> @@ -61,7 +61,7 @@ </PmtTpInf> <ReqdColltnDt>${key['date'].isoformat()}</ReqdColltnDt> <Cdtr> - ${PartyIdentification(group.company.party, id=False)} + ${PartyIdentification(group.company.party)} </Cdtr> <CdtrAcct> ${Account(group.journal.sepa_bank_account_number)} @@ -75,7 +75,7 @@ <!-- ChrgsAcct --> <!-- ChrgsAcctAgt --> <CdtrSchmeId> - ${PartyIdentification(group.company.party)} + ${PartyIdentification(group.company.party, id='sepa')} </CdtrSchmeId> <DrctDbtTxInf py:for="payment in payments"> <PmtId> diff -r d93b59d8aa87 -r e84edb3e2966 template/pain.008.001.04.xml --- a/template/pain.008.001.04.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/template/pain.008.001.04.xml Tue Oct 08 18:24:45 2019 +0200 @@ -17,7 +17,7 @@ <!-- PmtTpInf --> <!-- ReqdColltnDt --> <InitgPty> - ${PartyIdentification(group.sepa_initiating_party)} + ${PartyIdentification(group.sepa_initiating_party, id=group.journal.sepa_receivable_initiator_id)} </InitgPty> <!-- FwdgAgt --> </GrpHdr> @@ -42,7 +42,7 @@ </PmtTpInf> <ReqdColltnDt>${key['date'].isoformat()}</ReqdColltnDt> <Cdtr> - ${PartyIdentification(group.company.party, id=False)} + ${PartyIdentification(group.company.party)} </Cdtr> <CdtrAcct> ${Account(group.journal.sepa_bank_account_number)} @@ -56,7 +56,7 @@ <!-- ChrgsAcct --> <!-- ChrgsAcctAgt --> <CdtrSchmeId> - ${PartyIdentification(group.company.party)} + ${PartyIdentification(group.company.party, id='sepa')} </CdtrSchmeId> <DrctDbtTxInf py:for="payment in payments"> <PmtId> diff -r d93b59d8aa87 -r e84edb3e2966 template/pain.008.003.02.xml --- a/template/pain.008.003.02.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/template/pain.008.003.02.xml Tue Oct 08 18:24:45 2019 +0200 @@ -17,7 +17,7 @@ <NbOfTxs>${sum(len(payments) for _, payments in group.sepa_payments)}</NbOfTxs> <CtrlSum>${sum(p.amount for p in group.payments)}</CtrlSum> <InitgPty> - ${PartyIdentification(group.sepa_initiating_party, with_address=False)} + ${PartyIdentification(group.sepa_initiating_party, id=group.journal.sepa_receivable_initiator_id, with_address=False)} </InitgPty> </GrpHdr> <PmtInf py:for="key, payments in group.sepa_payments"> @@ -38,7 +38,7 @@ </PmtTpInf> <ReqdColltnDt>${key['date'].isoformat()}</ReqdColltnDt> <Cdtr> - ${PartyIdentification(group.company.party, id=False)} + ${PartyIdentification(group.company.party)} </Cdtr> <CdtrAcct> ${Account(group.journal.sepa_bank_account_number)} @@ -49,7 +49,7 @@ <!-- UltmtCdtr --> <ChrgBr>SLEV</ChrgBr> <CdtrSchmeId> - ${PartyIdentification(group.company.party, with_name=False, with_address=False)} + ${PartyIdentification(group.company.party, id='sepa', with_name=False, with_address=False)} </CdtrSchmeId> <DrctDbtTxInf py:for="payment in payments"> <PmtId> diff -r d93b59d8aa87 -r e84edb3e2966 view/party_identifier_form.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/view/party_identifier_form.xml Tue Oct 08 18:24:45 2019 +0200 @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<!-- This file is part of Tryton. The COPYRIGHT file at the top level of +this repository contains the full copyright notices and license terms. --> +<data> + <xpath expr="//field[@name='code']" position="after"> + <label name="sepa_es_suffix"/> + <field name="sepa_es_suffix"/> + </xpath> +</data> diff -r d93b59d8aa87 -r e84edb3e2966 view/payment_journal_form.xml --- a/view/payment_journal_form.xml Sat Sep 28 23:59:08 2019 +0200 +++ b/view/payment_journal_form.xml Tue Oct 08 18:24:45 2019 +0200 @@ -15,5 +15,9 @@ <field name="sepa_payable_flavor"/> <label name="sepa_receivable_flavor"/> <field name="sepa_receivable_flavor"/> + <label name="sepa_payable_initiator_id"/> + <field name="sepa_payable_initiator_id"/> + <label name="sepa_receivable_initiator_id"/> + <field name="sepa_receivable_initiator_id"/> </xpath> </data>