From 06c8094eca516985fe2f487e3ab469d998f9edd9 Mon Sep 17 00:00:00 2001 From: hugues de keyzer Date: Tue, 27 Jan 2026 14:34:29 +0100 Subject: [PATCH] [ADD] hr_work_entry_contract_attendance_oca add new module hr_work_entry_contract_attendance_oca. --- .../README.rst | 111 +++++ .../__init__.py | 5 + .../__manifest__.py | 22 + .../i18n/fr.po | 86 ++++ .../hr_work_entry_contract_attendance_oca.pot | 81 ++++ .../models/__init__.py | 9 + .../models/hr_attendance.py | 11 + .../models/hr_contract.py | 113 +++++ .../models/hr_work_entry.py | 11 + .../models/res_company.py | 18 + .../models/res_config_settings.py | 13 + .../pyproject.toml | 3 + .../readme/CONFIGURE.md | 1 + .../readme/CONTRIBUTORS.md | 2 + .../readme/DESCRIPTION.md | 1 + .../readme/ROADMAP.md | 4 + .../readme/USAGE.md | 8 + .../static/description/index.html | 459 ++++++++++++++++++ .../tests/__init__.py | 5 + ...t_hr_work_entry_contract_attendance_oca.py | 25 + .../views/hr_contract_views.xml | 22 + .../views/hr_work_entry_views.xml | 33 ++ .../views/res_config_settings_views.xml | 27 ++ 23 files changed, 1070 insertions(+) create mode 100644 hr_work_entry_contract_attendance_oca/README.rst create mode 100644 hr_work_entry_contract_attendance_oca/__init__.py create mode 100644 hr_work_entry_contract_attendance_oca/__manifest__.py create mode 100644 hr_work_entry_contract_attendance_oca/i18n/fr.po create mode 100644 hr_work_entry_contract_attendance_oca/i18n/hr_work_entry_contract_attendance_oca.pot create mode 100644 hr_work_entry_contract_attendance_oca/models/__init__.py create mode 100644 hr_work_entry_contract_attendance_oca/models/hr_attendance.py create mode 100644 hr_work_entry_contract_attendance_oca/models/hr_contract.py create mode 100644 hr_work_entry_contract_attendance_oca/models/hr_work_entry.py create mode 100644 hr_work_entry_contract_attendance_oca/models/res_company.py create mode 100644 hr_work_entry_contract_attendance_oca/models/res_config_settings.py create mode 100644 hr_work_entry_contract_attendance_oca/pyproject.toml create mode 100644 hr_work_entry_contract_attendance_oca/readme/CONFIGURE.md create mode 100644 hr_work_entry_contract_attendance_oca/readme/CONTRIBUTORS.md create mode 100644 hr_work_entry_contract_attendance_oca/readme/DESCRIPTION.md create mode 100644 hr_work_entry_contract_attendance_oca/readme/ROADMAP.md create mode 100644 hr_work_entry_contract_attendance_oca/readme/USAGE.md create mode 100644 hr_work_entry_contract_attendance_oca/static/description/index.html create mode 100644 hr_work_entry_contract_attendance_oca/tests/__init__.py create mode 100644 hr_work_entry_contract_attendance_oca/tests/test_hr_work_entry_contract_attendance_oca.py create mode 100644 hr_work_entry_contract_attendance_oca/views/hr_contract_views.xml create mode 100644 hr_work_entry_contract_attendance_oca/views/hr_work_entry_views.xml create mode 100644 hr_work_entry_contract_attendance_oca/views/res_config_settings_views.xml diff --git a/hr_work_entry_contract_attendance_oca/README.rst b/hr_work_entry_contract_attendance_oca/README.rst new file mode 100644 index 00000000000..2b1ec3bf8b3 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/README.rst @@ -0,0 +1,111 @@ +=================================== +Work Entries from Attendances (OCA) +=================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:d3209c999db5377db1ecf1c4b76982d3adb21492ecc2f9e4bd65c50ac8033823 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fhr-lightgray.png?logo=github + :target: https://github.com/OCA/hr/tree/18.0/hr_work_entry_contract_attendance_oca + :alt: OCA/hr +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/hr-18-0/hr-18-0-hr_work_entry_contract_attendance_oca + :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/hr&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Generate work entries from attendances. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +The work entry type to use for work entries generated from attendances +can be configured in the Employees settings, Work Entries section. + +Usage +===== + +Currently, work entries for attendances are not created automatically +(see `roadmap <#known-issues-roadmap>`__). Instead, they must be +generated by using the "Regenerate Work Entries" wizard from the work +entry views. + +Separate work entries are created for each attendance. The generated +work entries are not restricted to the working schedule: only the lunch +breaks are removed (if they are present in the working schedule). + +.. note:: + + The ``hr_work_entry_usability`` module is useful to be able to access + work entries more easily, as it adds menu entries for them. + +Known issues / Roadmap +====================== + +These features are not yet implemented, but would be quite useful: + +- Generate work entries automatically when attendances are + created/modified. +- Update/delete (draft) work entries when attendances are + modified/deleted. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Coop IT Easy SC + +Contributors +------------ + +- `Coop IT Easy SC `__: + + - hugues de keyzer + +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/hr `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_work_entry_contract_attendance_oca/__init__.py b/hr_work_entry_contract_attendance_oca/__init__.py new file mode 100644 index 00000000000..c58124864e1 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from . import models diff --git a/hr_work_entry_contract_attendance_oca/__manifest__.py b/hr_work_entry_contract_attendance_oca/__manifest__.py new file mode 100644 index 00000000000..b90166f7db8 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/__manifest__.py @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +{ + "name": "Work Entries from Attendances (OCA)", + "summary": "Generate work entries from attendances", + "version": "18.0.1.0.0", + "category": "Human Resources/Employees", + "website": "https://github.com/OCA/hr", + "author": "Coop IT Easy SC, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": [ + "hr_attendance", + "hr_work_entry_contract", + ], + "data": [ + "views/hr_contract_views.xml", + "views/hr_work_entry_views.xml", + "views/res_config_settings_views.xml", + ], +} diff --git a/hr_work_entry_contract_attendance_oca/i18n/fr.po b/hr_work_entry_contract_attendance_oca/i18n/fr.po new file mode 100644 index 00000000000..91be8eeca47 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/i18n/fr.po @@ -0,0 +1,86 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_work_entry_contract_attendance_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: fr\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" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields,help:hr_work_entry_contract_attendance_oca.field_hr_contract__work_entry_source +msgid "" +"\n" +" Defines the source for work entries generation\n" +"\n" +" Working Schedule: Work entries will be generated from the working " +"hours below.\n" +" Attendances: Work entries will be generated from the employee's " +"attendances. (requires Attendance app)\n" +" Planning: Work entries will be generated from the employee's " +"planning. (requires Planning app)\n" +" " +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_hr_attendance +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_hr_work_entry__attendance_id +msgid "Attendance" +msgstr "Présence" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_res_company__attendance_hr_work_entry_type_id +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_res_config_settings__attendance_hr_work_entry_type_id +#: model_terms:ir.ui.view,arch_db:hr_work_entry_contract_attendance_oca.res_config_settings_view_form +msgid "Attendance Work Entry Type" +msgstr "Type de prestation pour les présences" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields.selection,name:hr_work_entry_contract_attendance_oca.selection__hr_contract__work_entry_source__attendance_oca +msgid "Attendances" +msgstr "Présences" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_res_company +msgid "Companies" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_hr_contract +msgid "Employee Contract" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_hr_work_entry +msgid "HR Work Entry" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_hr_attendance__work_entry_ids +#: model_terms:ir.ui.view,arch_db:hr_work_entry_contract_attendance_oca.res_config_settings_view_form +msgid "Work Entries" +msgstr "Prestations" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_hr_contract__work_entry_source +msgid "Work Entry Source" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model_terms:ir.ui.view,arch_db:hr_work_entry_contract_attendance_oca.res_config_settings_view_form +msgid "Work entry type to use for work entries corresponding to an attendance." +msgstr "" +"Type de prestation à utiliser pour les prestations correspondant à une " +"présence." diff --git a/hr_work_entry_contract_attendance_oca/i18n/hr_work_entry_contract_attendance_oca.pot b/hr_work_entry_contract_attendance_oca/i18n/hr_work_entry_contract_attendance_oca.pot new file mode 100644 index 00000000000..f60f29e946a --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/i18n/hr_work_entry_contract_attendance_oca.pot @@ -0,0 +1,81 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_work_entry_contract_attendance_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.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_work_entry_contract_attendance_oca +#: model:ir.model.fields,help:hr_work_entry_contract_attendance_oca.field_hr_contract__work_entry_source +msgid "" +"\n" +" Defines the source for work entries generation\n" +"\n" +" Working Schedule: Work entries will be generated from the working hours below.\n" +" Attendances: Work entries will be generated from the employee's attendances. (requires Attendance app)\n" +" Planning: Work entries will be generated from the employee's planning. (requires Planning app)\n" +" " +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_hr_attendance +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_hr_work_entry__attendance_id +msgid "Attendance" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_res_company__attendance_hr_work_entry_type_id +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_res_config_settings__attendance_hr_work_entry_type_id +#: model_terms:ir.ui.view,arch_db:hr_work_entry_contract_attendance_oca.res_config_settings_view_form +msgid "Attendance Work Entry Type" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields.selection,name:hr_work_entry_contract_attendance_oca.selection__hr_contract__work_entry_source__attendance_oca +msgid "Attendances" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_res_company +msgid "Companies" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_hr_contract +msgid "Employee Contract" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model,name:hr_work_entry_contract_attendance_oca.model_hr_work_entry +msgid "HR Work Entry" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_hr_attendance__work_entry_ids +#: model_terms:ir.ui.view,arch_db:hr_work_entry_contract_attendance_oca.res_config_settings_view_form +msgid "Work Entries" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model:ir.model.fields,field_description:hr_work_entry_contract_attendance_oca.field_hr_contract__work_entry_source +msgid "Work Entry Source" +msgstr "" + +#. module: hr_work_entry_contract_attendance_oca +#: model_terms:ir.ui.view,arch_db:hr_work_entry_contract_attendance_oca.res_config_settings_view_form +msgid "" +"Work entry type to use for work entries corresponding to an attendance." +msgstr "" diff --git a/hr_work_entry_contract_attendance_oca/models/__init__.py b/hr_work_entry_contract_attendance_oca/models/__init__.py new file mode 100644 index 00000000000..0a0af866a0f --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/models/__init__.py @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from . import hr_attendance +from . import hr_contract +from . import hr_work_entry +from . import res_company +from . import res_config_settings diff --git a/hr_work_entry_contract_attendance_oca/models/hr_attendance.py b/hr_work_entry_contract_attendance_oca/models/hr_attendance.py new file mode 100644 index 00000000000..5fbd6533de5 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/models/hr_attendance.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import fields, models + + +class HrAttendance(models.Model): + _inherit = "hr.attendance" + + work_entry_ids = fields.One2many("hr.work.entry", "attendance_id", "Work Entries") diff --git a/hr_work_entry_contract_attendance_oca/models/hr_contract.py b/hr_work_entry_contract_attendance_oca/models/hr_contract.py new file mode 100644 index 00000000000..2adb2935e81 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/models/hr_contract.py @@ -0,0 +1,113 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from collections import defaultdict + +import pytz + +from odoo import api, fields, models + +from odoo.addons.hr_work_entry_contract.models.hr_work_intervals import WorkIntervals + + +class HrContract(models.Model): + _inherit = "hr.contract" + + work_entry_source = fields.Selection( + selection_add=[("attendance_oca", "Attendances")], + ondelete={"attendance_oca": "set default"}, + ) + + def _get_hr_attendances(self, start_dt, end_dt): + return self.env["hr.attendance"].search( + [ + ("employee_id", "=", self.employee_id.id), + "|", + # attendances fully contained in the interval + "&", + ("check_in", ">=", start_dt), + ("check_out", "<=", end_dt), + "|", + # attendances overlapping the start of the interval + "&", + ("check_in", "<", start_dt), + ("check_out", ">", start_dt), + # attendances overlapping the end of the interval + "&", + ("check_in", "<", end_dt), + ("check_out", ">", end_dt), + ] + ) + + @api.model + def _hr_attendances_to_work_intervals( + self, hr_attendances, default_check_out_dt=None + ): + intervals = [] + # the dates in the intervals in contract_intervals have a + # timezone, while the check_in and check_out fields of hr.attendance + # are naive utc datetime values. the returned intervals must contain + # dates with a timezone. + for hr_attendance in hr_attendances: + intervals.append( + ( + pytz.utc.localize(hr_attendance.check_in), + pytz.utc.localize(hr_attendance.check_out or default_check_out_dt), + hr_attendance, + ) + ) + return WorkIntervals(intervals) + + def _get_hr_attendance_contract_intervals(self, start_dt, end_dt, only_lunch=False): + # this is the same code as _get_attendance_intervals() from + # hr_work_entry_contract, except that the work_entry_source value is + # not checked, and there is an option to return only lunch break + # intervals. + employees_by_calendar = defaultdict(lambda: self.env["hr.employee"]) + for contract in self: + employees_by_calendar[contract.resource_calendar_id] |= contract.employee_id + result = dict() + for calendar, employees in employees_by_calendar.items(): + result.update( + calendar._attendance_intervals_batch( + start_dt, + end_dt, + resources=employees.resource_id, + tz=pytz.timezone(calendar.tz), + lunch=only_lunch, + ) + ) + return result + + def _get_attendance_intervals(self, start_dt, end_dt): + result = super()._get_attendance_intervals(start_dt, end_dt) + contracts = self.filtered(lambda c: c.work_entry_source == "attendance_oca") + lunch_contract_intervals = contracts._get_hr_attendance_contract_intervals( + start_dt, end_dt, only_lunch=True + ) + for contract in contracts: + hr_attendances = contract._get_hr_attendances(start_dt, end_dt) + hr_attendance_intervals = self._hr_attendances_to_work_intervals( + hr_attendances + ) + # difference with the attendances first return the attendance + # intervals minus the lunch break intervals from the contracts, but + # still linked to the hr.attendance record. + resource_id = contract.employee_id.resource_id.id + result[resource_id] = ( + hr_attendance_intervals - lunch_contract_intervals[resource_id] + ) + return result + + def _get_interval_work_entry_type(self, interval): + if self.work_entry_source == "attendance_oca": + return self.env.company.attendance_hr_work_entry_type_id + return super()._get_interval_work_entry_type(interval) + + def _get_more_vals_attendance_interval(self, interval): + result = super()._get_more_vals_attendance_interval(interval) + if self.work_entry_source == "attendance_oca": + if interval[2]._name == "hr.attendance": + result += [("attendance_id", interval[2].id)] + return result diff --git a/hr_work_entry_contract_attendance_oca/models/hr_work_entry.py b/hr_work_entry_contract_attendance_oca/models/hr_work_entry.py new file mode 100644 index 00000000000..52f62c52b59 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/models/hr_work_entry.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import fields, models + + +class HrWorkEntry(models.Model): + _inherit = "hr.work.entry" + + attendance_id = fields.Many2one("hr.attendance", "Attendance") diff --git a/hr_work_entry_contract_attendance_oca/models/res_company.py b/hr_work_entry_contract_attendance_oca/models/res_company.py new file mode 100644 index 00000000000..a921cc3d607 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/models/res_company.py @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + def _get_default_attendance_hr_work_entry_type(self): + return self.env.ref("hr_work_entry.work_entry_type_attendance") + + attendance_hr_work_entry_type_id = fields.Many2one( + "hr.work.entry.type", + "Attendance Work Entry Type", + default=_get_default_attendance_hr_work_entry_type, + ) diff --git a/hr_work_entry_contract_attendance_oca/models/res_config_settings.py b/hr_work_entry_contract_attendance_oca/models/res_config_settings.py new file mode 100644 index 00000000000..7e4708aad21 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/models/res_config_settings.py @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + attendance_hr_work_entry_type_id = fields.Many2one( + related="company_id.attendance_hr_work_entry_type_id", readonly=False + ) diff --git a/hr_work_entry_contract_attendance_oca/pyproject.toml b/hr_work_entry_contract_attendance_oca/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/hr_work_entry_contract_attendance_oca/readme/CONFIGURE.md b/hr_work_entry_contract_attendance_oca/readme/CONFIGURE.md new file mode 100644 index 00000000000..e67d637ae8d --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/readme/CONFIGURE.md @@ -0,0 +1 @@ +The work entry type to use for work entries generated from attendances can be configured in the Employees settings, Work Entries section. diff --git a/hr_work_entry_contract_attendance_oca/readme/CONTRIBUTORS.md b/hr_work_entry_contract_attendance_oca/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..c8cb5ac629f --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +* [Coop IT Easy SC](https://coopiteasy.be): + * hugues de keyzer diff --git a/hr_work_entry_contract_attendance_oca/readme/DESCRIPTION.md b/hr_work_entry_contract_attendance_oca/readme/DESCRIPTION.md new file mode 100644 index 00000000000..5c46117ae53 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/readme/DESCRIPTION.md @@ -0,0 +1 @@ +Generate work entries from attendances. diff --git a/hr_work_entry_contract_attendance_oca/readme/ROADMAP.md b/hr_work_entry_contract_attendance_oca/readme/ROADMAP.md new file mode 100644 index 00000000000..6e031e213a2 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/readme/ROADMAP.md @@ -0,0 +1,4 @@ +These features are not yet implemented, but would be quite useful: + +* Generate work entries automatically when attendances are created/modified. +* Update/delete (draft) work entries when attendances are modified/deleted. diff --git a/hr_work_entry_contract_attendance_oca/readme/USAGE.md b/hr_work_entry_contract_attendance_oca/readme/USAGE.md new file mode 100644 index 00000000000..3371b4bee1a --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/readme/USAGE.md @@ -0,0 +1,8 @@ +Currently, work entries for attendances are not created automatically (see [roadmap](#known-issues-roadmap)). +Instead, they must be generated by using the "Regenerate Work Entries" wizard from the work entry views. + +Separate work entries are created for each attendance. +The generated work entries are not restricted to the working schedule: only the lunch breaks are removed (if they are present in the working schedule). + +> [!NOTE] +> The `hr_work_entry_usability` module is useful to be able to access work entries more easily, as it adds menu entries for them. diff --git a/hr_work_entry_contract_attendance_oca/static/description/index.html b/hr_work_entry_contract_attendance_oca/static/description/index.html new file mode 100644 index 00000000000..4f8ea1424ca --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/static/description/index.html @@ -0,0 +1,459 @@ + + + + + +Work Entries from Attendances (OCA) + + + +
+

Work Entries from Attendances (OCA)

+ + +

Beta License: AGPL-3 OCA/hr Translate me on Weblate Try me on Runboat

+

Generate work entries from attendances.

+

Table of contents

+ +
+

Configuration

+

The work entry type to use for work entries generated from attendances +can be configured in the Employees settings, Work Entries section.

+
+
+

Usage

+

Currently, work entries for attendances are not created automatically +(see roadmap). Instead, they must be +generated by using the “Regenerate Work Entries” wizard from the work +entry views.

+

Separate work entries are created for each attendance. The generated +work entries are not restricted to the working schedule: only the lunch +breaks are removed (if they are present in the working schedule).

+
+

Note

+

The hr_work_entry_usability module is useful to be able to access +work entries more easily, as it adds menu entries for them.

+
+
+
+

Known issues / Roadmap

+

These features are not yet implemented, but would be quite useful:

+
    +
  • Generate work entries automatically when attendances are +created/modified.
  • +
  • Update/delete (draft) work entries when attendances are +modified/deleted.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub 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.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Coop IT Easy SC
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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/hr project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/hr_work_entry_contract_attendance_oca/tests/__init__.py b/hr_work_entry_contract_attendance_oca/tests/__init__.py new file mode 100644 index 00000000000..ee1fc6bee1c --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/tests/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from . import test_hr_work_entry_contract_attendance_oca diff --git a/hr_work_entry_contract_attendance_oca/tests/test_hr_work_entry_contract_attendance_oca.py b/hr_work_entry_contract_attendance_oca/tests/test_hr_work_entry_contract_attendance_oca.py new file mode 100644 index 00000000000..cfde07884a4 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/tests/test_hr_work_entry_contract_attendance_oca.py @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2026 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo.tests.common import TransactionCase + + +class TestHRWorkEntryContractAttendanceOCA(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + # TODO: test that a work entry is created when an attendance (with + # check_out set) is created + + # TODO: test that a work entry is created when an attendance with + # check_out not set is modified by setting its check_out field + + # TODO: test that a work entry is updated when an attendance is modified + + # TODO: test that modifying an attendance linked to a validated work entry + # raises an error + + # TODO: test that modifying an attendance linked to multiple work entries + # updates all work entries accordingly diff --git a/hr_work_entry_contract_attendance_oca/views/hr_contract_views.xml b/hr_work_entry_contract_attendance_oca/views/hr_contract_views.xml new file mode 100644 index 00000000000..ea1aa9123d2 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/views/hr_contract_views.xml @@ -0,0 +1,22 @@ + + + + + hr.contract.view.form.inherit.work.entry + hr.contract + + + + + + + + + diff --git a/hr_work_entry_contract_attendance_oca/views/hr_work_entry_views.xml b/hr_work_entry_contract_attendance_oca/views/hr_work_entry_views.xml new file mode 100644 index 00000000000..4afc9203d49 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/views/hr_work_entry_views.xml @@ -0,0 +1,33 @@ + + + + + hr.work.entry.calendar.inherit + hr.work.entry + + + + + + + + + + hr.work.entry.form.inherit + hr.work.entry + + + + + + + + diff --git a/hr_work_entry_contract_attendance_oca/views/res_config_settings_views.xml b/hr_work_entry_contract_attendance_oca/views/res_config_settings_views.xml new file mode 100644 index 00000000000..e68a6582102 --- /dev/null +++ b/hr_work_entry_contract_attendance_oca/views/res_config_settings_views.xml @@ -0,0 +1,27 @@ + + + + + res.config.settings.view.form.inherit + res.config.settings + + + + + + + + + + + +