Initial commit: OCA Mrp packages (117 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:05 +02:00
commit 277e84fd7a
4403 changed files with 395154 additions and 0 deletions

View file

@ -0,0 +1,46 @@
# HR Timesheet Sheet Restriction
Odoo addon: hr_timesheet_time_restriction
## Installation
```bash
pip install odoo-bringout-oca-timesheet-hr_timesheet_time_restriction
```
## Dependencies
This addon depends on:
- hr_timesheet
## Manifest Information
- **Name**: HR Timesheet Sheet Restriction
- **Version**: 16.0.1.0.1
- **Category**: Human Resources
- **License**: AGPL-3
- **Installable**: True
## Source
Based on [OCA/timesheet](https://github.com/OCA/timesheet) branch 16.0, addon `hr_timesheet_time_restriction`.
## License
This package maintains the original AGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Reports: doc/REPORTS.md
- Security: doc/SECURITY.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Hr_timesheet_time_restriction Module - hr_timesheet_time_restriction
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for hr_timesheet_time_restriction. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,5 @@
# Dependencies
This addon depends on:
- [hr_timesheet](../../odoo-bringout-oca-ocb-hr_timesheet)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon hr_timesheet_time_restriction or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-timesheet-hr_timesheet_time_restriction"
# or
uv pip install odoo-bringout-oca-timesheet-hr_timesheet_time_restriction"
```

View file

@ -0,0 +1,14 @@
# Models
Detected core models and extensions in hr_timesheet_time_restriction.
```mermaid
classDiagram
class account_analytic_line
class project_project
class res_config_settings
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: hr_timesheet_time_restriction. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon hr_timesheet_time_restriction
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,74 @@
# Security
Access control and security definitions in hr_timesheet_time_restriction.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[all_odoo_addons_repos.txt](../all_odoo_addons_repos.txt)**
- 318 model access rules
- **[bosnian_translations.json](../bosnian_translations.json)**
- 50 model access rules
- **[bosnian_translations_output.json](../bosnian_translations_output.json)**
- 444 model access rules
- **[CHANGELOG.md](../CHANGELOG.md)**
- 132 model access rules
- **[delete_all_odoo_addons.sh](../delete_all_odoo_addons.sh)**
- 50 model access rules
- **[delete_odoo_addons.sh](../delete_odoo_addons.sh)**
- 44 model access rules
- **[doc](../doc)**
- **[docker](../docker)**
- **[input](../input)**
- **[nix](../nix)**
- **[odoo.conf](../odoo.conf)**
- 58 model access rules
- **[odoo_packages_bez_l10n.txt](../odoo_packages_bez_l10n.txt)**
- 1947 model access rules
- **[odoo_packages_bringout.txt](../odoo_packages_bringout.txt)**
- 1947 model access rules
- **[odoo_packages.txt](../odoo_packages.txt)**
- 2085 model access rules
- **[output](../output)**
- **[packages](../packages)**
- **[PACKAGES.md](../PACKAGES.md)**
- 298 model access rules
- **[README.md](../README.md)**
- 338 model access rules
- **[scripts](../scripts)**
- **[temp](../temp)**
- **[TRANSLATION_BS_SUMMARY.md](../TRANSLATION_BS_SUMMARY.md)**
- 146 model access rules
- **[verify_deletions.sh](../verify_deletions.sh)**
- 55 model access rules
## Record Rules
Row-level security rules defined in:
## Security Groups & Configuration
Security groups and permissions defined in:
- **[res_groups.xml](../hr_timesheet_time_restriction/security/res_groups.xml)**
- 1 security groups defined
```mermaid
graph TB
subgraph "Security Layers"
A[Users] --> B[Groups]
B --> C[Access Control Lists]
C --> D[Models]
B --> E[Record Rules]
E --> F[Individual Records]
end
```
Security files overview:
- **[res_groups.xml](../hr_timesheet_time_restriction/security/res_groups.xml)**
- Security groups, categories, and XML-based rules
Notes
- Access Control Lists define which groups can access which models
- Record Rules provide row-level security (filter records by user/group)
- Security groups organize users and define permission sets
- All security is enforced at the ORM level by Odoo

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon hr_timesheet_time_restriction
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,98 @@
==============================
HR Timesheet Sheet Restriction
==============================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:e3dbea674c6d47ce9372c2773f21b8b783da54b981ed549d226333f2f0c61e9f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ftimesheet-lightgray.png?logo=github
:target: https://github.com/OCA/timesheet/tree/16.0/hr_timesheet_time_restriction
:alt: OCA/timesheet
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/timesheet-16-0/timesheet-16-0-hr_timesheet_time_restriction
: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=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This app restricts user from creating timesheets for past dates (or changing date of timesheet to a past date) based on project configuration or global restriction settings.
Use case is to make sure that new timesheets are not added to a specific time range after that period has been reported and/or billed.
In user, it's possible to activate flag "No timesheet date restriction" to override behavior.
**Table of contents**
.. contents::
:local:
Configuration
=============
Specific project timesheet restriction:
- Project > Configuration > Projects > open project > enable "Use timesheet restriction" and set "Timesheet Restriction (days)"
Global timesheet restriction:
- Timesheets > Configuration > Settings > enable "Use timesheet restriction" and set "Timesheet Restriction (days)" (note: it will only apply to newly created projects)
Ignore user timesheet restriction:
- Settings > Users & Companies > Users > open user > Technical: set "No Timesheet Date Restriction"
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_time_restriction%0Aversion:%2016.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
~~~~~~~
* Cetmix
Contributors
~~~~~~~~~~~~
* Ooops404 <https://www.ooops404.com/>
* Cetmix <https://cetmix.com/>
Other credits
~~~~~~~~~~~~~
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
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.
This module is part of the `OCA/timesheet <https://github.com/OCA/timesheet/tree/16.0/hr_timesheet_time_restriction>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1,2 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import models

View file

@ -0,0 +1,22 @@
# Copyright 2022 Dinar Gabbasov
# Copyright 2022 Ooops404
# Copyright 2022 Cetmix
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "HR Timesheet Sheet Restriction",
"version": "16.0.1.0.1",
"category": "Human Resources",
"website": "https://github.com/OCA/timesheet",
"author": "Cetmix, Odoo Community Association (OCA)",
"license": "AGPL-3",
"installable": True,
"application": False,
"summary": "Restrictions on the creation of time sheets for past dates",
"depends": ["hr_timesheet"],
"data": [
"security/res_groups.xml",
"views/project_project_view.xml",
"views/res_config_settings_view.xml",
],
}

View file

@ -0,0 +1,134 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * hr_timesheet_time_restriction
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "Allowed days must be a non-negative integer."
msgstr "Dozvoljeni dani moraju biti ne-negativan cijeli broj."
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_account_analytic_line
msgid "Analytic Line"
msgstr "Analitička stavka"
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.res_config_settings_view_form
msgid "Auto-enable Timesheet restriction"
msgstr "Auto-omogući ograničenje radnih listova"
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_res_config_settings
msgid "Config Settings"
msgstr "Postavke"
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "Invalid date value for timesheet record."
msgstr "Neispravna vrijednost datuma za zapis radnog lista."
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,help:hr_timesheet_time_restriction.field_project_project__timesheet_restriction_days
msgid ""
"Maximum number of days before today allowed for a timesheet. Set to 0 to "
"disable projectlevel restriction."
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:res.groups,name:hr_timesheet_time_restriction.group_timesheet_time_manager
msgid "No Timesheet Date Restriction"
msgstr "Nema ograničenja datuma radnog lista"
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_project_project
msgid "Project"
msgstr "Projekat"
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/res_config_settings.py:0
#, python-format
msgid "The day of the timesheet restriction must not be negative."
msgstr "Dan ograničenja radnih listova ne smije biti negativan."
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/project_project.py:0
#, python-format
msgid "The number of days for timesheet restriction must not be negative."
msgstr "Broj dana za ograničenje radnih listova ne smije biti negativan."
#. module: hr_timesheet_time_restriction
#: model:res.groups,comment:hr_timesheet_time_restriction.group_timesheet_time_manager
msgid "The user will be able to change the date on the timesheet."
msgstr "Korisnik će moći mijenjati datum na radnom listu."
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.res_config_settings_view_form
msgid "Timesheet Restriction (days)"
msgstr "Ograničenje radnih listova (dani)"
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.edit_project
msgid "Timesheet Restriction (days):"
msgstr "Ograničenje radnih listova (dani):"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project__timesheet_restriction_days
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings__timesheet_restriction_days
msgid "Timesheet Restriction Days"
msgstr "Dani ograničenja radnih listova"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project__use_timesheet_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings__use_timesheet_restriction
msgid "Use Timesheet Restriction"
msgstr "Koristi ograničenje radnih listova"
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.edit_project
msgid "Use timesheet restriction"
msgstr "Koristi ograničenje radnih listova"
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/res_config_settings.py:0
#, python-format
msgid "Warning!"
msgstr "Upozorenje!"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,help:hr_timesheet_time_restriction.field_project_project__use_timesheet_restriction
msgid ""
"Whether to enforce date restriction for this project based on the global "
"setting."
msgstr ""
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "You cannot set a timesheet for a date different from current date."
msgstr "Ne možete postaviti radni list za datum različit od trenutnog datuma."
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "You cannot set a timesheet more than %(days)s days from current date."
msgstr "Ne možete postaviti radni list više od %(days)s dana od trenutnog datuma."

View file

@ -0,0 +1,134 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * hr_timesheet_time_restriction
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "Allowed days must be a non-negative integer."
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_account_analytic_line
msgid "Analytic Line"
msgstr ""
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.res_config_settings_view_form
msgid "Auto-enable Timesheet restriction"
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "Invalid date value for timesheet record."
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,help:hr_timesheet_time_restriction.field_project_project__timesheet_restriction_days
msgid ""
"Maximum number of days before today allowed for a timesheet. Set to 0 to "
"disable projectlevel restriction."
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:res.groups,name:hr_timesheet_time_restriction.group_timesheet_time_manager
msgid "No Timesheet Date Restriction"
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_project_project
msgid "Project"
msgstr ""
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/res_config_settings.py:0
#, python-format
msgid "The day of the timesheet restriction must not be negative."
msgstr ""
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/project_project.py:0
#, python-format
msgid "The number of days for timesheet restriction must not be negative."
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:res.groups,comment:hr_timesheet_time_restriction.group_timesheet_time_manager
msgid "The user will be able to change the date on the timesheet."
msgstr ""
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.res_config_settings_view_form
msgid "Timesheet Restriction (days)"
msgstr ""
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.edit_project
msgid "Timesheet Restriction (days):"
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project__timesheet_restriction_days
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings__timesheet_restriction_days
msgid "Timesheet Restriction Days"
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project__use_timesheet_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings__use_timesheet_restriction
msgid "Use Timesheet Restriction"
msgstr ""
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.edit_project
msgid "Use timesheet restriction"
msgstr ""
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/res_config_settings.py:0
#, python-format
msgid "Warning!"
msgstr ""
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,help:hr_timesheet_time_restriction.field_project_project__use_timesheet_restriction
msgid ""
"Whether to enforce date restriction for this project based on the global "
"setting."
msgstr ""
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "You cannot set a timesheet for a date different from current date."
msgstr ""
#. module: hr_timesheet_time_restriction
#. odoo-python
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "You cannot set a timesheet more than %(days)s days from current date."
msgstr ""

View file

@ -0,0 +1,134 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * hr_timesheet_time_restriction
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-01-16 18:45+0000\n"
"Last-Translator: Francesco Foresti <francesco.foresti@ooops404.com>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.14.1\n"
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_account_analytic_line
msgid "Analytic Line"
msgstr "Riga analitica"
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.res_config_settings_view_form
msgid "Auto-enable Timesheet restriction"
msgstr "Abilita automaticamente restrizioni foglio ore"
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_res_config_settings
msgid "Config Settings"
msgstr "Impostazioni configurazione"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_account_analytic_line__display_name
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project__display_name
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_account_analytic_line__id
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project__id
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings__id
msgid "ID"
msgstr "ID"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_account_analytic_line____last_update
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project____last_update
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
#. module: hr_timesheet_time_restriction
#: model:res.groups,name:hr_timesheet_time_restriction.group_timesheet_time_manager
msgid "No Timesheet Date Restriction"
msgstr "Nessuna restrizione data foglio ore"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,help:hr_timesheet_time_restriction.field_project_project__timesheet_restriction_days
msgid "Not active if equal to 0."
msgstr "Non è attivo se uguale a 0."
#. module: hr_timesheet_time_restriction
#: model:ir.model,name:hr_timesheet_time_restriction.model_project_project
msgid "Project"
msgstr "Progetto"
#. module: hr_timesheet_time_restriction
#: code:addons/hr_timesheet_time_restriction/models/project_project.py:0
#: code:addons/hr_timesheet_time_restriction/models/res_config_settings.py:0
#, python-format
msgid "The day of the timesheet restriction must not be negative."
msgstr "Il giorno della restrizione fogli ore non deve essere negativo."
#. module: hr_timesheet_time_restriction
#: model:res.groups,comment:hr_timesheet_time_restriction.group_timesheet_time_manager
msgid "The user will be able to change the date on the timesheet."
msgstr "L'utente potrà cambiare la data sul foglio ore."
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.res_config_settings_view_form
msgid "Timesheet Restriction (days)"
msgstr "Restrizione foglio ore (giorni)"
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.edit_project
msgid "Timesheet Restriction (days):"
msgstr "Restrizione foglio ore (giorni):"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project__timesheet_restriction_days
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings__timesheet_restriction_days
msgid "Timesheet Restriction Days"
msgstr "Giorni restrizione foglio ore"
#. module: hr_timesheet_time_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_project_project__use_timesheet_restriction
#: model:ir.model.fields,field_description:hr_timesheet_time_restriction.field_res_config_settings__use_timesheet_restriction
msgid "Use Timesheet Restriction"
msgstr "Usa restrizioni foglio ore"
#. module: hr_timesheet_time_restriction
#: model_terms:ir.ui.view,arch_db:hr_timesheet_time_restriction.edit_project
msgid "Use timesheet restriction"
msgstr "Usa restrizioni foglio ore"
#. module: hr_timesheet_time_restriction
#: code:addons/hr_timesheet_time_restriction/models/res_config_settings.py:0
#, python-format
msgid "Warning!"
msgstr "Attenzione!"
#. module: hr_timesheet_time_restriction
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "You cannot set a timesheet for a date different from current date"
msgstr ""
"Non puoi impostare un foglio ore per una data diversa da quella odierna"
#. module: hr_timesheet_time_restriction
#: code:addons/hr_timesheet_time_restriction/models/account_analytic_line.py:0
#, python-format
msgid "You cannot set a timesheet more than {days} days from current date."
msgstr ""
"Non puoi impostare un foglio ore a più di {days} giorni dalla data corrente."
#, python-format
#~ msgid ""
#~ "You cannot change or create a timesheet with a specified \"date\" in the "
#~ "past."
#~ msgstr ""
#~ "Non puoi modificare o creare un foglio ore con una data nel passato."

View file

@ -0,0 +1,3 @@
from . import account_analytic_line
from . import project_project
from . import res_config_settings

View file

@ -0,0 +1,119 @@
# Copyright 2022 Dinar Gabbasov
# Copyright 2022 Ooops404
# Copyright 2022 Cetmix
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import logging
from datetime import date, timedelta
from odoo import _, api, models
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"
@api.constrains("date")
def _check_project_date(self):
"""
Checks and validates the project timesheet date based on defined restrictions
Attributes:
date (fields.Date): The timesheet entry date.
project_id (fields.Many2one): The project associated with the timesheet entry.
Raises:
ValidationError: Raised if the timesheet date does not adhere to the allowed days
of entry. The restrictions can be defined either globally or at the project level.
"""
if not self._is_time_manager():
use_global, global_days = self._read_global_restriction()
for record in self:
if (
record.date
and record.project_id
and (use_global or record.project_id.use_timesheet_restriction)
):
allowed_days = (
record.project_id.timesheet_restriction_days or global_days
)
self._validate_timesheet_date(record, allowed_days)
def _is_time_manager(self):
"""Return True if the current user is in the timesheet time manager group."""
is_manager = self.user_has_groups(
"hr_timesheet_time_restriction.group_timesheet_time_manager"
)
if not is_manager:
_logger.warning(
"Unauthorized attempt to bypass timesheet restriction by user %s (ID=%s)",
self.env.user.login or self.env.user.name,
self.env.uid,
)
return is_manager
def _read_global_restriction(self):
"""
Reads global restriction settings for timesheets.
Returns:
tuple: A tuple where the first element is a boolean indicating whether
timesheet restrictions are enabled and the second element is an integer
representing the number of restriction days configured.
"""
params = self.env["ir.config_parameter"].sudo()
use_flag = bool(
params.get_param(
"hr_timesheet_time_restriction.use_timesheet_restriction", False
)
)
days = int(
params.get_param(
"hr_timesheet_time_restriction.timesheet_restriction_days", 0
)
)
return use_flag, days
def _validate_timesheet_date(self, record, allowed_days):
"""
Validates the date of the timesheet record against the allowed policy settings,
ensuring entries comply with the set boundaries.
Args:
record (Record): The timesheet record containing the date to be validated.
allowed_days (int): The number of days allowed for backdating timesheets. If set
to 0, only today's date is permissible.
Raises:
ValidationError: If the record date is older than allowed_days or does not match
today's date when allowed_days is set to 0.
"""
if not record.date or not isinstance(record.date, date):
raise ValidationError(_("Invalid date value for timesheet record."))
if allowed_days < 0:
raise ValidationError(_("Allowed days must be a non-negative integer."))
today = date.today()
delta = today - record.date
if allowed_days > 0:
# Disallow entries older than allowed_days
if delta > timedelta(days=allowed_days):
raise ValidationError(
_(
"You cannot set a timesheet more than %(days)s days from current date.",
days=allowed_days,
)
)
else:
if record.date != today:
raise ValidationError(
_(
"You cannot set a timesheet for a date different from current date."
)
)

View file

@ -0,0 +1,67 @@
# Copyright 2022 Dinar Gabbasov
# Copyright 2022 Ooops404
# Copyright 2022 Cetmix
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class ProjectProject(models.Model):
_inherit = "project.project"
timesheet_restriction_days = fields.Integer(
default=0,
help="Maximum number of days before today allowed for a timesheet. "
"Set to 0 to disable projectlevel restriction.",
)
use_timesheet_restriction = fields.Boolean(
default=lambda self: self._default_use_timesheet_restriction(),
help="Whether to enforce date restriction for this project "
"based on the global setting.",
)
@api.model
def _default_use_timesheet_restriction(self):
"""
This method provides a default value for the 'use_timesheet_restriction' field by
fetching the configuration parameter
'hr_timesheet_time_restriction.use_timesheet_restriction'.
It ensures that the returned value is a boolean.
Returns:
bool: The default value of the 'use_timesheet_restriction' field based on the
system configuration parameter.
"""
return bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("hr_timesheet_time_restriction.use_timesheet_restriction", False)
)
@api.constrains("timesheet_restriction_days")
def _check_timesheet_restriction_days(self):
"""
Checks and validates the timesheet restriction days for projects
Raises:
ValidationError:
If the `timesheet_restriction_days` field contains a negative value
in a project record.
"""
# Skip validation if global restriction is disabled
global_flag = (
self.env["ir.config_parameter"]
.sudo()
.get_param("hr_timesheet_time_restriction.use_timesheet_restriction", False)
)
if not bool(global_flag):
return
for project in self:
if project.timesheet_restriction_days < 0:
raise ValidationError(
_(
"The number of days for timesheet restriction must not be negative."
)
)

View file

@ -0,0 +1,35 @@
# Copyright 2022 Dinar Gabbasov
# Copyright 2022 Ooops404
# Copyright 2022 Cetmix
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
timesheet_restriction_days = fields.Integer(
config_parameter="hr_timesheet_time_restriction.timesheet_restriction_days",
default=0,
)
use_timesheet_restriction = fields.Boolean(
config_parameter="hr_timesheet_time_restriction.use_timesheet_restriction"
)
@api.onchange("timesheet_restriction_days")
def _onchange_timesheet_restriction_days(self):
"""
Check that `timesheet_restriction_days` not negative
"""
if self.timesheet_restriction_days < 0:
self.timesheet_restriction_days = 0
return {
"warning": {
"title": _("Warning!"),
"message": _(
"The day of the timesheet restriction must not be negative."
),
},
}

View file

@ -0,0 +1,8 @@
Specific project timesheet restriction:
- Project > Configuration > Projects > open project > enable "Use timesheet restriction" and set "Timesheet Restriction (days)"
Global timesheet restriction:
- Timesheets > Configuration > Settings > enable "Use timesheet restriction" and set "Timesheet Restriction (days)" (note: it will only apply to newly created projects)
Ignore user timesheet restriction:
- Settings > Users & Companies > Users > open user > Technical: set "No Timesheet Date Restriction"

View file

@ -0,0 +1,2 @@
* Ooops404 <https://www.ooops404.com/>
* Cetmix <https://cetmix.com/>

View file

@ -0,0 +1 @@
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

View file

@ -0,0 +1,5 @@
This app restricts user from creating timesheets for past dates (or changing date of timesheet to a past date) based on project configuration or global restriction settings.
Use case is to make sure that new timesheets are not added to a specific time range after that period has been reported and/or billed.
In user, it's possible to activate flag "No timesheet date restriction" to override behavior.

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="group_timesheet_time_manager" model="res.groups">
<field name="name">No Timesheet Date Restriction</field>
<field name="category_id" ref="base.module_category_usability" />
<field name="implied_ids" eval="[(6, 0, [ref('base.group_user')])]" />
<field
name="comment"
>The user will be able to change the date on the timesheet.</field>
</record>
</odoo>

View file

@ -0,0 +1,454 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>HR Timesheet Sheet Restriction</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="hr-timesheet-sheet-restriction">
<h1 class="title">HR Timesheet Sheet Restriction</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:e3dbea674c6d47ce9372c2773f21b8b783da54b981ed549d226333f2f0c61e9f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/timesheet/tree/16.0/hr_timesheet_time_restriction"><img alt="OCA/timesheet" src="https://img.shields.io/badge/github-OCA%2Ftimesheet-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/timesheet-16-0/timesheet-16-0-hr_timesheet_time_restriction"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/timesheet&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This app restricts user from creating timesheets for past dates (or changing date of timesheet to a past date) based on project configuration or global restriction settings.</p>
<p>Use case is to make sure that new timesheets are not added to a specific time range after that period has been reported and/or billed.</p>
<p>In user, its possible to activate flag “No timesheet date restriction” to override behavior.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#other-credits" id="toc-entry-6">Other credits</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<dl class="docutils">
<dt>Specific project timesheet restriction:</dt>
<dd><ul class="first last simple">
<li>Project &gt; Configuration &gt; Projects &gt; open project &gt; enable “Use timesheet restriction” and set “Timesheet Restriction (days)”</li>
</ul>
</dd>
<dt>Global timesheet restriction:</dt>
<dd><ul class="first last simple">
<li>Timesheets &gt; Configuration &gt; Settings &gt; enable “Use timesheet restriction” and set “Timesheet Restriction (days)” (note: it will only apply to newly created projects)</li>
</ul>
</dd>
<dt>Ignore user timesheet restriction:</dt>
<dd><ul class="first last simple">
<li>Settings &gt; Users &amp; Companies &gt; Users &gt; open user &gt; Technical: set “No Timesheet Date Restriction”</li>
</ul>
</dd>
</dl>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/timesheet/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/timesheet/issues/new?body=module:%20hr_timesheet_time_restriction%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<ul class="simple">
<li>Cetmix</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li>Ooops404 &lt;<a class="reference external" href="https://www.ooops404.com/">https://www.ooops404.com/</a>&gt;</li>
<li>Cetmix &lt;<a class="reference external" href="https://cetmix.com/">https://cetmix.com/</a>&gt;</li>
</ul>
</div>
<div class="section" id="other-credits">
<h2><a class="toc-backref" href="#toc-entry-6">Other credits</a></h2>
<ul class="simple">
<li>Odoo Community Association: <a class="reference external" href="https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg">Icon</a>.</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/timesheet/tree/16.0/hr_timesheet_time_restriction">OCA/timesheet</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,2 @@
from . import test_enabled_hr_timesheet_time_restriction
from . import test_disabled_hr_timesheet_time_restriction

View file

@ -0,0 +1,150 @@
# Copyright 2022 Dinar Gabbasov
# Copyright 2022 Ooops404
# Copyright 2022 Cetmix
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from datetime import date, timedelta
from odoo.exceptions import ValidationError
from odoo.tests import common
class TestHrTimesheetTimeRestriction(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# We create an active employee for the current user, if he is not yet
employee = cls.env["hr.employee"].search(
[("user_id", "=", cls.env.uid)], limit=1
)
if not employee:
employee = cls.env["hr.employee"].create(
{
"name": cls.env.user.name or "Test Employee",
"user_id": cls.env.uid,
"active": True,
"company_id": cls.env.company.id,
}
)
cls.employee = employee
# We create a project, task and configuration
cls.project = cls.env["project.project"].create({"name": "Test project"})
cls.analytic_account = cls.project.analytic_account_id
cls.task = cls.env["project.task"].create(
{
"name": "Test task",
"project_id": cls.project.id,
}
)
cls.config = cls.env["res.config.settings"].create({})
# The global restriction is disabled by default
cls.config.use_timesheet_restriction = False
cls.config.execute()
cls._base_vals = {
"task_id": cls.task.id,
"project_id": cls.project.id,
"account_id": cls.analytic_account.id,
"employee_id": cls.employee.id,
"name": "Test line",
}
@classmethod
def _create_line(cls, days_offset):
"""create analytic line offset by days_offset from today"""
vals = dict(cls._base_vals)
vals["date"] = date.today() + timedelta(days=days_offset)
return cls.env["account.analytic.line"].create(vals)
def test_project_restriction_days(self):
"""With a disconnected global restriction (use_timesheet_restriction = False),
the restriction specified at the project level is not applied
"""
self.project.timesheet_restriction_days = 1
# check that we can create new timesheet
self.assertTrue(
self._create_line(0),
"Timesheet should be created for today",
)
# check that we can create new timesheet with date before
# that current date - 1
self.assertTrue(
self._create_line(-2),
"Timesheet should be created when global restriction is disabled",
)
def test_project_restriction_days_by_config(self):
ConfigSettings = self.env["res.config.settings"].create({})
ConfigSettings.timesheet_restriction_days = 1
ConfigSettings.use_timesheet_restriction = True
ConfigSettings.execute()
# check that we can create new timesheet
self.assertTrue(
self._create_line(-1),
"Timesheet should be created for date within allowed range",
)
# check that we cannot create new timesheet with date before
# that current date - 1
with self.assertRaises(ValidationError):
self._create_line(-2)
def test_project_restriction_days_ignore_config(self):
ConfigSettings = self.env["res.config.settings"].create({})
ConfigSettings.timesheet_restriction_days = 1
ConfigSettings.use_timesheet_restriction = True
ConfigSettings.execute()
self.project.timesheet_restriction_days = 2
self.assertTrue(self._create_line(-2))
with self.assertRaises(ValidationError):
self._create_line(-3)
def test_project_restriction_days_ignore_for_timesheet_time_manager(self):
"""
Users included in the timesheet_time_manager group ignore the dates.
"""
group_id = self.ref(
"hr_timesheet_time_restriction.group_timesheet_time_manager"
)
self.env.user.write({"groups_id": [(4, group_id)]})
self.project.timesheet_restriction_days = 1
# check that we can create new timesheet with date before
# that current date - 1
self.assertTrue(
self._create_line(-2),
"Timesheet should be created for timesheet time manager regardless of date",
)
def test_set_negative_project_restriction_days(self):
"""
Global restriction is OFF project field accepts negative values
without raising and stores them asis.
"""
try:
self.project.timesheet_restriction_days = -1
except ValidationError:
self.project.timesheet_restriction_days = 0
self.assertEqual(self.project.timesheet_restriction_days, -1)
def test_set_negative_config_restriction_days(self):
ConfigSettings = self.env["res.config.settings"].create({})
ConfigSettings.timesheet_restriction_days = -1
ConfigSettings._onchange_timesheet_restriction_days()
ConfigSettings.execute()
self.assertEqual(ConfigSettings.timesheet_restriction_days, 0)
params = self.env["ir.config_parameter"].sudo()
params.search(
[
(
"key",
"in",
[
"hr_timesheet_time_restriction.use_timesheet_restriction",
"hr_timesheet_time_restriction.timesheet_restriction_days",
],
)
]
).unlink()
ConfigSettings_2 = self.env["res.config.settings"].create({})
ConfigSettings_2.execute()
self.assertFalse(ConfigSettings_2.use_timesheet_restriction)
self.assertEqual(ConfigSettings_2.timesheet_restriction_days, 0)

View file

@ -0,0 +1,149 @@
# Copyright 2022 Dinar Gabbasov
# Copyright 2022 Ooops404
# Copyright 2022 Cetmix
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import logging
from datetime import date, timedelta
from odoo.exceptions import ValidationError
from odoo.tests import common
_logger = logging.getLogger(__name__)
class TestHrTimesheetTimeRestriction(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.project = cls.env["project.project"].create(
{
"name": "Test project",
"use_timesheet_restriction": True,
}
)
cls.analytic_account = cls.project.analytic_account_id
cls.task = cls.env["project.task"].create(
{
"name": "Test task",
"project_id": cls.project.id,
}
)
cls.config = cls.env["res.config.settings"].create({})
cls.config.use_timesheet_restriction = True
cls.config.execute()
# Create or find an active employee for the current user
employee = cls.env["hr.employee"].search(
[("user_id", "=", cls.env.uid)], limit=1
)
if not employee:
employee = cls.env["hr.employee"].create(
{
"name": cls.env.user.name or "Test Employee",
"user_id": cls.env.uid,
"active": True,
"company_id": cls.env.company.id,
}
)
cls.employee = employee
cls._base_vals = {
"task_id": cls.task.id,
"project_id": cls.project.id,
"account_id": cls.analytic_account.id,
"employee_id": cls.employee.id,
"name": "Test line",
}
def _create_line(self, days_offset):
vals = dict(self._base_vals)
vals["date"] = date.today() + timedelta(days=days_offset)
return self.env["account.analytic.line"].create(vals)
def test_project_restriction_days(self):
self.project.timesheet_restriction_days = 1
# check that we can create new timesheet
line = self._create_line(0)
self.assertTrue(line)
with self.assertRaises(ValidationError):
self._create_line(-2)
def test_project_restriction_days_by_config(self):
self.config.timesheet_restriction_days = 1
self.config.execute()
# Yesterday (offset -1): allowed, so line should be created
line = self._create_line(-1)
self.assertTrue(line)
# Two days ago (offset -2): should raise
with self.assertRaises(ValidationError):
self._create_line(-2)
def test_project_restriction_days_ignore_config(self):
# Global = 1, but project override = 2
self.config.timesheet_restriction_days = 1
self.config.execute()
self.project.timesheet_restriction_days = 2
# Two days ago (offset -2): allowed
line = self._create_line(-2)
self.assertTrue(line)
# Three days ago (offset -3): should raise
with self.assertRaises(ValidationError):
self._create_line(-3)
def test_project_restriction_days_ignore_for_timesheet_time_manager(self):
group = self.ref("hr_timesheet_time_restriction.group_timesheet_time_manager")
self.env.user.write({"groups_id": [(4, group)]})
self.project.timesheet_restriction_days = 1
# check that we can create new timesheet with date before
# that current date - 1
line = self._create_line(-2)
self.assertTrue(line, "Timesheet should be created")
def test_set_negative_project_restriction_days(self):
with self.assertRaises(ValidationError):
self.project.timesheet_restriction_days = -1
self.assertEqual(self.project.timesheet_restriction_days, 0)
def test_set_negative_config_restriction_days(self):
ConfigSettings = self.env["res.config.settings"].create({})
ConfigSettings.timesheet_restriction_days = -1
ConfigSettings._onchange_timesheet_restriction_days()
ConfigSettings.execute()
self.assertEqual(ConfigSettings.timesheet_restriction_days, 0)
def test_global_restriction_without_project_flag(self):
self.project.use_timesheet_restriction = False
self.config.timesheet_restriction_days = 1
self.config.execute()
# Yesterday (1) it is allowed, the day before yesterday (2)
# it is prohibited
self.assertTrue(self._create_line(-1))
with self.assertRaises(ValidationError):
self._create_line(-2)
def test_zero_day_restriction_past_and_future(self):
self.config.timesheet_restriction_days = 0
self.config.execute()
# yesterday
with self.assertRaises(ValidationError):
self._create_line(-1)
# tomorrow
with self.assertRaises(ValidationError):
self._create_line(1)
def test_validate_negative_allowed_days(self):
line = self._create_line(0)
with self.assertRaises(ValidationError):
line._validate_timesheet_date(line, -5)
def test_validate_invalid_date_value(self):
vals = dict(self._base_vals)
vals["date"] = False
dummy = self.env["account.analytic.line"].new(vals)
with self.assertRaises(ValidationError):
dummy._validate_timesheet_date(dummy, 1)

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="edit_project" model="ir.ui.view">
<field name="name">project.project.view.form</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project" />
<field name="arch" type="xml">
<xpath expr="//group[@name='group_time_managment']" position="inside">
<div>
<label
for="use_timesheet_restriction"
string="Use timesheet restriction"
/>
<field name="use_timesheet_restriction" />
</div>
<div attrs="{'invisible': [('use_timesheet_restriction', '=', False)]}">
<label
for="timesheet_restriction_days"
string="Timesheet Restriction (days):"
/>
<field name="timesheet_restriction_days" />
</div>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="hr_timesheet.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath
expr="//div[@data-key='hr_timesheet']/div[hasclass('o_settings_container')]"
position="inside"
>
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="use_timesheet_restriction" />
</div>
<div class="o_setting_right_pane">
<label
for="use_timesheet_restriction"
string="Auto-enable Timesheet restriction"
/>
<div class="content-group">
<div class="mt16">
</div>
</div>
</div>
<div
class="o_setting_right_pane"
attrs="{'invisible': [('use_timesheet_restriction', '=', False)]}"
>
<label
for="timesheet_restriction_days"
string="Timesheet Restriction (days)"
/>
<div class="content-group">
<div class="mt16">
<field name="timesheet_restriction_days" />
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,42 @@
[project]
name = "odoo-bringout-oca-timesheet-hr_timesheet_time_restriction"
version = "16.0.0"
description = "HR Timesheet Sheet Restriction - Restrictions on the creation of time sheets for past dates"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-hr_timesheet>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["hr_timesheet_time_restriction"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]