[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
This commit is contained in:
jbm-odoo
2019-01-21 11:29:30 +00:00
committed by Yannick Tivisse
parent 344877a081
commit a3daff6bc1
48 changed files with 612 additions and 363 deletions
@@ -13,7 +13,7 @@
</record>
<!-- Analytic Accounts -->
<record id="analytic_absences" model="account.analytic.account">
<field name="name">Leaves</field>
<field name="name">Time Off</field>
<field name="group_id" ref="analytic.analytic_group_internal"/>
</record>
<record id="analytic_internal" model="account.analytic.account">
+2 -2
View File
@@ -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.
+11 -10
View File
@@ -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',
+2 -2
View File
@@ -3,7 +3,7 @@
<data noupdate="1">
<!-- Casual leave -->
<record id="holiday_status_cl" model="hr.leave.type">
<field name="name" eval="'Legal Leaves '+time.strftime('%Y')"/>
<field name="name" eval="'Paid Time Off '+time.strftime('%Y')"/>
<field name="allocation_type">fixed</field>
<field name="color_name">black</field>
<field name="validity_start" eval="time.strftime('%Y-01-01')"/>
@@ -21,7 +21,7 @@
<!-- Sick leave -->
<record id="holiday_status_sl" model="hr.leave.type">
<field name="name">Sick Leaves</field>
<field name="name">Sick Time Off</field>
<field name="allocation_type">no</field>
<field name="color_name">red</field>
<field name="validity_start" eval="time.strftime('%Y-01-01')"/>
+9 -38
View File
@@ -11,21 +11,12 @@
</record>
<record id="hr_holiday_status_dv" model="hr.leave.type">
<field name="name">Parental Leaves</field>
<field name="name">Parental Time Off</field>
<field name="allocation_type">fixed</field>
<field name="color_name">brown</field>
<field name="validation_type">both</field>
</record>
<record id="hr_holiday_status_tb" model="hr.leave.type">
<field name="name">Team Building</field>
<field name="allocation_type">no</field>
<field name="color_name">lightblue</field>
<field name="validation_type">manager</field>
<field name="validity_start" eval="time.strftime('%Y-%m-27')"/>
<field name="validity_stop" eval="time.strftime('%Y-%m-27')"/>
</record>
<record id="holiday_status_cla" model="hr.leave.type">
<field name="name" eval="'Paid Time Off ' + time.strftime('%Y')"/>
<field name="allocation_type">fixed</field>
@@ -34,30 +25,10 @@
<field name="validity_stop" eval="time.strftime('%Y-12-31')"/>
</record>
<!-- ++++++++++++++++++++++ All Company ++++++++++++++++++++++ -->
<record id="hr_holidays_team_building" model="hr.leave">
<field name="name" eval="'Team Building ' + time.strftime('%Y-%m')"/>
<field name="holiday_status_id" ref="hr_holiday_status_tb"/>
<field eval="time.strftime('%Y-%m-27 9:00:00')" name="date_from"/>
<field eval="time.strftime('%Y-%m-27 18:00:00')" name="date_to"/>
<field name="number_of_days">1</field>
<field name="holiday_type">company</field>
<field name ="mode_company_id" ref="base.main_company"/>
<field name ="department_id" ref="hr.dep_management"/>
</record>
<function model="hr.leave" name="action_approve">
<value model="hr.leave" search="[
('state', '=', 'confirm'),
('id', 'in', [ref('hr_holidays.hr_holidays_team_building')])
]"/>
</function>
<!-- ++++++++++++++++++++++ Mitchell Admin ++++++++++++++++++++++ -->
<record id="hr_holidays_allocation_cl" model="hr.leave.allocation">
<field name="name">Legal Leaves for Mitchell Admin</field>
<field name="name">Paid Time Off for Mitchell Admin</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field name="number_of_days">20</field>
<field name="employee_id" ref="hr.employee_admin"/>
@@ -137,7 +108,7 @@
<!-- ++++++++++++++++++++++ Ronnie Hart ++++++++++++++++++++++ -->
<record id="hr_holidays_allocation_cl_al" model="hr.leave.allocation">
<field name="name">Legal Leaves for Ronnie Hart</field>
<field name="name">Paid Time Off for Ronnie Hart</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field name="number_of_days">20</field>
<field name="employee_id" ref="hr.employee_al"/>
@@ -145,7 +116,7 @@
</record>
<record id="hr_holidays_allocation_pl_al" model="hr.leave.allocation">
<field name="name">Parental Leaves</field>
<field name="name">Parental Time Off</field>
<field name="holiday_status_id" ref="hr_holiday_status_dv"/>
<field name="number_of_days">10</field>
<field name="employee_id" ref="hr.employee_al"/>
@@ -216,7 +187,7 @@
<!-- ++++++++++++++++++++++ Anita Oliver ++++++++++++++++++++++ -->
<record id="hr_holidays_allocation_cl_mit" model="hr.leave.allocation">
<field name="name">Legal Leaves for Anita Oliver</field>
<field name="name">Paid Time Off for Anita Oliver</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field name="number_of_days">20</field>
<field name="employee_id" ref="hr.employee_mit"/>
@@ -297,7 +268,7 @@
<!-- ++++++++++++++++++++++ Gilles Gravie ++++++++++++++++++++++ -->
<record id="hr_holidays_allocation_cl_qdp" model="hr.leave.allocation">
<field name="name">Legal Leaves for Gilles Gravie</field>
<field name="name">Paid Time Off for Gilles Gravie</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field name="number_of_days">20</field>
<field name="employee_id" ref="hr.employee_qdp"/>
@@ -370,7 +341,7 @@
<!-- ++++++++++++++++++++++ Audrey Peterson ++++++++++++++++++++++ -->
<record id="hr_holidays_allocation_cl_fpi" model="hr.leave.allocation">
<field name="name">Legal Leaves for Audrey Peterson</field>
<field name="name">Paid Time Off for Audrey Peterson</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field name="number_of_days">20</field>
<field name="employee_id" ref="hr.employee_fpi"/>
@@ -473,7 +444,7 @@
<!-- ++++++++++++++++++++++ Olivia ++++++++++++++++++++++ -->
<record id="hr_holidays_allocation_cl_vad" model="hr.leave.allocation">
<field name="name">Legal Leaves for Olivia</field>
<field name="name">Paid Time Off for Olivia</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field name="number_of_days">20</field>
<field name="employee_id" ref="hr.employee_niv"/>
@@ -547,7 +518,7 @@
<!-- ++++++++++++++++++++++ Kim ++++++++++++++++++++++ -->
<record id="hr_holidays_allocation_cl_kim" model="hr.leave.allocation">
<field name="name">Legal Leaves for Kim</field>
<field name="name">Paid Time Off for Kim</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field name="number_of_days">20</field>
<field name="employee_id" ref="hr.employee_jve"/>
+1 -1
View File
@@ -2,7 +2,7 @@
<odoo>
<record id="hr_leave_allocation_cron_accrual" model="ir.cron">
<field name="name">Accrual Leave: Updates the number of leaves</field>
<field name="name">Accrual Time Off: Updates the number of time off</field>
<field name="model_id" ref="model_hr_leave_allocation"/>
<field name="state">code</field>
<field name="code">model._update_accrual()</field>
+8 -8
View File
@@ -3,12 +3,12 @@
<data noupdate="1">
<!-- Leave specific activities -->
<record id="mail_act_leave_approval" model="mail.activity.type">
<field name="name">Leaves Approval</field>
<field name="name">Time Off Approval</field>
<field name="icon">fa-sun-o</field>
<field name="res_model_id" ref="hr_holidays.model_hr_leave"/>
</record>
<record id="mail_act_leave_second_approval" model="mail.activity.type">
<field name="name">Leaves Second Approve</field>
<field name="name">Time Off Second Approve</field>
<field name="icon">fa-sun-o</field>
<field name="res_model_id" ref="hr_holidays.model_hr_leave"/>
</record>
@@ -27,9 +27,9 @@
<!-- Holidays-related subtypes for messaging / Chatter -->
<record id="mt_leave" model="mail.message.subtype">
<field name="name">Leave</field>
<field name="name">Time Off</field>
<field name="res_model">hr.leave</field>
<field name="description">Leave Request</field>
<field name="description">Time Off Request</field>
</record>
<record id="mt_leave_home_working" model="mail.message.subtype">
<field name="name">Home Working</field>
@@ -37,14 +37,14 @@
<field name="description">Home Working</field>
</record>
<record id="mt_leave_sick" model="mail.message.subtype">
<field name="name">Sick Leave</field>
<field name="name">Sick Time Off</field>
<field name="res_model">hr.leave</field>
<field name="description">Sick Leave</field>
<field name="description">Sick Time Off</field>
</record>
<record id="mt_leave_unpaid" model="mail.message.subtype">
<field name="name">Unpaid Leave</field>
<field name="name">Unpaid Time Off</field>
<field name="res_model">hr.leave</field>
<field name="description">Unpaid Leave</field>
<field name="description">Unpaid Time Off</field>
</record>
<!-- Allocation-related subtypes for messaging / Chatter -->
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="paperformat_hrsummary" model="report.paperformat">
<field name="name">Leaves Summary</field>
<field name="name">Time Off Summary</field>
<field name="default" eval="True"/>
<field name="format">custom</field>
<field name="page_height">297</field>
+10 -10
View File
@@ -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):
+35 -31
View File
@@ -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
@@ -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
+76 -32
View File
@@ -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
@@ -3,7 +3,7 @@
<report
id="action_report_holidayssummary"
string="Leaves Summary"
string="Time Off Summary"
model="hr.leave"
report_type="qweb-pdf"
name="hr_holidays.report_holidayssummary"
@@ -16,7 +16,7 @@
<report
id="action_report_holidayssummary2"
string="Leaves Summary"
string="Time Off Summary"
model="hr.leave.allocation"
report_type="qweb-pdf"
name="hr_holidays.report_holidayssummary"
@@ -5,10 +5,10 @@
<t t-call="web.html_container">
<t t-call="web.internal_layout">
<div class="page">
<h3 class="mb32">Leaves Summary</h3>
<h3 class="mb32">Time Off Summary</h3>
<t t-set="info" t-value="get_header_info"/>
<h3 class="text-center mb32">
Analyze from <u><t t-esc="info['start_date']"/></u> to <u><t t-esc="info['end_date']"/></u> of the <u><t t-esc="info['holiday_type']"/></u> Leaves.
Analyze from <u><t t-esc="info['start_date']"/></u> to <u><t t-esc="info['end_date']"/></u> of the <u><t t-esc="info['holiday_type']"/></u> Time Off.
</h3>
<table class="table table-bordered mb32" style="table-layout:auto">
@@ -63,7 +63,7 @@
<thead>
<tr>
<th class="col-1">Color</th>
<th class="text-center">Leave Type</th>
<th class="text-center">Time Off Type</th>
</tr>
</thead>
<tbody>
+2 -2
View File
@@ -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)
+6 -34
View File
@@ -5,22 +5,22 @@
<field name="name">hr.holidays.filter</field>
<field name="model">hr.leave.report</field>
<field name="arch" type="xml">
<search string="Search Leave">
<search string="Search Time Off">
<field name="name"/>
<filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
<filter string="Approved Requests" domain="[('state', '=', 'validate')]" name="validated"/>
<separator/>
<filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an leave type that is 'active' (active field is True)"/>
<filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an time off type that is 'active' (active field is True)"/>
<separator/>
<filter string="My Department Leaves" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Leaves"/>
<filter name="my_team_leaves" string="My Team Leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/>
<filter string="My Department Time Off" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Time Off"/>
<filter name="my_team_leaves" string="My Team Time Off" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Time Off of Your Team Member"/>
<separator/>
<filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="hr_holidays.group_hr_holidays_manager"/>
<separator/>
<filter string="To Do" name="todo" domain="[('payslip_status', '=', False), ('holiday_status_id.active', '=', True)]"/>
<separator/>
<filter name="year" string="Current Year"
domain="[('holiday_status_id.active', '=', True)]" help="Active Leaves"/>
domain="[('holiday_status_id.active', '=', True)]" help="Active Time Off"/>
<separator/>
<filter string="My Requests" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
<separator/>
@@ -110,29 +110,8 @@
</field>
</record>
<record id="open_company_allocation" model="ir.actions.act_window">
<field name="name">Leaves Summary</field>
<field name="res_model">hr.leave.report</field>
<field name="src_model">hr.employee</field>
<field name="view_type">form</field>
<field name="view_mode">tree,kanban,form</field>
<field name="view_id" eval="hr_leave_report_tree"/>
<field name="context">{
'search_default_group_type': 1,
'search_default_year': 1 ,
'search_default_my_leaves': 1,
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave request
</p>
</field>
<field name="domain">[('holiday_type','=','employee')]</field>
<field name="search_view_id" ref="view_hr_holidays_filter_report"/>
</record>
<record id="act_hr_employee_holiday_request" model="ir.actions.act_window">
<field name="name">Leaves</field>
<field name="name">Time Off</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr.leave.report</field>
<field name="src_model">hr.employee</field>
@@ -147,11 +126,4 @@
<field name="search_view_id" ref="view_hr_holidays_filter_report"/>
</record>
<menuitem
id="menu_open_company_allocation"
name="Summary"
parent="menu_hr_holidays_my_leaves"
action="open_company_allocation"
sequence="21"/>
</odoo>
@@ -24,7 +24,7 @@
</record>
<record id="hr_leave_rule_employee" model="ir.rule">
<field name="name">Leaves: employee: read all</field>
<field name="name">Time Off: employee: read all</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="perm_create" eval="False"/>
@@ -34,7 +34,7 @@
</record>
<record id="hr_leave_rule_employee_update" model="ir.rule">
<field name="name">Leaves: employee: create/write/unlink not validated own</field>
<field name="name">Time Off: employee: create/write/unlink not validated own</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[('employee_id.user_id','=',user.id), ('state', 'in', ['draft', 'confirm', 'cancel', 'refuse'])]</field>
<field name="perm_read" eval="False"/>
@@ -42,7 +42,7 @@
</record>
<record id="hr_leave_rule_team_leader_update" model="ir.rule">
<field name="name">Leaves: Team Leader: write on employee's leaves in his team</field>
<field name="name">Time Off: Team Leader: write on employee's time off in his team</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[('employee_id.leave_manager_id', '=' , user.id)]</field>
<field name="perm_read" eval="False"/>
@@ -52,7 +52,7 @@
</record>
<record id="hr_leave_rule_officer_read" model="ir.rule">
<field name="name">Leaves: officer: read all</field>
<field name="name">Time Off: officer: read all</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="perm_create" eval="False"/>
@@ -62,7 +62,7 @@
</record>
<record id="hr_leave_rule_officer_update" model="ir.rule">
<field name="name">Leaves: officer: create/write/unlink own + managed employee + without department + same or managed department</field>
<field name="name">Time Off: officer: create/write/unlink own + managed employee + without department + same or managed department</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">['|',
'|', ('employee_id.user_id', '=', user.id), ('employee_id.parent_id.user_id', '=', user.id),
@@ -72,14 +72,14 @@
</record>
<record id="hr_leave_rule_manager" model="ir.rule">
<field name="name">Leaves: manager: no limit</field>
<field name="name">Time Off: manager: no limit</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_hr_holidays_manager'))]"/>
</record>
<record id="hr_leave_rule_multicompany" model="ir.rule">
<field name="name">Leaves: multi company global rule</field>
<field name="name">Time Off: multi company global rule</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="global" eval="True"/>
<field name="domain_force">['|', ('holiday_status_id.company_id', '=', False), ('holiday_status_id.company_id', 'in', [user.company_id.id])]</field>
@@ -130,14 +130,14 @@
</record>
<record id="resource_leaves_team_leader" model="ir.rule">
<field name="name">Leaves Resources: Team Leader</field>
<field name="name">Time Off Resources: Team Leader</field>
<field name="model_id" ref="model_resource_calendar_leaves"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('hr_holidays.group_hr_holidays_team_leader'))]"/>
</record>
<record id="hr_holidays_status_rule_multi_company" model="ir.rule">
<field name="name">Leaves multi company rule</field>
<field name="name">Time Off multi company rule</field>
<field name="model_id" ref="model_hr_leave_type"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','in',[user.company_id.id])]</field>
@@ -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 = '<div class="o_form_view"><div class="o_form_sheet_bg"><div class="o_form_sheet"><div id="counter_leaves" class="row">';
leave_state += '</div></div></div></div>';
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 = $('<div class="col text-center border-right"><h1 class="text-nowrap"></h1><h3></h3></div>');
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);
});
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="hr_holidays.dashboard.calendar.header">
<tr id="header_calendar_leaves">
<th colspan="7">
<button class="btn o_header_calendar_button_prev float-left" type="button">
<span class="fa fa-arrow-left"/>
</button>
<button class="btn o_header_calendar_button_next float-right" type="button">
<span class="fa fa-arrow-right"/>
</button>
<h2 class="o_header_calendar_month"><t t-esc="current_month"/></h2>
</th>
</tr>
</t>
<t t-name="hr_holidays.dashboard.calendar.button">
<button class="btn btn-primary btn-time-off" type="button">
<t t-esc="time_off"/>
</button>
<button class="btn btn-secondary btn-allocation" type="button">
<t t-esc="request"/>
</button>
</t>
</templates>
@@ -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'),
@@ -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',
})
@@ -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'),
@@ -13,12 +13,12 @@
<filter string="Approved Allocations" domain="[('state', '=', 'validate')]" name="validated"/>
<filter string="Accrual Allocations" domain="[('accrual', '=', True)]" name="accrual_allocation"/>
<separator/>
<filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on allocations that belong to an leave type that is 'active' (active field is True)"/>
<filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on allocations that belong to an time off type that is 'active' (active field is True)"/>
<separator/>
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
<separator/>
<filter string="My Department Leaves" name="department" domain="[('department_id.member_ids.user_id', '=', uid)]" help="My Department Leaves"/>
<filter string="My Team Leaves" name="my_team_leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/>
<filter string="My Department Time Off" name="department" domain="[('department_id.member_ids.user_id', '=', uid)]" help="My Department Time Off"/>
<filter string="My Team Time Off" name="my_team_leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Time Off of Your Team Member"/>
<separator/>
<filter name="year" string="Current Year"
domain="[('holiday_status_id.active', '=', True)]" help="Active Allocations"/>
@@ -68,7 +68,7 @@
<field name="type_request_unit" invisible="1"/>
<field name="name" attrs="{'readonly': [('state', 'not in', ('draft', 'confirm'))]}"/>
<field name="holiday_status_id" context="{'employee_id':employee_id}"
domain="[('valid', '=', True), ('allocation_type', 'in', ['fixed_allocation', 'no'])]"/>
domain="[('valid', '=', True), ('allocation_type', '=', 'fixed_allocation')]"/>
<field name="number_of_days" invisible="1"/>
<label for="number_of_days" string="Duration"/>
@@ -153,7 +153,7 @@
attrs="{'required': [('accrual', '=', True)]}"/>
<field name="unit_per_interval"
attrs="{'required': [('accrual', '=', True)]}"/>
<span class="ml8">of leaves every</span>
<span class="ml8">of time off every</span>
<field name="interval_number" class="ml8"
attrs="{'required': [('accrual', '=', True)]}"/>
<field name="interval_unit"
@@ -276,7 +276,10 @@
<field name="view_mode">tree,kanban,form,activity</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave allocation request
Create a new time off allocation request
</p><p>
Time Off Officers allocate time off days to employees (e.g. paid time off).<br/>
Employees request allocations to Time Off Officers (e.g. recuperation days).
</p>
</field>
<field name="context">{}</field>
@@ -304,7 +307,10 @@
<field name="domain">[]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave allocation request
Create a new time off allocation request
</p><p>
Time Off Officers allocate time off days to employees (e.g. paid time off).<br/>
Employees request allocations to Time Off Officers (e.g. recuperation days).
</p>
</field>
</record>
@@ -318,14 +324,17 @@
<field name="domain">[]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave allocation
Create a new time off allocation
</p><p>
Time Off Officers allocate time off days to employees (e.g. paid time off).<br/>
Employees request allocations to Time Off Officers (e.g. recuperation days).
</p>
</field>
</record>
<menuitem
id="menu_open_allocation"
name="Allocations Requests"
name="Allocation Requests"
parent="menu_hr_holidays_my_leaves"
action="hr_leave_allocation_action_my"
sequence="6"/>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr="script[last()]" position="after">
<script type="text/javascript" src="/hr_holidays/static/src/js/time_off_calendar.js"></script>
</xpath>
</template>
</odoo>
@@ -5,8 +5,8 @@
<field name="name">hr.leave.type.filter</field>
<field name="model">hr.leave.type</field>
<field name="arch" type="xml">
<search string="Search Leave Type">
<field name="name" string="Leave Types"/>
<search string="Search Time Off Type">
<field name="name" string="Time Off Types"/>
<field name="code"/>
<field name="categ_id"/>
</search>
@@ -17,7 +17,7 @@
<field name="name">hr.leave.type.form</field>
<field name="model">hr.leave.type</field>
<field name="arch" type="xml">
<form string="Leave Type">
<form string="Time Off Type">
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" type="object" name="action_see_days_allocated" icon="fa-archive">
@@ -29,7 +29,7 @@
<button class="oe_stat_button" type="object" name="action_see_group_leaves" icon="fa-archive">
<div class="o_stat_info">
<field name="group_days_leave"/>
<span class="o_stat_text">Group Leaves</span>
<span class="o_stat_text">Group Time Off</span>
</div>
</button>
<button class="oe_stat_button" type="object" name="toggle_active" icon="fa-archive">
@@ -43,10 +43,8 @@
<group>
<group name="description" string="Options">
<field name="code"/>
<field name="unpaid"/>
<field name="request_unit" widget="radio"/>
<field name="sequence" groups="base.group_no_one"/>
<field name="time_type" groups="base.group_no_one"/>
</group>
<group name="validation" string="Validation">
<field name="validation_type" widget="radio"/>
@@ -58,7 +56,7 @@
<field name="validity_start"/>
<field name="validity_stop"/>
</group>
<group name="notification" string="Notification" colspan="2">
<group name="notification" string="Notification" colspan="2" groups="base.group_no_one">
<group>
<field name="leave_notif_subtype_id"
domain="[('res_model','=','hr.leave')]"
@@ -91,8 +89,8 @@
<strong><field name="name"/></strong>
</div>
<div>
<span>Max Leaves: <field name="max_leaves"/></span>
<span class="float-right">Leaves Taken: <field name="leaves_taken"/></span>
<span>Max Time Off: <field name="max_leaves"/></span>
<span class="float-right">Time Off Taken: <field name="leaves_taken"/></span>
</div>
</div>
</t>
@@ -105,7 +103,7 @@
<field name="name">hr.leave.type.normal.tree</field>
<field name="model">hr.leave.type</field>
<field name="arch" type="xml">
<tree string="Leave Type">
<tree string="Time Off Type">
<field name="sequence" widget="handle"/>
<field name="display_name"/>
<field name="code"/>
@@ -118,7 +116,7 @@
</record>
<record id="open_view_holiday_status" model="ir.actions.act_window">
<field name="name">Leave Types</field>
<field name="name">Time Off Types</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr.leave.type</field>
<field name="view_type">form</field>
@@ -134,7 +132,7 @@
<menuitem
id="hr_holidays_status_menu_configuration"
action="open_view_holiday_status"
name="Leave Types"
name="Time Off Types"
parent="menu_hr_holidays_configuration"
groups="hr_holidays.group_hr_holidays_user"
sequence="1"/>
+75 -66
View File
@@ -18,30 +18,30 @@
<field name="name">hr.holidays.filter</field>
<field name="model">hr.leave</field>
<field name="arch" type="xml">
<search string="Search Leave">
<search string="Search Time Off">
<field name="employee_id"/>
<field name="department_id" operator="child_of"/>
<field name="holiday_status_id"/>
<field name="name"/>
<filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
<filter string="Approved Leaves" domain="[('state', '=', 'validate')]" name="validated"/>
<filter string="Approved Time Off" domain="[('state', '=', 'validate')]" name="validated"/>
<separator/>
<filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an leave type that is 'active' (active field is True)"/>
<filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an time off type that is 'active' (active field is True)"/>
<separator/>
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
<separator/>
<filter string="My Department Leaves" name="department" domain="[('department_id.member_ids.user_id', '=', uid)]" help="My Department Leaves"/>
<filter string="People I Manage" name="managed_people" domain="[('employee_id.parent_id.user_id', '=', uid)]" help="Leaves of people you are manager of"/>
<filter string="My Team Leaves" name="my_team_leaves" domain="[('employee_id.leave_manager_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_team_leader" help="Leaves of Your Team Members"/>
<filter string="My Department Time Off" name="department" domain="[('department_id.member_ids.user_id', '=', uid)]" help="My Department Time Off"/>
<filter string="People I Manage" name="managed_people" domain="[('employee_id.parent_id.user_id', '=', uid)]" help="Time off of people you are manager of"/>
<filter string="My Team Time Off" name="my_team_leaves" domain="[('employee_id.leave_manager_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_team_leader" help="Time off of Your Team Members"/>
<separator/>
<filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="hr_holidays.group_hr_holidays_manager"/>
<separator/>
<filter string="To Do" name="todo" domain="[('payslip_status', '=', False), ('holiday_status_id.active', '=', True)]"/>
<separator/>
<filter name="year" string="Current Year"
domain="[('holiday_status_id.active', '=', True)]" help="Active Leaves"/>
domain="[('holiday_status_id.active', '=', True)]" help="Active Time Off"/>
<separator/>
<filter string="My Leaves" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
<filter string="My Time Off" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
<separator/>
<filter string="Late Activities" name="activities_overdue"
domain="[('activity_ids.date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]"
@@ -98,7 +98,7 @@
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
<a t-if="widget.editable" role="menuitem" type="edit" class="dropdown-item">Edit Leave</a>
<a t-if="widget.editable" role="menuitem" type="edit" class="dropdown-item">Edit Time Off</a>
<a t-if="widget.deletable" role="menuitem" type="delete" class="dropdown-item">Delete</a>
<a t-if="record.can_approve.value" name="action_approve" type="object"
role="menuitem" class="dropdown-item">Approve</a>
@@ -126,7 +126,17 @@
<field name="name">hr.leave.view.calendar</field>
<field name="model">hr.leave</field>
<field name="arch" type="xml">
<calendar string="Leave Request" date_start="date_from" date_stop="date_to" mode="month" quick_add="False" color="employee_id">
<calendar string="Time Off Request" event_open_popup="true" date_start="date_from" date_stop="date_to" mode="month" quick_add="False" color="employee_id">
<field name="display_name"/>
</calendar>
</field>
</record>
<record id="hr_leave_view_dashboard" model="ir.ui.view">
<field name="name">hr.leave.view.dashboard</field>
<field name="model">hr.leave</field>
<field name="arch" type="xml">
<calendar js_class="time_off_calendar" string="Time Off Request" event_open_popup="true" date_start="date_from" date_stop="date_to" mode="month" quick_add="False" color="employee_id">
<field name="display_name"/>
</calendar>
</field>
@@ -137,7 +147,7 @@
<field name="model">hr.leave</field>
<field name="priority">32</field>
<field name="arch" type="xml">
<form string="Leave Request">
<form string="Time Off Request">
<field name="can_reset" invisible="1"/>
<field name="can_approve" invisible="1"/>
<header>
@@ -153,20 +163,7 @@
<field name="leave_type_request_unit" invisible="1"/>
<group>
<group>
<field name="holiday_status_id" context="{'employee_id':employee_id, 'default_date_from':date_from}" options="{'no_create': True, 'no_open': True}"/>
<field name="number_of_days" invisible="1"/>
<label for="number_of_days_display" string="Duration"/>
<div>
<div attrs="{'invisible': [('leave_type_request_unit', '=', 'hour')]}">
<field name="number_of_days_display" nolabel="1" class="oe_inline"/>
<span class="ml8">Days</span>
</div>
<div attrs="{'invisible': [('leave_type_request_unit', '!=', 'hour')]}">
<field name="number_of_hours_display" nolabel="1" class="oe_inline"/>
<span class="ml8">Hours</span>
</div>
</div>
<field name="holiday_status_id" domain="['|',('allocation_type', 'in', ['fixed_allocation', 'no']),'&amp;',('allocation_type', '=', 'fixed'), ('max_leaves', '>', '0')]" context="{'employee_id':employee_id, 'default_date_from':date_from}" options="{'no_create': True, 'no_open': True}"/>
<label for="request_date_from" string="From"/>
<div>
@@ -193,10 +190,14 @@
'invisible': [('request_unit_half', '=', False)]}"/>
</div>
<div class="o_row o_row_readonly">
<label for="request_unit_half"
attrs="{'readonly': [('state', 'not in', ('draft', 'confirm'))]}"/>
<field name="request_unit_half"
attrs="{'readonly': [('state', 'not in', ('draft', 'confirm'))]}"/>
<label for="request_unit_half" attrs="{
'readonly': [('state', 'not in', ('draft', 'confirm'))],
'invisible': [('leave_type_request_unit', '=', 'day')]
}"/>
<field name="request_unit_half" attrs="{
'readonly': [('state', 'not in', ('draft', 'confirm'))],
'invisible': [('leave_type_request_unit', '=', 'day')]
}"/>
<label for="request_unit_hours" attrs="{
'readonly': [('state', 'not in', ('draft', 'confirm'))],
'invisible': [('leave_type_request_unit', '!=', 'hour')]
@@ -229,7 +230,24 @@
'invisible': [('request_unit_hours', '=', False)]}"/>
</div>
</div>
<field name="number_of_days" invisible="1"/>
<label for="number_of_days" class="oe_edit_only" string="Duration (days)" groups="hr_holidays.group_hr_holidays_manager"/>
<field name="number_of_days" class="oe_edit_only" nolabel="1" groups="hr_holidays.group_hr_holidays_manager"/>
<label for="number_of_days_display" string="Duration"/>
<div>
<div attrs="{'invisible': [('leave_type_request_unit', '=', 'hour')]}">
<field name="number_of_days_display" nolabel="1" class="oe_inline"/>
<span class="ml8">Days</span>
</div>
<div attrs="{'invisible': [('leave_type_request_unit', '!=', 'hour')]}">
<field name="number_of_hours_display" nolabel="1" class="oe_inline"/>
<span class="ml8">Hours</span>
</div>
</div>
<field name="name" attrs="{'readonly': [('state', 'not in', ('draft', 'confirm'))]}"/>
</group>
<group name="col_right">
<field name="employee_id" invisible="1"/>
@@ -287,7 +305,7 @@
<field name="name">hr.holidays.view.tree</field>
<field name="model">hr.leave</field>
<field name="arch" type="xml">
<tree decoration-danger="state == 'refuse'" decoration-info="state == 'draft'" string="Leave Requests"
<tree decoration-danger="state == 'refuse'" decoration-info="state == 'draft'" string="Time Off Requests"
decoration-bf="message_needaction == True">
<field name="employee_id"/>
<field name="department_id"/>
@@ -296,7 +314,7 @@
<field name="name"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="number_of_days" string="Number of Days" sum="Total leaves"/>
<field name="number_of_days" string="Number of Days" sum="Total time off"/>
<field name="state"/>
<field name="payslip_status" widget="toggle_button" options='{"active": "Reported in last payslips", "inactive": "To Report in Payslip"}' groups="hr_holidays.group_hr_holidays_manager"/>
<field name="category_id" invisible="1"/>
@@ -346,17 +364,18 @@
</record>
<record id="action_hr_holidays_dashboard" model="ir.actions.act_window">
<field name="name">All Leaves</field>
<field name="name">All Time Off</field>
<field name="res_model">hr.leave</field>
<field name="view_type">form</field>
<field name="view_mode">calendar</field>
<field name="context">{'search_default_year': 1, 'search_default_group_employee': 1}</field>
<field name="domain">[('holiday_type','=','employee'), ('state', '!=', 'refuse')]</field>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
<field name="view_id" ref="hr_leave_view_calendar"/>
</record>
<menuitem
name="Leaves"
name="Time Off"
id="menu_hr_holidays_root"
sequence="95"
web_icon="hr_holidays,static/description/icon.png"
@@ -372,7 +391,7 @@
<menuitem
id="menu_hr_holidays_my_leaves"
name="My Leaves"
name="My Time Off"
parent="menu_hr_holidays_root"
sequence="3"/>
@@ -409,20 +428,16 @@
groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"/>
<record id="hr_leave_action_new_request" model="ir.actions.act_window">
<field name="name">New Request</field>
<field name="name">Dashboard</field>
<field name="res_model">hr.leave</field>
<field name="view_type">form</field>
<field name="view_mode">calendar,tree,form,activity</field>
<field name="context">{}</field>
<field name="domain">[('user_id', '=', uid)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave request
Create a new time off request
</p><p>
Once you have recorded your leave request, it will be sent
to a manager for validation. Be sure to set the right leave
type (recuperation, legal leaves, sickness) and the exact
number of open days related to your leave.
Once a time off request is posted, your manager is notified for validation.
</p>
</field>
</record>
@@ -430,7 +445,7 @@
<field name="sequence">1</field>
<field name="view_mode">calendar</field>
<field name="act_window_id" ref="hr_leave_action_new_request"/>
<field name="view_id" ref="hr_leave_view_calendar"/>
<field name="view_id" ref="hr_leave_view_dashboard"/>
</record>
<record id="hr_leave_action_new_request_view_tree" model="ir.actions.act_window.view">
<field name="sequence">2</field>
@@ -452,7 +467,7 @@
sequence="2"/>
<record id="hr_leave_action_my" model="ir.actions.act_window">
<field name="name">Leaves Requests</field>
<field name="name">Time Off Requests</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr.leave</field>
<field name="view_type">form</field>
@@ -461,12 +476,9 @@
<field name="domain">[('user_id', '=', uid)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave request
Create a new time off request
</p><p>
Once you have recorded your leave request, it will be sent
to a manager for validation. Be sure to set the right leave
type (recuperation, legal leaves, sickness) and the exact
number of open days related to your leave.
Once a time off request is posted, your manager is notified for validation.
</p>
</field>
</record>
@@ -490,7 +502,7 @@
sequence="4"/>
<record id="hr_leave_action_all" model="ir.actions.act_window">
<field name="name">Leaves</field>
<field name="name">Time Off</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr.leave</field>
<field name="view_type">form</field>
@@ -499,13 +511,13 @@
<field name="domain">[]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave request
Create a new time off request
</p>
</field>
</record>
<record id="hr_leave_action_action_approve_department" model="ir.actions.act_window">
<field name="name">Department Leaves</field>
<field name="name">Department Time Off</field>
<field name="res_model">hr.leave</field>
<field name="view_type">form</field>
<field name="view_mode">tree,kanban,form,calendar,activity</field>
@@ -516,13 +528,13 @@
<field name="domain">[]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave request
Create a new time off request
</p>
</field>
</record>
<record id="hr_leave_action_action_department" model="ir.actions.act_window">
<field name="name">Leaves Analysis</field>
<field name="name">Time Off Analysis</field>
<field name="res_model">hr.leave</field>
<field name="view_type">form</field>
<field name="view_mode">graph,pivot</field>
@@ -535,21 +547,21 @@
<menuitem
id="menu_open_department_leave_approve"
name="Leaves"
name="Time Off"
parent="hr_holidays_menu_manager_approve"
groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user,hr_holidays.group_hr_holidays_team_leader"
action="hr_leave_action_action_approve_department"
sequence="1"/>
<menuitem
id="menu_open_employee_leave"
name="Leaves"
name="Time Off"
parent="hr_holidays_menu_manager_all"
groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user,hr_holidays.group_hr_holidays_team_leader"
action="hr_leave_action_all"
sequence="1"/>
<record id="hr_leave_action_payroll" model="ir.actions.act_window">
<field name="name">Leaves Requests</field>
<field name="name">Time Off Requests</field>
<field name="res_model">hr.leave</field>
<field name="view_type">form</field>
<field name="view_mode">tree,kanban,form,calendar,activity</field>
@@ -557,19 +569,16 @@
<field name="domain">[]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new leave request
Create a new time off request
</p><p>
Once you have recorded your leave request, it will be sent
to a manager for validation. Be sure to set the right leave
type (recuperation, legal leaves, sickness) and the exact
number of open days related to your leave.
Once a time off request is posted, your manager is notified for validation.
</p>
</field>
</record>
<menuitem
id="hr_holidays_menu_manager_payroll_to_report"
name="Leaves to report"
name="Time Off to report"
parent="hr_holidays_menu_manager_payroll"
groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
action="hr_leave_action_payroll"
@@ -580,7 +589,7 @@
<field name="model">hr.leave</field>
<field name="priority">20</field>
<field name="arch" type="xml">
<pivot string="Leaves Summary">
<pivot string="Time Off Summary">
<field name="employee_id" type="row"/>
<field name="date_from" type="col"/>
<field name="number_of_days" type="measure"/>
@@ -593,7 +602,7 @@
<field name="model">hr.leave</field>
<field name="priority">20</field>
<field name="arch" type="xml">
<graph string="Leaves Summary">
<graph string="Time Off Summary">
<field name="employee_id"/>
<field name="number_of_days" type="measure"/>
</graph>
@@ -601,7 +610,7 @@
</record>
<record id="action_hr_available_holidays_report" model="ir.actions.act_window">
<field name="name">Leaves Analysis</field>
<field name="name">Time Off Analysis</field>
<field name="res_model">hr.leave</field>
<field name="view_type">form</field>
<field name="view_mode">graph,pivot,calendar,form</field>
@@ -616,7 +625,7 @@
</record>
<menuitem
name="Leaves Analysis"
name="Time Off Analysis"
id="menu_hr_available_holidays_report_tree"
action="action_hr_available_holidays_report"
parent="menu_hr_holidays_report" sequence="5"/>
+6 -6
View File
@@ -33,7 +33,7 @@
<div t-if="record.leave_to_approve_count.raw_value > 0" class="row">
<div class="col-9">
<a name="%(hr_leave_action_action_approve_department)d" type="action">
Leave Requests
Time Off Requests
</a>
</div>
<div class="col-3">
@@ -69,7 +69,7 @@
<div class="row">
<div class="col-12 text-left">
<a name="%(hr_leave_action_action_approve_department)d" type="action">
<t t-esc="record.leave_to_approve_count.raw_value or 0"/> Leave Requests
<t t-esc="record.leave_to_approve_count.raw_value or 0"/> Time Off Requests
</a>
</div>
</div>
@@ -87,7 +87,7 @@
<div class="row">
<div class="col-12 text-left">
<a name="%(hr_leave_action_action_department)d" type="action">
Leaves
Time Off
</a>
</div>
</div>
@@ -137,7 +137,7 @@
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='active_group']" position="after">
<group string="Leaves">
<group string="Time Off">
<label for="remaining_leaves"/>
<div>
<field name="remaining_leaves" class="oe_inline"/> days
@@ -153,8 +153,8 @@
icon="fa-calendar"
attrs="{'invisible': [('show_leaves','=', False)]}"
groups="base.group_user"
help="Remaining leaves">
<field string="Leaves Left" name="leaves_count" widget="statinfo"/>
help="Remaining time off">
<field string="Time Off Left" name="leaves_count" widget="statinfo"/>
</button>
</button>
</field>
@@ -9,7 +9,7 @@ from odoo.exceptions import UserError
class HolidaysSummaryDept(models.TransientModel):
_name = 'hr.holidays.summary.dept'
_description = 'HR Leaves Summary Report By Department'
_description = 'HR Time Off Summary Report By Department'
date_from = fields.Date(string='From', required=True, default=lambda *a: time.strftime('%Y-%m-01'))
depts = fields.Many2many('hr.department', 'summary_dept_rel', 'sum_id', 'dept_id', string='Department(s)')
@@ -17,7 +17,7 @@ class HolidaysSummaryDept(models.TransientModel):
('Approved', 'Approved'),
('Confirmed', 'Confirmed'),
('both', 'Both Approved and Confirmed')
], string='Leave Type', required=True, default='Approved')
], string='Time Off Type', required=True, default='Approved')
@api.multi
def print_report(self):
@@ -5,7 +5,7 @@
<field name="name">hr.holidays.summary.dept.form</field>
<field name="model">hr.holidays.summary.dept</field>
<field name="arch" type="xml">
<form string="Leaves by Department">
<form string="Time Off by Department">
<group>
<field name="date_from" />
<field name="holiday_type"/>
@@ -20,7 +20,7 @@
</record>
<record id="action_hr_holidays_summary_dept" model="ir.actions.act_window">
<field name="name">Leaves by Department</field>
<field name="name">Time Off by Department</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr.holidays.summary.dept</field>
<field name="view_type">form</field>
@@ -8,7 +8,7 @@ from odoo import api, fields, models
class HolidaysSummaryEmployee(models.TransientModel):
_name = 'hr.holidays.summary.employee'
_description = 'HR Leaves Summary Report By Employee'
_description = 'HR Time Off Summary Report By Employee'
date_from = fields.Date(string='From', required=True, default=lambda *a: time.strftime('%Y-%m-01'))
emp = fields.Many2many('hr.employee', 'summary_emp_rel', 'sum_id', 'emp_id', string='Employee(s)')
@@ -16,7 +16,7 @@ class HolidaysSummaryEmployee(models.TransientModel):
('Approved', 'Approved'),
('Confirmed', 'Confirmed'),
('both', 'Both Approved and Confirmed')
], string='Select Leave Type', required=True, default='Approved')
], string='Select Time Off Type', required=True, default='Approved')
@api.multi
def print_report(self):
@@ -5,7 +5,7 @@
<field name="name">hr.holidays.summary.employee.form</field>
<field name="model">hr.holidays.summary.employee</field>
<field name="arch" type="xml">
<form string="Leaves Summary">
<form string="Time Off Summary">
<group col="4" colspan="6">
<field name="date_from"/>
<newline/>
@@ -22,7 +22,7 @@
</record>
<record id="action_hr_holidays_summary_employee" model="ir.actions.act_window">
<field name="name">Leaves Summary</field>
<field name="name">Time Off Summary</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr.holidays.summary.employee</field>
<field name="view_type">form</field>
+1 -1
View File
@@ -110,7 +110,7 @@
</record>
<record id="benefit_type_leave" model="hr.benefit.type">
<field name="name">Generic Leave</field>
<field name="name">Generic Time Off</field>
<field name="code">LEAVE100</field>
<field name="color">3</field>
<field name="is_leave">True</field>
+3 -3
View File
@@ -161,21 +161,21 @@
</record>
<record id="benefit_type_long_leave" model="hr.benefit.type">
<field name="name">Long Term Leave</field>
<field name="name">Long Term Time Off</field>
<field name="code">LEAVE200</field>
<field name="is_leave">True</field>
<field name="color">4</field>
</record>
<record id="benefit_type_unpaid_leave" model="hr.benefit.type">
<field name="name">Unpaid Leave</field>
<field name="name">Unpaid Time Off</field>
<field name="code">LEAVE300</field>
<field name="is_leave">True</field>
<field name="color">5</field>
</record>
<record id="benefit_type_sick_leave" model="hr.benefit.type">
<field name="name">Sick Leave</field>
<field name="name">Sick Time Off</field>
<field name="code">LEAVE400</field>
<field name="is_leave">True</field>
<field name="color">5</field>
+1 -1
View File
@@ -35,7 +35,7 @@ class TestBenefit(TestPayslipBase):
'code': 'LEAVE100'
})
self.leave_type = self.env['hr.leave.type'].create({
'name': 'Legal Leaves',
'name': 'Paid Time Off',
'time_type': 'leave',
'allocation_type': 'no',
'benefit_type_id': self.benefit_type_leave.id
@@ -24,7 +24,7 @@
<div class="oe_kanban_bottom_left">
<field name="hr_presence_state" invisible="1"/>
<button name="action_set_present" type="object" class="btn btn-primary btn-sm mt8 fa fa-check" title="Set as present" attrs="{'invisible': [('hr_presence_state', '!=', 'to_define')]}"> </button>
<button name="action_open_leave_request" type="object" class="btn btn-primary btn-sm mt8">Leaves</button>
<button name="action_open_leave_request" type="object" class="btn btn-primary btn-sm mt8">Time Off</button>
<button name="action_send_sms" type="object" class="btn btn-primary btn-sm mt8">SMS</button>
<button name="action_send_mail" type="object" class="btn btn-primary btn-sm mt8">Mail</button>
</div>
@@ -8,7 +8,7 @@ class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
module_project_timesheet_synchro = fields.Boolean("Awesome Timesheet")
module_project_timesheet_holidays = fields.Boolean("Leaves")
module_project_timesheet_holidays = fields.Boolean("Time Off")
project_time_mode_id = fields.Many2one(
'uom.uom', related='company_id.project_time_mode_id', string='Project Time Unit', readonly=False,
help="This will set the unit of measure used in projects and tasks.\n"
@@ -72,7 +72,7 @@
</div>
</div>
<div name="section_leaves" groups="base.group_no_one">
<h2>Leaves</h2>
<h2>Time Off</h2>
<div class="row mt16 o_settings_container" name="timesheet_control">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
@@ -81,7 +81,7 @@
<div class="o_setting_right_pane">
<label for="module_project_timesheet_holidays"/>
<div class="text-muted">
Validated leaves create timesheets automatically.
Validated time off create timesheets automatically.
</div>
<div class="content-group">
<div id="module_project_timesheet_holidays"/>
@@ -61,7 +61,7 @@ class HrContract(models.Model):
meal_voucher_amount = fields.Monetary(string="Meal Vouchers",
tracking=True,
help="Amount the employee receives in the form of meal vouchers per worked day.")
holidays = fields.Float(string='Legal Leaves',
holidays = fields.Float(string='Paid Time Off',
help="Number of days of paid leaves the employee gets per year.")
holidays_editable = fields.Boolean(string="Editable Leaves", default=True)
holidays_compensation = fields.Monetary(compute='_compute_holidays_compensation', string="Holidays Compensation")
@@ -6,7 +6,7 @@ from odoo import api, fields, models, _
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
default_holidays = fields.Float(string="Legal Leaves", default_model="hr.contract")
default_holidays = fields.Float(string="Paid Time Off", default_model="hr.contract")
default_commission_on_target = fields.Float(string="Commission on Target", default_model="hr.contract")
default_fuel_card = fields.Float(string="Fuel Card", default_model="hr.contract")
default_representation_fees = fields.Float(string="Representation Fees", default_model="hr.contract")
@@ -75,7 +75,7 @@
<field name="payslip_id" ref="hr_payslip_demo"/>
</record>
<record id="hr_line_demo3" model="hr.payslip.worked_days">
<field name="name">Legal leave</field>
<field name="name">Paid Time Off</field>
<field name="code">LEAVE100</field>
<field name="number_of_days">4</field>
<field name="number_of_hours">32</field>
@@ -140,7 +140,7 @@
<field name="payslip_id" ref="hr_payslip_demo_may"/>
</record>
<record id="hr_line_demo3_may" model="hr.payslip.worked_days">
<field name="name">Sick Leave</field>
<field name="name">Sick Time Off</field>
<field name="code">LEAVE200</field>
<field name="number_of_days">1</field>
<field name="number_of_hours">8</field>
@@ -2,10 +2,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Timesheet when on Leaves',
'name': 'Timesheet when on Time Off',
'version': '1.0',
'category': 'Human Resources',
'summary': 'Schedule timesheet when on leaves',
'summary': 'Schedule timesheet when on time off',
'description': """
Bridge module to integrate leaves in timesheet
================================================
@@ -16,26 +16,38 @@ class HolidaysType(models.Model):
company = self.company_id if self.company_id else self.env.user.company_id
return company.leave_timesheet_task_id.id
timesheet_generate = fields.Boolean('Generate Timesheet', default=True, help="If checked, when validating a leave, timesheet will be generated in the Vacation Project of the company.")
timesheet_project_id = fields.Many2one('project.project', string="Internal Project", default=_default_project_id, help="The project will contain the timesheet generated when a leave is validated.")
timesheet_task_id = fields.Many2one('project.task', string="Internal Task for timesheet", default=_default_task_id, domain="[('project_id', '=', timesheet_project_id)]")
timesheet_generate = fields.Boolean('Generate Timesheet', default=True, help="If checked, when validating a time off, timesheet will be generated in the Vacation Project of the company.")
timesheet_project_id = fields.Many2one('project.project', string="Project", default=_default_project_id, help="The project will contain the timesheet generated when a time off is validated.")
timesheet_task_id = fields.Many2one('project.task', string="Task for timesheet", default=_default_task_id, domain="[('project_id', '=', timesheet_project_id)]")
@api.onchange('timesheet_generate')
@api.onchange('timesheet_task_id')
def _onchange_timesheet_generate(self):
if self.timesheet_generate:
company = self.company_id if self.company_id else self.env.user.company_id
self.timesheet_project_id = company.leave_timesheet_project_id
self.timesheet_task_id = company.leave_timesheet_task_id
if self.timesheet_task_id or self.timesheet_project_id:
self.timesheet_generate = True
else:
self.timesheet_project_id = False
self.timesheet_task_id = False
self.timesheet_generate = False
@api.constrains('timesheet_generate')
@api.onchange('timesheet_project_id')
def _onchange_timesheet_project(self):
company = self.company_id if self.company_id else self.env.user.company_id
default_task_id = company.leave_timesheet_task_id
if default_task_id and default_task_id.project_id == self.timesheet_project_id:
self.timesheet_task_id = default_task_id
else:
self.timesheet_task_id = False
if self.timesheet_project_id:
self.timesheet_generate = True
else:
self.timesheet_generate = False
@api.constrains('timesheet_generate', 'timesheet_project_id', 'timesheet_task_id')
def _check_timesheet_generate(self):
for holiday_status in self:
if holiday_status.timesheet_generate:
if not holiday_status.timesheet_project_id or not holiday_status.timesheet_task_id:
raise ValidationError(_('Both the internal project and task are required to generate timesheet for the leaves.'))
raise ValidationError(_('Both the internal project and task are required to\
generate timesheet for the time of. If you don\'t want timesheet, you must let\
empty internal project and task.'))
class Holidays(models.Model):
@@ -9,9 +9,9 @@ class Company(models.Model):
leave_timesheet_project_id = fields.Many2one(
'project.project', string="Internal Project",
help="Default project value for timesheet generated from leave type.")
help="Default project value for timesheet generated from time off type.")
leave_timesheet_task_id = fields.Many2one(
'project.task', string="Leave Task",
'project.task', string="Time Off Task",
domain="[('project_id', '=', leave_timesheet_project_id)]")
def init(self):
@@ -37,7 +37,7 @@ class Company(models.Model):
})
if not company.leave_timesheet_task_id:
task = self.env['project.task'].sudo().create({
'name': _('Leaves'),
'name': _('Time Off'),
'project_id': company.leave_timesheet_project_id.id,
'active': False,
'company_id': False,
@@ -8,4 +8,4 @@ class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
leave_timesheet_project_id = fields.Many2one(related='company_id.leave_timesheet_project_id', string="Internal Project", readonly=False)
leave_timesheet_task_id = fields.Many2one(related='company_id.leave_timesheet_task_id', string="Leave Task", readonly=False)
leave_timesheet_task_id = fields.Many2one(related='company_id.leave_timesheet_task_id', string="Time Off Task", readonly=False)
@@ -6,10 +6,13 @@
<field name="model">hr.leave.type</field>
<field name="inherit_id" ref="hr_holidays.edit_holiday_status_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='unpaid']" position="after">
<field name="timesheet_generate"/>
<field name="timesheet_project_id" attrs="{'invisible': [('timesheet_generate', '!=', True)], 'required': [('timesheet_generate', '=', True)]}" context="{'active_test': False}" groups="base.group_no_one"/>
<field name="timesheet_task_id" attrs="{'invisible': [('timesheet_generate', '!=', True)], 'required': [('timesheet_generate', '=', True)]}" context="{'active_test': False}" groups="base.group_no_one"/>
<xpath expr="//group[@name='calendar']" position="after">
<group name="timesheet" string="Timesheet" groups="base.group_no_one">
<field name="timesheet_project_id" context="{'active_test': False}"/>
<field name="timesheet_task_id" context="{'active_test': False}"/>
<field name="timesheet_generate" invisible="1"/>
</group>
</xpath>
</field>
</record>
+7 -7
View File
@@ -174,9 +174,9 @@ class ResourceCalendar(models.Model):
'resource.calendar.attendance', 'calendar_id', 'Working Time',
copy=True, default=_get_default_attendance_ids)
leave_ids = fields.One2many(
'resource.calendar.leaves', 'calendar_id', 'Leaves')
'resource.calendar.leaves', 'calendar_id', 'Time Off')
global_leave_ids = fields.One2many(
'resource.calendar.leaves', 'calendar_id', 'Global Leaves',
'resource.calendar.leaves', 'calendar_id', 'Global Time Off',
domain=[('resource_id', '=', False)]
)
hours_per_day = fields.Float("Average Hour per Day", default=HOURS_PER_DAY,
@@ -506,7 +506,7 @@ class ResourceResource(models.Model):
class ResourceCalendarLeaves(models.Model):
_name = "resource.calendar.leaves"
_description = "Resource Leaves Detail"
_description = "Resource Time Off Detail"
name = fields.Char('Reason')
company_id = fields.Many2one(
@@ -517,14 +517,14 @@ class ResourceCalendarLeaves(models.Model):
date_to = fields.Datetime('End Date', required=True)
resource_id = fields.Many2one(
"resource.resource", 'Resource',
help="If empty, this is a generic holiday for the company. If a resource is set, the holiday/leave is only for this resource")
time_type = fields.Selection([('leave', 'Leave'), ('other', 'Other')], default='leave',
help="Whether this should be computed as a holiday or as work time (eg: formation)")
help="If empty, this is a generic time off for the company. If a resource is set, the time off is only for this resource")
time_type = fields.Selection([('leave', 'Time Off'), ('other', 'Other')], default='leave',
help="Whether this should be computed as a time off or as work time (eg: formation)")
@api.constrains('date_from', 'date_to')
def check_dates(self):
if self.filtered(lambda leave: leave.date_from > leave.date_to):
raise ValidationError(_('The start date of the leave must be earlier end date.'))
raise ValidationError(_('The start date of the time off must be earlier end date.'))
@api.onchange('resource_id')
def onchange_resource(self):
+6 -6
View File
@@ -86,7 +86,7 @@
<field name="name">resource.calendar.leaves.search</field>
<field name="model">resource.calendar.leaves</field>
<field name="arch" type="xml">
<search string="Search Working Period Leaves">
<search string="Search Working Period Time Off">
<field name="name"/>
<field name="resource_id"/>
<field name="company_id" groups="base.group_multi_company"/>
@@ -147,14 +147,14 @@
</record>
<record id="action_resource_calendar_leave_tree" model="ir.actions.act_window">
<field name="name">Resource Leaves</field>
<field name="name">Resource Time Off</field>
<field name="res_model">resource.calendar.leaves</field>
<field name="view_mode">tree,form,calendar</field>
<field name="search_view_id" ref="view_resource_calendar_leaves_search"/>
</record>
<record id="resource_calendar_leaves_action_from_calendar" model="ir.actions.act_window">
<field name="name">Resource Leaves</field>
<field name="name">Resource Time Off</field>
<field name="res_model">resource.calendar.leaves</field>
<field name="view_mode">tree,form,calendar</field>
<field name="context">{
@@ -174,7 +174,7 @@
<act_window
id="resource_calendar_resources_leaves"
name="Resources Leaves"
name="Resources Time Off"
res_model="resource.calendar.leaves"
src_model="resource.calendar"
view_mode="calendar,tree,form"
@@ -239,7 +239,7 @@
<sheet string="Working Time">
<div class="oe_button_box" name="button_box">
<button name="%(resource_calendar_leaves_action_from_calendar)d" type="action"
string="Leaves" icon="fa-calendar-times-o"
string="Time Off" icon="fa-calendar-times-o"
class="oe_stat_button"
groups="base.group_no_one"/>
<button name="%(resource_resource_action_from_calendar)d" type="action"
@@ -261,7 +261,7 @@
<page string="Working Hours" name="working_hours">
<field name="attendance_ids"/>
</page>
<page string="Global Leaves" name="global_leaves">
<page string="Global Time Off" name="global_leaves">
<field name="global_leave_ids">
<tree editable="top">
<field name="name"/>
@@ -90,8 +90,8 @@
</record>
<record model="ir.module.category" id="module_category_hr_holidays">
<field name="name">Leaves</field>
<field name="description">Helps you manage your leaves.</field>
<field name="name">Time Off</field>
<field name="description">Helps you manage your time off.</field>
<field name="sequence">10</field>
</record>