Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] #57 allow higher value overtime for holidays #81

Merged
merged 4 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
- name: Run tests
run: oca_run_tests
- name: Generate coverage.xml
run: coverage xml --include '*.py'
run: coverage xml --include '*.py' --omit '**/tests/*'
- uses: codecov/codecov-action@v3
with:
files: coverage.xml
Expand Down
4 changes: 4 additions & 0 deletions verdigado_attendance/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@
"views/base_ical.xml",
"views/hr_attendance_view.xml",
"views/hr_attendance_report.xml",
"views/hr_employee.xml",
"views/hr_leave_type.xml",
"views/hr_leave.xml",
"views/hr_menu_human_resources_configuration.xml",
"views/menu.xml",
"views/res_config_settings.xml",
],
"demo": [
"demo/res_users.xml",
Expand All @@ -60,10 +62,12 @@
],
"web.assets_backend": [
"verdigado_attendance/static/src/scss/backend.scss",
"verdigado_attendance/static/src/js/hr_attendance.js",
"verdigado_attendance/static/src/js/systray.esm.js",
"verdigado_attendance/static/src/js/time_off_calendar.js",
],
"web.assets_qweb": [
"verdigado_attendance/static/src/xml/hr_attendance.xml",
"verdigado_attendance/static/src/xml/hr_holidays.xml",
"verdigado_attendance/static/src/xml/systray.xml",
"verdigado_attendance/static/src/xml/time_off_calendar.xml",
Expand Down
3 changes: 3 additions & 0 deletions verdigado_attendance/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from . import hr_attendance_break
from . import hr_attendance_overtime
from . import hr_attendance_report
from . import hr_employee
from . import hr_leave
from . import hr_leave_type
from . import res_config_settings
from . import res_company
from . import res_users
60 changes: 53 additions & 7 deletions verdigado_attendance/models/hr_attendance.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo import models
from odoo import _, fields, models

from .hr_attendance_break import DatetimeWithoutSeconds

Expand All @@ -10,14 +10,21 @@ class HrAttendance(models.Model):

check_in = DatetimeWithoutSeconds()
check_out = DatetimeWithoutSeconds()
apply_holiday_overtime_factor = fields.Boolean()

def _update_overtime(self, employee_attendance_dates=None):
"""Recreate missing overtime records"""
"""
Recreate missing overtime records to generate correct expected hours
Create adjustments for extra overtime by holiday factor
"""
result = super()._update_overtime(
employee_attendance_dates=employee_attendance_dates
)
if not self.exists():
return result
if employee_attendance_dates is None:
employee_attendance_dates = self._get_attendances_dates()

missing_vals = []
for employee, attendance_dates in employee_attendance_dates.items():
dates = [attendance_date for _dummy, attendance_date in attendance_dates]
Expand All @@ -28,10 +35,49 @@ def _update_overtime(self, employee_attendance_dates=None):
("date", "in", dates),
]
)
missing_vals += [
{"employee_id": employee.id, "date": attendance_date}
for attendance_date in set(dates)
- set(existing_overtime.mapped("date"))
]
for date in dates:
overtime = existing_overtime.filtered(
lambda x: x.date == date and not x.adjustment
)
if not overtime:
# create overtime record for days where worked hours == expected hours
missing_vals += [{"employee_id": employee.id, "date": date}]
continue
holiday_overtime = existing_overtime.filtered(
lambda x: x.date == date and x.holiday_overtime_for_overtime_id
)
factor = employee._get_effective_holiday_overtime_factor(date)
if factor != 1 and any(self.mapped("apply_holiday_overtime_factor")):
# create or update adjustment record to represent extra holiday overtime
duration = overtime.duration * factor - overtime.duration
if holiday_overtime:
holiday_overtime.sudo().duration = duration
else:
missing_vals.append(
{
"employee_id": employee.id,
"date": overtime.date,
"adjustment": True,
"duration": duration,
"holiday_overtime_for_overtime_id": overtime.id,
"note": _("Extra overtime from holiday factor (%.2f)")
% factor,
}
)
else:
holiday_overtime.sudo().unlink()
self.env["hr.attendance.overtime"].sudo().create(missing_vals)
return result

def write(self, vals):
"""Make super update overtimes if we write the factor flag"""
if "apply_holiday_overtime_factor" in vals and not {
"employee_id",
"check_in",
"check_out",
} & set(vals):
result = True
for this in self:
result &= this.write(dict(vals, employee_id=this.employee_id.id))
return result
return super().write(vals)
16 changes: 16 additions & 0 deletions verdigado_attendance/models/hr_attendance_overtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from datetime import datetime, time

import pytz
from psycopg2.extensions import AsIs

from odoo import api, fields, models

Expand All @@ -11,6 +12,21 @@ class HrAttendanceOvertime(models.Model):
_inherit = "hr.attendance.overtime"

expected_hours = fields.Float(compute="_compute_expected_hours", store=True)
holiday_overtime_for_overtime_id = fields.Many2one(
"hr.attendance.overtime", ondelete="cascade"
)

def init(self):
"""forbid more than one holiday overtime adjustment per day/employee"""
result = super().init()
self.env.cr.execute(
"""
CREATE UNIQUE INDEX IF NOT EXISTS hr_attendance_overtime_holiday_adjustment
ON %s (employee_id, date)
WHERE adjustment IS TRUE AND holiday_overtime_for_overtime_id IS NOT NULL""",
(AsIs(self._table),),
)
return result

@api.depends("date", "employee_id", "duration")
def _compute_expected_hours(self):
Expand Down
54 changes: 54 additions & 0 deletions verdigado_attendance/models/hr_employee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo import fields, models


class HrEmployee(models.Model):
_inherit = "hr.employee"

custom_holiday_overtime_factor = fields.Boolean(
help="Use a custom overtime factor for holidays/weekens instead of the company's",
groups="hr.group_hr_user",
)
holiday_overtime_factor = fields.Float(
default=0,
help="When activated on holidays/weekends, overtime is multiplied with this factor",
groups="hr.group_hr_user",
)

def _get_effective_holiday_overtime_factor(self, date=None):
"""Return an employee's effective overtime factor for some date"""
self.ensure_one()
self = self.sudo()
date = (
date
or self.env["hr.attendance"]._get_day_start_and_day(
self,
fields.Datetime.now(),
)[1]
)
return (
(
self.custom_holiday_overtime_factor
and self.holiday_overtime_factor
or self.company_id.holiday_overtime_factor
)
if (
date.isoweekday() >= 6
or self.env["hr.holidays.public"].is_public_holiday(date, self.id)
)
else 1
)

def _attendance_action_change(self):
"""React to default flag for overtime factor"""
result = super()._attendance_action_change()
if "default_apply_holiday_overtime_factor" in self.env.context:
result.write(
{
"apply_holiday_overtime_factor": self.env.context[
"default_apply_holiday_overtime_factor"
],
}
)
return result
7 changes: 6 additions & 1 deletion verdigado_attendance/models/res_company.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo import models
from odoo import fields, models


class ResCompany(models.Model):
_inherit = "res.company"

holiday_overtime_factor = fields.Float(
default=1,
help="When activated on holidays/weekends, overtime is multiplied with this factor",
)

def write(self, vals):
"""Don't delete overtime records that are adjustments when changing overtime settings"""
if "hr_attendance_overtime" in vals or "overtime_start_date" in vals:
Expand Down
13 changes: 13 additions & 0 deletions verdigado_attendance/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2023 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0)


from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

holiday_overtime_factor = fields.Float(
related="company_id.holiday_overtime_factor", readonly=False
)
11 changes: 11 additions & 0 deletions verdigado_attendance/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo import api, models


class ResUsers(models.Model):
_inherit = "res.users"

@api.model
def get_effective_holiday_overtime_factor(self):
return self.env.user.employee_id._get_effective_holiday_overtime_factor()
33 changes: 33 additions & 0 deletions verdigado_attendance/static/src/js/hr_attendance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Copyright 2023 Hunki Enterprises BV
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */

odoo.define("verdigado_attendance.hr_attendance", function (require) {
"use strict";

var myAttendances = require("hr_attendance.my_attendances");

myAttendances.include({
willStart: function () {
var self = this;
var promise = this._rpc({
model: "res.users",
method: "get_effective_holiday_overtime_factor",
}).then(function (data) {
self.effective_holiday_overtime_factor = data;
});
return Promise.all([this._super.apply(this, arguments), promise]);
},
_rpc: function (params) {
if (
params &&
params.model === "hr.employee" &&
params.method === "attendance_manual"
) {
params.context.default_apply_holiday_overtime_factor = this.$(
"#apply_holiday_overtime"
).is(":checked");
}
return this._super.apply(this, arguments);
},
});
});
16 changes: 16 additions & 0 deletions verdigado_attendance/static/src/xml/hr_attendance.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf8" ?>
<templates>
<t t-inherit="hr_attendance.HrAttendanceMyMainMenu">
<xpath expr="//h4[@t-if='checked_in']" position="after">
<div t-if="checked_in and widget.effective_holiday_overtime_factor != 1">
<input type="checkbox" id="apply_holiday_overtime" checked="" />
<label for="apply_holiday_overtime">
Apply holiday overtime factor
<!-- prettier-ignore-start -->
(<t t-out="widget.effective_holiday_overtime_factor" />)
<!-- prettier-ignore-end -->
</label>
</div>
</xpath>
</t>
</templates>
Loading
Loading