diff --git a/.copier-answers.yml b/.copier-answers.yml index e0af472..21392fe 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,6 +1,6 @@ # Do NOT update manually; changes here will be overwritten by Copier -_commit: v1.20 -_src_path: https://github.com/OCA/oca-addons-repo-template.git +_commit: v2.2.1 +_src_path: https://github.com/coopiteasy/oca-addons-repo-template.git ci: GitHub convert_readme_fragments_to_markdown: false generate_requirements_txt: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..e6a7954 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,14 @@ +## Description + + + +## Odoo task (if applicable) + + + +## Checklist before approval + +- [ ] Tests are present (or not needed). +- [ ] Credits/copyright have been changed correctly. +- [ ] Change log snippet is present. +- [ ] (If a new module) Moving this to OCA has been considered. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e6a8605..d71cce9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,23 +10,6 @@ on: - "16.0-ocabot-*" jobs: - unreleased-deps: - runs-on: ubuntu-latest - name: Detect unreleased dependencies - steps: - - uses: actions/checkout@v3 - - run: | - for reqfile in requirements.txt test-requirements.txt ; do - if [ -f ${reqfile} ] ; then - result=0 - # reject non-comment lines that contain a / (i.e. URLs, relative paths) - grep "^[^#].*/" ${reqfile} || result=$? - if [ $result -eq 0 ] ; then - echo "Unreleased dependencies found in ${reqfile}." - exit 1 - fi - fi - done test: runs-on: ubuntu-22.04 container: ${{ matrix.container }} diff --git a/README.md b/README.md index 53e5d64..0477a31 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Available addons addon | version | maintainers | summary --- | --- | --- | --- [hr_timesheet_auto_creation](hr_timesheet_auto_creation/) | 16.0.1.0.1 | | Create weekly timesheets for employees automatically +[hr_timesheet_project_prevent_creation](hr_timesheet_project_prevent_creation/) | 16.0.1.0.0 | [![carmenbianca](https://github.com/carmenbianca.png?size=30px)](https://github.com/carmenbianca) | Prevent creation of projects and tasks from timesheets. +[hr_timesheet_sheet_current](hr_timesheet_sheet_current/) | 16.0.1.0.0 | | Allow to access the current timesheet sheet directly from the menu [//]: # (end addons) diff --git a/hr_timesheet_overtime/README.rst b/hr_timesheet_overtime/README.rst new file mode 100644 index 0000000..a94be95 --- /dev/null +++ b/hr_timesheet_overtime/README.rst @@ -0,0 +1,71 @@ +============================= +Timesheet/Contract - Overtime +============================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:864f762c46f2380ae49c8c705bd419243ee57a30b935ef491a26df7837f119d4 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-coopiteasy%2Fcie--timesheet-lightgray.png?logo=github + :target: https://github.com/coopiteasy/cie-timesheet/tree/16.0/hr_timesheet_overtime + :alt: coopiteasy/cie-timesheet + +|badge1| |badge2| |badge3| + +Computes overtime hours according to employee's contracts. + +**Table of contents** + +.. contents:: + :local: + +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 `_: + + * Vincent Van Rossem + * Robin Keunen + * Manuel Claeys Bouuaert + * hugues de keyzer + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been paid for by +`Pro Velo `_. + +Maintainers +~~~~~~~~~~~ + +This module is part of the `coopiteasy/cie-timesheet `_ project on GitHub. + +You are welcome to contribute. diff --git a/hr_timesheet_overtime/__init__.py b/hr_timesheet_overtime/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/hr_timesheet_overtime/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/hr_timesheet_overtime/__manifest__.py b/hr_timesheet_overtime/__manifest__.py new file mode 100644 index 0000000..2c2099d --- /dev/null +++ b/hr_timesheet_overtime/__manifest__.py @@ -0,0 +1,26 @@ +# Copyright 2020 Coop IT Easy SC +# - Vincent Van Rossem +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Timesheet/Contract - Overtime", + "version": "16.0.2.0.0", + "category": "Human Resources", + "summary": "Overtime Calculation", + "author": "Coop IT Easy SC, Odoo Community Association (OCA)", + "website": "https://github.com/coopiteasy/cie-timesheet", + "license": "AGPL-3", + "depends": [ + "company_today", + "hr_timesheet_sheet", + "resource_work_time_from_contracts", + ], + "data": [ + "security/ir.model.access.csv", + "views/hr_employee_view.xml", + "views/resource_view.xml", + "views/hr_timesheet_sheet_view.xml", + ], + "demo": [ + "demo/hr_contract_demo.xml", + ], +} diff --git a/hr_timesheet_overtime/demo/hr_contract_demo.xml b/hr_timesheet_overtime/demo/hr_contract_demo.xml new file mode 100644 index 0000000..b87eac9 --- /dev/null +++ b/hr_timesheet_overtime/demo/hr_contract_demo.xml @@ -0,0 +1,123 @@ + + + + + + + 4/5 + + + + 1/5 + + + + + Monday morning + 0 + 09 + 13 + + + + + Monday afternoon + 0 + 14 + 18 + + + + + Tuesday morning + 1 + 09 + 13 + + + + + Tuesday afternoon + 1 + 14 + 18 + + + + + Wednesday morning + 2 + 09 + 13 + + + + + Wednesday afternoon + 2 + 14 + 18 + + + + + Thursday morning + 3 + 09 + 13 + + + + + Thursday afternoon + 3 + 14 + 18 + + + + + + Friday morning + 4 + 09 + 13 + + + + + Friday afternoon + 4 + 14 + 18 + + + + + + + Mitchell Admin Contract #1 + + + + + + + + + + + Mitchell Admin Contract #2 + + + + + + + + + + diff --git a/hr_timesheet_overtime/i18n/hr_timesheet_overtime.pot b/hr_timesheet_overtime/i18n/hr_timesheet_overtime.pot new file mode 100644 index 0000000..28facea --- /dev/null +++ b/hr_timesheet_overtime/i18n/hr_timesheet_overtime.pot @@ -0,0 +1,298 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_timesheet_overtime +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.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_timesheet_overtime +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.hr_timesheet_sheet_form +msgid "Timesheet" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_timesheet_sheet__active +msgid "Active" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model,name:hr_timesheet_overtime.model_account_analytic_line +msgid "Analytic Line" +msgstr "" + +#. module: hr_timesheet_overtime +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.view_hr_timesheet_sheet_filter +msgid "Archived" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__company_id +msgid "Company" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__create_uid +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__create_uid +msgid "Created by" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__create_date +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__create_date +msgid "Created on" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_employee__current_day_working_time +msgid "Current Day Working Hours" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_timesheet_sheet__daily_overtime +msgid "Daily Overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_timesheet_sheet__daily_working_time +msgid "Daily Working Hours" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__dayofweek +msgid "Day of Week" +msgstr "" + +#. module: hr_timesheet_overtime +#: model_terms:ir.actions.act_window,help:hr_timesheet_overtime.action_resource_overtime_form +msgid "Define rate" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__display_name +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__display_name +msgid "Display Name" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model,name:hr_timesheet_overtime.model_hr_employee +msgid "Employee" +msgstr "" + +#. module: hr_timesheet_overtime +#: selection:resource.overtime.rate,dayofweek:0 +msgid "Friday" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_employee__current_day_working_time +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_timesheet_sheet__daily_working_time +msgid "Hours to work for the current day" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_timesheet_sheet__working_time +msgid "Hours to work for the timesheet period" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__id +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__id +msgid "ID" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_employee__initial_overtime +msgid "Initial Overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_employee__initial_overtime +msgid "Initial Overtime to start Overtime Start Date with" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime____last_update +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate____last_update +msgid "Last Modified on" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__write_uid +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__write_date +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__write_date +msgid "Last Updated on" +msgstr "" + +#. module: hr_timesheet_overtime +#: selection:resource.overtime.rate,dayofweek:0 +msgid "Monday" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__name +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__name +msgid "Name" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.ui.menu,name:hr_timesheet_overtime.menu_hr_timesheet_overtime +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.hr_timesheet_overtime_view_employee_form +msgid "Overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.actions.act_window,name:hr_timesheet_overtime.action_resource_overtime_form +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__rate_ids +#: model:ir.ui.menu,name:hr_timesheet_overtime.menu_hr_timesheet_overtime_rate +#: model:ir.ui.menu,name:hr_timesheet_overtime.menu_resource_overtime +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.resource_overtime_form +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.view_resource_overtime_rate_form +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.view_resource_overtime_rate_tree +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.view_resource_overtime_search +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.view_resource_overtime_tree +msgid "Overtime Rate" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_employee__overtime_start_date +msgid "Overtime Start Date" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_employee__overtime_start_date +msgid "Overtime Start Date to compute overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_timesheet_sheet__total_overtime +msgid "Overtime Total" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_timesheet_sheet__daily_overtime +msgid "Overtime for the current day" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_timesheet_sheet__timesheet_overtime +msgid "Overtime for this timesheet period" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_timesheet_sheet__timesheet_overtime_trimmed +msgid "Overtime for this timesheet period, from the employee's start date until today" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_timesheet_sheet__total_overtime +msgid "Overtime total since employee's overtime start date" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__rate +msgid "Rate" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model,name:hr_timesheet_overtime.model_resource_overtime_rate +msgid "Rate detail" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model,name:hr_timesheet_overtime.model_resource_overtime +msgid "Resource Overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime_rate__overtime_id +msgid "Resource's Overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: selection:resource.overtime.rate,dayofweek:0 +msgid "Saturday" +msgstr "" + +#. module: hr_timesheet_overtime +#: model_terms:ir.ui.view,arch_db:hr_timesheet_overtime.view_resource_overtime_search +msgid "Search Overtime Rate" +msgstr "" + +#. module: hr_timesheet_overtime +#: selection:resource.overtime.rate,dayofweek:0 +msgid "Sunday" +msgstr "" + +#. module: hr_timesheet_overtime +#: selection:resource.overtime.rate,dayofweek:0 +msgid "Thursday" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_timesheet_sheet__timesheet_overtime +msgid "Timesheet Overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model,name:hr_timesheet_overtime.model_hr_timesheet_sheet +msgid "Timesheet Sheet" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_employee__timesheet_sheet_ids +msgid "Timesheet sheets" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.actions.act_window,name:hr_timesheet_overtime.act_hr_timesheet_sheet_2_hr_analytic_timesheet +msgid "Timesheets" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_employee__total_overtime +msgid "Total Overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,help:hr_timesheet_overtime.field_hr_employee__total_overtime +msgid "Total Overtime since Overtime Start Date" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_timesheet_sheet__timesheet_overtime_trimmed +msgid "Trimmed Timesheet Overtime" +msgstr "" + +#. module: hr_timesheet_overtime +#: selection:resource.overtime.rate,dayofweek:0 +msgid "Tuesday" +msgstr "" + +#. module: hr_timesheet_overtime +#: selection:resource.overtime.rate,dayofweek:0 +msgid "Wednesday" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_resource_overtime__manager +msgid "Workgroup Manager" +msgstr "" + +#. module: hr_timesheet_overtime +#: model:ir.model.fields,field_description:hr_timesheet_overtime.field_hr_timesheet_sheet__working_time +msgid "Working Hours" +msgstr "" + diff --git a/hr_timesheet_overtime/models/__init__.py b/hr_timesheet_overtime/models/__init__.py new file mode 100644 index 0000000..c01dbe6 --- /dev/null +++ b/hr_timesheet_overtime/models/__init__.py @@ -0,0 +1,5 @@ +from . import account_analytic_line +from . import hr_employee +from . import hr_timesheet_sheet +from . import resource_overtime +from . import resource_overtime_rate diff --git a/hr_timesheet_overtime/models/account_analytic_line.py b/hr_timesheet_overtime/models/account_analytic_line.py new file mode 100644 index 0000000..4e33ceb --- /dev/null +++ b/hr_timesheet_overtime/models/account_analytic_line.py @@ -0,0 +1,45 @@ +# Copyright 2020 Coop IT Easy SC +# - Vincent Van Rossem +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class AnalyticLine(models.Model): + """ + Apply on account analytic lines the rate defined in resource.overtime.rate + """ + + _inherit = "account.analytic.line" + + def create(self, values): + self._update_values(values) + return super().create(values) + + def write(self, values): + if not self.env.context.get("create"): # sale module + self._update_values(values) + return super().write(values) + + def _update_values(self, values): + """ + Update values if date or unit_amount fields have changed + """ + if "date" in values or "unit_amount" in values: + date = values.get("date", self.date) + unit_amount = values.get("unit_amount", self.unit_amount) + + # rate management + weekday = fields.Date.from_string(date).weekday() + rate = ( + self.env["resource.overtime.rate"] + .search([("dayofweek", "=", weekday)], limit=1) + .rate + or 1.0 + ) + + # update + values["unit_amount"] = unit_amount * rate diff --git a/hr_timesheet_overtime/models/hr_employee.py b/hr_timesheet_overtime/models/hr_employee.py new file mode 100644 index 0000000..1917221 --- /dev/null +++ b/hr_timesheet_overtime/models/hr_employee.py @@ -0,0 +1,91 @@ +# Copyright 2020 Coop IT Easy SC +# - Vincent Van Rossem +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import date, datetime, timedelta + +from pytz import timezone + +from odoo import api, fields, models + + +class HrEmployee(models.Model): + _inherit = "hr.employee" + + # Numeric fields + current_day_working_time = fields.Float( + "Current Day Working Hours", + compute="_compute_current_day_working_time", + help="Hours to work for the current day", + ) + initial_overtime = fields.Float( + default=0.0, + help="Initial Overtime to start Overtime Start Date with", + ) + total_overtime = fields.Float( + compute="_compute_total_overtime", + help="Total Overtime since Overtime Start Date", + store=True, + ) + timesheet_sheet_ids = fields.One2many( + string="Timesheet sheets", + comodel_name="hr_timesheet.sheet", + inverse_name="employee_id", + ) + + # Date fields + overtime_start_date = fields.Date( + required=True, + default=date.today().replace(month=1, day=1), + help="Overtime Start Date to compute overtime", + ) + + def get_working_time(self, start_date, end_date=None): + """ + Get the working hours for a given date range according to the + employee's contracts + @param start_date: date + @param end_date: date + @return: total of working hours + """ + self.ensure_one() + if end_date is None: + end_date = start_date + tz = timezone(self.tz) + start_dt = tz.localize( + datetime(start_date.year, start_date.month, start_date.day) + ) + end_dt = tz.localize( + datetime(end_date.year, end_date.month, end_date.day) + ) + timedelta(days=1) + work_time_per_day = self.list_normal_work_time_per_day(start_dt, end_dt) + # .list_normal_work_time_per_day() returns a list of tuples: + # (date, work time) + return sum(work_time[1] for work_time in work_time_per_day) + + def _compute_current_day_working_time(self): + """ + Computes working hours for the current day according to the employee's + contracts. + """ + current_day = date.today() + for employee in self: + employee.current_day_working_time = employee.get_working_time(current_day) + + @api.depends( + "initial_overtime", + "overtime_start_date", + "timesheet_sheet_ids.timesheet_overtime_trimmed", + ) + def _compute_total_overtime(self): + """ + Computes total overtime since employee's overtime start date + """ + for employee in self: + sheets = self.env["hr_timesheet.sheet"].search( + [ + ("employee_id", "=", employee.id), + ("date_end", ">=", employee.overtime_start_date), + ] + ) + overtime = sum(sheet.timesheet_overtime_trimmed for sheet in sheets) + employee.total_overtime = employee.initial_overtime + overtime diff --git a/hr_timesheet_overtime/models/hr_timesheet_sheet.py b/hr_timesheet_overtime/models/hr_timesheet_sheet.py new file mode 100644 index 0000000..08b9ec1 --- /dev/null +++ b/hr_timesheet_overtime/models/hr_timesheet_sheet.py @@ -0,0 +1,150 @@ +# Copyright 2020 Coop IT Easy SC +# - Vincent Van Rossem +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging +from datetime import date, timedelta + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + + +class HrTimesheetSheet(models.Model): + _inherit = "hr_timesheet.sheet" + + active = fields.Boolean(default=True) + # Numeric fields + daily_working_time = fields.Float( + "Daily Working Hours", + related="employee_id.current_day_working_time", + help="Hours to work for the current day", + ) + working_time = fields.Float( + "Working Hours", + compute="_compute_working_time", + help="Hours to work for the timesheet period", + store=True, + ) + daily_overtime = fields.Float( + compute="_compute_daily_overtime", + help="Overtime for the current day", + ) + timesheet_overtime = fields.Float( + compute="_compute_timesheet_overtime", + help="Overtime for this timesheet period", + store=True, + ) + timesheet_overtime_trimmed = fields.Float( + compute="_compute_timesheet_overtime_trimmed", + help="Overtime for this timesheet period, from the employee's start date until" + " today", + store=True, + ) + total_overtime = fields.Float( + related="employee_id.total_overtime", + help="Overtime total since employee's overtime start date", + ) + + def get_worked_time(self, start_date, end_date=None): + """ + Get total of worked hours from account analytic lines + for a given date range + @param start_date: date + @param end_date: date + @return: total of worked hours + """ + self.ensure_one() + if end_date is None: + end_date = start_date + aal = self.env["account.analytic.line"].search( + [ + ("sheet_id", "=", self.id), + ("date", ">=", start_date), + ("date", "<=", end_date), + ] + ) + return sum(line.unit_amount for line in aal) + + @api.depends( + "active", + "date_start", + "date_end", + "employee_id", + "employee_id.contract_ids", + "employee_id.contract_ids.date_start", + "employee_id.contract_ids.date_end", + "employee_id.contract_ids.resource_calendar_id", + "employee_id.contract_ids.resource_calendar_id.attendance_ids", + "employee_id.contract_ids.resource_calendar_id.attendance_ids.dayofweek", + "employee_id.contract_ids.resource_calendar_id.attendance_ids.hour_from", + "employee_id.contract_ids.resource_calendar_id.attendance_ids.hour_to", + "employee_id.resource_calendar_id", + "employee_id.resource_calendar_id.attendance_ids", + "employee_id.resource_calendar_id.attendance_ids.dayofweek", + "employee_id.resource_calendar_id.attendance_ids.hour_from", + "employee_id.resource_calendar_id.attendance_ids.hour_to", + ) + def _compute_working_time(self): + for sheet in self: + sheet.working_time = sheet.employee_id.get_working_time( + sheet.date_start, sheet.date_end + ) + + def _compute_daily_overtime(self): + """ + Computes overtime for the current day + """ + current_day = date.today() + for sheet in self: + working_time = sheet.employee_id.get_working_time(current_day) + worked_time = sheet.get_worked_time(current_day) + sheet.daily_overtime = worked_time - working_time + + @api.depends("total_time", "working_time") + def _compute_timesheet_overtime(self): + for sheet in self: + sheet.timesheet_overtime = sheet.total_time - sheet.working_time + + @api.depends( + "active", + "date_start", + "date_end", + "company_id.today", + "employee_id", + "employee_id.contract_ids", + "employee_id.contract_ids.date_end", + "employee_id.contract_ids.date_start", + "employee_id.contract_ids.resource_calendar_id", + "employee_id.contract_ids.resource_calendar_id.attendance_ids", + "employee_id.contract_ids.resource_calendar_id.attendance_ids.dayofweek", + "employee_id.contract_ids.resource_calendar_id.attendance_ids.hour_from", + "employee_id.contract_ids.resource_calendar_id.attendance_ids.hour_to", + "employee_id.overtime_start_date", + "employee_id.resource_calendar_id", + "employee_id.resource_calendar_id.attendance_ids", + "employee_id.resource_calendar_id.attendance_ids.dayofweek", + "employee_id.resource_calendar_id.attendance_ids.hour_from", + "employee_id.resource_calendar_id.attendance_ids.hour_to", + "timesheet_ids.unit_amount", + ) + def _compute_timesheet_overtime_trimmed(self): + """ + Computes overtime for the timesheet period + (from the start date (included) to the current date (not included)) + """ + current_day = date.today() + for sheet in self: + employee = sheet.employee_id + start_date = sheet.date_start + end_date = sheet.date_end + if current_day < start_date or employee.overtime_start_date > end_date: + sheet.timesheet_overtime_trimmed = 0.0 + continue + if employee.overtime_start_date > start_date: + start_date = employee.overtime_start_date + if current_day <= end_date: + end_date = current_day - timedelta(days=1) + + working_time = employee.get_working_time(start_date, end_date) + worked_time = sheet.get_worked_time(start_date, end_date) + sheet.timesheet_overtime_trimmed = worked_time - working_time diff --git a/hr_timesheet_overtime/models/resource_overtime.py b/hr_timesheet_overtime/models/resource_overtime.py new file mode 100644 index 0000000..264f056 --- /dev/null +++ b/hr_timesheet_overtime/models/resource_overtime.py @@ -0,0 +1,30 @@ +# Copyright 2020 Coop IT Easy SC +# - Vincent Van Rossem +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ResourceOvertime(models.Model): + _name = "resource.overtime" + _description = "Resource Overtime" + + # String fields + name = fields.Char(required=True) + + # Relational fields + rate_ids = fields.One2many( + string="Overtime Rate", + comodel_name="resource.overtime.rate", + inverse_name="overtime_id", + copy=True, + ) + company_id = fields.Many2one( + string="Company", + comodel_name="res.company", + default=lambda self: self.env.company, + ) + manager = fields.Many2one( + string="Workgroup Manager", + comodel_name="res.users", + default=lambda self: self.env.uid, + ) diff --git a/hr_timesheet_overtime/models/resource_overtime_rate.py b/hr_timesheet_overtime/models/resource_overtime_rate.py new file mode 100644 index 0000000..cb69d2b --- /dev/null +++ b/hr_timesheet_overtime/models/resource_overtime_rate.py @@ -0,0 +1,36 @@ +# Copyright 2020 Coop IT Easy SC +# - Vincent Van Rossem +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ResourceOvertimeRate(models.Model): + _name = "resource.overtime.rate" + _description = "Rate detail" + _order = "dayofweek" + + # String fields + name = fields.Char(required=True) + dayofweek = fields.Selection( + string="Day of Week", + selection=[ + ("0", "Monday"), + ("1", "Tuesday"), + ("2", "Wednesday"), + ("3", "Thursday"), + ("4", "Friday"), + ("5", "Saturday"), + ("6", "Sunday"), + ], + required=True, + index=True, + ) + + # Numeric fields + rate = fields.Float(default="1.00", digits=(3, 2)) + overtime_id = fields.Many2one( + "resource.overtime", + string="Resource's Overtime", + required=True, + ondelete="cascade", + ) diff --git a/hr_timesheet_overtime/readme/CONTRIBUTORS.rst b/hr_timesheet_overtime/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..2149bda --- /dev/null +++ b/hr_timesheet_overtime/readme/CONTRIBUTORS.rst @@ -0,0 +1,6 @@ +* `Coop IT Easy SC `_: + + * Vincent Van Rossem + * Robin Keunen + * Manuel Claeys Bouuaert + * hugues de keyzer diff --git a/hr_timesheet_overtime/readme/CREDITS.rst b/hr_timesheet_overtime/readme/CREDITS.rst new file mode 100644 index 0000000..e409ea1 --- /dev/null +++ b/hr_timesheet_overtime/readme/CREDITS.rst @@ -0,0 +1,2 @@ +The development of this module has been paid for by +`Pro Velo `_. diff --git a/hr_timesheet_overtime/readme/DESCRIPTION.rst b/hr_timesheet_overtime/readme/DESCRIPTION.rst new file mode 100644 index 0000000..3eed2f4 --- /dev/null +++ b/hr_timesheet_overtime/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Computes overtime hours according to employee's contracts. diff --git a/hr_timesheet_overtime/security/ir.model.access.csv b/hr_timesheet_overtime/security/ir.model.access.csv new file mode 100644 index 0000000..d8789f4 --- /dev/null +++ b/hr_timesheet_overtime/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_resource_overtime,resource.overtime,model_resource_overtime,hr.group_hr_user,1,1,1,1 +access_resource_overtime_rate,resource.overtime.rate,model_resource_overtime_rate,hr.group_hr_user,1,1,1,1 +access_resource_overtime_rate_user,resource.overtime.rate,model_resource_overtime_rate,base.group_user,1,0,0,0 diff --git a/hr_timesheet_overtime/static/description/icon.png b/hr_timesheet_overtime/static/description/icon.png new file mode 100644 index 0000000..3a0328b Binary files /dev/null and b/hr_timesheet_overtime/static/description/icon.png differ diff --git a/hr_timesheet_overtime/static/description/index.html b/hr_timesheet_overtime/static/description/index.html new file mode 100644 index 0000000..802d4a3 --- /dev/null +++ b/hr_timesheet_overtime/static/description/index.html @@ -0,0 +1,427 @@ + + + + + +Timesheet/Contract - Overtime + + + +
+

Timesheet/Contract - Overtime

+ + +

Beta License: AGPL-3 coopiteasy/cie-timesheet

+

Computes overtime hours according to employee’s contracts.

+

Table of contents

+ +
+

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:
      +
    • Vincent Van Rossem
    • +
    • Robin Keunen
    • +
    • Manuel Claeys Bouuaert
    • +
    • hugues de keyzer
    • +
    +
  • +
+
+
+

Other credits

+

The development of this module has been paid for by +Pro Velo.

+
+
+

Maintainers

+

This module is part of the coopiteasy/cie-timesheet project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/hr_timesheet_overtime/tests/__init__.py b/hr_timesheet_overtime/tests/__init__.py new file mode 100644 index 0000000..304ffd0 --- /dev/null +++ b/hr_timesheet_overtime/tests/__init__.py @@ -0,0 +1 @@ +from . import test_overtime diff --git a/hr_timesheet_overtime/tests/test_overtime.py b/hr_timesheet_overtime/tests/test_overtime.py new file mode 100644 index 0000000..52f12e9 --- /dev/null +++ b/hr_timesheet_overtime/tests/test_overtime.py @@ -0,0 +1,423 @@ +# Copyright 2020 Coop IT Easy SC +# - Vincent Van Rossem +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import date + +from freezegun import freeze_time + +from odoo.tests.common import TransactionCase + + +class TestOvertime(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + # users + user1_dict = {"name": "User 1", "login": "user1", "password": "user1"} + cls.user1 = cls.env["res.users"].create(user1_dict) + + # employees + employee1_dict = { + "name": "Employee 1", + "user_id": cls.user1.id, + "address_id": cls.user1.partner_id.id, + "overtime_start_date": "2019-01-01", + } + cls.employee1 = cls.env["hr.employee"].create(employee1_dict) + + # working hours + # calendar have default attendance_ids, force it to have none. + calendar = cls.env["resource.calendar"].create( + {"name": "Calendar", "attendance_ids": False} + ) + for day in range(5): + cls.env["resource.calendar.attendance"].create( + { + "name": "Attendance", + "dayofweek": str(day), + "hour_from": 9.0, + "hour_to": 18.0, + "calendar_id": calendar[0].id, + } + ) + + # contracts + contract_dict = { + "name": "Contract 1", + "employee_id": cls.employee1.id, + "wage": 0.0, + "resource_calendar_id": calendar.id, + "date_start": "2019-01-01", + } + + cls.contract1 = cls.env["hr.contract"].create(contract_dict) + + # projects + cls.project_01 = cls.env["project.project"].create({"name": "Project 01"}) + + # create ts + ts1_dict = { + "employee_id": cls.employee1.id, + "date_start": "2019-12-02", + "date_end": "2019-12-08", + } + cls.ts1 = cls.env["hr_timesheet.sheet"].create(ts1_dict) + + # create and link aal + # monday 02/12/2019 + cls.env["account.analytic.line"].create( + { + "project_id": cls.project_01.id, + "amount": 0.0, + "date": "2019-12-02", + "name": "-", + "sheet_id": cls.ts1.id, + "unit_amount": 10.0, # 1 hour overtime + "user_id": cls.employee1.user_id.id, + } + ) + # tuesday 03/12/2019 -> friday 06/12/2019 + for day in range(3, 7): + cls.env["account.analytic.line"].create( + { + "project_id": cls.project_01.id, + "amount": 0.0, + "date": date(2019, 12, day), + "name": "-", + "sheet_id": cls.ts1.id, + "unit_amount": 9.0, # expected time + "user_id": cls.employee1.user_id.id, + } + ) + + def test_overtime_01(self): + """ + A timesheet and its analytic line with one hour extra time + """ + + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 1) + self.assertEqual(self.ts1.timesheet_overtime, 1) + self.assertEqual(self.ts1.total_overtime, 1) + + def test_overtime_02(self): + """ + Change overtime start date + """ + ts2 = self.env["hr_timesheet.sheet"].create( + { + "employee_id": self.employee1.id, + "date_start": "2019-12-09", + "date_end": "2019-12-15", + } + ) + + # create and link aal + # monday and tuesday + for day in range(9, 11): + self.env["account.analytic.line"].create( + { + "project_id": self.project_01.id, + "amount": 0.0, + "date": date(2019, 12, day), + "name": "-", + "sheet_id": ts2.id, + "unit_amount": 10.0, # 1 hour overtime + "user_id": self.employee1.user_id.id, + } + ) + # wednesday -> thursday + for day in range(10, 13): + self.env["account.analytic.line"].create( + { + "project_id": self.project_01.id, + "amount": 0.0, + "date": date(2019, 12, day), + "name": "-", + "sheet_id": ts2.id, + "unit_amount": 9.0, # expected time + "user_id": self.employee1.user_id.id, + } + ) + + self.employee1.write({"overtime_start_date": "2019-12-10"}) + + # overtime for any timesheet takes overtime_start_date into account + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 0) + self.assertEqual(self.ts1.timesheet_overtime, 1) + # it should start computing on tuesday + self.assertEqual(ts2.timesheet_overtime_trimmed, 1) + self.assertEqual(ts2.timesheet_overtime, 2) + self.assertEqual(ts2.total_overtime, 1) + # total_overtime is just a link to the employee's total overtime + self.assertEqual(self.ts1.total_overtime, 1) + + def test_overtime_03(self): + """ + Change initial overtime + """ + self.employee1.write({"initial_overtime": 10}) + + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 1) + self.assertEqual(self.ts1.timesheet_overtime, 1) + self.assertEqual(self.ts1.total_overtime, 11) + + def test_overtime_04(self): + """ + Worker did not work on a day he was expected to work on. + """ + ts2 = self.env["hr_timesheet.sheet"].create( + { + "employee_id": self.employee1.id, + "date_start": "2019-12-09", + "date_end": "2019-12-15", + } + ) + + # create and link aal + # monday + self.env["account.analytic.line"].create( + { + "project_id": self.project_01.id, + "amount": 0.0, + "date": "2019-12-09", + "name": "-", + "sheet_id": ts2.id, + "unit_amount": 10.0, # 1 hour overtime + "user_id": self.employee1.user_id.id, + } + ) + # tuesday -> thursday + for day in range(10, 13): + self.env["account.analytic.line"].create( + { + "project_id": self.project_01.id, + "amount": 0.0, + "date": date(2019, 12, day), + "name": "-", + "sheet_id": ts2.id, + "unit_amount": 9.0, # expected time + "user_id": self.employee1.user_id.id, + } + ) + + self.assertEqual(ts2.timesheet_overtime_trimmed, -8) + self.assertEqual(ts2.timesheet_overtime, -8) + self.assertEqual(ts2.total_overtime, -7) + + def test_overtime_05(self): + """ + Multiple contracts + """ + + # end previous contract + self.contract1.date_end = "2020-01-06" + + # create new contract + # working hours : half-time + calendar = self.env["resource.calendar"].create( + {"name": "Calendar", "attendance_ids": False} + ) + for day in range(5): # from monday to friday + self.env["resource.calendar.attendance"].create( + { + "name": "Attendance", + "dayofweek": str(day), + "hour_from": 9.0, + "hour_to": 13.0, + "calendar_id": calendar[0].id, + } + ) + + # contracts + contract_dict = { + "name": "Contract 2", + "employee_id": self.employee1.id, + "wage": 0.0, + "resource_calendar_id": calendar.id, + "date_start": "2020-01-07", + } + + self.contract2 = self.env["hr.contract"].create(contract_dict) + + # create ts + ts2_dict = { + "employee_id": self.employee1.id, + "date_start": "2020-01-06", + "date_end": "2020-01-12", + } + self.ts2 = self.env["hr_timesheet.sheet"].create(ts2_dict) + + # create and link aal + # monday + self.env["account.analytic.line"].create( + { + "project_id": self.project_01.id, + "amount": 0.0, + "date": "2020-01-06", + "name": "-", + "sheet_id": self.ts2.id, + "unit_amount": 9.0, # expected time from previous contract + "user_id": self.employee1.user_id.id, + } + ) + + # tuesday 07/01/2020 -> friday 10/01/2020 + for day in range(7, 11): + self.env["account.analytic.line"].create( + { + "project_id": self.project_01.id, + "amount": 0.0, + "date": date(2020, 1, day), + "name": "-", + "sheet_id": self.ts2.id, + "unit_amount": 4.0, # expected time from new contract + "user_id": self.employee1.user_id.id, + } + ) + + self.assertEqual(self.ts2.timesheet_overtime_trimmed, 0) + self.assertEqual(self.ts2.timesheet_overtime, 0) + self.assertEqual(self.ts2.total_overtime, 1) # 1 hour overtime from ts1 + + def test_overtime_archived_timesheet(self): + """ + Archived timesheets + """ + ts2 = self.env["hr_timesheet.sheet"].create( + { + "employee_id": self.employee1.id, + "date_start": "2019-12-09", + "date_end": "2019-12-15", + } + ) + + # create and link aal + # monday -> friday + for day in range(9, 14): + self.env["account.analytic.line"].create( + { + "project_id": self.project_01.id, + "amount": 0.0, + "date": date(2019, 12, day), + "name": "-", + "sheet_id": ts2.id, + "unit_amount": 10.0, # 1 hour overtime + "user_id": self.employee1.user_id.id, + } + ) + + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 1) + self.assertEqual(self.ts1.timesheet_overtime, 1) + self.assertEqual(self.ts1.total_overtime, 6) + self.assertEqual(ts2.timesheet_overtime_trimmed, 5) + self.assertEqual(ts2.timesheet_overtime, 5) + self.assertEqual(ts2.total_overtime, 6) + self.assertEqual(self.employee1.total_overtime, 6) + + self.ts1.write({"active": False}) + # an inactive timesheet still has the same overtime + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 1) + self.assertEqual(self.ts1.timesheet_overtime, 1) + self.assertEqual(self.ts1.total_overtime, 5) + self.assertEqual(ts2.timesheet_overtime_trimmed, 5) + self.assertEqual(ts2.timesheet_overtime, 5) + self.assertEqual(ts2.total_overtime, 5) + self.assertEqual(self.employee1.total_overtime, 5) + + ts2.write({"active": False}) + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 1) + self.assertEqual(self.ts1.timesheet_overtime, 1) + self.assertEqual(self.ts1.total_overtime, 0) + self.assertEqual(ts2.timesheet_overtime_trimmed, 5) + self.assertEqual(ts2.timesheet_overtime, 5) + self.assertEqual(ts2.total_overtime, 0) + self.assertEqual(self.employee1.total_overtime, 0) + + self.ts1.write({"active": True}) + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 1) + self.assertEqual(self.ts1.timesheet_overtime, 1) + self.assertEqual(self.ts1.total_overtime, 1) + self.assertEqual(ts2.timesheet_overtime_trimmed, 5) + self.assertEqual(ts2.timesheet_overtime, 5) + self.assertEqual(ts2.total_overtime, 1) + self.assertEqual(self.employee1.total_overtime, 1) + + # The subsequent tests verify whether the stored fields respond correctly to + # changing variables. + + def test_stored_change_contract_date_start(self): + """When contract_id.date_start is changed, adjust correctly.""" + # These initial assertions are not repeated in subsequent tests. + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 1) + self.assertEqual(self.ts1.timesheet_overtime, 1) + self.assertEqual(self.ts1.total_overtime, 1) + self.assertEqual(self.ts1.working_time, 9 * 5) + self.contract1.date_start = date(2019, 12, 3) + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 10) + self.assertEqual(self.ts1.timesheet_overtime, 10) + self.assertEqual(self.ts1.total_overtime, 10) + self.assertEqual(self.ts1.working_time, 9 * 4) + + def test_stored_change_contract_date_end(self): + """When contract_id.date_end is changed, adjust correctly.""" + self.contract1.date_end = date(2019, 12, 5) + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 10) + self.assertEqual(self.ts1.timesheet_overtime, 10) + self.assertEqual(self.ts1.total_overtime, 10) + self.assertEqual(self.ts1.working_time, 9 * 4) + + def test_stored_change_attendance_hour_to(self): + """When contract_id.resource_calendar_id.attendance_ids.hour_to is changed, + adjust correctly. + """ + self.contract1.resource_calendar_id.attendance_ids[0].hour_to = 17 + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 2) + self.assertEqual(self.ts1.timesheet_overtime, 2) + self.assertEqual(self.ts1.total_overtime, 2) + self.assertEqual(self.ts1.working_time, 9 * 5 - 1) + + def test_stored_change_attendance_hour_from(self): + """When contract_id.resource_calendar_id.attendance_ids.hour_from is changed, + adjust correctly. + """ + self.contract1.resource_calendar_id.attendance_ids[0].hour_from = 10 + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 2) + self.assertEqual(self.ts1.timesheet_overtime, 2) + self.assertEqual(self.ts1.total_overtime, 2) + self.assertEqual(self.ts1.working_time, 9 * 5 - 1) + + def test_stored_change_initial_overtime(self): + """When employee_id.initial_overtime is changed, adjust accordingly.""" + self.employee1.initial_overtime = 1 + self.assertEqual(self.ts1.total_overtime, 2) + + def test_stored_change_overtime_start_date(self): + """When employee_id.overtime_start_date is changed, adjust accordingly.""" + self.employee1.overtime_start_date = date(2020, 1, 1) + # No matching records. + self.assertEqual(self.ts1.total_overtime, 0) + + def test_stored_change_today(self): + """When today is changed, adjust accordingly.""" + # More hours of work in the week + line = self.env["account.analytic.line"].search( + [ + ("date", "=", "2019-12-4"), + ("sheet_id", "=", self.ts1.id), + ] + ) + line.unit_amount = 10 + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 2) + self.assertEqual(self.ts1.timesheet_overtime, 2) + self.assertEqual(self.ts1.total_overtime, 2) + self.assertEqual(self.ts1.working_time, 9 * 5) + + # Time travel into the past before the overtime. + with freeze_time("2019-12-3"): + self.ts1.company_id.cron_update_today() + # Not affected by the extra overtime + self.assertEqual(self.ts1.timesheet_overtime_trimmed, 1) + self.assertEqual(self.ts1.total_overtime, 1) + self.assertEqual(self.ts1.working_time, 9 * 5) + # Affected by the extra overtime + self.assertEqual(self.ts1.timesheet_overtime, 2) diff --git a/hr_timesheet_overtime/views/hr_employee_view.xml b/hr_timesheet_overtime/views/hr_employee_view.xml new file mode 100644 index 0000000..7b40e72 --- /dev/null +++ b/hr_timesheet_overtime/views/hr_employee_view.xml @@ -0,0 +1,25 @@ + + + + + hr.employee.view.form + hr.employee + + + + + + + + + + + + + + + + diff --git a/hr_timesheet_overtime/views/hr_timesheet_sheet_view.xml b/hr_timesheet_overtime/views/hr_timesheet_sheet_view.xml new file mode 100644 index 0000000..10c7965 --- /dev/null +++ b/hr_timesheet_overtime/views/hr_timesheet_sheet_view.xml @@ -0,0 +1,115 @@ + + + + + Timesheets + account.analytic.line + {'search_default_sheet_id': active_id} + + + + hr.timesheet.sheet.form + hr_timesheet.sheet + + + + +
+ + +
+
+ + + + + + + +
+
+ + + hr.timesheet.sheet.tree + hr_timesheet.sheet + + + + + + + + + + + + + hr.timesheet.sheet.filter + hr_timesheet.sheet + + + + + + + + + + + + + + +
diff --git a/hr_timesheet_overtime/views/resource_view.xml b/hr_timesheet_overtime/views/resource_view.xml new file mode 100644 index 0000000..bb2babd --- /dev/null +++ b/hr_timesheet_overtime/views/resource_view.xml @@ -0,0 +1,93 @@ + + + + + + resource.overtime.search + resource.overtime + + + + + + + + + + + resource.overtime.form + resource.overtime + +
+ + + + + + + +
+
+ + + resource.overtime.tree + resource.overtime + + + + + + + + + + + Overtime Rate + resource.overtime + tree,form + + +

+ Define rate +

+
+
+ + + + resource.overtime.rate.tree + resource.overtime.rate + + + + + + + + + + + resource.overtime.rate.form + resource.overtime.rate + +
+ + + + + +
+
+
+ + + +
diff --git a/hr_timesheet_project_prevent_creation/README.rst b/hr_timesheet_project_prevent_creation/README.rst new file mode 100644 index 0000000..945ca3a --- /dev/null +++ b/hr_timesheet_project_prevent_creation/README.rst @@ -0,0 +1,70 @@ +====================================================== +Prevent creation of projects and tasks from timesheets +====================================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:06155003ca79f40df34a07a97ec1d217fabf85272ba89d79d4726e2d7747b915 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-coopiteasy%2Fcie--timesheet-lightgray.png?logo=github + :target: https://github.com/coopiteasy/cie-timesheet/tree/16.0/hr_timesheet_project_prevent_creation + :alt: coopiteasy/cie-timesheet + +|badge1| |badge2| |badge3| + +Prevent creation of projects and tasks from timesheets. + +**Table of contents** + +.. contents:: + :local: + +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 `_: + + * Carmen Bianca Bakker + +Maintainers +~~~~~~~~~~~ + +.. |maintainer-carmenbianca| image:: https://github.com/carmenbianca.png?size=40px + :target: https://github.com/carmenbianca + :alt: carmenbianca + +Current maintainer: + +|maintainer-carmenbianca| + +This module is part of the `coopiteasy/cie-timesheet `_ project on GitHub. + +You are welcome to contribute. diff --git a/hr_timesheet_project_prevent_creation/__init__.py b/hr_timesheet_project_prevent_creation/__init__.py new file mode 100644 index 0000000..0870657 --- /dev/null +++ b/hr_timesheet_project_prevent_creation/__init__.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2022 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/hr_timesheet_project_prevent_creation/__manifest__.py b/hr_timesheet_project_prevent_creation/__manifest__.py new file mode 100644 index 0000000..317847c --- /dev/null +++ b/hr_timesheet_project_prevent_creation/__manifest__.py @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2022 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +{ + "name": "Prevent creation of projects and tasks from timesheets", + "summary": """ + Prevent creation of projects and tasks from timesheets.""", + "version": "16.0.1.0.0", + "category": "Human Resources", + "website": "https://github.com/coopiteasy/cie-timesheet", + "author": "Coop IT Easy SC", + "maintainers": ["carmenbianca"], + "license": "AGPL-3", + "application": False, + "depends": [ + "hr_timesheet", + "hr_timesheet_sheet", + ], + "excludes": [], + "data": [ + "views/hr_timesheet_sheet_views.xml", + "views/hr_timesheet_views.xml", + ], + "demo": [], + "qweb": [], +} diff --git a/hr_timesheet_project_prevent_creation/i18n/hr_timesheet_project_prevent_creation.pot b/hr_timesheet_project_prevent_creation/i18n/hr_timesheet_project_prevent_creation.pot new file mode 100644 index 0000000..78d58d5 --- /dev/null +++ b/hr_timesheet_project_prevent_creation/i18n/hr_timesheet_project_prevent_creation.pot @@ -0,0 +1,13 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.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" diff --git a/hr_timesheet_project_prevent_creation/readme/CONTRIBUTORS.rst b/hr_timesheet_project_prevent_creation/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..d64451e --- /dev/null +++ b/hr_timesheet_project_prevent_creation/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Coop IT Easy SC `_: + + * Carmen Bianca Bakker diff --git a/hr_timesheet_project_prevent_creation/readme/DESCRIPTION.rst b/hr_timesheet_project_prevent_creation/readme/DESCRIPTION.rst new file mode 100644 index 0000000..5cbc768 --- /dev/null +++ b/hr_timesheet_project_prevent_creation/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Prevent creation of projects and tasks from timesheets. diff --git a/hr_timesheet_project_prevent_creation/views/hr_timesheet_sheet_views.xml b/hr_timesheet_project_prevent_creation/views/hr_timesheet_sheet_views.xml new file mode 100644 index 0000000..7a5b290 --- /dev/null +++ b/hr_timesheet_project_prevent_creation/views/hr_timesheet_sheet_views.xml @@ -0,0 +1,35 @@ + + + + + hr_timesheet.sheet.form.hr_timesheet_project_prevent_creation + hr_timesheet.sheet + + + + {'no_create': True} + + + {'no_create': True} + + + {'no_create': True} + + + {'no_create': True} + + + + diff --git a/hr_timesheet_project_prevent_creation/views/hr_timesheet_views.xml b/hr_timesheet_project_prevent_creation/views/hr_timesheet_views.xml new file mode 100644 index 0000000..b39dbc4 --- /dev/null +++ b/hr_timesheet_project_prevent_creation/views/hr_timesheet_views.xml @@ -0,0 +1,23 @@ + + + + + account.analytic.line.tree.hr_timesheet_project_prevent_creation + account.analytic.line + + + + {'no_create': True} + + + {'no_create': True} + + + + diff --git a/hr_timesheet_sheet_current/README.rst b/hr_timesheet_sheet_current/README.rst index 09cec2e..925a52a 100644 --- a/hr_timesheet_sheet_current/README.rst +++ b/hr_timesheet_sheet_current/README.rst @@ -2,10 +2,13 @@ My Current Timesheet Sheet ========================== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:eb7445ee3b12aba1bf0ffe173670671bb8f6b98f1ef2d596e6fa2be5fc77b306 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -14,10 +17,10 @@ My Current Timesheet Sheet :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-coopiteasy%2Fcie--timesheet-lightgray.png?logo=github - :target: https://github.com/coopiteasy/cie-timesheet/tree/12.0/hr_timesheet_sheet_current + :target: https://github.com/coopiteasy/cie-timesheet/tree/16.0/hr_timesheet_sheet_current :alt: coopiteasy/cie-timesheet -|badge1| |badge2| |badge3| +|badge1| |badge2| |badge3| Allow to access the current timesheet sheet directly from the menu. @@ -33,8 +36,8 @@ 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 smashing it by providing a detailed and welcomed -`feedback `_. +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. @@ -56,6 +59,6 @@ Contributors Maintainers ~~~~~~~~~~~ -This module is part of the `coopiteasy/cie-timesheet `_ project on GitHub. +This module is part of the `coopiteasy/cie-timesheet `_ project on GitHub. You are welcome to contribute. diff --git a/hr_timesheet_sheet_current/i18n/hr_timesheet_sheet_current.pot b/hr_timesheet_sheet_current/i18n/hr_timesheet_sheet_current.pot index 937000a..b2f3e93 100644 --- a/hr_timesheet_sheet_current/i18n/hr_timesheet_sheet_current.pot +++ b/hr_timesheet_sheet_current/i18n/hr_timesheet_sheet_current.pot @@ -1,12 +1,12 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * hr_timesheet_sheet_current +# * hr_timesheet_sheet_current # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: <>\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,7 +20,8 @@ msgid "My Current Timesheet Sheet" msgstr "" #. module: hr_timesheet_sheet_current -#: code:addons/hr_timesheet_sheet_current/models/hr_timesheet_sheet.py:26 +#. odoo-python +#: code:addons/hr_timesheet_sheet_current/models/hr_timesheet_sheet.py:0 #, python-format msgid "Open Timesheet" msgstr "" @@ -29,4 +30,3 @@ msgstr "" #: model:ir.model,name:hr_timesheet_sheet_current.model_hr_timesheet_sheet msgid "Timesheet Sheet" msgstr "" - diff --git a/setup/hr_timesheet_overtime/odoo/addons/hr_timesheet_overtime b/setup/hr_timesheet_overtime/odoo/addons/hr_timesheet_overtime new file mode 120000 index 0000000..a1ddf24 --- /dev/null +++ b/setup/hr_timesheet_overtime/odoo/addons/hr_timesheet_overtime @@ -0,0 +1 @@ +../../../../hr_timesheet_overtime \ No newline at end of file diff --git a/setup/hr_timesheet_overtime/setup.py b/setup/hr_timesheet_overtime/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/hr_timesheet_overtime/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/hr_timesheet_project_prevent_creation/odoo/addons/hr_timesheet_project_prevent_creation b/setup/hr_timesheet_project_prevent_creation/odoo/addons/hr_timesheet_project_prevent_creation new file mode 120000 index 0000000..1991885 --- /dev/null +++ b/setup/hr_timesheet_project_prevent_creation/odoo/addons/hr_timesheet_project_prevent_creation @@ -0,0 +1 @@ +../../../../hr_timesheet_project_prevent_creation \ No newline at end of file diff --git a/setup/hr_timesheet_project_prevent_creation/setup.py b/setup/hr_timesheet_project_prevent_creation/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/hr_timesheet_project_prevent_creation/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..347d445 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,2 @@ +git+https://github.com/coopiteasy/addons@16.0#subdirectory=setup/company_today +git+https://github.com/coopiteasy/addons@16.0#subdirectory=setup/resource_work_time_from_contracts