Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions base_user_role_activity/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
=====================
User roles activities
=====================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:b3aa00f609d45dcd82a69b2a3e13b327096ab1d25883b543e3e45538e854d40c
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github
:target: https://github.com/OCA/server-backend/tree/18.0/base_user_role_activity
:alt: OCA/server-backend
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-backend-18-0/server-backend-18-0-base_user_role_activity
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-backend&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

System Parameter
================

The activity is created ``base_user_role_activity.reminder_days = 30``
(days) before the end date by a scheduled action "User role expire
reminder" and automatically removed, when asignment end date on the user
role assignment is adapted.

.. image:: https://raw.githubusercontent.com/OCA/server-backend/18.0/base_user_role_activity/static/description/role_assignment.drawio.png
:alt: role assignemnt

Activity
========

When a user role is about to expire (default=30 days), an activity is
created for the manager of a user (employee). When the user has no
Manager, the activity is assigend to user. The activity is linked to the
users res.partner entity.

.. image:: https://raw.githubusercontent.com/OCA/server-backend/18.0/base_user_role_activity/static/description/activity.drawio.png
:alt: activity

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-backend/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`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**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* glueckkanja AG

Contributors
------------

- Christopher Rogos <crogos@gmail.com> (https://www.glueckkanja.com)

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-CRogos| image:: https://github.com/CRogos.png?size=40px
:target: https://github.com/CRogos
:alt: CRogos

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-CRogos|

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.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions base_user_role_activity/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
21 changes: 21 additions & 0 deletions base_user_role_activity/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2025 gluekkanja AG <https://wwww.glueckkanja.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).


{
"name": "User roles activities",
"summary": "Create activities to remind about user role expirations",
"version": "18.0.1.0.0",
"category": "Tools",
"author": "glueckkanja AG, Odoo Community Association (OCA)",
"license": "LGPL-3",
"maintainers": ["CRogos"],
"website": "https://github.com/OCA/server-backend",
"depends": ["base_user_role", "hr"],
"data": [
"data/ir_cron.xml",
"data/mail_activity_type.xml",
"data/config_parameter.xml",
],
"installable": True,
}
11 changes: 11 additions & 0 deletions base_user_role_activity/data/config_parameter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record
forcecreate="True"
id="config_online_sync_request_timeout"
model="ir.config_parameter"
>
<field name="key">base_user_role_activity.reminder_days</field>
<field name="value">30</field>
</record>
</odoo>
12 changes: 12 additions & 0 deletions base_user_role_activity/data/ir_cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record model="ir.cron" id="cron_role_reminder">
<field name='name'>User role expire reminder</field>
<field name='interval_number'>1</field>
<field name='interval_type'>days</field>
<field name="active">True</field>
<field name="model_id" ref="base_user_role.model_res_users_role_line" />
<field name="state">code</field>
<field name="code">model.cron_role_reminder()</field>
</record>
</odoo>
17 changes: 17 additions & 0 deletions base_user_role_activity/data/mail_activity_type.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="mail_activity_role_expire" model="mail.activity.type">
<field name="name">User Role Expires</field>
<field name="icon">fa-clock-o</field>
<field name="keep_done">True</field>
<field name="res_model">res.partner</field>
<field name="default_note" type="html">
<div>
A user role assignment for %%(user)s is about to expire.
<br />- %%(roles)s
<br /><br />
Please review and take the necessary action.
</div>
</field>
</record>
</odoo>
2 changes: 2 additions & 0 deletions base_user_role_activity/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import role_line
from . import user
56 changes: 56 additions & 0 deletions base_user_role_activity/models/role_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import datetime
import logging

from odoo import api, fields, models

_logger = logging.getLogger(__name__)


class ResUsersRoleLine(models.Model):
_inherit = "res.users.role.line"

def write(self, vals):
res = super().write(vals)

if "date_to" in vals:
self.mapped("user_id").activity_update_role_reminder()
return

return res

def unlink(self):
users = self.mapped("user_id")
res = super().unlink()
users.activity_update_role_reminder()
return res

@api.model
def cron_role_reminder(self):
_logger.info("Trigger role expiration reminders")
users = self.search(self._get_reminder_days_domain()).mapped("user_id")
users = users.filtered(
lambda user: not user.partner_id.activity_search(
["base_user_role_activity.mail_activity_role_expire"]
)
)
users.activity_update_role_reminder()

@api.model
def _get_reminder_days_domain(self):
reminder_days = int(
self.env["ir.config_parameter"]
.sudo()
.get_param("base_user_role_activity.reminder_days", 30)
)
domain = [
("is_enabled", "=", True),
(
"date_to",
"<=",
fields.Date.today() + datetime.timedelta(days=reminder_days),
),
"|",
("date_from", "=", False),
("date_from", "<", fields.Date.today()), # ignore short term roles
]
return domain
74 changes: 74 additions & 0 deletions base_user_role_activity/models/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from markupsafe import Markup

from odoo import models


class ResUsers(models.Model):
_inherit = "res.users"

def activity_update_role_reminder(self):
activity_type_xmlid = "base_user_role_activity.mail_activity_role_expire"
for user in self:
expiring_lines = user.role_line_ids.filtered_domain(
self.env["res.users.role.line"]._get_reminder_days_domain()
)
existing_active_activities = user.partner_id.activity_search(
[activity_type_xmlid]
)
if not expiring_lines:
if existing_active_activities:
# cleanup outdated activities
existing_active_activities.unlink()
continue

min_deadline = min(expiring_lines.mapped("date_to"))
if existing_active_activities:
existing_active_activity = existing_active_activities[0]
if existing_active_activity.date_deadline == min_deadline:
continue # No update needed
# something changed, remove existing activity and recreate
existing_active_activities.unlink()

existing_activities = user.partner_id.with_context(
active_test=False
).activity_search(
[activity_type_xmlid],
additional_domain=[("date_deadline", ">=", min_deadline)],
)
if existing_activities:
continue # An activity with the correct deadline already processed

activity_type_id = self.env["ir.model.data"]._xmlid_to_res_id(
activity_type_xmlid, raise_if_not_found=False
)
activity_type = self.env["mail.activity.type"].browse(activity_type_id)

# receive manager independet of selected company
employees = (
self.env["hr.employee"]
.sudo()
.search([("user_id", "=", user.id), ("parent_id", "!=", False)])
)
manager = employees.filtered(
lambda e: e.company_id == self.env.company
).parent_id
if not manager and employees:
manager = employees[0].parent_id

user.partner_id.activity_schedule(
activity_type_id=activity_type_id,
date_deadline=min(expiring_lines.mapped("date_to")),
note=activity_type.default_note
% {
"user": user.self._get_html_link(),
"roles": Markup(
"<br/> -".join(
[
f"{line.role_id.name} ({line.date_to})"
for line in expiring_lines
]
)
),
},
user_id=manager.user_id.id or user.id,
)
3 changes: 3 additions & 0 deletions base_user_role_activity/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
3 changes: 3 additions & 0 deletions base_user_role_activity/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Christopher Rogos \<<crogos@gmail.com>\>
(<https://www.glueckkanja.com>)

14 changes: 14 additions & 0 deletions base_user_role_activity/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# System Parameter


The activity is created `base_user_role_activity.reminder_days = 30` (days) before the end date by a scheduled action "User role expire reminder" and automatically removed, when asignment end date on the user role assignment is adapted.

![role assignemnt](../static/description/role_assignment.drawio.png)


# Activity
When a user role is about to expire (default=30 days), an activity is created for the manager of a user (employee). When the user has no Manager, the activity is assigend to user. The activity is linked to the users res.partner entity.

![activity](../static/description/activity.drawio.png)


Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading