diff --git a/account_ebics/README.rst b/account_ebics/README.rst index 634a617..a7337f2 100644 --- a/account_ebics/README.rst +++ b/account_ebics/README.rst @@ -25,6 +25,7 @@ Remark: The EBICS 'Test Mode' for uploading orders requires Fintech 4.3.4 or higher. SWIFT 3SKey support requires Fintech 6.4 or higher. + | We also recommend to consider the installation of the following modules: diff --git a/account_ebics/data/ebics_file_format.xml b/account_ebics/data/ebics_file_format.xml index e720900..42119b9 100644 --- a/account_ebics/data/ebics_file_format.xml +++ b/account_ebics/data/ebics_file_format.xml @@ -1,4 +1,4 @@ - + @@ -9,7 +9,9 @@ down C52 camt.052 - bank to customer account report in format camt.052 + bank to customer account report in format camt.052 c52.xml @@ -18,16 +20,20 @@ down Z52 camt.052 - bank to customer account report in format camt.052 + bank to customer account report in format camt.052 c52.xml - + camt.053 down C53 camt.053 - Bank to customer statement report in format camt.053 + Bank to customer statement report in format camt.053 c53.xml @@ -36,16 +42,20 @@ down Z53 camt.053 - Bank to customer statement report in format camt.053 + Bank to customer statement report in format camt.053 c53.xml - + camt.054 down C54 camt.054 - Bank to customer debit credit notification in format camt.054 + Bank to customer debit credit notification in format camt.054 c52.xml @@ -54,7 +64,9 @@ down Z54 camt.054 - Bank to customer debit credit notification in format camt.054 + Bank to customer debit credit notification in format camt.054 c52.xml @@ -63,7 +75,9 @@ down FDL cfonb120 - Bank to customer statement report in format cfonb120 + Bank to customer statement report in format cfonb120 cfonb120.dat @@ -71,19 +85,23 @@ pain.002 down CDZ - Payment status report for direct debit in format pain.002 + Payment status report for direct debit in format pain.002 psr.xml - + pain.002 down Z01 pain.002 - Payment status report for direct debit in format pain.002 + Payment status report for direct debit in format pain.002 psr.xml - + @@ -114,7 +132,9 @@ pain.008.001.02.sdd up CDD - Sepa Core Direct Debit Order in format pain.008.001.02 + Sepa Core Direct Debit Order in format pain.008.001.02 xml @@ -122,15 +142,19 @@ pain.008.001.02.sdd up XE3 - Sepa Core Direct Debit Order in format pain.008.001.02 + Sepa Core Direct Debit Order in format pain.008.001.02 xml - + pain.008.001.02.sbb up CDB - Sepa Direct Debit (B2B) Order in format pain.008.001.02 + Sepa Direct Debit (B2B) Order in format pain.008.001.02 xml @@ -138,7 +162,9 @@ pain.008.001.02.sbb up XE4 - Sepa Direct Debit (B2B) Order in format pain.008.001.02 + Sepa Direct Debit (B2B) Order in format pain.008.001.02 xml diff --git a/account_ebics/migrations/13.0.1.1/noupdate_changes.xml b/account_ebics/migrations/13.0.1.1/noupdate_changes.xml index ae3cbe1..0c91541 100644 --- a/account_ebics/migrations/13.0.1.1/noupdate_changes.xml +++ b/account_ebics/migrations/13.0.1.1/noupdate_changes.xml @@ -3,16 +3,20 @@ EBICS Configuration model company rule - - - ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] + + + ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] EBICS File model company rule - - - ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] + + + ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] diff --git a/account_ebics/migrations/13.0.1.1/post-migration.py b/account_ebics/migrations/13.0.1.1/post-migration.py index a51a3ac..c1ef5d4 100644 --- a/account_ebics/migrations/13.0.1.1/post-migration.py +++ b/account_ebics/migrations/13.0.1.1/post-migration.py @@ -1,9 +1,10 @@ # Copyright 2009-2020 Noviat. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openupgradelib import openupgrade # pylint: disable=W7936 import os +from openupgradelib import openupgrade # pylint: disable=W7936 + @openupgrade.migrate() def migrate(env, version): @@ -16,58 +17,67 @@ def _ebics_config_upgrade(env, version): env.cr.execute("SELECT * FROM ebics_config") cfg_datas = env.cr.dictfetchall() for cfg_data in cfg_datas: - cfg = env['ebics.config'].browse(cfg_data['id']) - journal = env['account.journal'].search( - [('bank_account_id', '=', cfg_data['bank_id'])]) - keys_fn_old = cfg_data['ebics_keys'] + cfg = env["ebics.config"].browse(cfg_data["id"]) + journal = env["account.journal"].search( + [("bank_account_id", "=", cfg_data["bank_id"])] + ) + keys_fn_old = cfg_data["ebics_keys"] ebics_keys_root = os.path.dirname(keys_fn_old) if os.path.isfile(keys_fn_old): - keys_fn = ebics_keys_root + '/' + cfg_data['ebics_user'] + '_keys' + keys_fn = ebics_keys_root + "/" + cfg_data["ebics_user"] + "_keys" os.rename(keys_fn_old, keys_fn) - state = cfg_data['state'] == 'active' and 'confirm' or 'draft' - cfg.write({ - 'company_ids': [(6, 0, [cfg_data['company_id']])], - 'journal_ids': [(6, 0, journal.ids)], - 'ebics_keys': ebics_keys_root, - 'state': state, - }) + state = cfg_data["state"] == "active" and "confirm" or "draft" + cfg.write( + { + "company_ids": [(6, 0, [cfg_data["company_id"]])], + "journal_ids": [(6, 0, journal.ids)], + "ebics_keys": ebics_keys_root, + "state": state, + } + ) user_vals = { - 'ebics_config_id': cfg_data['id'], - 'name': cfg_data['ebics_user'], + "ebics_config_id": cfg_data["id"], + "name": cfg_data["ebics_user"], } for fld in [ - 'signature_class', 'ebics_passphrase', - 'ebics_ini_letter_fn', 'ebics_public_bank_keys_fn', - 'ebics_key_x509', 'ebics_key_x509_dn_cn', - 'ebics_key_x509_dn_o', 'ebics_key_x509_dn_ou', - 'ebics_key_x509_dn_c', 'ebics_key_x509_dn_st', - 'ebics_key_x509_dn_l', 'ebics_key_x509_dn_e', - 'ebics_file_format_ids', 'state']: + "signature_class", + "ebics_passphrase", + "ebics_ini_letter_fn", + "ebics_public_bank_keys_fn", + "ebics_key_x509", + "ebics_key_x509_dn_cn", + "ebics_key_x509_dn_o", + "ebics_key_x509_dn_ou", + "ebics_key_x509_dn_c", + "ebics_key_x509_dn_st", + "ebics_key_x509_dn_l", + "ebics_key_x509_dn_e", + "ebics_file_format_ids", + "state", + ]: if cfg_data.get(fld): - if fld == 'ebics_file_format_ids': + if fld == "ebics_file_format_ids": user_vals[fld] = [(6, 0, cfg_data[fld])] - elif fld == 'state' and cfg_data['state'] == 'active': - user_vals['state'] = 'active_keys' + elif fld == "state" and cfg_data["state"] == "active": + user_vals["state"] = "active_keys" else: user_vals[fld] = cfg_data[fld] - ebics_userid = env['ebics.userid'].create(user_vals) + ebics_userid = env["ebics.userid"].create(user_vals) env.cr.execute( """ UPDATE ir_attachment SET res_model = 'ebics.userid', res_id = %s WHERE name in ('ebics_ini_letter', 'ebics_public_bank_keys'); """ - % ebics_userid.id) + % ebics_userid.id + ) if len(cfg_datas) == 1: - env.cr.execute( - "UPDATE ebics_file SET ebics_userid_id = %s" % ebics_userid.id) + env.cr.execute("UPDATE ebics_file SET ebics_userid_id = %s" % ebics_userid.id) def _noupdate_changes(env, version): openupgrade.load_data( - env.cr, - 'account_ebics', - 'migrations/13.0.1.1/noupdate_changes.xml' + env.cr, "account_ebics", "migrations/13.0.1.1/noupdate_changes.xml" ) diff --git a/account_ebics/migrations/13.0.1.3/post-migration.py b/account_ebics/migrations/13.0.1.3/post-migration.py index a082120..6c0dd75 100644 --- a/account_ebics/migrations/13.0.1.3/post-migration.py +++ b/account_ebics/migrations/13.0.1.3/post-migration.py @@ -2,16 +2,18 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). _FILE_FORMATS = [ - {'xml_id_name': 'ebics_ff_C52', - 'download_process_method': 'camt.052', - }, - {'xml_id_name': 'ebics_ff_C53', - 'download_process_method': 'camt.053', - }, - {'xml_id_name': 'ebics_ff_FDL_camt_xxx_cfonb120_stm', - 'download_process_method': 'cfonb120', - }, - + { + "xml_id_name": "ebics_ff_C52", + "download_process_method": "camt.052", + }, + { + "xml_id_name": "ebics_ff_C53", + "download_process_method": "camt.053", + }, + { + "xml_id_name": "ebics_ff_FDL_camt_xxx_cfonb120_stm", + "download_process_method": "cfonb120", + }, ] @@ -21,21 +23,22 @@ def migrate(cr, version): def _update_file_format(cr, ff): - cr.execute( + cr.execute( # pylint: disable=E8103 """ SELECT res_id FROM ir_model_data WHERE module='account_ebics' AND name='{}' - """.format(ff['xml_id_name']) + """.format( + ff["xml_id_name"] + ) ) res = cr.fetchone() if res: - cr.execute( + cr.execute( # pylint: disable=E8103 """ UPDATE ebics_file_format SET download_process_method='{download_process_method}' WHERE id={ff_id}; """.format( - download_process_method=ff['download_process_method'], - ff_id=res[0] - ) + download_process_method=ff["download_process_method"], ff_id=res[0] + ) ) diff --git a/account_ebics/migrations/13.0.1.3/pre-migration.py b/account_ebics/migrations/13.0.1.3/pre-migration.py index 7799b26..04212cd 100644 --- a/account_ebics/migrations/13.0.1.3/pre-migration.py +++ b/account_ebics/migrations/13.0.1.3/pre-migration.py @@ -2,36 +2,45 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). _FILE_FORMATS = [ - {'old_xml_id_name': 'ebics_ff_camt_052_001_02_stm', - 'new_xml_id_name': 'ebics_ff_C52', - 'new_name': 'camt.052', - }, - {'old_xml_id_name': 'ebics_ff_camt_053_001_02_stm', - 'new_xml_id_name': 'ebics_ff_C53', - 'new_name': 'camt.053', - }, - {'old_xml_id_name': 'ebics_ff_camt_xxx_cfonb120_stm', - 'new_xml_id_name': 'ebics_ff_FDL_camt_xxx_cfonb120_stm', - }, - {'old_xml_id_name': 'ebics_ff_pain_001_001_03_sct', - 'new_xml_id_name': 'ebics_ff_CCT', - }, - {'old_xml_id_name': 'ebics_ff_pain_001', - 'new_xml_id_name': 'ebics_ff_XE2', - 'new_name': 'pain.001.001.03', - }, - {'old_xml_id_name': 'ebics_ff_pain_008_001_02_sdd', - 'new_xml_id_name': 'ebics_ff_CDD', - }, - {'old_xml_id_name': 'ebics_ff_pain_008', - 'new_xml_id_name': 'ebics_ff_XE3', - }, - {'old_xml_id_name': 'ebics_ff_pain_008_001_02_sbb', - 'new_xml_id_name': 'ebics_ff_CDB', - }, - {'old_xml_id_name': 'ebics_ff_pain_001_001_02_sct', - 'new_xml_id_name': 'ebics_ff_FUL_pain_001_001_02_sct', - }, + { + "old_xml_id_name": "ebics_ff_camt_052_001_02_stm", + "new_xml_id_name": "ebics_ff_C52", + "new_name": "camt.052", + }, + { + "old_xml_id_name": "ebics_ff_camt_053_001_02_stm", + "new_xml_id_name": "ebics_ff_C53", + "new_name": "camt.053", + }, + { + "old_xml_id_name": "ebics_ff_camt_xxx_cfonb120_stm", + "new_xml_id_name": "ebics_ff_FDL_camt_xxx_cfonb120_stm", + }, + { + "old_xml_id_name": "ebics_ff_pain_001_001_03_sct", + "new_xml_id_name": "ebics_ff_CCT", + }, + { + "old_xml_id_name": "ebics_ff_pain_001", + "new_xml_id_name": "ebics_ff_XE2", + "new_name": "pain.001.001.03", + }, + { + "old_xml_id_name": "ebics_ff_pain_008_001_02_sdd", + "new_xml_id_name": "ebics_ff_CDD", + }, + { + "old_xml_id_name": "ebics_ff_pain_008", + "new_xml_id_name": "ebics_ff_XE3", + }, + { + "old_xml_id_name": "ebics_ff_pain_008_001_02_sbb", + "new_xml_id_name": "ebics_ff_CDB", + }, + { + "old_xml_id_name": "ebics_ff_pain_001_001_02_sct", + "new_xml_id_name": "ebics_ff_FUL_pain_001_001_02_sct", + }, ] @@ -44,11 +53,13 @@ def migrate(cr, version): def _update_file_format(cr, ff): - cr.execute( + cr.execute( # pylint: disable=E8103 """ SELECT id, res_id FROM ir_model_data WHERE module='account_ebics' AND name='{}' - """.format(ff['old_xml_id_name']) + """.format( + ff["old_xml_id_name"] + ) ) res = cr.fetchone() if res: @@ -59,7 +70,7 @@ def _update_file_format(cr, ff): """.format( new_xml_id_name=ff["new_xml_id_name"], xml_id=res[0] ) - if ff.get('new_name'): + if ff.get("new_name"): query += """ UPDATE ebics_file_format SET name='{new_name}' @@ -67,4 +78,4 @@ def _update_file_format(cr, ff): """.format( new_name=ff["new_name"], ff_id=res[1] ) - cr.execute(query) + cr.execute(query) # pylint: disable=E8103 diff --git a/account_ebics/models/account_bank_statement.py b/account_ebics/models/account_bank_statement.py index dde134d..e1e1c2b 100644 --- a/account_ebics/models/account_bank_statement.py +++ b/account_ebics/models/account_bank_statement.py @@ -5,7 +5,6 @@ from odoo import fields, models class AccountBankStatement(models.Model): - _inherit = 'account.bank.statement' + _inherit = "account.bank.statement" - ebics_file_id = fields.Many2one( - comodel_name='ebics.file', string='EBICS Data File') + ebics_file_id = fields.Many2one(comodel_name="ebics.file", string="EBICS Data File") diff --git a/account_ebics/models/ebics_config.py b/account_ebics/models/ebics_config.py index 8c616c7..1dcb4fb 100644 --- a/account_ebics/models/ebics_config.py +++ b/account_ebics/models/ebics_config.py @@ -1,9 +1,9 @@ -# Copyright 2009-2020 Noviat. +# Copyright 2009-2022 Noviat. # License LGPL-3 or later (http://www.gnu.org/licenses/lpgl). import logging -import re import os +import re from odoo import _, api, fields, models from odoo.exceptions import UserError @@ -16,118 +16,151 @@ class EbicsConfig(models.Model): EBICS configuration is stored in a separate object in order to allow extra security policies on this object. """ - _name = 'ebics.config' - _description = 'EBICS Configuration' - _order = 'name' + + _name = "ebics.config" + _description = "EBICS Configuration" + _order = "name" name = fields.Char( - string='Name', - readonly=True, states={'draft': [('readonly', False)]}, - required=True) + string="Name", + readonly=True, + states={"draft": [("readonly", False)]}, + required=True, + ) journal_ids = fields.Many2many( - comodel_name='account.journal', - readonly=True, states={'draft': [('readonly', False)]}, - string='Bank Accounts', + comodel_name="account.journal", + readonly=True, + states={"draft": [("readonly", False)]}, + string="Bank Accounts", domain="[('type', '=', 'bank')]", - required=True) + required=True, + ) ebics_host = fields.Char( - string='EBICS HostID', required=True, - readonly=True, states={'draft': [('readonly', False)]}, + string="EBICS HostID", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, help="Contact your bank to get the EBICS HostID." - "\nIn France the BIC is usually allocated to the HostID " - "whereas in Germany it tends to be an institute specific string " - "of 8 characters.") + "\nIn France the BIC is usually allocated to the HostID " + "whereas in Germany it tends to be an institute specific string " + "of 8 characters.", + ) ebics_url = fields.Char( - string='EBICS URL', required=True, - readonly=True, states={'draft': [('readonly', False)]}, - help="Contact your bank to get the EBICS URL.") + string="EBICS URL", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + help="Contact your bank to get the EBICS URL.", + ) ebics_version = fields.Selection( - selection=[('H003', 'H003 (2.4)'), - ('H004', 'H004 (2.5)')], - string='EBICS protocol version', - readonly=True, states={'draft': [('readonly', False)]}, - required=True, default='H004') + selection=[("H003", "H003 (2.4)"), ("H004", "H004 (2.5)")], + string="EBICS protocol version", + readonly=True, + states={"draft": [("readonly", False)]}, + required=True, + default="H004", + ) ebics_partner = fields.Char( - string='EBICS PartnerID', required=True, - readonly=True, states={'draft': [('readonly', False)]}, + string="EBICS PartnerID", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, help="Organizational unit (company or individual) " - "that concludes a contract with the bank. " - "\nIn this contract it will be agreed which order types " - "(file formats) are used, which accounts are concerned, " - "which of the customer's users (subscribers) " - "communicate with the EBICS bank server and the authorisations " - "that these users will possess. " - "\nIt is identified by the PartnerID.") + "that concludes a contract with the bank. " + "\nIn this contract it will be agreed which order types " + "(file formats) are used, which accounts are concerned, " + "which of the customer's users (subscribers) " + "communicate with the EBICS bank server and the authorisations " + "that these users will possess. " + "\nIt is identified by the PartnerID.", + ) ebics_userid_ids = fields.One2many( - comodel_name='ebics.userid', - inverse_name='ebics_config_id', - readonly=True, states={'draft': [('readonly', False)]}, + comodel_name="ebics.userid", + inverse_name="ebics_config_id", + readonly=True, + states={"draft": [("readonly", False)]}, help="Human users or a technical system that is/are " - "assigned to a customer. " - "\nOn the EBICS bank server it is identified " - "by the combination of UserID and PartnerID. " - "The technical subscriber serves only for the data exchange " - "between customer and financial institution. " - "The human user also can authorise orders.") + "assigned to a customer. " + "\nOn the EBICS bank server it is identified " + "by the combination of UserID and PartnerID. " + "The technical subscriber serves only for the data exchange " + "between customer and financial institution. " + "The human user also can authorise orders.", + ) ebics_files = fields.Char( - string='EBICS Files Root', required=True, - readonly=True, states={'draft': [('readonly', False)]}, + string="EBICS Files Root", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, default=lambda self: self._default_ebics_files(), - help="Root Directory for EBICS File Transfer Folders.") + help="Root Directory for EBICS File Transfer Folders.", + ) # We store the EBICS keys in a separate directory in the file system. # This directory requires special protection to reduce fraude. ebics_keys = fields.Char( - string='EBICS Keys Root', required=True, - readonly=True, states={'draft': [('readonly', False)]}, + string="EBICS Keys Root", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, default=lambda self: self._default_ebics_keys(), - help="Root Directory for storing the EBICS Keys.") + help="Root Directory for storing the EBICS Keys.", + ) ebics_key_version = fields.Selection( - selection=[('A005', 'A005 (RSASSA-PKCS1-v1_5)'), - ('A006', 'A006 (RSASSA-PSS)')], - string='EBICS key version', - default='A006', - readonly=True, states={'draft': [('readonly', False)]}, - help="The key version of the electronic signature.") + selection=[("A005", "A005 (RSASSA-PKCS1-v1_5)"), ("A006", "A006 (RSASSA-PSS)")], + string="EBICS key version", + default="A006", + readonly=True, + states={"draft": [("readonly", False)]}, + help="The key version of the electronic signature.", + ) ebics_key_bitlength = fields.Integer( - string='EBICS key bitlength', + string="EBICS key bitlength", default=2048, - readonly=True, states={'draft': [('readonly', False)]}, + readonly=True, + states={"draft": [("readonly", False)]}, help="The bit length of the generated keys. " - "\nThe value must be between 1536 and 4096.") + "\nThe value must be between 1536 and 4096.", + ) ebics_file_format_ids = fields.Many2many( - comodel_name='ebics.file.format', - column1='config_id', column2='format_id', - string='EBICS File Formats', - readonly=True, states={'draft': [('readonly', False)]}, + comodel_name="ebics.file.format", + column1="config_id", + column2="format_id", + string="EBICS File Formats", + readonly=True, + states={"draft": [("readonly", False)]}, ) state = fields.Selection( - [('draft', 'Draft'), - ('confirm', 'Confirmed')], - string='State', - default='draft', - required=True, readonly=True) - order_number = fields.Char( - size=4, readonly=True, states={'draft': [('readonly', False)]}, - help="Specify the number for the next order." - "\nThis number should match the following pattern : " - "[A-Z]{1}[A-Z0-9]{3}") - active = fields.Boolean( - string='Active', default=True) - company_ids = fields.Many2many( - comodel_name='res.company', - string='Companies', + [("draft", "Draft"), ("confirm", "Confirmed")], + string="State", + default="draft", required=True, - help="Companies sharing this EBICS contract.") + readonly=True, + ) + order_number = fields.Char( + size=4, + readonly=True, + states={"draft": [("readonly", False)]}, + help="Specify the number for the next order." + "\nThis number should match the following pattern : " + "[A-Z]{1}[A-Z0-9]{3}", + ) + active = fields.Boolean(string="Active", default=True) + company_ids = fields.Many2many( + comodel_name="res.company", + string="Companies", + required=True, + help="Companies sharing this EBICS contract.", + ) @api.model def _default_ebics_files(self): - return '/'.join(['/home/odoo/ebics_files', self._cr.dbname]) + return "/".join(["/home/odoo/ebics_files", self._cr.dbname]) @api.model def _default_ebics_keys(self): - return '/'.join(['/etc/odoo/ebics_keys', self._cr.dbname]) + return "/".join(["/etc/odoo/ebics_keys", self._cr.dbname]) - @api.constrains('order_number') + @api.constrains("order_number") def _check_order_number(self): for cfg in self: nbr = cfg.order_number @@ -140,26 +173,28 @@ class EbicsConfig(models.Model): if not pattern.match(nbr): ok = False if not ok: - raise UserError(_( - "Order Number should comply with the following pattern:" - "\n[A-Z]{1}[A-Z0-9]{3}")) + raise UserError( + _( + "Order Number should comply with the following pattern:" + "\n[A-Z]{1}[A-Z0-9]{3}" + ) + ) - @api.onchange('journal_ids') + @api.onchange("journal_ids") def _onchange_journal_ids(self): - self.company_ids = self.journal_ids.mapped('company_id') + self.company_ids = self.journal_ids.mapped("company_id") def unlink(self): for ebics_config in self: - if ebics_config.state == 'active': - raise UserError(_( - "You cannot remove active EBICS configurations.")) + if ebics_config.state == "active": + raise UserError(_("You cannot remove active EBICS configurations.")) return super(EbicsConfig, self).unlink() def set_to_draft(self): - return self.write({'state': 'draft'}) + return self.write({"state": "draft"}) def set_to_confirm(self): - return self.write({'state': 'confirm'}) + return self.write({"state": "confirm"}) def _get_order_number(self): return self.order_number @@ -167,31 +202,37 @@ class EbicsConfig(models.Model): def _update_order_number(self, OrderID): o_list = list(OrderID) for i, c in enumerate(reversed(o_list), start=1): - if c == '9': - o_list[-i] = 'A' + if c == "9": + o_list[-i] = "A" break - if c == 'Z': + if c == "Z": continue else: o_list[-i] = chr(ord(c) + 1) break - next = ''.join(o_list) - if next == 'ZZZZ': - next = 'A000' - self.order_number = next + next_order_number = "".join(o_list) + if next_order_number == "ZZZZ": + next_order_number = "A000" + self.order_number = next_order_number def _check_ebics_keys(self): - dirname = self.ebics_keys or '' + dirname = self.ebics_keys or "" if not os.path.exists(dirname): - raise UserError(_( - "EBICS Keys Root Directory %s is not available." - "\nPlease contact your system administrator.") - % dirname) + raise UserError( + _( + "EBICS Keys Root Directory %s is not available." + "\nPlease contact your system administrator." + ) + % dirname + ) def _check_ebics_files(self): - dirname = self.ebics_files or '' + dirname = self.ebics_files or "" if not os.path.exists(dirname): - raise UserError(_( - "EBICS Files Root Directory %s is not available." - "\nPlease contact your system administrator.") - % dirname) + raise UserError( + _( + "EBICS Files Root Directory %s is not available." + "\nPlease contact your system administrator." + ) + % dirname + ) diff --git a/account_ebics/models/ebics_file.py b/account_ebics/models/ebics_file.py index ea3e4e8..43baece 100644 --- a/account_ebics/models/ebics_file.py +++ b/account_ebics/models/ebics_file.py @@ -1,4 +1,4 @@ -# Copyright 2009-2021 Noviat. +# Copyright 2009-2022 Noviat. # License LGPL-3 or later (http://www.gnu.org/licenses/lpgl). import base64 @@ -6,113 +6,119 @@ import logging from odoo import _, fields, models from odoo.exceptions import UserError +from odoo.tools.safe_eval import safe_eval _logger = logging.getLogger(__name__) class EbicsFile(models.Model): - _name = 'ebics.file' - _description = 'Object to store EBICS Data Files' - _order = 'date desc' + _name = "ebics.file" + _description = "Object to store EBICS Data Files" + _order = "date desc" _sql_constraints = [ - ('name_uniq', 'unique (name, format_id)', - 'This File has already been down- or uploaded !') + ( + "name_uniq", + "unique (name, format_id)", + "This File has already been down- or uploaded !", + ) ] - name = fields.Char(string='Filename') - data = fields.Binary(string='File', readonly=True) + name = fields.Char(string="Filename") + data = fields.Binary(string="File", readonly=True) format_id = fields.Many2one( - comodel_name='ebics.file.format', - string='EBICS File Formats', - readonly=True) - type = fields.Selection( - related='format_id.type', - readonly=True) + comodel_name="ebics.file.format", string="EBICS File Formats", readonly=True + ) + type = fields.Selection(related="format_id.type", readonly=True) date_from = fields.Date( - readonly=True, - help="'Date From' as entered in the download wizard.") + readonly=True, help="'Date From' as entered in the download wizard." + ) date_to = fields.Date( - readonly=True, - help="'Date To' as entered in the download wizard.") + readonly=True, help="'Date To' as entered in the download wizard." + ) date = fields.Datetime( - required=True, readonly=True, - help='File Upload/Download date') + required=True, readonly=True, help="File Upload/Download date" + ) bank_statement_ids = fields.One2many( - comodel_name='account.bank.statement', - inverse_name='ebics_file_id', - string='Generated Bank Statements', readonly=True) + comodel_name="account.bank.statement", + inverse_name="ebics_file_id", + string="Generated Bank Statements", + readonly=True, + ) state = fields.Selection( - [('draft', 'Draft'), - ('done', 'Done')], - string='State', - default='draft', - required=True, readonly=True) + [("draft", "Draft"), ("done", "Done")], + string="State", + default="draft", + required=True, + readonly=True, + ) user_id = fields.Many2one( - comodel_name='res.users', string='User', + comodel_name="res.users", + string="User", default=lambda self: self.env.user, - readonly=True) + readonly=True, + ) ebics_userid_id = fields.Many2one( - comodel_name='ebics.userid', - string='EBICS UserID', - ondelete='restrict', - readonly=True) - note = fields.Text(string='Notes') - note_process = fields.Text(string='Notes') + comodel_name="ebics.userid", + string="EBICS UserID", + ondelete="restrict", + readonly=True, + ) + note = fields.Text(string="Notes") + note_process = fields.Text(string="Notes") company_ids = fields.Many2many( - comodel_name='res.company', - string='Companies', - help="Companies sharing this EBICS file.") + comodel_name="res.company", + string="Companies", + help="Companies sharing this EBICS file.", + ) def unlink(self): ff_methods = self._file_format_methods() for ebics_file in self: - if ebics_file.state == 'done': - raise UserError(_( - "You can only remove EBICS files in state 'Draft'.")) + if ebics_file.state == "done": + raise UserError(_("You can only remove EBICS files in state 'Draft'.")) # execute format specific actions ff = ebics_file.format_id.download_process_method if ff in ff_methods: - if ff_methods[ff].get('unlink'): - ff_methods[ff]['unlink'](ebics_file) + if ff_methods[ff].get("unlink"): + ff_methods[ff]["unlink"](ebics_file) # remove bank statements ebics_file.bank_statement_ids.unlink() return super(EbicsFile, self).unlink() def set_to_draft(self): - return self.write({'state': 'draft'}) + return self.write({"state": "draft"}) def set_to_done(self): - return self.write({'state': 'done'}) + return self.write({"state": "done"}) def process(self): self.ensure_one() - ctx = dict( - self.env.context, - allowed_company_ids=self.env.user.company_ids.ids) + ctx = dict(self.env.context, allowed_company_ids=self.env.user.company_ids.ids) self = self.with_context(ctx) - self.note_process = '' + self.note_process = "" ff_methods = self._file_format_methods() ff = self.format_id.download_process_method if ff in ff_methods: - if ff_methods[ff].get('process'): - res = ff_methods[ff]['process'](self) - self.state = 'done' + if ff_methods[ff].get("process"): + res = ff_methods[ff]["process"](self) + self.state = "done" return res else: return self._process_undefined_format() def action_open_bank_statements(self): self.ensure_one() - action = self.env['ir.actions.act_window']._for_xml_id( - 'account.action_bank_statement_tree') - domain = eval(action.get('domain') or '[]') - domain += [('id', 'in', self._context.get('statement_ids'))] - action.update({'domain': domain}) + action = self.env["ir.actions.act_window"]._for_xml_id( + "account.action_bank_statement_tree" + ) + domain = safe_eval(action.get("domain") or "[]") + domain += [("id", "in", self._context.get("statement_ids"))] + action.update({"domain": domain}) return action def button_close(self): self.ensure_one() - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} def _file_format_methods(self): """ @@ -120,35 +126,45 @@ class EbicsFile(models.Model): for extra file formats. """ res = { - 'cfonb120': - {'process': self._process_cfonb120, - 'unlink': self._unlink_cfonb120}, - 'camt.052': - {'process': self._process_camt052, - 'unlink': self._unlink_camt052}, - 'camt.053': - {'process': self._process_camt053, - 'unlink': self._unlink_camt053}, - 'camt.054': - {'process': self._process_camt054, - 'unlink': self._unlink_camt054}, - 'pain.002': - {'process': self._process_pain002, - 'unlink': self._unlink_pain002}, + "cfonb120": { + "process": self._process_cfonb120, + "unlink": self._unlink_cfonb120, + }, + "camt.052": { + "process": self._process_camt052, + "unlink": self._unlink_camt052, + }, + "camt.053": { + "process": self._process_camt053, + "unlink": self._unlink_camt053, + }, + "camt.054": { + "process": self._process_camt054, + "unlink": self._unlink_camt054, + }, + "pain.002": { + "process": self._process_pain002, + "unlink": self._unlink_pain002, + }, } return res def _check_import_module(self, module, raise_if_not_found=True): - mod = self.env['ir.module.module'].sudo().search( - [('name', '=like', module), - ('state', '=', 'installed')]) + mod = ( + self.env["ir.module.module"] + .sudo() + .search([("name", "=like", module), ("state", "=", "installed")]) + ) if not mod: if raise_if_not_found: - raise UserError(_( - "The module to process the '%s' format is not installed " - "on your system. " - "\nPlease install module '%s'") - % (self.format_id.name, module)) + raise UserError( + _( + "The module to process the '%s' format is not installed " + "on your system. " + "\nPlease install module '%s'" + ) + % (self.format_id.name, module) + ) return False return True @@ -156,19 +172,19 @@ class EbicsFile(models.Model): notifications = [] st_line_ids = [] statement_ids = [] - if res.get('context'): - notifications = res['context'].get('notifications', []) - st_line_ids = res['context'].get('statement_line_ids', []) + if res.get("context"): + notifications = res["context"].get("notifications", []) + st_line_ids = res["context"].get("statement_line_ids", []) if notifications: for notif in notifications: parts = [] - for k in ['type', 'message', 'details']: + for k in ["type", "message", "details"]: if notif.get(k): - msg = '%s: %s' % (k, notif[k]) + msg = "{}: {}".format(k, notif[k]) parts.append(msg) - self.note_process += '\n'.join(parts) - self.note_process += '\n' - self.note_process += '\n' + self.note_process += "\n".join(parts) + self.note_process += "\n" + self.note_process += "\n" if st_line_ids: self.flush() self.env.cr.execute( @@ -185,36 +201,37 @@ class EbicsFile(models.Model): WHERE absl.id IN %s ORDER BY date, company_id """, - (tuple(st_line_ids),) + (tuple(st_line_ids),), ) sts_data = self.env.cr.dictfetchall() else: sts_data = [] st_cnt = len(sts_data) if st_cnt: - self.note_process += _( - "%s bank statements have been imported: " - ) % st_cnt - self.note_process += '\n' + self.note_process += _("%s bank statements have been imported: ") % st_cnt + self.note_process += "\n" for st_data in sts_data: self.note_process += ("\n%s, %s (%s)") % ( - st_data['date'], st_data['name'], st_data['company_name']) - statement_ids = [x['statement_id'] for x in sts_data] + st_data["date"], + st_data["name"], + st_data["company_name"], + ) + statement_ids = [x["statement_id"] for x in sts_data] if statement_ids: self.sudo().bank_statement_ids = [(6, 0, statement_ids)] ctx = dict(self.env.context, statement_ids=statement_ids) - module = __name__.split('addons.')[1].split('.')[0] - result_view = self.env.ref('%s.ebics_file_view_form_result' % module) + module = __name__.split("addons.")[1].split(".")[0] + result_view = self.env.ref("%s.ebics_file_view_form_result" % module) return { - 'name': _('Import EBICS File'), - 'res_id': self.id, - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': self._name, - 'view_id': result_view.id, - 'target': 'new', - 'context': ctx, - 'type': 'ir.actions.act_window', + "name": _("Import EBICS File"), + "res_id": self.id, + "view_type": "form", + "view_mode": "form", + "res_model": self._name, + "view_id": result_view.id, + "target": "new", + "context": ctx, + "type": "ir.actions.act_window", } @staticmethod @@ -223,60 +240,63 @@ class EbicsFile(models.Model): We do not support the standard _journal_creation_wizard since a single cfonb120 file may contain statements from different legal entities. """ - import_module = 'account_statement_import_fr_cfonb' + import_module = "account_statement_import_fr_cfonb" self._check_import_module(import_module) - wiz_model = 'account.statement.import' + wiz_model = "account.statement.import" data_file = base64.b64decode(self.data) - lines = data_file.split(b'\n') + lines = data_file.split(b"\n") wiz_vals_list = [] - st_lines = b'' + st_lines = b"" transactions = False for line in lines: rec_type = line[0:2] acc_number = line[21:32] - st_lines += line + b'\n' - if rec_type == b'04': + st_lines += line + b"\n" + if rec_type == b"04": transactions = True - if rec_type == b'07': + if rec_type == b"07": if transactions: - fn = '_'.join([acc_number.decode(), self.name]) - wiz_vals_list.append({ - 'statement_filename': fn, - 'statement_file': base64.b64encode(st_lines) - }) - st_lines = b'' + fn = "_".join([acc_number.decode(), self.name]) + wiz_vals_list.append( + { + "statement_filename": fn, + "statement_file": base64.b64encode(st_lines), + } + ) + st_lines = b"" transactions = False result = { - 'type': 'ir.actions.client', - 'tag': 'bank_statement_reconciliation_view', - 'context': {'statement_line_ids': [], - 'company_ids': self.env.user.company_ids.ids, - 'notifications': []}, + "type": "ir.actions.client", + "tag": "bank_statement_reconciliation_view", + "context": { + "statement_line_ids": [], + "company_ids": self.env.user.company_ids.ids, + "notifications": [], + }, } - wiz_ctx = dict(self.env.context, active_model='ebics.file') + wiz_ctx = dict(self.env.context, active_model="ebics.file") for i, wiz_vals in enumerate(wiz_vals_list, start=1): wiz = self.env[wiz_model].with_context(wiz_ctx).create(wiz_vals) res = wiz.import_file_button() - ctx = res.get('context') - if (res.get('res_model') - == 'account.bank.statement.import.journal.creation'): - message = _( - "Error detected while importing statement number %s.\n" - ) % i + ctx = res.get("context") + if res.get("res_model") == "account.bank.statement.import.journal.creation": + message = _("Error detected while importing statement number %s.\n") % i message += _("No financial journal found.") - details = _( - 'Bank account number: %s' - ) % ctx.get('default_bank_acc_number') - result['context']['notifications'].extend([{ - 'type': 'warning', - 'message': message, - 'details': details, - }]) + details = _("Bank account number: %s") % ctx.get( + "default_bank_acc_number" + ) + result["context"]["notifications"].extend( + [ + { + "type": "warning", + "message": message, + "details": details, + } + ] + ) continue - result['context']['statement_line_ids'].extend( - ctx['statement_line_ids']) - result['context']['notifications'].extend( - ctx['notifications']) + result["context"]["statement_line_ids"].extend(ctx["statement_line_ids"]) + result["context"]["notifications"].extend(ctx["notifications"]) return self._process_result_action(result) @staticmethod @@ -285,11 +305,10 @@ class EbicsFile(models.Model): Placeholder for cfonb120 specific actions before removing the EBICS data file and its related bank statements. """ - pass @staticmethod def _process_camt052(self): - import_module = 'account_statement_import_camt' + import_module = "account_statement_import_camt" self._check_import_module(import_module) return self._process_camt053(self) @@ -299,11 +318,10 @@ class EbicsFile(models.Model): Placeholder for camt052 specific actions before removing the EBICS data file and its related bank statements. """ - pass @staticmethod def _process_camt054(self): - import_module = 'account_statement_import_camt' + import_module = "account_statement_import_camt" self._check_import_module(import_module) return self._process_camt053(self) @@ -313,84 +331,89 @@ class EbicsFile(models.Model): Placeholder for camt054 specific actions before removing the EBICS data file and its related bank statements. """ - pass @staticmethod def _process_camt053(self): modules = [ - ('oca', 'account_statement_import_camt'), - ('oe', 'account_bank_statement_import_camt'), + ("oca", "account_statement_import_camt"), + ("oe", "account_bank_statement_import_camt"), ] found = False - for src, mod in modules: + for _src, mod in modules: if self._check_import_module(mod, raise_if_not_found=False): found = True break if not found: - raise UserError(_( - "The module to process the '%s' format is not installed " - "on your system. " - "\nPlease install one of the following modules: \n%s." - ) % (self.format_id.name, ', '.join([x[1] for x in modules])) + raise UserError( + _( + "The module to process the '%s' format is not installed " + "on your system. " + "\nPlease install one of the following modules: \n%s." + ) + % (self.format_id.name, ", ".join([x[1] for x in modules])) ) - if src == 'oca': + if _src == "oca": self._process_camt053_oca() else: self._process_camt053_oe() def _process_camt053_oca(self): - wiz_model = 'account.statement.import' + wiz_model = "account.statement.import" wiz_vals = { - 'statement_filename': self.name, - 'statement_file': self.data, + "statement_filename": self.name, + "statement_file": self.data, } result = { - 'type': 'ir.actions.client', - 'tag': 'bank_statement_reconciliation_view', - 'context': {'statement_line_ids': [], - 'company_ids': self.env.user.company_ids.ids, - 'notifications': []}, + "type": "ir.actions.client", + "tag": "bank_statement_reconciliation_view", + "context": { + "statement_line_ids": [], + "company_ids": self.env.user.company_ids.ids, + "notifications": [], + }, } - wiz_ctx = dict(self.env.context, active_model='ebics.file') + wiz_ctx = dict(self.env.context, active_model="ebics.file") wiz = self.env[wiz_model].with_context(wiz_ctx).create(wiz_vals) res = wiz.import_file_button() - ctx = res.get('context') - if (res.get('res_model') - == 'account.bank.statement.import.journal.creation'): - message = _( - "Error detected while importing statement %s.\n" - ) % self.name + ctx = res.get("context") + if res.get("res_model") == "account.bank.statement.import.journal.creation": + message = _("Error detected while importing statement %s.\n") % self.name message += _("No financial journal found.") - details = _( - 'Bank account number: %s' - ) % ctx.get('default_bank_acc_number') - result['context']['notifications'].extend([{ - 'type': 'warning', - 'message': message, - 'details': details, - }]) - result['context']['statement_line_ids'].extend( - ctx['statement_line_ids']) - result['context']['notifications'].extend( - ctx['notifications']) + details = _("Bank account number: %s") % ctx.get("default_bank_acc_number") + result["context"]["notifications"].extend( + [ + { + "type": "warning", + "message": message, + "details": details, + } + ] + ) + result["context"]["statement_line_ids"].extend(ctx["statement_line_ids"]) + result["context"]["notifications"].extend(ctx["notifications"]) return self._process_result_action(result) def _process_camt053_oe(self): - wiz_model = 'account.bank.statement.import' + wiz_model = "account.bank.statement.import" wiz_vals = { - 'attachment_ids': [(0, 0, {'name': self.name, - 'datas': self.data, - 'store_fname': self.name})]} - ctx = dict(self.env.context, active_model='ebics.file') + "attachment_ids": [ + ( + 0, + 0, + {"name": self.name, "datas": self.data, "store_fname": self.name}, + ) + ] + } + ctx = dict(self.env.context, active_model="ebics.file") wiz = self.env[wiz_model].with_context(ctx).create(wiz_vals) res = wiz.import_file() - if res.get('res_model') \ - == 'account.bank.statement.import.journal.creation': - if res.get('context'): - bank_account = res['context'].get('default_bank_acc_number') - raise UserError(_( - "No financial journal found for Company Bank Account %s" - ) % bank_account) + if res.get("res_model") == "account.bank.statement.import.journal.creation": + if res.get("context"): + bank_account = res["context"].get("default_bank_acc_number") + raise UserError( + _("No financial journal found for Company Bank Account %s") + % bank_account + ) return self._process_result_action(res) @staticmethod @@ -399,7 +422,6 @@ class EbicsFile(models.Model): Placeholder for camt053 specific actions before removing the EBICS data file and its related bank statements. """ - pass @staticmethod def _process_pain002(self): @@ -408,7 +430,6 @@ class EbicsFile(models.Model): TODO: add import logic based upon OCA 'account_payment_return_import' """ - pass @staticmethod def _unlink_pain002(self): @@ -419,8 +440,11 @@ class EbicsFile(models.Model): raise NotImplementedError def _process_undefined_format(self): - raise UserError(_( - "The current version of the 'account_ebics' module " - "has no support to automatically process EBICS files " - "with format %s." - ) % self.format_id.name) + raise UserError( + _( + "The current version of the 'account_ebics' module " + "has no support to automatically process EBICS files " + "with format %s." + ) + % self.format_id.name + ) diff --git a/account_ebics/models/ebics_file_format.py b/account_ebics/models/ebics_file_format.py index 5864644..8dee0a9 100644 --- a/account_ebics/models/ebics_file_format.py +++ b/account_ebics/models/ebics_file_format.py @@ -5,58 +5,60 @@ from odoo import api, fields, models class EbicsFileFormat(models.Model): - _name = 'ebics.file.format' - _description = 'EBICS File Formats' - _order = 'type,name,order_type' + _name = "ebics.file.format" + _description = "EBICS File Formats" + _order = "type,name,order_type" name = fields.Char( - string='Request Type', + string="Request Type", required=True, help="E.g. camt.xxx.cfonb120.stm, pain.001.001.03.sct.\n" - "Specify camt.052, camt.053, camt.054 for camt " - "Order Types such as C53, Z53, C54, Z54.\n" - "This name has to match the 'Request Type' in your " - "EBICS contract for Order Type 'FDL' or 'FUL'.\n") + "Specify camt.052, camt.053, camt.054 for camt " + "Order Types such as C53, Z53, C54, Z54.\n" + "This name has to match the 'Request Type' in your " + "EBICS contract for Order Type 'FDL' or 'FUL'.\n", + ) type = fields.Selection( - selection=[('down', 'Download'), - ('up', 'Upload')], - required=True) + selection=[("down", "Download"), ("up", "Upload")], required=True + ) order_type = fields.Char( - string='Order Type', + string="Order Type", required=True, help="E.g. C53 (check your EBICS contract).\n" - "For most banks in France you should use the " - "format neutral Order Types 'FUL' for upload " - "and 'FDL' for download.") + "For most banks in France you should use the " + "format neutral Order Types 'FUL' for upload " + "and 'FDL' for download.", + ) download_process_method = fields.Selection( - selection='_selection_download_process_method', + selection="_selection_download_process_method", help="Enable processing within Odoo of the downloaded file " - "via the 'Process' button." - "E.g. specify camt.053 to import a camt.053 file and create " - "a bank statement.") + "via the 'Process' button." + "E.g. specify camt.053 to import a camt.053 file and create " + "a bank statement.", + ) # TODO: # move signature_class parameter so that it can be set per EBICS config signature_class = fields.Selection( - selection=[('E', 'Single signature'), - ('T', 'Transport signature')], - string='Signature Class', + selection=[("E", "Single signature"), ("T", "Transport signature")], + string="Signature Class", help="Please doublecheck the security of your Odoo " - "ERP system when using class 'E' to prevent unauthorised " - "users to make supplier payments." - "\nLeave this field empty to use the default " - "defined for your EBICS UserID.") + "ERP system when using class 'E' to prevent unauthorised " + "users to make supplier payments." + "\nLeave this field empty to use the default " + "defined for your EBICS UserID.", + ) description = fields.Char() suffix = fields.Char( required=True, - help="Specify the filename suffix for this File Format." - "\nE.g. c53.xml") + help="Specify the filename suffix for this File Format." "\nE.g. c53.xml", + ) @api.model def _selection_download_process_method(self): - methods = self.env['ebics.file']._file_format_methods().keys() + methods = self.env["ebics.file"]._file_format_methods().keys() return [(x, x) for x in methods] - @api.onchange('type') + @api.onchange("type") def _onchange_type(self): - if self.type == 'up': + if self.type == "up": self.download_process_method = False diff --git a/account_ebics/models/ebics_userid.py b/account_ebics/models/ebics_userid.py index d3e8818..f7fc99f 100644 --- a/account_ebics/models/ebics_userid.py +++ b/account_ebics/models/ebics_userid.py @@ -13,224 +13,240 @@ from odoo.exceptions import UserError _logger = logging.getLogger(__name__) - -""" -logging.basicConfig( - level=logging.DEBUG, - format='[%(asctime)s] %(levelname)s - %(name)s: %(message)s') -""" - +# logging.basicConfig( +# level=logging.DEBUG, +# format='[%(asctime)s] %(levelname)s - %(name)s: %(message)s') try: import fintech - from fintech.ebics import EbicsKeyRing, EbicsBank, EbicsUser,\ - EbicsClient, EbicsFunctionalError, EbicsTechnicalError - fintech.cryptolib = 'cryptography' + from fintech.ebics import ( + EbicsBank, + EbicsClient, + EbicsFunctionalError, + EbicsKeyRing, + EbicsTechnicalError, + EbicsUser, + ) + + fintech.cryptolib = "cryptography" except ImportError: - _logger.warning('Failed to import fintech') + _logger.warning("Failed to import fintech") class EbicsBank(EbicsBank): - def _next_order_id(self, partnerid): """ EBICS protocol version H003 requires generation of the OrderID. The OrderID must be a string between 'A000' and 'ZZZZ' and unique for each partner id. """ - return hasattr(self, '_order_number') and self._order_number or 'A000' + return hasattr(self, "_order_number") and self._order_number or "A000" class EbicsUserID(models.Model): - _name = 'ebics.userid' - _description = 'EBICS UserID' - _order = 'name' + _name = "ebics.userid" + _description = "EBICS UserID" + _order = "name" name = fields.Char( - string='EBICS UserID', required=True, - readonly=True, states={'draft': [('readonly', False)]}, + string="EBICS UserID", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, help="Human users or a technical system that is/are " - "assigned to a customer. " - "\nOn the EBICS bank server it is identified " - "by the combination of UserID and PartnerID. " - "The technical subscriber serves only for the data exchange " - "between customer and financial institution. " - "The human user also can authorise orders.") + "assigned to a customer. " + "\nOn the EBICS bank server it is identified " + "by the combination of UserID and PartnerID. " + "The technical subscriber serves only for the data exchange " + "between customer and financial institution. " + "The human user also can authorise orders.", + ) ebics_config_id = fields.Many2one( - comodel_name='ebics.config', - string='EBICS Configuration', - ondelete='cascade') + comodel_name="ebics.config", string="EBICS Configuration", ondelete="cascade" + ) user_ids = fields.Many2many( - comodel_name='res.users', - string='Users', + comodel_name="res.users", + string="Users", required=True, help="Users who are allowed to use this EBICS UserID for " - " bank transactions.") + " bank transactions.", + ) # Currently only a singe signature class per user is supported # Classes A and B are not yet supported. signature_class = fields.Selection( - selection=[('E', 'Single signature'), - ('T', 'Transport signature')], - string='Signature Class', - required=True, default='T', - readonly=True, states={'draft': [('readonly', False)]}, + selection=[("E", "Single signature"), ("T", "Transport signature")], + string="Signature Class", + required=True, + default="T", + readonly=True, + states={"draft": [("readonly", False)]}, help="Default signature class." - "This default can be overriden for specific " - "EBICS transactions (cf. File Formats).") - ebics_keys_fn = fields.Char( - compute='_compute_ebics_keys_fn') - ebics_keys_found = fields.Boolean( - compute='_compute_ebics_keys_found') - ebics_passphrase = fields.Char( - string='EBICS Passphrase') + "This default can be overriden for specific " + "EBICS transactions (cf. File Formats).", + ) + ebics_keys_fn = fields.Char(compute="_compute_ebics_keys_fn") + ebics_keys_found = fields.Boolean(compute="_compute_ebics_keys_found") + ebics_passphrase = fields.Char(string="EBICS Passphrase") ebics_ini_letter = fields.Binary( - string='EBICS INI Letter', readonly=True, - help="INI-letter PDF document to be sent to your bank.") - ebics_ini_letter_fn = fields.Char( - string='INI-letter Filename', readonly=True) + string="EBICS INI Letter", + readonly=True, + help="INI-letter PDF document to be sent to your bank.", + ) + ebics_ini_letter_fn = fields.Char(string="INI-letter Filename", readonly=True) ebics_public_bank_keys = fields.Binary( - string='EBICS Public Bank Keys', readonly=True, - help="EBICS Public Bank Keys to be checked for consistency.") + string="EBICS Public Bank Keys", + readonly=True, + help="EBICS Public Bank Keys to be checked for consistency.", + ) ebics_public_bank_keys_fn = fields.Char( - string='EBICS Public Bank Keys Filename', readonly=True) + string="EBICS Public Bank Keys Filename", readonly=True + ) swift_3skey = fields.Boolean( - string='Enable 3SKey support', + string="Enable 3SKey support", help="Transactions for this user will be signed " - "by means of the SWIFT 3SKey token.") - swift_3skey_certificate = fields.Binary( - string='3SKey Certficate') - swift_3skey_certificate_fn = fields.Char( - string='EBICS Public Bank Keys Filename') + "by means of the SWIFT 3SKey token.", + ) + swift_3skey_certificate = fields.Binary(string="3SKey Certficate") + swift_3skey_certificate_fn = fields.Char(string="EBICS Public Bank Keys Filename") # X.509 Distinguished Name attributes used to # create self-signed X.509 certificates ebics_key_x509 = fields.Boolean( - string='X509 support', - help="Set this flag in order to work with " - "self-signed X.509 certificates") + string="X509 support", + help="Set this flag in order to work with " "self-signed X.509 certificates", + ) ebics_key_x509_dn_cn = fields.Char( - string='Common Name [CN]', - readonly=True, states={'draft': [('readonly', False)]}, + string="Common Name [CN]", + readonly=True, + states={"draft": [("readonly", False)]}, ) ebics_key_x509_dn_o = fields.Char( - string='Organization Name [O]', - readonly=True, states={'draft': [('readonly', False)]}, + string="Organization Name [O]", + readonly=True, + states={"draft": [("readonly", False)]}, ) ebics_key_x509_dn_ou = fields.Char( - string='Organizational Unit Name [OU]', - readonly=True, states={'draft': [('readonly', False)]}, + string="Organizational Unit Name [OU]", + readonly=True, + states={"draft": [("readonly", False)]}, ) ebics_key_x509_dn_c = fields.Char( - string='Country Name [C]', - readonly=True, states={'draft': [('readonly', False)]}, + string="Country Name [C]", + readonly=True, + states={"draft": [("readonly", False)]}, ) ebics_key_x509_dn_st = fields.Char( - string='State Or Province Name [ST]', - readonly=True, states={'draft': [('readonly', False)]}, + string="State Or Province Name [ST]", + readonly=True, + states={"draft": [("readonly", False)]}, ) ebics_key_x509_dn_l = fields.Char( - string='Locality Name [L]', - readonly=True, states={'draft': [('readonly', False)]}, + string="Locality Name [L]", + readonly=True, + states={"draft": [("readonly", False)]}, ) ebics_key_x509_dn_e = fields.Char( - string='Email Address', - readonly=True, states={'draft': [('readonly', False)]}, + string="Email Address", + readonly=True, + states={"draft": [("readonly", False)]}, ) state = fields.Selection( - [('draft', 'Draft'), - ('init', 'Initialisation'), - ('get_bank_keys', 'Get Keys from Bank'), - ('to_verify', 'Verification'), - ('active_keys', 'Active Keys')], - string='State', - default='draft', - required=True, readonly=True) - active = fields.Boolean( - string='Active', default=True) - company_ids = fields.Many2many( - comodel_name='res.company', - string='Companies', + [ + ("draft", "Draft"), + ("init", "Initialisation"), + ("get_bank_keys", "Get Keys from Bank"), + ("to_verify", "Verification"), + ("active_keys", "Active Keys"), + ], + string="State", + default="draft", required=True, - help="Companies sharing this EBICS contract.") + readonly=True, + ) + active = fields.Boolean(string="Active", default=True) + company_ids = fields.Many2many( + comodel_name="res.company", + string="Companies", + required=True, + help="Companies sharing this EBICS contract.", + ) - @api.depends('name') + @api.depends("name") def _compute_ebics_keys_fn(self): for rec in self: keys_dir = rec.ebics_config_id.ebics_keys rec.ebics_keys_fn = ( - rec.name - and keys_dir - and (keys_dir + '/' + rec.name + '_keys')) - - @api.depends('ebics_keys_fn') - def _compute_ebics_keys_found(self): - for rec in self: - rec.ebics_keys_found = ( - rec.ebics_keys_fn - and os.path.isfile(rec.ebics_keys_fn) + rec.name and keys_dir and (keys_dir + "/" + rec.name + "_keys") ) - @api.constrains('ebics_passphrase') + @api.depends("ebics_keys_fn") + def _compute_ebics_keys_found(self): + for rec in self: + rec.ebics_keys_found = rec.ebics_keys_fn and os.path.isfile( + rec.ebics_keys_fn + ) + + @api.constrains("ebics_passphrase") def _check_ebics_passphrase(self): for rec in self: if not rec.ebics_passphrase or len(rec.ebics_passphrase) < 8: - raise UserError(_( - "The passphrase must be at least 8 characters long")) + raise UserError(_("The passphrase must be at least 8 characters long")) - @api.onchange('signature_class') + @api.onchange("signature_class") def _onchange_signature_class(self): - if self.signature_class == 'T': + if self.signature_class == "T": self.swift_3skey = False - @api.onchange('swift_3skey') + @api.onchange("swift_3skey") def _onchange_swift_3skey(self): if self.swift_3skey: self.ebics_key_x509 = True def set_to_draft(self): - return self.write({'state': 'draft'}) + return self.write({"state": "draft"}) def set_to_active_keys(self): - return self.write({'state': 'active_keys'}) + return self.write({"state": "active_keys"}) def set_to_get_bank_keys(self): - return self.write({'state': 'get_bank_keys'}) + return self.write({"state": "get_bank_keys"}) - def ebics_init_1(self): + def ebics_init_1(self): # noqa: C901 """ Initialization of bank keys - Step 1: Create new keys and certificates for this user """ self.ensure_one() self.ebics_config_id._check_ebics_files() - if self.state != 'draft': + if self.state != "draft": raise UserError( - _("Set state to 'draft' before Bank Key (re)initialisation.")) + _("Set state to 'draft' before Bank Key (re)initialisation.") + ) if not self.ebics_passphrase: - raise UserError( - _("Set a passphrase.")) + raise UserError(_("Set a passphrase.")) if self.swift_3skey and not self.swift_3skey_certificate: - raise UserError( - _("3SKey certificate missing.")) + raise UserError(_("3SKey certificate missing.")) ebics_version = self.ebics_config_id.ebics_version try: keyring = EbicsKeyRing( - keys=self.ebics_keys_fn, - passphrase=self.ebics_passphrase) + keys=self.ebics_keys_fn, passphrase=self.ebics_passphrase + ) bank = EbicsBank( keyring=keyring, hostid=self.ebics_config_id.ebics_host, - url=self.ebics_config_id.ebics_url) + url=self.ebics_config_id.ebics_url, + ) user = EbicsUser( keyring=keyring, partnerid=self.ebics_config_id.ebics_partner, - userid=self.name) + userid=self.name, + ) except Exception: exctype, value = exc_info()[:2] error = _("EBICS Initialisation Error:") - error += '\n' + str(exctype) + '\n' + str(value) + error += "\n" + str(exctype) + "\n" + str(value) raise UserError(error) self.ebics_config_id._check_ebics_keys() @@ -240,60 +256,63 @@ class EbicsUserID(models.Model): # enable import of all type of certicates: A00x, X002, E002 if self.swift_3skey: kwargs = { - self.ebics_config_id.ebics_key_version: - base64.decodestring(self.swift_3skey_certificate), + self.ebics_config_id.ebics_key_version: base64.decodestring( + self.swift_3skey_certificate + ), } user.import_certificates(**kwargs) user.create_keys( keyversion=self.ebics_config_id.ebics_key_version, - bitlength=self.ebics_config_id.ebics_key_bitlength) + bitlength=self.ebics_config_id.ebics_key_bitlength, + ) except Exception: exctype, value = exc_info()[:2] error = _("EBICS Initialisation Error:") - error += '\n' + str(exctype) + '\n' + str(value) + error += "\n" + str(exctype) + "\n" + str(value) raise UserError(error) if self.swift_3skey and not self.ebics_key_x509: - raise UserError(_( - "The current version of this module " - "requires to X509 support when enabling 3SKey")) + raise UserError( + _( + "The current version of this module " + "requires to X509 support when enabling 3SKey" + ) + ) if self.ebics_key_x509: dn_attrs = { - 'commonName': self.ebics_key_x509_dn_cn, - 'organizationName': self.ebics_key_x509_dn_o, - 'organizationalUnitName': self.ebics_key_x509_dn_ou, - 'countryName': self.ebics_key_x509_dn_c, - 'stateOrProvinceName': self.ebics_key_x509_dn_st, - 'localityName': self.ebics_key_x509_dn_l, - 'emailAddress': self.ebics_key_x509_dn_e, + "commonName": self.ebics_key_x509_dn_cn, + "organizationName": self.ebics_key_x509_dn_o, + "organizationalUnitName": self.ebics_key_x509_dn_ou, + "countryName": self.ebics_key_x509_dn_c, + "stateOrProvinceName": self.ebics_key_x509_dn_st, + "localityName": self.ebics_key_x509_dn_l, + "emailAddress": self.ebics_key_x509_dn_e, } kwargs = {k: v for k, v in dn_attrs.items() if v} user.create_certificates(**kwargs) - client = EbicsClient( - bank, user, version=ebics_version) + client = EbicsClient(bank, user, version=ebics_version) # Send the public electronic signature key to the bank. ebics_config_bank = self.ebics_config_id.journal_ids[0].bank_id if not ebics_config_bank: - raise UserError(_( - "No bank defined for the financial journal " - "of the EBICS Config")) + raise UserError( + _("No bank defined for the financial journal " "of the EBICS Config") + ) try: supported_versions = client.HEV() if ebics_version not in supported_versions: err_msg = _("EBICS version mismatch.") + "\n" err_msg += _("Versions supported by your bank:") for k in supported_versions: - err_msg += "\n%s: %s " % (k, supported_versions[k]) + err_msg += "\n{}: {} ".format(k, supported_versions[k]) raise UserError(err_msg) - if ebics_version == 'H003': + if ebics_version == "H003": bank._order_number = self.ebics_config_id._get_order_number() OrderID = client.INI() - _logger.info( - '%s, EBICS INI command, OrderID=%s', self._name, OrderID) - if ebics_version == 'H003': + _logger.info("%s, EBICS INI command, OrderID=%s", self._name, OrderID) + if ebics_version == "H003": self.ebics_config_id._update_order_number(OrderID) except URLError: exctype, value = exc_info()[:2] @@ -303,69 +322,68 @@ class EbicsUserID(models.Model): self.name, tb, ) - raise UserError(_( - "urlopen error:\n url '%s' - %s") - % (self.ebics_config_id.ebics_url, str(value))) + raise UserError( + _("urlopen error:\n url '%s' - %s") + % (self.ebics_config_id.ebics_url, str(value)) + ) except EbicsFunctionalError: e = exc_info() error = _("EBICS Functional Error:") - error += '\n' - error += '%s (code: %s)' % (e[1].message, e[1].code) + error += "\n" + error += "{} (code: {})".format(e[1].message, e[1].code) raise UserError(error) except EbicsTechnicalError: e = exc_info() error = _("EBICS Technical Error:") - error += '\n' - error += '%s (code: %s)' % (e[1].message, e[1].code) + error += "\n" + error += "{} (code: {})".format(e[1].message, e[1].code) raise UserError(error) # Send the public authentication and encryption keys to the bank. - if ebics_version == 'H003': + if ebics_version == "H003": bank._order_number = self.ebics_config_id._get_order_number() OrderID = client.HIA() - _logger.info('%s, EBICS HIA command, OrderID=%s', self._name, OrderID) - if ebics_version == 'H003': + _logger.info("%s, EBICS HIA command, OrderID=%s", self._name, OrderID) + if ebics_version == "H003": self.ebics_config_id._update_order_number(OrderID) # Create an INI-letter which must be printed and sent to the bank. ebics_config_bank = self.ebics_config_id.journal_ids[0].bank_id cc = ebics_config_bank.country.code - if cc in ['FR', 'DE']: + if cc in ["FR", "DE"]: lang = cc else: - lang = self.env.user.lang or \ - self.env['res.lang'].search([])[0].code + lang = self.env.user.lang or self.env["res.lang"].search([])[0].code lang = lang[:2] - tmp_dir = os.path.normpath(self.ebics_config_id.ebics_files + '/tmp') + tmp_dir = os.path.normpath(self.ebics_config_id.ebics_files + "/tmp") if not os.path.isdir(tmp_dir): os.makedirs(tmp_dir, mode=0o700) fn_date = fields.Date.today().isoformat() - fn = '_'.join( - [self.ebics_config_id.ebics_host, 'ini_letter', fn_date]) + '.pdf' - full_tmp_fn = os.path.normpath(tmp_dir + '/' + fn) + fn = "_".join([self.ebics_config_id.ebics_host, "ini_letter", fn_date]) + ".pdf" + full_tmp_fn = os.path.normpath(tmp_dir + "/" + fn) user.create_ini_letter( - bankname=ebics_config_bank.name, - path=full_tmp_fn, - lang=lang) - with open(full_tmp_fn, 'rb') as f: + bankname=ebics_config_bank.name, path=full_tmp_fn, lang=lang + ) + with open(full_tmp_fn, "rb") as f: letter = f.read() - self.write({ - 'ebics_ini_letter': base64.encodebytes(letter), - 'ebics_ini_letter_fn': fn, - }) + self.write( + { + "ebics_ini_letter": base64.encodebytes(letter), + "ebics_ini_letter_fn": fn, + } + ) - return self.write({'state': 'init'}) + return self.write({"state": "init"}) def ebics_init_2(self): """ Initialization of bank keys - Step 2: Activation of the account by the bank. """ - if self.state != 'init': - raise UserError( - _("Set state to 'Initialisation'.")) + if self.state != "init": + raise UserError(_("Set state to 'Initialisation'.")) self.ensure_one() - return self.write({'state': 'get_bank_keys'}) + return self.write({"state": "get_bank_keys"}) def ebics_init_3(self): """ @@ -376,26 +394,27 @@ class EbicsUserID(models.Model): """ self.ensure_one() self.ebics_config_id._check_ebics_files() - if self.state != 'get_bank_keys': - raise UserError( - _("Set state to 'Get Keys from Bank'.")) + if self.state != "get_bank_keys": + raise UserError(_("Set state to 'Get Keys from Bank'.")) try: keyring = EbicsKeyRing( - keys=self.ebics_keys_fn, passphrase=self.ebics_passphrase) + keys=self.ebics_keys_fn, passphrase=self.ebics_passphrase + ) bank = EbicsBank( keyring=keyring, hostid=self.ebics_config_id.ebics_host, - url=self.ebics_config_id.ebics_url) + url=self.ebics_config_id.ebics_url, + ) user = EbicsUser( keyring=keyring, partnerid=self.ebics_config_id.ebics_partner, - userid=self.name) - client = EbicsClient( - bank, user, version=self.ebics_config_id.ebics_version) + userid=self.name, + ) + client = EbicsClient(bank, user, version=self.ebics_config_id.ebics_version) except Exception: exctype, value = exc_info()[:2] error = _("EBICS Initialisation Error:") - error += '\n' + str(exctype) + '\n' + str(value) + error += "\n" + str(exctype) + "\n" + str(value) raise UserError(error) try: @@ -403,28 +422,31 @@ class EbicsUserID(models.Model): except EbicsFunctionalError: e = exc_info() error = _("EBICS Functional Error:") - error += '\n' - error += '%s (code: %s)' % (e[1].message, e[1].code) + error += "\n" + error += "{} (code: {})".format(e[1].message, e[1].code) raise UserError(error) except Exception: exctype, value = exc_info()[:2] error = _("EBICS Initialisation Error:") - error += '\n' + str(exctype) + '\n' + str(value) + error += "\n" + str(exctype) + "\n" + str(value) raise UserError(error) public_bank_keys = public_bank_keys.encode() - tmp_dir = os.path.normpath(self.ebics_config_id.ebics_files + '/tmp') + tmp_dir = os.path.normpath(self.ebics_config_id.ebics_files + "/tmp") if not os.path.isdir(tmp_dir): os.makedirs(tmp_dir, mode=0o700) fn_date = fields.Date.today().isoformat() - fn = '_'.join( - [self.ebics_config_id.ebics_host, 'public_bank_keys', fn_date] - ) + '.txt' - self.write({ - 'ebics_public_bank_keys': base64.encodestring(public_bank_keys), - 'ebics_public_bank_keys_fn': fn, - 'state': 'to_verify', - }) + fn = ( + "_".join([self.ebics_config_id.ebics_host, "public_bank_keys", fn_date]) + + ".txt" + ) + self.write( + { + "ebics_public_bank_keys": base64.encodestring(public_bank_keys), + "ebics_public_bank_keys_fn": fn, + "state": "to_verify", + } + ) return True @@ -435,32 +457,32 @@ class EbicsUserID(models.Model): and activate the bank keyu. """ self.ensure_one() - if self.state != 'to_verify': - raise UserError( - _("Set state to 'Verification'.")) + if self.state != "to_verify": + raise UserError(_("Set state to 'Verification'.")) keyring = EbicsKeyRing( - keys=self.ebics_keys_fn, passphrase=self.ebics_passphrase) + keys=self.ebics_keys_fn, passphrase=self.ebics_passphrase + ) bank = EbicsBank( keyring=keyring, hostid=self.ebics_config_id.ebics_host, - url=self.ebics_config_id.ebics_url) + url=self.ebics_config_id.ebics_url, + ) bank.activate_keys() - return self.write({'state': 'active_keys'}) + return self.write({"state": "active_keys"}) def change_passphrase(self): self.ensure_one() ctx = dict(self._context, default_ebics_userid_id=self.id) - module = __name__.split('addons.')[1].split('.')[0] - view = self.env.ref( - '%s.ebics_change_passphrase_view_form' % module) + module = __name__.split("addons.")[1].split(".")[0] + view = self.env.ref("%s.ebics_change_passphrase_view_form" % module) return { - 'name': _('EBICS keys change passphrase'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'ebics.change.passphrase', - 'view_id': view.id, - 'target': 'new', - 'context': ctx, - 'type': 'ir.actions.act_window', + "name": _("EBICS keys change passphrase"), + "view_type": "form", + "view_mode": "form", + "res_model": "ebics.change.passphrase", + "view_id": view.id, + "target": "new", + "context": ctx, + "type": "ir.actions.act_window", } diff --git a/account_ebics/models/fintech_ebics_register.py b/account_ebics/models/fintech_ebics_register.py index f908637..613e5be 100644 --- a/account_ebics/models/fintech_ebics_register.py +++ b/account_ebics/models/fintech_ebics_register.py @@ -13,24 +13,25 @@ try: import fintech except ImportError: fintech = None - _logger.warning('Failed to import fintech') + _logger.warning("Failed to import fintech") -fintech_register_name = config.get('fintech_register_name') -fintech_register_keycode = config.get('fintech_register_keycode') -fintech_register_users = config.get('fintech_register_users') +fintech_register_name = config.get("fintech_register_name") +fintech_register_keycode = config.get("fintech_register_keycode") +fintech_register_users = config.get("fintech_register_users") try: if fintech: fintech_register_users = ( fintech_register_users - and [x.strip() for x in fintech_register_users.split(',')] + and [x.strip() for x in fintech_register_users.split(",")] or None ) - fintech.cryptolib = 'cryptography' + fintech.cryptolib = "cryptography" fintech.register( name=fintech_register_name, keycode=fintech_register_keycode, - users=fintech_register_users) + users=fintech_register_users, + ) except RuntimeError as e: if str(e) == "'register' can be called only once": pass @@ -39,7 +40,7 @@ except RuntimeError as e: fintech.register() except Exception: msg = "fintech.register error" - tb = ''.join(format_exception(*exc_info())) - msg += '\n%s' % tb + tb = "".join(format_exception(*exc_info())) + msg += "\n%s" % tb _logger.error(msg) fintech.register() diff --git a/account_ebics/security/ebics_security.xml b/account_ebics/security/ebics_security.xml index fc939a2..7b11eac 100644 --- a/account_ebics/security/ebics_security.xml +++ b/account_ebics/security/ebics_security.xml @@ -1,32 +1,38 @@ - + EBICS Manager - + EBICS Configuration model company rule - - - ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] + + + ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] EBICS UserID model company rule - - - ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] + + + ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] EBICS File model company rule - - - ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] + + + ['|', ('company_ids', '=', False), ('company_ids', 'in', user.company_ids.ids)] diff --git a/account_ebics/views/ebics_config_views.xml b/account_ebics/views/ebics_config_views.xml index f76c619..d407af5 100644 --- a/account_ebics/views/ebics_config_views.xml +++ b/account_ebics/views/ebics_config_views.xml @@ -6,10 +6,10 @@ ebics.config - - - - + + + + @@ -20,44 +20,63 @@
-
- + - - - - - - + + + + + + - - - - - + + + + + - + - + - + diff --git a/account_ebics/views/ebics_file_format_views.xml b/account_ebics/views/ebics_file_format_views.xml index 79ed9e2..03dd598 100644 --- a/account_ebics/views/ebics_file_format_views.xml +++ b/account_ebics/views/ebics_file_format_views.xml @@ -6,11 +6,11 @@ ebics.file.format - - - - - + + + + + @@ -22,20 +22,22 @@
- - - - + + + + - - + + - +
diff --git a/account_ebics/views/ebics_file_views.xml b/account_ebics/views/ebics_file_views.xml index a7376ae..5961602 100644 --- a/account_ebics/views/ebics_file_views.xml +++ b/account_ebics/views/ebics_file_views.xml @@ -7,18 +7,26 @@ - - - - - - + + + + + + - + - - - + + + @@ -31,14 +39,18 @@ ebics.file - - - - - - - - + + + + + + + + @@ -50,34 +62,55 @@
-
- - - - - - - - - + + + + + + + + + - + - - + +
@@ -91,12 +124,16 @@
- +
-
@@ -107,23 +144,23 @@ ir.actions.act_window ebics.file tree,form - + [('type','=','down')] - + - + tree - - + + - + form - - + + @@ -133,12 +170,16 @@ ebics.file - - - - - - + + + + + + @@ -150,22 +191,38 @@
-
- - - - - - - + + + + + + + - +
@@ -177,23 +234,23 @@ ir.actions.act_window ebics.file tree,form - + [('type','=','up')] - + - + tree - - + + - + form - - + +
diff --git a/account_ebics/views/ebics_userid_views.xml b/account_ebics/views/ebics_userid_views.xml index aa32002..8626d68 100644 --- a/account_ebics/views/ebics_userid_views.xml +++ b/account_ebics/views/ebics_userid_views.xml @@ -6,10 +6,10 @@ ebics.userid - - - - + + + + @@ -20,67 +20,136 @@
-
- - + + - - - - - - + + + + + + - - - + + + - + - Distinguished Name attributes used to create self-signed X.509 certificates: + Distinguished Name attributes used to create self-signed X.509 certificates: - - - - + + + + - - - + + + - - - + + + - - - + + + diff --git a/account_ebics/views/menu.xml b/account_ebics/views/menu.xml index a2a2c6a..b3e3b23 100644 --- a/account_ebics/views/menu.xml +++ b/account_ebics/views/menu.xml @@ -1,58 +1,76 @@ - + - + - + - + - + - + - + - + - + diff --git a/account_ebics/wizards/ebics_change_passphrase.py b/account_ebics/wizards/ebics_change_passphrase.py index 5321453..1ac046c 100644 --- a/account_ebics/wizards/ebics_change_passphrase.py +++ b/account_ebics/wizards/ebics_change_passphrase.py @@ -11,65 +11,58 @@ _logger = logging.getLogger(__name__) try: import fintech from fintech.ebics import EbicsKeyRing - fintech.cryptolib = 'cryptography' + + fintech.cryptolib = "cryptography" except ImportError: - _logger.warning('Failed to import fintech') + _logger.warning("Failed to import fintech") class EbicsChangePassphrase(models.TransientModel): - _name = 'ebics.change.passphrase' - _description = 'Change EBICS keys passphrase' + _name = "ebics.change.passphrase" + _description = "Change EBICS keys passphrase" ebics_userid_id = fields.Many2one( - comodel_name='ebics.userid', - string='EBICS UserID', - readonly=True) - old_pass = fields.Char( - string='Old Passphrase', - required=True) - new_pass = fields.Char( - string='New Passphrase', - required=True) - new_pass_check = fields.Char( - string='New Passphrase (verification)', - required=True) - note = fields.Text(string='Notes', readonly=True) + comodel_name="ebics.userid", string="EBICS UserID", readonly=True + ) + old_pass = fields.Char(string="Old Passphrase", required=True) + new_pass = fields.Char(string="New Passphrase", required=True) + new_pass_check = fields.Char(string="New Passphrase (verification)", required=True) + note = fields.Text(string="Notes", readonly=True) def change_passphrase(self): self.ensure_one() if self.old_pass != self.ebics_userid_id.ebics_passphrase: - raise UserError(_( - "Incorrect old passphrase.")) + raise UserError(_("Incorrect old passphrase.")) if self.new_pass != self.new_pass_check: - raise UserError(_( - "New passphrase verification error.")) + raise UserError(_("New passphrase verification error.")) if self.new_pass == self.ebics_userid_id.ebics_passphrase: - raise UserError(_( - "New passphrase equal to old passphrase.")) + raise UserError(_("New passphrase equal to old passphrase.")) try: keyring = EbicsKeyRing( keys=self.ebics_userid_id.ebics_keys_fn, - passphrase=self.ebics_userid_id.ebics_passphrase) + passphrase=self.ebics_userid_id.ebics_passphrase, + ) keyring.change_passphrase(self.new_pass) except ValueError as e: raise UserError(str(e)) self.ebics_userid.ebics_passphrase = self.new_pass self.note = "The EBICS Passphrase has been changed." - module = __name__.split('addons.')[1].split('.')[0] + module = __name__.split("addons.")[1].split(".")[0] result_view = self.env.ref( - '%s.ebics_change_passphrase_view_form_result' % module) + "%s.ebics_change_passphrase_view_form_result" % module + ) return { - 'name': _('EBICS Keys Change Passphrase'), - 'res_id': self.id, - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'ebics.change.passphrase', - 'view_id': result_view.id, - 'target': 'new', - 'type': 'ir.actions.act_window', + "name": _("EBICS Keys Change Passphrase"), + "res_id": self.id, + "view_type": "form", + "view_mode": "form", + "res_model": "ebics.change.passphrase", + "view_id": result_view.id, + "target": "new", + "type": "ir.actions.act_window", } def button_close(self): self.ensure_one() - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} diff --git a/account_ebics/wizards/ebics_change_passphrase.xml b/account_ebics/wizards/ebics_change_passphrase.xml index f0a33f5..bb24f0a 100644 --- a/account_ebics/wizards/ebics_change_passphrase.xml +++ b/account_ebics/wizards/ebics_change_passphrase.xml @@ -1,4 +1,4 @@ - + @@ -8,14 +8,19 @@
- - - + + +
-
@@ -28,9 +33,9 @@
- +
-
diff --git a/account_ebics/wizards/ebics_xfer.xml b/account_ebics/wizards/ebics_xfer.xml index 091c08b..3dfd4d9 100644 --- a/account_ebics/wizards/ebics_xfer.xml +++ b/account_ebics/wizards/ebics_xfer.xml @@ -1,4 +1,4 @@ - + @@ -8,23 +8,36 @@
- - - - - - - - + + + + + + + +
-
@@ -37,26 +50,43 @@
- - - - - - - - - - - + + + + + + + + + + +
-
@@ -69,11 +99,16 @@
- +
-
@@ -86,7 +121,7 @@ form new {'ebics_download': 1} - +
@@ -96,7 +131,7 @@ form new {'ebics_upload': 1} - +
diff --git a/account_ebics_oca_statement_import/__manifest__.py b/account_ebics_oca_statement_import/__manifest__.py index d9f3fef..a141b7a 100644 --- a/account_ebics_oca_statement_import/__manifest__.py +++ b/account_ebics_oca_statement_import/__manifest__.py @@ -2,16 +2,16 @@ # License LGPL-3 or later (http://www.gnu.org/licenses/lpgl). { - 'name': 'account_ebics with OCA Bank Statement Imoort', - 'summary': "Use OCA Bank Statement Import with account_ebics", - 'version': '14.0.1.0.0', - 'author': 'Noviat', - 'category': 'Hidden', - 'license': 'LGPL-3', - 'depends': [ - 'account_ebics', - 'account_statement_import', + "name": "account_ebics with OCA Bank Statement Imoort", + "summary": "Use OCA Bank Statement Import with account_ebics", + "version": "14.0.1.0.0", + "author": "Noviat", + "category": "Hidden", + "license": "LGPL-3", + "depends": [ + "account_ebics", + "account_statement_import", ], - 'installable': True, - 'auto_install': True, + "installable": True, + "auto_install": True, } diff --git a/account_ebics_oca_statement_import/wizards/account_statement_import.py b/account_ebics_oca_statement_import/wizards/account_statement_import.py index 430d1fc..cc49e00 100644 --- a/account_ebics_oca_statement_import/wizards/account_statement_import.py +++ b/account_ebics_oca_statement_import/wizards/account_statement_import.py @@ -3,32 +3,34 @@ import logging -from odoo import models, _ +from odoo import _, models _logger = logging.getLogger(__name__) class AccountStatementImport(models.TransientModel): - _inherit = 'account.statement.import' + _inherit = "account.statement.import" def _check_parsed_data(self, stmts_vals): """ Basic and structural verifications """ - if self.env.context.get('active_model') == 'ebics.file': + if self.env.context.get("active_model") == "ebics.file": message = False if len(stmts_vals) == 0: message = _("This file doesn't contain any statement.") if not message: no_st_line = True for vals in stmts_vals: - if vals['transactions'] and len(vals['transactions']) > 0: + if vals["transactions"] and len(vals["transactions"]) > 0: no_st_line = False break if no_st_line: - message = _('This file doesn\'t contain any transaction.') + message = _("This file doesn't contain any transaction.") if message: - log_msg = _( - "Error detected while processing and EBICS File" - ) + ':\n' + message + log_msg = ( + _("Error detected while processing and EBICS File") + + ":\n" + + message + ) _logger.warn(log_msg) return return super()._check_parsed_data(stmts_vals) @@ -43,20 +45,16 @@ class AccountStatementImport(models.TransientModel): We could also create empty bank statement (in state done) to clearly show days without transactions via the bank statement list view. """ - if self.env.context.get('active_model') == 'ebics.file': + if self.env.context.get("active_model") == "ebics.file": transactions = False for st_vals in stmts_vals: - if st_vals.get('transactions'): + if st_vals.get("transactions"): transactions = True break if not transactions: - message = _('This file doesn\'t contain any transaction.') + message = _("This file doesn't contain any transaction.") st_line_ids = [] - notifications = { - 'type': 'warning', - 'message': message, - 'details': '' - } + notifications = {"type": "warning", "message": message, "details": ""} return st_line_ids, [notifications] return super()._create_bank_statements(stmts_vals, result) diff --git a/account_ebics_oe/__manifest__.py b/account_ebics_oe/__manifest__.py index cdaa6fe..43a8872 100644 --- a/account_ebics_oe/__manifest__.py +++ b/account_ebics_oe/__manifest__.py @@ -2,19 +2,17 @@ # License LGPL-3 or later (http://www.gnu.org/licenses/lpgl). { - 'name': 'account_ebics on Odoo Enterprise', - 'summary': "Deploy account_ebics module on Odoo Enterprise", - 'version': '14.0.1.0.0', - 'author': 'Noviat', - 'category': 'Hidden', - 'license': 'LGPL-3', - 'depends': [ - 'account_ebics', - 'account_accountant', + "name": "account_ebics on Odoo Enterprise", + "summary": "Deploy account_ebics module on Odoo Enterprise", + "version": "14.0.1.0.0", + "author": "Noviat", + "category": "Hidden", + "license": "LGPL-3", + "depends": [ + "account_ebics", + "account_accountant", ], - 'data': [ - 'views/account_ebics_menu.xml' - ], - 'installable': True, - 'auto_install': True, + "data": ["views/account_ebics_menu.xml"], + "installable": True, + "auto_install": True, } diff --git a/account_ebics_oe/views/account_ebics_menu.xml b/account_ebics_oe/views/account_ebics_menu.xml index 0e68143..4f9f379 100644 --- a/account_ebics_oe/views/account_ebics_menu.xml +++ b/account_ebics_oe/views/account_ebics_menu.xml @@ -1,8 +1,8 @@ - + - + diff --git a/account_ebics_oe_statement_import/__manifest__.py b/account_ebics_oe_statement_import/__manifest__.py index 794f45a..c0b0337 100644 --- a/account_ebics_oe_statement_import/__manifest__.py +++ b/account_ebics_oe_statement_import/__manifest__.py @@ -2,16 +2,16 @@ # License LGPL-3 or later (http://www.gnu.org/licenses/lpgl). { - 'name': 'account_ebics with Odoo Enterprise Bank Statement Import', - 'summary': "Use Odoo Enterprise Bank Statement Import with account_ebics", - 'version': '14.0.1.0.0', - 'author': 'Noviat', - 'category': 'Hidden', - 'license': 'LGPL-3', - 'depends': [ - 'account_ebics_oe', - 'account_bank_statement_import', + "name": "account_ebics with Odoo Enterprise Bank Statement Import", + "summary": "Use Odoo Enterprise Bank Statement Import with account_ebics", + "version": "14.0.1.0.0", + "author": "Noviat", + "category": "Hidden", + "license": "LGPL-3", + "depends": [ + "account_ebics_oe", + "account_bank_statement_import", ], - 'installable': True, - 'auto_install': True, + "installable": True, + "auto_install": True, } diff --git a/account_ebics_oe_statement_import/wizards/account_bank_statement_import.py b/account_ebics_oe_statement_import/wizards/account_bank_statement_import.py index 937f807..3748d31 100644 --- a/account_ebics_oe_statement_import/wizards/account_bank_statement_import.py +++ b/account_ebics_oe_statement_import/wizards/account_bank_statement_import.py @@ -3,32 +3,34 @@ import logging -from odoo import models, _ +from odoo import _, models _logger = logging.getLogger(__name__) class AccountBankStatementImport(models.TransientModel): - _inherit = 'account.bank.statement.import' + _inherit = "account.bank.statement.import" def _check_parsed_data(self, stmts_vals, account_number): """ Basic and structural verifications """ - if self.env.context.get('active_model') == 'ebics.file': + if self.env.context.get("active_model") == "ebics.file": message = False if len(stmts_vals) == 0: message = _("This file doesn't contain any statement.") if not message: no_st_line = True for vals in stmts_vals: - if vals['transactions'] and len(vals['transactions']) > 0: + if vals["transactions"] and len(vals["transactions"]) > 0: no_st_line = False break if no_st_line: - message = _('This file doesn\'t contain any transaction.') + message = _("This file doesn't contain any transaction.") if message: - log_msg = _( - "Error detected while processing and EBICS File" - ) + ':\n' + message + log_msg = ( + _("Error detected while processing and EBICS File") + + ":\n" + + message + ) _logger.warn(log_msg) return super()._check_parsed_data(stmts_vals, account_number) @@ -43,20 +45,16 @@ class AccountBankStatementImport(models.TransientModel): We could also create empty bank statement (in state done) to clearly show days without transactions via the bank statement list view. """ - if self.env.context.get('active_model') == 'ebics.file': + if self.env.context.get("active_model") == "ebics.file": transactions = False for st_vals in stmts_vals: - if st_vals.get('transactions'): + if st_vals.get("transactions"): transactions = True break if not transactions: - message = _('This file doesn\'t contain any transaction.') + message = _("This file doesn't contain any transaction.") st_line_ids = [] - notifications = { - 'type': 'warning', - 'message': message, - 'details': '' - } + notifications = {"type": "warning", "message": message, "details": ""} return st_line_ids, [notifications] return super()._create_bank_statements(stmts_vals) diff --git a/account_ebics_payment_order/__manifest__.py b/account_ebics_payment_order/__manifest__.py index 5836ab7..35ad8a2 100644 --- a/account_ebics_payment_order/__manifest__.py +++ b/account_ebics_payment_order/__manifest__.py @@ -2,16 +2,14 @@ # License LGPL-3 or later (http://www.gnu.org/licenses/lpgl). { - 'name': 'Upload Payment Order via EBICS', - 'version': '14.0.1.0.0', - 'license': 'LGPL-3', - 'author': 'Noviat', - 'category': 'Accounting & Finance', - 'depends': [ - 'account_ebics', - 'account_payment_order'], - 'data': [ - 'views/account_payment_order.xml', + "name": "Upload Payment Order via EBICS", + "version": "14.0.1.0.0", + "license": "LGPL-3", + "author": "Noviat", + "category": "Accounting & Finance", + "depends": ["account_ebics", "account_payment_order"], + "data": [ + "views/account_payment_order.xml", ], - 'installable': True, + "installable": True, } diff --git a/account_ebics_payment_order/models/account_payment_order.py b/account_ebics_payment_order/models/account_payment_order.py index 64e9442..081e927 100644 --- a/account_ebics_payment_order/models/account_payment_order.py +++ b/account_ebics_payment_order/models/account_payment_order.py @@ -6,57 +6,69 @@ from odoo.exceptions import UserError class AccountPaymentOrder(models.Model): - _inherit = 'account.payment.order' + _inherit = "account.payment.order" def ebics_upload(self): self.ensure_one() ctx = self._context.copy() - attach = self.env['ir.attachment'].search( - [('res_model', '=', self._name), - ('res_id', '=', self.id)]) + attach = self.env["ir.attachment"].search( + [("res_model", "=", self._name), ("res_id", "=", self.id)] + ) if not attach: - raise UserError(_( - "This payment order doesn't contains attachements." - "\nPlease generate first the Payment Order file first.")) + raise UserError( + _( + "This payment order doesn't contains attachements." + "\nPlease generate first the Payment Order file first." + ) + ) elif len(attach) > 1: - raise UserError(_( - "This payment order contains multiple attachments." - "\nPlease remove the obsolete attachments or upload " - "the payment order file via the " - "EBICS Processing > EBICS Upload menu")) + raise UserError( + _( + "This payment order contains multiple attachments." + "\nPlease remove the obsolete attachments or upload " + "the payment order file via the " + "EBICS Processing > EBICS Upload menu" + ) + ) else: - origin = _("Payment Order") + ': ' + self.name - ebics_config = self.env['ebics.config'].search([ - ('journal_ids', '=', self.journal_id.id), - ('state', '=', 'confirm'), - ]) + origin = _("Payment Order") + ": " + self.name + ebics_config = self.env["ebics.config"].search( + [ + ("journal_ids", "=", self.journal_id.id), + ("state", "=", "confirm"), + ] + ) if not ebics_config: - raise UserError(_( - "No active EBICS configuration available " - "for the selected bank." - )) + raise UserError( + _( + "No active EBICS configuration available " + "for the selected bank." + ) + ) if len(ebics_config) == 1: ctx["default_ebics_config_id"] = ebics_config.id - ctx.update({ - 'default_upload_data': attach.datas, - 'default_upload_fname': attach.name, - 'origin': origin, - 'force_comany': self.company_id.id, - }) - ebics_xfer = self.env['ebics.xfer'].with_context(ctx).create({}) + ctx.update( + { + "default_upload_data": attach.datas, + "default_upload_fname": attach.name, + "origin": origin, + "force_comany": self.company_id.id, + } + ) + ebics_xfer = self.env["ebics.xfer"].with_context(ctx).create({}) ebics_xfer._onchange_ebics_config_id() ebics_xfer._onchange_upload_data() ebics_xfer._onchange_format_id() - view = self.env.ref('account_ebics.ebics_xfer_view_form_upload') + view = self.env.ref("account_ebics.ebics_xfer_view_form_upload") act = { - 'name': _('EBICS Upload'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'ebics.xfer', - 'view_id': view.id, - 'res_id': ebics_xfer.id, - 'type': 'ir.actions.act_window', - 'target': 'new', - 'context': ctx, + "name": _("EBICS Upload"), + "view_type": "form", + "view_mode": "form", + "res_model": "ebics.xfer", + "view_id": view.id, + "res_id": ebics_xfer.id, + "type": "ir.actions.act_window", + "target": "new", + "context": ctx, } return act diff --git a/account_ebics_payment_order/views/account_payment_order.xml b/account_ebics_payment_order/views/account_payment_order.xml index 9bd43c8..8f81e20 100644 --- a/account_ebics_payment_order/views/account_payment_order.xml +++ b/account_ebics_payment_order/views/account_payment_order.xml @@ -1,15 +1,20 @@ - + account.payment.order.form account.payment.order - +