[MIG] hr_effective_attendance_period: migrate to 18.0

This commit is contained in:
Stéphan Sainléger
2026-05-05 17:24:48 +02:00
parent 494f2b4926
commit 69bc951b3e
9 changed files with 325 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
==============================
hr_effective_attendance_period
==============================
Provide indication that attendance periods are effectively worked or not.
Description
===========
This module adds a boolean field on working time attendance lines to mark
which periods are effectively worked by employees. It provides utility methods
on the resource calendar to determine:
- Whether a day is a working day
- Whether a day is a full working day (both morning and afternoon)
- Whether all attendances of a day are effectively worked (no leaves)
Installation
============
Use Odoo normal module installation procedure to install
``hr_effective_attendance_period``.
This module depends on:
* ``base`` (Odoo core)
* ``resource`` (Odoo core)
Configuration
=============
Go to ``Configuration > Technical > Resource > Working Times`` and create
Working Times for your company. For each attendance line, check
``Effective Attendance Period`` to indicate which periods are effectively
attended by employees.
Known issues / Roadmap
======================
None yet.
Bug Tracker
===========
Bugs are tracked on `our issues website <https://github.com/elabore-coop/hr-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smash it by providing detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Stéphan Sainléger
Funders
-------
The development of this module has been financially supported by:
* Elabore (https://elabore.coop)
Maintainer
----------
This module is maintained by Elabore.

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,24 @@
# Copyright 2023 Stéphan Sainléger (Elabore)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "hr_effective_attendance_period",
"version": "18.0.1.0.0",
"author": "Elabore",
"website": "https://git.elabore.coop/elabore/hr-tools",
"maintainer": "Stéphan Sainléger",
"license": "AGPL-3",
"category": "Tools",
"summary": "Indicate whether attendance periods are effectively worked",
# any module necessary for this one to work correctly
"depends": [
"base",
"resource",
],
"data": [
"views/resource_views.xml",
],
"installable": True,
"auto_install": False,
"application": False,
}

View File

@@ -0,0 +1 @@
This directory should contain the *.po for Odoo translation.

View File

@@ -0,0 +1,49 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * hr_effective_attendance_period
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-24 21:50+0000\n"
"PO-Revision-Date: 2023-01-24 21:50+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: hr_effective_attendance_period
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar__display_name
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar_attendance__display_name
msgid "Display Name"
msgstr "Nom affiché"
#. module: hr_effective_attendance_period
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar_attendance__effective_attendance_period
msgid "Effective Attendance Period"
msgstr "Présence effective"
#. module: hr_effective_attendance_period
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar__id
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar_attendance__id
msgid "ID"
msgstr ""
#. module: hr_effective_attendance_period
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar____last_update
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar_attendance____last_update
msgid "Last Modified on"
msgstr "Dernière modification le"
#. module: hr_effective_attendance_period
#: model:ir.model,name:hr_effective_attendance_period.model_resource_calendar
msgid "Resource Working Time"
msgstr "Temps de travail de la ressource"
#. module: hr_effective_attendance_period
#: model:ir.model,name:hr_effective_attendance_period.model_resource_calendar_attendance
msgid "Work Detail"
msgstr "Détail du travail"

View File

@@ -0,0 +1,49 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * hr_effective_attendance_period
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-24 21:49+0000\n"
"PO-Revision-Date: 2023-01-24 21:49+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: hr_effective_attendance_period
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar__display_name
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar_attendance__display_name
msgid "Display Name"
msgstr ""
#. module: hr_effective_attendance_period
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar_attendance__effective_attendance_period
msgid "Effective Attendance Period"
msgstr ""
#. module: hr_effective_attendance_period
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar__id
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar_attendance__id
msgid "ID"
msgstr ""
#. module: hr_effective_attendance_period
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar____last_update
#: model:ir.model.fields,field_description:hr_effective_attendance_period.field_resource_calendar_attendance____last_update
msgid "Last Modified on"
msgstr ""
#. module: hr_effective_attendance_period
#: model:ir.model,name:hr_effective_attendance_period.model_resource_calendar
msgid "Resource Working Time"
msgstr ""
#. module: hr_effective_attendance_period
#: model:ir.model,name:hr_effective_attendance_period.model_resource_calendar_attendance
msgid "Work Detail"
msgstr ""

View File

@@ -0,0 +1 @@
from . import resource

View File

@@ -0,0 +1,97 @@
import math
from datetime import timedelta
import pytz
from odoo import fields, models
class ResourceCalendarAttendance(models.Model):
_inherit = "resource.calendar.attendance"
effective_attendance_period = fields.Boolean(store=True)
class ResourceCalendar(models.Model):
_inherit = "resource.calendar"
def _retrieve_day_matching_attendances(self, day):
domain = [
("calendar_id", "=", self.id),
("dayofweek", "=", day.weekday()),
("effective_attendance_period", "=", True),
]
if self.two_weeks_calendar:
# Employee has Even/Odd weekly calendar
week_type = 1 if int(math.floor((day.toordinal() - 1) / 7) % 2) else 0
domain.append(("week_type", "=", week_type))
result = self.env["resource.calendar.attendance"].search(domain)
return result
def is_working_day(self, day):
day_attendances = self._retrieve_day_matching_attendances(day)
if len(day_attendances) == 0:
# This day of the week is not supposed to be a working day
return False
else:
# This day of the week is supposed to be a working day
return True
def is_full_working_day(self, day):
day_attendances = self._retrieve_day_matching_attendances(day)
morning_worked = (
len(day_attendances.filtered(lambda x: x.day_period == "morning")) > 0
)
afternoon_worked = (
len(day_attendances.filtered(lambda x: x.day_period == "afternoon")) > 0
)
return morning_worked and afternoon_worked
def _compute_datetime_in_utc_tz(self, date):
user_tz = pytz.timezone(self.env.context.get("tz", "utc") or "utc")
return user_tz.localize(date).astimezone(pytz.utc)
def _is_worked_attendance(self, resource, day, attendance):
attendance_start = fields.Datetime.to_datetime(day.date()) + timedelta(
hours=attendance.hour_from
)
attendance_end = fields.Datetime.to_datetime(day.date()) + timedelta(
hours=attendance.hour_to
)
utc_start = self._compute_datetime_in_utc_tz(attendance_start)
utc_end = self._compute_datetime_in_utc_tz(attendance_end)
resource_leaves = (
self.env["resource.calendar.leaves"]
.sudo()
.search(
[
("company_id", "=", resource.company_id.id),
("date_from", "<=", utc_start),
("date_to", ">=", utc_end),
"|",
("resource_id", "=", resource.id),
("resource_id", "=", None),
]
)
)
if resource_leaves:
return False
else:
# a part or the whole attendance is worked
return True
def is_worked_day(self, resource, day):
day_attendances = self._retrieve_day_matching_attendances(day)
# If at least one attendance is worked, return True
for attendance in day_attendances:
if self._is_worked_attendance(resource, day, attendance):
return True
return False
def all_attendances_worked(self, resource, day):
day_attendances = self._retrieve_day_matching_attendances(day)
# If at least one attendance is not worked, return False
for attendance in day_attendances:
if not self._is_worked_attendance(resource, day, attendance):
return False
return True

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record
id="view_resource_calendar_attendance_tree_inherit_effective_attendance"
model="ir.ui.view"
>
<field name="name">resource.calendar.attendance.view.list.inherit</field>
<field name="model">resource.calendar.attendance</field>
<field
name="inherit_id"
ref="resource.view_resource_calendar_attendance_tree"
/>
<field name="arch" type="xml">
<xpath expr="//field[@name='week_type']" position="after">
<field name="effective_attendance_period" />
</xpath>
</field>
</record>
<record
id="view_resource_calendar_attendance_form_inherit_effective_attendance"
model="ir.ui.view"
>
<field name="name">resource.calendar.attendance.view.list.inherit</field>
<field name="model">resource.calendar.attendance</field>
<field
name="inherit_id"
ref="resource.view_resource_calendar_attendance_form"
/>
<field name="arch" type="xml">
<xpath expr="//field[@name='day_period']" position="after">
<field name="effective_attendance_period" />
</xpath>
</field>
</record>
</odoo>