Skip to content

Commit a82d616

Browse files
committed
[ADD] base_user_role_activity: create a reminder activity before rolse assignment expires
1 parent 82828fb commit a82d616

File tree

16 files changed

+861
-0
lines changed

16 files changed

+861
-0
lines changed

base_user_role_activity/README.rst

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
=====================
2+
User roles activities
3+
=====================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:b3aa00f609d45dcd82a69b2a3e13b327096ab1d25883b543e3e45538e854d40c
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
18+
:alt: License: LGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github
20+
:target: https://github.com/OCA/server-backend/tree/18.0/base_user_role_activity
21+
:alt: OCA/server-backend
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/server-backend-18-0/server-backend-18-0-base_user_role_activity
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-backend&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
Create an activity for the manager of a user when a role is about to
32+
expire.
33+
34+
**Table of contents**
35+
36+
.. contents::
37+
:local:
38+
39+
Bug Tracker
40+
===========
41+
42+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-backend/issues>`_.
43+
In case of trouble, please check there if your issue has already been reported.
44+
If you spotted it first, help us to smash it by providing a detailed and welcomed
45+
`feedback <https://github.com/OCA/server-backend/issues/new?body=module:%20base_user_role_activity%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
46+
47+
Do not contact contributors directly about support or help with technical issues.
48+
49+
Credits
50+
=======
51+
52+
Authors
53+
-------
54+
55+
* glueckkanja AG
56+
57+
Contributors
58+
------------
59+
60+
- Christopher Rogos <crogos@gmail.com> (https://www.glueckkanja.com)
61+
62+
Maintainers
63+
-----------
64+
65+
This module is maintained by the OCA.
66+
67+
.. image:: https://odoo-community.org/logo.png
68+
:alt: Odoo Community Association
69+
:target: https://odoo-community.org
70+
71+
OCA, or the Odoo Community Association, is a nonprofit organization whose
72+
mission is to support the collaborative development of Odoo features and
73+
promote its widespread use.
74+
75+
.. |maintainer-CRogos| image:: https://github.com/CRogos.png?size=40px
76+
:target: https://github.com/CRogos
77+
:alt: CRogos
78+
79+
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
80+
81+
|maintainer-CRogos|
82+
83+
This module is part of the `OCA/server-backend <https://github.com/OCA/server-backend/tree/18.0/base_user_role_activity>`_ project on GitHub.
84+
85+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2025 gluekkanja AG <https://wwww.glueckkanja.com>
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
4+
5+
{
6+
"name": "User roles activities",
7+
"version": "18.0.1.0.0",
8+
"category": "Tools",
9+
"author": "glueckkanja AG, Odoo Community Association (OCA)",
10+
"license": "LGPL-3",
11+
"maintainers": ["CRogos"],
12+
"website": "https://github.com/OCA/server-backend",
13+
"depends": ["base_user_role", "hr"],
14+
"data": [
15+
"data/ir_cron.xml",
16+
"data/mail_activity_type.xml",
17+
"data/config_parameter.xml",
18+
],
19+
"installable": True,
20+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo noupdate="1">
3+
<record
4+
forcecreate="True"
5+
id="config_online_sync_request_timeout"
6+
model="ir.config_parameter"
7+
>
8+
<field name="key">base_user_role_activity.reminder_days</field>
9+
<field name="value">30</field>
10+
</record>
11+
</odoo>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo noupdate="1">
3+
<record model="ir.cron" id="cron_role_reminder">
4+
<field name='name'>User role expire reminder</field>
5+
<field name='interval_number'>1</field>
6+
<field name='interval_type'>days</field>
7+
<field name="active">True</field>
8+
<field name="model_id" ref="base_user_role.model_res_users_role_line" />
9+
<field name="state">code</field>
10+
<field name="code">model.cron_role_reminder()</field>
11+
</record>
12+
</odoo>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo noupdate="1">
3+
<record id="mail_activity_role_expire" model="mail.activity.type">
4+
<field name="name">User Role Expires</field>
5+
<field name="icon">fa-clock-o</field>
6+
<field name="keep_done">True</field>
7+
<field name="res_model">res.partner</field>
8+
<field name="default_note" type="html">
9+
<div>
10+
A user role assignment for %%(user)s is about to expire.
11+
<br />- %%(roles)s
12+
<br /><br />
13+
Please review and take the necessary action.
14+
</div>
15+
</field>
16+
</record>
17+
</odoo>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import role_line
2+
from . import user
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import datetime
2+
import logging
3+
4+
from odoo import api, fields, models
5+
6+
_logger = logging.getLogger(__name__)
7+
8+
9+
class ResUsersRoleLine(models.Model):
10+
_inherit = "res.users.role.line"
11+
12+
def write(self, vals):
13+
res = super().write(vals)
14+
15+
if "date_to" in vals:
16+
self.mapped("user_id").activity_update_role_reminder()
17+
return
18+
19+
return res
20+
21+
def unlink(self):
22+
users = self.mapped("user_id")
23+
res = super().unlink()
24+
users.activity_update_role_reminder()
25+
return res
26+
27+
@api.model
28+
def cron_role_reminder(self):
29+
_logger.info("Trigger role expiration reminders")
30+
users = self.search(self._get_reminder_days_domain()).mapped("user_id")
31+
users = users.filtered(
32+
lambda user: not user.partner_id.activity_search(
33+
["base_user_role_activity.mail_activity_role_expire"]
34+
)
35+
)
36+
users.activity_update_role_reminder()
37+
38+
@api.model
39+
def _get_reminder_days_domain(self):
40+
reminder_days = int(
41+
self.env["ir.config_parameter"]
42+
.sudo()
43+
.get_param("base_user_role_activity.reminder_days", 30)
44+
)
45+
domain = [
46+
("is_enabled", "=", True),
47+
(
48+
"date_to",
49+
"<=",
50+
fields.Date.today() + datetime.timedelta(days=reminder_days),
51+
),
52+
"|",
53+
("date_from", "=", False),
54+
("date_from", "<", fields.Date.today()), # ignore short term roles
55+
]
56+
return domain
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from markupsafe import Markup
2+
3+
from odoo import models
4+
5+
6+
class ResUsers(models.Model):
7+
_inherit = "res.users"
8+
9+
def activity_update_role_reminder(self):
10+
activity_type_xmlid = "base_user_role_activity.mail_activity_role_expire"
11+
for user in self:
12+
expiring_lines = user.role_line_ids.filtered_domain(
13+
self.env["res.users.role.line"]._get_reminder_days_domain()
14+
)
15+
existing_active_activities = user.partner_id.activity_search(
16+
[activity_type_xmlid]
17+
)
18+
if not expiring_lines:
19+
if existing_active_activities:
20+
# cleanup outdated activities
21+
existing_active_activities.unlink()
22+
continue
23+
24+
min_deadline = min(expiring_lines.mapped("date_to"))
25+
if existing_active_activities:
26+
existing_active_activity = existing_active_activities[0]
27+
if existing_active_activity.date_deadline == min_deadline:
28+
continue # No update needed
29+
# something changed, remove existing activity and recreate
30+
existing_active_activities.unlink()
31+
32+
existing_activities = user.partner_id.with_context(
33+
active_test=False
34+
).activity_search(
35+
[activity_type_xmlid],
36+
additional_domain=[("date_deadline", ">=", min_deadline)],
37+
)
38+
if existing_activities:
39+
continue # An activity with the correct deadline already processed
40+
41+
activity_type_id = self.env["ir.model.data"]._xmlid_to_res_id(
42+
activity_type_xmlid, raise_if_not_found=False
43+
)
44+
activity_type = self.env["mail.activity.type"].browse(activity_type_id)
45+
user.partner_id.activity_schedule(
46+
activity_type_id=activity_type_id,
47+
date_deadline=min(expiring_lines.mapped("date_to")),
48+
note=activity_type.default_note
49+
% {
50+
"user": user.self._get_html_link(),
51+
"roles": Markup(
52+
"<br/> -".join(
53+
[
54+
f"{line.role_id.name} ({line.date_to})"
55+
for line in expiring_lines
56+
]
57+
)
58+
),
59+
},
60+
user_id=user.employee_parent_id.user_id.id or user.id,
61+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"

0 commit comments

Comments
 (0)