From 530b6b6cf0ba5139a64e383e66c39a57a1a8d56d Mon Sep 17 00:00:00 2001 From: Valentin Vinagre Urteaga Date: Tue, 21 Apr 2020 18:36:00 +0200 Subject: [PATCH] [IMP] account_banking_pain_base: black, isort, prettier --- account_banking_pain_base/__manifest__.py | 43 +- .../models/account_payment_line.py | 286 ++++---- .../models/account_payment_method.py | 31 +- .../models/account_payment_mode.py | 22 +- .../models/account_payment_order.py | 618 ++++++++++-------- .../models/bank_payment_line.py | 24 +- account_banking_pain_base/models/res_bank.py | 27 +- .../models/res_company.py | 51 +- .../models/res_config_settings.py | 18 +- account_banking_pain_base/post_install.py | 4 +- .../security/security.xml | 7 +- .../views/account_payment_line.xml | 33 +- .../views/account_payment_method.xml | 55 +- .../views/account_payment_mode.xml | 42 +- .../views/account_payment_order.xml | 34 +- .../views/bank_payment_line_view.xml | 30 +- .../views/res_config_settings.xml | 34 +- 17 files changed, 726 insertions(+), 633 deletions(-) diff --git a/account_banking_pain_base/__manifest__.py b/account_banking_pain_base/__manifest__.py index 1900ad351..1fa8e2815 100644 --- a/account_banking_pain_base/__manifest__.py +++ b/account_banking_pain_base/__manifest__.py @@ -4,29 +4,24 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - 'name': 'Account Banking PAIN Base Module', - 'summary': 'Base module for PAIN file generation', - 'version': '12.0.1.0.2', - 'license': 'AGPL-3', - 'author': "Akretion, " - "Noviat, " - "Tecnativa, " - "Odoo Community Association (OCA)", - 'website': 'https://github.com/OCA/bank-payment', - 'category': 'Hidden', - 'depends': ['account_payment_order'], - 'external_dependencies': { - 'python': ['unidecode', 'lxml'], - }, - 'data': [ - 'security/security.xml', - 'views/account_payment_line.xml', - 'views/account_payment_order.xml', - 'views/bank_payment_line_view.xml', - 'views/account_payment_mode.xml', - 'views/res_config_settings.xml', - 'views/account_payment_method.xml', + "name": "Account Banking PAIN Base Module", + "summary": "Base module for PAIN file generation", + "version": "12.0.1.0.2", + "license": "AGPL-3", + "author": "Akretion, " "Noviat, " "Tecnativa, " "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/bank-payment", + "category": "Hidden", + "depends": ["account_payment_order"], + "external_dependencies": {"python": ["unidecode", "lxml"],}, + "data": [ + "security/security.xml", + "views/account_payment_line.xml", + "views/account_payment_order.xml", + "views/bank_payment_line_view.xml", + "views/account_payment_mode.xml", + "views/res_config_settings.xml", + "views/account_payment_method.xml", ], - 'post_init_hook': 'set_default_initiating_party', - 'installable': True, + "post_init_hook": "set_default_initiating_party", + "installable": True, } diff --git a/account_banking_pain_base/models/account_payment_line.py b/account_banking_pain_base/models/account_payment_line.py index e90f40630..003c5a760 100644 --- a/account_banking_pain_base/models/account_payment_line.py +++ b/account_banking_pain_base/models/account_payment_line.py @@ -2,167 +2,171 @@ # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import models, fields +from odoo import fields, models class AccountPaymentLine(models.Model): - _inherit = 'account.payment.line' + _inherit = "account.payment.line" - priority = fields.Selection([ - ('NORM', 'Normal'), - ('HIGH', 'High')], - string='Priority', default='NORM', + 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.") + "the generated PAIN file.", + ) # 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", + 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.") + "set the category purpose, leave the field empty.", + ) purpose = fields.Selection( # Full category purpose list found on: # https://www.iso20022.org/external_code_list.page # Document "External Code Sets spreadsheet" version 31 August, 2018 selection=[ - ('ACCT', 'Account Management'), - ('CASH', 'Cash Management Transfer'), - ('COLL', 'Collection Payment'), - ('INTC', 'Intra Company Payment'), - ('LIMA', 'Liquidity Management'), - ('NETT', 'Netting'), - ('AGRT', 'Agricultural Transfer'), - ('BEXP', 'Business Expenses'), - ('COMC', 'Commercial Payment'), - ('CPYR', 'Copyright'), - ('GDDS', 'Purchase Sale Of Goods'), - ('LICF', 'License Fee'), - ('ROYA', 'Royalties'), - ('SCVE', 'Purchase Sale Of Services'), - ('SUBS', 'Subscription'), - ('SUPP', 'Supplier Payment'), - ('TRAD', 'Trade Services'), - ('CHAR', 'Charity Payment'), - ('COMT', 'Consumer Third Party Consolidated Payment'), - ('CLPR', 'Car Loan Principal Repayment'), - ('GOVI', 'Government Insurance'), - ('HLRP', 'Housing Loan Repayment'), - ('INSU', 'Insurance Premium'), - ('INTE', 'Interest'), - ('LBRI', 'Labor Insurance'), - ('LIFI', 'Life Insurance'), - ('LOAN', 'Loan'), - ('LOAR', 'Loan Repayment'), - ('PPTI', 'Property Insurance'), - ('RINP', 'Recurring Installment Payment'), - ('TRFD', 'Trust Fund'), - ('ADVA', 'Advance Payment'), - ('CCRD', 'Credit Card Payment '), - ('CFEE', 'Cancellation Fee'), - ('COST', 'Costs'), - ('DCRD', 'Debit Card Payment'), - ('GOVT', 'Government Payment'), - ('IHRP', 'Instalment Hire Purchase Agreement'), - ('INSM', 'Installment'), - ('MSVC', 'Multiple Service Types'), - ('NOWS', 'Not Otherwise Specified'), - ('OFEE', 'Opening Fee'), - ('OTHR', 'Other'), - ('PADD', 'Preauthorized debit'), - ('PTSP', 'Payment Terms'), - ('RCPT', 'Receipt Payment'), - ('RENT', 'Rent'), - ('STDY', 'Study'), - ('ANNI', 'Annuity'), - ('CMDT', 'Commodity Transfer'), - ('DERI', 'Derivatives'), - ('DIVD', 'Dividend'), - ('FREX', 'Foreign Exchange'), - ('HEDG', 'Hedging'), - ('PRME', 'Precious Metal'), - ('SAVG', 'Savings'), - ('SECU', 'Securities'), - ('TREA', 'Treasury Payment'), - ('ANTS', 'Anesthesia Services'), - ('CVCF', 'Convalescent Care Facility'), - ('DMEQ', 'Durable Medicale Equipment'), - ('DNTS', 'Dental Services'), - ('HLTC', 'Home Health Care'), - ('HLTI', 'Health Insurance'), - ('HSPC', 'Hospital Care'), - ('ICRF', 'Intermediate Care Facility'), - ('LTCF', 'Long Term Care Facility'), - ('MDCS', 'Medical Services'), - ('VIEW', 'Vision Care'), - ('ALMY', 'Alimony Payment'), - ('BECH', 'Child Benefit'), - ('BENE', 'Unemployment Disability Benefit'), - ('BONU', 'Bonus Payment.'), - ('COMM', 'Commission'), - ('PENS', 'Pension Payment'), - ('PRCP', 'Price Payment'), - ('SALA', 'Salary Payment'), - ('SSBE', 'Social Security Benefit'), - ('ESTX', 'Estate Tax'), - ('HSTX', 'Housing Tax'), - ('INTX', 'Income Tax'), - ('TAXS', 'Tax Payment'), - ('VATX', 'Value Added Tax Payment'), - ('AIRB', 'Air'), - ('BUSB', 'Bus'), - ('FERB', 'Ferry'), - ('RLWY', 'Railway'), - ('CBTV', 'Cable TV Bill'), - ('ELEC', 'Electricity Bill'), - ('ENRG', 'Energies'), - ('GASB', 'Gas Bill'), - ('NWCH', 'Network Charge'), - ('NWCM', 'Network Communication'), - ('OTLC', 'Other Telecom Related Bill'), - ('PHON', 'Telephone Bill'), - ('WTER', 'Water Bill'), + ("ACCT", "Account Management"), + ("CASH", "Cash Management Transfer"), + ("COLL", "Collection Payment"), + ("INTC", "Intra Company Payment"), + ("LIMA", "Liquidity Management"), + ("NETT", "Netting"), + ("AGRT", "Agricultural Transfer"), + ("BEXP", "Business Expenses"), + ("COMC", "Commercial Payment"), + ("CPYR", "Copyright"), + ("GDDS", "Purchase Sale Of Goods"), + ("LICF", "License Fee"), + ("ROYA", "Royalties"), + ("SCVE", "Purchase Sale Of Services"), + ("SUBS", "Subscription"), + ("SUPP", "Supplier Payment"), + ("TRAD", "Trade Services"), + ("CHAR", "Charity Payment"), + ("COMT", "Consumer Third Party Consolidated Payment"), + ("CLPR", "Car Loan Principal Repayment"), + ("GOVI", "Government Insurance"), + ("HLRP", "Housing Loan Repayment"), + ("INSU", "Insurance Premium"), + ("INTE", "Interest"), + ("LBRI", "Labor Insurance"), + ("LIFI", "Life Insurance"), + ("LOAN", "Loan"), + ("LOAR", "Loan Repayment"), + ("PPTI", "Property Insurance"), + ("RINP", "Recurring Installment Payment"), + ("TRFD", "Trust Fund"), + ("ADVA", "Advance Payment"), + ("CCRD", "Credit Card Payment "), + ("CFEE", "Cancellation Fee"), + ("COST", "Costs"), + ("DCRD", "Debit Card Payment"), + ("GOVT", "Government Payment"), + ("IHRP", "Instalment Hire Purchase Agreement"), + ("INSM", "Installment"), + ("MSVC", "Multiple Service Types"), + ("NOWS", "Not Otherwise Specified"), + ("OFEE", "Opening Fee"), + ("OTHR", "Other"), + ("PADD", "Preauthorized debit"), + ("PTSP", "Payment Terms"), + ("RCPT", "Receipt Payment"), + ("RENT", "Rent"), + ("STDY", "Study"), + ("ANNI", "Annuity"), + ("CMDT", "Commodity Transfer"), + ("DERI", "Derivatives"), + ("DIVD", "Dividend"), + ("FREX", "Foreign Exchange"), + ("HEDG", "Hedging"), + ("PRME", "Precious Metal"), + ("SAVG", "Savings"), + ("SECU", "Securities"), + ("TREA", "Treasury Payment"), + ("ANTS", "Anesthesia Services"), + ("CVCF", "Convalescent Care Facility"), + ("DMEQ", "Durable Medicale Equipment"), + ("DNTS", "Dental Services"), + ("HLTC", "Home Health Care"), + ("HLTI", "Health Insurance"), + ("HSPC", "Hospital Care"), + ("ICRF", "Intermediate Care Facility"), + ("LTCF", "Long Term Care Facility"), + ("MDCS", "Medical Services"), + ("VIEW", "Vision Care"), + ("ALMY", "Alimony Payment"), + ("BECH", "Child Benefit"), + ("BENE", "Unemployment Disability Benefit"), + ("BONU", "Bonus Payment."), + ("COMM", "Commission"), + ("PENS", "Pension Payment"), + ("PRCP", "Price Payment"), + ("SALA", "Salary Payment"), + ("SSBE", "Social Security Benefit"), + ("ESTX", "Estate Tax"), + ("HSTX", "Housing Tax"), + ("INTX", "Income Tax"), + ("TAXS", "Tax Payment"), + ("VATX", "Value Added Tax Payment"), + ("AIRB", "Air"), + ("BUSB", "Bus"), + ("FERB", "Ferry"), + ("RLWY", "Railway"), + ("CBTV", "Cable TV Bill"), + ("ELEC", "Electricity Bill"), + ("ENRG", "Energies"), + ("GASB", "Gas Bill"), + ("NWCH", "Network Charge"), + ("NWCM", "Network Communication"), + ("OTLC", "Other Telecom Related Bill"), + ("PHON", "Telephone Bill"), + ("WTER", "Water Bill"), ], help="If neither your bank nor your local regulations oblige you to " - "set the category purpose, leave the field empty.", + "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 # We now use communication_type ; you should add an option # in communication_type with selection_add=[] - communication_type = fields.Selection(selection_add=[('ISO', 'ISO')]) + 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 index e0863a6e8..9aa98b241 100644 --- a/account_banking_pain_base/models/account_payment_method.py +++ b/account_banking_pain_base/models/account_payment_method.py @@ -1,31 +1,34 @@ # © 2016 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError class AccountPaymentMethod(models.Model): - _inherit = 'account.payment.method' + _inherit = "account.payment.method" - pain_version = fields.Selection([], string='PAIN Version') + pain_version = fields.Selection([], string="PAIN Version") convert_to_ascii = fields.Boolean( - string='Convert to ASCII', default=True, + 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.") + "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) + raise UserError(_("No XSD file path found for payment method '%s'") % self.name) - _sql_constraints = [( - # Extending this constraint from account_payment_mode - 'code_payment_type_unique', - 'unique(code, payment_type, pain_version)', - 'A payment method of the same type already exists with this code' - ' and PAIN version' - )] + _sql_constraints = [ + ( + # Extending this constraint from account_payment_mode + "code_payment_type_unique", + "unique(code, payment_type, pain_version)", + "A payment method of the same type already exists with this code" + " and PAIN version", + ) + ] diff --git a/account_banking_pain_base/models/account_payment_mode.py b/account_banking_pain_base/models/account_payment_mode.py index 6797b4704..9b3a71d92 100644 --- a/account_banking_pain_base/models/account_payment_mode.py +++ b/account_banking_pain_base/models/account_payment_mode.py @@ -3,33 +3,39 @@ # © 2016 Antiun Ingenieria S.L. - Antonio Espinosa # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import models, fields +from odoo import fields, models class AccountPaymentMode(models.Model): - _inherit = 'account.payment.mode' + _inherit = "account.payment.mode" initiating_party_issuer = fields.Char( - string='Initiating Party Issuer', size=35, + string="Initiating Party Issuer", + size=35, help="This will be used as the 'Initiating Party Issuer' in the " "PAIN files generated by Odoo. If not defined, Initiating Party " "Issuer from company will be used.\n" "Common format (13): \n" "- Country code (2, optional)\n" "- Company idenfier (N, VAT)\n" - "- Service suffix (N, issued by bank)") + "- Service suffix (N, issued by bank)", + ) initiating_party_identifier = fields.Char( - string='Initiating Party Identifier', size=35, + string="Initiating Party Identifier", + size=35, help="This will be used as the 'Initiating Party Identifier' in " "the PAIN files generated by Odoo. If not defined, Initiating Party " "Identifier from company will be used.\n" "Common format (13): \n" "- Country code (2, optional)\n" "- Company idenfier (N, VAT)\n" - "- Service suffix (N, issued by bank)") + "- Service suffix (N, issued by bank)", + ) initiating_party_scheme = fields.Char( - string='Initiating Party Scheme', size=35, + string="Initiating Party Scheme", + size=35, help="This will be used as the 'Initiating Party Scheme Name' in " "the PAIN files generated by Odoo. This value is determined by the " "financial institution that will process the file. If not defined, " - "no scheme will be used.\n") + "no scheme will be used.\n", + ) diff --git a/account_banking_pain_base/models/account_payment_order.py b/account_banking_pain_base/models/account_payment_order.py index 3c70e221d..633ce4120 100644 --- a/account_banking_pain_base/models/account_payment_order.py +++ b/account_banking_pain_base/models/account_payment_order.py @@ -3,13 +3,14 @@ # © 2016 Antiun Ingenieria S.L. - Antonio Espinosa # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import models, fields, api, _, tools +import logging +from datetime import datetime + +from lxml import etree + +from odoo import _, api, fields, models, tools from odoo.exceptions import UserError from odoo.tools.safe_eval import safe_eval -from datetime import datetime -from lxml import etree -import logging - try: from unidecode import unidecode @@ -20,18 +21,21 @@ logger = logging.getLogger(__name__) class AccountPaymentOrder(models.Model): - _inherit = 'account.payment.order' + _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', + 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 : " @@ -40,32 +44,36 @@ class AccountPaymentOrder(models.Model): "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.") + "debtor.", + ) batch_booking = fields.Boolean( - string='Batch Booking', readonly=True, - states={'draft': [('readonly', False)], 'open': [('readonly', False)]}, - track_visibility='onchange', + 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.") + "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') + "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') + eur = self.env.ref("base.EUR") for order in self: sepa = True - if order.company_partner_bank_id.acc_type != 'iban': + 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': + if pline.partner_bank_id.acc_type != "iban": sepa = False break sepa = order.compute_sepa_final_hook(sepa) @@ -77,12 +85,13 @@ class AccountPaymentOrder(models.Model): return sepa @api.model - def _prepare_field(self, field_name, field_value, eval_ctx, - max_size=0, gen_args=None): + 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' + 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 @@ -90,40 +99,66 @@ class AccountPaymentOrder(models.Model): # 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'): + 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, '-') + value = value.replace(unallowed_ascii_char, "-") except Exception: - error_msg_prefix = _("Cannot compute the field '{field_name}'.") \ - .format( - field_name=field_name) + error_msg_prefix = _("Cannot compute the field '{field_name}'.").format( + field_name=field_name + ) error_msg_details_list = self.except_messages_prepare_field( - eval_ctx, field_name) - error_msg_data = _("Data for evaluation:\n" - "\tcontext: {eval_ctx}\n" - "\tfield path: {field_value}") \ - .format( - eval_ctx=eval_ctx, - field_value=field_value) - raise UserError('\n'.join( - [error_msg_prefix] - + error_msg_details_list - + [error_msg_data])) + eval_ctx, field_name + ) + error_msg_data = _( + "Data for evaluation:\n" + "\tcontext: {eval_ctx}\n" + "\tfield path: {field_value}" + ).format(eval_ctx=eval_ctx, field_value=field_value) + raise UserError( + "\n".join( + [error_msg_prefix] + error_msg_details_list + [error_msg_data] + ) + ) if not isinstance(value, str): raise UserError( - _("The type of the field '%s' is %s. It should be a string " - "or unicode.") - % (field_name, type(value))) + _( + "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) + % field_name + ) if max_size and len(value) > max_size: value = value[0:max_size] return value @@ -136,52 +171,52 @@ class AccountPaymentOrder(models.Model): :return: List containing the error messages. """ error_messages = list() - line = eval_ctx.get('line') + line = eval_ctx.get("line") if line: - error_messages.append( - _("Payment Line has reference '%s'.") % line.name) - partner_bank = eval_ctx.get('partner_bank') + error_messages.append(_("Payment Line has reference '%s'.") % line.name) + partner_bank = eval_ctx.get("partner_bank") if partner_bank: error_messages.append( - _("Partner's bank account is '%s'.") - % partner_bank.display_name) + _("Partner's bank account is '%s'.") % partner_bank.display_name + ) return error_messages @api.model def _validate_xml(self, xml_string, gen_args): - xsd_etree_obj = etree.parse( - tools.file_open(gen_args['pain_xsd_file'])) + 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 as e: - logger.warning( - "The XML file is invalid against the XML Schema Definition") + 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 " + _( + "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") - % str(e)) + "of the problem : %s" + ) + % str(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) + 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']) + "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) + filename = "{}{}.xml".format(gen_args["file_prefix"], self.name) return (xml_string, filename) @api.multi @@ -189,8 +224,8 @@ class AccountPaymentOrder(models.Model): 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, + "xsi": "http://www.w3.org/2001/XMLSchema-instance", + None: "urn:iso:std:iso:20022:tech:xsd:%s" % pain_flavor, } return nsmap @@ -201,149 +236,158 @@ class AccountPaymentOrder(models.Model): @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') + 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') + "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': + 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 = etree.SubElement(group_header, "BtchBookg") batch_booking.text = str(self.batch_booking).lower() - nb_of_transactions = etree.SubElement( - group_header, 'NbOfTxs') - control_sum = etree.SubElement(group_header, 'CtrlSum') + 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' + 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, 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') + self, + parent_node, + payment_info_ident, + 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") 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'] + "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') + if gen_args.get("pain_flavor") != "pain.001.001.02": + batch_booking = etree.SubElement(payment_info, "BtchBookg") batch_booking.text = str(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') + # 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' + 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') + 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 = 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 = 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_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' + 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) + 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''' + """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') + "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) + 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) + self.payment_mode_id.initiating_party_issuer + or self.payment_mode_id.company_id.initiating_party_issuer + ) initiating_party_scheme = ( - self.payment_mode_id.initiating_party_scheme or - self.payment_mode_id.company_id.initiating_party_scheme) + self.payment_mode_id.initiating_party_scheme + or self.payment_mode_id.company_id.initiating_party_scheme + ) # 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_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_scheme: iniparty_org_other_scheme = etree.SubElement( - iniparty_org_other, 'SchmeNm') + iniparty_org_other, "SchmeNm" + ) iniparty_org_other_scheme_name = etree.SubElement( - iniparty_org_other_scheme, 'Prtry') + iniparty_org_other_scheme, "Prtry" + ) iniparty_org_other_scheme_name.text = initiating_party_scheme if initiating_party_issuer: - iniparty_org_other_issuer = etree.SubElement( - iniparty_org_other, 'Issr') + 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 " + _( + "Missing 'Initiating Party Issuer' and/or " "'Initiating Party Identifier' for the company '%s'. " - "Both fields must have a value.") - % self.company_id.name) + "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): + 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 @@ -352,34 +396,30 @@ class AccountPaymentOrder(models.Model): 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'" + 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 = 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_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') + 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' + 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_id( - self, parent_node, party_type, partner): + def generate_party_id(self, parent_node, party_type, partner): """Generate an Id element for partner inside the parent node. party_type can currently be Cdtr or Dbtr. Notably, the initiating party orgid is generated with another mechanism and configured @@ -389,81 +429,101 @@ class AccountPaymentOrder(models.Model): @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') + 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 = 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_address_block( - self, parent_node, partner, gen_args): + def generate_address_block(self, parent_node, partner, gen_args): """Generate the piece of the XML corresponding to PstlAdr""" if partner.country_id: - postal_address = etree.SubElement(parent_node, 'PstlAdr') - if gen_args.get('pain_flavor').startswith( - 'pain.001.001.') or gen_args.get('pain_flavor').startswith( - 'pain.008.001.'): + postal_address = etree.SubElement(parent_node, "PstlAdr") + if gen_args.get("pain_flavor").startswith("pain.001.001.") or gen_args.get( + "pain_flavor" + ).startswith("pain.008.001."): if partner.zip: - pstcd = etree.SubElement(postal_address, 'PstCd') + pstcd = etree.SubElement(postal_address, "PstCd") pstcd.text = self._prepare_field( - 'Postal Code', 'partner.zip', - {'partner': partner}, 16, gen_args=gen_args) + "Postal Code", + "partner.zip", + {"partner": partner}, + 16, + gen_args=gen_args, + ) if partner.city: - twnnm = etree.SubElement(postal_address, 'TwnNm') + twnnm = etree.SubElement(postal_address, "TwnNm") twnnm.text = self._prepare_field( - 'Town Name', 'partner.city', - {'partner': partner}, 35, gen_args=gen_args) - country = etree.SubElement(postal_address, 'Ctry') + "Town Name", + "partner.city", + {"partner": partner}, + 35, + gen_args=gen_args, + ) + country = etree.SubElement(postal_address, "Ctry") country.text = self._prepare_field( - 'Country', 'partner.country_id.code', - {'partner': partner}, 2, gen_args=gen_args) + "Country", + "partner.country_id.code", + {"partner": partner}, + 2, + gen_args=gen_args, + ) if partner.street: - adrline1 = etree.SubElement(postal_address, 'AdrLine') + adrline1 = etree.SubElement(postal_address, "AdrLine") adrline1.text = self._prepare_field( - 'Adress Line1', 'partner.street', - {'partner': partner}, 70, gen_args=gen_args) + "Adress Line1", + "partner.street", + {"partner": partner}, + 70, + gen_args=gen_args, + ) return True @api.model def generate_party_block( - self, parent_node, party_type, order, partner_bank, gen_args, - bank_line=None): + 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'" + assert order in ("B", "C"), "Order can be 'B' or 'C'" party_type_label = _("Partner name") - if party_type == 'Cdtr': + if party_type == "Cdtr": party_type_label = _("Creditor name") - elif party_type == 'Dbtr': + elif party_type == "Dbtr": party_type_label = _("Debtor name") - name = 'partner_bank.partner_id.name' - eval_ctx = {'partner_bank': partner_bank} + name = "partner_bank.partner_id.name" + eval_ctx = {"partner_bank": partner_bank} party_name = self._prepare_field( - party_type_label, name, eval_ctx, gen_args.get('name_maxsize'), - gen_args=gen_args) + 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': + if order == "C": self.generate_party_agent( - parent_node, party_type, order, partner_bank, gen_args, - bank_line=bank_line) + 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 = etree.SubElement(party, "Nm") party_nm.text = party_name partner = partner_bank.partner_id @@ -472,81 +532,99 @@ class AccountPaymentOrder(models.Model): self.generate_party_id(party, party_type, partner) self.generate_party_acc_number( - parent_node, party_type, order, partner_bank, gen_args, - bank_line=bank_line) + parent_node, party_type, order, partner_bank, gen_args, bank_line=bank_line + ) - if order == 'B': + if order == "B": self.generate_party_agent( - parent_node, party_type, order, partner_bank, gen_args, - bank_line=bank_line) + 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) + 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') + 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': + 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_information, "CdtrRefTp" + ) creditor_ref_info_type_code = etree.SubElement( - creditor_ref_info_type, 'Cd') - creditor_ref_info_type_code.text = 'SCOR' + 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_ref_info_type, "Issr" + ) + creditor_ref_info_type_issuer.text = line.communication_type creditor_reference = etree.SubElement( - creditor_ref_information, 'CdtrRef') + creditor_ref_information, "CdtrRef" + ) else: - if gen_args.get('structured_remittance_issuer', True): + if gen_args.get("structured_remittance_issuer", True): creditor_ref_info_type = etree.SubElement( - creditor_ref_information, 'Tp') + creditor_ref_information, "Tp" + ) creditor_ref_info_type_or = etree.SubElement( - creditor_ref_info_type, 'CdOrPrtry') + 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_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_ref_info_type, "Issr" + ) + creditor_ref_info_type_issuer.text = line.communication_type - creditor_reference = etree.SubElement( - creditor_ref_information, 'Ref') + 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) + 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') + 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') + 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 f08ddcdd3..42f891dd2 100644 --- a/account_banking_pain_base/models/bank_payment_line.py +++ b/account_banking_pain_base/models/bank_payment_line.py @@ -1,25 +1,25 @@ # © 2013-2016 Akretion - Alexis de Lattre # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import models, fields, api +from odoo import api, fields, models class BankPaymentLine(models.Model): - _inherit = 'bank.payment.line' + _inherit = "bank.payment.line" - priority = fields.Selection( - related='payment_line_ids.priority', string='Priority') + priority = fields.Selection(related="payment_line_ids.priority", string="Priority") local_instrument = fields.Selection( - related='payment_line_ids.local_instrument', - string='Local Instrument') + related="payment_line_ids.local_instrument", string="Local Instrument" + ) category_purpose = fields.Selection( - related='payment_line_ids.category_purpose', string='Category Purpose') - purpose = fields.Selection( - related='payment_line_ids.purpose') + related="payment_line_ids.category_purpose", string="Category Purpose" + ) + purpose = fields.Selection(related="payment_line_ids.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', 'category_purpose', 'purpose'] + res = super( + BankPaymentLine, self + ).same_fields_payment_line_and_bank_payment_line() + res += ["priority", "local_instrument", "category_purpose", "purpose"] return res diff --git a/account_banking_pain_base/models/res_bank.py b/account_banking_pain_base/models/res_bank.py index c0d58f4ca..9d8dce3b2 100644 --- a/account_banking_pain_base/models/res_bank.py +++ b/account_banking_pain_base/models/res_bank.py @@ -3,17 +3,17 @@ import re -from odoo import api, models, _ +from odoo import _, api, models from odoo.exceptions import ValidationError BIC_REGEX = re.compile(r"[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$") class ResBank(models.Model): - _inherit = 'res.bank' + _inherit = "res.bank" @api.multi - @api.constrains('bic') + @api.constrains("bic") def _check_bic(self): """ This method strengthens the constraint on the BIC of the bank account @@ -22,14 +22,15 @@ class ResBank(models.Model): :raise: ValidationError if the BIC doesn't respect the regex of the SEPA pain schemas. """ - invalid_banks = self.filtered( - lambda r: r.bic and not BIC_REGEX.match(r.bic) - ) + invalid_banks = self.filtered(lambda r: r.bic and not BIC_REGEX.match(r.bic)) if invalid_banks: - raise ValidationError(_( - "The following Bank Identifier Codes (BIC) do not respect " - "the SEPA pattern:\n{bic_list}\n\nSEPA pattern: " - "{sepa_pattern}").format( - sepa_pattern=BIC_REGEX.pattern, - bic_list="\n".join(invalid_banks.mapped('bic')) - )) + raise ValidationError( + _( + "The following Bank Identifier Codes (BIC) do not respect " + "the SEPA pattern:\n{bic_list}\n\nSEPA pattern: " + "{sepa_pattern}" + ).format( + sepa_pattern=BIC_REGEX.pattern, + bic_list="\n".join(invalid_banks.mapped("bic")), + ) + ) diff --git a/account_banking_pain_base/models/res_company.py b/account_banking_pain_base/models/res_company.py index c2711a0a3..f00a88a8b 100644 --- a/account_banking_pain_base/models/res_company.py +++ b/account_banking_pain_base/models/res_company.py @@ -3,53 +3,56 @@ # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import models, fields import logging +from odoo import fields, models + logger = logging.getLogger(__name__) class ResCompany(models.Model): - _inherit = 'res.company' + _inherit = "res.company" initiating_party_issuer = fields.Char( - string='Initiating Party Issuer', size=35, + string="Initiating Party Issuer", + size=35, help="This will be used as the 'Initiating Party Issuer' in the " - "PAIN files generated by Odoo.") + "PAIN files generated by Odoo.", + ) initiating_party_identifier = fields.Char( - string='Initiating Party Identifier', size=35, + string="Initiating Party Identifier", + size=35, help="This will be used as the 'Initiating Party Identifier' in " - "the PAIN files generated by Odoo.") + "the PAIN files generated by Odoo.", + ) initiating_party_scheme = fields.Char( - string='Initiating Party Scheme', size=35, + string="Initiating Party Scheme", + size=35, help="This will be used as the 'Initiating Party Scheme Name' in " - "the PAIN files generated by Odoo.") + "the PAIN files generated by Odoo.", + ) def _default_initiating_party(self): - '''This method is called from post_install.py''' + """This method is called from post_install.py""" self.ensure_one() party_issuer_per_country = { - 'BE': 'KBO-BCE', # KBO-BCE = the registry of companies in Belgium + "BE": "KBO-BCE", # KBO-BCE = the registry of companies in Belgium } - logger.debug( - 'Calling _default_initiating_party on company %s', self.name) + logger.debug("Calling _default_initiating_party on company %s", self.name) country_code = self.country_id.code if not self.initiating_party_issuer: if country_code and country_code in party_issuer_per_country: - self.write({ - 'initiating_party_issuer': - party_issuer_per_country[country_code]}) - logger.info( - 'Updated initiating_party_issuer on company %s', - self.name) + self.write( + {"initiating_party_issuer": party_issuer_per_country[country_code]} + ) + logger.info("Updated initiating_party_issuer on company %s", self.name) party_identifier = False if not self.initiating_party_identifier: if self.vat and country_code: - if country_code == 'BE': - party_identifier = self.vat[2:].replace(' ', '') + if country_code == "BE": + party_identifier = self.vat[2:].replace(" ", "") if party_identifier: - self.write({ - 'initiating_party_identifier': party_identifier}) + self.write({"initiating_party_identifier": party_identifier}) logger.info( - 'Updated initiating_party_identifier on company %s', - self.name) + "Updated initiating_party_identifier on company %s", self.name + ) diff --git a/account_banking_pain_base/models/res_config_settings.py b/account_banking_pain_base/models/res_config_settings.py index f4e94ef79..f74e00844 100644 --- a/account_banking_pain_base/models/res_config_settings.py +++ b/account_banking_pain_base/models/res_config_settings.py @@ -6,24 +6,20 @@ from odoo import fields, models class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' + _inherit = "res.config.settings" initiating_party_issuer = fields.Char( - related='company_id.initiating_party_issuer', - readonly=False, + related="company_id.initiating_party_issuer", readonly=False, ) initiating_party_identifier = fields.Char( - related='company_id.initiating_party_identifier', - readonly=False, + related="company_id.initiating_party_identifier", readonly=False, ) initiating_party_scheme = fields.Char( - related='company_id.initiating_party_scheme', - readonly=False, + related="company_id.initiating_party_scheme", readonly=False, ) group_pain_multiple_identifier = fields.Boolean( - string='Multiple identifiers', - implied_group='account_banking_pain_base.' - 'group_pain_multiple_identifier', + string="Multiple identifiers", + implied_group="account_banking_pain_base." "group_pain_multiple_identifier", help="Enable this option if your country requires several SEPA/PAIN " - "identifiers like in Spain.", + "identifiers like in Spain.", ) diff --git a/account_banking_pain_base/post_install.py b/account_banking_pain_base/post_install.py index c438db127..a8ba7b995 100644 --- a/account_banking_pain_base/post_install.py +++ b/account_banking_pain_base/post_install.py @@ -2,12 +2,12 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import api, SUPERUSER_ID +from odoo import SUPERUSER_ID, api def set_default_initiating_party(cr, registry): with api.Environment.manage(): env = api.Environment(cr, SUPERUSER_ID, {}) - for company in env['res.company'].search([]): + for company in env["res.company"].search([]): company._default_initiating_party() return diff --git a/account_banking_pain_base/security/security.xml b/account_banking_pain_base/security/security.xml index 4915015a1..b3f6cbc84 100644 --- a/account_banking_pain_base/security/security.xml +++ b/account_banking_pain_base/security/security.xml @@ -1,10 +1,7 @@ - + - SEPA/PAIN Identifiers on Payment Modes - + - - diff --git a/account_banking_pain_base/views/account_payment_line.xml b/account_banking_pain_base/views/account_payment_line.xml index 88104f1a7..e71be074a 100644 --- a/account_banking_pain_base/views/account_payment_line.xml +++ b/account_banking_pain_base/views/account_payment_line.xml @@ -1,24 +1,23 @@ - + - - - - pain.base.account.payment.line - account.payment.line - - - - - - - + + 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 index 00685f7d6..266d9ff61 100644 --- a/account_banking_pain_base/views/account_payment_method.xml +++ b/account_banking_pain_base/views/account_payment_method.xml @@ -1,30 +1,33 @@ - + - - - - pain_base.account_payment_method.form - account.payment.method - - - - - + + pain_base.account_payment_method.form + account.payment.method + + + + + + - - - - - pain_base.account_payment_method.tree - 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 index 2334125fb..97d292c7b 100644 --- a/account_banking_pain_base/views/account_payment_mode.xml +++ b/account_banking_pain_base/views/account_payment_mode.xml @@ -1,24 +1,32 @@ - + - - - - pain_base.account.payment.mode.form - account.payment.mode - - - - - - - - - - - + + 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 index 59c886dab..8188bf27d 100644 --- a/account_banking_pain_base/views/account_payment_order.xml +++ b/account_banking_pain_base/views/account_payment_order.xml @@ -1,23 +1,25 @@ - + - - - - pain.base.account.payment.order.form - account.payment.order - - - - - - + + 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 85e85e310..1eb1e72d5 100644 --- a/account_banking_pain_base/views/bank_payment_line_view.xml +++ b/account_banking_pain_base/views/bank_payment_line_view.xml @@ -1,24 +1,20 @@ - + - - - - pain.base.bank.payment.line.form - bank.payment.line - - - - - - - + + pain.base.bank.payment.line.form + bank.payment.line + + + + + + + + - - - - + diff --git a/account_banking_pain_base/views/res_config_settings.xml b/account_banking_pain_base/views/res_config_settings.xml index a5942fb52..d677026de 100644 --- a/account_banking_pain_base/views/res_config_settings.xml +++ b/account_banking_pain_base/views/res_config_settings.xml @@ -1,4 +1,4 @@ - +