97 lines
4.4 KiB
Python
97 lines
4.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import requests
|
|
from lxml import etree
|
|
from hashlib import md5
|
|
from urllib import parse
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.addons.account_peppol.tools.demo_utils import handle_demo
|
|
|
|
TIMEOUT = 10
|
|
|
|
class ResPartner(models.Model):
|
|
_inherit = 'res.partner'
|
|
|
|
account_peppol_is_endpoint_valid = fields.Boolean(
|
|
string="PEPPOL endpoint validity",
|
|
help="The partner's EAS code and PEPPOL endpoint are valid",
|
|
compute="_compute_account_peppol_is_endpoint_valid", store=True,
|
|
copy=False,
|
|
)
|
|
account_peppol_validity_last_check = fields.Date(
|
|
string="Checked on",
|
|
help="Last Peppol endpoint verification",
|
|
readonly=True,
|
|
copy=False,
|
|
)
|
|
account_peppol_verification_label = fields.Selection(
|
|
selection=[
|
|
('not_verified', 'Not verified yet'),
|
|
('not_valid', 'Not valid'),
|
|
('valid', 'Valid'),
|
|
],
|
|
string='Peppol endpoint validity',
|
|
compute='_compute_account_peppol_verification_label',
|
|
copy=False,
|
|
) # field to compute the label to show for partner endpoint
|
|
|
|
@api.depends('peppol_eas', 'peppol_endpoint')
|
|
def _compute_account_peppol_is_endpoint_valid(self):
|
|
# Every change in peppol_eas or peppol_endpoint should set the validity back to False
|
|
self.account_peppol_is_endpoint_valid = False
|
|
|
|
@api.depends('account_peppol_is_endpoint_valid', 'account_peppol_validity_last_check')
|
|
def _compute_account_peppol_verification_label(self):
|
|
for partner in self:
|
|
if not partner.account_peppol_validity_last_check:
|
|
partner.account_peppol_verification_label = 'not_verified'
|
|
elif partner.account_peppol_is_endpoint_valid:
|
|
partner.account_peppol_verification_label = 'valid'
|
|
else:
|
|
partner.account_peppol_verification_label = 'not_valid'
|
|
|
|
@api.model
|
|
def _check_peppol_participant_exists(self, edi_identification):
|
|
hash_participant = md5(edi_identification.lower().encode()).hexdigest()
|
|
endpoint_participant = parse.quote_plus(f"iso6523-actorid-upis::{edi_identification}")
|
|
peppol_param = self.env['ir.config_parameter'].sudo().get_param('account_peppol.edi.mode', False)
|
|
sml_zone = 'acc.edelivery' if peppol_param == 'test' else 'edelivery'
|
|
smp_url = f"http://B-{hash_participant}.iso6523-actorid-upis.{sml_zone}.tech.ec.europa.eu/{endpoint_participant}"
|
|
|
|
try:
|
|
response = requests.get(smp_url, timeout=TIMEOUT)
|
|
except requests.exceptions.ConnectionError:
|
|
return False
|
|
if response.status_code != 200:
|
|
return False
|
|
participant_info = etree.XML(response.content)
|
|
participant_identifier = participant_info.findtext('{*}ParticipantIdentifier')
|
|
service_metadata = participant_info.find('.//{*}ServiceMetadataReference')
|
|
service_href = ''
|
|
if service_metadata is not None:
|
|
service_href = service_metadata.attrib.get('href', '')
|
|
if edi_identification != participant_identifier or 'hermes-belgium' in service_href:
|
|
# all Belgian companies are pre-registered on hermes-belgium, so they will
|
|
# technically have an existing SMP url but they are not real Peppol participants
|
|
return False
|
|
return True
|
|
|
|
@handle_demo
|
|
def button_account_peppol_check_partner_endpoint(self):
|
|
""" A basic check for whether a participant is reachable at the given
|
|
Peppol participant ID - peppol_eas:peppol_endpoint (ex: '9999:test')
|
|
The SML (Service Metadata Locator) assigns a DNS name to each peppol participant.
|
|
This DNS name resolves into the SMP (Service Metadata Publisher) of the participant.
|
|
The DNS address is of the following form:
|
|
- "http://B-" + hexstring(md5(lowercase(ID-VALUE))) + "." + ID-SCHEME + "." + SML-ZONE-NAME + "/" + url_encoded(ID-SCHEME + "::" + ID-VALUE)
|
|
(ref:https://peppol.helger.com/public/locale-en_US/menuitem-docs-doc-exchange)
|
|
"""
|
|
self.ensure_one()
|
|
|
|
edi_identification = f'{self.peppol_eas}:{self.peppol_endpoint}'.lower()
|
|
self.account_peppol_validity_last_check = fields.Date.context_today(self)
|
|
self.account_peppol_is_endpoint_valid = self._check_peppol_participant_exists(edi_identification)
|
|
return True
|