diff --git a/account_banking_pain_base/README.rst b/account_banking_pain_base/README.rst index df673dbf2..5e7f4774c 100644 --- a/account_banking_pain_base/README.rst +++ b/account_banking_pain_base/README.rst @@ -1,6 +1,7 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg :alt: License: AGPL-3 +================================ Account Banking PAIN Base Module ================================ @@ -16,7 +17,8 @@ Installation ============ This module depends on : -- account_banking_payment_export + +- account_payment_order This module is part of the OCA/bank-payment suite. @@ -30,10 +32,9 @@ Usage See 'readme' files of the OCA/bank-payment suite. - -For further information, please visit: - - * https://www.odoo.com/forum/help-1 +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/173/9.0 Known issues / Roadmap ====================== @@ -43,10 +44,10 @@ Known issues / Roadmap Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. -In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed feedback -`here `_. +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. Credits ======= @@ -54,9 +55,9 @@ Credits Contributors ------------ -* Alexis de Lattre +* Alexis de Lattre * Pedro M. Baeza -* Stéphane Bidoul +* Stéphane Bidoul * Ignacio Ibeas - Acysos S.L. * Alexandre Fayolle * Raphaël Valyi diff --git a/account_banking_pain_base/__openerp__.py b/account_banking_pain_base/__openerp__.py index 39ecf238b..809182e29 100644 --- a/account_banking_pain_base/__openerp__.py +++ b/account_banking_pain_base/__openerp__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2013-2015 Akretion - Alexis de Lattre +# © 2013-2016 Akretion - Alexis de Lattre # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza # © 2016 Antiun Ingenieria S.L. - Antonio Espinosa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). @@ -7,7 +7,7 @@ { 'name': 'Account Banking PAIN Base Module', 'summary': 'Base module for PAIN file generation', - 'version': '8.0.0.4.0', + 'version': '9.0.1.0.0', 'license': 'AGPL-3', 'author': "Akretion, " "Noviat, " @@ -17,15 +17,17 @@ 'website': 'https://github.com/OCA/bank-payment', 'contributors': ['Pedro M. Baeza '], 'category': 'Hidden', - 'depends': ['account_banking_payment_export'], + 'depends': ['account_payment_order'], 'external_dependencies': { 'python': ['unidecode', 'lxml'], }, 'data': [ - 'views/payment_line_view.xml', + 'views/account_payment_line.xml', + 'views/account_payment_order.xml', 'views/bank_payment_line_view.xml', - 'views/payment_mode_view.xml', + 'views/account_payment_mode.xml', 'views/res_company_view.xml', + 'views/account_payment_method.xml', ], 'post_init_hook': 'set_default_initiating_party', 'installable': True, diff --git a/account_banking_pain_base/models/__init__.py b/account_banking_pain_base/models/__init__.py index ffc2334f3..1e4dfae26 100644 --- a/account_banking_pain_base/models/__init__.py +++ b/account_banking_pain_base/models/__init__.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -# © 2013 Akretion - Alexis de Lattre +# © 2013-2016 Akretion - Alexis de Lattre # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import payment_line +from . import account_payment_line +from . import account_payment_order from . import bank_payment_line -from . import payment_mode +from . import account_payment_mode from . import res_company -from . import banking_export_pain -from . import res_partner_bank +from . import account_payment_method diff --git a/account_banking_pain_base/models/account_payment_line.py b/account_banking_pain_base/models/account_payment_line.py new file mode 100644 index 000000000..f599860e9 --- /dev/null +++ b/account_banking_pain_base/models/account_payment_line.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# © 2013-2016 Akretion - Alexis de Lattre +# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, fields + + +class AccountPaymentLine(models.Model): + _inherit = 'account.payment.line' + + priority = fields.Selection([ + ('NORM', 'Normal'), + ('HIGH', 'High')], + 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 = fields.Selection([], string='Local Instrument') + # PAIN allows 140 characters + communication = fields.Char(size=140) + # The field struct_communication_type has been dropped in v9 + # We now use communication_type ; you should add an option + # in communication_type with selection_add=[] + communication_type = fields.Selection(selection_add=[('ISO', 'ISO')]) diff --git a/account_banking_pain_base/models/account_payment_method.py b/account_banking_pain_base/models/account_payment_method.py new file mode 100644 index 000000000..9d9499036 --- /dev/null +++ b/account_banking_pain_base/models/account_payment_method.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# © 2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, fields, api, _ +from openerp.exceptions import UserError + + +class AccountPaymentMethod(models.Model): + _inherit = 'account.payment.method' + + pain_version = fields.Selection([], string='PAIN Version') + convert_to_ascii = fields.Boolean( + string='Convert to ASCII', default=True, + help="If active, Odoo will convert each accented character to " + "the corresponding unaccented character, so that only ASCII " + "characters are used in the generated PAIN file.") + + @api.multi + def get_xsd_file_path(self): + """This method is designed to be inherited in the SEPA modules""" + self.ensure_one() + raise UserError(_( + "No XSD file path found for payment method '%s'") % self.name) diff --git a/account_banking_pain_base/models/payment_mode.py b/account_banking_pain_base/models/account_payment_mode.py similarity index 55% rename from account_banking_pain_base/models/payment_mode.py rename to account_banking_pain_base/models/account_payment_mode.py index 832ff2bc9..e0e628a59 100644 --- a/account_banking_pain_base/models/payment_mode.py +++ b/account_banking_pain_base/models/account_payment_mode.py @@ -1,20 +1,15 @@ # -*- coding: utf-8 -*- -# © 2013-2015 Akretion - Alexis de Lattre +# © 2013-2016 Akretion - Alexis de Lattre # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza # © 2016 Antiun Ingenieria S.L. - Antonio Espinosa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, fields, api +from openerp import models, fields -class PaymentMode(models.Model): - _inherit = 'payment.mode' +class AccountPaymentMode(models.Model): + _inherit = 'account.payment.mode' - convert_to_ascii = fields.Boolean( - string='Convert to ASCII', default=True, - help="If active, Odoo will convert each accented caracter to " - "the corresponding unaccented caracter, so that only ASCII " - "caracters are used in the generated PAIN file.") initiating_party_issuer = fields.Char( string='Initiating Party Issuer', size=35, help="This will be used as the 'Initiating Party Issuer' in the " @@ -33,17 +28,3 @@ class PaymentMode(models.Model): "- Country code (2, optional)\n" "- Company idenfier (N, VAT)\n" "- Service suffix (N, issued by bank)") - sepa_type = fields.Char(compute="_compute_sepa_type") - - def _sepa_type_get(self): - """Defined to be inherited by child addons, for instance: - - account_banking_sepa_credit_transfer - - account_banking_sepa_direct_debit - """ - return False - - @api.multi - @api.depends('type') - def _compute_sepa_type(self): - for mode in self: - mode.sepa_type = mode._sepa_type_get() diff --git a/account_banking_pain_base/models/account_payment_order.py b/account_banking_pain_base/models/account_payment_order.py new file mode 100644 index 000000000..917df61dc --- /dev/null +++ b/account_banking_pain_base/models/account_payment_order.py @@ -0,0 +1,483 @@ +# -*- coding: utf-8 -*- +# © 2013-2016 Akretion - Alexis de Lattre +# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza +# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, fields, api, _ +from openerp.exceptions import UserError +from openerp.tools.safe_eval import safe_eval +from datetime import datetime +from lxml import etree +from openerp import tools +import logging + + +try: + from unidecode import unidecode +except ImportError: + unidecode = None + +logger = logging.getLogger(__name__) + + +class AccountPaymentOrder(models.Model): + _inherit = 'account.payment.order' + + sepa = fields.Boolean( + compute='compute_sepa', readonly=True, string="SEPA Payment") + charge_bearer = fields.Selection([ + ('SLEV', 'Following Service Level'), + ('SHAR', 'Shared'), + ('CRED', 'Borne by Creditor'), + ('DEBT', 'Borne by Debtor')], string='Charge Bearer', + default='SLEV', readonly=True, + states={'draft': [('readonly', False)], 'open': [('readonly', False)]}, + track_visibility='onchange', + help="Following service level : transaction charges are to be " + "applied following the rules agreed in the service level " + "and/or scheme (SEPA Core messages must use this). Shared : " + "transaction charges on the debtor side are to be borne by " + "the debtor, transaction charges on the creditor side are to " + "be borne by the creditor. Borne by creditor : all " + "transaction charges are to be borne by the creditor. Borne " + "by debtor : all transaction charges are to be borne by the " + "debtor.") + batch_booking = fields.Boolean( + string='Batch Booking', readonly=True, + states={'draft': [('readonly', False)], 'open': [('readonly', False)]}, + track_visibility='onchange', + help="If true, the bank statement will display only one debit " + "line for all the wire transfers of the SEPA XML file ; if " + "false, the bank statement will display one debit line per wire " + "transfer of the SEPA XML file.") + + @api.multi + @api.depends( + 'company_partner_bank_id.acc_type', + 'payment_line_ids.currency_id', + 'payment_line_ids.partner_bank_id.acc_type') + def compute_sepa(self): + eur = self.env.ref('base.EUR') + for order in self: + sepa = True + if order.company_partner_bank_id.acc_type != 'iban': + sepa = False + for pline in order.payment_line_ids: + if pline.currency_id != eur: + sepa = False + break + if pline.partner_bank_id.acc_type != 'iban': + sepa = False + break + sepa = order.compute_sepa_final_hook(sepa) + self.sepa = sepa + + @api.multi + def compute_sepa_final_hook(self, sepa): + self.ensure_one() + return sepa + + @api.model + def _prepare_field(self, field_name, field_value, eval_ctx, + max_size=0, gen_args=None): + """This function is designed to be inherited !""" + if gen_args is None: + gen_args = {} + assert isinstance(eval_ctx, dict), 'eval_ctx must contain a dict' + try: + value = safe_eval(field_value, eval_ctx) + # SEPA uses XML ; XML = UTF-8 ; UTF-8 = support for all characters + # But we are dealing with banks... + # and many banks don't want non-ASCCI characters ! + # cf section 1.4 "Character set" of the SEPA Credit Transfer + # Scheme Customer-to-bank guidelines + if gen_args.get('convert_to_ascii'): + value = unidecode(value) + unallowed_ascii_chars = [ + '"', '#', '$', '%', '&', '*', ';', '<', '>', '=', '@', + '[', ']', '^', '_', '`', '{', '}', '|', '~', '\\', '!'] + for unallowed_ascii_char in unallowed_ascii_chars: + value = value.replace(unallowed_ascii_char, '-') + except: + line = eval_ctx.get('line') + if line: + raise UserError( + _("Cannot compute the '%s' of the Payment Line with " + "reference '%s'.") + % (field_name, line.name)) + else: + raise UserError( + _("Cannot compute the '%s'.") % field_name) + if not isinstance(value, (str, unicode)): + raise UserError( + _("The type of the field '%s' is %s. It should be a string " + "or unicode.") + % (field_name, type(value))) + if not value: + raise UserError( + _("The '%s' is empty or 0. It should have a non-null value.") + % field_name) + if max_size and len(value) > max_size: + value = value[0:max_size] + return value + + @api.model + def _validate_xml(self, xml_string, gen_args): + xsd_etree_obj = etree.parse( + tools.file_open(gen_args['pain_xsd_file'])) + official_pain_schema = etree.XMLSchema(xsd_etree_obj) + + try: + root_to_validate = etree.fromstring(xml_string) + official_pain_schema.assertValid(root_to_validate) + except Exception, e: + logger.warning( + "The XML file is invalid against the XML Schema Definition") + logger.warning(xml_string) + logger.warning(e) + raise UserError( + _("The generated XML file is not valid against the official " + "XML Schema Definition. The generated XML file and the " + "full error have been written in the server logs. Here " + "is the error, which may give you an idea on the cause " + "of the problem : %s") + % unicode(e)) + return True + + @api.multi + def finalize_sepa_file_creation(self, xml_root, gen_args): + xml_string = etree.tostring( + xml_root, pretty_print=True, encoding='UTF-8', + xml_declaration=True) + logger.debug( + "Generated SEPA XML file in format %s below" + % gen_args['pain_flavor']) + logger.debug(xml_string) + self._validate_xml(xml_string, gen_args) + + filename = '%s%s.xml' % (gen_args['file_prefix'], self.name) + return (xml_string, filename) + + @api.multi + def generate_pain_nsmap(self): + self.ensure_one() + pain_flavor = self.payment_mode_id.payment_method_id.pain_version + nsmap = { + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + None: 'urn:iso:std:iso:20022:tech:xsd:%s' % pain_flavor, + } + return nsmap + + @api.multi + def generate_pain_attrib(self): + self.ensure_one() + return {} + + @api.model + def generate_group_header_block(self, parent_node, gen_args): + group_header = etree.SubElement(parent_node, 'GrpHdr') + message_identification = etree.SubElement( + group_header, 'MsgId') + message_identification.text = self._prepare_field( + 'Message Identification', + 'self.name', + {'self': self}, 35, gen_args=gen_args) + creation_date_time = etree.SubElement(group_header, 'CreDtTm') + creation_date_time.text = datetime.strftime( + datetime.today(), '%Y-%m-%dT%H:%M:%S') + if gen_args.get('pain_flavor') == 'pain.001.001.02': + # batch_booking is in "Group header" with pain.001.001.02 + # and in "Payment info" in pain.001.001.03/04 + batch_booking = etree.SubElement(group_header, 'BtchBookg') + batch_booking.text = unicode(self.batch_booking).lower() + nb_of_transactions = etree.SubElement( + group_header, 'NbOfTxs') + control_sum = etree.SubElement(group_header, 'CtrlSum') + # Grpg removed in pain.001.001.03 + if gen_args.get('pain_flavor') == 'pain.001.001.02': + grouping = etree.SubElement(group_header, 'Grpg') + grouping.text = 'GRPD' + self.generate_initiating_party_block(group_header, gen_args) + return group_header, nb_of_transactions, control_sum + + @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): + payment_info = etree.SubElement(parent_node, 'PmtInf') + payment_info_identification = etree.SubElement( + payment_info, 'PmtInfId') + payment_info_identification.text = self._prepare_field( + 'Payment Information Identification', + payment_info_ident, eval_ctx, 35, gen_args=gen_args) + payment_method = etree.SubElement(payment_info, 'PmtMtd') + payment_method.text = gen_args['payment_method'] + nb_of_transactions = False + control_sum = False + if gen_args.get('pain_flavor') != 'pain.001.001.02': + batch_booking = etree.SubElement(payment_info, 'BtchBookg') + batch_booking.text = unicode(self.batch_booking).lower() + # The "SEPA Customer-to-bank + # Implementation guidelines" for SCT and SDD says that control sum + # and nb_of_transactions should be present + # at both "group header" level and "payment info" level + nb_of_transactions = etree.SubElement( + payment_info, 'NbOfTxs') + control_sum = etree.SubElement(payment_info, 'CtrlSum') + payment_type_info = etree.SubElement( + payment_info, 'PmtTpInf') + if priority and gen_args['payment_method'] != 'DD': + instruction_priority = etree.SubElement( + payment_type_info, 'InstrPrty') + instruction_priority.text = priority + if self.sepa: + service_level = etree.SubElement(payment_type_info, 'SvcLvl') + service_level_code = etree.SubElement(service_level, 'Cd') + service_level_code.text = 'SEPA' + if local_instrument: + local_instrument_root = etree.SubElement( + payment_type_info, 'LclInstrm') + if gen_args.get('local_instrument_type') == 'proprietary': + local_instr_value = etree.SubElement( + local_instrument_root, 'Prtry') + else: + local_instr_value = etree.SubElement( + local_instrument_root, 'Cd') + local_instr_value.text = local_instrument + if sequence_type: + sequence_type_node = etree.SubElement( + payment_type_info, 'SeqTp') + sequence_type_node.text = sequence_type + + if gen_args['payment_method'] == 'DD': + request_date_tag = 'ReqdColltnDt' + else: + request_date_tag = 'ReqdExctnDt' + requested_date_node = etree.SubElement( + payment_info, request_date_tag) + requested_date_node.text = requested_date + return payment_info, nb_of_transactions, control_sum + + @api.model + def _must_have_initiating_party(self, gen_args): + '''This method is designed to be inherited in localization modules for + countries in which the initiating party is required''' + return False + + @api.model + def generate_initiating_party_block(self, parent_node, gen_args): + my_company_name = self._prepare_field( + 'Company Name', + 'self.company_partner_bank_id.partner_id.name', + {'self': self}, gen_args.get('name_maxsize'), gen_args=gen_args) + initiating_party = etree.SubElement(parent_node, 'InitgPty') + initiating_party_name = etree.SubElement(initiating_party, 'Nm') + initiating_party_name.text = my_company_name + initiating_party_identifier = ( + self.payment_mode_id.initiating_party_identifier or + self.payment_mode_id.company_id.initiating_party_identifier) + initiating_party_issuer = ( + self.payment_mode_id.initiating_party_issuer or + self.payment_mode_id.company_id.initiating_party_issuer) + # in pain.008.001.02.ch.01.xsd files they use + # initiating_party_identifier but not initiating_party_issuer + if initiating_party_identifier: + iniparty_id = etree.SubElement(initiating_party, 'Id') + iniparty_org_id = etree.SubElement(iniparty_id, 'OrgId') + iniparty_org_other = etree.SubElement(iniparty_org_id, 'Othr') + iniparty_org_other_id = etree.SubElement(iniparty_org_other, 'Id') + iniparty_org_other_id.text = initiating_party_identifier + if initiating_party_issuer: + iniparty_org_other_issuer = etree.SubElement( + iniparty_org_other, 'Issr') + iniparty_org_other_issuer.text = initiating_party_issuer + elif self._must_have_initiating_party(gen_args): + raise UserError( + _("Missing 'Initiating Party Issuer' and/or " + "'Initiating Party Identifier' for the company '%s'. " + "Both fields must have a value.") + % self.company_id.name) + return True + + @api.model + def generate_party_agent( + self, parent_node, party_type, order, partner_bank, gen_args, + bank_line=None): + """Generate the piece of the XML file corresponding to BIC + This code is mutualized between TRF and DD + Starting from Feb 1st 2016, we should be able to do + cross-border SEPA transfers without BIC, cf + http://www.europeanpaymentscouncil.eu/index.cfm/ + sepa-credit-transfer/iban-and-bic/ + In some localization (l10n_ch_sepa for example), they need the + bank_line argument""" + assert order in ('B', 'C'), "Order can be 'B' or 'C'" + if partner_bank.bank_bic: + party_agent = etree.SubElement(parent_node, '%sAgt' % party_type) + party_agent_institution = etree.SubElement( + party_agent, 'FinInstnId') + party_agent_bic = etree.SubElement( + party_agent_institution, gen_args.get('bic_xml_tag')) + party_agent_bic.text = partner_bank.bank_bic + else: + if order == 'B' or ( + order == 'C' and gen_args['payment_method'] == 'DD'): + party_agent = etree.SubElement( + parent_node, '%sAgt' % party_type) + party_agent_institution = etree.SubElement( + party_agent, 'FinInstnId') + party_agent_other = etree.SubElement( + party_agent_institution, 'Othr') + party_agent_other_identification = etree.SubElement( + party_agent_other, 'Id') + party_agent_other_identification.text = 'NOTPROVIDED' + # for Credit Transfers, in the 'C' block, if BIC is not provided, + # we should not put the 'Creditor Agent' block at all, + # as per the guidelines of the EPC + return True + + @api.model + def generate_party_acc_number( + self, parent_node, party_type, order, partner_bank, gen_args, + bank_line=None): + party_account = etree.SubElement( + parent_node, '%sAcct' % party_type) + party_account_id = etree.SubElement(party_account, 'Id') + if partner_bank.acc_type == 'iban': + party_account_iban = etree.SubElement( + party_account_id, 'IBAN') + party_account_iban.text = partner_bank.sanitized_acc_number + else: + party_account_other = etree.SubElement( + party_account_id, 'Othr') + party_account_other_id = etree.SubElement( + party_account_other, 'Id') + party_account_other_id.text = partner_bank.sanitized_acc_number + return True + + @api.model + def generate_party_block( + self, parent_node, party_type, order, partner_bank, gen_args, + bank_line=None): + """Generate the piece of the XML file corresponding to Name+IBAN+BIC + This code is mutualized between TRF and DD + In some localization (l10n_ch_sepa for example), they need the + bank_line argument""" + assert order in ('B', 'C'), "Order can be 'B' or 'C'" + if party_type == 'Cdtr': + party_type_label = 'Creditor' + elif party_type == 'Dbtr': + party_type_label = 'Debtor' + name = 'partner_bank.partner_id.name' + eval_ctx = {'partner_bank': partner_bank} + party_name = self._prepare_field( + '%s Name' % party_type_label, name, eval_ctx, + gen_args.get('name_maxsize'), gen_args=gen_args) + # At C level, the order is : BIC, Name, IBAN + # At B level, the order is : Name, IBAN, BIC + if order == 'C': + self.generate_party_agent( + parent_node, party_type, order, partner_bank, gen_args, + bank_line=bank_line) + party = etree.SubElement(parent_node, party_type) + party_nm = etree.SubElement(party, 'Nm') + party_nm.text = party_name + partner = partner_bank.partner_id + if partner.country_id: + postal_address = etree.SubElement(party, 'PstlAdr') + country = etree.SubElement(postal_address, 'Ctry') + country.text = self._prepare_field( + 'Country', 'partner.country_id.code', + {'partner': partner}, 2, gen_args=gen_args) + if partner.street: + adrline1 = etree.SubElement(postal_address, 'AdrLine') + adrline1.text = self._prepare_field( + 'Adress Line1', 'partner.street', + {'partner': partner}, 70, gen_args=gen_args) + if partner.city and partner.zip: + adrline2 = etree.SubElement(postal_address, 'AdrLine') + adrline2.text = self._prepare_field( + 'Address Line2', "partner.zip + ' ' + partner.city", + {'partner': partner}, 70, gen_args=gen_args) + + self.generate_party_acc_number( + parent_node, party_type, order, partner_bank, gen_args, + bank_line=bank_line) + + if order == 'B': + self.generate_party_agent( + parent_node, party_type, order, partner_bank, gen_args, + bank_line=bank_line) + return True + + @api.model + def generate_remittance_info_block(self, parent_node, line, gen_args): + remittance_info = etree.SubElement( + parent_node, 'RmtInf') + if line.communication_type == 'normal': + remittance_info_unstructured = etree.SubElement( + remittance_info, 'Ustrd') + remittance_info_unstructured.text = \ + self._prepare_field( + 'Remittance Unstructured Information', + 'line.communication', {'line': line}, 140, + gen_args=gen_args) + else: + remittance_info_structured = etree.SubElement( + remittance_info, 'Strd') + creditor_ref_information = etree.SubElement( + remittance_info_structured, 'CdtrRefInf') + if gen_args.get('pain_flavor') == 'pain.001.001.02': + creditor_ref_info_type = etree.SubElement( + creditor_ref_information, 'CdtrRefTp') + creditor_ref_info_type_code = etree.SubElement( + creditor_ref_info_type, 'Cd') + creditor_ref_info_type_code.text = 'SCOR' + # SCOR means "Structured Communication Reference" + creditor_ref_info_type_issuer = etree.SubElement( + creditor_ref_info_type, 'Issr') + creditor_ref_info_type_issuer.text = \ + line.communication_type + creditor_reference = etree.SubElement( + creditor_ref_information, 'CdtrRef') + else: + if gen_args.get('structured_remittance_issuer', True): + creditor_ref_info_type = etree.SubElement( + creditor_ref_information, 'Tp') + creditor_ref_info_type_or = etree.SubElement( + creditor_ref_info_type, 'CdOrPrtry') + creditor_ref_info_type_code = etree.SubElement( + creditor_ref_info_type_or, 'Cd') + creditor_ref_info_type_code.text = 'SCOR' + creditor_ref_info_type_issuer = etree.SubElement( + creditor_ref_info_type, 'Issr') + creditor_ref_info_type_issuer.text = \ + line.communication_type + + creditor_reference = etree.SubElement( + creditor_ref_information, 'Ref') + + creditor_reference.text = \ + self._prepare_field( + 'Creditor Structured Reference', + 'line.communication', {'line': line}, 35, + gen_args=gen_args) + return True + + @api.model + def generate_creditor_scheme_identification( + self, parent_node, identification, identification_label, + eval_ctx, scheme_name_proprietary, gen_args): + csi_id = etree.SubElement(parent_node, 'Id') + csi_privateid = etree.SubElement(csi_id, 'PrvtId') + csi_other = etree.SubElement(csi_privateid, 'Othr') + csi_other_id = etree.SubElement(csi_other, 'Id') + csi_other_id.text = self._prepare_field( + identification_label, identification, eval_ctx, gen_args=gen_args) + csi_scheme_name = etree.SubElement(csi_other, 'SchmeNm') + csi_scheme_name_proprietary = etree.SubElement( + csi_scheme_name, 'Prtry') + csi_scheme_name_proprietary.text = scheme_name_proprietary + return True diff --git a/account_banking_pain_base/models/bank_payment_line.py b/account_banking_pain_base/models/bank_payment_line.py index e67a46be6..bf802ce6e 100644 --- a/account_banking_pain_base/models/bank_payment_line.py +++ b/account_banking_pain_base/models/bank_payment_line.py @@ -10,13 +10,13 @@ class BankPaymentLine(models.Model): priority = fields.Selection( related='payment_line_ids.priority', string='Priority') - struct_communication_type = fields.Selection( - related='payment_line_ids.struct_communication_type', - string='Structured Communication Type') + local_instrument = fields.Selection( + related='payment_line_ids.local_instrument', + string='Local Instrument') @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', 'struct_communication_type'] + res += ['priority', 'local_instrument'] return res diff --git a/account_banking_pain_base/models/banking_export_pain.py b/account_banking_pain_base/models/banking_export_pain.py deleted file mode 100644 index c25229c42..000000000 --- a/account_banking_pain_base/models/banking_export_pain.py +++ /dev/null @@ -1,410 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2013-2015 Akretion - Alexis de Lattre -# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# © 2016 Antiun Ingenieria S.L. - Antonio Espinosa -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openerp import models, api, _ -from openerp.exceptions import Warning -from openerp.tools.safe_eval import safe_eval -from datetime import datetime -from lxml import etree -from openerp import tools -import logging -import base64 - - -try: - from unidecode import unidecode -except ImportError: - unidecode = None - -logger = logging.getLogger(__name__) - - -class BankingExportPain(models.AbstractModel): - _name = 'banking.export.pain' - - @api.model - def _validate_iban(self, iban): - """if IBAN is valid, returns IBAN - if IBAN is NOT valid, raises an error message""" - if self.env['res.partner.bank'].is_iban_valid(iban): - return iban.replace(' ', '') - else: - raise Warning(_("This IBAN is not valid : %s") % iban) - - @api.model - def _prepare_field(self, field_name, field_value, eval_ctx, - max_size=0, gen_args=None): - """This function is designed to be inherited !""" - if gen_args is None: - gen_args = {} - assert isinstance(eval_ctx, dict), 'eval_ctx must contain a dict' - try: - value = safe_eval(field_value, eval_ctx) - # SEPA uses XML ; XML = UTF-8 ; UTF-8 = support for all characters - # But we are dealing with banks... - # and many banks don't want non-ASCCI characters ! - # cf section 1.4 "Character set" of the SEPA Credit Transfer - # Scheme Customer-to-bank guidelines - if gen_args.get('convert_to_ascii'): - value = unidecode(value) - unallowed_ascii_chars = [ - '"', '#', '$', '%', '&', '*', ';', '<', '>', '=', '@', - '[', ']', '^', '_', '`', '{', '}', '|', '~', '\\', '!'] - for unallowed_ascii_char in unallowed_ascii_chars: - value = value.replace(unallowed_ascii_char, '-') - except: - line = eval_ctx.get('line') - if line: - raise Warning( - _("Cannot compute the '%s' of the Payment Line with " - "reference '%s'.") - % (field_name, line.name)) - else: - raise Warning( - _("Cannot compute the '%s'.") % field_name) - if not isinstance(value, (str, unicode)): - raise Warning( - _("The type of the field '%s' is %s. It should be a string " - "or unicode.") - % (field_name, type(value))) - if not value: - raise Warning( - _("The '%s' is empty or 0. It should have a non-null value.") - % field_name) - if max_size and len(value) > max_size: - value = value[0:max_size] - return value - - @api.model - def _validate_xml(self, xml_string, gen_args): - xsd_etree_obj = etree.parse( - tools.file_open(gen_args['pain_xsd_file'])) - official_pain_schema = etree.XMLSchema(xsd_etree_obj) - - try: - root_to_validate = etree.fromstring(xml_string) - official_pain_schema.assertValid(root_to_validate) - except Exception, e: - logger.warning( - "The XML file is invalid against the XML Schema Definition") - logger.warning(xml_string) - logger.warning(e) - raise Warning( - _("The generated XML file is not valid against the official " - "XML Schema Definition. The generated XML file and the " - "full error have been written in the server logs. Here " - "is the error, which may give you an idea on the cause " - "of the problem : %s") - % unicode(e)) - return True - - @api.multi - def finalize_sepa_file_creation( - self, xml_root, total_amount, transactions_count, gen_args): - xml_string = etree.tostring( - xml_root, pretty_print=True, encoding='UTF-8', - xml_declaration=True) - logger.debug( - "Generated SEPA XML file in format %s below" - % gen_args['pain_flavor']) - logger.debug(xml_string) - self._validate_xml(xml_string, gen_args) - - order_ref = [] - for order in self.payment_order_ids: - if order.reference: - order_ref.append(order.reference.replace('/', '-')) - filename = '%s%s.xml' % (gen_args['file_prefix'], '-'.join(order_ref)) - - self.write({ - 'nb_transactions': transactions_count, - 'total_amount': total_amount, - 'filename': filename, - 'file': base64.encodestring(xml_string), - 'state': 'finish', - }) - - action = { - 'name': _('SEPA File'), - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form,tree', - 'res_model': self._name, - 'res_id': self.ids[0], - 'target': 'new', - } - return action - - @api.model - def generate_group_header_block(self, parent_node, gen_args): - group_header_1_0 = etree.SubElement(parent_node, 'GrpHdr') - message_identification_1_1 = etree.SubElement( - group_header_1_0, 'MsgId') - message_identification_1_1.text = self._prepare_field( - 'Message Identification', - 'self.payment_order_ids[0].reference', - {'self': self}, 35, gen_args=gen_args) - creation_date_time_1_2 = etree.SubElement(group_header_1_0, 'CreDtTm') - creation_date_time_1_2.text = datetime.strftime( - datetime.today(), '%Y-%m-%dT%H:%M:%S') - if gen_args.get('pain_flavor') == 'pain.001.001.02': - # batch_booking is in "Group header" with pain.001.001.02 - # and in "Payment info" in pain.001.001.03/04 - batch_booking = etree.SubElement(group_header_1_0, 'BtchBookg') - batch_booking.text = unicode(self.batch_booking).lower() - nb_of_transactions_1_6 = etree.SubElement( - group_header_1_0, 'NbOfTxs') - control_sum_1_7 = etree.SubElement(group_header_1_0, 'CtrlSum') - # Grpg removed in pain.001.001.03 - if gen_args.get('pain_flavor') == 'pain.001.001.02': - grouping = etree.SubElement(group_header_1_0, 'Grpg') - grouping.text = 'GRPD' - self.generate_initiating_party_block(group_header_1_0, gen_args) - return group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 - - @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): - payment_info_2_0 = etree.SubElement(parent_node, 'PmtInf') - payment_info_identification_2_1 = etree.SubElement( - payment_info_2_0, 'PmtInfId') - payment_info_identification_2_1.text = self._prepare_field( - 'Payment Information Identification', - payment_info_ident, eval_ctx, 35, gen_args=gen_args) - payment_method_2_2 = etree.SubElement(payment_info_2_0, 'PmtMtd') - payment_method_2_2.text = gen_args['payment_method'] - nb_of_transactions_2_4 = False - control_sum_2_5 = False - if gen_args.get('pain_flavor') != 'pain.001.001.02': - batch_booking_2_3 = etree.SubElement(payment_info_2_0, 'BtchBookg') - batch_booking_2_3.text = unicode(self.batch_booking).lower() - # The "SEPA Customer-to-bank - # Implementation guidelines" for SCT and SDD says that control sum - # and nb_of_transactions should be present - # at both "group header" level and "payment info" level - nb_of_transactions_2_4 = etree.SubElement( - payment_info_2_0, 'NbOfTxs') - control_sum_2_5 = etree.SubElement(payment_info_2_0, 'CtrlSum') - payment_type_info_2_6 = etree.SubElement( - payment_info_2_0, 'PmtTpInf') - if priority and gen_args['payment_method'] != 'DD': - instruction_priority_2_7 = etree.SubElement( - payment_type_info_2_6, 'InstrPrty') - instruction_priority_2_7.text = priority - service_level_2_8 = etree.SubElement( - payment_type_info_2_6, 'SvcLvl') - service_level_code_2_9 = etree.SubElement(service_level_2_8, 'Cd') - service_level_code_2_9.text = 'SEPA' - if local_instrument: - local_instrument_2_11 = etree.SubElement( - payment_type_info_2_6, 'LclInstrm') - local_instr_code_2_12 = etree.SubElement( - local_instrument_2_11, 'Cd') - local_instr_code_2_12.text = local_instrument - if sequence_type: - sequence_type_2_14 = etree.SubElement( - payment_type_info_2_6, 'SeqTp') - sequence_type_2_14.text = sequence_type - - if gen_args['payment_method'] == 'DD': - request_date_tag = 'ReqdColltnDt' - else: - request_date_tag = 'ReqdExctnDt' - requested_date_2_17 = etree.SubElement( - payment_info_2_0, request_date_tag) - requested_date_2_17.text = requested_date - return payment_info_2_0, nb_of_transactions_2_4, control_sum_2_5 - - @api.model - def _must_have_initiating_party(self, gen_args): - '''This method is designed to be inherited in localization modules for - countries in which the initiating party is required''' - return False - - @api.model - def generate_initiating_party_block(self, parent_node, gen_args): - my_company_name = self._prepare_field( - 'Company Name', - 'self.payment_order_ids[0].mode.bank_id.partner_id.name', - {'self': self}, gen_args.get('name_maxsize'), gen_args=gen_args) - initiating_party_1_8 = etree.SubElement(parent_node, 'InitgPty') - initiating_party_name = etree.SubElement(initiating_party_1_8, 'Nm') - initiating_party_name.text = my_company_name - payment = self.payment_order_ids[0] - initiating_party_identifier = ( - payment.mode.initiating_party_identifier or - payment.company_id.initiating_party_identifier) - initiating_party_issuer = ( - payment.mode.initiating_party_issuer or - payment.company_id.initiating_party_issuer) - if initiating_party_identifier and initiating_party_issuer: - iniparty_id = etree.SubElement(initiating_party_1_8, 'Id') - iniparty_org_id = etree.SubElement(iniparty_id, 'OrgId') - iniparty_org_other = etree.SubElement(iniparty_org_id, 'Othr') - iniparty_org_other_id = etree.SubElement(iniparty_org_other, 'Id') - iniparty_org_other_id.text = initiating_party_identifier - iniparty_org_other_issuer = etree.SubElement( - iniparty_org_other, 'Issr') - iniparty_org_other_issuer.text = initiating_party_issuer - elif self._must_have_initiating_party(gen_args): - raise Warning( - _("Missing 'Initiating Party Issuer' and/or " - "'Initiating Party Identifier' for the company '%s'. " - "Both fields must have a value.") - % payment.company_id.name) - return True - - @api.model - def generate_party_agent( - self, parent_node, party_type, party_type_label, - order, party_name, iban, bic, eval_ctx, gen_args): - """Generate the piece of the XML file corresponding to BIC - This code is mutualized between TRF and DD""" - assert order in ('B', 'C'), "Order can be 'B' or 'C'" - try: - bic = self._prepare_field( - '%s BIC' % party_type_label, bic, eval_ctx, gen_args=gen_args) - party_agent = etree.SubElement(parent_node, '%sAgt' % party_type) - party_agent_institution = etree.SubElement( - party_agent, 'FinInstnId') - party_agent_bic = etree.SubElement( - party_agent_institution, gen_args.get('bic_xml_tag')) - party_agent_bic.text = bic - except Warning: - if order == 'C': - if iban[0:2] != gen_args['initiating_party_country_code']: - raise Warning( - _('Error:'), - _("The bank account with IBAN '%s' of partner '%s' " - "must have an associated BIC because it is a " - "cross-border SEPA operation.") - % (iban, party_name)) - if order == 'B' or ( - order == 'C' and gen_args['payment_method'] == 'DD'): - party_agent = etree.SubElement( - parent_node, '%sAgt' % party_type) - party_agent_institution = etree.SubElement( - party_agent, 'FinInstnId') - party_agent_other = etree.SubElement( - party_agent_institution, 'Othr') - party_agent_other_identification = etree.SubElement( - party_agent_other, 'Id') - party_agent_other_identification.text = 'NOTPROVIDED' - # for Credit Transfers, in the 'C' block, if BIC is not provided, - # we should not put the 'Creditor Agent' block at all, - # as per the guidelines of the EPC - return True - - @api.model - def generate_party_block( - self, parent_node, party_type, order, name, iban, bic, - eval_ctx, gen_args): - """Generate the piece of the XML file corresponding to Name+IBAN+BIC - This code is mutualized between TRF and DD""" - assert order in ('B', 'C'), "Order can be 'B' or 'C'" - if party_type == 'Cdtr': - party_type_label = 'Creditor' - elif party_type == 'Dbtr': - party_type_label = 'Debtor' - party_name = self._prepare_field( - '%s Name' % party_type_label, name, eval_ctx, - gen_args.get('name_maxsize'), gen_args=gen_args) - piban = self._prepare_field( - '%s IBAN' % party_type_label, iban, eval_ctx, gen_args=gen_args) - viban = self._validate_iban(piban) - # At C level, the order is : BIC, Name, IBAN - # At B level, the order is : Name, IBAN, BIC - if order == 'B': - gen_args['initiating_party_country_code'] = viban[0:2] - elif order == 'C': - self.generate_party_agent( - parent_node, party_type, party_type_label, - order, party_name, viban, bic, eval_ctx, gen_args) - party = etree.SubElement(parent_node, party_type) - party_nm = etree.SubElement(party, 'Nm') - party_nm.text = party_name - party_account = etree.SubElement( - parent_node, '%sAcct' % party_type) - party_account_id = etree.SubElement(party_account, 'Id') - party_account_iban = etree.SubElement( - party_account_id, 'IBAN') - party_account_iban.text = viban - if order == 'B': - self.generate_party_agent( - parent_node, party_type, party_type_label, - order, party_name, viban, bic, eval_ctx, gen_args) - return True - - @api.model - def generate_remittance_info_block(self, parent_node, line, gen_args): - remittance_info_2_91 = etree.SubElement( - parent_node, 'RmtInf') - if line.state == 'normal': - remittance_info_unstructured_2_99 = etree.SubElement( - remittance_info_2_91, 'Ustrd') - remittance_info_unstructured_2_99.text = \ - self._prepare_field( - 'Remittance Unstructured Information', - 'line.communication', {'line': line}, 140, - gen_args=gen_args) - else: - if not line.struct_communication_type: - raise Warning( - _("Missing 'Structured Communication Type' on payment " - "line with reference '%s'.") - % line.name) - remittance_info_structured_2_100 = etree.SubElement( - remittance_info_2_91, 'Strd') - creditor_ref_information_2_120 = etree.SubElement( - remittance_info_structured_2_100, 'CdtrRefInf') - if gen_args.get('pain_flavor') == 'pain.001.001.02': - creditor_ref_info_type_2_121 = etree.SubElement( - creditor_ref_information_2_120, 'CdtrRefTp') - creditor_ref_info_type_code_2_123 = etree.SubElement( - creditor_ref_info_type_2_121, 'Cd') - creditor_ref_info_type_issuer_2_125 = etree.SubElement( - creditor_ref_info_type_2_121, 'Issr') - creditor_reference_2_126 = etree.SubElement( - creditor_ref_information_2_120, 'CdtrRef') - else: - creditor_ref_info_type_2_121 = etree.SubElement( - creditor_ref_information_2_120, 'Tp') - creditor_ref_info_type_or_2_122 = etree.SubElement( - creditor_ref_info_type_2_121, 'CdOrPrtry') - creditor_ref_info_type_code_2_123 = etree.SubElement( - creditor_ref_info_type_or_2_122, 'Cd') - creditor_ref_info_type_issuer_2_125 = etree.SubElement( - creditor_ref_info_type_2_121, 'Issr') - creditor_reference_2_126 = etree.SubElement( - creditor_ref_information_2_120, 'Ref') - - creditor_ref_info_type_code_2_123.text = 'SCOR' - creditor_ref_info_type_issuer_2_125.text = \ - line.struct_communication_type - creditor_reference_2_126.text = \ - self._prepare_field( - 'Creditor Structured Reference', - 'line.communication', {'line': line}, 35, - gen_args=gen_args) - return True - - @api.model - def generate_creditor_scheme_identification( - self, parent_node, identification, identification_label, - eval_ctx, scheme_name_proprietary, gen_args): - csi_id = etree.SubElement(parent_node, 'Id') - csi_privateid = etree.SubElement(csi_id, 'PrvtId') - csi_other = etree.SubElement(csi_privateid, 'Othr') - csi_other_id = etree.SubElement(csi_other, 'Id') - csi_other_id.text = self._prepare_field( - identification_label, identification, eval_ctx, gen_args=gen_args) - csi_scheme_name = etree.SubElement(csi_other, 'SchmeNm') - csi_scheme_name_proprietary = etree.SubElement( - csi_scheme_name, 'Prtry') - csi_scheme_name_proprietary.text = scheme_name_proprietary - return True diff --git a/account_banking_pain_base/models/payment_line.py b/account_banking_pain_base/models/payment_line.py deleted file mode 100644 index 70f214721..000000000 --- a/account_banking_pain_base/models/payment_line.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2013-2015 Akretion - Alexis de Lattre -# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openerp import models, fields, api - - -class PaymentLine(models.Model): - _inherit = 'payment.line' - - @api.model - def _get_struct_communication_types(self): - return [('ISO', 'ISO')] - - priority = fields.Selection([ - ('NORM', 'Normal'), - ('HIGH', 'High')], - string='Priority', default='NORM', - help="This field will be used as the 'Instruction Priority' in " - "the generated PAIN file.") - # Update size from 64 to 140, because PAIN allows 140 caracters - communication = fields.Char(size=140) - struct_communication_type = fields.Selection( - '_get_struct_communication_types', - string='Structured Communication Type', default='ISO') diff --git a/account_banking_pain_base/models/res_partner_bank.py b/account_banking_pain_base/models/res_partner_bank.py deleted file mode 100644 index 76722a561..000000000 --- a/account_banking_pain_base/models/res_partner_bank.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2013 ACSONE SA/NV () -# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openerp import models, fields - - -class ResPartnerBank(models.Model): - _inherit = 'res.partner.bank' - - bank = fields.Many2one(help="If this field is set and the related bank " - "has a 'Bank Identifier Code', then this BIC " - "will be used to generate the credit " - "transfers and direct debits files.") - bank_bic = fields.Char(help="In the generation of credit transfer and " - "direct debit files, this BIC will be used " - "only when the 'Bank' field is empty, or " - "has a value but the field 'Bank Identifier " - "Code' is not set on the related bank.") diff --git a/account_banking_pain_base/post_install.py b/account_banking_pain_base/post_install.py index 90e46fec2..14a9259f9 100644 --- a/account_banking_pain_base/post_install.py +++ b/account_banking_pain_base/post_install.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 Akretion - Alexis de Lattre +# © 2015-2016 Akretion - Alexis de Lattre # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). diff --git a/account_banking_pain_base/views/account_payment_line.xml b/account_banking_pain_base/views/account_payment_line.xml new file mode 100644 index 000000000..640175857 --- /dev/null +++ b/account_banking_pain_base/views/account_payment_line.xml @@ -0,0 +1,23 @@ + + + + + + + pain.base.account.payment.line + account.payment.line + + + + + + + + + + + diff --git a/account_banking_pain_base/views/account_payment_method.xml b/account_banking_pain_base/views/account_payment_method.xml new file mode 100644 index 000000000..8d223b2b0 --- /dev/null +++ b/account_banking_pain_base/views/account_payment_method.xml @@ -0,0 +1,32 @@ + + + + + + + pain_base.account_payment_method.form + account.payment.method + + + + + + + + + + + pain_base.account_payment_method.tree + account.payment.method + + + + + + + + + + + diff --git a/account_banking_pain_base/views/account_payment_mode.xml b/account_banking_pain_base/views/account_payment_mode.xml new file mode 100644 index 000000000..29546b84e --- /dev/null +++ b/account_banking_pain_base/views/account_payment_mode.xml @@ -0,0 +1,27 @@ + + + + + + + + pain_base.account.payment.mode.form + account.payment.mode + + + + + + + + + + + + + diff --git a/account_banking_pain_base/views/account_payment_order.xml b/account_banking_pain_base/views/account_payment_order.xml new file mode 100644 index 000000000..e99928637 --- /dev/null +++ b/account_banking_pain_base/views/account_payment_order.xml @@ -0,0 +1,24 @@ + + + + + + + pain.base.account.payment.order.form + account.payment.order + + + + + + + + + + + + diff --git a/account_banking_pain_base/views/bank_payment_line_view.xml b/account_banking_pain_base/views/bank_payment_line_view.xml index 65e993ad6..672e4bbbe 100644 --- a/account_banking_pain_base/views/bank_payment_line_view.xml +++ b/account_banking_pain_base/views/bank_payment_line_view.xml @@ -1,6 +1,6 @@ @@ -10,13 +10,11 @@ pain.base.bank.payment.line.form bank.payment.line - + - + - - - + diff --git a/account_banking_pain_base/views/payment_line_view.xml b/account_banking_pain_base/views/payment_line_view.xml deleted file mode 100644 index 03759b568..000000000 --- a/account_banking_pain_base/views/payment_line_view.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - pain.base.payment.line.inside.order.form - payment.order - - - - - - - - - - - - - - diff --git a/account_banking_pain_base/views/payment_mode_view.xml b/account_banking_pain_base/views/payment_mode_view.xml deleted file mode 100644 index 3ab148671..000000000 --- a/account_banking_pain_base/views/payment_mode_view.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - add.convert_to_ascii.in.payment.mode.form - payment.mode - - - - - - - - - - - - - - - - - - -