Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TA#63122 [MIG] Module mail_activity_not_deleted #19

Merged
merged 9 commits into from
Mar 6, 2024
23 changes: 12 additions & 11 deletions .docker_files/main/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

{
'name': 'Main Module',
'version': '1.0.0',
'author': 'Numigi',
'maintainer': 'Numigi',
'website': 'https://www.numigi.com',
'license': 'LGPL-3',
'category': 'Other',
'summary': 'Install all addons required for testing.',
'depends': [
'fetchmail_outlook_by_company',
"name": "Main Module",
"version": "1.0.0",
"author": "Numigi",
"maintainer": "Numigi",
"website": "https://www.numigi.com",
"license": "LGPL-3",
"category": "Other",
"summary": "Install all addons required for testing.",
"depends": [
"fetchmail_outlook_by_company",
"mail_activity_not_deleted",
],
'installable': True,
"installable": True,
}
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ RUN mkdir -p "${THIRD_PARTY_ADDONS}" && chown -R odoo "${THIRD_PARTY_ADDONS}"
USER odoo

COPY fetchmail_outlook_by_company /mnt/extra-addons/fetchmail_outlook_by_company
COPY mail_activity_not_deleted /mnt/extra-addons/mail_activity_not_deleted

COPY .docker_files/main /mnt/extra-addons/main
COPY .docker_files/odoo.conf /etc/odoo
18 changes: 18 additions & 0 deletions mail_activity_not_deleted/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Mail Activity Not Deleted
=========================
Since version 11.0, Odoo adds the concept of activities.
One issue with this feature is that when an activity is completed, the activity record is deleted from the database.

This module deactivates terminated activities instead of deleting them from the mail_activity table.

New State
---------
The state Done is added to activities. Any completed activity is automatically set to Done.

New Field
---------
The field Date Done is added to activities. When completing the activity, this field is filled with the current time.

Contributors
------------
* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
4 changes: 4 additions & 0 deletions mail_activity_not_deleted/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# © 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from . import models
14 changes: 14 additions & 0 deletions mail_activity_not_deleted/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# © 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

{

Check warning on line 4 in mail_activity_not_deleted/__manifest__.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

mail_activity_not_deleted/__manifest__.py#L4

Statement seems to have no effect
'name': 'Mail Activity Not Deleted',
'version': '14.0.1.0.1',
'author': 'Numigi',
'maintainer': 'Numigi',
'license': 'LGPL-3',
'category': 'Other',
'summary': 'Deactivate terminated activities instead of deleting.',
'depends': ['mail'],
'installable': True,
}
33 changes: 33 additions & 0 deletions mail_activity_not_deleted/i18n/fr.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mail_activity_not_deleted
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-29 15:13-0400\n"
"PO-Revision-Date: 2018-05-29 19:13+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"X-Generator: Poedit 1.8.7.1\n"

#. module: mail_activity_not_deleted
#: model:ir.model.fields,field_description:mail_activity_not_deleted.field_mail_activity_active
msgid "Active"
msgstr "Actif"

#. module: mail_activity_not_deleted
#: model:ir.model,name:mail_activity_not_deleted.model_mail_activity
msgid "Activity"
msgstr "Activité"

#. module: mail_activity_not_deleted
#: model:ir.model.fields,field_description:mail_activity_not_deleted.field_mail_activity_date_done
msgid "Date Done"
msgstr "Terminé le"
4 changes: 4 additions & 0 deletions mail_activity_not_deleted/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# © 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from . import mail_activity, user, mail_activity_mixin

Check notice on line 4 in mail_activity_not_deleted/models/__init__.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

mail_activity_not_deleted/models/__init__.py#L4

'.mail_activity' imported but unused (F401)
39 changes: 39 additions & 0 deletions mail_activity_not_deleted/models/mail_activity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# © 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from datetime import datetime

from odoo import api, fields, models


class MailActivityInactivatedInsteadOfDeleted(models.Model):

_inherit = "mail.activity"

active = fields.Boolean(default=True)
date_done = fields.Datetime()

state = fields.Selection(selection_add=[("done", "Done")])

@api.depends("date_deadline")
def _compute_state(self):
super()._compute_state()

done_activities = self.filtered(lambda a: a.date_done)
for activity in done_activities:
activity.state = "done"

def unlink(self):
self.write(
{"active": False, "date_done": datetime.now()}
)
for activity in self:
activity._update_record_date_deadline()

return True

def _update_record_date_deadline(self):
"""Update the stored fields that depend on activity_ids on the related record."""
record = self.env[self.res_model].browse(self.res_id)
record.modified(["activity_ids"])
record.recompute()
12 changes: 12 additions & 0 deletions mail_activity_not_deleted/models/mail_activity_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# © 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from odoo import fields, models


class MailActivityMixin(models.AbstractModel):

_inherit = 'mail.activity.mixin'

# auto_join prevents the active filter from being automatically applied.
activity_ids = fields.One2many(auto_join=False)
83 changes: 83 additions & 0 deletions mail_activity_not_deleted/models/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# © 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from collections import defaultdict

from odoo import api, fields, models, modules


class User(models.Model):

_inherit = 'res.users'

@api.model
def systray_get_activities(self):
"""Prevent inactive activities from appearing in the main Odoo navbar.

This method is a copy from the method defined at:
odoo/addons/mail/models/res_users.py

Only the active filter was added in the sql query.
"""
query = """SELECT array_agg(res_id) as res_ids, m.id, count(*),
CASE
WHEN %(today)s::date - act.date_deadline::date = 0 Then 'today'
WHEN %(today)s::date - act.date_deadline::date > 0 Then 'overdue'
WHEN %(today)s::date - act.date_deadline::date < 0 Then 'planned'
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
AND act.active
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 = {}
for model_id in records_by_state_by_model:
model_dic = records_by_state_by_model[model_id]
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:
continue
module = self.env[model.model]._original_module
icon = module and modules.module.get_module_icon(module)
today = len(model_dic["today"] & set(allowed_records.ids))
overdue = len(model_dic["overdue"] & set(allowed_records.ids))
user_activities[model.model] = {
"name": model.name,
"model": model.model,
"type": "activity",
"icon": icon,
"total_count": today + overdue,
"today_count": today,
"overdue_count": overdue,
"planned_count": len(model_dic["planned"] & set(allowed_records.ids)),
"actions": [
{
"icon": "fa-clock-o",
"name": "Summary",
}
],
}
return list(user_activities.values())
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions mail_activity_not_deleted/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

from . import test_mail_activity
70 changes: 70 additions & 0 deletions mail_activity_not_deleted/tests/test_mail_activity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# © 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from datetime import datetime
from odoo.tests import common


class TestMailActivity(common.SavepointCase):

@classmethod
def setUpClass(cls):
super().setUpClass()
cls.partner = cls.env['res.partner'].create({'name': 'Test'})
cls.partner.write({
'activity_ids': [(0, 0, {
'res_id': cls.partner.id,
'res_model_id': cls.env.ref('base.model_res_partner').id,
'date_deadline': datetime.now().date(),
'user_id': cls.env.user.id,
})]
})
cls.activity = cls.partner.activity_ids

def test_when_activity_is_completed_then_it_is_inactive_instead_of_deleted(self):
self.assertTrue(self.activity.active)

self.activity.action_done()
self.assertTrue(self.activity.exists())
self.assertFalse(self.activity.active)

def test_when_activity_is_rescheduled_then_it_is_inactive_instead_of_deleted(self):
self.assertTrue(self.activity.active)

self.activity.action_done_schedule_next()
self.assertTrue(self.activity.exists())
self.assertFalse(self.activity.active)

def test_when_record_is_deactivated_then_the_activity_is_inactive_instead_of_deleted(self):
self.assertTrue(self.activity.active)

self.partner.active = False
self.activity.refresh()
self.assertTrue(self.activity.exists())
self.assertFalse(self.activity.active)

def test_the_date_done_is_computed_when_the_activity_is_completed(self):
self.assertFalse(self.activity.date_done)

time_before = datetime.now()
self.activity.action_done()
time_after = datetime.now()

self.assertLessEqual(time_before, self.activity.date_done)
self.assertLessEqual(self.activity.date_done, time_after)

def test_the_state_is_done_after_the_activity_is_completed(self):
self.assertNotEqual(self.activity.state, 'done')
self.activity.action_done()
self.activity.refresh()
self.assertEqual(self.activity.state, 'done')

def test_when_the_activity_is_archived_then_it_is_not_due_today(self):
self.assertEqual(self.partner.activity_state, 'today')
self.activity.action_done()
self.assertFalse(self.partner.activity_state)

def test_when_the_activity_is_archived_then_partner_has_no_activity_deadline(self):
self.assertTrue(self.partner.activity_date_deadline)
self.activity.action_done()
self.assertFalse(self.partner.activity_date_deadline)
Loading