From a3daff6bc121a42bf293ca91b8f522c26bac7c8a Mon Sep 17 00:00:00 2001 From: jbm-odoo Date: Thu, 13 Dec 2018 11:10:15 +0000 Subject: [PATCH] [IMP] hr_holidays: Rename 'leave' into 'time off' + add dashboard Purpose ======= We need to use an international word to define holidays and leaves, rename everything into Time Off. Employees needs a good dashboard and leaves summary Specification ============= 1/ Add a new dashboard: https://balsamiq.cloud/sm5j0d/pg5w04r Create a new dashboard with a calendar and a summary of all time off and time off request 2/ Rename leaves into Time Off everywhere 3/ Improve general usability TaskID: 1916871 closes odoo/odoo#29634 --- .../analytic/data/analytic_account_demo.xml | 2 +- addons/hr/README.md | 4 +- addons/hr_holidays/__manifest__.py | 21 +- addons/hr_holidays/data/hr_holidays_data.xml | 4 +- addons/hr_holidays/data/hr_holidays_demo.xml | 47 +---- addons/hr_holidays/data/ir_cron_data.xml | 2 +- addons/hr_holidays/data/mail_data.xml | 16 +- .../hr_holidays/data/report_paperformat.xml | 2 +- addons/hr_holidays/models/hr.py | 20 +- addons/hr_holidays/models/hr_leave.py | 66 +++--- .../hr_holidays/models/hr_leave_allocation.py | 46 ++--- addons/hr_holidays/models/hr_leave_type.py | 108 +++++++--- .../report/hr_holidays_reports.xml | 4 +- .../report/hr_holidays_templates.xml | 6 +- addons/hr_holidays/report/hr_leave_report.py | 4 +- .../hr_holidays/report/hr_leave_reports.xml | 40 +--- .../security/hr_holidays_security.xml | 18 +- .../static/src/js/time_off_calendar.js | 192 ++++++++++++++++++ .../static/src/xml/time_off_calendar.xml | 26 +++ .../hr_holidays/tests/test_holidays_flow.py | 2 +- .../hr_holidays/tests/test_hr_leave_type.py | 2 +- .../hr_holidays/tests/test_leave_requests.py | 2 +- .../views/hr_leave_allocation_views.xml | 27 ++- .../hr_holidays/views/hr_leave_template.xml | 8 + .../hr_holidays/views/hr_leave_type_views.xml | 22 +- addons/hr_holidays/views/hr_leave_views.xml | 141 +++++++------ addons/hr_holidays/views/hr_views.xml | 12 +- .../wizard/hr_holidays_summary_department.py | 4 +- .../hr_holidays_summary_department_views.xml | 4 +- .../wizard/hr_holidays_summary_employees.py | 4 +- .../hr_holidays_summary_employees_views.xml | 4 +- addons/hr_payroll/data/hr_payroll_data.xml | 2 +- addons/hr_payroll/data/hr_payroll_demo.xml | 6 +- addons/hr_payroll/tests/test_benefit.py | 2 +- .../hr_presence/views/hr_employee_views.xml | 2 +- .../models/res_config_settings.py | 2 +- .../views/res_config_settings_views.xml | 4 +- .../models/l10n_be_hr_payroll.py | 2 +- .../models/res_config_settings.py | 2 +- .../data/l10n_be_hr_payroll_account_demo.xml | 4 +- .../__manifest__.py | 4 +- .../models/hr_holidays.py | 36 ++-- .../models/res_company.py | 6 +- .../models/res_config_settings.py | 2 +- .../views/hr_holidays_views.xml | 11 +- addons/resource/models/resource.py | 14 +- addons/resource/views/resource_views.xml | 12 +- .../base/data/ir_module_category_data.xml | 4 +- 48 files changed, 612 insertions(+), 363 deletions(-) create mode 100644 addons/hr_holidays/static/src/js/time_off_calendar.js create mode 100644 addons/hr_holidays/static/src/xml/time_off_calendar.xml create mode 100644 addons/hr_holidays/views/hr_leave_template.xml diff --git a/addons/analytic/data/analytic_account_demo.xml b/addons/analytic/data/analytic_account_demo.xml index 296bfe97820..8ed8360590d 100644 --- a/addons/analytic/data/analytic_account_demo.xml +++ b/addons/analytic/data/analytic_account_demo.xml @@ -13,7 +13,7 @@ - Leaves + Time Off diff --git a/addons/hr/README.md b/addons/hr/README.md index a561812b47a..e118942284d 100644 --- a/addons/hr/README.md +++ b/addons/hr/README.md @@ -45,11 +45,11 @@ Keep track of the time spent by project, client or task. It's easy to record timesheets or check attendances for each employee. Get your analytic accounting posted automatically based on time spent on your projects. -Leaves Management +Time Off Management ----------------- Keep track of the vacation days accrued by each employee. Employees enter their -requests (paid leaves, sick leaves, etc), for managers to approve and +requests (paid time off, sick time off, etc), for managers to approve and validate. It's all done in just a few clicks. The agenda of each employee is updated accordingly. diff --git a/addons/hr_holidays/__manifest__.py b/addons/hr_holidays/__manifest__.py index 9c765ea2596..50f3d79f826 100644 --- a/addons/hr_holidays/__manifest__.py +++ b/addons/hr_holidays/__manifest__.py @@ -2,26 +2,26 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. { - 'name': 'Leaves', + 'name': 'Time Off', 'version': '1.5', 'category': 'Human Resources', - 'summary': 'Allocate leaves and follow leave requests', + 'summary': 'Allocate time off and follow time off requests', 'website': 'https://www.odoo.com/page/leaves', 'description': """ -Manage leave requests and allocations +Manage time off requests and allocations ===================================== -This application controls the leave schedule of your company. It allows employees to request leaves. Then, managers can review requests for leaves and approve or reject them. This way you can control the overall leave planning for the company or department. +This application controls the time off schedule of your company. It allows employees to request time off. Then, managers can review requests for time off and approve or reject them. This way you can control the overall time off planning for the company or department. -You can configure several kinds of leaves (sickness, paid days, ...) and allocate leaves to an employee or department quickly using leave allocation. An employee can also make a request for more days off by making a new Leave allocation. It will increase the total of available days for that leave type (if the request is accepted). +You can configure several kinds of time off (sickness, paid days, ...) and allocate time off to an employee or department quickly using time off allocation. An employee can also make a request for more days off by making a new time off allocation. It will increase the total of available days for that time off type (if the request is accepted). -You can keep track of leaves in different ways by following reports: +You can keep track of time off in different ways by following reports: -* Leaves Summary -* Leaves by Department -* Leaves Analysis +* Time Off Summary +* Time Off by Department +* Time Off Analysis -A synchronization with an internal agenda (Meetings of the CRM module) is also possible in order to automatically create a meeting when a leave request is accepted by setting up a type of meeting in Leave Type. +A synchronization with an internal agenda (Meetings of the CRM module) is also possible in order to automatically create a meeting when a time off request is accepted by setting up a type of meeting in time off Type. """, 'depends': ['hr', 'calendar', 'resource'], 'data': [ @@ -47,6 +47,7 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p 'report/hr_leave_reports.xml', 'views/hr_views.xml', + 'views/hr_leave_template.xml', ], 'demo': [ 'data/hr_holidays_demo.xml', diff --git a/addons/hr_holidays/data/hr_holidays_data.xml b/addons/hr_holidays/data/hr_holidays_data.xml index 56636156f7b..9b8b1b78512 100644 --- a/addons/hr_holidays/data/hr_holidays_data.xml +++ b/addons/hr_holidays/data/hr_holidays_data.xml @@ -3,7 +3,7 @@ - + fixed black @@ -21,7 +21,7 @@ - Sick Leaves + Sick Time Off no red diff --git a/addons/hr_holidays/data/hr_holidays_demo.xml b/addons/hr_holidays/data/hr_holidays_demo.xml index 4bc8f560b55..b39754605b5 100644 --- a/addons/hr_holidays/data/hr_holidays_demo.xml +++ b/addons/hr_holidays/data/hr_holidays_demo.xml @@ -11,21 +11,12 @@ - Parental Leaves + Parental Time Off fixed brown both - - Team Building - no - lightblue - manager - - - - fixed @@ -34,30 +25,10 @@ - - - - - - - - 1 - company - - - - - - - - - Legal Leaves for Mitchell Admin + Paid Time Off for Mitchell Admin 20 @@ -137,7 +108,7 @@ - Legal Leaves for Ronnie Hart + Paid Time Off for Ronnie Hart 20 @@ -145,7 +116,7 @@ - Parental Leaves + Parental Time Off 10 @@ -216,7 +187,7 @@ - Legal Leaves for Anita Oliver + Paid Time Off for Anita Oliver 20 @@ -297,7 +268,7 @@ - Legal Leaves for Gilles Gravie + Paid Time Off for Gilles Gravie 20 @@ -370,7 +341,7 @@ - Legal Leaves for Audrey Peterson + Paid Time Off for Audrey Peterson 20 @@ -473,7 +444,7 @@ - Legal Leaves for Olivia + Paid Time Off for Olivia 20 @@ -547,7 +518,7 @@ - Legal Leaves for Kim + Paid Time Off for Kim 20 diff --git a/addons/hr_holidays/data/ir_cron_data.xml b/addons/hr_holidays/data/ir_cron_data.xml index a162d5c51f5..4dca974e8ae 100644 --- a/addons/hr_holidays/data/ir_cron_data.xml +++ b/addons/hr_holidays/data/ir_cron_data.xml @@ -2,7 +2,7 @@ - Accrual Leave: Updates the number of leaves + Accrual Time Off: Updates the number of time off code model._update_accrual() diff --git a/addons/hr_holidays/data/mail_data.xml b/addons/hr_holidays/data/mail_data.xml index 9b0a62f06c4..253cac416fe 100644 --- a/addons/hr_holidays/data/mail_data.xml +++ b/addons/hr_holidays/data/mail_data.xml @@ -3,12 +3,12 @@ - Leaves Approval + Time Off Approval fa-sun-o - Leaves Second Approve + Time Off Second Approve fa-sun-o @@ -27,9 +27,9 @@ - Leave + Time Off hr.leave - Leave Request + Time Off Request Home Working @@ -37,14 +37,14 @@ Home Working - Sick Leave + Sick Time Off hr.leave - Sick Leave + Sick Time Off - Unpaid Leave + Unpaid Time Off hr.leave - Unpaid Leave + Unpaid Time Off diff --git a/addons/hr_holidays/data/report_paperformat.xml b/addons/hr_holidays/data/report_paperformat.xml index 88c02ae09b2..820e43fb008 100644 --- a/addons/hr_holidays/data/report_paperformat.xml +++ b/addons/hr_holidays/data/report_paperformat.xml @@ -1,7 +1,7 @@ - Leaves Summary + Time Off Summary custom 297 diff --git a/addons/hr_holidays/models/hr.py b/addons/hr_holidays/models/hr.py index e24007d8aa2..26ba7ec4d5d 100644 --- a/addons/hr_holidays/models/hr.py +++ b/addons/hr_holidays/models/hr.py @@ -16,7 +16,7 @@ class Department(models.Model): absence_of_today = fields.Integer( compute='_compute_leave_count', string='Absence by Today') leave_to_approve_count = fields.Integer( - compute='_compute_leave_count', string='Leave to Approve') + compute='_compute_leave_count', string='Time Off to Approve') allocation_to_approve_count = fields.Integer( compute='_compute_leave_count', string='Allocation to Approve') total_employee = fields.Integer( @@ -64,14 +64,14 @@ class Employee(models.Model): _inherit = "hr.employee" leave_manager_id = fields.Many2one( - 'res.users', string='Leave Responsible', + 'res.users', string='Time Off Responsible', domain=lambda self: [('groups_id', 'in', self.env.ref('hr_holidays.group_hr_holidays_team_leader').id)], - help="User responsible of leaves approval. Should be Team Leader or Department Manager.") + help="User responsible of time off approval. Should be Team Leader or Department Manager.") remaining_leaves = fields.Float( - compute='_compute_remaining_leaves', string='Remaining Legal Leaves', - help='Total number of legal leaves allocated to this employee, change this value to create allocation/leave request. ' - 'Total based on all the leave types without overriding limit.') - current_leave_state = fields.Selection(compute='_compute_leave_status', string="Current Leave Status", + compute='_compute_remaining_leaves', string='Remaining Paid Time Off', + help='Total number of paid time off allocated to this employee, change this value to create allocation/time off request. ' + 'Total based on all the time off types without overriding limit.') + current_leave_state = fields.Selection(compute='_compute_leave_status', string="Current Time Off Status", selection=[ ('draft', 'New'), ('confirm', 'Waiting Approval'), @@ -80,11 +80,11 @@ class Employee(models.Model): ('validate', 'Approved'), ('cancel', 'Cancelled') ]) - current_leave_id = fields.Many2one('hr.leave.type', compute='_compute_leave_status', string="Current Leave Type") + current_leave_id = fields.Many2one('hr.leave.type', compute='_compute_leave_status', string="Current Time Off Type") leave_date_from = fields.Date('From Date', compute='_compute_leave_status') leave_date_to = fields.Date('To Date', compute='_compute_leave_status') - leaves_count = fields.Float('Number of Leaves', compute='_compute_remaining_leaves') - show_leaves = fields.Boolean('Able to see Remaining Leaves', compute='_compute_show_leaves') + leaves_count = fields.Float('Number of Time Off', compute='_compute_remaining_leaves') + show_leaves = fields.Boolean('Able to see Remaining Time Off', compute='_compute_show_leaves') is_absent_totay = fields.Boolean('Absent Today', compute='_compute_absent_employee', search='_search_absent_employee') def _get_remaining_leaves(self): diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index dfb092ccaa0..b4f045bdc1b 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -57,7 +57,7 @@ class HolidaysRequest(models.Model): leave request leave type. """ _name = "hr.leave" - _description = "Leave" + _description = "Time Off" _order = "date_from desc" _inherit = ['mail.thread', 'mail.activity.mixin'] _mail_post_access = 'read' @@ -105,16 +105,16 @@ class HolidaysRequest(models.Model): ('validate1', 'Second Approval'), ('validate', 'Approved') ], string='Status', readonly=True, tracking=True, copy=False, default='confirm', - help="The status is set to 'To Submit', when a leave request is created." + - "\nThe status is 'To Approve', when leave request is confirmed by user." + - "\nThe status is 'Refused', when leave request is refused by manager." + - "\nThe status is 'Approved', when leave request is approved by manager.") - payslip_status = fields.Boolean('Reported in last payslips', help='Green this button when the leave has been taken into account in the payslip.') + help="The status is set to 'To Submit', when a time off request is created." + + "\nThe status is 'To Approve', when time off request is confirmed by user." + + "\nThe status is 'Refused', when time off request is refused by manager." + + "\nThe status is 'Approved', when time off request is approved by manager.") + payslip_status = fields.Boolean('Reported in last payslips', help='Green this button when the time off has been taken into account in the payslip.') report_note = fields.Text('HR Comments') user_id = fields.Many2one('res.users', string='User', related='employee_id.user_id', related_sudo=True, store=True, default=lambda self: self.env.uid, readonly=True) # leave type configuration holiday_status_id = fields.Many2one( - "hr.leave.type", string="Leave Type", required=True, readonly=True, + "hr.leave.type", string="Time Off Type", required=True, readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, domain=[('valid', '=', True)]) leave_type_request_unit = fields.Selection(related='holiday_status_id.request_unit', readonly=True) @@ -140,13 +140,13 @@ class HolidaysRequest(models.Model): number_of_days = fields.Float( 'Duration (Days)', copy=False, readonly=True, tracking=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, - help='Number of days of the leave request according to your working schedule.') + help='Number of days of the time off request. Used in the calculation. To manually correct the duration, use this field.') number_of_days_display = fields.Float( 'Duration in days', compute='_compute_number_of_days_display', copy=False, readonly=True, - help='Number of days of the leave request. Used for interface.') + help='Number of days of the time off request according to your working schedule. Used for interface.') number_of_hours_display = fields.Float( 'Duration in hours', compute='_compute_number_of_hours_display', copy=False, readonly=True, - help='Number of hours of the leave request according to your working schedule. Used for interface.') + help='Number of hours of the time off request according to your working schedule. Used for interface.') # details meeting_id = fields.Many2one('calendar.event', string='Meeting') parent_id = fields.Many2one('hr.leave', string='Parent', copy=False) @@ -167,10 +167,10 @@ class HolidaysRequest(models.Model): states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) first_approver_id = fields.Many2one( 'hr.employee', string='First Approval', readonly=True, copy=False, - help='This area is automatically filled by the user who validate the leave', oldname='manager_id') + help='This area is automatically filled by the user who validate the time off', oldname='manager_id') second_approver_id = fields.Many2one( 'hr.employee', string='Second Approval', readonly=True, copy=False, oldname='manager_id2', - help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)') + help='This area is automaticly filled by the user who validate the time off with second level (If time off type need second validation)') can_reset = fields.Boolean('Can reset', compute='_compute_can_reset') can_approve = fields.Boolean('Can Approve', compute='_compute_can_approve') @@ -301,6 +301,10 @@ class HolidaysRequest(models.Model): self.date_to = timezone(tz).localize(datetime.combine(self.request_date_to, hour_to)).astimezone(UTC).replace(tzinfo=None) self._onchange_leave_dates() + @api.onchange('holiday_status_id') + def _onchange_holiday_status_id(self): + self.request_unit_half = False + @api.onchange('request_unit_half') def _onchange_request_unit_half(self): if self.request_unit_half: @@ -408,7 +412,7 @@ class HolidaysRequest(models.Model): ] nholidays = self.search_count(domain) if nholidays: - raise ValidationError(_('You can not have 2 leaves that overlaps on the same day.')) + raise ValidationError(_('You can not have 2 time off that overlaps on the same day.')) @api.constrains('state', 'number_of_days', 'holiday_status_id') def _check_holidays(self): @@ -418,8 +422,8 @@ class HolidaysRequest(models.Model): leave_days = holiday.holiday_status_id.get_days(holiday.employee_id.id)[holiday.holiday_status_id.id] if float_compare(leave_days['remaining_leaves'], 0, precision_digits=2) == -1 or \ float_compare(leave_days['virtual_remaining_leaves'], 0, precision_digits=2) == -1: - raise ValidationError(_('The number of remaining leaves is not sufficient for this leave type.\n' - 'Please also check the leaves waiting for validation.')) + raise ValidationError(_('The number of remaining time off is not sufficient for this time off type.\n' + 'Please also check the time off waiting for validation.')) def _get_number_of_days(self, date_from, date_to, employee_id): """ Returns a float equals to the timedelta between two dates given as string.""" @@ -497,7 +501,7 @@ class HolidaysRequest(models.Model): if leave_type.validation_type == 'no_validation': holiday.sudo().action_validate() holiday.message_subscribe(partner_ids=[holiday._get_responsible_for_approval().partner_id.id]) - holiday.sudo().message_post(body=_("The leave has been automatically approved"), subtype="mt_comment") # Message from OdooBot (sudo) + holiday.sudo().message_post(body=_("The time off has been automatically approved"), subtype="mt_comment") # Message from OdooBot (sudo) holiday.activity_update() return holiday @@ -536,7 +540,7 @@ class HolidaysRequest(models.Model): @api.multi def unlink(self): for holiday in self.filtered(lambda holiday: holiday.state not in ['draft', 'cancel', 'confirm']): - raise UserError(_('You cannot delete a leave which is in %s state.') % (holiday.state,)) + raise UserError(_('You cannot delete a time off which is in %s state.') % (holiday.state,)) return super(HolidaysRequest, self).unlink() @api.multi @@ -549,7 +553,7 @@ class HolidaysRequest(models.Model): @api.multi def _create_resource_leave(self): - """ This method will create entry in resource calendar leave object at the time of holidays validated """ + """ This method will create entry in resource calendar time off object at the time of holidays validated """ for leave in self: date_from = fields.Datetime.from_string(leave.date_from) date_to = fields.Datetime.from_string(leave.date_to) @@ -567,12 +571,12 @@ class HolidaysRequest(models.Model): @api.multi def _remove_resource_leave(self): - """ This method will create entry in resource calendar leave object at the time of holidays cancel/removed """ + """ This method will create entry in resource calendar time off object at the time of holidays cancel/removed """ return self.env['resource.calendar.leaves'].search([('holiday_id', 'in', self.ids)]).unlink() def _validate_leave_request(self): - """ Validate leave requests (holiday_type='employee') - by creating a calendar event and a resource leaves. """ + """ Validate time off requests (holiday_type='employee') + by creating a calendar event and a resource time off. """ for holiday in self.filtered(lambda request: request.holiday_type == 'employee'): meeting_values = holiday._prepare_holidays_meeting_values() meeting = self.env['calendar.event'].with_context(no_mail_to_attendees=True).create(meeting_values) @@ -622,7 +626,7 @@ class HolidaysRequest(models.Model): def action_draft(self): for holiday in self: if holiday.state not in ['confirm', 'refuse']: - raise UserError(_('Leave request state must be "Refused" or "To Approve" in order to be reset to draft.')) + raise UserError(_('Time off request state must be "Refused" or "To Approve" in order to be reset to draft.')) holiday.write({ 'state': 'draft', 'first_approver_id': False, @@ -638,7 +642,7 @@ class HolidaysRequest(models.Model): @api.multi def action_confirm(self): if self.filtered(lambda holiday: holiday.state != 'draft'): - raise UserError(_('Leave request must be in Draft state ("To Submit") in order to confirm it.')) + raise UserError(_('Time off request must be in Draft state ("To Submit") in order to confirm it.')) self.write({'state': 'confirm'}) self.activity_update() return True @@ -648,7 +652,7 @@ class HolidaysRequest(models.Model): # if validation_type == 'both': this method is the first approval approval # if validation_type != 'both': this method calls action_validate() below if any(holiday.state != 'confirm' for holiday in self): - raise UserError(_('Leave request must be confirmed ("To Approve") in order to approve it.')) + raise UserError(_('Time off request must be confirmed ("To Approve") in order to approve it.')) current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) self.filtered(lambda hol: hol.validation_type == 'both').write({'state': 'validate1', 'first_approver_id': current_employee.id}) @@ -661,7 +665,7 @@ class HolidaysRequest(models.Model): current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) for holiday in self: if holiday.state not in ['confirm', 'validate1']: - raise UserError(_('Leave request must be confirmed in order to approve it.')) + raise UserError(_('Time off request must be confirmed in order to approve it.')) holiday.write({'state': 'validate'}) if holiday.validation_type == 'both': @@ -696,7 +700,7 @@ class HolidaysRequest(models.Model): current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) for holiday in self: if holiday.state not in ['confirm', 'validate', 'validate1']: - raise UserError(_('Leave request must be confirmed or validated in order to refuse it.')) + raise UserError(_('Time off request must be confirmed or validated in order to refuse it.')) if holiday.state == 'validate1': holiday.write({'state': 'refuse', 'first_approver_id': current_employee.id}) @@ -723,27 +727,27 @@ class HolidaysRequest(models.Model): if state == 'draft': if holiday.employee_id != current_employee and not is_manager: - raise UserError(_('Only a Leave Manager can reset other people leaves.')) + raise UserError(_('Only a Time Off Manager can reset other people time off.')) continue if not is_team_leader: - raise UserError(_('Only a Team Leader, Leave Officer or Manager can approve or refuse leave requests.')) + raise UserError(_('Only a Team Leader, Time Off Officer or Manager can approve or refuse time off requests.')) if is_team_leader: # use ir.rule based first access check: department, members, ... (see security.xml) holiday.check_access_rule('write') if holiday.employee_id == current_employee and not is_manager: - raise UserError(_('Only a Leave Manager can approve its own requests.')) + raise UserError(_('Only a Time Off Manager can approve its own requests.')) if (state == 'validate1' and val_type == 'both') or (state == 'validate' and val_type == 'manager'): manager = holiday.employee_id.parent_id or holiday.employee_id.department_id.manager_id if (manager and manager != current_employee) and not self.env.user.has_group('hr_holidays.group_hr_holidays_manager'): - raise UserError(_('You must be either %s\'s manager or Leave manager to approve this leave') % (holiday.employee_id.name)) + raise UserError(_('You must be either %s\'s manager or Time Off Manager to approve this time off') % (holiday.employee_id.name)) if state == 'validate' and val_type == 'both': if not self.env.user.has_group('hr_holidays.group_hr_holidays_manager'): - raise UserError(_('Only an Leave Manager can apply the second approval on leave requests.')) + raise UserError(_('Only an Time Off Manager can apply the second approval on time off requests.')) # ------------------------------------------------------------ # Activity methods diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index 81a0dedf9d7..87ed680ccf6 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -19,7 +19,7 @@ _logger = logging.getLogger(__name__) class HolidaysAllocation(models.Model): """ Allocation Requests Access specifications: similar to leave requests """ _name = "hr.leave.allocation" - _description = "Leaves Allocation" + _description = "Time Off Allocation" _inherit = ['mail.thread', 'mail.activity.mixin'] _mail_post_access = 'read' @@ -30,7 +30,7 @@ class HolidaysAllocation(models.Model): if self.user_has_groups('hr_holidays.group_hr_holidays_user'): domain = [('valid', '=', True)] else: - domain = [('valid', '=', True), ('allocation_type', 'in', ('no', 'fixed_allocation'))] + domain = [('valid', '=', True), ('allocation_type', '=', 'fixed_allocation')] return self.env['hr.leave.type'].search(domain, limit=1) name = fields.Char('Description') @@ -42,10 +42,10 @@ class HolidaysAllocation(models.Model): ('validate1', 'Second Approval'), ('validate', 'Approved') ], string='Status', readonly=True, tracking=True, copy=False, default='confirm', - help="The status is set to 'To Submit', when a leave request is created." + - "\nThe status is 'To Approve', when leave request is confirmed by user." + - "\nThe status is 'Refused', when leave request is refused by manager." + - "\nThe status is 'Approved', when leave request is approved by manager.") + help="The status is set to 'To Submit', when a time off request is created." + + "\nThe status is 'To Approve', when time off request is confirmed by user." + + "\nThe status is 'Refused', when time off request is refused by manager." + + "\nThe status is 'Approved', when time off request is approved by manager.") date_from = fields.Datetime( 'Start Date', readonly=True, index=True, copy=False, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, tracking=True) @@ -53,9 +53,9 @@ class HolidaysAllocation(models.Model): 'End Date', readonly=True, copy=False, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, tracking=True) holiday_status_id = fields.Many2one( - "hr.leave.type", string="Leave Type", required=True, readonly=True, + "hr.leave.type", string="Time Off Type", required=True, readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, - domain=[('valid', '=', True)], default=_default_holiday_status_id) + domain=[('valid', '=', True), ('allocation_type', '!=', 'no')], default=_default_holiday_status_id) employee_id = fields.Many2one( 'hr.employee', string='Employee', index=True, readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, tracking=True) @@ -63,7 +63,7 @@ class HolidaysAllocation(models.Model): notes = fields.Text('Reasons', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) # duration number_of_days = fields.Float( - 'Number of Days', tracking=True, + 'Number of Days', tracking=True, default=1, help='Duration in days. Reference field to use when necessary.') number_of_days_display = fields.Float( 'Duration (days)', compute='_compute_number_of_days_display', @@ -77,10 +77,10 @@ class HolidaysAllocation(models.Model): linked_request_ids = fields.One2many('hr.leave.allocation', 'parent_id', string='Linked Requests') first_approver_id = fields.Many2one( 'hr.employee', string='First Approval', readonly=True, copy=False, - help='This area is automatically filled by the user who validate the leave', oldname='manager_id') + help='This area is automatically filled by the user who validate the time off', oldname='manager_id') second_approver_id = fields.Many2one( 'hr.employee', string='Second Approval', readonly=True, copy=False, oldname='manager_id2', - help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)') + help='This area is automaticly filled by the user who validate the time off with second level (If time off type need second validation)') validation_type = fields.Selection('Validation Type', related='holiday_status_id.validation_type', readonly=True) can_reset = fields.Boolean('Can reset', compute='_compute_can_reset') can_approve = fields.Boolean('Can Approve', compute='_compute_can_approve') @@ -357,12 +357,12 @@ class HolidaysAllocation(models.Model): @api.multi def unlink(self): for holiday in self.filtered(lambda holiday: holiday.state not in ['draft', 'cancel', 'confirm']): - raise UserError(_('You cannot delete a leave which is in %s state.') % (holiday.state,)) + raise UserError(_('You cannot delete a time off which is in %s state.') % (holiday.state,)) return super(HolidaysAllocation, self).unlink() @api.multi def copy_data(self, default=None): - raise UserError(_('A leave cannot be duplicated.')) + raise UserError(_('A time off cannot be duplicated.')) #################################################### # Business methods @@ -392,7 +392,7 @@ class HolidaysAllocation(models.Model): def action_draft(self): for holiday in self: if holiday.state not in ['confirm', 'refuse']: - raise UserError(_('Leave request state must be "Refused" or "To Approve" in order to reset to Draft.')) + raise UserError(_('Time off request state must be "Refused" or "To Approve" in order to reset to Draft.')) holiday.write({ 'state': 'draft', 'first_approver_id': False, @@ -408,7 +408,7 @@ class HolidaysAllocation(models.Model): @api.multi def action_confirm(self): if self.filtered(lambda holiday: holiday.state != 'draft'): - raise UserError(_('Leave request must be in Draft state ("To Submit") in order to confirm it.')) + raise UserError(_('Time off request must be in Draft state ("To Submit") in order to confirm it.')) res = self.write({'state': 'confirm'}) self.activity_update() return res @@ -418,7 +418,7 @@ class HolidaysAllocation(models.Model): # if validation_type == 'both': this method is the first approval approval # if validation_type != 'both': this method calls action_validate() below if any(holiday.state != 'confirm' for holiday in self): - raise UserError(_('Leave request must be confirmed ("To Approve") in order to approve it.')) + raise UserError(_('Time off request must be confirmed ("To Approve") in order to approve it.')) current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) @@ -432,7 +432,7 @@ class HolidaysAllocation(models.Model): current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) for holiday in self: if holiday.state not in ['confirm', 'validate1']: - raise UserError(_('Leave request must be confirmed in order to approve it.')) + raise UserError(_('Time off request must be confirmed in order to approve it.')) holiday.write({'state': 'validate'}) if holiday.validation_type == 'both': @@ -470,7 +470,7 @@ class HolidaysAllocation(models.Model): current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) for holiday in self: if holiday.state not in ['confirm', 'validate', 'validate1']: - raise UserError(_('Leave request must be confirmed or validated in order to refuse it.')) + raise UserError(_('Time off request must be confirmed or validated in order to refuse it.')) if holiday.state == 'validate1': holiday.write({'state': 'refuse', 'first_approver_id': current_employee.id}) @@ -493,27 +493,27 @@ class HolidaysAllocation(models.Model): if state == 'draft': if holiday.employee_id != current_employee and not is_manager: - raise UserError(_('Only a Leave Manager can reset other people leaves.')) + raise UserError(_('Only a time off Manager can reset other people time off.')) continue if not is_officer: - raise UserError(_('Only a Leave Officer or Manager can approve or refuse leave requests.')) + raise UserError(_('Only a time off Officer or Manager can approve or refuse time off requests.')) if is_officer: # use ir.rule based first access check: department, members, ... (see security.xml) holiday.check_access_rule('write') if holiday.employee_id == current_employee and not is_manager: - raise UserError(_('Only a Leave Manager can approve its own requests.')) + raise UserError(_('Only a time off Manager can approve its own requests.')) if (state == 'validate1' and val_type == 'both') or (state == 'validate' and val_type == 'manager'): manager = holiday.employee_id.parent_id or holiday.employee_id.department_id.manager_id if (manager and manager != current_employee) and not self.env.user.has_group('hr_holidays.group_hr_holidays_manager'): - raise UserError(_('You must be either %s\'s manager or Leave manager to approve this leave') % (holiday.employee_id.name)) + raise UserError(_('You must be either %s\'s manager or time off manager to approve this time off') % (holiday.employee_id.name)) if state == 'validate' and val_type == 'both': if not self.env.user.has_group('hr_holidays.group_hr_holidays_manager'): - raise UserError(_('Only an Leave Manager can apply the second approval on leave requests.')) + raise UserError(_('Only an time off Manager can apply the second approval on time off requests.')) # ------------------------------------------------------------ # Activity methods diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py index af3baf9830b..a72328a0302 100644 --- a/addons/hr_holidays/models/hr_leave_type.py +++ b/addons/hr_holidays/models/hr_leave_type.py @@ -6,6 +6,8 @@ import datetime import logging +from collections import defaultdict + from odoo import api, fields, models from odoo.exceptions import ValidationError from odoo.tools.translate import _ @@ -16,16 +18,16 @@ _logger = logging.getLogger(__name__) class HolidaysType(models.Model): _name = "hr.leave.type" - _description = "Leave Type" + _description = "Time Off Type" _order = "sequence, id" - name = fields.Char('Leave Type', required=True, translate=True) + name = fields.Char('Time Off Type', required=True, translate=True) code = fields.Char('Code') sequence = fields.Integer(default=100, - help='The type with the smallest sequence is the default value in leave request') + help='The type with the smallest sequence is the default value in time off request') categ_id = fields.Many2one( 'calendar.event.type', string='Meeting Type', - help='Once a leave is validated, Odoo will create a corresponding meeting of this type in the calendar.') + help='Once a time off is validated, Odoo will create a corresponding meeting of this type in the calendar.') color_name = fields.Selection([ ('red', 'Red'), ('blue', 'Blue'), @@ -43,50 +45,50 @@ class HolidaysType(models.Model): ('lavender', 'Lavender'), ('wheat', 'Wheat'), ('ivory', 'Ivory')], string='Color in Report', required=True, default='red', - help='This color will be used in the leaves summary located in Reporting > Leaves by Department.') + help='This color will be used in the time off summary located in Reporting > Time off by Department.') active = fields.Boolean('Active', default=True, - help="If the active field is set to false, it will allow you to hide the leave type without removing it.") - max_leaves = fields.Float(compute='_compute_leaves', string='Maximum Allowed', - help='This value is given by the sum of all leaves requests with a positive value.') + help="If the active field is set to false, it will allow you to hide the time off type without removing it.") + max_leaves = fields.Float(compute='_compute_leaves', string='Maximum Allowed', search='_search_max_leaves', + help='This value is given by the sum of all time off requests with a positive value.') leaves_taken = fields.Float( - compute='_compute_leaves', string='Leaves Already Taken', - help='This value is given by the sum of all leaves requests with a negative value.') + compute='_compute_leaves', string='Time off Already Taken', + help='This value is given by the sum of all time off requests with a negative value.') remaining_leaves = fields.Float( - compute='_compute_leaves', string='Remaining Leaves', - help='Maximum Leaves Allowed - Leaves Already Taken') + compute='_compute_leaves', string='Remaining Time Off', + help='Maximum Time Off Allowed - Time Off Already Taken') virtual_remaining_leaves = fields.Float( - compute='_compute_leaves', string='Virtual Remaining Leaves', - help='Maximum Leaves Allowed - Leaves Already Taken - Leaves Waiting Approval') + compute='_compute_leaves', string='Virtual Remaining Time Off', + help='Maximum Time Off Allowed - Time Off Already Taken - Time Off Waiting Approval') group_days_allocation = fields.Float( compute='_compute_group_days_allocation', string='Days Allocated') group_days_leave = fields.Float( - compute='_compute_group_days_leave', string='Group Leaves') + compute='_compute_group_days_leave', string='Group Time Off') company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id) validation_type = fields.Selection([ ('no_validation', 'No Validation'), ('hr', 'Payroll Officer'), ('manager', 'Team Leader'), - ('both', 'Team Leader and Payroll Officer')], default='hr', string='Validation By') + ('both', 'Team Leader and Payroll Officer')], default='hr', string='Validation') allocation_type = fields.Selection([ - ('fixed', 'Fixed by HR'), - ('fixed_allocation', 'Fixed by HR + allocation request'), - ('no', 'No allocation')], - default='fixed', string='Mode', - help='\tFixed by HR: allocated by HR and cannot be bypassed; users can request leaves;' - '\tFixed by HR + allocation request: allocated by HR and users can request leaves and allocations;' - '\tNo allocation: no allocation by default, users can freely request leaves;') - validity_start = fields.Date("Start Date", default=fields.Date.today, - help='Adding validity to types of leaves so that it cannot be selected outside this time period') - validity_stop = fields.Date("End Date") + ('no', 'No Allocation Needed'), + ('fixed_allocation', 'Free Allocation Request'), + ('fixed', 'Allocated by HR only')], + default='no', string='Mode', + help='\tNo Allocation Needed: no allocation by default, users can freely request time off;' + '\tFree Allocation Request: allocated by HR and users can request time off and allocations;' + '\tAllocated by HR only: allocated by HR and cannot be bypassed; users can request time off;') + validity_start = fields.Date("From", default=fields.Date.today, + help='Adding validity to types of time off so that it cannot be selected outside this time period') + validity_stop = fields.Date("To") valid = fields.Boolean(compute='_compute_valid', search='_search_valid', help='This indicates if it is still possible to use this type of leave') - time_type = fields.Selection([('leave', 'Leave'), ('other', 'Other')], default='leave', string="Kind of Leave", + time_type = fields.Selection([('leave', 'Time Off'), ('other', 'Other')], default='leave', string="Kind of Leave", help="Whether this should be computed as a holiday or as work time (eg: formation)") request_unit = fields.Selection([ - ('day', 'Day'), ('hour', 'Hours')], - default='day', string='Take Leaves in', required=True) + ('day', 'Day'), ('half_day','Half Day'), ('hour', 'Hours')], + default='day', string='Take Time Off in', required=True) unpaid = fields.Boolean('Is Unpaid', default=False) - leave_notif_subtype_id = fields.Many2one('mail.message.subtype', string='Leave Notification Subtype') - allocation_notif_subtype_id = fields.Many2one('mail.message.subtype', string='Allocation Notification Subtype') + leave_notif_subtype_id = fields.Many2one('mail.message.subtype', string='Time Off Notification Subtype', default=lambda self: self.env.ref('hr_holidays.mt_leave', raise_if_not_found=False)) + allocation_notif_subtype_id = fields.Many2one('mail.message.subtype', string='Allocation Notification Subtype', default=lambda self: self.env.ref('hr_holidays.mt_leave_allocation', raise_if_not_found=False)) @api.multi @api.constrains('validity_start', 'validity_stop') @@ -118,6 +120,35 @@ class HolidaysType(models.Model): ('validity_stop', signs[0] if value else signs[1], dt), ('validity_start', signs[1] if value else signs[0], dt)] + def _search_max_leaves(self, operator, value): + value = float(value) + employee_id = self._get_contextual_employee_id() + leaves = defaultdict(int) + + if employee_id: + allocations = self.env['hr.leave.allocation'].search([ + ('employee_id', '=', employee_id), + ('state', '=', 'validate') + ]) + for allocation in allocations: + leaves[allocation.holiday_status_id.id] += allocation.number_of_days + valid_leave = [] + for leave in leaves: + if operator == '>': + if leaves[leave] > value: + valid_leave.append(leave) + elif operator == '<': + if leaves[leave] < value: + valid_leave.append(leave) + elif operator == '=': + if leaves[leave] == value: + valid_leave.append(leave) + elif operator == '!=': + if leaves[leave] != value: + valid_leave.append(leave) + + return [('id', 'in', valid_leave)] + @api.multi def get_days(self, employee_id): # need to use `dict` constructor to create a dict per id @@ -154,6 +185,19 @@ class HolidaysType(models.Model): return result + @api.multi + def get_days_all_request(self): + employee_id = self._get_contextual_employee_id() + + leaves_type = self.search([]) + leaves = leaves_type.get_days(employee_id) + leave_id_name = dict(zip(leaves_type.ids, leaves_type.mapped('name'))) + leave_id_allocation_type = dict(zip(leaves_type.ids, leaves_type.mapped('allocation_type'))) + result = [(leave_id_name[leave_id], {key: round(value, 2) for (key, value) in leave.items()}, leave_id_allocation_type[leave_id]) for leave_id, leave in leaves.items() if leave['virtual_remaining_leaves'] or leave['max_leaves']] + + sort_key = lambda l: (l[2] == 'fixed', l[2] == 'fixed_allocation', l[1]['virtual_remaining_leaves']) + return sorted(result, key=sort_key, reverse=True) + def _get_contextual_employee_id(self): if 'employee_id' in self._context: employee_id = self._context['employee_id'] @@ -235,7 +279,7 @@ class HolidaysType(models.Model): leave_ids = super(HolidaysType, self)._search(args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid) if not count and not order and employee_id: leaves = self.browse(leave_ids) - sort_key = lambda l: (l.allocation_type == 'fixed', l.allocation_type == 'fixed_allocation', l.virtual_remaining_leaves) + sort_key = lambda l: (l.allocation_type in ['fixed', 'fixed_allocation'], l.virtual_remaining_leaves) return leaves.sorted(key=sort_key, reverse=True).ids return leave_ids diff --git a/addons/hr_holidays/report/hr_holidays_reports.xml b/addons/hr_holidays/report/hr_holidays_reports.xml index 7c3a35eee6a..59030b52b99 100644 --- a/addons/hr_holidays/report/hr_holidays_reports.xml +++ b/addons/hr_holidays/report/hr_holidays_reports.xml @@ -3,7 +3,7 @@
-

Leaves Summary

+

Time Off Summary

- Analyze from to of the Leaves. + Analyze from to of the Time Off.

@@ -63,7 +63,7 @@ - + diff --git a/addons/hr_holidays/report/hr_leave_report.py b/addons/hr_holidays/report/hr_leave_report.py index 299d3705c53..0077b3c34e5 100644 --- a/addons/hr_holidays/report/hr_leave_report.py +++ b/addons/hr_holidays/report/hr_leave_report.py @@ -6,7 +6,7 @@ from odoo import api, fields, models, tools class LeaveReport(models.Model): _name = "hr.leave.report" - _description = 'Leave Summary / Report' + _description = 'Time Off Summary / Report' _auto = False _order = "date_from DESC, employee_id" @@ -15,7 +15,7 @@ class LeaveReport(models.Model): number_of_days = fields.Float('Number of Days', readonly=True) leave_type = fields.Selection([ ('allocation', 'Allocation Request'), - ('request', 'Leave Request') + ('request', 'Time Off Request') ], string='Request Type', readonly=True, oldname='type') department_id = fields.Many2one('hr.department', string='Department', readonly=True) category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True) diff --git a/addons/hr_holidays/report/hr_leave_reports.xml b/addons/hr_holidays/report/hr_leave_reports.xml index f818b8fbec6..94df580f612 100644 --- a/addons/hr_holidays/report/hr_leave_reports.xml +++ b/addons/hr_holidays/report/hr_leave_reports.xml @@ -5,22 +5,22 @@ hr.holidays.filterhr.leave.report - + - + - - + + + domain="[('holiday_status_id.active', '=', True)]" help="Active Time Off"/> @@ -110,29 +110,8 @@ - - Leaves Summary - hr.leave.report - hr.employee - form - tree,kanban,form - - { - 'search_default_group_type': 1, - 'search_default_year': 1 , - 'search_default_my_leaves': 1, - } - -

- Create a new leave request -

-
- [('holiday_type','=','employee')] - -
- - Leaves + Time Off ir.actions.act_window hr.leave.report hr.employee @@ -147,11 +126,4 @@ - - diff --git a/addons/hr_holidays/security/hr_holidays_security.xml b/addons/hr_holidays/security/hr_holidays_security.xml index 3e10c16ca65..df7317fa5d2 100644 --- a/addons/hr_holidays/security/hr_holidays_security.xml +++ b/addons/hr_holidays/security/hr_holidays_security.xml @@ -24,7 +24,7 @@ - Leaves: employee: read all + Time Off: employee: read all [(1, '=', 1)] @@ -34,7 +34,7 @@ - Leaves: employee: create/write/unlink not validated own + Time Off: employee: create/write/unlink not validated own [('employee_id.user_id','=',user.id), ('state', 'in', ['draft', 'confirm', 'cancel', 'refuse'])] @@ -42,7 +42,7 @@ - Leaves: Team Leader: write on employee's leaves in his team + Time Off: Team Leader: write on employee's time off in his team [('employee_id.leave_manager_id', '=' , user.id)] @@ -52,7 +52,7 @@ - Leaves: officer: read all + Time Off: officer: read all [(1, '=', 1)] @@ -62,7 +62,7 @@ - Leaves: officer: create/write/unlink own + managed employee + without department + same or managed department + Time Off: officer: create/write/unlink own + managed employee + without department + same or managed department ['|', '|', ('employee_id.user_id', '=', user.id), ('employee_id.parent_id.user_id', '=', user.id), @@ -72,14 +72,14 @@ - Leaves: manager: no limit + Time Off: manager: no limit [(1, '=', 1)] - Leaves: multi company global rule + Time Off: multi company global rule ['|', ('holiday_status_id.company_id', '=', False), ('holiday_status_id.company_id', 'in', [user.company_id.id])] @@ -130,14 +130,14 @@ - Leaves Resources: Team Leader + Time Off Resources: Team Leader [(1,'=',1)] - Leaves multi company rule + Time Off multi company rule ['|',('company_id','=',False),('company_id','in',[user.company_id.id])] diff --git a/addons/hr_holidays/static/src/js/time_off_calendar.js b/addons/hr_holidays/static/src/js/time_off_calendar.js new file mode 100644 index 00000000000..de9feca1225 --- /dev/null +++ b/addons/hr_holidays/static/src/js/time_off_calendar.js @@ -0,0 +1,192 @@ +odoo.define('hr_holidays.dashboard.view_custo', function(require) { + 'use strict'; + + var core = require('web.core'); + var CalendarController = require("web.CalendarController"); + var CalendarView = require("web.CalendarView"); + var viewRegistry = require('web.view_registry'); + + var _t = core._t; + var QWeb = core.qweb; + + + var TimeOffCalendarController = CalendarController.extend({ + events: _.extend({}, CalendarController.prototype.events, { + 'click .o_header_calendar_button_prev': '_onPrev', + 'click .o_header_calendar_button_next': '_onNext', + 'click .btn-time-off': '_onNewTimeOff', + 'click .btn-allocation': '_onNewAllocation', + }), + + /** + * @override + */ + start: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + self.$el.find('.o_calendar_sidebar_container').remove(); + self._renderSummaryTimeOff(); + self.$(".o_calendar_widget").fullCalendar('option', 'contentHeight', 500); + }); + }, + + /** + * Override to add correct date in header of calendar + * + * @override + */ + update: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + self._updateSummaryTimeOff(); + }); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * Render the buttons and add new button about + * times off and allocations request + * + * @override + */ + + renderButtons: function ($node) { + this.$buttons = $(QWeb.render('hr_holidays.dashboard.calendar.button', { + time_off: _t('New Time Off Request'), + request: _t('New Allocation Request'), + })); + + if ($node) { + this.$buttons.appendTo($node); + } else { + this.$('.o_calendar_buttons').replaceWith(this.$buttons); + } + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Create a header for calendar with previous and next month button. + * Add month and year on the header + * + * @private + */ + _renderCalendarHeader: function () { + this.$el.find('.fc-week-number').remove(); + + if (!this.$('#header_calendar_leaves').length) { + var current_month = this.model.data.target_date.format('MMMM YYYY'); + var header = $(QWeb.render('hr_holidays.dashboard.calendar.header', { + current_month: current_month, + })); + + this.$el.find('.fc-widget-header').find('thead').prepend(header); + } + }, + + /** + * Create a summary of all time off and requests allocations + * + * @private + */ + _renderSummaryTimeOff: function () { + var leave_state = '
'; + leave_state += '
'; + this.$el.find('.o_calendar_container').before( + $(leave_state) + ); + + this._updateSummaryTimeOff(); + }, + + /** + * Create a summary of all time off and requests allocations + * + * @private + */ + _updateSummaryTimeOff: function () { + var self = this; + + this._renderCalendarHeader(); + + return this._rpc({ + model: 'hr.leave.type', + method: 'get_days_all_request', + args: [this.context], + }) + .then(function (leaves) { + $('#counter_leaves').children().remove(); + for (var leave in leaves){ + var leave_box = $('

'); + var content = leaves[leave][1]; + var leave_count = leave_box.find('h1'); + leave_box.append(leave_count.text( + leaves[leave][2] !== 'no' ? content.virtual_remaining_leaves + " / " + content.max_leaves : -content.virtual_remaining_leaves + )); + var leave_name = leave_box.find('h3'); + leave_name.text(leaves[leave][0]); + leave_box.append(leave_name); + $('#counter_leaves').append(leave_box); + } + $('#counter_leaves').children().last().removeClass('border-right'); + + self.$el.find('.o_calendar_container').appendTo(self.$el.find('.o_form_sheet')); + }); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Action: create a new time off request + * + * @private + */ + _onNewTimeOff: function () { + var self = this; + this.do_action({ + type: 'ir.actions.act_window', + res_model: 'hr.leave', + view_type: 'form', + views: [[false,'form']], + target: 'new', + }, { + on_close: function () { + self.reload(); + } + }); + }, + + /** + * Action: create a new allocation request + * + * @private + */ + _onNewAllocation: function () { + this.do_action({ + type: 'ir.actions.act_window', + res_model: 'hr.leave.allocation', + view_type: 'form', + views: [[false,'form']], + target: 'new', + }), { + on_close: function () { + self.reload(); + } + }; + }, + }); + var TimeOffCalendarView = CalendarView.extend({ + config: _.extend({}, CalendarView.prototype.config, { + Controller: TimeOffCalendarController, + }), + }); + + viewRegistry.add('time_off_calendar', TimeOffCalendarView); +}); \ No newline at end of file diff --git a/addons/hr_holidays/static/src/xml/time_off_calendar.xml b/addons/hr_holidays/static/src/xml/time_off_calendar.xml new file mode 100644 index 00000000000..8e7746c228f --- /dev/null +++ b/addons/hr_holidays/static/src/xml/time_off_calendar.xml @@ -0,0 +1,26 @@ + + + + +
+ + + + + + + + + diff --git a/addons/hr_holidays/tests/test_holidays_flow.py b/addons/hr_holidays/tests/test_holidays_flow.py index 08e1be1b20d..2a825f46c95 100644 --- a/addons/hr_holidays/tests/test_holidays_flow.py +++ b/addons/hr_holidays/tests/test_holidays_flow.py @@ -168,7 +168,7 @@ class TestHolidaysFlow(TestHrHolidaysBase): hol3_status = self.env.ref('hr_holidays.holiday_status_cl').with_context(employee_id=employee_id) # I assign the dates in the holiday request for 1 day hol3 = Requests.create({ - 'name': 'Sick Leave', + 'name': 'Sick Time Off', 'holiday_status_id': hol3_status.id, 'date_from': datetime.today().strftime('%Y-%m-10 10:00:00'), 'date_to': datetime.today().strftime('%Y-%m-11 19:00:00'), diff --git a/addons/hr_holidays/tests/test_hr_leave_type.py b/addons/hr_holidays/tests/test_hr_leave_type.py index dfe1b77713d..953c87a3981 100644 --- a/addons/hr_holidays/tests/test_hr_leave_type.py +++ b/addons/hr_holidays/tests/test_hr_leave_type.py @@ -13,7 +13,7 @@ class TestHrLeaveType(TestHrHolidaysBase): def test_time_type(self): leave_type = self.env['hr.leave.type'].create({ - 'name': 'Legal Leaves', + 'name': 'Paid Time Off', 'time_type': 'leave', 'allocation_type': 'no', }) diff --git a/addons/hr_holidays/tests/test_leave_requests.py b/addons/hr_holidays/tests/test_leave_requests.py index 8dad86eae39..67d468b0a44 100644 --- a/addons/hr_holidays/tests/test_leave_requests.py +++ b/addons/hr_holidays/tests/test_leave_requests.py @@ -143,7 +143,7 @@ class TestLeaveRequests(TestHrHolidaysBase): """ Employee ask leav during a not valid validity time """ with self.assertRaises(ValidationError): self.env['hr.leave'].sudo(self.user_employee_id).create({ - 'name': 'Sick Leave', + 'name': 'Sick Time Off', 'employee_id': self.employee_emp_id, 'holiday_status_id': self.holidays_type_3.id, 'date_from': fields.Datetime.from_string('2017-07-03 06:00:00'), diff --git a/addons/hr_holidays/views/hr_leave_allocation_views.xml b/addons/hr_holidays/views/hr_leave_allocation_views.xml index 3b1da9e3cbe..aad32a99128 100644 --- a/addons/hr_holidays/views/hr_leave_allocation_views.xml +++ b/addons/hr_holidays/views/hr_leave_allocation_views.xml @@ -13,12 +13,12 @@ - + - - + + @@ -68,7 +68,7 @@ + domain="[('valid', '=', True), ('allocation_type', '=', 'fixed_allocation')]"/>
ColorLeave TypeTime Off Type
+ + +

+