Skip to content
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

Open
wants to merge 2 commits into
base: 17.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
78 changes: 78 additions & 0 deletions project_timesheet_holidays_contract/README.rst
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.
Comment on lines +31 to +32

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.


**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.
1 change: 1 addition & 0 deletions project_timesheet_holidays_contract/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
16 changes: 16 additions & 0 deletions project_timesheet_holidays_contract/__manifest__.py
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 ""
1 change: 1 addition & 0 deletions project_timesheet_holidays_contract/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import hr_employee
184 changes: 184 additions & 0 deletions project_timesheet_holidays_contract/models/hr_employee.py
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)

Check warning on line 14 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L14

Added line #L14 was not covered by tests

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()

Check warning on line 21 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L21

Added line #L21 was not covered by tests
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

Choose a reason for hiding this comment

The 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

Choose a reason for hiding this comment

The 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:

  • state == 'close': the contract is considered as expired for the employee
  • state == 'cancel': the contract is considered as cancelled for the employee
  • state == 'open' and kanban_state != 'blocked': the contract is considered as running for the employee
  • state == 'open' and kanban_state == 'blocked': the contract is considered as running for the employee, but will be closed as soon as the end date is reached
  • state == 'draft' and kanban_state != 'done': the contract is not running for the employee
  • state == 'draft' and kanban_state == 'done': the contract is not running for the employee, but will be moved to "open" as soon as the contract's start date is met

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

Check warning on line 48 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L48

Added line #L48 was not covered by tests

# If there are multiple non-cancelled contracts, raise an error
if len(valid_contracts) > 1:
raise ValidationError(_("This period overlaps multiple contracts!"))

Check warning on line 52 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L52

Added line #L52 was not covered by tests

# If no valid contracts remain, call `super()`
return super()._get_calendar(date_from)

Check warning on line 55 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L55

Added line #L55 was not covered by tests

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(

Check warning on line 68 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L68

Added line #L68 was not covered by tests
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)

Check warning on line 77 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L77

Added line #L77 was not covered by tests

result = {}

Check warning on line 79 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L79

Added line #L79 was not covered by tests
for employee in self:

Choose a reason for hiding this comment

The 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(

Check warning on line 83 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L82-L83

Added lines #L82 - L83 were not covered by tests
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

Check warning on line 93 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L93

Added line #L93 was not covered by tests

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(

Check warning on line 101 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L101

Added line #L101 was not covered by tests
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)

Check warning on line 109 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L109

Added line #L109 was not covered by tests

result = {}

Check warning on line 111 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L111

Added line #L111 was not covered by tests
for employee in self:
# Retrieve a calendar specific to the employee
employee_calendar = employee._get_calendar(from_datetime)
result.update(

Check warning on line 115 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L114-L115

Added lines #L114 - L115 were not covered by tests
super(HrEmployee, employee)._get_leave_days_data_batch(
from_datetime=from_datetime,
to_datetime=to_datetime,
calendar=employee_calendar,
domain=domain,
)
)

return result

Check warning on line 124 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L124

Added line #L124 was not covered by tests

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(

Check warning on line 161 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L161

Added line #L161 was not covered by tests
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)

Check warning on line 169 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L169

Added line #L169 was not covered by tests

result = []

Check warning on line 171 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L171

Added line #L171 was not covered by tests
for employee in self:
# Retrieve a calendar specific to the employee
employee_calendar = employee._get_calendar(from_datetime)
result.extend(

Check warning on line 175 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L174-L175

Added lines #L174 - L175 were not covered by tests
super(HrEmployee, employee).list_leaves(
from_datetime=from_datetime,
to_datetime=to_datetime,
calendar=employee_calendar,
domain=domain,
)
)

return result

Check warning on line 184 in project_timesheet_holidays_contract/models/hr_employee.py

View check run for this annotation

Codecov / codecov/patch

project_timesheet_holidays_contract/models/hr_employee.py#L184

Added line #L184 was not covered by tests
3 changes: 3 additions & 0 deletions project_timesheet_holidays_contract/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
2 changes: 2 additions & 0 deletions project_timesheet_holidays_contract/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Stéphane Mangin \<<[email protected]>\>
- Maksym Yankin \<<[email protected]>\>
1 change: 1 addition & 0 deletions project_timesheet_holidays_contract/readme/DESCRIPTION.md
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.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate here a bit?

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