Skip to content

Commit 9c2f8ee

Browse files
authored
Merge pull request #81 from hbrunn/15.0-57-holiday_overtime_factor
[ADD] #57 allow higher value overtime for holidays
2 parents 172b932 + 6742132 commit 9c2f8ee

15 files changed

+334
-51
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
- name: Run tests
7777
run: oca_run_tests
7878
- name: Generate coverage.xml
79-
run: coverage xml --include '*.py'
79+
run: coverage xml --include '*.py' --omit '**/tests/*'
8080
- uses: codecov/codecov-action@v3
8181
with:
8282
files: coverage.xml

verdigado_attendance/__manifest__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@
4141
"views/base_ical.xml",
4242
"views/hr_attendance_view.xml",
4343
"views/hr_attendance_report.xml",
44+
"views/hr_employee.xml",
4445
"views/hr_leave_type.xml",
4546
"views/hr_leave.xml",
4647
"views/hr_menu_human_resources_configuration.xml",
4748
"views/menu.xml",
49+
"views/res_config_settings.xml",
4850
],
4951
"demo": [
5052
"demo/res_users.xml",
@@ -60,10 +62,12 @@
6062
],
6163
"web.assets_backend": [
6264
"verdigado_attendance/static/src/scss/backend.scss",
65+
"verdigado_attendance/static/src/js/hr_attendance.js",
6366
"verdigado_attendance/static/src/js/systray.esm.js",
6467
"verdigado_attendance/static/src/js/time_off_calendar.js",
6568
],
6669
"web.assets_qweb": [
70+
"verdigado_attendance/static/src/xml/hr_attendance.xml",
6771
"verdigado_attendance/static/src/xml/hr_holidays.xml",
6872
"verdigado_attendance/static/src/xml/systray.xml",
6973
"verdigado_attendance/static/src/xml/time_off_calendar.xml",

verdigado_attendance/models/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
from . import hr_attendance_break
66
from . import hr_attendance_overtime
77
from . import hr_attendance_report
8+
from . import hr_employee
89
from . import hr_leave
910
from . import hr_leave_type
11+
from . import res_config_settings
1012
from . import res_company
13+
from . import res_users
Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
22

3-
from odoo import models
3+
from odoo import _, fields, models
44

55
from .hr_attendance_break import DatetimeWithoutSeconds
66

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

1111
check_in = DatetimeWithoutSeconds()
1212
check_out = DatetimeWithoutSeconds()
13+
apply_holiday_overtime_factor = fields.Boolean()
1314

1415
def _update_overtime(self, employee_attendance_dates=None):
15-
"""Recreate missing overtime records"""
16+
"""
17+
Recreate missing overtime records to generate correct expected hours
18+
Create adjustments for extra overtime by holiday factor
19+
"""
1620
result = super()._update_overtime(
1721
employee_attendance_dates=employee_attendance_dates
1822
)
23+
if not self.exists():
24+
return result
1925
if employee_attendance_dates is None:
2026
employee_attendance_dates = self._get_attendances_dates()
27+
2128
missing_vals = []
2229
for employee, attendance_dates in employee_attendance_dates.items():
2330
dates = [attendance_date for _dummy, attendance_date in attendance_dates]
@@ -28,10 +35,49 @@ def _update_overtime(self, employee_attendance_dates=None):
2835
("date", "in", dates),
2936
]
3037
)
31-
missing_vals += [
32-
{"employee_id": employee.id, "date": attendance_date}
33-
for attendance_date in set(dates)
34-
- set(existing_overtime.mapped("date"))
35-
]
38+
for date in dates:
39+
overtime = existing_overtime.filtered(
40+
lambda x: x.date == date and not x.adjustment
41+
)
42+
if not overtime:
43+
# create overtime record for days where worked hours == expected hours
44+
missing_vals += [{"employee_id": employee.id, "date": date}]
45+
continue
46+
holiday_overtime = existing_overtime.filtered(
47+
lambda x: x.date == date and x.holiday_overtime_for_overtime_id
48+
)
49+
factor = employee._get_effective_holiday_overtime_factor(date)
50+
if factor != 1 and any(self.mapped("apply_holiday_overtime_factor")):
51+
# create or update adjustment record to represent extra holiday overtime
52+
duration = overtime.duration * factor - overtime.duration
53+
if holiday_overtime:
54+
holiday_overtime.sudo().duration = duration
55+
else:
56+
missing_vals.append(
57+
{
58+
"employee_id": employee.id,
59+
"date": overtime.date,
60+
"adjustment": True,
61+
"duration": duration,
62+
"holiday_overtime_for_overtime_id": overtime.id,
63+
"note": _("Extra overtime from holiday factor (%.2f)")
64+
% factor,
65+
}
66+
)
67+
else:
68+
holiday_overtime.sudo().unlink()
3669
self.env["hr.attendance.overtime"].sudo().create(missing_vals)
3770
return result
71+
72+
def write(self, vals):
73+
"""Make super update overtimes if we write the factor flag"""
74+
if "apply_holiday_overtime_factor" in vals and not {
75+
"employee_id",
76+
"check_in",
77+
"check_out",
78+
} & set(vals):
79+
result = True
80+
for this in self:
81+
result &= this.write(dict(vals, employee_id=this.employee_id.id))
82+
return result
83+
return super().write(vals)

verdigado_attendance/models/hr_attendance_overtime.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from datetime import datetime, time
44

55
import pytz
6+
from psycopg2.extensions import AsIs
67

78
from odoo import api, fields, models
89

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

1314
expected_hours = fields.Float(compute="_compute_expected_hours", store=True)
15+
holiday_overtime_for_overtime_id = fields.Many2one(
16+
"hr.attendance.overtime", ondelete="cascade"
17+
)
18+
19+
def init(self):
20+
"""forbid more than one holiday overtime adjustment per day/employee"""
21+
result = super().init()
22+
self.env.cr.execute(
23+
"""
24+
CREATE UNIQUE INDEX IF NOT EXISTS hr_attendance_overtime_holiday_adjustment
25+
ON %s (employee_id, date)
26+
WHERE adjustment IS TRUE AND holiday_overtime_for_overtime_id IS NOT NULL""",
27+
(AsIs(self._table),),
28+
)
29+
return result
1430

1531
@api.depends("date", "employee_id", "duration")
1632
def _compute_expected_hours(self):
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
2+
3+
from odoo import fields, models
4+
5+
6+
class HrEmployee(models.Model):
7+
_inherit = "hr.employee"
8+
9+
custom_holiday_overtime_factor = fields.Boolean(
10+
help="Use a custom overtime factor for holidays/weekens instead of the company's",
11+
groups="hr.group_hr_user",
12+
)
13+
holiday_overtime_factor = fields.Float(
14+
default=0,
15+
help="When activated on holidays/weekends, overtime is multiplied with this factor",
16+
groups="hr.group_hr_user",
17+
)
18+
19+
def _get_effective_holiday_overtime_factor(self, date=None):
20+
"""Return an employee's effective overtime factor for some date"""
21+
self.ensure_one()
22+
self = self.sudo()
23+
date = (
24+
date
25+
or self.env["hr.attendance"]._get_day_start_and_day(
26+
self,
27+
fields.Datetime.now(),
28+
)[1]
29+
)
30+
return (
31+
(
32+
self.custom_holiday_overtime_factor
33+
and self.holiday_overtime_factor
34+
or self.company_id.holiday_overtime_factor
35+
)
36+
if (
37+
date.isoweekday() >= 6
38+
or self.env["hr.holidays.public"].is_public_holiday(date, self.id)
39+
)
40+
else 1
41+
)
42+
43+
def _attendance_action_change(self):
44+
"""React to default flag for overtime factor"""
45+
result = super()._attendance_action_change()
46+
if "default_apply_holiday_overtime_factor" in self.env.context:
47+
result.write(
48+
{
49+
"apply_holiday_overtime_factor": self.env.context[
50+
"default_apply_holiday_overtime_factor"
51+
],
52+
}
53+
)
54+
return result

verdigado_attendance/models/res_company.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
22

3-
from odoo import models
3+
from odoo import fields, models
44

55

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

9+
holiday_overtime_factor = fields.Float(
10+
default=1,
11+
help="When activated on holidays/weekends, overtime is multiplied with this factor",
12+
)
13+
914
def write(self, vals):
1015
"""Don't delete overtime records that are adjustments when changing overtime settings"""
1116
if "hr_attendance_overtime" in vals or "overtime_start_date" in vals:
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2023 Hunki Enterprises BV
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0)
3+
4+
5+
from odoo import fields, models
6+
7+
8+
class ResConfigSettings(models.TransientModel):
9+
_inherit = "res.config.settings"
10+
11+
holiday_overtime_factor = fields.Float(
12+
related="company_id.holiday_overtime_factor", readonly=False
13+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
2+
3+
from odoo import api, models
4+
5+
6+
class ResUsers(models.Model):
7+
_inherit = "res.users"
8+
9+
@api.model
10+
def get_effective_holiday_overtime_factor(self):
11+
return self.env.user.employee_id._get_effective_holiday_overtime_factor()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* Copyright 2023 Hunki Enterprises BV
2+
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
3+
4+
odoo.define("verdigado_attendance.hr_attendance", function (require) {
5+
"use strict";
6+
7+
var myAttendances = require("hr_attendance.my_attendances");
8+
9+
myAttendances.include({
10+
willStart: function () {
11+
var self = this;
12+
var promise = this._rpc({
13+
model: "res.users",
14+
method: "get_effective_holiday_overtime_factor",
15+
}).then(function (data) {
16+
self.effective_holiday_overtime_factor = data;
17+
});
18+
return Promise.all([this._super.apply(this, arguments), promise]);
19+
},
20+
_rpc: function (params) {
21+
if (
22+
params &&
23+
params.model === "hr.employee" &&
24+
params.method === "attendance_manual"
25+
) {
26+
params.context.default_apply_holiday_overtime_factor = this.$(
27+
"#apply_holiday_overtime"
28+
).is(":checked");
29+
}
30+
return this._super.apply(this, arguments);
31+
},
32+
});
33+
});

0 commit comments

Comments
 (0)