Browse Source

Aug 17: [ADD] Initital Commits 'hr_leave_dashboard'

pull/254/merge
Cybrosys Technologies 8 months ago
parent
commit
bab0b586c4
  1. 54
      hr_leave_dashboard/README.rst
  2. 23
      hr_leave_dashboard/__init__.py
  3. 62
      hr_leave_dashboard/__manifest__.py
  4. 7
      hr_leave_dashboard/doc/RELEASE_NOTES.md
  5. 22
      hr_leave_dashboard/models/__init__.py
  6. 181
      hr_leave_dashboard/models/hr_leave.py
  7. 22
      hr_leave_dashboard/report/__init__.py
  8. 111
      hr_leave_dashboard/report/hr_leave_report.py
  9. 89
      hr_leave_dashboard/report/hr_leave_report_templates.xml
  10. 11
      hr_leave_dashboard/report/hr_leave_reports.xml
  11. BIN
      hr_leave_dashboard/static/description/assets/icons/check.png
  12. BIN
      hr_leave_dashboard/static/description/assets/icons/chevron.png
  13. BIN
      hr_leave_dashboard/static/description/assets/icons/cogs.png
  14. BIN
      hr_leave_dashboard/static/description/assets/icons/consultation.png
  15. BIN
      hr_leave_dashboard/static/description/assets/icons/ecom-black.png
  16. BIN
      hr_leave_dashboard/static/description/assets/icons/education-black.png
  17. BIN
      hr_leave_dashboard/static/description/assets/icons/hotel-black.png
  18. BIN
      hr_leave_dashboard/static/description/assets/icons/license.png
  19. BIN
      hr_leave_dashboard/static/description/assets/icons/lifebuoy.png
  20. BIN
      hr_leave_dashboard/static/description/assets/icons/manufacturing-black.png
  21. BIN
      hr_leave_dashboard/static/description/assets/icons/pos-black.png
  22. BIN
      hr_leave_dashboard/static/description/assets/icons/puzzle.png
  23. BIN
      hr_leave_dashboard/static/description/assets/icons/restaurant-black.png
  24. BIN
      hr_leave_dashboard/static/description/assets/icons/service-black.png
  25. BIN
      hr_leave_dashboard/static/description/assets/icons/trading-black.png
  26. BIN
      hr_leave_dashboard/static/description/assets/icons/training.png
  27. BIN
      hr_leave_dashboard/static/description/assets/icons/update.png
  28. BIN
      hr_leave_dashboard/static/description/assets/icons/user.png
  29. BIN
      hr_leave_dashboard/static/description/assets/icons/wrench.png
  30. BIN
      hr_leave_dashboard/static/description/assets/misc/categories.png
  31. BIN
      hr_leave_dashboard/static/description/assets/misc/check-box.png
  32. BIN
      hr_leave_dashboard/static/description/assets/misc/compass.png
  33. BIN
      hr_leave_dashboard/static/description/assets/misc/corporate.png
  34. BIN
      hr_leave_dashboard/static/description/assets/misc/customer-support.png
  35. BIN
      hr_leave_dashboard/static/description/assets/misc/cybrosys-logo.png
  36. BIN
      hr_leave_dashboard/static/description/assets/misc/features.png
  37. BIN
      hr_leave_dashboard/static/description/assets/misc/logo.png
  38. BIN
      hr_leave_dashboard/static/description/assets/misc/pictures.png
  39. BIN
      hr_leave_dashboard/static/description/assets/misc/pie-chart.png
  40. BIN
      hr_leave_dashboard/static/description/assets/misc/right-arrow.png
  41. BIN
      hr_leave_dashboard/static/description/assets/misc/star.png
  42. BIN
      hr_leave_dashboard/static/description/assets/misc/support.png
  43. BIN
      hr_leave_dashboard/static/description/assets/misc/whatsapp.png
  44. BIN
      hr_leave_dashboard/static/description/assets/modules/chatter_view.jpg
  45. BIN
      hr_leave_dashboard/static/description/assets/modules/dynamic_financial_reports.png
  46. BIN
      hr_leave_dashboard/static/description/assets/modules/hid_menu.png
  47. BIN
      hr_leave_dashboard/static/description/assets/modules/login_style.png
  48. BIN
      hr_leave_dashboard/static/description/assets/modules/login_user.png
  49. BIN
      hr_leave_dashboard/static/description/assets/modules/qr_code.png
  50. BIN
      hr_leave_dashboard/static/description/assets/screenshots/hero.gif
  51. BIN
      hr_leave_dashboard/static/description/assets/screenshots/hr_leave_dashboard_01.png
  52. BIN
      hr_leave_dashboard/static/description/assets/screenshots/hr_leave_dashboard_02.png
  53. BIN
      hr_leave_dashboard/static/description/banner.png
  54. BIN
      hr_leave_dashboard/static/description/icon.png
  55. 524
      hr_leave_dashboard/static/description/index.html
  56. 89
      hr_leave_dashboard/static/src/css/hr_leave_dashboard.css
  57. 435
      hr_leave_dashboard/static/src/js/time_off_emp_dashboard.js
  58. 213
      hr_leave_dashboard/static/src/scss/hr_org_chart.scss
  59. 10
      hr_leave_dashboard/static/src/scss/time_off_dashboard.scss
  60. 14
      hr_leave_dashboard/static/src/scss/variables.scss
  61. 169
      hr_leave_dashboard/static/src/xml/emp_org_chart_templates.xml
  62. 245
      hr_leave_dashboard/static/src/xml/time_off_calendar.xml

54
hr_leave_dashboard/README.rst

@ -0,0 +1,54 @@
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
Hr Leave Dashboard
==================
This module helps you to brings a multipurpose graphical dashboard for Time Off module and making the relationship management better and easier.
Installation
============
- www.odoo.com/documentation/15.0/setup/install.html
- Install our custom addon
Configuration
=============
* No additional configurations needed.
Company
-------
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__
License
-------
General Public License, Version 3 (AGPL v3).
(https://www.gnu.org/licenses/agpl-3.0-standalone.html)
Credits
-------
* Developer: (v15) Safa Faheem,
(v16) Rahul Rajeev,
(v17) Safa Faheem,
Contact: odoo@cybrosys.com
Contacts
--------
* Mail Contact : odoo@cybrosys.com
* Website : https://cybrosys.com
Bug Tracker
-----------
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported.
Maintainer
==========
.. image:: https://cybrosys.com/images/logo.png
:target: https://cybrosys.com
This module is maintained by Cybrosys Technologies.
For support and more information, please visit `Our Website <https://cybrosys.com/>`__
Further information
===================
HTML Description: `<static/description/index.html>`__

23
hr_leave_dashboard/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import models
from . import report

62
hr_leave_dashboard/__manifest__.py

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
{
'name': "Hr Leave Dashboard",
'version': '15.0.1.0.0',
'category': 'Human Resources',
'summary': """Advanced Leave Dashboard helps to view your and your
subordinate's details""",
'description': """Advanced Leave Dashboard brings a multipurpose graphical
dashboard for Time Off module and making the relationship management better
and easier""",
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'website': "https://www.cybrosys.com",
'depends': ['base', 'hr_holidays', 'hr_org_chart'],
'data': [
'report/hr_leave_reports.xml',
'report/hr_leave_report_templates.xml',
],
'assets': {
'web.assets_backend': [
'hr_leave_dashboard/static/src/js/time_off_emp_dashboard.js',
'hr_leave_dashboard/static/src/css/hr_leave_dashboard.css',
'hr_leave_dashboard/static/src/scss/variables.scss',
'hr_leave_dashboard/static/src/scss/hr_org_chart.scss',
'hr_leave_dashboard/static/src/scss/time_off_dashboard.scss',
'hr_holidays/static/src/scss/time_off.scss',
],
'web.assets_qweb': [
'hr_leave_dashboard/static/src/xml/emp_org_chart_templates.xml',
'hr_leave_dashboard/static/src/xml/time_off_calendar.xml',
]
},
'external_dependencies': {
'python': ['pandas'],
},
'images': ['static/description/banner.png'],
'license': 'AGPL-3',
'installable': True,
'auto_install': False,
'application': False,
}

7
hr_leave_dashboard/doc/RELEASE_NOTES.md

@ -0,0 +1,7 @@
## Module <hr_leave_dashboard>
#### 08.08.2024
#### Version 15.0.1.0.0
#### ADD
- Initial Commit Hr Leave Dashboard

22
hr_leave_dashboard/models/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import hr_leave

181
hr_leave_dashboard/models/hr_leave.py

@ -0,0 +1,181 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import pytz
from odoo import api, fields, models
class HrLeave(models.Model):
"""Inherit the model hr.leave to introduce supplementary functionality
aimed at incorporating specific employee details."""
_inherit = 'hr.leave'
def _prepare_employee_data(self, employee):
"""Function to prepare employee data for the dashboard"""
return {
'id': employee.id,
'name': employee.name,
'job_id': employee.job_id.name,
'approval_status_count': self.get_approval_status_count(employee.id)
}
@api.model
def get_current_employee(self):
"""This function fetches current employee details in a dictionary"""
current_employee = self.env.user.employee_ids
current_shift = self.get_current_shift()
upcoming_holidays = self.get_upcoming_holidays()
absentees = self.get_absentees()
approval_status_count = self.get_approval_status_count(current_employee.id)
return {
'id': current_employee.id,
'name': current_employee.name,
'job_id': current_employee.job_id.id,
'image_1920': current_employee.image_1920,
'work_email': current_employee.work_email,
'work_phone': current_employee.work_phone,
'resource_calendar_id': current_employee.resource_calendar_id.name,
'link': '/mail/view?model=%s&res_id=%s' % (
'hr.employee.public', current_employee.id,),
'department_id': current_employee.department_id.name,
'company': current_employee.company_id.name,
'job_position': current_employee.job_id.name,
'parent_id': current_employee.parent_id.ids,
'child_ids': current_employee.child_ids.ids,
'child_all_count': current_employee.child_all_count,
'manager': self._prepare_employee_data(
current_employee.parent_id) if (
current_employee.parent_id) else {},
'manager_all_count': len(current_employee.parent_id.ids),
'children': [self._prepare_employee_data(child) for child in
current_employee.child_ids if
child != current_employee],
'current_shift': current_shift,
'upcoming_holidays': upcoming_holidays,
'absentees': absentees,
'approval_status_count': approval_status_count,
'is_manager': self.env.user.has_group('hr_holidays.group_hr_holidays_manager')
}
@api.model
def get_absentees(self):
"""The function retrieves a list of employees who are absent on the
current date by querying the hr_leave table and comparing the
date_from and date_to fields of validated leave requests. It returns
a list of dictionaries containing the employee's name, employee_id,
date_from, and date_to"""
current_employee = self.env.user.employee_ids
children = [self._prepare_employee_data(child) for child in
current_employee.child_ids if
child != current_employee]
child_list = [child.get('id') for child in children]
if len(child_list) > 1:
query = "SELECT employee_id,name,date_from,date_to FROM hr_leave " \
"INNER JOIN hr_employee ON hr_leave.employee_id = " \
"hr_employee.id WHERE state = 'validate' AND " \
"employee_id in %s" % str(tuple(child_list))
self._cr.execute(query)
elif len(child_list) == 1:
query = "SELECT employee_id,name,date_from,date_to FROM hr_leave " \
"INNER JOIN hr_employee ON hr_leave.employee_id = " \
"hr_employee.id WHERE state = 'validate' AND " \
"employee_id = %s" % child_list[0]
self._cr.execute(query)
leave = self._cr.dictfetchall()
absentees = [
leave[leave_date] for leave_date in range(len(leave))
if leave[leave_date].get('date_from') <= fields.datetime.now() <= leave[
leave_date].get('date_to')
]
return absentees
@api.model
def get_current_shift(self):
""" This function fetches current employee's current shift"""
current_employee = self.env.user.employee_ids
employee_tz = current_employee.tz or self.env.context.get('tz')
employee_pytz = pytz.timezone(employee_tz) if employee_tz else pytz.utc
employee_datetime = fields.datetime.now().astimezone(employee_pytz)
hour = employee_datetime.strftime("%H")
minute = employee_datetime.strftime("%M")
day = employee_datetime.strftime("%A")
time = hour + '.' + minute
day_num = '0' if day == 'Monday' else '1' if day == 'Tuesday' \
else '2' if day == 'Wednesday' else '3' if day == 'Thursday' \
else '4' if day == 'Friday' else '5' if day == 'Saturday' else '6'
for shift in current_employee.resource_calendar_id.attendance_ids:
if shift.dayofweek == day_num and shift.hour_from <= float(
time) <= shift.hour_to:
return shift.name
return False
@api.model
def get_upcoming_holidays(self):
""" This function fetches upcoming holidays"""
current_employee = self.env.user.employee_ids
employee_tz = current_employee.tz or self.env.context.get('tz')
employee_pytz = pytz.timezone(employee_tz) if employee_tz else pytz.utc
employee_datetime = fields.datetime.now().astimezone(employee_pytz)
query = "SELECT * FROM public.resource_calendar_leaves WHERE " \
"resource_id is null"
self._cr.execute(query)
holidays = self._cr.dictfetchall()
upcoming_holidays = [holiday for holiday in holidays if
employee_datetime.date() < holiday.get(
'date_to').date()]
return upcoming_holidays
@api.model
def get_approval_status_count(self, current_employee):
""" This function fetches approval status count"""
return {
'validate_count': self.env['hr.leave'].search_count([
('employee_id', '=', current_employee),
('state', '=', 'validate')
]),
'confirm_count': self.env['hr.leave'].search_count([
('employee_id', '=', current_employee),
('state', '=', 'confirm')
]),
'refuse_count': self.env['hr.leave'].search_count([
('employee_id', '=', current_employee),
('state', '=', 'refuse')
])
}
@api.model
def get_all_validated_leaves(self):
""" This function fetches all validated leaves"""
leaves = self.env['hr.leave'].search([('state', '=', 'validate')])
all_validated_leaves = [
{
'id': leave.id,
'employee_id': leave.employee_id.id,
'employee_name': leave.employee_id.name,
'request_date_from': leave.request_date_from,
'request_date_to': leave.request_date_to,
'leave_type_id': leave.holiday_status_id.id,
'leave_type': leave.holiday_status_id.name,
'number_of_days': leave.number_of_days
}
for leave in leaves
]
return all_validated_leaves

22
hr_leave_dashboard/report/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import hr_leave_report

111
hr_leave_dashboard/report/hr_leave_report.py

@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import pandas
from datetime import timedelta
from odoo import api, fields, models
from odoo.tools import date_utils
class HrLeaveReport(models.AbstractModel):
"""Model for the dashboard for viewing the employees leave"""
_name = 'report.hr_leave_dashboard.hr_leave_report'
_description = 'HR Leave Report'
@api.model
def _get_report_values(self, docids, data=None):
"""Function for getting the report values"""
if data.get('duration') == 'this_month':
option = pandas.date_range(
date_utils.start_of(fields.Date.today(), 'month'),
date_utils.end_of(fields.Date.today(), 'month') - timedelta(
days=0),
freq='d').strftime(
"%Y-%m-%d").tolist()
elif data.get('duration') == 'this_year':
option = pandas.date_range(
date_utils.start_of(fields.Date.today(), 'year'),
date_utils.end_of(fields.Date.today(), 'year') - timedelta(
days=0),
freq='d').strftime(
"%Y-%m-%d").tolist()
elif data.get('duration') == 'this_week':
option = pandas.date_range(
date_utils.start_of(fields.Date.today(), 'week'),
date_utils.end_of(fields.Date.today(), 'week') - timedelta(
days=0),
freq='d').strftime(
"%Y-%m-%d").tolist()
else:
option = [str(fields.Date.today())]
if not self.env.user.employee_ids.child_ids:
query = """SELECT l.id, lt.id as hr_leave_type_id, e.id as
emp_id, e.name as emp_name, e.department_id as emp_department,
e.parent_id as emp_parent_id, request_date_from, request_date_to,
l.number_of_days, lt.name as leave_type,
SUM(al.number_of_days) AS allocated_days, SUM(CASE WHEN l.state =
'validate' THEN l.number_of_days ELSE 0 END) AS taken_days,
SUM(al.number_of_days) - SUM(CASE WHEN l.state = 'validate' THEN
l.number_of_days ELSE 0 END) AS balance_days FROM hr_employee e
inner join hr_leave_allocation al ON al.employee_id = e.id inner
join hr_leave l on l.employee_id = e.id inner join hr_leave_type
lt on l.holiday_status_id = lt.id WHERE l.state = 'validate' AND
e.department_id = '%s' GROUP BY e.id,lt.id,l.id""" % \
self.env.user.employee_ids.department_id.id
else:
query = """SELECT l.id, lt.id as hr_leave_type_id, e.id as
emp_id, e.name as emp_name, e.department_id as emp_department,
e.parent_id as emp_parent_id, request_date_from, request_date_to,
l.number_of_days, lt.name as leave_type,
SUM(al.number_of_days) AS allocated_days, SUM(CASE WHEN l.state =
'validate' THEN l.number_of_days ELSE 0 END) AS taken_days,
SUM(al.number_of_days) - SUM(CASE WHEN l.state = 'validate' THEN
l.number_of_days ELSE 0 END) AS balance_days FROM hr_employee e
inner join hr_leave_allocation al ON al.employee_id = e.id inner
join hr_leave l on l.employee_id = e.id inner join hr_leave_type
lt on l.holiday_status_id = lt.id WHERE l.state = 'validate'
GROUP BY e.id,lt.id,l.id"""
self.env.cr.execute(query)
leave_data = self.env.cr.dictfetchall()
filtered_list = []
filtered_tuple = []
for leave in leave_data:
leave_list = pandas.date_range(leave.get('request_date_from'),
leave.get(
'request_date_to') - timedelta(
days=1), freq='d').strftime(
"%Y-%m-%d").tolist()
for date in leave_list:
if date in option:
filtered_list.append(leave)
break
for leave in filtered_list:
if (leave.get('hr_leave_type_id'), leave.get('emp_id')) in \
filtered_tuple:
filtered_list.remove(leave)
else:
filtered_tuple.append(
(leave.get('hr_leave_type_id'), leave.get('emp_id')))
return {
'duration': data.get('duration'),
'filtered_list': filtered_list,
}

89
hr_leave_dashboard/report/hr_leave_report_templates.xml

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Template for the hr leave report -->
<template id="hr_leave_report">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<div class="page">
<div class="oe_structure">
<div class="text-center">
<h1>Leave Report</h1>
</div>
<div class="text-center" t-if="duration=='today'">
<h5>Absentees Today</h5>
</div>
<div class="text-center" t-if="duration=='this_week'">
<h5>Absentees this week</h5>
</div>
<div class="text-center" t-if="duration=='this_month'">
<h5>Absentees this month</h5>
</div>
<div class="text-center" t-if="duration=='this_year'">
<h5>Absentees this year</h5>
</div>
<div class="row">
<table class="table">
<thead>
<tr>
<th>
<strong>
Employee ID
</strong>
</th>
<th>
<strong>
Employee Name
</strong>
</th>
<th>
<strong>
Leave Type
</strong>
</th>
<th>
<strong>
Allocated Balance
</strong>
</th>
<th>
<strong>
Taken Leaves
</strong>
</th>
<th>
<strong>
Remaining Balance
</strong>
</th>
</tr>
</thead>
<t t-foreach="filtered_list" t-as="leave_data">
<tr>
<td>
<span t-esc="leave_data['emp_id']"/>
</td>
<td>
<span t-esc="leave_data['emp_name']"/>
</td>
<td>
<span t-esc="leave_data['leave_type']"/>
</td>
<td>
<span t-esc="leave_data['allocated_days']"/>
</td>
<td>
<span t-esc="leave_data['taken_days']"/>
</td>
<td>
<span t-esc="leave_data['balance_days']"/>
</td>
</tr>
</t>
</table>
</div>
</div>
</div>
</t>
</t>
</template>
</odoo>

11
hr_leave_dashboard/report/hr_leave_reports.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Report Action-->
<record id="hr_leave_report_action" model="ir.actions.report">
<field name="name">Leave report</field>
<field name="model">hr.leave.report</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">hr_leave_dashboard.hr_leave_report</field>
<field name="report_file">hr_leave_dashboard.hr_leave_report</field>
</record>
</odoo>

BIN
hr_leave_dashboard/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
hr_leave_dashboard/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
hr_leave_dashboard/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
hr_leave_dashboard/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
hr_leave_dashboard/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
hr_leave_dashboard/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
hr_leave_dashboard/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
hr_leave_dashboard/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
hr_leave_dashboard/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
hr_leave_dashboard/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
hr_leave_dashboard/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
hr_leave_dashboard/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
hr_leave_dashboard/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
hr_leave_dashboard/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
hr_leave_dashboard/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
hr_leave_dashboard/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
hr_leave_dashboard/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
hr_leave_dashboard/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
hr_leave_dashboard/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/categories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/check-box.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/compass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/corporate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/customer-support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/cybrosys-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/features.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

BIN
hr_leave_dashboard/static/description/assets/misc/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/pictures.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/right-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
hr_leave_dashboard/static/description/assets/misc/star.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
hr_leave_dashboard/static/description/assets/misc/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
hr_leave_dashboard/static/description/assets/modules/chatter_view.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
hr_leave_dashboard/static/description/assets/modules/dynamic_financial_reports.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
hr_leave_dashboard/static/description/assets/modules/hid_menu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
hr_leave_dashboard/static/description/assets/modules/login_style.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
hr_leave_dashboard/static/description/assets/modules/login_user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
hr_leave_dashboard/static/description/assets/modules/qr_code.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
hr_leave_dashboard/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
hr_leave_dashboard/static/description/assets/screenshots/hr_leave_dashboard_01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

BIN
hr_leave_dashboard/static/description/assets/screenshots/hr_leave_dashboard_02.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
hr_leave_dashboard/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
hr_leave_dashboard/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

524
hr_leave_dashboard/static/description/index.html

@ -0,0 +1,524 @@
<div style="background-color: #714B67; height: 810px; width: 100%; padding: 15px; position: relative;">
<!-- TITLE BAR -->
<div class="d-flex align-items-center justify-content-between"
style="border-bottom: 1px solid #875A7B; padding: 15px; display: flex; justify-content: space-between; align-items: center;">
<img src="assets/misc/cybrosys-logo.png" width="42" height="42"
style="width: 42px; height: 42px;"/>
<div>
<div style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;"
class="mr-2">
<i class="fa fa-check mr-1"></i>Community
</div>
<div style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;"
class="mr-2">
<i class="fa fa-check mr-1"></i>Enterprise
</div>
</div>
</div>
<!-- END OF TITLE BAR -->
<div class="container">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
<!-- APP HERO -->
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;">
Hr Leave Dashboard</h1>
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;">
This Module Helps You View Your and Your Subordinate's Details.
</p>
<img src="./assets/screenshots/hero.gif" class="img-responsive"
width="100%" height="auto"/>
<!-- END OF APP HERO -->
</div>
</div>
</div>
</div>
<!-- NAVIGATION SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px; margin-top: 300px;">
x
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/compass.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Explore This Module</h2>
</div>
<div class="row my-4" style="font-family: 'Montserrat', sans-serif;">
<div class="col-sm-12 col-md-6 my-3">
<a href="#overview">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Overview</span>
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">Learn more about this module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
<div class="col-sm-12 col-md-6 my-3">
<a href="#features">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Features</span>
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View features of this module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
<div class="col-sm-12 col-md-6 my-3">
<a href="#screenshots">
<div class="d-flex justify-content-between align-items-center"
style="background-color: #f5f5f5; padding: 30px; width: 100%;">
<div>
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Screenshots</span>
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View screenshots for this module</span>
</div>
<img src="assets/misc/right-arrow.png" width="36" height="36"/>
</div>
</a>
</div>
</div>
<!-- END OF NAVIGATION SECTION -->
<!-- OVERVIEW SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="overview">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/pie-chart.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Overview </h2>
</div>
<div class="row"
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;">
<div class="col-sm-12 py-4">
This module helps you view your and your subordinate's details and approval status of your all employees. With this module, you can print hr leave report on basis of today, this week, this month and this year in pdf format.
</div>
</div>
<!-- END OF OVERVIEW SECTION -->
<!-- FEATURES SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="features">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/features.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Features </h2>
</div>
<div class="row"
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;">
<div class="col-sm-12 col-md-6">
<div class="d-flex align-items-center"
style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Hr Leave Dashboard.</span>
</div>
</div>
<div class="col-sm-12 col-md-6">
<div class="d-flex align-items-center"
style="margin-top: 30px; margin-bottom: 30px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">HR Leave PDF Report.</span>
</div>
</div>
</div>
<!-- END OF FEATURES SECTION -->
<!-- SCREENSHOTS SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"
id="screenshots">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/pictures.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Screenshots </h2>
</div>
<div class="row">
<div class="col-sm-12">
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">
HR Leave Dashboard</h3>
<img src="assets/screenshots/hr_leave_dashboard_01.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">
HR Leave PDF Report</h3>
<img src="assets/screenshots/hr_leave_dashboard_02.png" class="img-thumbnail">
</div>
</div>
</div>
<!-- END OF SCREENSHOTS SECTION -->
<!-- SUGGESTED PRODUCTS -->
<div class="row">
<div class="col-lg-12 d-flex flex-column justify-content-center"
style="text-align: center; padding: 2.5rem 1rem !important;">
<h2 style="color: #212529 !important;">Suggested Products</h2>
<hr style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
<div id="demo1" class="row carousel slide" data-ride="carousel">
<!-- The slideshow -->
<div class="carousel-inner">
<div class="carousel-item active" style="min-height:0px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/user_audit/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/dynamic_financial_reports.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/login_user_detail/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/login_user.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/hide_menu_user/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/hid_menu.png">
</div>
</a>
</div>
</div>
<div class="carousel-item" style="min-height:0px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/customer_product_qrcode/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/qr_code.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/web_login_styles/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/login_style.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/advanced_chatter_view/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/chatter_view.jpg">
</div>
</a>
</div>
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1" data-slide="prev"
style="left:-25px;width: 35px;color: #000;">
<span class="carousel-control-prev-icon">
<i class="fa fa-chevron-left" style="font-size:24px"></i>
</span>
</a>
<a class="carousel-control-next" href="#demo1" data-slide="next"
style="right:-25px;width: 35px;color: #000;">
<span class="carousel-control-next-icon">
<i class="fa fa-chevron-right" style="font-size:24px"></i>
</span>
</a>
</div>
</div>
</div>
<!-- END OF SUGGESTED PRODUCTS -->
<!-- OUR SERVICES -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/star.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Our Services </h2>
</div>
<div class="container my-5">
<div class="row">
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/cogs.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Customization</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/wrench.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Implementation</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/lifebuoy.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Support</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/user.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Hire Odoo Developer</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #54a0ff !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/puzzle.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Integration</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/update.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Migration</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/consultation.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Consultancy</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/training.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Implementation</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/license.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Licensing Consultancy</h6>
</div>
</div>
</div>
<!-- END OUR SERVICES -->
<!-- OUR INDUSTRIES -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/corporate.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Our Industries </h2>
</div>
<div class="container my-5">
<div class="row">
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/trading-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Trading </h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Easily procure and sell your products</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/pos-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
POS </h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Easy configuration and convivial experience</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/education-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Education </h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
A platform for educational management</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/manufacturing-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Manufacturing </h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Plan, track and schedule your operations</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/ecom-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
E-commerce &amp; Website </h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Mobile friendly, awe-inspiring product pages</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/service-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Service Management </h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Keep track of services and invoice</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/restaurant-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Restaurant </h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Run your bar or restaurant methodically</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;">
<img src="assets/icons/hotel-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Hotel Management </h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
An all-inclusive hotel management application</p>
</div>
</div>
</div>
</div>
<!-- END OF OUR INDUSTRIES -->
<!-- SUPPORT -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/customer-support.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Support </h2>
</div>
<div class="container mt-5">
<div class="row">
<div class="col-sm-12 col-md-6">
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;">
<div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color: #714B67; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;">
<img src="assets/misc/support.png" height="48" width="48"
style="width: 42px; height: 42px;"/>
</div>
<div>
<h4>Need Help?</h4>
<p style="line-height: 100%;">Got questions or need help?
Get in touch.</p>
<a href="mailto:odoo@cybrosys.com">
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;">
odoo@cybrosys.com</p>
</a>
</div>
</div>
</div>
<div class="col-sm-12 col-md-6">
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;">
<div class="mr-4 d-flex justify-content-center align-items-center"
style="background-color: #2AC44D; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;">
<img src="assets/misc/whatsapp.png" height="52" width="52"
style="width: 52px; height: 52px;"/>
</div>
<div>
<h4>WhatsApp</h4>
<p style="line-height: 100%;">Say hi to us on WhatsApp!</p>
<a href="https://api.whatsapp.com/send?phone=918606827707">
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;">
+91 86068 27707</p>
</a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 my-5 d-flex justify-content-center align-items-center">
<img src="assets/misc/logo.png" width="144" height="31"
style="width:144px; height: 31px; margin-top: 40px;"/>
</div>
</div>
</div>
<!-- END OF SUPPORT -->

89
hr_leave_dashboard/static/src/css/hr_leave_dashboard.css

@ -0,0 +1,89 @@
td.fc-day.fc-widget-content.fc-public-holiday{
background-color:red !important;
}
td.fc-day.fc-widget-content.fc-sat{
background-color:red !important;
}
td.fc-day.fc-widget-content.fc-sun{
background-color:red !important;
}
.o_timeoff_duration{
margin-top:12px !important;
font-size: 30px;
font-weight: bold;
}
.employee_details{
font-size:12px !important;
margin-top: 12px;
margin-bottom: 12px;
}
.o_timeoff_card:not(:last-child) {
font-size: 12px !important;
padding: 12px !important;
position:relative;
}
.o_timeoff_card .o_timeoff_duration{
margin-bottom:12px !important;
}
.o_treeEntry:before, .o_treeEntry:after{
background: #7a539e !important;
}
.duration{
width: 190px;
float: left;
margin: 1px 2px 0px 10px;
height: 29px;
padding: 5px;
margin-bottom: 21px;
}
.time_off_dashboard_table tr th{
background-color: #eaeef3 !important;
font-size: 11px;
}
.employee_image{
border-radius: 50%;
width: 50px;
height: 50px;
overflow: hidden;
position: relative;
}
.employee_name{
font-size: 16px !important;
width: 165px;
/* position: absolute; */
right:62px;
top:24px;
}
.employee_box{
display:flex;
justify-content:center;
align-items:center;
}
.divider-box{
float: left;
margin-bottom: 12px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.box-content{
width: 100%;
margin-right: 2px;
padding: 10px;
display: flex;
align-items: center;
justify-content: center;
height: 80px;
margin-top: 0px;
border: 1px solid #cebce1;
background-color: #eaedf3;
color: #573674;
}
.divide-leave{
padding:10px;
border: 1px solid #cebce1;
background-color: #eaedf3;
color: #573674;
margin-top:10px;
}

435
hr_leave_dashboard/static/src/js/time_off_emp_dashboard.js

@ -0,0 +1,435 @@
odoo.define('hr_leave_dashboard.TimeOffDashboard', function(require) {
'use strict';
var core = require('web.core');
const config = require('web.config');
var CalendarRenderer = require("web.CalendarRenderer");
var CalendarPopover = require("web.CalendarPopover")
var CalendarController = require("web.CalendarController");
var CalendarModel = require("web.CalendarModel");
var viewRegistry = require('web.view_registry');
var CalendarView = require("web.CalendarView");
var QWeb = core.qweb;
var _t = core._t;
var session = require('web.session');
// Extending the calendar view to add the details to the dashboard.
var TimeOffCalendarController = CalendarController.extend({
events: _.extend({}, CalendarController.prototype.events, {
'click .btn-time-off': '_onNewTimeOff',
'click .btn-allocation': '_onNewAllocation',
'click .print-pdf-report': 'printPdfReport',
}),
/**
* @override
*/
start: function () {
this.$el.addClass('o_timeoff_calendar');
return this._super(...arguments);
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Render the buttons and add new button about
* time off and allocations request
*
* @override
*/
renderButtons: function ($node) {
this._super.apply(this, arguments);
$(QWeb.render('hr_holidays.dashboard.calendar.button', {
time_off: _t('New Time Off'),
request: _t('Allocation Request'),
})).appendTo(this.$buttons);
if ($node) {
this.$buttons.appendTo($node);
} else {
this.$('.o_calendar_buttons').replaceWith(this.$buttons);
}
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
_getNewTimeOffContext: function() {
const { date_from, date_to } = this.model._getTimeOffDates(moment());
return {
'default_date_from': date_from,
'default_date_to': date_to,
'lang': this.context.lang
}
},
/**
* Action: create a new time off request
*
* @private
*/
_onNewTimeOff: function () {
this._rpc({
model: 'ir.ui.view',
method: 'get_view_id',
args: ['hr_holidays.hr_leave_view_form_dashboard_new_time_off'],
}).then((ids) => {
this.timeOffDialog = new dialogs.FormViewDialog(this, {
res_model: "hr.leave",
view_id: ids,
context: this._getNewTimeOffContext(),
title: _t("New time off"),
disable_multiple_selection: true,
on_saved: () => {
this.reload();
},
});
this.timeOffDialog.open();
});
},
/**
* Action: create a new allocation request
*
* @private
*/
_onNewAllocation: function () {
let self = this;
self._rpc({
model: 'ir.ui.view',
method: 'get_view_id',
args: ['hr_holidays.hr_leave_allocation_view_form_dashboard'],
}).then(function(ids) {
self.allocationDialog = new dialogs.FormViewDialog(self, {
res_model: "hr.leave.allocation",
view_id: ids,
context: {
'default_state': 'confirm',
'lang': self.context.lang,
},
title: _t("New Allocation"),
disable_multiple_selection: true,
on_saved: function() {
self.reload();
},
});
self.allocationDialog.open();
});
},
// Action to print the pdf report of the timeoff.
printPdfReport: function () {
const duration = $(this.el.querySelectorAll("#duration")).val();
var self = this
this._rpc({
model: 'hr.leave',
method: 'get_all_validated_leaves',
args: [
],
}).then(function(data) {
var action = {
type: "ir.actions.report",
report_type: "qweb-pdf",
report_name: "hr_leave_dashboard.hr_leave_report",
report_file: "hr_leave_dashboard.hr_leave_report",
data: {
'duration': duration,
'all_validated_leaves': data,
}
}
return self.do_action(action)
})
},
/**
* @override
*/
_setEventTitle: function () {
return _t('Time Off Request');
},
});
var TimeOffCalendarPopover = CalendarPopover.extend({
template: 'hr_holidays.calendar.popover',
init: function (parent, eventInfo) {
this._super.apply(this, arguments);
const state = this.event.extendedProps.record.state;
this.canDelete = state && ['validate', 'refuse'].indexOf(state) === -1;
this.canEdit = state !== undefined;
this.displayFields = [];
if (this.modelName === "hr.leave.report.calendar") {
const duration = this.event.extendedProps.record.display_name.split(':').slice(-1);
this.display_name = _.str.sprintf(_t("Time Off : %s"), duration);
} else {
this.display_name = this.event.extendedProps.record.display_name;
}
},
});
var TimeOffPopoverRenderer = CalendarRenderer.extend({
template: "TimeOff.CalendarView.extend",
/**
* We're overriding this to display the weeknumbers on the year view
*
* @override
* @private
*/
_getFullCalendarOptions: function () {
const oldOptions = this._super(...arguments);
// Parameters
oldOptions.views.dayGridYear.weekNumbers = true;
oldOptions.views.dayGridYear.weekNumbersWithinDays = false;
return oldOptions;
},
config: _.extend({}, CalendarRenderer.prototype.config, {
CalendarPopover: TimeOffCalendarPopover,
}),
_getPopoverParams: function (eventData) {
let params = this._super.apply(this, arguments);
let calendarIcon;
let state = eventData.extendedProps.record.state;
if (state === 'validate') {
calendarIcon = 'fa-calendar-check-o';
} else if (state === 'refuse') {
calendarIcon = 'fa-calendar-times-o';
} else if(state) {
calendarIcon = 'fa-calendar-o';
}
params['title'] = eventData.extendedProps.record.display_name.split(':').slice(0, -1).join(':');
params['template'] = QWeb.render('hr_holidays.calendar.popover.placeholder', {color: this.getColor(eventData.color_index), calendarIcon: calendarIcon});
return params;
},
_render: function () {
var self = this;
return this._super.apply(this, arguments).then(function () {
self.$el.parent().find('.o_calendar_mini').hide();
});
},
/**
* @override
* @private
*/
_renderCalendar: function() {
this._super.apply(this, arguments);
let weekNumbers = this.$el.find('.fc-week-number');
weekNumbers.each( function() {
let weekRow = this.parentNode;
// By default, each month has 6 weeks displayed, hide the week number if there is no days for the week
if(!weekRow.children[1].classList.length && !weekRow.children[weekRow.children.length-1].classList.length) {
this.innerHTML = '';
}
});
},
});
var TimeOffCalendarRenderer = TimeOffPopoverRenderer.extend({
_render: function () {
var self = this;
return this._super.apply(this, arguments).then(function () {
return self._rpc({
model: 'hr.leave.type',
method: 'get_days_all_request',
context: self.state.context,
});
}).then(function (result) {
self._rpc({
model: 'hr.leave',
method: 'get_current_employee',
}).then(function (res) {
self.$el.parent().find('.o_calendar_mini').hide();
self.$el.parent().find('.o_timeoff_container').remove();
self._rpc({
route: '/hr/get_org_chart',
params: {
employee_id: res.id,
context: session.user_context,
},
})
.then(function (data) {
if (result.length > 0) {
if (config.device.isMobile) {
result.forEach((data) => {
const elem = QWeb.render('hr_holidays.dashboard_calendar_header_mobile', {
timeoff: data,
});
self.$el.find('.o_calendar_filter_item[data-value=' + data[3] + '] .o_cw_filter_title').append(elem);
});
} else {
const elem = QWeb.render('hr_holidays.dashboard_calendar_header', {
timeoffs: result,
employee: res,
employee_org: data,
});
self.$el.before(elem);
}
}
$('[data-toggle="popover"]').each(function () {
$(this).popover({
html: true,
title: function () {
var $title = $(QWeb.render('hr_orgchart_emp_popover_title', {
employee: {
name: $(this).data('emp-name'),
id: $(this).data('emp-id'),
},
}));
$title.on('click',
'.o_employee_redirect', _.bind(self._onEmployeeRedirect, self));
return $title;
},
container: this,
placement: 'left',
trigger: 'focus',
content: function () {
var $content = $(QWeb.render('hr_orgchart_emp_popover_content', {
employee: {
id: $(this).data('emp-id'),
name: $(this).data('emp-name'),
direct_sub_count: parseInt($(this).data('emp-dir-subs')),
indirect_sub_count: parseInt($(this).data('emp-ind-subs')),
},
}));
$content.on('click',
'.o_employee_sub_redirect', _.bind(self._onEmployeeSubRedirect, self));
return $content;
},
template: QWeb.render('hr_orgchart_emp_popover', {}),
});
});
});
})
});
},
// Function to redirect toi the employee form view.
_onEmployeeRedirect: function (event) {
var self = this;
event.preventDefault();
var employee_id = parseInt($(event.currentTarget).data('employee-id'));
return this._rpc({
model: 'hr.employee',
method: 'get_formview_action',
args: [employee_id],
}).then(function(action) {
return self.do_action(action);
});
},
// Function to redirect to the sub employee's form view
_onEmployeeSubRedirect: function (event) {
event.preventDefault();
var employee_id = parseInt($(event.currentTarget).data('employee-id'));
var employee_name = $(event.currentTarget).data('employee-name');
var type = $(event.currentTarget).data('type') || 'direct';
var self = this;
if (employee_id) {
this._getSubordinatesData(employee_id, type).then(function(data) {
var domain = [['id', 'in', data]];
return self._rpc({
model: 'hr.employee',
method: 'get_formview_action',
args: [employee_id],
}).then(function(action) {
action = _.extend(action, {
'name': _t('Team'),
'view_mode': 'kanban,list,form',
'views': [[false, 'kanban'], [false, 'list'], [false, 'form']],
'domain': domain,
'context': {
'default_parent_id': employee_id,
}
});
delete action['res_id'];
return self.do_action(action);
});
});
}
},
// Function to get the details of sub employees.
_getSubordinatesData: function (employee_id, type) {
return this._rpc({
route: '/hr/get_subordinates',
params: {
employee_id: employee_id,
subordinates_type: type,
context: session.user_context,
},
});
},
});
const TimeOffCalendarModel = CalendarModel.extend({
calendarEventToRecord(event) {
const res = this._super(...arguments);
if (['day', 'week'].includes(this.data.scale)) {
const { date_from, date_to } = this._getTimeOffDates(event.start.clone());
res['date_from'] = date_from;
res['date_to'] = date_to;
}
return res;
},
_getTimeOffDates(date_from) {
date_from.set({
'hour': 0,
'minute': 0,
'second': 0
});
let date_to = date_from.clone().set({
'hour': 23,
'minute': 59,
'second': 59
});
date_from.subtract(this.getSession().getTZOffset(date_from), 'minutes');
date_from = date_from.locale('en').format('YYYY-MM-DD HH:mm:ss');
date_to.subtract(this.getSession().getTZOffset(date_to), 'minutes');
date_to = date_to.locale('en').format('YYYY-MM-DD HH:mm:ss');
return {
date_from,
date_to,
}
},
});
var TimeOffCalendarView = CalendarView.extend({
config: _.extend({}, CalendarView.prototype.config, {
Controller: TimeOffCalendarController,
Renderer: TimeOffCalendarRenderer,
Model: TimeOffCalendarModel,
}),
});
/**
* Calendar shown in the "Everyone" menu
*/
var TimeOffCalendarAllView = CalendarView.extend({
config: _.extend({}, CalendarView.prototype.config, {
Controller: TimeOffCalendarController,
Renderer: TimeOffPopoverRenderer,
Model: TimeOffCalendarModel,
}),
});
viewRegistry.add('time_off_calendar', TimeOffCalendarView);
viewRegistry.add('time_off_calendar_all', TimeOffCalendarAllView);
});

213
hr_leave_dashboard/static/src/scss/hr_org_chart.scss

@ -0,0 +1,213 @@
$tmp-gap-base: $o-hr-org-chart-entry-pic-size*0.7;
// ORGANIGRAM LINES
.o_org_chart_group_down {
position: relative;
}
.o_org_chart_group_down {
padding-left: $tmp-gap-base;
height: fit-content;
&:before {
@include o-hr-org-chart-line;
border-left-width: $o-hr-org-chart-entry-line-w;
height: 100%;
@include o-position-absolute(
$top: $o-hr-org-chart-entry-v-gap*-1,
$left: $tmp-gap-base*0.5 + $o-hr-org-chart-entry-pic-size*0.1 + $o-hr-org-chart-entry-line-w*0.5
);
}
.o_org_chart_entry {
&:before {
@include o-hr-org-chart-line;
border-top-width: $o-hr-org-chart-entry-line-w;
@include size($tmp-gap-base, 0);
@include o-position-absolute(
$left: $tmp-gap-base*-0.5 + $o-hr-org-chart-entry-pic-size*0.1 + $o-hr-org-chart-entry-line-w*0.5,
$top: $o-hr-org-chart-entry-pic-size*0.5
);
}
&:last-of-type {
&:before {
height: 50%;
}
}
&.o_org_chart_more {
margin-top: $o-hr-org-chart-entry-v-gap;
&:before {
top: 15px;
}
}
}
}
// ORGANIGRAM DESIGN
.o_org_chart_entry {
margin-bottom: $o-hr-org-chart-entry-v-gap;
overflow: visible;
margin-top: 0;
&, .o_media_left, .media-body {
position: relative;
}
.o_media_left {
padding-right: 10px;
}
.media-body {
vertical-align: middle;
.badge {
float: right;
cursor: pointer;
margin-right: 5px;
color: gray('600');
background: $o-hr-org-chart-bg;
border: 1px solid gray('600');
&:hover {
color: $o-brand-primary;
border-color: $o-brand-primary;
}
&:focus {
outline: none;
}
}
strong {
display: block;
line-height: 1.2;
font-size: 11px;
color: lighten(gray('600'), 15%);
}
}
.o_media_object {
display: block;
width: $o-hr-org-chart-entry-pic-size*0.8;
height: $o-hr-org-chart-entry-pic-size*0.8;
margin: $o-hr-org-chart-entry-pic-size*0.1;
box-shadow: 0 0 0 $o-hr-org-chart-entry-line-w darken($o-hr-org-chart-bg, 20%);
background-size: cover;
background-position: center center;
background-color: $o-view-background-color;
&.card {
height: 20px;
box-shadow: none;
border-color: transparent;
padding: 0;
position: relative;
color: $body-color;
.o_org_chart_show_more {
line-height: 13px;
}
&:hover {
border-color: $o-hr-org-chart-entry-border-color;
color:$o-brand-primary;
}
}
}
&.o_org_chart_entry_manager, &.o_org_chart_entry_sub {
.o_media_left {
padding-right: 0;
}
.media-body > a {
padding-left: 10px;
max-width: 100%;
display: block;
.o_media_heading {
color: lighten(gray('600'), 5%);
font-size: 13px;
}
}
&:hover {
.o_media_object {
box-shadow: 0 0 0 $o-hr-org-chart-entry-line-w*2 rgba($o-brand-primary, 0.6);
}
.media-body > a {
.o_media_heading {
color: $o-brand-primary;
}
strong {
color: lighten(gray('600'), 5%);
}
}
}
}
&.o_org_chart_entry_self {
&:not(:first-child) {
margin-top: $o-hr-org-chart-entry-v-gap*1.5;
}
strong {
color: $text-muted;
}
.o_media_object {
width: $o-hr-org-chart-entry-pic-size;
height: $o-hr-org-chart-entry-pic-size;
margin: 0;
border: $o-hr-org-chart-entry-line-w*2 solid $o-brand-primary;
box-shadow: inset 0 0 0 $o-hr-org-chart-entry-line-w*2 white;
}
.media-body {
opacity: 1;
}
}
}
// POP OVER
.o_org_chart_popup.popover {
max-width: 400px;
margin-right: 5px;
min-width:230px;
.popover-header {
height: 47px;
line-height: 33px;
padding-right: 50px;
> a {
@include o-position-absolute($right: 14px);
}
span {
@include size(30px, 30px);
margin-right: 10px;
border-radius: 100%;
background-position: center;
background-size: cover;
float: left;
box-shadow: 0 1px 1px;
}
}
.table {
margin-bottom: 0;
}
}
// Right to Left specific style to flip the popover arrow
.o_rtl {
.o_org_chart_popup.popover .arrow {
left: 100%;
-webkit-transform: matrix(-1, 0, 0, 1, 0, 0);
-moz-transform: matrix(-1, 0, 0, 1, 0, 0);
-o-transform: matrix(-1, 0, 0, 1, 0, 0);
transform: matrix(-1, 0, 0, 1, 0, 0);
}
}

10
hr_leave_dashboard/static/src/scss/time_off_dashboard.scss

@ -0,0 +1,10 @@
.o_timeoff_dashboard {
display: flex;
justify-content: space-between;
height: auto;
box-shadow: inset 0 -1px 0 $border-color;
position: sticky;
top: 0;
z-index: 100;
background-color: $o-webclient-background-color;
}

14
hr_leave_dashboard/static/src/scss/variables.scss

@ -0,0 +1,14 @@
$o-hr-org-chart-bg: white;
$o-hr-org-chart-border-color: $o-brand-secondary;
$o-hr-org-chart-entry-v-gap: 6px;
$o-hr-org-chart-entry-pic-size: 46px;
$o-hr-org-chart-entry-line-w: 1px;
$o-hr-org-chart-entry-border-color: darken($o-hr-org-chart-bg, 25%);
// MIXINS
@mixin o-hr-org-chart-line {
content: '';
background-color: $o-hr-org-chart-bg;
border: 0px solid $o-hr-org-chart-entry-border-color;
}

169
hr_leave_dashboard/static/src/xml/emp_org_chart_templates.xml

@ -0,0 +1,169 @@
<?xml version="1.0" encoding="UTF-8"?>
<template id="template" xml:space="preserve">
<!-- Templates for employee org chart.-->
<t t-name="hr_leave_dashboard.hr_org_chart_employee_content">
<div t-attf-class="o_org_chart_group_#{employee_type != 'manager' ? 'down' : ''}">
<div t-attf-class="o_org_chart_entry o_org_chart_entry_#{employee_type} media">
<div class="o_media_left">
<!-- &lt;!&ndash; NOTE: Since by the default on not squared images odoo add white borders,-->
<!-- use bg-images to get a clean and centred images &ndash;&gt;-->
<a t-if="! is_self"
class="o_media_object rounded-circle o_employee_redirect"
t-att-style="'background-image:url(\'/web/image/hr.employee.public/' + employee.id + '/avatar_1024/\')'"
t-att-alt="employee.name"
t-att-data-employee-id="employee.id"
t-att-href="employee.link"/>
<div t-if="is_self"
class="o_media_object rounded-circle"
t-att-style="'background-image:url(\'/web/image/hr.employee.public/' + employee.id + '/avatar_1024/\')'"/>
</div>
<div class="media-body">
<span
t-if="employee.indirect_sub_count &gt; 0"
class="badge badge-pill"
tabindex="0"
data-trigger="focus"
t-att-data-emp-name="employee.name"
t-att-data-emp-id="employee.id"
t-att-data-emp-dir-subs="employee.direct_sub_count"
t-att-data-emp-ind-subs="employee.indirect_sub_count"
data-toggle="popover" t-att-style="'float:right;'">
<t t-esc="employee.indirect_sub_count"/>
</span>
<t t-if="!is_self">
<a t-att-href="employee.link" class="o_employee_redirect"
t-att-data-employee-id="employee.id">
<h5 class="o_media_heading"><b><t t-esc="employee.name"/></b></h5>
<strong><t t-esc="employee.job_title"/></strong>
</a>
</t>
<t t-if="is_self">
<h5 class="o_media_heading"><b><t t-esc="employee.name"/></b></h5>
<strong><t t-esc="employee.job_title"/></strong>
</t>
</div>
</div>
</div>
</t>
<t t-name="hr_leave_dashboard.hr_org_chart">
<!-- NOTE: Desidered behaviour:
The maximun number of people is always 7 (including 'self'). Managers have priority over suburdinates
Eg. 1 Manager + 1 self = show just 5 subordinates (if availables)
Eg. 0 Manager + 1 self = show 6 subordinates (if available)
-->
<t t-set="emp_count" t-value="0"/>
<div t-if='employee_org.managers.length &gt; 0'>
<t t-if='employee_org.managers_more'>
<div class="o_org_chart_entry o_org_chart_more media">
<div class="o_media_left">
<a class="text-center o_employee_more_managers"
t-att-data-employee-id="employee_org.managers[0].id">
<i t-attf-class="fa fa-angle-double-up" role="img"
aria-label="More managers" title="More managers"/>
</a>
</div>
</div>
</t>
<t t-foreach="employee_org.managers" t-as="employee">
<t t-set="emp_count" t-value="emp_count + 1"/>
<t t-call="hr_leave_dashboard.hr_org_chart_employee_content">
<t t-set="employee_type" t-value="'manager'"/>
</t>
</t>
</div>
<t t-if="employee_org.children.length || employee_org.managers.length"
t-call="hr_leave_dashboard.hr_org_chart_employee_content">
<t t-set="employee_type" t-value="'self'"/>
<t t-set="employee" t-value="employee_org.self"/>
</t>
<t t-if="!employee_org.children.length &amp;&amp; !employee_org.managers.length">
<div class="alert alert-info" role="alert">
<p><b>No hierarchy position.</b></p>
<p>This employee has no manager or subordinate.</p>
<p>In order to get an organigram, set a manager and save the record.</p>
</div>
</t>
<div t-if="employee_org.children.length">
<t t-foreach="employee_org.children" t-as="employee">
<t t-set="emp_count" t-value="emp_count + 1"/>
<t t-if="emp_count &lt; 20">
<t t-call="hr_leave_dashboard.hr_org_chart_employee_content">
<t t-set="employee_type" t-value="'sub'"/>
</t>
</t>
</t>
<t t-if="(employee_org.children.length + employee_org.managers.length) &gt; 19">
<div class="o_org_chart_entry o_org_chart_more media">
<div class="o_media_left">
<a href="#"
t-att-data-employee-id="employee_org.self.id"
t-att-data-employee-name="employee_org.self.name"
class="o_org_chart_show_more text-center o_employee_sub_redirect">See All</a>
</div>
</div>
</t>
</div>
</t>
<!-- Templates for popover in the employee org chart-->
<t t-name="hr_orgchart_emp_popover">
<div class="popover o_org_chart_popup" role="tooltip"><div class="arrow"></div>
<h3 class="popover-header"></h3>
<div class="popover-body"></div></div>
</t>
<t t-name="hr_orgchart_emp_popover_content">
<table class="table table-sm">
<thead>
<td class="text-right"><t t-esc="employee.direct_sub_count"/></td>
<td>
<a href="#" class="o_employee_sub_redirect" data-type='direct'
t-att-data-employee-name="employee.name"
t-att-data-employee-id="employee.id">
<b>Direct subordinates</b></a>
</td>
</thead>
<tbody>
<tr>
<td class="text-right">
<t t-esc="employee.indirect_sub_count - employee.direct_sub_count"/>
</td>
<td>
<a href="#" class="o_employee_sub_redirect"
data-type='indirect'
t-att-data-employee-name="employee.name"
t-att-data-employee-id="employee.id">
Indirect subordinates</a>
</td>
</tr>
<tr>
<td class="text-right"><t t-esc="employee.indirect_sub_count"/></td>
<td>
<a href="#" class="o_employee_sub_redirect"
data-type='total'
t-att-data-employee-name="employee.name"
t-att-data-employee-id="employee.id">
Total</a>
</td>
</tr>
</tbody>
</table>
</t>
<t t-name="hr_orgchart_emp_popover_title">
<div>
<span t-att-style='"background-image:url(\"/web/image/hr.employee.public/" + employee.id + "/avatar_1024/\")"'/>
<a href="#" class="float-right o_employee_redirect"
t-att-data-employee-id="employee.id"><i class="fa fa-external-link" role="img" aria-label='Redirect' title="Redirect"></i></a>
<b><t t-esc="employee.name"/></b>
</div>
</t>
</template>

245
hr_leave_dashboard/static/src/xml/time_off_calendar.xml

@ -0,0 +1,245 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<!-- Templates for the details in the timeoff dashboard.-->
<div t-name="hr_leave_dashboard.TimeOffEmpCard"
t-inherit="hr_holidays.dashboard_calendar_header"
t-inherit-mode='extension'
owl="1" class="o_timeoff_dashboard">
<xpath expr="//div[hasclass('o_timeoff_container')]"
position="replace">
<div class="o_timeoff_container d-flex">
<div t-foreach="timeoffs" t-as="timeoff"
t-attf-class="o_timeoff_card flex-grow-1 d-flex flex-column py-3">
<t t-set="requires_allocation"
t-value="timeoff[2] === 'yes'"/>
<t t-set="has_icon"
t-value="timeoff[1]['icon'] !== false"/>
<t t-set="cl" t-value="'text-muted'"/>
<b><span t-esc="timeoff[0]" class="o_timeoff_name o_timeoff_big"/></b>
<div class="mt-1">
<t t-if="requires_allocation">
<t t-if="has_icon"><img height="30px" t-attf-src="{{timeoff[1]['icon']}}"/></t>
<span t-esc="timeoff[1]['virtual_remaining_leaves']"
class="o_timeoff_huge o_timeoff_purple font-weight-bold align-middle"/>
<br/>
<t class="o_timeoff_big o_timeoff_purple"
t-if="timeoff[1]['request_unit'] == 'hour'"><span class="o_timeoff_purple">HOURS</span></t>
<t class="o_timeoff_big o_timeoff_purple"
t-else=""><span class="o_timeoff_purple">DAYS</span></t>
<span class="o_timeoff_purple">AVAILABLE </span>
</t>
<t t-else="">
<t t-if="has_icon"><img height="30px" t-attf-src="{{timeoff[1]['icon']}}"/></t>
<span t-esc="timeoff[1]['virtual_leaves_taken']"
class="o_timeoff_huge o_timeoff_purple font-weight-bold align-middle"/>
<br/>
<t t-if="timeoff[1]['request_unit'] == 'hour'"><span class="o_timeoff_purple">HOURS</span></t>
<t t-else=""><span class="o_timeoff_purple">DAYS</span></t>
<span class="o_timeoff_purple">TAKEN</span>
</t>
</div>
</div>
<div t-attf-class="o_timeoff_card flex-grow-1 d-flex flex-column py-3 text-odoo"
style="width:22%;">
<t t-set="id" t-value="employee.id"/>
<t t-set="name" t-value="employee.name"/>
<t t-set="work_email" t-value="employee.work_email"/>
<t t-set="work_phone" t-value="employee.work_phone"/>
<t t-set="department_id"
t-value="employee.department_id"/>
<t t-set="resource_calendar_id"
t-value="employee.resource_calendar_id"/>
<t t-set="company" t-value="employee.company"/>
<t t-set="job_position" t-value="employee.job_position"/>
<t t-set="child_ids" t-value="employee.child_ids"/>
<t t-set="child_count" t-value="employee.child_count"/>
<t t-set="image" t-value="employee.image_1920"/>
<div class="w-100 mt-2 mb-2 employee_box">
<div class="employee_image">
<t t-if="image">
<img style="width: 50px; height: 50px;"
t-attf-src="data:image/png;base64,{{image}}"/>
</t>
</div>
<span class="o_timeoff_duration employee_name">
<t t-esc="name"/>
</span>
</div>
<div class="employee_details" style="width:100%;">
<div class="col-lg-6 divider-box"
style="float:left; margin-bottom:12px;">
<div class="text-uppercase box-content">
<t t-if="job_position"
name="duration_unit">Job Position: <t t-esc="job_position"/>
</t>
</div>
</div>
<div class="col-lg-6 divider-box"
style="float:left; margin-bottom:12px;">
<div class="box-content">
<t t-if="resource_calendar_id"
name="duration_unit">Working Hours: <t
t-esc="resource_calendar_id"/>
</t>
</div>
</div>
<div class="col-lg-12 divider-box"
style="float:left; margin-bottom:12px;">
<div class="box-content">
<t t-if="work_email"
name="duration_unit">EMAIL: <t t-esc="work_email"/></t>
</div>
</div>
<div class="col-lg-6 divider-box"
style="float:left; margin-bottom:12px;">
<div class="box-content">
<t t-if="work_phone"
name="duration_unit">PHONE: <t t-esc="work_phone"/></t>
</div>
</div>
<div class="col-lg-6 divider-box"
style="float:left; margin-bottom:12px;">
<div class="text-uppercase box-content">
<t t-if="department_id"
name="duration_unit">Department: <t t-esc="department_id"/></t>
</div>
</div>
<div class="col-lg-12 divider-box"
style="float:left; margin-bottom:12px;">
<div class="text-uppercase box-content">
<t t-if="company" name="duration_unit">Company: <t t-esc="company"/></t>
</div>
</div>
</div>
</div>
<t t-set="is_self"
t-value="employee.id == view_employee_id"/>
<t t-set="child_count" t-value="employee.child_all_count"/>
<t t-set="manager_count"
t-value="employee.manager_all_count"/>
<section t-if="employee_type == 'self'"
t-attf-class="o_org_chart_entry_self_container #{manager_count &gt; 0 ? 'o_org_chart_has_managers' : ''}">
<div t-attf-class="o_org_chart_entry o_org_chart_entry_#{employee_type} d-flex position-relative py-2 overflow-visible #{manager_count &gt; 0 ? 'o_treeEntry' : ''}">
<t t-call="hr_leave_dashboard.hr_org_chart">
<t t-set="is_self" t-value="is_self"/>
</t>
</div>
</section>
<div t-else=""
t-attf-class="o_org_chart_entry o_org_chart_entry_#{employee_type} o_treeEntry d-flex position-relative py-2 overflow-visible">
<t t-call="hr_leave_dashboard.hr_org_chart">
<t t-set="is_self" t-value="is_self"/>
</t>
</div>
<div class="o_timeoff_card py-3 text-odoo"
style="width:18%;">
<t t-set="id" t-value="employee.id"/>
<t t-set="department_id"
t-value="employee.department_id"/>
<t t-set="emp_count" t-value="0"/>
<t t-set="child_count"
t-value="employee.child_all_count"/>
<t t-set="children" t-value="employee.children"/>
<t t-set="absentees" t-value="employee.absentees"/>
<t t-set="shift" t-value="employee.current_shift"/>
<t t-set="upcoming_holidays"
t-value="employee.upcoming_holidays"/>
<span class="o_timeoff_duration">
<t t-esc="department_id"/>
</span>
<div t-if="shift">
<div class="text-uppercase col-lg-12 divide-leave ">
<t t-if="shift" name="duration_unit">Current Shift: <t t-esc="shift"/></t>
</div>
</div>
<div t-if="upcoming_holidays.length>0"
class="col-lg-12 divide-leave">
<div class="text-success">Upcoming Holidays</div>
<t t-foreach="upcoming_holidays" t-as="holiday"
t-key="holiday.id">
<div>
<t t-esc="holiday.name"/>
</div>
<div>
(<t t-esc="holiday.date_from"/>)
</div>
</t>
</div>
<div t-if="absentees.length>0"
class="col-lg-12 divide-leave">
<div class="text-danger">On Leave</div>
<t t-foreach="absentees" t-as="child"
t-key="child.employee_id">
<div>
<t t-esc="child.name"/>
</div>
</t>
</div>
</div>
<div class="o_timeoff_card approval_status_card py-3 text-odoo"
style="height: 475px;overflow-y: scroll;">
<t t-set="approval_status_count"
t-value="employee.approval_status_count"/>
<t t-set="children" t-value="employee.children"/>
<t t-if="!employee.is_manager">
<div class="hr_holiday_user">
<span class="o_timeoff_duration">
<t t-esc="approval_status_count.validate_count"/>
</span>
<div class="text-uppercase">
<span>Approved</span>
</div>
<span class="o_timeoff_duration">
<t t-esc="approval_status_count.confirm_count"/>
</span>
<div class="text-uppercase">
<span>To Approve</span>
</div>
<span class="o_timeoff_duration">
<t t-esc="approval_status_count.refuse_count"/>
</span>
<div class="text-uppercase">
<span>Refused</span>
</div>
</div>
</t>
<t t-else="">
<div class="hr_holiday_manager">
<div class="pdf_report">
<select id="duration" class="duration">
<option value="today">Today</option>
<option value="this_week">This week</option>
<option value="this_month">This month</option>
<option value="this_year">This Year</option>
</select>
<button type="button"
class="btn btn-primary print-pdf-report">Print PDF</button>
</div>
<table class="table table-hover time_off_dashboard_table">
<thead>
<tr>
<th>Name</th>
<th>Approved</th>
<th>To Approve</th>
<th>Refused</th>
</tr>
</thead>
<tbody>
<t t-foreach="children" t-as="child"
t-key="child.id">
<tr>
<td><t t-esc="child.name"/></td>
<td><t t-esc="child.approval_status_count.validate_count"/></td>
<td><t t-esc="child.approval_status_count.confirm_count"/></td>
<td><t t-esc="child.approval_status_count.refuse_count"/></td>
</tr>
</t>
</tbody>
</table>
</div>
</t>
</div>
</div>
</xpath>
</div>
</templates>
Loading…
Cancel
Save