Skip to content

Commit 269155e

Browse files
committed
[ADD] hr_timesheet_weekly_cutoff: weekly cutoff control for timesheets
1 parent 3b0c53a commit 269155e

18 files changed

Lines changed: 1198 additions & 0 deletions
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
==========================
2+
HR Timesheet Weekly Cutoff
3+
==========================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:475378f57d0d480ff140d302b25968b18666c5dcb6b3dc570ff73447d041098d
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%2Ftimesheet-lightgray.png?logo=github
20+
:target: https://github.com/OCA/timesheet/tree/18.0/hr_timesheet_weekly_cutoff
21+
:alt: OCA/timesheet
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/timesheet-18-0/timesheet-18-0-hr_timesheet_weekly_cutoff
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/timesheet&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module adds a weekly cutoff control for timesheet entries.
32+
33+
Companies can define a weekly cutoff day after which users cannot
34+
register timesheets for previous dates.
35+
36+
Authorized users can temporarily bypass the restriction when required.
37+
38+
**Table of contents**
39+
40+
.. contents::
41+
:local:
42+
43+
Configuration
44+
=============
45+
46+
Go to:
47+
48+
- **Timesheets → Configuration → Settings**
49+
50+
and configure:
51+
52+
- **Weekly Cutoff Day**
53+
54+
Usage
55+
=====
56+
57+
If the cutoff day is configured as Monday:
58+
59+
- On Monday, users can still register timesheets for:
60+
61+
- Monday
62+
- Sunday
63+
- Saturday
64+
- Friday
65+
- Thursday
66+
- Wednesday
67+
- Tuesday
68+
69+
- Starting Tuesday, users can no longer register timesheets dated Monday
70+
or earlier.
71+
72+
Users belonging to the group:
73+
74+
- Timesheet Weekly Cutoff Bypass
75+
76+
can register timesheets outside the weekly cutoff period.
77+
78+
Timesheets registered outside the allowed cutoff period by bypass users
79+
are automatically marked as:
80+
81+
- Outside Weekly Cutoff
82+
83+
This flag can be used later for:
84+
85+
- reporting
86+
- auditing
87+
- filtering
88+
89+
Technical Notes
90+
---------------
91+
92+
The weekly cutoff restriction is enforced during both:
93+
94+
- record creation
95+
- record update
96+
97+
This ensures compatibility with:
98+
99+
- editable tree views
100+
- imports
101+
- RPC calls
102+
- automated processes
103+
104+
The restriction validation is applied per record to ensure correct
105+
behavior during multi-record operations.
106+
107+
Bug Tracker
108+
===========
109+
110+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/timesheet/issues>`_.
111+
In case of trouble, please check there if your issue has already been reported.
112+
If you spotted it first, help us to smash it by providing a detailed and welcomed
113+
`feedback <https://github.com/OCA/timesheet/issues/new?body=module:%20hr_timesheet_weekly_cutoff%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
114+
115+
Do not contact contributors directly about support or help with technical issues.
116+
117+
Credits
118+
=======
119+
120+
Authors
121+
-------
122+
123+
* Spearhead
124+
125+
Contributors
126+
------------
127+
128+
- John Triviño
129+
130+
Maintainers
131+
-----------
132+
133+
This module is maintained by the OCA.
134+
135+
.. image:: https://odoo-community.org/logo.png
136+
:alt: Odoo Community Association
137+
:target: https://odoo-community.org
138+
139+
OCA, or the Odoo Community Association, is a nonprofit organization whose
140+
mission is to support the collaborative development of Odoo features and
141+
promote its widespread use.
142+
143+
.. |maintainer-JT-Odoo| image:: https://github.com/JT-Odoo.png?size=40px
144+
:target: https://github.com/JT-Odoo
145+
:alt: JT-Odoo
146+
147+
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
148+
149+
|maintainer-JT-Odoo|
150+
151+
This module is part of the `OCA/timesheet <https://github.com/OCA/timesheet/tree/18.0/hr_timesheet_weekly_cutoff>`_ project on GitHub.
152+
153+
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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "HR Timesheet Weekly Cutoff",
3+
"version": "18.0.1.0.0",
4+
"category": "Timesheets",
5+
"website": "https://github.com/OCA/timesheet",
6+
"summary": "Weekly cutoff control for timesheets",
7+
"author": "Spearhead, Odoo Community Association (OCA)",
8+
"maintainers": ["JT-Odoo"],
9+
"license": "LGPL-3",
10+
"depends": [
11+
"hr_timesheet",
12+
],
13+
"data": [
14+
"security/groups_security.xml",
15+
"views/res_config_settings_views.xml",
16+
],
17+
"installable": True,
18+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
# * hr_timesheet_weekly_cutoff
4+
#
5+
msgid ""
6+
msgstr ""
7+
"Project-Id-Version: Odoo Server 18.0+e\n"
8+
"Report-Msgid-Bugs-To: \n"
9+
"POT-Creation-Date: 2026-05-10 21:57+0000\n"
10+
"PO-Revision-Date: 2026-05-10 17:02-0500\n"
11+
"Last-Translator: \n"
12+
"Language-Team: \n"
13+
"Language: es_EC\n"
14+
"MIME-Version: 1.0\n"
15+
"Content-Type: text/plain; charset=UTF-8\n"
16+
"Content-Transfer-Encoding: 8bit\n"
17+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
18+
"X-Generator: Poedit 3.9\n"
19+
20+
#. module: hr_timesheet_weekly_cutoff
21+
#. odoo-python
22+
#: code:addons/hr_timesheet_weekly_cutoff/models/account_analytic_line.py:0
23+
msgid "A timesheet date is required."
24+
msgstr "Se requiere una fecha para la hoja de horas trabajadas."
25+
26+
#. module: hr_timesheet_weekly_cutoff
27+
#: model:ir.model,name:hr_timesheet_weekly_cutoff.model_account_analytic_line
28+
msgid "Analytic Line"
29+
msgstr "Línea analítica"
30+
31+
#. module: hr_timesheet_weekly_cutoff
32+
#: model:ir.model,name:hr_timesheet_weekly_cutoff.model_res_company
33+
msgid "Companies"
34+
msgstr "Empresas"
35+
36+
#. module: hr_timesheet_weekly_cutoff
37+
#: model:ir.model,name:hr_timesheet_weekly_cutoff.model_res_config_settings
38+
msgid "Config Settings"
39+
msgstr "Ajustes de configuración"
40+
41+
#. module: hr_timesheet_weekly_cutoff
42+
#: model_terms:ir.ui.view,arch_db:hr_timesheet_weekly_cutoff.res_config_settings_form_day_lock
43+
msgid "Define the weekly cut off day for time tracking."
44+
msgstr "Defina el día límite semanal para el registro del tiempo."
45+
46+
#. module: hr_timesheet_weekly_cutoff
47+
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__4
48+
msgid "Friday"
49+
msgstr "Viernes"
50+
51+
#. module: hr_timesheet_weekly_cutoff
52+
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__0
53+
msgid "Monday"
54+
msgstr "Lunes"
55+
56+
#. module: hr_timesheet_weekly_cutoff
57+
#: model:ir.model.fields,field_description:hr_timesheet_weekly_cutoff.field_account_analytic_line__is_outside_weekly_cutoff
58+
msgid "Outside Weekly Cutoff"
59+
msgstr "Fuera del límite semanal"
60+
61+
#. module: hr_timesheet_weekly_cutoff
62+
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__5
63+
msgid "Saturday"
64+
msgstr "Sábado"
65+
66+
#. module: hr_timesheet_weekly_cutoff
67+
#: model_terms:ir.ui.view,arch_db:hr_timesheet_weekly_cutoff.res_config_settings_form_day_lock
68+
msgid ""
69+
"Select the day used as the weekly closing date\n"
70+
" for time tracking.\n"
71+
" <br/>\n"
72+
" Once the configured day has passed, it will\n"
73+
" not be possible to register hours with dates\n"
74+
" prior to the weekly cutoff."
75+
msgstr ""
76+
"Seleccione el día que se utilizará como fecha de cierre semanal\n"
77+
"para el registro de horas.\n"
78+
"\n"
79+
"<br/>\n"
80+
"Una vez transcurrido el día configurado, no será posible\n"
81+
"registrar horas con fechas anteriores a la fecha límite semanal."
82+
83+
#. module: hr_timesheet_weekly_cutoff
84+
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__6
85+
msgid "Sunday"
86+
msgstr "Domingo"
87+
88+
#. module: hr_timesheet_weekly_cutoff
89+
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__3
90+
msgid "Thursday"
91+
msgstr "Jueves"
92+
93+
#. module: hr_timesheet_weekly_cutoff
94+
#: model:res.groups,name:hr_timesheet_weekly_cutoff.group_bypass_timesheet_lock
95+
msgid "Timesheet Weekly Cutoff Bypass"
96+
msgstr "Omisión del límite semanal de la hoja de horas"
97+
98+
#. module: hr_timesheet_weekly_cutoff
99+
#: model:res.groups,name:hr_timesheet_weekly_cutoff.group_timesheet_lock_manager
100+
msgid "Timesheet Weekly Cutoff Manager"
101+
msgstr "Gerente de cierre semanal de hojas de horas"
102+
103+
#. module: hr_timesheet_weekly_cutoff
104+
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__1
105+
msgid "Tuesday"
106+
msgstr "Martes"
107+
108+
#. module: hr_timesheet_weekly_cutoff
109+
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__2
110+
msgid "Wednesday"
111+
msgstr "Miércoles"
112+
113+
#. module: hr_timesheet_weekly_cutoff
114+
#: model_terms:ir.ui.view,arch_db:hr_timesheet_weekly_cutoff.res_config_settings_form_day_lock
115+
msgid "Weekly Cutoff Control"
116+
msgstr "Control de corte semanal"
117+
118+
#. module: hr_timesheet_weekly_cutoff
119+
#: model:ir.model.fields,field_description:hr_timesheet_weekly_cutoff.field_res_company__timesheet_lock_weekday
120+
#: model:ir.model.fields,field_description:hr_timesheet_weekly_cutoff.field_res_config_settings__timesheet_lock_weekday
121+
msgid "Weekly Cutoff Day"
122+
msgstr "Día límite semanal"
123+
124+
#. module: hr_timesheet_weekly_cutoff
125+
#. odoo-python
126+
#: code:addons/hr_timesheet_weekly_cutoff/models/account_analytic_line.py:0
127+
msgid "You cannot register timesheets dated due to the weekly cutoff control."
128+
msgstr "No puede registrar hojas de horas con fecha límite debido al control de corte semanal."
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import res_company
2+
from . import res_config_settings
3+
from . import account_analytic_line
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from datetime import timedelta
2+
3+
from odoo import api, fields, models
4+
from odoo.exceptions import ValidationError
5+
6+
7+
class AccountAnalyticLine(models.Model):
8+
_inherit = "account.analytic.line"
9+
10+
is_outside_weekly_cutoff = fields.Boolean(
11+
string="Outside Weekly Cutoff",
12+
readonly=True,
13+
copy=False,
14+
index=True,
15+
)
16+
17+
def _get_allowed_from_date(self):
18+
self.ensure_one()
19+
company = self.company_id
20+
today = fields.Date.context_today(self)
21+
lock_weekday = int(company.timesheet_lock_weekday or 0)
22+
current_weekday = today.weekday()
23+
days_since_lock = (current_weekday - lock_weekday) % 7
24+
lock_date = today - timedelta(days=days_since_lock)
25+
if current_weekday == lock_weekday:
26+
allowed_from_date = lock_date - timedelta(days=6)
27+
else:
28+
allowed_from_date = lock_date + timedelta(days=1)
29+
return allowed_from_date
30+
31+
def _is_outside_cutoff(self):
32+
self.ensure_one()
33+
if not self.date:
34+
raise ValidationError(self.env._("A timesheet date is required."))
35+
allowed_from_date = self._get_allowed_from_date()
36+
return self.date < allowed_from_date
37+
38+
def _validate_timesheet_lock(self):
39+
has_bypass_group = self.env.user.has_group(
40+
"hr_timesheet_weekly_cutoff.group_bypass_timesheet_lock"
41+
)
42+
outside_weekly_cutoff = False
43+
is_outside_cutoff = self._is_outside_cutoff()
44+
if has_bypass_group:
45+
if is_outside_cutoff:
46+
outside_weekly_cutoff = True
47+
if is_outside_cutoff and not outside_weekly_cutoff:
48+
raise ValidationError(
49+
self.env._(
50+
"You cannot register timesheets dated "
51+
f"{fields.Date.to_string(self.date)} "
52+
"due to the weekly cutoff control."
53+
)
54+
)
55+
return outside_weekly_cutoff
56+
57+
@api.model_create_multi
58+
def create(self, vals_list):
59+
for vals in vals_list:
60+
record = self.new(vals)
61+
weekly_cutoff = record._validate_timesheet_lock()
62+
vals["is_outside_weekly_cutoff"] = weekly_cutoff
63+
return super().create(vals_list)
64+
65+
def write(self, vals):
66+
result = super().write(vals)
67+
for record in self:
68+
weekly_cutoff = record._validate_timesheet_lock()
69+
if "date" in vals:
70+
record.is_outside_weekly_cutoff = weekly_cutoff
71+
return result
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from odoo import fields, models
2+
3+
4+
class ResCompany(models.Model):
5+
_inherit = "res.company"
6+
7+
timesheet_lock_weekday = fields.Selection(
8+
selection=[
9+
("0", "Monday"),
10+
("1", "Tuesday"),
11+
("2", "Wednesday"),
12+
("3", "Thursday"),
13+
("4", "Friday"),
14+
("5", "Saturday"),
15+
("6", "Sunday"),
16+
],
17+
string="Weekly Cutoff Day",
18+
default="0",
19+
)

0 commit comments

Comments
 (0)