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
This commit is contained in:
1 parent
977ebe483f
commit
3a1bdb65bf
18 files changed
+182
-52
No files matched your search
@@ -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
|
||||
|
||||
+13
-13
@@ -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')
|
||||
@@ -14,9 +14,15 @@ The Journal has some fields when the process method is SEPA:
|
||||
- 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
|
||||
|
||||
@@ -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
|
||||
@@ -18,5 +18,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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>
|
||||
@@ -10,10 +10,10 @@ from trytond.i18n import gettext
|
||||
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 @@ class Party(metaclass=PoolMeta):
|
||||
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 @@ class PartyIdentifier(metaclass=PoolMeta):
|
||||
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
|
||||
@@ -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>
|
||||
|
||||
+21
-3
@@ -27,9 +27,6 @@ from trytond.modules.company import CompanyReport
|
||||
|
||||
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 @@ else:
|
||||
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 @@ class Journal(metaclass=PoolMeta):
|
||||
},
|
||||
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',
|
||||
},
|
||||
|
||||
+31
-11
@@ -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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</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 -->
|
||||
|
||||
+31
-11
@@ -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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
${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 -->
|
||||
|
||||
@@ -36,7 +36,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<!-- PmtTpInf -->
|
||||
<!-- ReqdColltnDt -->
|
||||
<InitgPty>
|
||||
${PartyIdentification(group.sepa_initiating_party)}
|
||||
${PartyIdentification(group.sepa_initiating_party, id=group.journal.sepa_payable_initiator_id)}
|
||||
</InitgPty>
|
||||
<!-- FwdgAgt -->
|
||||
</GrpHdr>
|
||||
|
||||
@@ -17,7 +17,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<!-- PmtTpInf -->
|
||||
<!-- ReqdColltnDt -->
|
||||
<InitgPty>
|
||||
${PartyIdentification(group.sepa_initiating_party)}
|
||||
${PartyIdentification(group.sepa_initiating_party, id=group.journal.sepa_payable_initiator_id)}
|
||||
</InitgPty>
|
||||
<!-- FwdgAgt -->
|
||||
</GrpHdr>
|
||||
|
||||
@@ -17,7 +17,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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">
|
||||
|
||||
@@ -36,7 +36,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<!-- 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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<!-- ChrgsAcct -->
|
||||
<!-- ChrgsAcctAgt -->
|
||||
<CdtrSchmeId>
|
||||
${PartyIdentification(group.company.party)}
|
||||
${PartyIdentification(group.company.party, id='sepa')}
|
||||
</CdtrSchmeId>
|
||||
<DrctDbtTxInf py:for="payment in payments">
|
||||
<PmtId>
|
||||
|
||||
@@ -17,7 +17,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<!-- 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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<!-- ChrgsAcct -->
|
||||
<!-- ChrgsAcctAgt -->
|
||||
<CdtrSchmeId>
|
||||
${PartyIdentification(group.company.party)}
|
||||
${PartyIdentification(group.company.party, id='sepa')}
|
||||
</CdtrSchmeId>
|
||||
<DrctDbtTxInf py:for="payment in payments">
|
||||
<PmtId>
|
||||
|
||||
@@ -17,7 +17,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
</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 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<!-- 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>
|
||||
|
||||
@@ -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>
|
||||
@@ -15,5 +15,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<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>
|
||||
Reference in new issue
Block a user