[FIX] sale: send invoice only when ready to be send

Issues
------

In the module sale, with the automatic invoicing configured.
Payment transaction generate, post and send the invoice when
they are post process.

In some country the invoice is not directly ready to be sent
due to some edi document

Solution
--------

use the mechanism introduce with https://github.com/odoo/odoo/commit/3a29371eb70309f46e3b8938434287fefc23b351
to delay the sending. Introduce a cron that check invoice to send

closes odoo/odoo#81526

X-original-commit: 97b74d8304337d901d9422418cbd0dcdcc46fcf5
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
Thibault Francois
2021-12-22 14:02:37 +00:00
parent 01ff1cd226
commit 2daa9a1673
6 changed files with 84 additions and 9 deletions
+10
View File
@@ -6,3 +6,13 @@ from . import controllers
from . import report
from . import wizard
from . import populate
from odoo.api import Environment, SUPERUSER_ID
def _synchronize_cron(cr, registry):
env = Environment(cr, SUPERUSER_ID, {'active_test': False})
send_invoice_cron = env.ref('sale.send_invoice_cron', raise_if_not_found=False)
if send_invoice_cron:
config = env['ir.config_parameter'].get_param('sale.automatic_invoice', False)
send_invoice_cron.active = bool(config)
+1
View File
@@ -72,5 +72,6 @@ This module contains all the common features of Sales Management and eCommerce.
'sale/static/tests/sales_team_dashboard_tests.js',
],
},
'post_init_hook': '_synchronize_cron',
'license': 'LGPL-3',
}
+12
View File
@@ -16,5 +16,17 @@
<field name="key">sale.default_confirmation_template</field>
<field name="value" ref="sale.mail_template_sale_confirmation"/>
</record>
<record model="ir.cron" id="send_invoice_cron">
<field name="name">automatic invoicing: send ready invoice</field>
<field name="model_id" ref="payment.model_payment_transaction" />
<field name="state">code</field>
<field name="code">model._cron_send_invoice()</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
</record>
</data>
</odoo>
+16
View File
@@ -109,3 +109,19 @@ class AccountMove(models.Model):
for (order, name) in todo:
order.message_post(body=_("Invoice %s paid", name))
return res
def _get_invoice_delivery_partner_id(self):
# OVERRIDE
self.ensure_one()
return self.partner_shipping_id.id or super(AccountMove, self)._get_invoice_delivery_partner_id()
def _action_invoice_ready_to_be_sent(self):
# OVERRIDE
# Make sure the send invoice CRON is called when an invoice becomes ready to be sent by mail.
res = super()._action_invoice_ready_to_be_sent()
send_invoice_cron = self.env.ref('sale.send_invoice_cron', raise_if_not_found=False)
if send_invoice_cron:
send_invoice_cron._trigger()
return res
+41 -9
View File
@@ -3,6 +3,9 @@
import logging
import re
from datetime import datetime
from dateutil import relativedelta
from odoo import api, fields, models, _, SUPERUSER_ID
from odoo.tools import float_compare
@@ -98,17 +101,46 @@ class PaymentTransaction(models.Model):
self._invoice_sale_orders()
res = super()._reconcile_after_done()
if self.env['ir.config_parameter'].sudo().get_param('sale.automatic_invoice') and any(so.state in ('sale', 'done') for so in self.sale_order_ids):
default_template = self.env['ir.config_parameter'].sudo().get_param('sale.default_invoice_email_template')
if default_template:
for trans in self.filtered(lambda t: t.sale_order_ids.filtered(lambda so: so.state in ('sale', 'done'))):
trans = trans.with_company(trans.acquirer_id.company_id).with_context(
mark_invoice_as_sent=True,
company_id=trans.acquirer_id.company_id.id,
)
for invoice in trans.invoice_ids.with_user(SUPERUSER_ID):
invoice.message_post_with_template(int(default_template), email_layout_xmlid="mail.mail_notification_paynow")
self.filtered(lambda t: t.sale_order_ids.filtered(lambda so: so.state in ('sale', 'done')))._send_invoice()
return res
def _send_invoice(self):
default_template = self.env['ir.config_parameter'].sudo().get_param('sale.default_invoice_email_template')
if not default_template:
return
for trans in self:
trans = trans.with_company(trans.acquirer_id.company_id).with_context(
company_id=trans.acquirer_id.company_id.id,
)
invoice_to_send = trans.invoice_ids.filtered(
lambda i: not i.is_move_sent and i.state == 'posted' and i._is_ready_to_be_sent()
)
invoice_to_send.is_move_sent = True # Mark invoice as sent
for invoice in invoice_to_send.with_user(SUPERUSER_ID):
invoice.message_post_with_template(int(default_template), email_layout_xmlid="mail.mail_notification_paynow")
def _cron_send_invoice(self):
"""
Cron to send invoice that where not ready to be send directly after posting
"""
if not self.env['ir.config_parameter'].sudo().get_param('sale.automatic_invoice'):
return
# No need to retrieve old transactions
retry_limit_date = datetime.now() - relativedelta.relativedelta(days=2)
# Retrieve all transactions matching the criteria for post-processing
self.search([
('state', '=', 'done'),
('is_post_processed', '=', True),
('invoice_ids', 'in', self.env['account.move']._search([
('is_move_sent', '=', False),
('state', '=', 'posted'),
])),
('sale_order_ids.state', 'in', ('sale', 'done')),
('last_state_change', '>=', retry_limit_date),
])._send_invoice()
def _invoice_sale_orders(self):
if self.env['ir.config_parameter'].sudo().get_param('sale.automatic_invoice'):
for trans in self.filtered(lambda t: t.sale_order_ids):
@@ -75,6 +75,10 @@ class ResConfigSettings(models.TransientModel):
if self.default_invoice_policy != 'order':
self.env['ir.config_parameter'].set_param('sale.automatic_invoice', False)
send_invoice_cron = self.env.ref('sale.send_invoice_cron', raise_if_not_found=False)
if send_invoice_cron:
send_invoice_cron.active = self.automatic_invoice
@api.onchange('use_quotation_validity_days')
def _onchange_use_quotation_validity_days(self):
if self.quotation_validity_days <= 0: