Merge pull request #366 from akretion/10-category-purpose

[10.0] Add support for ISO20022 "Category Purpose"
This commit is contained in:
Pedro M. Baeza
2017-05-29 21:58:43 +02:00
committed by GitHub
9 changed files with 89 additions and 20 deletions

View File

@@ -15,10 +15,47 @@ class AccountPaymentLine(models.Model):
string='Priority', default='NORM',
help="This field will be used as 'Instruction Priority' in "
"the generated PAIN file.")
# local_instrument is used in some countries, for example
# switzerland, cf l10n_ch_sepa that adds some entries in
# the selection field
# local_instrument is used for instant credit transfers which
# will begin on November 2017, cf account_banking_sepa_credit_transfer
# It is also used in some countries such as switzerland,
# cf l10n_ch_pain_base that adds some entries in the selection field
local_instrument = fields.Selection([], string='Local Instrument')
category_purpose = fields.Selection([
# Full category purpose list found on:
# https://www.iso20022.org/external_code_list.page
# Document "External Code Sets spreadsheet" version Feb 8th 2017
('BONU', 'Bonus Payment'),
('CASH', 'Cash Management Transfer'),
('CBLK', 'Card Bulk Clearing'),
('CCRD', 'Credit Card Payment'),
('CORT', 'Trade Settlement Payment'),
('DCRD', 'Debit Card Payment'),
('DIVI', 'Dividend'),
('DVPM', 'Deliver Against Payment'),
('EPAY', 'ePayment'),
('FCOL', 'Fee Collection'),
('GOVT', 'Government Payment'),
('HEDG', 'Hedging'),
('ICCP', 'Irrevocable Credit Card Payment'),
('IDCP', 'Irrevocable Debit Card Payment'),
('INTC', 'Intra-Company Payment'),
('INTE', 'Interest'),
('LOAN', 'Loan'),
('OTHR', 'Other Payment'),
('PENS', 'Pension Payment'),
('RVPM', 'Receive Against Payment'),
('SALA', 'Salary Payment'),
('SECU', 'Securities'),
('SSBE', 'Social Security Benefit'),
('SUPP', 'Supplier Payment'),
('TAXS', 'Tax Payment'),
('TRAD', 'Trade'),
('TREA', 'Treasury Payment'),
('VATX', 'VAT Payment'),
('WHLD', 'WithHolding'),
], string="Category Purpose",
help="If neither your bank nor your local regulations oblige you to "
"set the category purpose, leave the field empty.")
# PAIN allows 140 characters
communication = fields.Char(size=140)
# The field struct_communication_type has been dropped in v9

View File

@@ -203,8 +203,8 @@ class AccountPaymentOrder(models.Model):
@api.model
def generate_start_payment_info_block(
self, parent_node, payment_info_ident,
priority, local_instrument, sequence_type, requested_date,
eval_ctx, gen_args):
priority, local_instrument, category_purpose, sequence_type,
requested_date, eval_ctx, gen_args):
payment_info = etree.SubElement(parent_node, 'PmtInf')
payment_info_identification = etree.SubElement(
payment_info, 'PmtInfId')
@@ -249,7 +249,12 @@ class AccountPaymentOrder(models.Model):
sequence_type_node = etree.SubElement(
payment_type_info, 'SeqTp')
sequence_type_node.text = sequence_type
if category_purpose:
category_purpose_node = etree.SubElement(
payment_type_info, 'CtgyPurp')
category_purpose_code = etree.SubElement(
category_purpose_node, 'Cd')
category_purpose_code.text = category_purpose
if gen_args['payment_method'] == 'DD':
request_date_tag = 'ReqdColltnDt'
else:

View File

@@ -13,10 +13,12 @@ class BankPaymentLine(models.Model):
local_instrument = fields.Selection(
related='payment_line_ids.local_instrument',
string='Local Instrument')
category_purpose = fields.Selection(
related='payment_line_ids.category_purpose', string='Category Purpose')
@api.model
def same_fields_payment_line_and_bank_payment_line(self):
res = super(BankPaymentLine, self).\
same_fields_payment_line_and_bank_payment_line()
res += ['priority', 'local_instrument']
res += ['priority', 'local_instrument', 'category_purpose']
return res

View File

@@ -13,7 +13,8 @@
<field name="arch" type="xml">
<field name="communication_type" position="before">
<field name="priority"/>
<field name="local_instrument" invisible="1"/>
<field name="local_instrument"/>
<field name="category_purpose"/>
</field>
</field>
</record>

View File

@@ -13,7 +13,8 @@
<field name="arch" type="xml">
<field name="partner_bank_id" position="after">
<field name="priority"/>
<field name="local_instrument" invisible="1"/>
<field name="local_instrument"/>
<field name="category_purpose"/>
</field>
</field>
</record>

View File

@@ -2,3 +2,4 @@
from . import account_payment_method
from . import account_payment_order
from . import account_payment_line

View File

@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# © 2017 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, fields
class AccountPaymentLine(models.Model):
_inherit = 'account.payment.line'
# local_instrument 'INST' used for instant credit transfers
# which will begin on November 21st 2017, cf
# https://www.europeanpaymentscouncil.eu/document-library/
# rulebooks/2017-sepa-instant-credit-transfer-rulebook
local_instrument = fields.Selection(
selection_add=[('INST', 'Instant Transfer')])

View File

@@ -78,34 +78,37 @@ class AccountPaymentOrder(models.Model):
transactions_count_a = 0
amount_control_sum_a = 0.0
lines_per_group = {}
# key = (requested_date, priority, local_instrument)
# key = (requested_date, priority, local_instrument, categ_purpose)
# values = list of lines as object
for line in self.bank_line_ids:
priority = line.priority
local_instrument = line.local_instrument
categ_purpose = line.category_purpose
# The field line.date is the requested payment date
# taking into account the 'date_prefered' setting
# cf account_banking_payment_export/models/account_payment.py
# in the inherit of action_open()
key = (line.date, priority, local_instrument)
key = (line.date, priority, local_instrument, categ_purpose)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
lines_per_group[key] = [line]
for (requested_date, priority, local_instrument), lines in\
lines_per_group.items():
for (requested_date, priority, local_instrument, categ_purpose),\
lines in lines_per_group.items():
# B. Payment info
payment_info, nb_of_transactions_b, control_sum_b = \
self.generate_start_payment_info_block(
pain_root,
"self.name + '-' "
"+ requested_date.replace('-', '') + '-' + priority + "
"'-' + local_instrument",
priority, local_instrument, False, requested_date, {
"'-' + local_instrument + '-' + category_purpose",
priority, local_instrument, categ_purpose,
False, requested_date, {
'self': self,
'priority': priority,
'requested_date': requested_date,
'local_instrument': local_instrument or 'NOinstr',
'category_purpose': categ_purpose or 'NOcateg',
}, gen_args)
self.generate_party_block(
payment_info, 'Dbtr', 'B',

View File

@@ -68,6 +68,7 @@ class AccountPaymentOrder(models.Model):
for line in self.bank_line_ids:
transactions_count_a += 1
priority = line.priority
categ_purpose = line.category_purpose
# The field line.date is the requested payment date
# taking into account the 'date_prefered' setting
# cf account_banking_payment_export/models/account_payment.py
@@ -106,25 +107,27 @@ class AccountPaymentOrder(models.Model):
line.mandate_id.recurrent_sequence_type
assert seq_type_label is not False
seq_type = seq_type_map[seq_type_label]
key = (line.date, priority, seq_type, scheme)
key = (line.date, priority, categ_purpose, seq_type, scheme)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
lines_per_group[key] = [line]
for (requested_date, priority, sequence_type, scheme), lines in \
lines_per_group.items():
for (requested_date, priority, categ_purpose, sequence_type, scheme),\
lines in lines_per_group.items():
# B. Payment info
payment_info, nb_of_transactions_b, control_sum_b = \
self.generate_start_payment_info_block(
pain_root,
"self.name + '-' + "
"sequence_type + '-' + requested_date.replace('-', '') "
"+ '-' + priority",
priority, scheme, sequence_type, requested_date, {
"+ '-' + priority + '-' + category_purpose",
priority, scheme, categ_purpose,
sequence_type, requested_date, {
'self': self,
'sequence_type': sequence_type,
'priority': priority,
'category_purpose': categ_purpose or 'NOcateg',
'requested_date': requested_date,
}, gen_args)