Skip to content
Snippets Groups Projects
Commit 43a6702a authored by OCA Git Bot's avatar OCA Git Bot
Browse files

Merge remote-tracking branch 'odoo/14.0' into 14.0

parents d0dd76e9 10c4091f
No related branches found
No related tags found
No related merge requests found
...@@ -168,7 +168,11 @@ You receive this email because you are: ...@@ -168,7 +168,11 @@ You receive this email because you are:
@api.model @api.model
def run(self, autocommit=False): def run(self, autocommit=False):
schedulers = self.search([('done', '=', False), ('scheduled_date', '<=', datetime.strftime(fields.datetime.now(), tools.DEFAULT_SERVER_DATETIME_FORMAT))]) schedulers = self.search([
('event_id.active', '=', True),
('done', '=', False),
('scheduled_date', '<=', datetime.strftime(fields.datetime.now(), tools.DEFAULT_SERVER_DATETIME_FORMAT))
])
for scheduler in schedulers: for scheduler in schedulers:
try: try:
with self.env.cr.savepoint(): with self.env.cr.savepoint():
......
...@@ -256,3 +256,68 @@ class TestMailSchedule(TestEventCommon, MockEmail): ...@@ -256,3 +256,68 @@ class TestMailSchedule(TestEventCommon, MockEmail):
fields_values={'subject': '%s: today' % test_event.name, fields_values={'subject': '%s: today' % test_event.name,
'email_from': self.user_eventmanager.company_id.email_formatted, 'email_from': self.user_eventmanager.company_id.email_formatted,
}) })
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
def test_archived_event_mail_schedule(self):
""" Test mail scheduling for archived events """
event_cron_id = self.env.ref('event.event_mail_scheduler')
# deactivate other schedulers to avoid messing with crons
self.env['event.mail'].search([]).unlink()
# freeze some datetimes, and ensure more than 1D+1H before event starts
# to ease time-based scheduler check
now = datetime(2023, 7, 24, 14, 30, 15)
event_date_begin = datetime(2023, 7, 26, 8, 0, 0)
event_date_end = datetime(2023, 7, 28, 18, 0, 0)
with freeze_time(now):
test_event = self.env['event.event'].with_user(self.user_eventmanager).create({
'name': 'TestEventMail',
'auto_confirm': True,
'date_begin': event_date_begin,
'date_end': event_date_end,
'event_mail_ids': [
(0, 0, { # right at subscription
'interval_unit': 'now',
'interval_type': 'after_sub',
'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_subscription')}),
(0, 0, { # 3 hours before event
'interval_nbr': 3,
'interval_unit': 'hours',
'interval_type': 'before_event',
'template_id': self.env['ir.model.data'].xmlid_to_res_id('event.event_reminder')})
]
})
# check event scheduler
scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id)])
self.assertEqual(len(scheduler), 2, 'event: wrong scheduler creation')
event_prev_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')])
with freeze_time(now), self.mock_mail_gateway():
self.env['event.registration'].with_user(self.user_eventuser).create({
'event_id': test_event.id,
'name': 'Reg1',
'email': 'reg1@example.com',
})
self.env['event.registration'].with_user(self.user_eventuser).create({
'event_id': test_event.id,
'name': 'Reg2',
'email': 'reg2@example.com',
})
# check emails effectively sent
self.assertEqual(len(self._new_mails), 2, 'event: should have 2 scheduled emails (1 / registration)')
# Archive the Event
test_event.action_archive()
# execute cron to run schedulers
now_start = event_date_begin + relativedelta(hours=-3)
with freeze_time(now_start), self.mock_mail_gateway():
event_cron_id.method_direct_trigger()
# check that scheduler is not executed
self.assertFalse(event_prev_scheduler.mail_sent, 'event: reminder scheduler should not run')
self.assertFalse(event_prev_scheduler.done, 'event: reminder scheduler should not run')
...@@ -117,29 +117,46 @@ GROUP BY channel_moderator.res_users_id""", [tuple(self.ids)]) ...@@ -117,29 +117,46 @@ GROUP BY channel_moderator.res_users_id""", [tuple(self.ids)])
@api.model @api.model
def systray_get_activities(self): def systray_get_activities(self):
activities = self.env["mail.activity"].search([("user_id", "=", self.env.uid)]) query = """SELECT array_agg(res_id) as res_ids, m.id, count(*),
activities_by_record_by_model_name = defaultdict(lambda: defaultdict(lambda: self.env["mail.activity"])) CASE
for activity in activities: WHEN %(today)s::date - act.date_deadline::date = 0 Then 'today'
record = self.env[activity.res_model].browse(activity.res_id) WHEN %(today)s::date - act.date_deadline::date > 0 Then 'overdue'
activities_by_record_by_model_name[activity.res_model][record] += activity WHEN %(today)s::date - act.date_deadline::date < 0 Then 'planned'
model_ids = list({self.env["ir.model"]._get(name).id for name in activities_by_record_by_model_name.keys()}) END AS states
FROM mail_activity AS act
JOIN ir_model AS m ON act.res_model_id = m.id
WHERE user_id = %(user_id)s
GROUP BY m.id, states;
"""
self.env.cr.execute(query, {
'today': fields.Date.context_today(self),
'user_id': self.env.uid,
})
activity_data = self.env.cr.dictfetchall()
records_by_state_by_model = defaultdict(lambda: {"today": set(), "overdue": set(), "planned": set(), "all": set()})
for data in activity_data:
records_by_state_by_model[data["id"]][data["states"]] = set(data["res_ids"])
records_by_state_by_model[data["id"]]["all"] = records_by_state_by_model[data["id"]]["all"] | set(data["res_ids"])
user_activities = {} user_activities = {}
for model_name, activities_by_record in activities_by_record_by_model_name.items(): for model_id in records_by_state_by_model:
domain = [("id", "in", list({r.id for r in activities_by_record.keys()}))] model_dic = records_by_state_by_model[model_id]
allowed_records = self.env[model_name].search(domain) model = self.env["ir.model"].browse(model_id).with_prefetch(tuple(records_by_state_by_model.keys()))
allowed_records = self.env[model.model].search([("id", "in", tuple(model_dic["all"]))])
if not allowed_records: if not allowed_records:
continue continue
module = self.env[model_name]._original_module module = self.env[model.model]._original_module
icon = module and modules.module.get_module_icon(module) icon = module and modules.module.get_module_icon(module)
user_activities[model_name] = { today = len(model_dic["today"] & set(allowed_records.ids))
"name": self.env["ir.model"]._get(model_name).with_prefetch(model_ids).name, overdue = len(model_dic["overdue"] & set(allowed_records.ids))
"model": model_name, user_activities[model.model] = {
"name": model.name,
"model": model.model,
"type": "activity", "type": "activity",
"icon": icon, "icon": icon,
"total_count": 0, "total_count": today + overdue,
"today_count": 0, "today_count": today,
"overdue_count": 0, "overdue_count": overdue,
"planned_count": 0, "planned_count": len(model_dic["planned"] & set(allowed_records.ids)),
"actions": [ "actions": [
{ {
"icon": "fa-clock-o", "icon": "fa-clock-o",
...@@ -147,13 +164,6 @@ GROUP BY channel_moderator.res_users_id""", [tuple(self.ids)]) ...@@ -147,13 +164,6 @@ GROUP BY channel_moderator.res_users_id""", [tuple(self.ids)])
} }
], ],
} }
for record, activities in activities_by_record.items():
if record not in allowed_records:
continue
for activity in activities:
user_activities[model_name]["%s_count" % activity.state] += 1
if activity.state in ("today", "overdue"):
user_activities[model_name]["total_count"] += 1
return list(user_activities.values()) return list(user_activities.values())
......
...@@ -95,7 +95,7 @@ class StockProductionLot(models.Model): ...@@ -95,7 +95,7 @@ class StockProductionLot(models.Model):
for lot in alert_lots: for lot in alert_lots:
lot.activity_schedule( lot.activity_schedule(
'product_expiry.mail_activity_type_alert_date_reached', 'product_expiry.mail_activity_type_alert_date_reached',
user_id=lot.product_id.responsible_id.id or SUPERUSER_ID, user_id=lot.product_id.with_company(lot.company_id).responsible_id.id or lot.product_id.responsible_id.id or SUPERUSER_ID,
note=_("The alert date has been reached for this lot/serial number") note=_("The alert date has been reached for this lot/serial number")
) )
alert_lots.write({ alert_lots.write({
......
...@@ -21,7 +21,7 @@ class WebsiteVisitor(models.Model): ...@@ -21,7 +21,7 @@ class WebsiteVisitor(models.Model):
self.flush() self.flush()
left_visitors = self.filtered(lambda visitor: not visitor.email or not visitor.mobile) left_visitors = self.filtered(lambda visitor: not visitor.email or not visitor.mobile)
leads = left_visitors.mapped('lead_ids').sorted('create_date', reverse=True) leads = left_visitors.sudo().mapped('lead_ids').sorted('create_date', reverse=True)
visitor_to_lead_ids = dict((visitor.id, visitor.lead_ids.ids) for visitor in left_visitors) visitor_to_lead_ids = dict((visitor.id, visitor.lead_ids.ids) for visitor in left_visitors)
for visitor in left_visitors: for visitor in left_visitors:
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//button[@id='w_visitor_visit_counter']" position="before"> <xpath expr="//button[@id='w_visitor_visit_counter']" position="before">
<button name="%(website_crm.website_visitor_crm_lead_action)d" type="action" class="oe_stat_button" icon="fa-star" <button name="%(website_crm.website_visitor_crm_lead_action)d" type="action" class="oe_stat_button" icon="fa-star"
attrs="{'invisible': [('lead_count', '=', 0)]}"> groups="sales_team.group_sale_salesman" attrs="{'invisible': [('lead_count', '=', 0)]}">
<field name="lead_count" widget="statinfo" string="Leads"/> <field name="lead_count" widget="statinfo" string="Leads"/>
</button> </button>
</xpath> </xpath>
......
...@@ -18,13 +18,7 @@ def get_test_modules(module): ...@@ -18,13 +18,7 @@ def get_test_modules(module):
feed unittest.TestLoader.loadTestsFromModule() """ feed unittest.TestLoader.loadTestsFromModule() """
# Try to import the module # Try to import the module
results = _get_tests_modules('odoo.addons', module) results = _get_tests_modules('odoo.addons', module)
results += list(_get_upgrade_test_modules(module))
try:
importlib.import_module('odoo.upgrade.%s' % module)
except ImportError:
pass
else:
results += list(_get_upgrade_test_modules(module))
return results return results
...@@ -53,16 +47,26 @@ def _get_tests_modules(path, module): ...@@ -53,16 +47,26 @@ def _get_tests_modules(path, module):
return result return result
def _get_upgrade_test_modules(module): def _get_upgrade_test_modules(module):
upg = importlib.import_module("odoo.upgrade") upgrade_modules = (
for path in map(Path, upg.__path__): f"odoo.upgrade.{module}",
for test in (path / module / "tests").glob("test_*.py"): f"odoo.addons.{module}.migrations",
spec = importlib.util.spec_from_file_location(f"odoo.upgrade.{module}.tests.{test.stem}", test) f"odoo.addons.{module}.upgrades",
if not spec: )
continue for module_name in upgrade_modules:
pymod = importlib.util.module_from_spec(spec) try:
sys.modules[spec.name] = pymod upg = importlib.import_module(module_name)
spec.loader.exec_module(pymod) except ImportError:
yield pymod continue
for path in map(Path, upg.__path__):
for test in path.glob("tests/test_*.py"):
spec = importlib.util.spec_from_file_location(f"{upg.__name__}.tests.{test.stem}", test)
if not spec:
continue
pymod = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = pymod
spec.loader.exec_module(pymod)
yield pymod
def make_suite(module_names, position='at_install'): def make_suite(module_names, position='at_install'):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment