Files
odoo_source/addons/account_peppol/models/res_partner.py
T
aliya 10363b3ae1 [FIX] account_peppol: fix peppol ready state
- Create a Partner who is a valid Peppol participant
- Clear their UBL format - they are still displayed as valid (Bug 1)
- Create an invoice for that partner and confirm it. The Peppol state changes to 'ready'
- Erase eas or endpoint on that partner and verify - the partner is now not a valid Peppol participant
- Reset the invoice to draft, confirm again: the peppol move state is still `ready`

1. Do not set a participant as valid if a peppol-incompatible edi format has been selected
2. Only save Peppol move state if it's processing/done already. Otherwise, let users clear it by resetting to draft.
(until we implement giving them control over this field)

opw-3784945

closes odoo/odoo#159852

Signed-off-by: Laurent Smet (las) <las@odoo.com>
2024-04-04 17:02:58 +00:00

100 lines
4.6 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",
compute="_compute_account_peppol_is_endpoint_valid", store=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', 'ubl_cii_format')
def _compute_account_peppol_is_endpoint_valid(self):
for partner in self:
partner.button_account_peppol_check_partner_endpoint()
@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_user = self.env.company.account_edi_proxy_client_ids.filtered(lambda user: user.proxy_type == 'peppol')
sml_zone = 'acc.edelivery' if peppol_user.edi_mode == '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()
if not (self.peppol_eas and self.peppol_endpoint) or self.ubl_cii_format in (False, 'facturx', 'oioubl_201'):
self.account_peppol_is_endpoint_valid = False
else:
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 False