mirror of
https://github.com/brain-tec/account_ebics.git
synced 2026-11-06 18:10:39 +00:00
ZZZZ is accepted by the order number constraint, but the next number was computed as 0000, which the constraint then rejects, blocking H003 uploads. Wrap it to A000 like ZZZY already does.
205 lines
6.8 KiB
Python
205 lines
6.8 KiB
Python
# Copyright 2009-2024 Noviat.
|
|
# License LGPL-3 or later (https://www.gnu.org/licenses/lgpl).
|
|
|
|
import logging
|
|
import os
|
|
import re
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.exceptions import UserError
|
|
|
|
_logger = logging.getLogger(__name__) # lint-ignore=unused-logger
|
|
|
|
|
|
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 = fields.Char(
|
|
required=True,
|
|
)
|
|
journal_ids = fields.Many2many(
|
|
comodel_name="account.journal",
|
|
relation="account_journal_ebics_config_rel",
|
|
string="Bank Accounts",
|
|
domain="[('type', '=', 'bank')]",
|
|
)
|
|
ebics_host = fields.Char(
|
|
string="EBICS HostID",
|
|
required=True,
|
|
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.",
|
|
)
|
|
ebics_url = fields.Char(
|
|
string="EBICS URL",
|
|
required=True,
|
|
help="Contact your bank to get the EBICS URL.",
|
|
)
|
|
ebics_version = fields.Selection(
|
|
selection=[
|
|
("H003", "H003 (2.4)"),
|
|
("H004", "H004 (2.5)"),
|
|
("H005", "H005 (3.0)"),
|
|
],
|
|
string="EBICS protocol version",
|
|
required=True,
|
|
default="H004",
|
|
)
|
|
ebics_partner = fields.Char(
|
|
string="EBICS PartnerID",
|
|
required=True,
|
|
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.",
|
|
)
|
|
ebics_userid_ids = fields.One2many(
|
|
comodel_name="ebics.userid",
|
|
inverse_name="ebics_config_id",
|
|
string="EBICS UserID",
|
|
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.",
|
|
)
|
|
# 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,
|
|
default=lambda self: self._default_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",
|
|
help="The key version of the electronic signature.",
|
|
)
|
|
ebics_key_bitlength = fields.Integer(
|
|
string="EBICS key bitlength",
|
|
default=2048,
|
|
help="The bit length of the generated keys. "
|
|
"\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 Format",
|
|
)
|
|
state = fields.Selection(
|
|
selection=[("draft", "Draft"), ("confirm", "Confirmed")],
|
|
default="draft",
|
|
required=True,
|
|
readonly=True,
|
|
)
|
|
order_number = fields.Char(
|
|
size=4,
|
|
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(default=True)
|
|
company_ids = fields.Many2many(
|
|
comodel_name="res.company",
|
|
relation="ebics_config_res_company_rel",
|
|
string="Companies",
|
|
compute="_compute_company_ids",
|
|
store=True,
|
|
help="Companies sharing this EBICS contract.",
|
|
)
|
|
|
|
@api.model
|
|
def _default_ebics_keys(self):
|
|
return f"/etc/odoo/ebics_keys/{self._cr.dbname}"
|
|
|
|
@api.constrains("ebics_key_bitlength")
|
|
def _check_ebics_key_bitlength(self):
|
|
for cfg in self:
|
|
if cfg.ebics_version == "H005" and cfg.ebics_key_bitlength < 2048:
|
|
raise UserError(self.env._("EBICS key bitlength must be >= 2048."))
|
|
|
|
@api.constrains("order_number")
|
|
def _check_order_number(self):
|
|
for cfg in self:
|
|
nbr = cfg.order_number
|
|
ok = True
|
|
if nbr:
|
|
if len(nbr) != 4:
|
|
ok = False
|
|
else:
|
|
pattern = re.compile("[A-Z]{1}[A-Z0-9]{3}")
|
|
if not pattern.match(nbr):
|
|
ok = False
|
|
if not ok:
|
|
raise UserError(
|
|
self.env._( # pylint: disable=W8120
|
|
"Order Number should comply with the following pattern:"
|
|
"\n[A-Z]{1}[A-Z0-9]{3}"
|
|
)
|
|
)
|
|
|
|
@api.depends("journal_ids.company_id")
|
|
def _compute_company_ids(self):
|
|
for rec in self:
|
|
rec.company_ids = rec.journal_ids.mapped("company_id")
|
|
|
|
@api.ondelete(at_uninstall=False)
|
|
def _unlink_except_confirm(self):
|
|
if any(rec.state == "confirm" for rec in self):
|
|
raise UserError(
|
|
self.env._("You cannot remove active EBICS configurations.")
|
|
)
|
|
|
|
def set_to_draft(self):
|
|
return self.write({"state": "draft"})
|
|
|
|
def set_to_confirm(self):
|
|
return self.write({"state": "confirm"})
|
|
|
|
def _get_order_number(self):
|
|
return self.order_number
|
|
|
|
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"
|
|
break
|
|
if c == "Z":
|
|
o_list[-i] = "0"
|
|
continue
|
|
o_list[-i] = chr(ord(c) + 1)
|
|
break
|
|
next_order_number = "".join(o_list)
|
|
if "ZZZZ" in (OrderID, next_order_number):
|
|
next_order_number = "A000"
|
|
self.order_number = next_order_number
|
|
|
|
def _check_ebics_keys(self):
|
|
dirname = self.ebics_keys or ""
|
|
if not os.path.exists(dirname):
|
|
raise UserError(
|
|
self.env._(
|
|
"EBICS Keys Root Directory %(dir)s is not available."
|
|
"\nPlease contact your system administrator.",
|
|
dir=dirname,
|
|
)
|
|
)
|