191 lines
8.8 KiB
Python
191 lines
8.8 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from base64 import b64encode
|
||
|
|
||
|
from odoo import api, fields, models, _
|
||
|
from odoo.addons.account_edi_proxy_client.models.account_edi_proxy_user import AccountEdiProxyError
|
||
|
|
||
|
|
||
|
class AccountMoveSend(models.TransientModel):
|
||
|
_inherit = 'account.move.send'
|
||
|
|
||
|
checkbox_send_peppol = fields.Boolean(
|
||
|
string='Send via PEPPOL',
|
||
|
compute='_compute_checkbox_send_peppol', store=True, readonly=False,
|
||
|
help='Send the invoice via PEPPOL',
|
||
|
)
|
||
|
enable_peppol = fields.Boolean(compute='_compute_enable_peppol')
|
||
|
# technical field needed for computing a warning text about the peppol configuration
|
||
|
peppol_warning = fields.Char(
|
||
|
string="Warning",
|
||
|
compute="_compute_peppol_warning",
|
||
|
)
|
||
|
account_peppol_edi_mode_info = fields.Char(compute='_compute_account_peppol_edi_mode_info')
|
||
|
|
||
|
def _get_wizard_values(self):
|
||
|
# EXTENDS 'account'
|
||
|
values = super()._get_wizard_values()
|
||
|
values['send_peppol'] = self.checkbox_send_peppol
|
||
|
return values
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# COMPUTE METHODS
|
||
|
# -------------------------------------------------------------------------
|
||
|
|
||
|
@api.depends('enable_peppol')
|
||
|
def _compute_checkbox_send_peppol(self):
|
||
|
for wizard in self:
|
||
|
wizard.checkbox_send_peppol = wizard.enable_peppol
|
||
|
|
||
|
@api.depends('checkbox_send_peppol')
|
||
|
def _compute_checkbox_ubl_cii_xml(self):
|
||
|
# extends 'account_edi_ubl_cii'
|
||
|
super()._compute_checkbox_ubl_cii_xml()
|
||
|
for wizard in self:
|
||
|
if wizard.checkbox_send_peppol and wizard.enable_ubl_cii_xml and not wizard.checkbox_ubl_cii_xml:
|
||
|
wizard.checkbox_ubl_cii_xml = True
|
||
|
|
||
|
@api.depends('checkbox_send_peppol')
|
||
|
def _compute_mail_attachments_widget(self):
|
||
|
# EXTENDS 'account' - add depends
|
||
|
super()._compute_mail_attachments_widget()
|
||
|
|
||
|
@api.depends('move_ids')
|
||
|
def _compute_peppol_warning(self):
|
||
|
for wizard in self:
|
||
|
invalid_partners = wizard.move_ids.partner_id.filtered(
|
||
|
lambda partner: not partner.account_peppol_is_endpoint_valid)
|
||
|
if not invalid_partners:
|
||
|
wizard.peppol_warning = False
|
||
|
else:
|
||
|
names = ', '.join(invalid_partners[:5].mapped('display_name'))
|
||
|
wizard.peppol_warning = _("The following partners are not correctly configured to receive Peppol documents. "
|
||
|
"Please check and verify their Peppol endpoint and the Electronic Invoicing format: "
|
||
|
"%s", names)
|
||
|
|
||
|
@api.depends('enable_ubl_cii_xml')
|
||
|
def _compute_enable_peppol(self):
|
||
|
for wizard in self:
|
||
|
# show peppol option if either the ubl option is available or any move already has a ubl file generated
|
||
|
# and moves are not processing/done and if partners have an edi format set to one that works for peppol
|
||
|
invalid_partners = wizard.move_ids.partner_id.filtered(
|
||
|
lambda partner: partner.ubl_cii_format in {False, 'facturx', 'oioubl_201'})
|
||
|
wizard.enable_peppol = (
|
||
|
wizard.company_id.account_peppol_proxy_state == 'active' \
|
||
|
and (
|
||
|
wizard.enable_ubl_cii_xml
|
||
|
or any(m.ubl_cii_xml_id and m.peppol_move_state not in ('processing', 'done') for m in wizard.move_ids)
|
||
|
)
|
||
|
and not invalid_partners
|
||
|
)
|
||
|
|
||
|
@api.depends('company_id.account_edi_proxy_client_ids.edi_mode')
|
||
|
def _compute_account_peppol_edi_mode_info(self):
|
||
|
mode_strings = {
|
||
|
'test': _('Test'),
|
||
|
'demo': _('Demo'),
|
||
|
}
|
||
|
for wizard in self:
|
||
|
edi_user = wizard.company_id.account_edi_proxy_client_ids.filtered(
|
||
|
lambda usr: usr.proxy_type == 'peppol'
|
||
|
)
|
||
|
mode = mode_strings.get(edi_user.edi_mode)
|
||
|
wizard.account_peppol_edi_mode_info = f' ({mode})' if mode else ''
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# ATTACHMENTS
|
||
|
# -------------------------------------------------------------------------
|
||
|
|
||
|
def _needs_ubl_cii_placeholder(self):
|
||
|
return super()._needs_ubl_cii_placeholder() and not self.checkbox_send_peppol
|
||
|
|
||
|
# -------------------------------------------------------------------------
|
||
|
# BUSINESS ACTIONS
|
||
|
# -------------------------------------------------------------------------
|
||
|
|
||
|
def action_send_and_print(self, force_synchronous=False, allow_fallback_pdf=False, **kwargs):
|
||
|
# Extends 'account' to force ubl xml checkbox if sending via peppol
|
||
|
self.ensure_one()
|
||
|
|
||
|
if all([self.checkbox_send_peppol, self.enable_peppol, self.enable_ubl_cii_xml, not self.checkbox_ubl_cii_xml]):
|
||
|
self.checkbox_ubl_cii_xml = True
|
||
|
if self.checkbox_send_peppol and self.enable_peppol:
|
||
|
for move in self.move_ids:
|
||
|
if not move.peppol_move_state or move.peppol_move_state == 'ready':
|
||
|
move.peppol_move_state = 'to_send'
|
||
|
|
||
|
return super().action_send_and_print(force_synchronous=force_synchronous, allow_fallback_pdf=allow_fallback_pdf, **kwargs)
|
||
|
|
||
|
@api.model
|
||
|
def _call_web_service_after_invoice_pdf_render(self, invoices_data):
|
||
|
# Overrides 'account'
|
||
|
super()._call_web_service_after_invoice_pdf_render(invoices_data)
|
||
|
|
||
|
params = {'documents': []}
|
||
|
invoices_data_peppol = {}
|
||
|
for invoice, invoice_data in invoices_data.items():
|
||
|
if invoice_data.get('send_peppol'):
|
||
|
if invoice_data.get('ubl_cii_xml_attachment_values'):
|
||
|
xml_file = invoice_data['ubl_cii_xml_attachment_values']['raw']
|
||
|
filename = invoice_data['ubl_cii_xml_attachment_values']['name']
|
||
|
elif invoice.ubl_cii_xml_id and invoice.peppol_move_state not in ('processing', 'canceled', 'done'):
|
||
|
xml_file = invoice.ubl_cii_xml_id.raw
|
||
|
filename = invoice.ubl_cii_xml_id.name
|
||
|
else:
|
||
|
invoice.peppol_move_state = 'skipped'
|
||
|
continue
|
||
|
|
||
|
if not invoice.partner_id.peppol_eas or not invoice.partner_id.peppol_endpoint:
|
||
|
# should never happen but in case it does, we need to handle it
|
||
|
invoice.peppol_move_state = 'error'
|
||
|
invoice_data['error'] = _('The partner is missing Peppol EAS and/or Endpoint identifier.')
|
||
|
continue
|
||
|
|
||
|
if not invoice.partner_id.account_peppol_is_endpoint_valid:
|
||
|
invoice.peppol_move_state = 'error'
|
||
|
invoice_data['error'] = _('Please verify partner configuration in partner settings.')
|
||
|
continue
|
||
|
|
||
|
receiver_identification = f"{invoice.partner_id.peppol_eas}:{invoice.partner_id.peppol_endpoint}"
|
||
|
params['documents'].append({
|
||
|
'filename': filename,
|
||
|
'receiver': receiver_identification,
|
||
|
'ubl': b64encode(xml_file).decode(),
|
||
|
})
|
||
|
invoices_data_peppol[invoice] = invoice_data
|
||
|
|
||
|
if not params['documents']:
|
||
|
return
|
||
|
|
||
|
edi_user = next(iter(invoices_data)).company_id.account_edi_proxy_client_ids.filtered(
|
||
|
lambda u: u.proxy_type == 'peppol')
|
||
|
|
||
|
try:
|
||
|
response = edi_user._make_request(
|
||
|
f"{edi_user._get_server_url()}/api/peppol/1/send_document",
|
||
|
params=params,
|
||
|
)
|
||
|
if response.get('error'):
|
||
|
# at the moment the only error that can happen here is ParticipantNotReady error
|
||
|
for invoice, invoice_data in invoices_data_peppol.items():
|
||
|
invoice.peppol_move_state = 'error'
|
||
|
invoice_data['error'] = response['error']['message']
|
||
|
except AccountEdiProxyError as e:
|
||
|
for invoice, invoice_data in invoices_data_peppol.items():
|
||
|
invoice.peppol_move_state = 'error'
|
||
|
invoice_data['error'] = e.message
|
||
|
else:
|
||
|
# the response only contains message uuids,
|
||
|
# so we have to rely on the order to connect peppol messages to account.move
|
||
|
invoices = self.env['account.move']
|
||
|
for i, (invoice, invoice_data) in enumerate(invoices_data_peppol.items()):
|
||
|
invoice.peppol_message_uuid = response['messages'][i]['message_uuid']
|
||
|
invoice.peppol_move_state = 'processing'
|
||
|
invoices |= invoice
|
||
|
log_message = _('The document has been sent to the Peppol Access Point for processing')
|
||
|
invoices._message_log_batch(bodies=dict((invoice.id, log_message) for invoice in invoices_data_peppol))
|
||
|
|
||
|
if self._can_commit():
|
||
|
self._cr.commit()
|