summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/hr_holidays/i18n/hr_holidays.pot8
-rw-r--r--addons/hr_holidays/models/hr_leave.py10
-rw-r--r--addons/hr_holidays/tests/test_automatic_leave_dates.py85
-rw-r--r--addons/hr_work_entry/__manifest__.py1
-rw-r--r--addons/hr_work_entry/security/ir.model.access.csv3
-rw-r--r--addons/hr_work_entry/views/resource_views.xml57
-rw-r--r--addons/resource/models/resource.py89
-rw-r--r--addons/web/static/src/scss/report.scss17
8 files changed, 213 insertions, 57 deletions
diff --git a/addons/hr_holidays/i18n/hr_holidays.pot b/addons/hr_holidays/i18n/hr_holidays.pot
index a696285504a..5c00fc18ecb 100644
--- a/addons/hr_holidays/i18n/hr_holidays.pot
+++ b/addons/hr_holidays/i18n/hr_holidays.pot
@@ -2535,6 +2535,14 @@ msgid ""
msgstr ""
#. module: hr_holidays
+#: code:addons/hr_holidays/models/hr_leave.py:0
+#, python-format
+msgid ""
+"The following employees are not supposed to work during that period:\n"
+" %s"
+msgstr ""
+
+#. module: hr_holidays
#: model:ir.model.constraint,message:hr_holidays.constraint_hr_leave_allocation_interval_number_check
msgid "The interval number should be greater than 0"
msgstr ""
diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py
index e7cd61bc870..bb48c9ba6a9 100644
--- a/addons/hr_holidays/models/hr_leave.py
+++ b/addons/hr_holidays/models/hr_leave.py
@@ -549,7 +549,13 @@ class HolidaysRequest(models.Model):
else:
holiday.can_approve = True
- @api.constrains('date_from', 'date_to', 'state', 'employee_id')
+ @api.constrains('date_from', 'date_to')
+ def _check_number_of_days(self):
+ leaves = self.filtered(lambda l: l.employee_id and not l.number_of_days)
+ if leaves:
+ raise ValidationError(_('The following employees are not supposed to work during that period:\n %s') % ','.join(leaves.mapped('employee_id.name')))
+
+ @api.constrains('date_from', 'date_to', 'employee_id')
def _check_date(self):
for holiday in self.filtered('employee_id'):
domain = [
@@ -924,7 +930,7 @@ class HolidaysRequest(models.Model):
'parent_id': self.id,
'employee_id': employee.id,
'state': 'validate',
- } for employee in employees]
+ } for employee in employees if work_days_data[employee.id]['days']]
def action_draft(self):
if any(holiday.state not in ['confirm', 'refuse'] for holiday in self):
diff --git a/addons/hr_holidays/tests/test_automatic_leave_dates.py b/addons/hr_holidays/tests/test_automatic_leave_dates.py
index 3fb05d7f9e5..c19bf449ccc 100644
--- a/addons/hr_holidays/tests/test_automatic_leave_dates.py
+++ b/addons/hr_holidays/tests/test_automatic_leave_dates.py
@@ -4,6 +4,7 @@ from datetime import date, datetime
from odoo.tests.common import Form
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
+from odoo.exceptions import ValidationError
class TestAutomaticLeaveDates(TestHrHolidaysCommon):
@@ -25,15 +26,16 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
employee = self.employee_emp
employee.resource_calendar_id = calendar
- with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
- leave_form.holiday_status_id = self.leave_type
- leave_form.request_date_from = date(2019, 9, 2)
- leave_form.request_date_to = date(2019, 9, 2)
- leave_form.request_unit_half = True
- leave_form.request_date_from_period = 'am'
+ with self.assertRaises(ValidationError):
+ with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
+ leave_form.holiday_status_id = self.leave_type
+ leave_form.request_date_from = date(2019, 9, 2)
+ leave_form.request_date_to = date(2019, 9, 2)
+ leave_form.request_unit_half = True
+ leave_form.request_date_from_period = 'am'
- self.assertEqual(leave_form.number_of_days_display, 0)
- self.assertEqual(leave_form.number_of_hours_text, '0.0 Hours')
+ self.assertEqual(leave_form.number_of_days_display, 0)
+ self.assertEqual(leave_form.number_of_hours_text, '0.0 Hours')
def test_single_attendance_on_morning_and_afternoon(self):
calendar = self.env['resource.calendar'].create({
@@ -164,18 +166,19 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
employee = self.employee_emp
employee.resource_calendar_id = calendar
- with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
- leave_form.holiday_status_id = self.leave_type
- leave_form.request_date_from = date(2019, 9, 2)
- leave_form.request_date_to = date(2019, 9, 2)
- leave_form.request_unit_half = True
- leave_form.request_date_from_period = 'am'
+ with self.assertRaises(ValidationError):
+ with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
+ leave_form.holiday_status_id = self.leave_type
+ leave_form.request_date_from = date(2019, 9, 2)
+ leave_form.request_date_to = date(2019, 9, 2)
+ leave_form.request_unit_half = True
+ leave_form.request_date_from_period = 'am'
- self.assertEqual(leave_form.number_of_days_display, 0)
- self.assertEqual(leave_form.number_of_hours_text, '0.0 Hours')
- self.assertEqual(leave_form.date_from, datetime(2019, 9, 2, 6, 0, 0))
- self.assertEqual(leave_form.date_to, datetime(2019, 9, 2, 10, 0, 0))
+ self.assertEqual(leave_form.number_of_days_display, 0)
+ self.assertEqual(leave_form.number_of_hours_text, '0.0 Hours')
+ self.assertEqual(leave_form.date_from, datetime(2019, 9, 2, 6, 0, 0))
+ self.assertEqual(leave_form.date_to, datetime(2019, 9, 2, 10, 0, 0))
def test_attendance_previous_day(self):
self.env.user.tz = 'Europe/Brussels'
@@ -193,18 +196,19 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
employee = self.employee_emp
employee.resource_calendar_id = calendar
- with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
- leave_form.holiday_status_id = self.leave_type
- leave_form.request_date_from = date(2019, 9, 3)
- leave_form.request_date_to = date(2019, 9, 3)
- leave_form.request_unit_half = True
- leave_form.request_date_from_period = 'am'
+ with self.assertRaises(ValidationError):
+ with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
+ leave_form.holiday_status_id = self.leave_type
+ leave_form.request_date_from = date(2019, 9, 3)
+ leave_form.request_date_to = date(2019, 9, 3)
+ leave_form.request_unit_half = True
+ leave_form.request_date_from_period = 'am'
- self.assertEqual(leave_form.number_of_days_display, 0)
- self.assertEqual(leave_form.number_of_hours_text, '0.0 Hours')
- self.assertEqual(leave_form.date_from, datetime(2019, 9, 3, 6, 0, 0))
- self.assertEqual(leave_form.date_to, datetime(2019, 9, 3, 10, 0, 0))
+ self.assertEqual(leave_form.number_of_days_display, 0)
+ self.assertEqual(leave_form.number_of_hours_text, '0.0 Hours')
+ self.assertEqual(leave_form.date_from, datetime(2019, 9, 3, 6, 0, 0))
+ self.assertEqual(leave_form.date_to, datetime(2019, 9, 3, 10, 0, 0))
def test_2weeks_calendar(self):
self.env.user.tz = 'Europe/Brussels'
@@ -276,15 +280,16 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
employee = self.employee_emp
employee.resource_calendar_id = calendar
- with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
- leave_form.holiday_status_id = self.leave_type
- # even week, does not work
- leave_form.request_date_from = date(2019, 9, 2)
- leave_form.request_date_to = date(2019, 9, 2)
- leave_form.request_unit_half = True
- leave_form.request_date_from_period = 'am'
-
- self.assertEqual(leave_form.number_of_days_display, 0)
- self.assertEqual(leave_form.number_of_hours_text, '0.0 Hours')
- self.assertEqual(leave_form.date_from, datetime(2019, 9, 2, 6, 0, 0))
- self.assertEqual(leave_form.date_to, datetime(2019, 9, 2, 10, 0, 0))
+ with self.assertRaises(ValidationError):
+ with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
+ leave_form.holiday_status_id = self.leave_type
+ # even week, does not work
+ leave_form.request_date_from = date(2019, 9, 2)
+ leave_form.request_date_to = date(2019, 9, 2)
+ leave_form.request_unit_half = True
+ leave_form.request_date_from_period = 'am'
+
+ self.assertEqual(leave_form.number_of_days_display, 0)
+ self.assertEqual(leave_form.number_of_hours_text, '0.0 Hours')
+ self.assertEqual(leave_form.date_from, datetime(2019, 9, 2, 6, 0, 0))
+ self.assertEqual(leave_form.date_to, datetime(2019, 9, 2, 10, 0, 0))
diff --git a/addons/hr_work_entry/__manifest__.py b/addons/hr_work_entry/__manifest__.py
index 4c2fe52cd77..aaee9c1ab4c 100644
--- a/addons/hr_work_entry/__manifest__.py
+++ b/addons/hr_work_entry/__manifest__.py
@@ -16,6 +16,7 @@
'security/ir.model.access.csv',
'data/hr_work_entry_data.xml',
'views/hr_work_entry_views.xml',
+ 'views/resource_views.xml',
],
'qweb': [
"static/src/xml/work_entry_templates.xml",
diff --git a/addons/hr_work_entry/security/ir.model.access.csv b/addons/hr_work_entry/security/ir.model.access.csv
index 7350f08cb78..ac50de79e81 100644
--- a/addons/hr_work_entry/security/ir.model.access.csv
+++ b/addons/hr_work_entry/security/ir.model.access.csv
@@ -1,5 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_hr_work_entry_officer,access_hr_work_entry_officer,model_hr_work_entry,hr.group_hr_user,1,1,1,1
+access_hr_work_entry_officer,access_hr_work_entry_officer,model_hr_work_entry,hr.group_hr_user,1,1,1,0
+access_hr_work_entry_system,access_hr_work_entry_system,model_hr_work_entry,base.group_system,1,1,1,1
access_hr_work_entry_type_officer,access_hr_work_entry_type_officer,model_hr_work_entry_type,hr.group_hr_user,1,0,0,0
access_hr_work_entry_type_manager,access_hr_work_entry_type_manager,model_hr_work_entry_type,hr.group_hr_manager,1,1,1,1
access_hr_work_entry_employee,access_hr_work_entry_employee,model_hr_user_work_entry_employee,hr.group_hr_user,1,1,1,1 \ No newline at end of file
diff --git a/addons/hr_work_entry/views/resource_views.xml b/addons/hr_work_entry/views/resource_views.xml
new file mode 100644
index 00000000000..0d8aae76617
--- /dev/null
+++ b/addons/hr_work_entry/views/resource_views.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="resource_calendar_leave_view_form" model="ir.ui.view">
+ <field name="name">resource.calendar.leaves.view.form</field>
+ <field name="model">resource.calendar.leaves</field>
+ <field name="inherit_id" ref="resource.resource_calendar_leave_form"/>
+ <field name="arch" type="xml">
+ <field name="resource_id" position="after">
+ <field name="work_entry_type_id"/>
+ </field>
+ </field>
+ </record>
+
+ <record id="resource_calendar_leave_view_tree" model="ir.ui.view">
+ <field name="name">resource.calendar.leaves.view.tree</field>
+ <field name="model">resource.calendar.leaves</field>
+ <field name="inherit_id" ref="resource.resource_calendar_leave_tree"/>
+ <field name="arch" type="xml">
+ <field name="date_to" position="after">
+ <field name="work_entry_type_id"/>
+ </field>
+ </field>
+ </record>
+
+ <record id="resource_calendar_attendance_view_tree" model="ir.ui.view">
+ <field name="name">resource.calendar.attendance.view.tree</field>
+ <field name="model">resource.calendar.attendance</field>
+ <field name="inherit_id" ref="resource.view_resource_calendar_attendance_tree"/>
+ <field name="arch" type="xml">
+ <field name="week_type" position="after">
+ <field name="work_entry_type_id"/>
+ </field>
+ </field>
+ </record>
+
+ <record id="resource_calendar_attendance_view_form" model="ir.ui.view">
+ <field name="name">resource.calendar.attendance.view.form</field>
+ <field name="model">resource.calendar.attendance</field>
+ <field name="inherit_id" ref="resource.view_resource_calendar_attendance_form"/>
+ <field name="arch" type="xml">
+ <field name="day_period" position="after">
+ <field name="work_entry_type_id"/>
+ </field>
+ </field>
+ </record>
+
+ <record id="resource_calendar_view_form" model="ir.ui.view">
+ <field name="name">resource.calendar.view.form</field>
+ <field name="model">resource.calendar</field>
+ <field name="inherit_id" ref="resource.resource_calendar_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//page[@name='global_leaves']//field[@name='date_to']" position="after">
+ <field name="work_entry_type_id"/>
+ </xpath>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/resource/models/resource.py b/addons/resource/models/resource.py
index 51edc52d8a7..4697c35b035 100644
--- a/addons/resource/models/resource.py
+++ b/addons/resource/models/resource.py
@@ -159,18 +159,33 @@ class ResourceCalendar(models.Model):
if not res.get('name') and res.get('company_id'):
res['name'] = _('Working Hours of %s', self.env['res.company'].browse(res['company_id']).name)
if 'attendance_ids' in fields and not res.get('attendance_ids'):
- res['attendance_ids'] = [
- (0, 0, {'name': _('Monday Morning'), 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
- (0, 0, {'name': _('Monday Afternoon'), 'dayofweek': '0', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
- (0, 0, {'name': _('Tuesday Morning'), 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
- (0, 0, {'name': _('Tuesday Afternoon'), 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
- (0, 0, {'name': _('Wednesday Morning'), 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
- (0, 0, {'name': _('Wednesday Afternoon'), 'dayofweek': '2', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
- (0, 0, {'name': _('Thursday Morning'), 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
- (0, 0, {'name': _('Thursday Afternoon'), 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
- (0, 0, {'name': _('Friday Morning'), 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
- (0, 0, {'name': _('Friday Afternoon'), 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})
- ]
+ company_id = res.get('company_id', self.env.company.id)
+ company = self.env['res.company'].browse(company_id)
+ company_attendance_ids = company.resource_calendar_id.attendance_ids
+ if company_attendance_ids:
+ res['attendance_ids'] = [
+ (0, 0, {
+ 'name': attendance.name,
+ 'dayofweek': attendance.dayofweek,
+ 'hour_from': attendance.hour_from,
+ 'hour_to': attendance.hour_to,
+ 'day_period': attendance.day_period,
+ })
+ for attendance in company_attendance_ids
+ ]
+ else:
+ res['attendance_ids'] = [
+ (0, 0, {'name': _('Monday Morning'), 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
+ (0, 0, {'name': _('Monday Afternoon'), 'dayofweek': '0', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
+ (0, 0, {'name': _('Tuesday Morning'), 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
+ (0, 0, {'name': _('Tuesday Afternoon'), 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
+ (0, 0, {'name': _('Wednesday Morning'), 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
+ (0, 0, {'name': _('Wednesday Afternoon'), 'dayofweek': '2', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
+ (0, 0, {'name': _('Thursday Morning'), 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
+ (0, 0, {'name': _('Thursday Afternoon'), 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
+ (0, 0, {'name': _('Friday Morning'), 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
+ (0, 0, {'name': _('Friday Afternoon'), 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})
+ ]
return res
name = fields.Char(required=True)
@@ -181,13 +196,14 @@ class ResourceCalendar(models.Model):
default=lambda self: self.env.company)
attendance_ids = fields.One2many(
'resource.calendar.attendance', 'calendar_id', 'Working Time',
- copy=True)
+ compute='_compute_attendance_ids', store=True, readonly=False, copy=True)
leave_ids = fields.One2many(
'resource.calendar.leaves', 'calendar_id', 'Time Off')
global_leave_ids = fields.One2many(
'resource.calendar.leaves', 'calendar_id', 'Global Time Off',
+ compute='_compute_global_leave_ids', store=True, readonly=False,
domain=[('resource_id', '=', False)], copy=True,
- )
+ )
hours_per_day = fields.Float("Average Hour per Day", default=HOURS_PER_DAY,
help="Average hours per day a resource is supposed to work with this calendar.")
tz = fields.Selection(
@@ -197,6 +213,42 @@ class ResourceCalendar(models.Model):
two_weeks_calendar = fields.Boolean(string="Calendar in 2 weeks mode")
two_weeks_explanation = fields.Char('Explanation', compute="_compute_two_weeks_explanation")
+ @api.depends('company_id')
+ def _compute_attendance_ids(self):
+ for calendar in self.filtered(lambda c: not c._origin or c._origin.company_id != c.company_id):
+ company_calendar = calendar.company_id.resource_calendar_id
+ calendar.write({
+ 'two_weeks_calendar': company_calendar.two_weeks_calendar,
+ 'hours_per_day': company_calendar.hours_per_day,
+ 'tz': company_calendar.tz,
+ 'attendance_ids': [(5, 0, 0)] + [
+ (0, 0, {
+ 'name': attendance.name,
+ 'dayofweek': attendance.dayofweek,
+ 'date_from': attendance.date_from,
+ 'date_to': attendance.date_to,
+ 'hour_from': attendance.hour_from,
+ 'hour_to': attendance.hour_to,
+ 'day_period': attendance.day_period,
+ 'week_type': attendance.week_type,
+ 'display_type': attendance.display_type,
+ 'sequence': attendance.sequence,
+ }) for attendance in company_calendar.attendance_ids if not attendance.resource_id]
+ })
+
+ @api.depends('company_id')
+ def _compute_global_leave_ids(self):
+ for calendar in self.filtered(lambda c: not c._origin or c._origin.company_id != c.company_id):
+ calendar.write({
+ 'global_leave_ids': [(5, 0, 0)] + [
+ (0, 0, {
+ 'name': leave.name,
+ 'date_from': leave.date_from,
+ 'date_to': leave.date_to,
+ 'time_type': leave.time_type,
+ }) for leave in calendar.company_id.resource_calendar_id.global_leave_ids]
+ })
+
@api.returns('self', lambda value: value.id)
def copy(self, default=None):
self.ensure_one()
@@ -743,6 +795,15 @@ class ResourceCalendar(models.Model):
else:
return revert(day_dt)
+ def _get_max_number_of_hours(self, start, end):
+ self.ensure_one()
+ if not self.attendance_ids:
+ return 0
+ mapped_data = defaultdict(lambda: 0)
+ for attendance in self.attendance_ids.filtered(lambda a: (not a.date_from or not a.date_to) or (a.date_from <= end and a.date_to >= start)):
+ mapped_data[(attendance.week_type, attendance.dayofweek)] += attendance.hour_to - attendance.hour_from
+ return max(mapped_data.values())
+
class ResourceCalendarAttendance(models.Model):
_name = "resource.calendar.attendance"
diff --git a/addons/web/static/src/scss/report.scss b/addons/web/static/src/scss/report.scss
index 91500a689a7..0edc31005f4 100644
--- a/addons/web/static/src/scss/report.scss
+++ b/addons/web/static/src/scss/report.scss
@@ -60,6 +60,23 @@ div#total {
td {
border-top: 1px solid black !important;
}
+ &.o_border_bottom {
+ td {
+ border-bottom: 1px solid black !important;
+ }
+ }
+ }
+ }
+ }
+}
+
+table {
+ thead {
+ &.o_black_border {
+ tr {
+ th {
+ border-bottom: 2px solid black !important;
+ }
}
}
}