summaryrefslogtreecommitdiff
path: root/addons/hr_holidays/report
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/hr_holidays/report
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/hr_holidays/report')
-rw-r--r--addons/hr_holidays/report/__init__.py6
-rw-r--r--addons/hr_holidays/report/holidays_summary_report.py124
-rw-r--r--addons/hr_holidays/report/hr_holidays_reports.xml28
-rw-r--r--addons/hr_holidays/report/hr_holidays_templates.xml82
-rw-r--r--addons/hr_holidays/report/hr_leave_report.py111
-rw-r--r--addons/hr_holidays/report/hr_leave_report_calendar.py68
-rw-r--r--addons/hr_holidays/report/hr_leave_report_calendar.xml40
-rw-r--r--addons/hr_holidays/report/hr_leave_reports.xml118
8 files changed, 577 insertions, 0 deletions
diff --git a/addons/hr_holidays/report/__init__.py b/addons/hr_holidays/report/__init__.py
new file mode 100644
index 00000000..c59b1e68
--- /dev/null
+++ b/addons/hr_holidays/report/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import holidays_summary_report
+from . import hr_leave_report
+from . import hr_leave_report_calendar
diff --git a/addons/hr_holidays/report/holidays_summary_report.py b/addons/hr_holidays/report/holidays_summary_report.py
new file mode 100644
index 00000000..74d41d19
--- /dev/null
+++ b/addons/hr_holidays/report/holidays_summary_report.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import calendar
+
+from datetime import timedelta
+from dateutil.relativedelta import relativedelta
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
+
+
+class HrHolidaySummaryReport(models.AbstractModel):
+ _name = 'report.hr_holidays.report_holidayssummary'
+ _description = 'Holidays Summary Report'
+
+ def _get_header_info(self, start_date, holiday_type):
+ st_date = fields.Date.from_string(start_date)
+ return {
+ 'start_date': fields.Date.to_string(st_date),
+ 'end_date': fields.Date.to_string(st_date + relativedelta(days=59)),
+ 'holiday_type': 'Confirmed and Approved' if holiday_type == 'both' else holiday_type
+ }
+
+ def _date_is_day_off(self, date):
+ return date.weekday() in (calendar.SATURDAY, calendar.SUNDAY,)
+
+ def _get_day(self, start_date):
+ res = []
+ start_date = fields.Date.from_string(start_date)
+ for x in range(0, 60):
+ color = '#ababab' if self._date_is_day_off(start_date) else ''
+ res.append({'day_str': start_date.strftime('%a'), 'day': start_date.day , 'color': color})
+ start_date = start_date + relativedelta(days=1)
+ return res
+
+ def _get_months(self, start_date):
+ # it works for geting month name between two dates.
+ res = []
+ start_date = fields.Date.from_string(start_date)
+ end_date = start_date + relativedelta(days=59)
+ while start_date <= end_date:
+ last_date = start_date + relativedelta(day=1, months=+1, days=-1)
+ if last_date > end_date:
+ last_date = end_date
+ month_days = (last_date - start_date).days + 1
+ res.append({'month_name': start_date.strftime('%B'), 'days': month_days})
+ start_date += relativedelta(day=1, months=+1)
+ return res
+
+ def _get_leaves_summary(self, start_date, empid, holiday_type):
+ res = []
+ count = 0
+ start_date = fields.Date.from_string(start_date)
+ end_date = start_date + relativedelta(days=59)
+ for index in range(0, 60):
+ current = start_date + timedelta(index)
+ res.append({'day': current.day, 'color': ''})
+ if self._date_is_day_off(current) :
+ res[index]['color'] = '#ababab'
+ # count and get leave summary details.
+ holiday_type = ['confirm','validate'] if holiday_type == 'both' else ['confirm'] if holiday_type == 'Confirmed' else ['validate']
+ holidays = self.env['hr.leave'].search([
+ ('employee_id', '=', empid), ('state', 'in', holiday_type),
+ ('date_from', '<=', str(end_date)),
+ ('date_to', '>=', str(start_date))
+ ])
+ for holiday in holidays:
+ # Convert date to user timezone, otherwise the report will not be consistent with the
+ # value displayed in the interface.
+ date_from = fields.Datetime.from_string(holiday.date_from)
+ date_from = fields.Datetime.context_timestamp(holiday, date_from).date()
+ date_to = fields.Datetime.from_string(holiday.date_to)
+ date_to = fields.Datetime.context_timestamp(holiday, date_to).date()
+ for index in range(0, ((date_to - date_from).days + 1)):
+ if date_from >= start_date and date_from <= end_date:
+ res[(date_from-start_date).days]['color'] = holiday.holiday_status_id.color_name
+ date_from += timedelta(1)
+ count += holiday.number_of_days
+ employee = self.env['hr.employee'].browse(empid)
+ return {'emp': employee.name, 'display': res, 'sum': count}
+
+ def _get_data_from_report(self, data):
+ res = []
+ Employee = self.env['hr.employee']
+ if 'depts' in data:
+ for department in self.env['hr.department'].browse(data['depts']):
+ res.append({
+ 'dept': department.name,
+ 'data': [
+ self._get_leaves_summary(data['date_from'], emp.id, data['holiday_type'])
+ for emp in Employee.search([('department_id', '=', department.id)])
+ ],
+ 'color': self._get_day(data['date_from']),
+ })
+ elif 'emp' in data:
+ res.append({'data': [
+ self._get_leaves_summary(data['date_from'], emp.id, data['holiday_type'])
+ for emp in Employee.browse(data['emp'])
+ ]})
+ return res
+
+ def _get_holidays_status(self):
+ res = []
+ for holiday in self.env['hr.leave.type'].search([]):
+ res.append({'color': holiday.color_name, 'name': holiday.name})
+ return res
+
+ @api.model
+ def _get_report_values(self, docids, data=None):
+ if not data.get('form'):
+ raise UserError(_("Form content is missing, this report cannot be printed."))
+
+ holidays_report = self.env['ir.actions.report']._get_report_from_name('hr_holidays.report_holidayssummary')
+ holidays = self.env['hr.leave'].browse(self.ids)
+ return {
+ 'doc_ids': self.ids,
+ 'doc_model': holidays_report.model,
+ 'docs': holidays,
+ 'get_header_info': self._get_header_info(data['form']['date_from'], data['form']['holiday_type']),
+ 'get_day': self._get_day(data['form']['date_from']),
+ 'get_months': self._get_months(data['form']['date_from']),
+ 'get_data_from_report': self._get_data_from_report(data['form']),
+ 'get_holidays_status': self._get_holidays_status(),
+ }
diff --git a/addons/hr_holidays/report/hr_holidays_reports.xml b/addons/hr_holidays/report/hr_holidays_reports.xml
new file mode 100644
index 00000000..fbec8363
--- /dev/null
+++ b/addons/hr_holidays/report/hr_holidays_reports.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<odoo>
+
+ <record id="action_report_holidayssummary" model="ir.actions.report">
+ <field name="name">Time Off Summary</field>
+ <field name="model">hr.holidays.summary.dept</field>
+ <field name="report_type">qweb-pdf</field>
+ <field name="report_name">hr_holidays.report_holidayssummary</field>
+ <field name="report_file">hr_holidays.report_holidayssummary</field>
+ </record>
+
+ <record id="action_report_holidayssummary" model="ir.actions.report">
+ <field name="paperformat_id" ref="hr_holidays.paperformat_hrsummary"/>
+ </record>
+
+ <record id="action_report_holidayssummary2" model="ir.actions.report">
+ <field name="name">Time Off Summary</field>
+ <field name="model">hr.leave.allocation</field>
+ <field name="report_type">qweb-pdf</field>
+ <field name="report_name">hr_holidays.report_holidayssummary</field>
+ <field name="report_file">hr_holidays.report_holidayssummary</field>
+ </record>
+
+ <record id="action_report_holidayssummary" model="ir.actions.report">
+ <field name="paperformat_id" ref="hr_holidays.paperformat_hrsummary"/>
+ </record>
+
+</odoo>
diff --git a/addons/hr_holidays/report/hr_holidays_templates.xml b/addons/hr_holidays/report/hr_holidays_templates.xml
new file mode 100644
index 00000000..dc2d46b8
--- /dev/null
+++ b/addons/hr_holidays/report/hr_holidays_templates.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+<template id="report_holidayssummary">
+ <t t-call="web.html_container">
+ <t t-call="web.internal_layout">
+ <div class="page">
+ <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> Time Off.
+ </h3>
+
+ <table class="table table-bordered mb32" style="table-layout:auto">
+ <thead>
+ <tr>
+ <th>Month</th>
+ <t t-foreach="get_months" t-as="month">
+ &lt;th class="text-center" colspan=<t t-esc="month['days']"/>&gt;<t t-esc="month['month_name']"/>&lt;/th&gt;
+ </t>
+ <th/>
+ </tr>
+ <tr>
+ <td rowspan="2">
+ <strong>Departments and Employees</strong>
+ </td>
+ <t t-foreach="get_day" t-as="day">
+ &lt;td class="text-center oe_leftfit oe_rightfit" style="background-color:<t t-esc="day['color']"/>!important; font-size: 8px; min-width: 18px"&gt; <t t-esc="day['day_str']"/>&lt;/td&gt;
+ </t>
+ <td/>
+ </tr>
+ <tr>
+ <t t-foreach="get_day" t-as="day">
+ &lt;td class="text-center oe_leftfit oe_rightfit" style="background-color:<t t-esc="day['color']"/>!important; font-size: 10px" &gt; <t t-esc="day['day']"/>&lt;/td&gt;
+ </t>
+ <td class="text-center">Sum</td>
+ </tr>
+ </thead>
+ <tbody>
+ <t t-foreach="get_data_from_report" t-as="obj">
+ <tr t-if="'dept' in obj">
+ <td style="background-color:#ababab">
+ <strong><t t-esc="obj['dept']"/></strong>
+ </td>
+ <t t-foreach="obj['color']" t-as="c">
+ &lt;td style=background-color:<t t-esc="c['color']"/> !important/&gt;
+ </t>
+ <td/>
+ </tr>
+ <tr t-foreach="obj['data']" t-as="emp">
+ <td><t t-esc="emp['emp']"/></td>
+ <t t-foreach="emp['display']" t-as="details">
+ &lt;td style=background-color:<t t-esc="details['color']"/> !important /&gt;
+ </t>
+ <td class="text-center"><strong><t t-esc="emp['sum']"/></strong></td>
+ </tr>
+ </t>
+ </tbody>
+ </table>
+
+ <div class="col-3 offset-5 mt32">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th class="col-1">Color</th>
+ <th class="text-center">Time Off Type</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr t-foreach="get_holidays_status" t-as="status">
+ &lt;td style=background-color:<t t-esc="status['color']"/>!important &gt;&lt;/td&gt;
+ <td><t t-esc="status['name']"/></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </t>
+ </t>
+</template>
+
+</odoo>
diff --git a/addons/hr_holidays/report/hr_leave_report.py b/addons/hr_holidays/report/hr_leave_report.py
new file mode 100644
index 00000000..3ea1f0e7
--- /dev/null
+++ b/addons/hr_holidays/report/hr_leave_report.py
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, tools, exceptions, _
+from odoo.osv import expression
+
+
+class LeaveReport(models.Model):
+ _name = "hr.leave.report"
+ _description = 'Time Off Summary / Report'
+ _auto = False
+ _order = "date_from DESC, employee_id"
+
+ employee_id = fields.Many2one('hr.employee', string="Employee", readonly=True)
+ name = fields.Char('Description', readonly=True)
+ number_of_days = fields.Float('Number of Days', readonly=True)
+ leave_type = fields.Selection([
+ ('allocation', 'Allocation'),
+ ('request', 'Time Off')
+ ], string='Request Type', readonly=True)
+ department_id = fields.Many2one('hr.department', string='Department', readonly=True)
+ category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True)
+ holiday_status_id = fields.Many2one("hr.leave.type", string="Leave Type", readonly=True)
+ state = fields.Selection([
+ ('draft', 'To Submit'),
+ ('cancel', 'Cancelled'),
+ ('confirm', 'To Approve'),
+ ('refuse', 'Refused'),
+ ('validate1', 'Second Approval'),
+ ('validate', 'Approved')
+ ], string='Status', readonly=True)
+ holiday_type = fields.Selection([
+ ('employee', 'By Employee'),
+ ('category', 'By Employee Tag')
+ ], string='Allocation Mode', readonly=True)
+ date_from = fields.Datetime('Start Date', readonly=True)
+ date_to = fields.Datetime('End Date', readonly=True)
+ payslip_status = fields.Boolean('Reported in last payslips', readonly=True)
+
+ def init(self):
+ tools.drop_view_if_exists(self._cr, 'hr_leave_report')
+
+ self._cr.execute("""
+ CREATE or REPLACE view hr_leave_report as (
+ SELECT row_number() over(ORDER BY leaves.employee_id) as id,
+ leaves.employee_id as employee_id, leaves.name as name,
+ leaves.number_of_days as number_of_days, leaves.leave_type as leave_type,
+ leaves.category_id as category_id, leaves.department_id as department_id,
+ leaves.holiday_status_id as holiday_status_id, leaves.state as state,
+ leaves.holiday_type as holiday_type, leaves.date_from as date_from,
+ leaves.date_to as date_to, leaves.payslip_status as payslip_status
+ from (select
+ allocation.employee_id as employee_id,
+ allocation.private_name as name,
+ allocation.number_of_days as number_of_days,
+ allocation.category_id as category_id,
+ allocation.department_id as department_id,
+ allocation.holiday_status_id as holiday_status_id,
+ allocation.state as state,
+ allocation.holiday_type,
+ null as date_from,
+ null as date_to,
+ FALSE as payslip_status,
+ 'allocation' as leave_type
+ from hr_leave_allocation as allocation
+ union all select
+ request.employee_id as employee_id,
+ request.private_name as name,
+ (request.number_of_days * -1) as number_of_days,
+ request.category_id as category_id,
+ request.department_id as department_id,
+ request.holiday_status_id as holiday_status_id,
+ request.state as state,
+ request.holiday_type,
+ request.date_from as date_from,
+ request.date_to as date_to,
+ request.payslip_status as payslip_status,
+ 'request' as leave_type
+ from hr_leave as request) leaves
+ );
+ """)
+
+ @api.model
+ def action_time_off_analysis(self):
+ domain = [('holiday_type', '=', 'employee')]
+
+ if self.env.context.get('active_ids'):
+ domain = expression.AND([
+ domain,
+ [('employee_id', 'in', self.env.context.get('active_ids', []))]
+ ])
+
+ return {
+ 'name': _('Time Off Analysis'),
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'hr.leave.report',
+ 'view_mode': 'tree,pivot,form',
+ 'search_view_id': self.env.ref('hr_holidays.view_hr_holidays_filter_report').id,
+ 'domain': domain,
+ 'context': {
+ 'search_default_group_type': True,
+ 'search_default_year': True,
+ 'search_default_validated': True,
+ }
+ }
+
+ @api.model
+ def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
+ if not self.user_has_groups('hr_holidays.group_hr_holidays_user') and 'name' in groupby:
+ raise exceptions.UserError(_('Such grouping is not allowed.'))
+ return super(LeaveReport, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
diff --git a/addons/hr_holidays/report/hr_leave_report_calendar.py b/addons/hr_holidays/report/hr_leave_report_calendar.py
new file mode 100644
index 00000000..0a5b45db
--- /dev/null
+++ b/addons/hr_holidays/report/hr_leave_report_calendar.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, tools, SUPERUSER_ID
+
+from odoo.addons.base.models.res_partner import _tz_get
+
+
+class LeaveReportCalendar(models.Model):
+ _name = "hr.leave.report.calendar"
+ _description = 'Time Off Calendar'
+ _auto = False
+ _order = "start_datetime DESC, employee_id"
+
+ name = fields.Char(string='Name', readonly=True)
+ start_datetime = fields.Datetime(string='From', readonly=True)
+ stop_datetime = fields.Datetime(string='To', readonly=True)
+ tz = fields.Selection(_tz_get, string="Timezone", readonly=True)
+ duration = fields.Float(string='Duration', readonly=True)
+ employee_id = fields.Many2one('hr.employee', readonly=True)
+ company_id = fields.Many2one('res.company', readonly=True)
+ state = fields.Selection([
+ ('draft', 'To Submit'),
+ ('cancel', 'Cancelled'), # YTI This state seems to be unused. To remove
+ ('confirm', 'To Approve'),
+ ('refuse', 'Refused'),
+ ('validate1', 'Second Approval'),
+ ('validate', 'Approved')
+ ], readonly=True)
+
+ def init(self):
+ tools.drop_view_if_exists(self._cr, 'hr_leave_report_calendar')
+ self._cr.execute("""CREATE OR REPLACE VIEW hr_leave_report_calendar AS
+ (SELECT
+ row_number() OVER() AS id,
+ CONCAT(em.name, ': ', hl.duration_display) AS name,
+ hl.date_from AS start_datetime,
+ hl.date_to AS stop_datetime,
+ hl.employee_id AS employee_id,
+ hl.state AS state,
+ em.company_id AS company_id,
+ CASE
+ WHEN hl.holiday_type = 'employee' THEN rr.tz
+ ELSE %s
+ END AS tz
+ FROM hr_leave hl
+ LEFT JOIN hr_employee em
+ ON em.id = hl.employee_id
+ LEFT JOIN resource_resource rr
+ ON rr.id = em.resource_id
+ WHERE
+ hl.state IN ('confirm', 'validate', 'validate1')
+ ORDER BY id);
+ """, [self.env.company.resource_calendar_id.tz or self.env.user.tz or 'UTC'])
+
+ def _read(self, fields):
+ res = super()._read(fields)
+ if self.env.context.get('hide_employee_name') and 'employee_id' in self.env.context.get('group_by', []):
+ name_field = self._fields['name']
+ for record in self.with_user(SUPERUSER_ID):
+ self.env.cache.set(record, name_field, record.name.split(':')[-1].strip())
+ return res
+
+ @api.model
+ def get_unusual_days(self, date_from, date_to=None):
+ # Checking the calendar directly allows to not grey out the leaves taken
+ # by the employee
+ return self.env['hr.leave'].get_unusual_days(date_from, date_to=date_to)
diff --git a/addons/hr_holidays/report/hr_leave_report_calendar.xml b/addons/hr_holidays/report/hr_leave_report_calendar.xml
new file mode 100644
index 00000000..c7f8a78e
--- /dev/null
+++ b/addons/hr_holidays/report/hr_leave_report_calendar.xml
@@ -0,0 +1,40 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<odoo>
+ <record id="action_hr_holidays_dashboard" model="ir.actions.act_window">
+ <field name="name">All Time Off</field>
+ <field name="res_model">hr.leave.report.calendar</field>
+ <field name="view_mode">calendar</field>
+ <!-- YTI : Ecrabouille explicitely those fields, as this is deployed on
+ a stable release. -->
+ <field name="view_id"/>
+ <field name="search_view_id"/>
+ <field name="domain">[('employee_id.active','=',True)]</field>
+ <field name="context">{'hide_employee_name': 1}</field>
+ </record>
+
+ <record id="hr_leave_report_calendar_view" model="ir.ui.view">
+ <field name="name">hr.leave.report.calendar.view</field>
+ <field name="model">hr.leave.report.calendar</field>
+ <field name="arch" type="xml">
+ <calendar string="Time Off" date_start="start_datetime" date_stop="stop_datetime" mode="month" quick_add="False" color="employee_id" event_open_popup="True" js_class="time_off_calendar_all" show_unusual_days="True">
+ <field name="name"/>
+ <field name="employee_id" filters="1" invisible="1"/>
+ </calendar>
+ </field>
+ </record>
+
+ <record id="hr_leave_report_calendar_view_form" model="ir.ui.view">
+ <field name="name">hr.leave.report.calendar.view.form</field>
+ <field name="model">hr.leave.report.calendar</field>
+ <field name="arch" type="xml">
+ <form string="Time Off">
+ <group>
+ <field name="name"/>
+ <field name="start_datetime"/>
+ <field name="stop_datetime"/>
+ <field name="employee_id" />
+ </group>
+ </form>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/hr_holidays/report/hr_leave_reports.xml b/addons/hr_holidays/report/hr_leave_reports.xml
new file mode 100644
index 00000000..efe3f81b
--- /dev/null
+++ b/addons/hr_holidays/report/hr_leave_reports.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<odoo>
+
+ <record id="view_hr_holidays_filter_report" model="ir.ui.view">
+ <field name="name">hr.holidays.filter</field>
+ <field name="model">hr.leave.report</field>
+ <field name="arch" type="xml">
+ <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 time off type that is 'active' (active field is True)"/>
+ <separator/>
+ <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 name="year" string="Current Year"
+ 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/>
+ <field name="employee_id"/>
+ <field name="department_id" operator="child_of"/>
+ <field name="holiday_status_id"/>
+ <group expand="0" string="Group By">
+ <filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
+ <filter name="group_type" string="Type" context="{'group_by':'holiday_status_id'}"/>
+ <separator/>
+ <filter name="group_date_from" string="Start Date" context="{'group_by':'date_from'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <record id="hr_leave_report_tree" model="ir.ui.view">
+ <field name="name">report.hr.holidays.report.leave_all.tree</field>
+ <field name="model">hr.leave.report</field>
+ <field name="arch" type="xml">
+ <tree create="0" edit="0" delete="0">
+ <field name="employee_id"/>
+ <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
+ <field name="leave_type"/>
+ <field name="date_from"/>
+ <field name="date_to"/>
+ <field name="state"/>
+ <field name="name"/>
+ </tree>
+ </field>
+ </record>
+
+ <!-- TODO: See if we need to keep this -->
+ <record id="hr_leave_report_kanban" model="ir.ui.view">
+ <field name="name">report.hr.holidays.report.leave_all.kanban</field>
+ <field name="model">hr.leave.report</field>
+ <field name="arch" type="xml">
+ <kanban class="o_kanban_mobile" create="0">
+ <field name="employee_id"/>
+ <field name="date_from"/>
+ <field name="date_to"/>
+ <field name="name"/>
+ <field name="number_of_days"/>
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_global_click">
+ <div>
+ <span>
+ <img t-att-src="kanban_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="oe_kanban_avatar o_image_40_cover float-left mr4"/>
+ </span>
+ <span>
+ <div>
+ <strong class="o_kanban_record_title"><t t-esc="record.employee_id.value"/></strong>
+ <span class="float-right">
+ <field name="state" widget="label_selection" options="{'classes': {'draft': 'default', 'validate': 'success','confirm': 'default', 'cancel': 'danger'}}"/>
+ </span>
+ </div>
+ <div class="text-muted o_kanban_record_subtitle">
+ <t t-esc="record.name.value"/>
+ </div>
+ </span>
+ </div>
+ <hr class="mt4 mb8"/>
+ <div class="o_kanban_record_bottom mt8 mb4">
+ <div t-attf-class="oe_kanban_bottom_left #{record.date_from.value ? 'mt8 mb4': ''}">
+ <table class="text-right" t-if="record.date_from.value">
+ <tr>
+ <td style="padding-bottom:4px"><small class="text-muted">from</small></td>
+ <td style="padding:0 0 4px 4px"><t t-esc="record.date_from.value"/></td>
+ </tr>
+ <tr>
+ <td><small class="text-muted">to</small></td>
+ <td style="padding-left:4px"><t t-esc="record.date_to.value"/></td>
+ </tr>
+ </table>
+ </div>
+ <div t-attf-class="oe_kanban_bottom_right #{record.date_from.value ? 'mt8': ''}">
+ <span class="badge badge-pill"><t t-esc="record.number_of_days.value"/> days</span>
+ </div>
+ </div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <record id="act_hr_employee_holiday_request" model="ir.actions.server">
+ <field name="name">Time off Analysis</field>
+ <field name="model_id" ref="hr_holidays.model_hr_leave_report"/>
+ <field name="binding_model_id" ref="hr.model_hr_employee"/>
+ <field name="state">code</field>
+ <field name="groups_id" eval="[(4, ref('base.group_user'))]"/>
+ <field name="code">
+ action = model.action_time_off_analysis()
+ </field>
+ </record>
+
+</odoo>