diff --git a/addons/sale/__init__.py b/addons/sale/__init__.py index b91ad4c9d06..a67265753e7 100644 --- a/addons/sale/__init__.py +++ b/addons/sale/__init__.py @@ -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) diff --git a/addons/sale/__manifest__.py b/addons/sale/__manifest__.py index 94035204a25..ddc91098081 100644 --- a/addons/sale/__manifest__.py +++ b/addons/sale/__manifest__.py @@ -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', } diff --git a/addons/sale/data/sale_data.xml b/addons/sale/data/sale_data.xml index 0f3abd5937b..8c7037993fb 100644 --- a/addons/sale/data/sale_data.xml +++ b/addons/sale/data/sale_data.xml @@ -16,5 +16,17 @@ sale.default_confirmation_template + + + automatic invoicing: send ready invoice + + code + model._cron_send_invoice() + + 1 + days + -1 + + diff --git a/addons/sale/models/account_move.py b/addons/sale/models/account_move.py index 3552fe478f8..c854192372a 100644 --- a/addons/sale/models/account_move.py +++ b/addons/sale/models/account_move.py @@ -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 diff --git a/addons/sale/models/payment_transaction.py b/addons/sale/models/payment_transaction.py index f7e5d83c5a4..efaf2de2545 100644 --- a/addons/sale/models/payment_transaction.py +++ b/addons/sale/models/payment_transaction.py @@ -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): diff --git a/addons/sale/models/res_config_settings.py b/addons/sale/models/res_config_settings.py index df4482fe99a..242c97d508e 100644 --- a/addons/sale/models/res_config_settings.py +++ b/addons/sale/models/res_config_settings.py @@ -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: