diff --git a/hr_timesheet_attendance_ux/README.rst b/hr_timesheet_attendance_ux/README.rst index d92cfd45..6cc7ad97 100644 --- a/hr_timesheet_attendance_ux/README.rst +++ b/hr_timesheet_attendance_ux/README.rst @@ -14,8 +14,16 @@ Attendance Timesheet UX ======================= -#. Add link from timesheets to timesheet vs attendance analysis +#. Add link from timesheets to timesheet vs attendance analysis. Timesheet + users can read their own lines of that analysis. #. Timesheets/All Timesheets odoo menu is set visible only in developer mode. +#. When you set the employee or the date of a timesheet, the time spent is + suggested: the attendance hours of that day in the user's timezone (open + attendances count up to now, refused ones are left out), minus the break, + minus the time already logged that day. Timesheets from time off requests + are not counted as already logged. +#. The task selector of a timesheet does not offer tasks in a folded stage or + template tasks. Installation ============ @@ -36,7 +44,9 @@ Usage To use this module, you need to: -#. Go to ... +#. Go to Timesheets > Timesheets > My Timesheet / Attendance to compare your + attendance time with your timesheets, day by day. +#. Create a timesheet and check the suggested time spent. .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot @@ -46,7 +56,7 @@ Bug Tracker =========== Bugs are tracked on `GitHub Issues -`_. In case of trouble, please +`_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed feedback. diff --git a/hr_timesheet_attendance_ux/__manifest__.py b/hr_timesheet_attendance_ux/__manifest__.py index 08a3eb26..a2636b57 100644 --- a/hr_timesheet_attendance_ux/__manifest__.py +++ b/hr_timesheet_attendance_ux/__manifest__.py @@ -19,7 +19,7 @@ ############################################################################## { "name": "Attendance Timesheet UX", - "version": "19.0.1.1.0", + "version": "20.0.1.0.0", "category": "Human Resources", "sequence": 14, "author": "ADHOC SA", @@ -30,11 +30,11 @@ "hr_timesheet_attendance", ], "data": [ + "security/ir.access.csv", "views/account_analytic_line_views.xml", - "views/hr_attendance_views.xml", "views/hr_timesheet_attendance_report_views.xml", ], - "installable": False, + "installable": True, "auto_install": False, "application": False, } diff --git a/hr_timesheet_attendance_ux/i18n/es.po b/hr_timesheet_attendance_ux/i18n/es.po index b7083768..623fe432 100644 --- a/hr_timesheet_attendance_ux/i18n/es.po +++ b/hr_timesheet_attendance_ux/i18n/es.po @@ -7,84 +7,132 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 19.0+e\n" +"Project-Id-Version: Odoo Server 20.0+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-28 20:55+0000\n" -"PO-Revision-Date: 2025-12-11 18:11+0000\n" +"POT-Creation-Date: 2026-11-11 13:47+0000\n" +"PO-Revision-Date: 2026-11-11 13:47+0000\n" "Last-Translator: BAF \n" -"Language-Team: Spanish \n" +"Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=3; plural=(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0)" -" ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2);\n" "X-Generator: Weblate 5.10.4\n" +#. module: hr_timesheet_attendance_ux +#: model:ir.module.module,description:base.module_hr_timesheet_attendance_ux +msgid "" +".. |company| replace:: ADHOC SA\n" +"\n" +".. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png\n" +" :alt: ADHOC SA\n" +" :target: https://www.adhoc.com.ar\n" +"\n" +".. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png\n" +"\n" +".. image:: https://img.shields.io/badge/license-AGPL--3-blue.png\n" +" :target: https://www.gnu.org/licenses/agpl\n" +" :alt: License: AGPL-3\n" +"\n" +"=======================\n" +"Attendance Timesheet UX\n" +"=======================\n" +"\n" +"#. Add link from timesheets to timesheet vs attendance analysis. Timesheet\n" +" users can read their own lines of that analysis.\n" +"#. Timesheets/All Timesheets odoo menu is set visible only in developer mode.\n" +"#. When you set the employee or the date of a timesheet, the time spent is\n" +" suggested: the attendance hours of that day in the user's timezone (open\n" +" attendances count up to now, refused ones are left out), minus the break,\n" +" minus the time already logged that day. Timesheets from time off requests\n" +" are not counted as already logged.\n" +"#. The task selector of a timesheet does not offer tasks in a folded stage or\n" +" template tasks.\n" +"\n" +"Installation\n" +"============\n" +"\n" +"To install this module, you need to:\n" +"\n" +"#. Only need to install the module\n" +"\n" +"Configuration\n" +"=============\n" +"\n" +"To configure this module, you need to:\n" +"\n" +"#. Nothing to configure\n" +"\n" +"Usage\n" +"=====\n" +"\n" +"To use this module, you need to:\n" +"\n" +"#. Go to Timesheets > Timesheets > My Timesheet / Attendance to compare your\n" +" attendance time with your timesheets, day by day.\n" +"#. Create a timesheet and check the suggested time spent.\n" +"\n" +".. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas\n" +" :alt: Try me on Runbot\n" +" :target: http://runbot.adhoc.com.ar/\n" +"\n" +"Bug Tracker\n" +"===========\n" +"\n" +"Bugs are tracked on `GitHub Issues\n" +"`_. In case of trouble, please\n" +"check there if your issue has already been reported. If you spotted it first,\n" +"help us smashing it by providing a detailed and welcomed feedback.\n" +"\n" +"Credits\n" +"=======\n" +"\n" +"Images\n" +"------\n" +"\n" +"* |company| |icon|\n" +"\n" +"Contributors\n" +"------------\n" +"\n" +"Maintainer\n" +"----------\n" +"\n" +"|company_logo|\n" +"\n" +"This module is maintained by the |company|.\n" +"\n" +"To contribute to this module, please visit https://www.adhoc.com.ar.\n" +msgstr "" + #. module: hr_timesheet_attendance_ux #: model:ir.model,name:hr_timesheet_attendance_ux.model_account_analytic_line msgid "Analytic Line" msgstr "Línea analítica" #. module: hr_timesheet_attendance_ux -#: model:ir.model,name:hr_timesheet_attendance_ux.model_hr_attendance -msgid "Attendance" -msgstr "Asistencia" - -#. module: hr_timesheet_attendance_ux -#: model:ir.ui.menu,name:hr_timesheet_attendance_ux.menu_hr_attendance_my_attendances -msgid "Check In / Check Out" -msgstr "Entrada / Salida" +#: model:ir.module.module,shortdesc:base.module_hr_timesheet_attendance_ux +msgid "Attendance Timesheet UX" +msgstr "Asistencias y parte de horas UX" #. module: hr_timesheet_attendance_ux #: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_account_analytic_line__display_name -#: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_hr_attendance__display_name msgid "Display Name" -msgstr "Nombre para mostrar" +msgstr "Mostrar nombre" #. module: hr_timesheet_attendance_ux #: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_account_analytic_line__id -#: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_hr_attendance__id msgid "ID" msgstr "ID" -#. module: hr_timesheet_attendance_ux -#: model:ir.actions.act_window,name:hr_timesheet_attendance_ux.hr_attendance_action -#: model:ir.ui.menu,name:hr_timesheet_attendance_ux.my_hr_attendance -msgid "My Attendances" -msgstr "Mis asistencias" - #. module: hr_timesheet_attendance_ux #: model:ir.actions.act_window,name:hr_timesheet_attendance_ux.action_hr_timesheet_attendance_report_user_week #: model:ir.ui.menu,name:hr_timesheet_attendance_ux.menu_hr_timesheet_attendance_report_user_week msgid "My Timesheet / Attendance" msgstr "Mis Parte de Horas / Asistencias" -#. module: hr_timesheet_attendance_ux -#: model_terms:ir.actions.act_window,help:hr_timesheet_attendance_ux.hr_attendance_action -msgid "" -"Please make sure you're using the correct filter if you expected to see any." -msgstr "" -"Por favor asegúrate de estar usando el filtro correcto si esperabas ver " -"alguno" - -#. module: hr_timesheet_attendance_ux -#: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_hr_attendance__current_worked_hours -msgid "Service Hours" -msgstr "Horas de Servicio" - #. module: hr_timesheet_attendance_ux #: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_account_analytic_line__task_id msgid "Task" msgstr "Tarea" - -#. module: hr_timesheet_attendance_ux -#: model_terms:ir.ui.view,arch_db:hr_timesheet_attendance_ux.view_attendance_tree -msgid "Total service" -msgstr "Servicio total" - -#. module: hr_timesheet_attendance_ux -#: model_terms:ir.actions.act_window,help:hr_timesheet_attendance_ux.hr_attendance_action -msgid "Your attendance records will be displayed here." -msgstr "Tus registros de asistencias serán mostrados acá" diff --git a/hr_timesheet_attendance_ux/i18n/hr_timesheet_attendance_ux.pot b/hr_timesheet_attendance_ux/i18n/hr_timesheet_attendance_ux.pot index f15ba77f..627c8753 100644 --- a/hr_timesheet_attendance_ux/i18n/hr_timesheet_attendance_ux.pot +++ b/hr_timesheet_attendance_ux/i18n/hr_timesheet_attendance_ux.pot @@ -4,10 +4,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 19.0+e\n" +"Project-Id-Version: Odoo Server 20.0+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-11-22 02:34+0000\n" -"PO-Revision-Date: 2025-11-22 02:34+0000\n" +"POT-Creation-Date: 2026-11-11 13:47+0000\n" +"PO-Revision-Date: 2026-11-11 13:47+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -16,66 +16,118 @@ msgstr "" "Plural-Forms: \n" #. module: hr_timesheet_attendance_ux -#: model:ir.model,name:hr_timesheet_attendance_ux.model_account_analytic_line -msgid "Analytic Line" +#: model:ir.module.module,description:base.module_hr_timesheet_attendance_ux +msgid "" +".. |company| replace:: ADHOC SA\n" +"\n" +".. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png\n" +" :alt: ADHOC SA\n" +" :target: https://www.adhoc.com.ar\n" +"\n" +".. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png\n" +"\n" +".. image:: https://img.shields.io/badge/license-AGPL--3-blue.png\n" +" :target: https://www.gnu.org/licenses/agpl\n" +" :alt: License: AGPL-3\n" +"\n" +"=======================\n" +"Attendance Timesheet UX\n" +"=======================\n" +"\n" +"#. Add link from timesheets to timesheet vs attendance analysis. Timesheet\n" +" users can read their own lines of that analysis.\n" +"#. Timesheets/All Timesheets odoo menu is set visible only in developer mode.\n" +"#. When you set the employee or the date of a timesheet, the time spent is\n" +" suggested: the attendance hours of that day in the user's timezone (open\n" +" attendances count up to now, refused ones are left out), minus the break,\n" +" minus the time already logged that day. Timesheets from time off requests\n" +" are not counted as already logged.\n" +"#. The task selector of a timesheet does not offer tasks in a folded stage or\n" +" template tasks.\n" +"\n" +"Installation\n" +"============\n" +"\n" +"To install this module, you need to:\n" +"\n" +"#. Only need to install the module\n" +"\n" +"Configuration\n" +"=============\n" +"\n" +"To configure this module, you need to:\n" +"\n" +"#. Nothing to configure\n" +"\n" +"Usage\n" +"=====\n" +"\n" +"To use this module, you need to:\n" +"\n" +"#. Go to Timesheets > Timesheets > My Timesheet / Attendance to compare your\n" +" attendance time with your timesheets, day by day.\n" +"#. Create a timesheet and check the suggested time spent.\n" +"\n" +".. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas\n" +" :alt: Try me on Runbot\n" +" :target: http://runbot.adhoc.com.ar/\n" +"\n" +"Bug Tracker\n" +"===========\n" +"\n" +"Bugs are tracked on `GitHub Issues\n" +"`_. In case of trouble, please\n" +"check there if your issue has already been reported. If you spotted it first,\n" +"help us smashing it by providing a detailed and welcomed feedback.\n" +"\n" +"Credits\n" +"=======\n" +"\n" +"Images\n" +"------\n" +"\n" +"* |company| |icon|\n" +"\n" +"Contributors\n" +"------------\n" +"\n" +"Maintainer\n" +"----------\n" +"\n" +"|company_logo|\n" +"\n" +"This module is maintained by the |company|.\n" +"\n" +"To contribute to this module, please visit https://www.adhoc.com.ar.\n" msgstr "" #. module: hr_timesheet_attendance_ux -#: model:ir.model,name:hr_timesheet_attendance_ux.model_hr_attendance -msgid "Attendance" +#: model:ir.model,name:hr_timesheet_attendance_ux.model_account_analytic_line +msgid "Analytic Line" msgstr "" #. module: hr_timesheet_attendance_ux -#: model:ir.ui.menu,name:hr_timesheet_attendance_ux.menu_hr_attendance_my_attendances -msgid "Check In / Check Out" +#: model:ir.module.module,shortdesc:base.module_hr_timesheet_attendance_ux +msgid "Attendance Timesheet UX" msgstr "" #. module: hr_timesheet_attendance_ux #: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_account_analytic_line__display_name -#: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_hr_attendance__display_name msgid "Display Name" msgstr "" #. module: hr_timesheet_attendance_ux #: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_account_analytic_line__id -#: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_hr_attendance__id msgid "ID" msgstr "" -#. module: hr_timesheet_attendance_ux -#: model:ir.actions.act_window,name:hr_timesheet_attendance_ux.hr_attendance_action -#: model:ir.ui.menu,name:hr_timesheet_attendance_ux.my_hr_attendance -msgid "My Attendances" -msgstr "" - #. module: hr_timesheet_attendance_ux #: model:ir.actions.act_window,name:hr_timesheet_attendance_ux.action_hr_timesheet_attendance_report_user_week #: model:ir.ui.menu,name:hr_timesheet_attendance_ux.menu_hr_timesheet_attendance_report_user_week msgid "My Timesheet / Attendance" msgstr "" -#. module: hr_timesheet_attendance_ux -#: model_terms:ir.actions.act_window,help:hr_timesheet_attendance_ux.hr_attendance_action -msgid "" -"Please make sure you're using the correct filter if you expected to see any." -msgstr "" - -#. module: hr_timesheet_attendance_ux -#: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_hr_attendance__current_worked_hours -msgid "Service Hours" -msgstr "" - #. module: hr_timesheet_attendance_ux #: model:ir.model.fields,field_description:hr_timesheet_attendance_ux.field_account_analytic_line__task_id msgid "Task" msgstr "" - -#. module: hr_timesheet_attendance_ux -#: model_terms:ir.ui.view,arch_db:hr_timesheet_attendance_ux.view_attendance_tree -msgid "Total service" -msgstr "" - -#. module: hr_timesheet_attendance_ux -#: model_terms:ir.actions.act_window,help:hr_timesheet_attendance_ux.hr_attendance_action -msgid "Your attendance records will be displayed here." -msgstr "" diff --git a/hr_timesheet_attendance_ux/models/__init__.py b/hr_timesheet_attendance_ux/models/__init__.py index 50f69399..f69550ae 100644 --- a/hr_timesheet_attendance_ux/models/__init__.py +++ b/hr_timesheet_attendance_ux/models/__init__.py @@ -1,2 +1 @@ -from . import hr_attendance from . import account_analytic_line diff --git a/hr_timesheet_attendance_ux/models/account_analytic_line.py b/hr_timesheet_attendance_ux/models/account_analytic_line.py index 1b3b7a6e..07bb2545 100644 --- a/hr_timesheet_attendance_ux/models/account_analytic_line.py +++ b/hr_timesheet_attendance_ux/models/account_analytic_line.py @@ -6,14 +6,23 @@ class AccountAnalyticLine(models.Model): _inherit = "account.analytic.line" - task_id = fields.Many2one( - domain="[('company_id', '=', company_id), ('project_id.allow_timesheets', '=', True), ('project_id', '=?', project_id), ('stage_id.fold', '=', False)]" - ) + def _domain_task_id(self): + domain = ( + "[('project_id.allow_timesheets', '=', True), " + "('project_id', '=?', project_id), ('stage_id.fold', '=', False), ('has_template_ancestor', '=', False)" + ) + # Keep the condition that project_timesheet_holidays adds to the domain this field replaces. + if "is_timeoff_task" in self.env["project.task"]._fields: + domain += ", ('is_timeoff_task', '=', False)" + return domain + "]" + + task_id = fields.Many2one(domain=_domain_task_id) @api.onchange("employee_id", "date") def onchange_compute_hours(self): user_employee = self.default_get(["employee_id", "user_id"]).get("employee_id") hr_attendance = self.env["hr.attendance"] + now = fields.Datetime.now() for rec in self: end = rec.date + timedelta(hours=23, minutes=59, seconds=59) employee_id = rec.employee_id.id or user_employee @@ -22,6 +31,7 @@ def onchange_compute_hours(self): ("employee_id", "=", employee_id), ("check_in", ">=", rec.date), ("check_in", "<=", end), + ("state", "!=", "refused"), ] ) domain = [("employee_id", "=", employee_id), ("date", "=", rec.date)] @@ -32,5 +42,9 @@ def onchange_compute_hours(self): # tenga en cuenta las lineas que son por ausencia total_time_register = self.search(domain) total_time_register = sum(total_time_register.mapped("unit_amount")) - current_worked_hours = sum(attendances.mapped("current_worked_hours")) + # Open attendances count up to now; the unpaid break is not worked time. + current_worked_hours = sum( + ((att.check_out or now) - att.check_in).total_seconds() / 3600.0 - att.break_duration + for att in attendances + ) rec.unit_amount = current_worked_hours - total_time_register diff --git a/hr_timesheet_attendance_ux/models/hr_attendance.py b/hr_timesheet_attendance_ux/models/hr_attendance.py deleted file mode 100644 index 2a16371b..00000000 --- a/hr_timesheet_attendance_ux/models/hr_attendance.py +++ /dev/null @@ -1,17 +0,0 @@ -from odoo import fields, models - - -class HrAttendance(models.Model): - _inherit = "hr.attendance" - - current_worked_hours = fields.Float( - string="Service Hours", - compute="_compute_current_worked_hours", - ) - - def _compute_current_worked_hours(self): - for attendance in self: - check_out_datetime = attendance.check_out or fields.Datetime.now() - check_in_datetime = attendance.check_in - delta = check_out_datetime - check_in_datetime - attendance.current_worked_hours = delta.total_seconds() / 3600.0 diff --git a/hr_timesheet_attendance_ux/security/ir.access.csv b/hr_timesheet_attendance_ux/security/ir.access.csv new file mode 100644 index 00000000..8df69573 --- /dev/null +++ b/hr_timesheet_attendance_ux/security/ir.access.csv @@ -0,0 +1,2 @@ +id,name,model_id,group_id/id,operation,domain +access_hr_timesheet_attendance_report_user,Timesheet attendance Report: own,hr.timesheet.attendance.report,hr_timesheet.group_hr_timesheet_user,r,"[('employee_id', '=', user.employee_id.id)]" diff --git a/hr_timesheet_attendance_ux/tests/__init__.py b/hr_timesheet_attendance_ux/tests/__init__.py new file mode 100644 index 00000000..f761030b --- /dev/null +++ b/hr_timesheet_attendance_ux/tests/__init__.py @@ -0,0 +1 @@ +from . import test_hr_timesheet_attendance_ux diff --git a/hr_timesheet_attendance_ux/tests/test_hr_timesheet_attendance_ux.py b/hr_timesheet_attendance_ux/tests/test_hr_timesheet_attendance_ux.py new file mode 100644 index 00000000..5cd1cac2 --- /dev/null +++ b/hr_timesheet_attendance_ux/tests/test_hr_timesheet_attendance_ux.py @@ -0,0 +1,142 @@ +from datetime import datetime, time, timedelta + +from odoo import fields +from odoo.addons.hr_timesheet.tests.test_timesheet import TestCommonTimesheet +from odoo.tests import Form, freeze_time, tagged +from odoo.tools.safe_eval import safe_eval + + +@tagged("post_install", "-at_install") +class TestHrTimesheetAttendanceUx(TestCommonTimesheet): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.today = fields.Date.today() + # A past day, so a closed attendance never ends in the future. + cls.day = cls.today - timedelta(days=1) + + def _new_timesheet_form(self, day): + form = Form( + # The suggestion cuts the day in the user's timezone: fix it. + self.env["account.analytic.line"].with_user(self.user_employee).with_context(tz="UTC"), + view="hr_timesheet.hr_timesheet_line_form", + ) + form.project_id = self.project_customer + form.date = day + return form + + def test_suggested_hours_discount_break_and_logged_time(self): + # The form runs in UTC, so these times stay inside the same day. + self.env["hr.attendance"].create( + { + "employee_id": self.empl_employee.id, + "check_in": datetime.combine(self.day, time(9)), + "check_out": datetime.combine(self.day, time(17)), + "break_duration": 0.5, + } + ) + self.env["account.analytic.line"].create( + { + "name": "Already logged", + "project_id": self.project_customer.id, + "employee_id": self.empl_employee.id, + "date": self.day, + "unit_amount": 2.0, + } + ) + form = self._new_timesheet_form(self.day) + # 8 attended hours - 0.5 break - 2 already logged + self.assertAlmostEqual(form.unit_amount, 5.5) + line = form.save() + self.assertEqual(line.employee_id, self.empl_employee) + self.assertEqual(line.date, self.day) + + with self.subTest("refused attendance is left out"): + line.unlink() + refused = self.env["hr.attendance"].create( + { + "employee_id": self.empl_employee.id, + "check_in": datetime.combine(self.day, time(18)), + "check_out": datetime.combine(self.day, time(20)), + } + ) + refused.action_refuse() + self.assertEqual(refused.state, "refused") + form = self._new_timesheet_form(self.day) + self.assertAlmostEqual(form.unit_amount, 5.5) + + with self.subTest("open attendance counts up to now"): + self.env["hr.attendance"].search([("employee_id", "=", self.empl_employee.id)]).unlink() + with freeze_time(datetime.combine(self.today, time(15))): + self.env["hr.attendance"].create( + { + "employee_id": self.empl_employee.id, + "check_in": datetime.combine(self.today, time(12)), + } + ) + form = self._new_timesheet_form(self.today) + self.assertAlmostEqual(form.unit_amount, 3.0) + + def test_task_domain_hides_folded_and_template_tasks(self): + Task = self.env["project.task"] + open_stage = self.env["project.task.type"].create({"name": "Open", "fold": False}) + folded_stage = self.env["project.task.type"].create({"name": "Done", "fold": True}) + self.project_customer.type_ids = [(6, 0, (open_stage | folded_stage).ids)] + open_task = Task.create( + {"name": "Open task", "project_id": self.project_customer.id, "stage_id": open_stage.id} + ) + folded_task = Task.create( + {"name": "Folded task", "project_id": self.project_customer.id, "stage_id": folded_stage.id} + ) + template_task = Task.create( + { + "name": "Template task", + "project_id": self.project_customer.id, + "stage_id": open_stage.id, + "is_template": True, + } + ) + domain = self.env["account.analytic.line"]._fields["task_id"]._description_domain(self.env) + company_id = self.project_customer.company_id.id + available = Task.search(safe_eval(domain, {"company_id": company_id, "project_id": self.project_customer.id})) + self.assertIn(open_task, available) + self.assertNotIn(folded_task, available) + self.assertNotIn(template_task, available) + + with self.subTest("task of a project without company is offered"): + project = self.env["project.project"].create( + {"name": "No company", "allow_timesheets": True, "company_id": False} + ) + task = Task.create({"name": "No company task", "project_id": project.id}) + self.assertFalse(task.company_id) + available = Task.search(safe_eval(domain, {"company_id": company_id, "project_id": project.id})) + self.assertIn(task, available) + + with self.subTest("time off task is not offered"): + if "is_timeoff_task" not in Task._fields: + self.skipTest("project_timesheet_holidays is not installed") + timeoff_task = self.env.company.leave_timesheet_task_id + self.assertTrue(timeoff_task) + available = Task.search(safe_eval(domain, {"company_id": company_id, "project_id": False})) + self.assertNotIn(timeoff_task, available) + self.assertTrue(available, "the domain still offers other tasks") + + def test_timesheet_user_reads_own_attendance_report(self): + for employee in self.empl_employee | self.empl_employee2: + self.env["hr.attendance"].create( + { + "employee_id": employee.id, + "check_in": datetime.combine(self.day, time(9)), + "check_out": datetime.combine(self.day, time(17)), + } + ) + # The report is an SQL view: write the computed worked hours first. + self.env.flush_all() + Report = self.env["hr.timesheet.attendance.report"] + self.assertEqual( + Report.search([("employee_id", "in", (self.empl_employee | self.empl_employee2).ids)]).employee_id, + self.empl_employee | self.empl_employee2, + ) + own = Report.with_user(self.user_employee).search([]) + self.assertEqual(own.employee_id, self.empl_employee) + self.assertAlmostEqual(sum(own.mapped("total_attendance")), 8.0) diff --git a/hr_timesheet_attendance_ux/views/hr_attendance_views.xml b/hr_timesheet_attendance_ux/views/hr_attendance_views.xml deleted file mode 100644 index 7b0244eb..00000000 --- a/hr_timesheet_attendance_ux/views/hr_attendance_views.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - hr.attendance.list - hr.attendance - - primary - - - - - - - - - {"search_default_my_attendances": 1} - - - - My Attendances - hr.attendance - list,kanban,form - {"search_default_my_attendances": 1} - - - -

Your attendance records will be displayed here.

-

Please make sure you're using the correct filter if you expected to see any.

-
-
- - - - - -
diff --git a/hr_timesheet_attendance_ux/views/hr_timesheet_attendance_report_views.xml b/hr_timesheet_attendance_ux/views/hr_timesheet_attendance_report_views.xml index df2d3e9e..4a5606f5 100644 --- a/hr_timesheet_attendance_ux/views/hr_timesheet_attendance_report_views.xml +++ b/hr_timesheet_attendance_ux/views/hr_timesheet_attendance_report_views.xml @@ -7,7 +7,6 @@ [('employee_id.user_id', '=', uid)] pivot { - 'search_default_user_id': uid, 'pivot_column_groupby': [], 'pivot_measures': ['total_attendance', 'total_timesheet', 'total_difference'], 'pivot_row_groupby': ['date:day'],