[REF] Lunch: Use dynamic daily crons for auto emails
The Lunch module can automatically send an email to a supplier with the daily orders at a configured time. It can also remind the users via chat so they don't forget to order their sanddwish. Before, two high frequency cron jobs were responsible to check every 20 and 5 minutes if we reached the moment when to send the email to the suppliers or the notification to the users. Together the two crons were executed 360 times a day. The new model uses a dedicated daily cron per supplier record and per alert record, the dedicated cron is created on the fly and its moment of execution automatically updated to reflect the supplier/alert record. See also #41858 for prior work. closes odoo/odoo#63749 Task: 2416741 Related: odoo/upgrade#2044 Signed-off-by: Julien Castiaux <Julien00859@users.noreply.github.com>
This commit is contained in:
@@ -36,7 +36,6 @@ If you want to save your employees' time and avoid them to always have coins in
|
||||
'views/lunch_views.xml',
|
||||
'data/mail_template_data.xml',
|
||||
'data/lunch_data.xml',
|
||||
'data/ir_cron_data.xml'
|
||||
],
|
||||
'demo': ['data/lunch_demo.xml'],
|
||||
'qweb': ['static/src/xml/lunch_templates.xml', ],
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="ir_cron_lunch" model="ir.cron">
|
||||
<field name="name">Lunch: automatic email send</field>
|
||||
<field name="model_id" ref="model_lunch_supplier"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._auto_email_send()</field>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">20</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
</record>
|
||||
<record id="ir_cron_lunch_alerts" model="ir.cron">
|
||||
<field name="name">Lunch: alert chat notification</field>
|
||||
<field name="model_id" ref="model_lunch_alert"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._notify_chat()</field>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">5</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,16 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import pytz
|
||||
import logging
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.osv import expression
|
||||
|
||||
from .lunch_supplier import float_to_time
|
||||
from datetime import datetime, timedelta
|
||||
from textwrap import dedent
|
||||
|
||||
from odoo.addons.base.models.res_partner import _tz_get
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
WEEKDAY_TO_NAME = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
|
||||
CRON_DEPENDS = {'name', 'active', 'mode', 'until', 'notification_time', 'notification_moment', 'tz'}
|
||||
|
||||
|
||||
class LunchAlert(models.Model):
|
||||
""" Alerts to display during a lunch order. An alert can be specific to a
|
||||
@@ -35,6 +39,7 @@ class LunchAlert(models.Model):
|
||||
('am', 'AM'),
|
||||
('pm', 'PM')], default='am', required=True)
|
||||
tz = fields.Selection(_tz_get, string='Timezone', required=True, default=lambda self: self.env.user.tz or 'UTC')
|
||||
cron_id = fields.Many2one('ir.cron', ondelete='cascade', required=True, readonly=True)
|
||||
|
||||
until = fields.Date('Show Until')
|
||||
recurrency_monday = fields.Boolean('Monday', default=True)
|
||||
@@ -84,36 +89,98 @@ class LunchAlert(models.Model):
|
||||
])
|
||||
])
|
||||
|
||||
def _sync_cron(self):
|
||||
""" Synchronise the related cron fields to reflect this alert """
|
||||
for alert in self:
|
||||
alert = alert.with_context(tz=alert.tz)
|
||||
|
||||
cron_required = (
|
||||
alert.active
|
||||
and alert.mode == 'chat'
|
||||
and (not alert.until or fields.Date.context_today(alert) <= alert.until)
|
||||
)
|
||||
|
||||
sendat_tz = pytz.timezone(alert.tz).localize(datetime.combine(
|
||||
fields.Date.context_today(alert, fields.Datetime.now()),
|
||||
float_to_time(alert.notification_time, alert.notification_moment)))
|
||||
lc = alert.cron_id.lastcall
|
||||
if ((
|
||||
lc and sendat_tz.date() <= fields.Datetime.context_timestamp(alert, lc).date()
|
||||
) or (
|
||||
not lc and sendat_tz <= fields.Datetime.context_timestamp(alert, fields.Datetime.now())
|
||||
)):
|
||||
sendat_tz += timedelta(days=1)
|
||||
sendat_utc = sendat_tz.astimezone(pytz.UTC).replace(tzinfo=None)
|
||||
|
||||
alert.cron_id.name = f"Lunch: alert chat notification ({alert.name})"
|
||||
alert.cron_id.active = cron_required
|
||||
alert.cron_id.nextcall = sendat_utc
|
||||
alert.cron_id.code = dedent(f"""\
|
||||
# This cron is dynamically controlled by {self._description}.
|
||||
# Do NOT modify this cron, modify the related record instead.
|
||||
env['{self._name}'].browse([{alert.id}])._notify_chat()""")
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
crons = self.env['ir.cron'].sudo().create([
|
||||
{
|
||||
'user_id': self.env.ref('base.user_root').id,
|
||||
'active': False,
|
||||
'interval_type': 'days',
|
||||
'interval_number': 1,
|
||||
'numbercall': -1,
|
||||
'doall': False,
|
||||
'name': "Lunch: alert chat notification",
|
||||
'model_id': self.env['ir.model']._get_id(self._name),
|
||||
'state': 'code',
|
||||
'code': "",
|
||||
}
|
||||
for _ in range(len(vals_list))
|
||||
])
|
||||
for vals, cron in zip(vals_list, crons):
|
||||
vals['cron_id'] = cron.id
|
||||
|
||||
alerts = super().create(vals_list)
|
||||
alerts._sync_cron()
|
||||
return alerts
|
||||
|
||||
def write(self, values):
|
||||
super().write(values)
|
||||
if not CRON_DEPENDS.isdisjoint(values):
|
||||
self._sync_cron()
|
||||
|
||||
def unlink(self):
|
||||
crons = self.cron_id
|
||||
super().unlink()
|
||||
crons.unlink()
|
||||
|
||||
def _notify_chat(self):
|
||||
records = self.search([('mode', '=', 'chat'), ('active', '=', True)])
|
||||
# Called daily by cron
|
||||
self.ensure_one()
|
||||
|
||||
today = fields.Date.today()
|
||||
now = fields.Datetime.now()
|
||||
if not self.available_today:
|
||||
_logger.warning("cancelled, not available today")
|
||||
if self.cron_id and self.until and fields.Date.context_today(self) > self.until:
|
||||
self.cron_id.unlink()
|
||||
self.cron_id = False
|
||||
return
|
||||
|
||||
for alert in records:
|
||||
notification_to = now.astimezone(pytz.timezone(alert.tz)).replace(second=0, microsecond=0, tzinfo=None)
|
||||
notification_from = notification_to - timedelta(minutes=5)
|
||||
send_at = datetime.combine(fields.Date.today(),
|
||||
float_to_time(alert.notification_time, alert.notification_moment))
|
||||
if not self.active or self.mode != 'chat':
|
||||
raise ValueError("Cannot send a chat notification in the current state")
|
||||
|
||||
if alert.available_today and send_at > notification_from and send_at <= notification_to:
|
||||
order_domain = [('state', '!=', 'cancelled')]
|
||||
order_domain = [('state', '!=', 'cancelled')]
|
||||
|
||||
if alert.location_ids.ids:
|
||||
order_domain = expression.AND([order_domain, [('user_id.last_lunch_location_id', 'in', alert.location_ids.ids)]])
|
||||
if self.location_ids.ids:
|
||||
order_domain = expression.AND([order_domain, [('user_id.last_lunch_location_id', 'in', self.location_ids.ids)]])
|
||||
|
||||
if alert.recipients != 'everyone':
|
||||
weeks = 1
|
||||
if self.recipients != 'everyone':
|
||||
weeksago = fields.Date.today() - timedelta(weeks=(
|
||||
1 if self.recipients == 'last_week' else
|
||||
4 if self.recipients == 'last_month' else
|
||||
52 # if self.recipients == 'last_year'
|
||||
))
|
||||
order_domain = expression.AND([order_domain, [('date', '>=', weeksago)]])
|
||||
|
||||
if alert.recipients == 'last_month':
|
||||
weeks = 4
|
||||
else: # last_year
|
||||
weeks = 52
|
||||
|
||||
delta = timedelta(weeks=weeks)
|
||||
order_domain = expression.AND([order_domain, [('date', '>=', today - delta)]])
|
||||
|
||||
orders = self.env['lunch.order'].search(order_domain).mapped('user_id')
|
||||
partner_ids = [user.partner_id.id for user in orders]
|
||||
if partner_ids:
|
||||
self.env['mail.thread'].message_notify(body=alert.message, partner_ids=partner_ids)
|
||||
partners = self.env['lunch.order'].search(order_domain).user_id.partner_id
|
||||
if partners:
|
||||
self.env['mail.thread'].message_notify(body=self.message, partner_ids=partners.ids)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import math
|
||||
import pytz
|
||||
|
||||
from datetime import datetime, time
|
||||
from datetime import datetime, time, timedelta
|
||||
from textwrap import dedent
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.osv import expression
|
||||
@@ -14,6 +14,7 @@ from odoo.addons.base.models.res_partner import _tz_get
|
||||
|
||||
|
||||
WEEKDAY_TO_NAME = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
|
||||
CRON_DEPENDS = {'name', 'active', 'send_by', 'automatic_email_time', 'moment', 'tz'}
|
||||
|
||||
def float_to_time(hours, moment='am', tz=None):
|
||||
""" Convert a number of hours into a time object. """
|
||||
@@ -59,6 +60,7 @@ class LunchSupplier(models.Model):
|
||||
('mail', 'Email'),
|
||||
], 'Send Order By', default='phone')
|
||||
automatic_email_time = fields.Float('Order Time', default=12.0, required=True)
|
||||
cron_id = fields.Many2one('ir.cron', ondelete='cascade', required=True, readonly=True)
|
||||
|
||||
recurrency_monday = fields.Boolean('Monday', default=True)
|
||||
recurrency_tuesday = fields.Boolean('Tuesday', default=True)
|
||||
@@ -103,46 +105,106 @@ class LunchSupplier(models.Model):
|
||||
res.append((supplier.id, supplier.name))
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _auto_email_send(self):
|
||||
"""
|
||||
This method is called every 20 minutes via a cron.
|
||||
Its job is simply to get all the orders made for each supplier and send an email
|
||||
automatically to the supplier if the supplier is configured for it and we are ready
|
||||
to send it (usually at 11am or so)
|
||||
"""
|
||||
records = self.search([('send_by', '=', 'mail')])
|
||||
def _sync_cron(self):
|
||||
for supplier in self:
|
||||
supplier = supplier.with_context(tz=supplier.tz)
|
||||
|
||||
for supplier in records:
|
||||
send_at = datetime.combine(fields.Date.today(),
|
||||
float_to_time(supplier.automatic_email_time, supplier.moment, supplier.tz)).astimezone(pytz.UTC).replace(tzinfo=None)
|
||||
if supplier.available_today and fields.Datetime.now() > send_at:
|
||||
lines = self.env['lunch.order'].search([('supplier_id', '=', supplier.id),
|
||||
('state', '=', 'ordered'), ('date', '=', fields.Date.today())])
|
||||
sendat_tz = pytz.timezone(supplier.tz).localize(datetime.combine(
|
||||
fields.Date.context_today(supplier),
|
||||
float_to_time(supplier.automatic_email_time, supplier.moment)))
|
||||
lc = supplier.cron_id.lastcall
|
||||
if ((
|
||||
lc and sendat_tz.date() <= fields.Datetime.context_timestamp(supplier, lc).date()
|
||||
) or (
|
||||
not lc and sendat_tz <= fields.Datetime.context_timestamp(supplier, fields.Datetime.now())
|
||||
)):
|
||||
sendat_tz += timedelta(days=1)
|
||||
sendat_utc = sendat_tz.astimezone(pytz.UTC).replace(tzinfo=None)
|
||||
|
||||
if lines:
|
||||
order = {
|
||||
'company_name': lines[0].company_id.name,
|
||||
'currency_id': lines[0].currency_id.id,
|
||||
'supplier_id': supplier.partner_id.id,
|
||||
'supplier_name': supplier.name,
|
||||
'email_from': supplier.responsible_id.email_formatted,
|
||||
}
|
||||
supplier.cron_id.active = supplier.active and supplier.send_by == 'mail'
|
||||
supplier.cron_id.name = f"Lunch: send automatic email to {supplier.name}"
|
||||
supplier.cron_id.nextcall = sendat_utc
|
||||
supplier.cron_id.code = dedent(f"""\
|
||||
# This cron is dynamically controlled by {self._description}.
|
||||
# Do NOT modify this cron, modify the related record instead.
|
||||
env['{self._name}'].browse([{supplier.id}])._send_auto_email()""")
|
||||
|
||||
_lines = [{
|
||||
'product': line.product_id.name,
|
||||
'note': line.note,
|
||||
'quantity': line.quantity,
|
||||
'price': line.price,
|
||||
'toppings': line.display_toppings,
|
||||
'username': line.user_id.name,
|
||||
} for line in lines]
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
crons = self.env['ir.cron'].sudo().create([
|
||||
{
|
||||
'user_id': self.env.ref('base.user_root').id,
|
||||
'active': False,
|
||||
'interval_type': 'days',
|
||||
'interval_number': 1,
|
||||
'numbercall': -1,
|
||||
'doall': False,
|
||||
'name': "Lunch: send automatic email",
|
||||
'model_id': self.env['ir.model']._get_id(self._name),
|
||||
'state': 'code',
|
||||
'code': "",
|
||||
}
|
||||
for _ in range(len(vals_list))
|
||||
])
|
||||
for vals, cron in zip(vals_list, crons):
|
||||
vals['cron_id'] = cron.id
|
||||
|
||||
order['amount_total'] = sum(line.price for line in lines)
|
||||
suppliers = super().create(vals_list)
|
||||
suppliers._sync_cron()
|
||||
return suppliers
|
||||
|
||||
self.env.ref('lunch.lunch_order_mail_supplier').with_context(order=order, lines=_lines).send_mail(supplier.id)
|
||||
def write(self, values):
|
||||
super().write(values)
|
||||
if not CRON_DEPENDS.isdisjoint(values):
|
||||
self._sync_cron()
|
||||
|
||||
lines.action_confirm()
|
||||
def unlink(self):
|
||||
crons = self.cron_id
|
||||
super().unlink()
|
||||
crons.unlink()
|
||||
|
||||
def _send_auto_email(self):
|
||||
""" Send an email to the supplier with the order of the day """
|
||||
# Called daily by cron
|
||||
self.ensure_one()
|
||||
|
||||
if not self.available_today:
|
||||
return
|
||||
|
||||
if self.send_by != 'mail':
|
||||
raise ValueError("Cannot send an email to this supplier")
|
||||
|
||||
orders = self.env['lunch.order'].search([
|
||||
('supplier_id', '=', self.id),
|
||||
('state', '=', 'ordered'),
|
||||
('date', '=', fields.Date.context_today(self.with_context(tz=self.tz))),
|
||||
])
|
||||
if not orders:
|
||||
return
|
||||
|
||||
order = {
|
||||
'company_name': orders[0].company_id.name,
|
||||
'currency_id': orders[0].currency_id.id,
|
||||
'supplier_id': self.partner_id.id,
|
||||
'supplier_name': self.name,
|
||||
'email_from': self.responsible_id.email_formatted,
|
||||
'amount_total': sum(order.price for order in orders),
|
||||
}
|
||||
|
||||
email_orders = [{
|
||||
'product': order.product_id.name,
|
||||
'note': order.note,
|
||||
'quantity': order.quantity,
|
||||
'price': order.price,
|
||||
'toppings': order.display_toppings,
|
||||
'username': order.user_id.name,
|
||||
} for order in orders]
|
||||
|
||||
self.env.ref('lunch.lunch_order_mail_supplier').with_context(
|
||||
order=order, lines=email_orders
|
||||
).send_mail(self.id)
|
||||
|
||||
orders.action_confirm()
|
||||
|
||||
@api.depends('recurrency_end_date', 'recurrency_monday', 'recurrency_tuesday',
|
||||
'recurrency_wednesday', 'recurrency_thursday', 'recurrency_friday',
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import common
|
||||
from . import test_alert
|
||||
from . import test_product_report
|
||||
from . import test_supplier
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from freezegun import freeze_time
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
fakenow = datetime(2021, 1, 29, 12, 20, 0)
|
||||
|
||||
@freeze_time(fakenow)
|
||||
class TestsCommon(common.TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.fakenow = fakenow
|
||||
|
||||
def setUp(self):
|
||||
super(TestsCommon, self).setUp()
|
||||
@@ -30,6 +38,17 @@ class TestsCommon(common.TransactionCase):
|
||||
],
|
||||
})
|
||||
|
||||
self.partner_kothai = self.env['res.partner'].create({
|
||||
'name': 'Kothai',
|
||||
})
|
||||
|
||||
self.supplier_kothai = self.env['lunch.supplier'].create({
|
||||
'partner_id': self.partner_kothai.id,
|
||||
'send_by': 'mail',
|
||||
'automatic_email_time': 10,
|
||||
'tz': 'America/New_York',
|
||||
})
|
||||
|
||||
self.partner_coin_gourmand = self.env['res.partner'].create({
|
||||
'name': 'Coin Gourmand',
|
||||
})
|
||||
@@ -73,3 +92,21 @@ class TestsCommon(common.TransactionCase):
|
||||
self.env['lunch.cashmove'].create({
|
||||
'amount': 100,
|
||||
})
|
||||
|
||||
self.alert_ny = self.env['lunch.alert'].create({
|
||||
'name': 'New York UTC-5',
|
||||
'mode': 'chat',
|
||||
'notification_time': 10,
|
||||
'notification_moment': 'am',
|
||||
'tz': 'America/New_York',
|
||||
'message': "",
|
||||
}).with_context(tz='America/New_York')
|
||||
|
||||
self.alert_tokyo = self.env['lunch.alert'].create({
|
||||
'name': 'Tokyo UTC+9',
|
||||
'mode': 'chat',
|
||||
'notification_time': 8,
|
||||
'notification_moment': 'am',
|
||||
'tz': 'Asia/Tokyo',
|
||||
'message': "",
|
||||
}).with_context(tz='Asia/Tokyo')
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import datetime, timedelta
|
||||
from odoo import fields
|
||||
from odoo.addons.lunch.tests.common import TestsCommon
|
||||
|
||||
|
||||
class TestAlarm(TestsCommon):
|
||||
def test_cron_sync_create(self):
|
||||
cron_ny = self.alert_ny.cron_id
|
||||
self.assertTrue(cron_ny.active)
|
||||
self.assertEqual(cron_ny.name, "Lunch: alert chat notification (New York UTC-5)")
|
||||
self.assertEqual(
|
||||
[line for line in cron_ny.code.splitlines() if not line.lstrip().startswith("#")],
|
||||
["env['lunch.alert'].browse([%i])._notify_chat()" % self.alert_ny.id])
|
||||
self.assertEqual(cron_ny.nextcall, datetime(2021, 1, 29, 15, 0)) # New-york is UTC-5
|
||||
|
||||
tokyo_cron = self.alert_tokyo.cron_id
|
||||
self.assertEqual(tokyo_cron.nextcall, datetime(2021, 1, 29, 23, 0)) # Tokyo is UTC+9 but the cron is posponed
|
||||
|
||||
def test_cron_sync_active(self):
|
||||
cron_ny = self.alert_ny.cron_id
|
||||
|
||||
self.alert_ny.active = False
|
||||
self.assertFalse(cron_ny.active)
|
||||
self.alert_ny.active = True
|
||||
self.assertTrue(cron_ny.active)
|
||||
|
||||
self.alert_ny.mode = 'alert'
|
||||
self.assertFalse(cron_ny.active)
|
||||
self.alert_ny.mode = 'chat'
|
||||
self.assertTrue(cron_ny.active)
|
||||
|
||||
ctx_today = fields.Date.context_today(self.alert_ny, self.fakenow)
|
||||
self.alert_ny.until = ctx_today - timedelta(days=1)
|
||||
self.assertFalse(cron_ny.active)
|
||||
self.alert_ny.until = ctx_today + timedelta(days=2)
|
||||
self.assertTrue(cron_ny.active)
|
||||
self.alert_ny.until = False
|
||||
self.assertTrue(cron_ny.active)
|
||||
|
||||
def test_cron_sync_nextcall(self):
|
||||
cron_ny = self.alert_ny.cron_id
|
||||
old_nextcall = cron_ny.nextcall
|
||||
|
||||
self.alert_ny.notification_time -= 5
|
||||
self.assertEqual(cron_ny.nextcall, old_nextcall - timedelta(hours=5) + timedelta(days=1))
|
||||
|
||||
# Simulate cron execution
|
||||
cron_ny.lastcall = old_nextcall - timedelta(hours=5)
|
||||
cron_ny.nextcall += timedelta(days=1)
|
||||
|
||||
self.alert_ny.notification_time += 7
|
||||
self.assertEqual(cron_ny.nextcall, old_nextcall + timedelta(days=1, hours=2))
|
||||
|
||||
self.alert_ny.notification_time -= 1
|
||||
self.assertEqual(cron_ny.nextcall, old_nextcall + timedelta(days=1, hours=1))
|
||||
@@ -1,9 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import pytz
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime, time, timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo import fields
|
||||
@@ -25,6 +24,18 @@ class TestSupplier(TestsCommon):
|
||||
self.saturday_1pm = datetime(2018, 11, 3, 13, 0, 0)
|
||||
self.saturday_8pm = datetime(2018, 11, 3, 20, 0, 0)
|
||||
|
||||
def test_send_email_cron(self):
|
||||
self.supplier_kothai.cron_id.ensure_one()
|
||||
self.assertEqual(self.supplier_kothai.cron_id.nextcall.time(), time(15, 0))
|
||||
self.assertEqual(self.supplier_kothai.cron_id.code, f"""\
|
||||
# This cron is dynamically controlled by Lunch Supplier.
|
||||
# Do NOT modify this cron, modify the related record instead.
|
||||
env['lunch.supplier'].browse([{self.supplier_kothai.id}])._send_auto_email()""")
|
||||
|
||||
cron_id = self.supplier_kothai.cron_id.id
|
||||
self.supplier_kothai.unlink()
|
||||
self.assertFalse(self.env['ir.cron'].search([('id', '=', cron_id)]))
|
||||
|
||||
def test_compute_available_today(self):
|
||||
tests = [(self.monday_1am, True), (self.monday_10am, True),
|
||||
(self.monday_1pm, True), (self.monday_8pm, True),
|
||||
@@ -76,7 +87,7 @@ class TestSupplier(TestsCommon):
|
||||
line.action_order()
|
||||
assert line.state == 'ordered'
|
||||
|
||||
self.supplier_pizza_inn._auto_email_send()
|
||||
self.supplier_pizza_inn._send_auto_email()
|
||||
|
||||
assert line.state == 'confirmed'
|
||||
|
||||
@@ -94,7 +105,7 @@ class TestSupplier(TestsCommon):
|
||||
assert line.state == 'ordered'
|
||||
assert line2.state == 'ordered'
|
||||
|
||||
self.supplier_pizza_inn._auto_email_send()
|
||||
self.supplier_pizza_inn._send_auto_email()
|
||||
|
||||
assert line.state == 'confirmed'
|
||||
assert line2.state == 'ordered'
|
||||
@@ -121,4 +132,43 @@ class TestSupplier(TestsCommon):
|
||||
|
||||
assert all(line.state == 'ordered' for line in [line_1, line_2, line_3])
|
||||
|
||||
self.supplier_pizza_inn._auto_email_send()
|
||||
self.supplier_pizza_inn._send_auto_email()
|
||||
|
||||
def test_cron_sync_create(self):
|
||||
cron_ny = self.supplier_kothai.cron_id # I am at New-York
|
||||
self.assertTrue(cron_ny.active)
|
||||
self.assertEqual(cron_ny.name, "Lunch: send automatic email to Kothai")
|
||||
self.assertEqual(
|
||||
[line for line in cron_ny.code.splitlines() if not line.lstrip().startswith("#")],
|
||||
["env['lunch.supplier'].browse([%i])._send_auto_email()" % self.supplier_kothai.id])
|
||||
self.assertEqual(cron_ny.nextcall, datetime(2021, 1, 29, 15, 0)) # New-york is UTC-5
|
||||
|
||||
def test_cron_sync_active(self):
|
||||
cron_ny = self.supplier_kothai.cron_id
|
||||
|
||||
self.supplier_kothai.active = False
|
||||
self.assertFalse(cron_ny.active)
|
||||
self.supplier_kothai.active = True
|
||||
self.assertTrue(cron_ny.active)
|
||||
|
||||
self.supplier_kothai.send_by = 'phone'
|
||||
self.assertFalse(cron_ny.active)
|
||||
self.supplier_kothai.send_by = 'mail'
|
||||
self.assertTrue(cron_ny.active)
|
||||
|
||||
def test_cron_sync_nextcall(self):
|
||||
cron_ny = self.supplier_kothai.cron_id
|
||||
old_nextcall = cron_ny.nextcall
|
||||
|
||||
self.supplier_kothai.automatic_email_time -= 5
|
||||
self.assertEqual(cron_ny.nextcall, old_nextcall - timedelta(hours=5) + timedelta(days=1))
|
||||
|
||||
# Simulate cron execution
|
||||
cron_ny.lastcall = old_nextcall - timedelta(hours=5)
|
||||
cron_ny.nextcall += timedelta(days=1)
|
||||
|
||||
self.supplier_kothai.automatic_email_time += 7
|
||||
self.assertEqual(cron_ny.nextcall, old_nextcall + timedelta(days=1, hours=2))
|
||||
|
||||
self.supplier_kothai.automatic_email_time -= 1
|
||||
self.assertEqual(cron_ny.nextcall, old_nextcall + timedelta(days=1, hours=1))
|
||||
|
||||
Reference in New Issue
Block a user