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

[ADD] hr_attendance_overtime_from_work_calendar #139

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions hr_attendance_overtime_from_work_calendar/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
**This file is going to be generated by oca-gen-addon-readme.**

*Manual changes will be overwritten.*

Please provide content in the ``readme`` directory:

* **DESCRIPTION.rst** (required)
* INSTALL.rst (optional)
* CONFIGURE.rst (optional)
* **USAGE.rst** (optional, highly recommended)
* DEVELOP.rst (optional)
* ROADMAP.rst (optional)
* HISTORY.rst (optional, recommended)
* **CONTRIBUTORS.rst** (optional, highly recommended)
* CREDITS.rst (optional)

Content of this README will also be drawn from the addon manifest,
from keys such as name, authors, maintainers, development_status,
and license.

A good, one sentence summary in the manifest is also highly recommended.


Automatic changelog generation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`HISTORY.rst` can be auto generated using `towncrier <https://pypi.org/project/towncrier>`_.

Just put towncrier compatible changelog fragments into `readme/newsfragments`
and the changelog file will be automatically generated and updated when a new fragment is added.

Please refer to `towncrier` documentation to know more.

NOTE: the changelog will be automatically generated when using `/ocabot merge $option`.
If you need to run it manually, refer to `OCA/maintainer-tools README <https://github.com/OCA/maintainer-tools>`_.
2 changes: 2 additions & 0 deletions hr_attendance_overtime_from_work_calendar/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from .hooks import post_init_hook
25 changes: 25 additions & 0 deletions hr_attendance_overtime_from_work_calendar/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2023 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0)

{
"name": "Overtime from work calendars",
"summary": "Calculate overtime records for all work calendar dates",
"version": "15.0.1.0.0",
"development_status": "Alpha",
"category": "Human Resources/Attendances",
"website": "https://github.com/OCA/hr-attendance",
"author": "Hunki Enterprises BV, Odoo Community Association (OCA)",
"maintainers": ["hbrunn"],
"license": "AGPL-3",
"application": False,
"installable": True,
"preloadable": True,
"post_init_hook": "post_init_hook",
"depends": [
"hr_attendance",
],
"data": [
"data/ir_cron.xml",
],
"demo": [],
}
13 changes: 13 additions & 0 deletions hr_attendance_overtime_from_work_calendar/data/ir_cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf8" ?>
<odoo>
<record id="cron_update_overtime" model="ir.cron">
<field name="name">Update overtime</field>
<field name="numbercall">-1</field>
<field name="interval_type">days</field>
<field name="interval_number">1</field>
<field name="nextcall">2023-01-01 00:30</field>
<field name="state">code</field>
<field name="model_id" ref="hr_attendance.model_hr_attendance_overtime" />
<field name="code">model._compute_missing_overtime()</field>
</record>
</odoo>
8 changes: 8 additions & 0 deletions hr_attendance_overtime_from_work_calendar/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright 2023 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0)
from odoo import SUPERUSER_ID, api


def post_init_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
env["hr.attendance"].search([])._update_overtime()
2 changes: 2 additions & 0 deletions hr_attendance_overtime_from_work_calendar/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import hr_attendance
from . import hr_attendance_overtime
71 changes: 71 additions & 0 deletions hr_attendance_overtime_from_work_calendar/models/hr_attendance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright 2023 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0)


from datetime import timedelta

from odoo import models


class HrAttendance(models.Model):
_inherit = "hr.attendance"

def _update_overtime(self, employee_attendance_dates=None):
"""Compute overtime as $worked_hours - $(hours according to work calendar)"""
# TODO: allow to configure this behavior per company
for this in self:
day_start_utc, date = self._get_day_start_and_day(
this.employee_id, this.check_in
)
day_end_utc = day_start_utc + timedelta(days=1)
# TODO: handle cases where an attendance spans multiple local days
worked_hours = sum(
self.search(
[
("employee_id", "=", this.employee_id.id),
("check_in", ">=", day_start_utc),
("check_in", "<", day_end_utc),
]
).mapped("worked_hours")
)

expected_hours = this.employee_id.with_context(
# use hr_public_holidays if installed
exclude_public_holidays=True,
)._get_work_days_data_batch(day_start_utc, day_end_utc)[
this.employee_id.id
][
"hours"
]

# TODO: support company thresholds
overtime_hours = worked_hours - expected_hours

overtime = (
self.env["hr.attendance.overtime"]
.sudo()
.search(
[
("employee_id", "=", this.employee_id.id),
("date", "=", date),
("adjustment", "=", False),
]
)
)

if not overtime:
self.env["hr.attendance.overtime"].sudo().create(
{
"employee_id": this.employee_id.id,
"date": date,
"duration": overtime_hours,
"duration_real": overtime_hours,
}
)
else:
overtime.write(
{
"duration": overtime_hours,
"duration_real": overtime_hours,
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2023 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0)


import logging
from datetime import date, datetime, time, timedelta

from pytz import timezone, utc

from odoo import models

_logger = logging.getLogger(__name__)


class HrAttendanceOvertime(models.Model):
_inherit = "hr.attendance.overtime"

def _compute_missing_overtime(self, companies=None, end_date=None):
"""Create overtime entries from company.overtime_start_date to end date or yesterday"""
end_date = end_date or date.today() - timedelta(days=1)
for company in (companies or self.env.company).filtered("overtime_start_date"):
for employee in self.env["hr.employee"].search(
[("company_id", "=", company.id)]
):
self._compute_missing_overtime_employee(
employee, company.overtime_start_date, end_date
)

def _compute_missing_overtime_employee(self, employee, start_date, end_date):
"""Create overtime entries for employee from start_date to end_date"""
employee_timezone = timezone(employee.tz)
existing_overtime = set(
self.env["hr.attendance.overtime"]
.search(
[
("date", ">=", start_date),
("date", "<=", end_date),
("employee_id", "=", employee.id),
("adjustment", "=", False),
]
)
.mapped("date")
)
overtime_date = start_date
while overtime_date <= end_date:
if overtime_date in existing_overtime:
overtime_date += timedelta(days=1)
continue
_logger.info(
"calculating overtime for #%d on %s", employee.id, overtime_date
)
employee_date = (
employee_timezone.localize(datetime.combine(overtime_date, time()))
.astimezone(utc)
.replace(tzinfo=None)
)

self.env["hr.attendance"].new(
{
"employee_id": employee.id,
"check_in": employee_date,
"check_out": employee_date,
}
)._update_overtime()
overtime_date += timedelta(days=1)
17 changes: 17 additions & 0 deletions hr_attendance_overtime_from_work_calendar/readme/CONFIGURE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.. Audience: users that will configure Odoo to make this module work.

Purpose: help them configure Odoo appropriately.

⚠️ Cautions:

- Do not assume the user knows how to find the menus. Guide them there.

⛔ REMOVE THIS FILE if the module works out of the box and there is nothing to configure.

To configure this module, you need to:

#. Go to ...

.. figure:: ../static/description/image.png
:alt: alternative description
:width: 600 px
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.. Audience: everybody.

Purpose:

* Firstname Lastname <email.address@example.org> (optional company website url)
* Second Person <second.person@example.org> (optional company website url)
* Moduon Employee (`Moduon <https://www.moduon.team/>`__)
18 changes: 18 additions & 0 deletions hr_attendance_overtime_from_work_calendar/readme/CREDITS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.. Audience: everybody.

Purpose: thank financial contributors.

⚠️ Cautions:

- DO NOT include contributors. These go into ./CONTRIBUTORS.rst.
- DO NOT include authors. These go in the "author" key in ../__manifest__.py.
- DO NOT include maintainers. These go in the "maintainers" key in ../__manifest__.py.
- Only include other companies or persons that have financed the development
of this module. Maybe through direct funding, crowdfunding, etc.

⛔ REMOVE THIS FILE if there are no other financial supporters.

The development of this module has been financially supported by:

* Company 1 name
* Company 2 name
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This module replaces Odoo core's overtime calculation with one that satisfies the expectation::

overtime = (time recorded in hr.attendance records) - (expected working hours as per working calendar)

for every given day.
15 changes: 15 additions & 0 deletions hr_attendance_overtime_from_work_calendar/readme/INSTALL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.. Audience: system administrators.

Purpose: help them provide this module's technical requirements.

⚠️ Cautions:

- DO NOT specify Python or Odoo dependencies. These are specified in
../__manifest__.py and Odoo will raise an error automatically with a
specific message if one of these is missing.

⛔ REMOVE THIS FILE if there are no special installation instructions.

To install this module, you need to:

#. Do this ...
31 changes: 31 additions & 0 deletions hr_attendance_overtime_from_work_calendar/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.. Audience: end users, functional reviewers.

Purpose: help them use this module.

⚠️ Cautions:

- Required section.

- This section implies that somebody (maybe another user with more privileges)
already followed any instructions in ./CONFIGURE.rst.

- Guide the user, step by step, to see the module effect in Odoo.

- DO NOT ASSUME that the user knows where to find stuff or technical terms.

Bad example:
#. Create a new partner.

Good example:
#. Go to the "Contacts" app.
#. Create a new contact.

- IT MUST NOT contain reStructuredText sections, only body text (paragraphs, lists,
tables, etc).

If you need a more elaborate structure to explain the addon, please create a
Sphinx documentation (which may include this file as a "quick start" section).

To use this module, you need to:

#. Go to ...
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_overtime_calculation
Loading