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
153 changes: 153 additions & 0 deletions hr_timesheet_weekly_cutoff/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
==========================
HR Timesheet Weekly Cutoff
==========================

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

.. |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%2Ftimesheet-lightgray.png?logo=github
:target: https://github.com/OCA/timesheet/tree/18.0/hr_timesheet_weekly_cutoff
:alt: OCA/timesheet
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/timesheet-18-0/timesheet-18-0-hr_timesheet_weekly_cutoff
: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/timesheet&target_branch=18.0
:alt: Try me on Runboat

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

This module adds a weekly cutoff control for timesheet entries.

Companies can define a weekly cutoff day after which users cannot
register timesheets for previous dates.

Authorized users can temporarily bypass the restriction when required.

**Table of contents**

.. contents::
:local:

Configuration
=============

Go to:

- **Timesheets → Configuration → Settings**

and configure:

- **Weekly Cutoff Day**

Usage
=====

If the cutoff day is configured as Monday:

- On Monday, users can still register timesheets for:

- Monday
- Sunday
- Saturday
- Friday
- Thursday
- Wednesday
- Tuesday

- Starting Tuesday, users can no longer register timesheets dated Monday
or earlier.

Users belonging to the group:

- Timesheet Weekly Cutoff Bypass

can register timesheets outside the weekly cutoff period.

Timesheets registered outside the allowed cutoff period by bypass users
are automatically marked as:

- Outside Weekly Cutoff

This flag can be used later for:

- reporting
- auditing
- filtering

Technical Notes
---------------

The weekly cutoff restriction is enforced during both:

- record creation
- record update

This ensures compatibility with:

- editable tree views
- imports
- RPC calls
- automated processes

The restriction validation is applied per record to ensure correct
behavior during multi-record operations.

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/timesheet/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/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**>`_.

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

Credits
=======

Authors
-------

* Spearhead

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

- John Triviño

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-JT-Odoo| image:: https://github.com/JT-Odoo.png?size=40px
:target: https://github.com/JT-Odoo
:alt: JT-Odoo

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

|maintainer-JT-Odoo|

This module is part of the `OCA/timesheet <https://github.com/OCA/timesheet/tree/18.0/hr_timesheet_weekly_cutoff>`_ 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 hr_timesheet_weekly_cutoff/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
18 changes: 18 additions & 0 deletions hr_timesheet_weekly_cutoff/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "HR Timesheet Weekly Cutoff",
"version": "18.0.1.0.0",
"category": "Timesheets",
"website": "https://github.com/OCA/timesheet",
"summary": "Weekly cutoff control for timesheets",
"author": "Spearhead, Odoo Community Association (OCA)",
"maintainers": ["JT-Odoo"],
"license": "LGPL-3",
"depends": [
"hr_timesheet",
],
"data": [
"security/groups_security.xml",
"views/res_config_settings_views.xml",
],
"installable": True,
}
128 changes: 128 additions & 0 deletions hr_timesheet_weekly_cutoff/i18n/es.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * hr_timesheet_weekly_cutoff
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-10 21:57+0000\n"
"PO-Revision-Date: 2026-05-10 17:02-0500\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es_EC\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.9\n"

#. module: hr_timesheet_weekly_cutoff
#. odoo-python
#: code:addons/hr_timesheet_weekly_cutoff/models/account_analytic_line.py:0
msgid "A timesheet date is required."
msgstr "Se requiere una fecha para la hoja de horas trabajadas."

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model,name:hr_timesheet_weekly_cutoff.model_account_analytic_line
msgid "Analytic Line"
msgstr "Línea analítica"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model,name:hr_timesheet_weekly_cutoff.model_res_company
msgid "Companies"
msgstr "Empresas"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model,name:hr_timesheet_weekly_cutoff.model_res_config_settings
msgid "Config Settings"
msgstr "Ajustes de configuración"

#. module: hr_timesheet_weekly_cutoff
#: model_terms:ir.ui.view,arch_db:hr_timesheet_weekly_cutoff.res_config_settings_form_day_lock
msgid "Define the weekly cut off day for time tracking."
msgstr "Defina el día límite semanal para el registro del tiempo."

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__4
msgid "Friday"
msgstr "Viernes"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__0
msgid "Monday"
msgstr "Lunes"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields,field_description:hr_timesheet_weekly_cutoff.field_account_analytic_line__is_outside_weekly_cutoff
msgid "Outside Weekly Cutoff"
msgstr "Fuera del límite semanal"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__5
msgid "Saturday"
msgstr "Sábado"

#. module: hr_timesheet_weekly_cutoff
#: model_terms:ir.ui.view,arch_db:hr_timesheet_weekly_cutoff.res_config_settings_form_day_lock
msgid ""
"Select the day used as the weekly closing date\n"
" for time tracking.\n"
" <br/>\n"
" Once the configured day has passed, it will\n"
" not be possible to register hours with dates\n"
" prior to the weekly cutoff."
msgstr ""
"Seleccione el día que se utilizará como fecha de cierre semanal\n"
"para el registro de horas.\n"
"\n"
"<br/>\n"
"Una vez transcurrido el día configurado, no será posible\n"
"registrar horas con fechas anteriores a la fecha límite semanal."

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__6
msgid "Sunday"
msgstr "Domingo"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__3
msgid "Thursday"
msgstr "Jueves"

#. module: hr_timesheet_weekly_cutoff
#: model:res.groups,name:hr_timesheet_weekly_cutoff.group_bypass_timesheet_lock
msgid "Timesheet Weekly Cutoff Bypass"
msgstr "Omisión del límite semanal de la hoja de horas"

#. module: hr_timesheet_weekly_cutoff
#: model:res.groups,name:hr_timesheet_weekly_cutoff.group_timesheet_lock_manager
msgid "Timesheet Weekly Cutoff Manager"
msgstr "Gerente de cierre semanal de hojas de horas"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__1
msgid "Tuesday"
msgstr "Martes"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields.selection,name:hr_timesheet_weekly_cutoff.selection__res_company__timesheet_lock_weekday__2
msgid "Wednesday"
msgstr "Miércoles"

#. module: hr_timesheet_weekly_cutoff
#: model_terms:ir.ui.view,arch_db:hr_timesheet_weekly_cutoff.res_config_settings_form_day_lock
msgid "Weekly Cutoff Control"
msgstr "Control de corte semanal"

#. module: hr_timesheet_weekly_cutoff
#: model:ir.model.fields,field_description:hr_timesheet_weekly_cutoff.field_res_company__timesheet_lock_weekday
#: model:ir.model.fields,field_description:hr_timesheet_weekly_cutoff.field_res_config_settings__timesheet_lock_weekday
msgid "Weekly Cutoff Day"
msgstr "Día límite semanal"

#. module: hr_timesheet_weekly_cutoff
#. odoo-python
#: code:addons/hr_timesheet_weekly_cutoff/models/account_analytic_line.py:0
msgid "You cannot register timesheets dated due to the weekly cutoff control."
msgstr "No puede registrar hojas de horas con fecha límite debido al control de corte semanal."
3 changes: 3 additions & 0 deletions hr_timesheet_weekly_cutoff/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import res_company
from . import res_config_settings
from . import account_analytic_line
77 changes: 77 additions & 0 deletions hr_timesheet_weekly_cutoff/models/account_analytic_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from datetime import timedelta

from odoo import api, fields, models
from odoo.exceptions import ValidationError


class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"

is_outside_weekly_cutoff = fields.Boolean(
string="Outside Weekly Cutoff",
readonly=True,
copy=False,
index=True,
)

def _get_allowed_from_date(self):
self.ensure_one()
company = self.company_id
today = fields.Date.context_today(self)
lock_weekday = int(company.timesheet_lock_weekday or 0)
current_weekday = today.weekday()
days_since_lock = (current_weekday - lock_weekday) % 7
lock_date = today - timedelta(days=days_since_lock)
if current_weekday == lock_weekday:
allowed_from_date = lock_date - timedelta(days=6)
else:
allowed_from_date = lock_date + timedelta(days=1)
return allowed_from_date

def _is_outside_cutoff(self):
self.ensure_one()
if not self.date:
raise ValidationError(self.env._("A timesheet date is required."))
allowed_from_date = self._get_allowed_from_date()
return self.date < allowed_from_date

def _validate_timesheet_lock(self):
company = self.company_id
if not company.timesheet_lock_weekday:
return False
has_bypass_group = self.env.user.has_group(
"hr_timesheet_weekly_cutoff.group_bypass_timesheet_lock"
)
outside_weekly_cutoff = False
is_outside_cutoff = self._is_outside_cutoff()
if has_bypass_group:
if is_outside_cutoff:
outside_weekly_cutoff = True
if is_outside_cutoff and not outside_weekly_cutoff:
raise ValidationError(
self.env._(
"You cannot register timesheets dated "
f"{fields.Date.to_string(self.date)} "
"due to the weekly cutoff control."
)
)
return outside_weekly_cutoff

@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if "product_id" in vals:
continue
record = self.new(vals)
weekly_cutoff = record._validate_timesheet_lock()
vals["is_outside_weekly_cutoff"] = weekly_cutoff
return super().create(vals_list)

def write(self, vals):
result = super().write(vals)
if "timesheet_invoice_id" not in vals:
for record in self:
weekly_cutoff = record._validate_timesheet_lock()
if "date" in vals:
record.is_outside_weekly_cutoff = weekly_cutoff
return result
Loading
Loading