[IMP] account_peppol: improve move states and filters

Structure `peppol_move_state` according to the following logic:
1. Ready to Send / Invoices confirmed addressed to partners that are Peppol valid and not in one of the other statuses. Default behavior upon posting such an invoice.
2. Queued / In case of asynchronous sending only, the instruction to send has been given via the Send & Print but the cron wasn't run yet. It is still possible to cancel the sending.
3. Pending Reception / Too late to cancel, synchronous or asynchronous sending makes it now impossible to cancel the sending
4. Canceled / Once asked explicitly by the user. It's instant.
5. Done     / Upon confirmation or delivery received from the AP
6. Error / Upon confirmation of a notification of error received from the AP

Add an action on sale journals to open invoices that are ready to be sent via Peppol

- Peppol ready filter in Customer Invoices view
- Rename `PEPPOL state` to `Peppol status` for consistency
- Pre-fill valid phone numbers in `account_peppol_phone_number` field
- Fix async sending in demo mode

closes odoo/odoo#139615

Signed-off-by: Laurent Smet (las) <las@odoo.com>
This commit is contained in:
aliya
2023-10-25 18:11:11 +00:00
parent 489faccb78
commit 556b05125b
7 changed files with 61 additions and 8 deletions
@@ -1,6 +1,6 @@
# -*- coding:utf-8 -*-
from odoo import fields, models
from odoo import _, fields, models
class AccountJournal(models.Model):
@@ -22,3 +22,14 @@ class AccountJournal(models.Model):
('company_id', 'in', self.company_id.ids),
])
edi_users._peppol_get_message_status()
def action_peppol_ready_moves(self):
return {
'name': _("Peppol Ready invoices"),
'type': 'ir.actions.act_window',
'view_mode': 'list',
'res_model': 'account.move',
'context': {
'search_default_peppol_ready': 1,
}
}
+18 -3
View File
@@ -11,15 +11,16 @@ class AccountMove(models.Model):
peppol_message_uuid = fields.Char(string='PEPPOL message ID')
peppol_move_state = fields.Selection(
selection=[
('to_send', 'To Send'),
('processing', 'Processing'),
('ready', 'Ready to send'),
('to_send', 'Queued'),
('processing', 'Pending Reception'),
('canceled', 'Canceled'),
('done', 'Done'),
('error', 'Error'),
],
compute='_compute_peppol_move_state', store=True,
string='PEPPOL status',
copy=False,
readonly=True,
)
peppol_is_demo_uuid = fields.Boolean(compute="_compute_peppol_is_demo_uuid")
@@ -29,8 +30,22 @@ class AccountMove(models.Model):
if any(move.peppol_move_state in {'processing', 'done'} for move in self):
raise UserError(_("Cannot cancel an entry that has already been sent to PEPPOL"))
self.peppol_move_state = 'canceled'
self.send_and_print_values = False
@api.depends('peppol_message_uuid')
def _compute_peppol_is_demo_uuid(self):
for move in self:
move.peppol_is_demo_uuid = (move.peppol_message_uuid or '').startswith('demo_')
@api.depends('state')
def _compute_peppol_move_state(self):
for move in self:
if all([
move.company_id.account_peppol_proxy_state == 'active',
move.partner_id.account_peppol_is_endpoint_valid,
move.state == 'posted',
not move.peppol_move_state,
]):
move.peppol_move_state = 'ready'
else:
move.peppol_move_state = move.peppol_move_state
+17 -2
View File
@@ -43,6 +43,7 @@ class ResCompany(models.Model):
account_peppol_migration_key = fields.Char(string="Migration Key")
account_peppol_phone_number = fields.Char(
string='Phone number (for validation)',
compute='_compute_account_peppol_phone_number', store=True, readonly=False,
help='You will receive a verification code to this phone number',
)
account_peppol_proxy_state = fields.Selection(
@@ -72,7 +73,7 @@ class ResCompany(models.Model):
# HELPER METHODS
# -------------------------------------------------------------------------
def _sanitize_peppol_phone_number(self):
def _sanitize_peppol_phone_number(self, phone_number=None):
self.ensure_one()
error_message = _(
@@ -83,7 +84,10 @@ class ResCompany(models.Model):
if not phonenumbers:
raise ValidationError(error_message)
phone_number = self.account_peppol_phone_number
phone_number = phone_number or self.account_peppol_phone_number
if not phone_number:
return
if not phone_number.startswith('+'):
phone_number = f'+{phone_number}'
@@ -164,6 +168,17 @@ class ResCompany(models.Model):
if not company.account_peppol_contact_email:
company.account_peppol_contact_email = company.email
@api.depends('phone')
def _compute_account_peppol_phone_number(self):
for company in self:
if not company.account_peppol_phone_number:
try:
# precompute only if it's a valid phone number
company._sanitize_peppol_phone_number(company.phone)
company.account_peppol_phone_number = company.phone
except ValidationError:
continue
# -------------------------------------------------------------------------
# LOW-LEVEL METHODS
# -------------------------------------------------------------------------
+1 -1
View File
@@ -58,7 +58,7 @@ def _mock_make_request(func, self, *args, **kwargs):
return {
'messages': [{
'message_uuid': 'demo_%s' % uuid.uuid4(),
} for i in args[1]],
} for i in args[1]['documents']],
}
endpoint = args[0].split('/')[-1]
@@ -17,6 +17,9 @@
<div class="w-100">
<a type="object" name="peppol_get_message_status" groups="account.group_account_invoice">Fetch Peppol invoice status</a>
</div>
<div class="w-100">
<a type="object" name="action_peppol_ready_moves" groups="account.group_account_invoice">Peppol ready invoices</a>
</div>
</t>
<t t-elif="journal_type == 'purchase'">
<t t-if="record.is_peppol_journal.raw_value">
@@ -72,7 +72,12 @@
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
<field name="arch" type="xml">
<xpath expr="//search/group/filter[@name='status']" position="after">
<filter string="PEPPOL state" name="peppol_move_state" context="{'group_by': 'peppol_move_state'}"/>
<filter string="Peppol status" name="peppol_move_state" context="{'group_by': 'peppol_move_state'}"/>
</xpath>
<xpath expr="//filter[@name='to_check']" position='after'>
<separator/>
<filter name="peppol_ready" string="Peppol Ready" domain="[('state', '=', 'posted'), ('peppol_move_state', '=', 'ready')]"/>
<separator/>
</xpath>
</field>
</record>
@@ -107,6 +107,10 @@ class AccountMoveSend(models.TransientModel):
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)