-
-
Notifications
You must be signed in to change notification settings - Fork 785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[17.0][ADD] project_timesheet_holidays_contract
#1290
base: 17.0
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
=================================== | ||
Project Timesheet Holidays Contract | ||
=================================== | ||
|
||
.. | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! This file is generated by oca-gen-addon-readme !! | ||
!! changes will be overwritten. !! | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! source digest: sha256:95a3e09029476c69a57b09b463ee292eddb297be29fdbccbec221324b1845e40 | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
|
||
.. |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%2Fproject-lightgray.png?logo=github | ||
:target: https://github.com/OCA/project/tree/17.0/project_timesheet_holidays_contract | ||
:alt: OCA/project | ||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png | ||
:target: https://translation.odoo-community.org/projects/project-17-0/project-17-0-project_timesheet_holidays_contract | ||
: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/project&target_branch=17.0 | ||
:alt: Try me on Runboat | ||
|
||
|badge1| |badge2| |badge3| |badge4| |badge5| | ||
|
||
Module allows to take into account employee contract along with provided | ||
end date when timesheet on time off. | ||
|
||
**Table of contents** | ||
|
||
.. contents:: | ||
:local: | ||
|
||
Bug Tracker | ||
=========== | ||
|
||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/project/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/project/issues/new?body=module:%20project_timesheet_holidays_contract%0Aversion:%2017.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 | ||
------- | ||
|
||
* Camptocamp | ||
|
||
Contributors | ||
------------ | ||
|
||
- Stéphane Mangin <[email protected]> | ||
- Maksym Yankin <[email protected]> | ||
|
||
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/project <https://github.com/OCA/project/tree/17.0/project_timesheet_holidays_contract>`_ project on GitHub. | ||
|
||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import models |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Copyright 2024 Camptocamp SA | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
{ | ||
"name": "Project Timesheet Holidays Contract", | ||
"summary": "Automatically log employees' leave in timesheets " | ||
"according to their contracts", | ||
"version": "17.0.1.0.0", | ||
"category": "Project", | ||
"author": "Camptocamp, Odoo Community Association (OCA)", | ||
"website": "https://github.com/OCA/project", | ||
"license": "AGPL-3", | ||
"depends": ["project_timesheet_holidays", "hr_contract"], | ||
"data": [], | ||
"installable": True, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Translation of Odoo Server. | ||
# This file contains the translation of the following modules: | ||
# * project_timesheet_holidays_contract | ||
# | ||
msgid "" | ||
msgstr "" | ||
"Project-Id-Version: Odoo Server 17.0+e\n" | ||
"Report-Msgid-Bugs-To: \n" | ||
"POT-Creation-Date: 2024-05-24 09:41+0000\n" | ||
"PO-Revision-Date: 2024-05-24 09:41+0000\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: project_timesheet_holidays_contract | ||
#: model:ir.model,name:project_timesheet_holidays_contract.model_hr_employee | ||
msgid "Employee" | ||
msgstr "" | ||
|
||
#. module: project_timesheet_holidays_contract | ||
#. odoo-python | ||
#: code:addons/project_timesheet_holidays_contract/models/hr_employee.py:0 | ||
#, python-format | ||
msgid "This period overlaps multiple contracts!" | ||
msgstr "" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import hr_employee |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
# Copyright 2024 Camptocamp SA | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
from datetime import datetime | ||
|
||
from odoo import _, fields, models | ||
from odoo.exceptions import ValidationError | ||
|
||
|
||
class HrEmployee(models.Model): | ||
_inherit = "hr.employee" | ||
|
||
def _get_calendar(self, date_from=None): | ||
if not date_from: # no `date_from` => call `super()` | ||
return super()._get_calendar(date_from) | ||
|
||
date_from = ( | ||
date_from if not isinstance(date_from, datetime) else date_from.date() | ||
) | ||
date_to = self.env.context.get("date_to", None) | ||
if date_to is None: | ||
date_to = fields.Date.today() | ||
date_to = date_to if not isinstance(date_to, datetime) else date_to.date() | ||
|
||
contracts = ( | ||
self.sudo() | ||
.with_context(active_test=True) | ||
.env["hr.contract"] | ||
.search( | ||
[ | ||
("employee_id", "=", self.id), | ||
("date_start", "<=", date_from), | ||
"|", | ||
("date_end", ">=", date_to), | ||
("date_end", "=", False), | ||
] | ||
) | ||
) | ||
|
||
# If only one contract, return it regardless of its state | ||
if len(contracts) == 1: | ||
return contracts[0].resource_calendar_id | ||
|
||
# Filter out cancelled contracts if there are multiple contracts | ||
valid_contracts = contracts.filtered(lambda c: c.state != "cancel") | ||
Comment on lines
+39
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So in case there's only one contract that is canceled, it still must be used? 🤨
Comment on lines
+24
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested this w/ Odoo standard code, and I think states are not handled correctly. Contracts can be differentiated in these macro-groups:
This is something that should be taken into account when retrieving the employee's contract, and therefore its calendar. |
||
|
||
# If exactly one non-cancelled contract, return it | ||
if len(valid_contracts) == 1: | ||
return valid_contracts[0].resource_calendar_id | ||
|
||
# If there are multiple non-cancelled contracts, raise an error | ||
if len(valid_contracts) > 1: | ||
raise ValidationError(_("This period overlaps multiple contracts!")) | ||
|
||
# If no valid contracts remain, call `super()` | ||
return super()._get_calendar(date_from) | ||
|
||
def _get_work_days_data_batch( | ||
self, | ||
from_datetime, | ||
to_datetime, | ||
compute_leaves=True, | ||
calendar=None, | ||
domain=None, | ||
): | ||
# OVERRIDE calendar in favor of the one taken from employee's contract | ||
# Exit early if a calendar is already provided | ||
if calendar: | ||
return super()._get_work_days_data_batch( | ||
from_datetime=from_datetime, | ||
to_datetime=to_datetime, | ||
compute_leaves=compute_leaves, | ||
calendar=calendar, | ||
domain=domain, | ||
) | ||
|
||
# Update context on the whole recordset | ||
self = self.with_context(date_to=to_datetime) | ||
|
||
result = {} | ||
for employee in self: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For all these functions, wouldn't grouping employees by calendar improve perf instead of iterating through all the employees? |
||
# Retrieve a calendar specific to the employee | ||
employee_calendar = employee._get_calendar(from_datetime) | ||
result.update( | ||
super(HrEmployee, employee)._get_work_days_data_batch( | ||
from_datetime=from_datetime, | ||
to_datetime=to_datetime, | ||
compute_leaves=compute_leaves, | ||
calendar=employee_calendar, | ||
domain=domain, | ||
) | ||
) | ||
|
||
return result | ||
|
||
def _get_leave_days_data_batch( | ||
self, from_datetime, to_datetime, calendar=None, domain=None | ||
): | ||
# OVERRIDE calendar in favor of the one taken from employee's contract | ||
# Exit early if a calendar is already provided | ||
if calendar: | ||
return super()._get_leave_days_data_batch( | ||
from_datetime=from_datetime, | ||
to_datetime=to_datetime, | ||
calendar=calendar, | ||
domain=domain, | ||
) | ||
|
||
# Update context on the whole recordset | ||
self = self.with_context(date_to=to_datetime) | ||
|
||
result = {} | ||
for employee in self: | ||
# Retrieve a calendar specific to the employee | ||
employee_calendar = employee._get_calendar(from_datetime) | ||
result.update( | ||
super(HrEmployee, employee)._get_leave_days_data_batch( | ||
from_datetime=from_datetime, | ||
to_datetime=to_datetime, | ||
calendar=employee_calendar, | ||
domain=domain, | ||
) | ||
) | ||
|
||
return result | ||
|
||
def list_work_time_per_day( | ||
self, from_datetime, to_datetime, calendar=None, domain=None | ||
): | ||
# OVERRIDE calendar in favor of the one taken from employee's contract | ||
# Exit early if a calendar is already provided | ||
if calendar: | ||
return super().list_work_time_per_day( | ||
from_datetime=from_datetime, | ||
to_datetime=to_datetime, | ||
calendar=calendar, | ||
domain=domain, | ||
) | ||
|
||
# Update context on the whole recordset | ||
self = self.with_context(date_to=to_datetime) | ||
|
||
result = [] | ||
for employee in self: | ||
# Retrieve a calendar specific to the employee | ||
employee_calendar = employee._get_calendar(from_datetime) | ||
result.extend( | ||
super(HrEmployee, employee).list_work_time_per_day( | ||
from_datetime=from_datetime, | ||
to_datetime=to_datetime, | ||
calendar=employee_calendar, | ||
domain=domain, | ||
) | ||
) | ||
|
||
return result | ||
|
||
def list_leaves(self, from_datetime, to_datetime, calendar=None, domain=None): | ||
# OVERRIDE calendar in favor of the one taken from employee's contract | ||
# Exit early if a calendar is already provided | ||
if calendar: | ||
return super().list_leaves( | ||
from_datetime=from_datetime, | ||
to_datetime=to_datetime, | ||
calendar=calendar, | ||
domain=domain, | ||
) | ||
|
||
# Update context on the whole recordset | ||
self = self.with_context(date_to=to_datetime) | ||
|
||
result = [] | ||
for employee in self: | ||
# Retrieve a calendar specific to the employee | ||
employee_calendar = employee._get_calendar(from_datetime) | ||
result.extend( | ||
super(HrEmployee, employee).list_leaves( | ||
from_datetime=from_datetime, | ||
to_datetime=to_datetime, | ||
calendar=employee_calendar, | ||
domain=domain, | ||
) | ||
) | ||
|
||
return result | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[build-system] | ||
requires = ["whool"] | ||
build-backend = "whool.buildapi" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
- Stéphane Mangin \<<[email protected]>\> | ||
- Maksym Yankin \<<[email protected]>\> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Module allows to take into account employee contract along with provided end date when timesheet on time off. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you elaborate here a bit? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO you could develop a bit more how this works, because after reading it twice, I'm still not sure what the module does.