import logging import requests from markupsafe import Markup from lxml import etree from hashlib import md5 from urllib import parse from odoo import _, api, fields, models from odoo.exceptions import ValidationError from odoo.addons.l10n_dk_nemhandel.tools.demo_utils import handle_demo TIMEOUT = 10 _logger = logging.getLogger(__name__) class ResPartner(models.Model): _inherit = 'res.partner' invoice_sending_method = fields.Selection( selection_add=[('nemhandel', 'By Nemhandel')], ) invoice_edi_format = fields.Selection(selection_add=[('oioubl_21', "OIOUBL 2.1")]) nemhandel_verification_state = fields.Selection( selection=[ ('not_verified', 'Not verified yet'), ('not_valid', 'Not on Nemhandel'), # Is not on Nemhandel ('valid', 'Valid'), ], string='Nemhandel endpoint verification', company_dependent=True, ) nemhandel_identifier_type = fields.Selection( string='Nemhandel Endpoint Type', help='Unique identifier used by OIOUBL and Nemhandel', compute="_compute_nemhandel_identifier_type", store=True, readonly=False, tracking=True, selection=[ ('0088', "EAN/GLN"), ('0184', "CVR"), ('9918', "IBAN"), ('0198', "SE"), ], ) nemhandel_identifier_value = fields.Char( string='Nemhandel Endpoint', help='Code used to identify the Endpoint on Nemhandel', compute="_compute_nemhandel_identifier_value", store=True, readonly=False, tracking=True, ) is_using_nemhandel = fields.Boolean(compute='_compute_is_using_nemhandel') # ------------------------------------------------------------------------- # COMPUTE METHODS # ------------------------------------------------------------------------- @api.depends('country_code', 'vat', 'company_registry') def _compute_nemhandel_identifier_type(self): for partner in self: partner.nemhandel_identifier_type = partner.nemhandel_identifier_type country_code = partner._deduce_country_code() if country_code == 'DK' and not partner.nemhandel_identifier_type: partner.nemhandel_identifier_type = '0184' elif country_code != 'DK': partner.nemhandel_identifier_type = '' @api.depends('country_code', 'vat', 'company_registry', 'nemhandel_identifier_type') def _compute_nemhandel_identifier_value(self): for partner in self: if partner.nemhandel_identifier_value != partner._origin.nemhandel_identifier_value: # value changed, don't override it partner.nemhandel_identifier_value = partner.nemhandel_identifier_value continue country_code = partner._deduce_country_code() if country_code == 'DK' and partner.nemhandel_identifier_type == '0184': partner.nemhandel_identifier_value = partner.company_registry elif country_code == 'DK': partner.nemhandel_identifier_value = partner.nemhandel_identifier_value else: partner.nemhandel_identifier_value = '' @api.depends_context('allowed_company_ids') @api.depends('invoice_edi_format') def _compute_is_using_nemhandel(self): nemhandel_user = self.env.company.sudo().nemhandel_edi_user for partner in self: partner.is_using_nemhandel = nemhandel_user and partner.invoice_edi_format == 'oioubl_21' # ------------------------------------------------------------------------- # CONSTRAINT # ------------------------------------------------------------------------- @api.constrains('invoice_edi_format', 'invoice_sending_method') def _check_nemhandel_send_oioubl(self): if self.filtered(lambda partner: partner.invoice_edi_format != 'oioubl_21' and partner.invoice_sending_method == 'nemhandel'): raise ValidationError(_('On Nemhandel, only OIOUBL 2.1 is supported.')) # ------------------------------------------------------------------------- # OVERRIDE AND HELPERS # ------------------------------------------------------------------------- def _get_edi_builder(self, invoice_edi_format): # EXTENDS 'account_edi_ubl_cii' if invoice_edi_format == 'oioubl_21': return self.env['account.edi.xml.oioubl_21'] return super()._get_edi_builder(invoice_edi_format) def _get_ubl_cii_formats_info(self): # EXTENDS 'account_edi_ubl_cii' formats_info = super()._get_ubl_cii_formats_info() formats_info['oioubl_21'] = {'countries': ['DK'], 'on_peppol': False} return formats_info def _get_suggested_invoice_edi_format(self): # EXTENDS 'account' if self.country_code == 'DK': return 'oioubl_21' return super()._get_suggested_invoice_edi_format() @api.model def _get_nemhandel_participant_info(self, edi_identification): hash_participant = md5(edi_identification.lower().encode()).hexdigest() endpoint_participant = parse.quote_plus(f"iso6523-actorid-upis::{edi_identification}") nemhandel_user = self.env.company.sudo().nemhandel_edi_user edi_mode = nemhandel_user and nemhandel_user.edi_mode or self.env['ir.config_parameter'].sudo().get_param('l10n_dk_nemhandel.edi.mode') sml_zone = 'edel.sml-demo' if edi_mode == 'test' else 'edel.sml' smp_url = f"http://B-{hash_participant}.iso6523-actorid-upis.{sml_zone}.dataudveksling.dk/{endpoint_participant}" try: response = requests.get(smp_url, timeout=TIMEOUT) response.raise_for_status() except requests.exceptions.RequestException as e: _logger.info(e) return None return response.content def _l10n_dk_nemhandel_log_verification_state_update(self, company, old_value, new_value): # log the update of the nemhandel verification state # we do this instead of regular tracking because of the customized message # and because we want to log the change for every company in the db if old_value == new_value: return nemhandel_verification_state_field = self._fields['nemhandel_verification_state'] selection_values = dict(nemhandel_verification_state_field.selection) old_label = selection_values[old_value] if old_value else False # get translated labels new_label = selection_values[new_value] if new_value else False body = Markup("""