Browse Source

July 11: [ADD] Initial commit 'hr_payroll_dashboard'

pull/320/head
RisvanaCybro 10 months ago
parent
commit
5c66997b8c
  1. 42
      hr_payroll_dashboard/README.rst
  2. 22
      hr_payroll_dashboard/__init__.py
  3. 54
      hr_payroll_dashboard/__manifest__.py
  4. 7
      hr_payroll_dashboard/doc/RELEASE_NOTES.md
  5. 27
      hr_payroll_dashboard/models/__init__.py
  6. 42
      hr_payroll_dashboard/models/hr_attendance.py
  7. 52
      hr_payroll_dashboard/models/hr_contract.py
  8. 260
      hr_payroll_dashboard/models/hr_employee.py
  9. 96
      hr_payroll_dashboard/models/hr_expense.py
  10. 50
      hr_payroll_dashboard/models/hr_leave.py
  11. 53
      hr_payroll_dashboard/models/hr_payslip.py
  12. BIN
      hr_payroll_dashboard/static/description/assets/icons/check.png
  13. BIN
      hr_payroll_dashboard/static/description/assets/icons/chevron.png
  14. BIN
      hr_payroll_dashboard/static/description/assets/icons/cogs.png
  15. BIN
      hr_payroll_dashboard/static/description/assets/icons/consultation.png
  16. BIN
      hr_payroll_dashboard/static/description/assets/icons/ecom-black.png
  17. BIN
      hr_payroll_dashboard/static/description/assets/icons/education-black.png
  18. BIN
      hr_payroll_dashboard/static/description/assets/icons/hotel-black.png
  19. BIN
      hr_payroll_dashboard/static/description/assets/icons/license.png
  20. BIN
      hr_payroll_dashboard/static/description/assets/icons/lifebuoy.png
  21. BIN
      hr_payroll_dashboard/static/description/assets/icons/manufacturing-black.png
  22. BIN
      hr_payroll_dashboard/static/description/assets/icons/pos-black.png
  23. BIN
      hr_payroll_dashboard/static/description/assets/icons/puzzle.png
  24. BIN
      hr_payroll_dashboard/static/description/assets/icons/restaurant-black.png
  25. BIN
      hr_payroll_dashboard/static/description/assets/icons/service-black.png
  26. BIN
      hr_payroll_dashboard/static/description/assets/icons/trading-black.png
  27. BIN
      hr_payroll_dashboard/static/description/assets/icons/training.png
  28. BIN
      hr_payroll_dashboard/static/description/assets/icons/update.png
  29. BIN
      hr_payroll_dashboard/static/description/assets/icons/user.png
  30. BIN
      hr_payroll_dashboard/static/description/assets/icons/wrench.png
  31. BIN
      hr_payroll_dashboard/static/description/assets/misc/categories.png
  32. BIN
      hr_payroll_dashboard/static/description/assets/misc/check-box.png
  33. BIN
      hr_payroll_dashboard/static/description/assets/misc/compass.png
  34. BIN
      hr_payroll_dashboard/static/description/assets/misc/corporate.png
  35. BIN
      hr_payroll_dashboard/static/description/assets/misc/customer-support.png
  36. BIN
      hr_payroll_dashboard/static/description/assets/misc/cybrosys-logo.png
  37. BIN
      hr_payroll_dashboard/static/description/assets/misc/features.png
  38. BIN
      hr_payroll_dashboard/static/description/assets/misc/logo.png
  39. BIN
      hr_payroll_dashboard/static/description/assets/misc/pictures.png
  40. BIN
      hr_payroll_dashboard/static/description/assets/misc/pie-chart.png
  41. BIN
      hr_payroll_dashboard/static/description/assets/misc/right-arrow.png
  42. BIN
      hr_payroll_dashboard/static/description/assets/misc/star.png
  43. BIN
      hr_payroll_dashboard/static/description/assets/misc/support.png
  44. BIN
      hr_payroll_dashboard/static/description/assets/misc/whatsapp.png
  45. BIN
      hr_payroll_dashboard/static/description/assets/modules/11.png
  46. BIN
      hr_payroll_dashboard/static/description/assets/modules/12.png
  47. BIN
      hr_payroll_dashboard/static/description/assets/modules/13.png
  48. BIN
      hr_payroll_dashboard/static/description/assets/modules/14.png
  49. BIN
      hr_payroll_dashboard/static/description/assets/modules/15.png
  50. BIN
      hr_payroll_dashboard/static/description/assets/modules/16.png
  51. BIN
      hr_payroll_dashboard/static/description/assets/screenshots.zip
  52. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/1.png
  53. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/2.png
  54. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/3.png
  55. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/4.png
  56. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/5.png
  57. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/6.png
  58. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/v16-hero.gif
  59. BIN
      hr_payroll_dashboard/static/description/banner.png
  60. BIN
      hr_payroll_dashboard/static/description/icon.png
  61. 699
      hr_payroll_dashboard/static/description/index.html
  62. 1023
      hr_payroll_dashboard/static/src/css/dashboard.css
  63. 426
      hr_payroll_dashboard/static/src/css/lib/nv.d3.css
  64. 431
      hr_payroll_dashboard/static/src/css/style.scss
  65. 748
      hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js
  66. 7828
      hr_payroll_dashboard/static/src/js/lib/d3.min.js
  67. 268
      hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml
  68. 12
      hr_payroll_dashboard/views/dashboard_views.xml

42
hr_payroll_dashboard/README.rst

@ -0,0 +1,42 @@
.. 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 Payroll Dashboard
====================
* Detailed Dashboard View for Payroll Module
License
-------
General Public License, Version 3 (AGPL v3).
(https://www.gnu.org/licenses/agpl-3.0-standalone.html)
Company
-------
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__
Credits
-------
* Developer: (V16)Anfas Faisal K, 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 https://www.cybrosys.com
Further information
===================
HTML Description: `<static/description/index.html>`__

22
hr_payroll_dashboard/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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

54
hr_payroll_dashboard/__manifest__.py

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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 Payroll Dashboard",
'version': '16.0.1.0.0',
'category': 'Human Resource',
'summary': """Detailed Dashboard View for Payroll Module""",
'description': "This module helps you to see the Overview of Payroll, "
"here You can see the details of Attendance, Leaves, "
"Payslip contracts, etc.",
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'website': "https://www.cybrosys.com",
'depends': ['hr_payroll_community', 'hr_attendance', 'hr_expense'],
'data': [
'views/dashboard_views.xml',
],
'assets': {
'web.assets_backend': [
'hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js',
'hr_payroll_dashboard/static/src/css/lib/nv.d3.css',
'hr_payroll_dashboard/static/src/css/dashboard.css',
'hr_payroll_dashboard/static/src/css/style.scss',
'hr_payroll_dashboard/static/src/js/lib/d3.min.js',
'hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml'
],
},
'images': ['static/description/banner.png'],
"external_dependencies": {"python": ["pandas"]},
'license': "AGPL-3",
'installable': True,
'auto_install': False,
'application': True,
}

7
hr_payroll_dashboard/doc/RELEASE_NOTES.md

@ -0,0 +1,7 @@
## Module <hr_payroll_dashboard>
#### 03.06.2024
#### Version 16.0.1.0.0
##### ADD
- Initial commit for HR Payroll Dashboard

27
hr_payroll_dashboard/models/__init__.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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_attendance
from . import hr_contract
from . import hr_employee
from . import hr_expense
from . import hr_leave
from . import hr_payslip

42
hr_payroll_dashboard/models/hr_attendance.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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 odoo import api, fields, models
class HrAttendance(models.Model):
"""
This class extends the HR Attendance model to include additional fields
and functionalities.
"""
_inherit = 'hr.attendance'
attendance_date = fields.Date(compute="_compute_attendance_date",
store=True,
help="The date of attendance based on the "
"check-in time.")
@api.depends('check_in')
def _compute_attendance_date(self):
"""Compute function for the attendance date"""
for rec in self:
if rec.check_in:
rec.attendance_date = rec.check_in.date()

52
hr_payroll_dashboard/models/hr_contract.py

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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 odoo import api, fields, models
class Contract(models.Model):
"""
This class extends the HR Contract model to include additional fields
and functionalities.
"""
_inherit = 'hr.contract'
state_label = fields.Char(compute="_compute_state_label", store=True,
help="A representation of the contract state.")
@api.depends('state')
def _compute_state_label(self):
"""Compute to get the label value of the contract state"""
for record in self:
record.state_label = dict(self._fields['state'].selection).get(
record.state)
@api.model
def get_employee_contract(self):
"""Return employees contract details"""
cr = self._cr
cr.execute("""SELECT hr_contract.state_label,count(*)
FROM hr_contract
JOIN hr_employee ON hr_employee.id=hr_contract.employee_id
GROUP BY hr_contract.state_label""")
dat = cr.fetchall()
data = [{'label': d[0], 'value': d[1]} for d in dat]
return data

260
hr_payroll_dashboard/models/hr_employee.py

@ -0,0 +1,260 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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 as pd
from datetime import timedelta, datetime, date
from collections import defaultdict
from dateutil.relativedelta import relativedelta
from pytz import utc
from odoo.tools import float_utils
from odoo import api, fields, models
from odoo.http import request
ROUNDING_FACTOR = 16
class Employee(models.Model):
"""
This class extends the HR Employee model to include additional fields
and functionalities
"""
_inherit = 'hr.employee'
is_manager = fields.Boolean(compute='_compute_is_manager',
help="Flag indicating whether the employee is a"
"manager.")
def _compute_is_manager(self):
"""Compute function for checking whether it is a manager or not"""
for rec in self:
if (rec.env.user.has_group
('hr_payroll_community.group_hr_payroll_community_manager')):
rec.is_manager = True
else:
rec.is_manager = False
@api.model
def get_user_employee_info(self):
"""To get the employee information"""
uid = request.session.uid
employee_user_id = self.env['hr.employee'].sudo().search([
('user_id', '=', uid)
], limit=1)
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
attendance_count = self.env['hr.attendance'].sudo().search(
[('employee_id', '=', employee_user_id.id),
('attendance_date', '=', date.today())])
manager_attendance_count = self.env['hr.attendance'].sudo().search(
[('attendance_date', '=', date.today())])
leave_request_count = self.env['hr.leave'].sudo().search(
[('employee_id', '=', employee_user_id.id),
('request_date_from', '=', date.today())])
manager_leave_request = self.env['hr.leave'].sudo().search(
[('request_date_from', '=', date.today())])
employee_contracts = self.env['hr.contract'].sudo().search([
('employee_id', '=', employee_user_id.id)])
payslips = self.env['hr.payslip'].sudo().search([
('employee_id', '=', employee_user_id.id)])
salary_rules = self.env['hr.salary.rule'].sudo().search([])
salary_structures = self.env['hr.payroll.structure'].sudo().search([])
salary_rule_count = len(salary_rules)
salary_structure_count = len(salary_structures)
emp_leave = len(manager_leave_request) if employee_user_id.is_manager \
else len(leave_request_count)
payslip_count = len(payslips) if not employee_user_id.is_manager \
else len(self.env['hr.payslip'].sudo().search([]))
emp_contracts_count = len(employee_contracts) \
if not employee_user_id.is_manager else len(
self.env['hr.contract'].sudo().search([]))
attendance_today = len(manager_attendance_count) \
if employee_user_id.is_manager else len(attendance_count)
if employee:
data = {
'emp_timesheets': attendance_today,
'emp_leave': emp_leave,
'emp_contracts_count': emp_contracts_count,
'payslip_count': payslip_count,
'leave_requests': leave_request_count,
'salary_rule_count': salary_rule_count,
'salary_structure_count': salary_structure_count,
'attendance_state': employee[0]['attendance_state'],
}
employee[0].update(data)
return employee
def get_work_days_dashboard(self, from_datetime, to_datetime,
compute_leaves=False, calendar=None,
domain=None):
"""
Calculate the total work days between two datetimes.
"""
resource = self.resource_id
calendar = calendar or self.resource_calendar_id
if not from_datetime.tzinfo:
from_datetime = from_datetime.replace(tzinfo=utc)
if not to_datetime.tzinfo:
to_datetime = to_datetime.replace(tzinfo=utc)
from_full = from_datetime - timedelta(days=1)
to_full = to_datetime + timedelta(days=1)
intervals = calendar._attendance_intervals_batch(from_full, to_full,
resource)
day_total = defaultdict(float)
for start, stop, meta in intervals[resource.id]:
day_total[start.date()] += (stop - start).total_seconds() / 3600
if compute_leaves:
intervals = calendar._work_intervals_batch(from_datetime,
to_datetime, resource,
domain)
else:
intervals = calendar._attendance_intervals_batch(from_datetime,
to_datetime,
resource)
day_hours = defaultdict(float)
for start, stop, meta in intervals[resource.id]:
day_hours[start.date()] += (stop - start).total_seconds() / 3600
days = sum(
float_utils.round(ROUNDING_FACTOR * day_hours[day] / day_total[
day]) / ROUNDING_FACTOR
for day in day_hours
)
return days
@api.model
def get_department_leave(self):
""" Return department leave details."""
month_list = [format(datetime.now() - relativedelta(months=i), '%B %Y')
for i in range(5, -1, -1)]
self.env.cr.execute(
"""select id, name from hr_department where active=True """)
departments = self.env.cr.dictfetchall()
department_list = [x['name'] for x in departments]
graph_result = [{
'l_month': month,
'leave': {dept['name']: 0 for dept in departments}
} for month in month_list]
sql = """
SELECT h.id, h.employee_id,h.department_id
, extract('month' FROM y)::int AS leave_month
, to_char(y, 'Month YYYY') as month_year
, GREATEST(y , h.date_from) AS date_from
, LEAST (y + interval '1 month', h.date_to) AS date_to
FROM (select * from hr_leave where state = 'validate') h
, generate_series(date_trunc('month', date_from::timestamp)
, date_trunc('month', date_to::timestamp)
, interval '1 month') y
where date_trunc('month', GREATEST(y , h.date_from)) >= date_trunc('month', now()) - interval '6 month' and
date_trunc('month', GREATEST(y , h.date_from)) <= date_trunc('month', now())
and h.department_id is not null
"""
self.env.cr.execute(sql)
results = self.env.cr.dictfetchall()
leave_lines = [{
'department': line['department_id'],
'l_month': line['month_year'],
'days': self.browse(line['employee_id']).get_work_days_dashboard(
fields.Datetime.from_string(line['date_from']),
fields.Datetime.from_string(line['date_to'])
)
} for line in results]
if leave_lines:
df = pd.DataFrame(leave_lines)
rf = df.groupby(['l_month', 'department']).sum()
result_lines = rf.to_dict('index')
for month in month_list:
for line in result_lines:
if month.replace(' ', '') == line[0].replace(' ', ''):
match = list(filter(lambda d: d['l_month'] in [month],
graph_result))[0]['leave']
dept_name = self.env['hr.department'].browse(
line[1]).name
if match:
match[dept_name] = result_lines[line]['days']
for result in graph_result:
result['l_month'] = result['l_month'].split(' ')[:1][0].strip()[
:3] + " " + \
result['l_month'].split(' ')[1:2][0]
return graph_result, department_list
@api.model
def employee_leave_trend(self):
"""Return employee monthly leave details"""
month_list = [format(datetime.now() - relativedelta(months=i), '%B %Y')
for i in range(5, -1, -1)]
uid = request.session.uid
employee = False
employee_user = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
employees = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee_user:
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
elif employees:
employee = self.env['hr.employee'].sudo().search_read([], limit=1)
graph_result = [{
'l_month': month,
'leave': 0
} for month in month_list]
sql = """
SELECT h.id, h.employee_id
, extract('month' FROM y)::int AS leave_month
, to_char(y, 'Month YYYY') as month_year
, GREATEST(y , h.date_from) AS date_from
, LEAST (y + interval '1 month', h.date_to) AS date_to
FROM (select * from hr_leave where state = 'validate') h
, generate_series(date_trunc('month', date_from::timestamp)
, date_trunc('month', date_to::timestamp)
, interval '1 month') y
where date_trunc('month', GREATEST(y , h.date_from)) >=
date_trunc('month', now()) - interval '6 month' and
date_trunc('month', GREATEST(y , h.date_from)) <=
date_trunc('month', now())
and h.employee_id = %s
"""
if employee:
self.env.cr.execute(sql, (employee[0]['id'],))
results = self.env.cr.dictfetchall()
leave_lines = [{
'l_month': line['month_year'],
'days': self.browse(
line['employee_id']).get_work_days_dashboard(
fields.Datetime.from_string(line['date_from']),
fields.Datetime.from_string(line['date_to'])
)
} for line in results]
if leave_lines:
df = pd.DataFrame(leave_lines)
rf = df.groupby(['l_month']).sum()
result_lines = rf.to_dict('index')
for line in result_lines:
match = list(filter(lambda d: d['l_month'].replace(
' ', '') == line.replace(' ', ''), graph_result))
if match:
match[0]['leave'] = result_lines[line]['days']
for result in graph_result:
result['l_month'] = result['l_month'].split(' ')[:1][0].strip()[
:3] \
+ " " + result['l_month'].split(' ')[1:2][0]
return graph_result
else:
return False

96
hr_payroll_dashboard/models/hr_expense.py

@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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 datetime import datetime
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.http import request
class HrExpense(models.Model):
"""
This class extends the HR Expense model to include additional fields
and functionalities.
"""
_inherit = 'hr.expense'
state_label = fields.Char(compute="_compute_state_label", store=True)
@api.depends('state')
def _compute_state_label(self):
"""Compute function for the expense state label"""
for record in self:
record.state_label = dict(self._fields['state'].selection).get(
record.state)
@api.model
def get_employee_expense(self):
"""Return employee expense details"""
cr = self._cr
month_list = [format(datetime.now() - relativedelta(months=i), '%B %Y')
for i in range(11, -1, -1)]
approved_trend = [{'l_month': month, 'count': 0}
for month in month_list]
uid = request.session.uid
employee = False
employee_user = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
employees = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee_user:
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
elif employees:
employee = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee:
employee_id = self.env['hr.employee'].browse(employee[0]['id'])
if not employee_id.is_manager:
sql = ('''select to_char(date, 'Month YYYY') as l_month,
count(id) from hr_expense
WHERE date BETWEEN CURRENT_DATE - INTERVAL '12 months'
AND CURRENT_DATE + interval '1 month - 1 day'
AND hr_expense.employee_id = %s
group by l_month''')
self.env.cr.execute(sql, (employee[0]['id'],))
else:
sql = ('''select to_char(date, 'Month YYYY') as l_month,
count(id) from hr_expense WHERE date
BETWEEN CURRENT_DATE - INTERVAL
'12 months' AND CURRENT_DATE + interval '1 month - 1 day'
group by l_month''')
self.env.cr.execute(sql)
approved_data = cr.fetchall()
for line in approved_data:
match = list(filter(lambda d: d['l_month'].replace(
' ', '') == line[0].replace(' ', ''), approved_trend))
if match:
match[0]['count'] = line[1]
for expense in approved_trend:
expense['l_month'] = expense[
'l_month'].split(' ')[:1][0].strip()[
:3]
graph_result = [{
'values': approved_trend
}]
return graph_result
else:
return False

50
hr_payroll_dashboard/models/hr_leave.py

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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 odoo import api, fields, models
class HrLeave(models.Model):
"""
This class extends the HR Leave model to include additional fields
and functionalities specific to the requirements of the application.
"""
_inherit = 'hr.leave'
state_string = fields.Char(compute="_compute_state_string", store=True,
help="A representation of the leave state.")
@api.depends('state')
def _compute_state_string(self):
"""Compute the label of the leave state"""
for rec in self:
rec.state_string = dict(self._fields[
'state'].selection).get(rec.state)
@api.model
def get_employee_time_off(self):
"""Return employee time off details"""
self._cr.execute("""SELECT hr_leave.state_string, count(*)
FROM hr_employee JOIN hr_leave ON hr_leave.employee_id=hr_employee.id
GROUP BY hr_leave.state_string""")
dat = self._cr.fetchall()
data = [{'label': d[0], 'value': d[1]} for d in dat]
return data

53
hr_payroll_dashboard/models/hr_payslip.py

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# 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 odoo import api ,fields, models
class HrPayslip(models.Model):
"""
This class extends the Hr Payslip model to include additional fields
and functionalities.
"""
_inherit = 'hr.payslip'
_description = 'Employee Payroll'
payslip_state = fields.Char(compute="_compute_payslip_state", store=True,
help="A representation of the payslip state.")
@api.depends('state')
def _compute_payslip_state(self):
"""Compute the label value of the payslip state"""
for rec in self:
rec.payslip_state = dict(self._fields[
'state'].selection).get(rec.state)
@api.model
def get_employee_payslips(self):
"""Return employee payslip details"""
self._cr.execute(
"""SELECT hr_payslip.payslip_state,count(*) FROM hr_employee
JOIN hr_payslip ON hr_payslip.employee_id=hr_employee.id
GROUP BY hr_payslip.payslip_state
""")
dat = self._cr.fetchall()
data = [{'label': d[0], 'value': d[1]} for d in dat if d[0] is not None]
return data

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
hr_payroll_dashboard/static/description/assets/modules/11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
hr_payroll_dashboard/static/description/assets/modules/12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
hr_payroll_dashboard/static/description/assets/modules/13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
hr_payroll_dashboard/static/description/assets/modules/14.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
hr_payroll_dashboard/static/description/assets/modules/15.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
hr_payroll_dashboard/static/description/assets/modules/16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots.zip

Binary file not shown.

BIN
hr_payroll_dashboard/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/v16-hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
hr_payroll_dashboard/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
hr_payroll_dashboard/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

699
hr_payroll_dashboard/static/description/index.html

@ -0,0 +1,699 @@
<div style="background-color: #714B67; min-height: 600px; width: 100%; padding: 15px; position: relative;">
<!-- TITLE BAR -->
<div
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>
</div>
<!-- END OF TITLE BAR -->
<!-- APP HERO -->
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;">
Hr Payroll Dashboard
</h1>
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;">
Detailed Dashboard View for Payroll Module.
</p>
<!-- END OF APP HERO -->
<img src="assets/screenshots/v16-hero.gif"
style="width: 75%; height: auto; position: absolute; margin-left: auto; margin-right: auto; top: 45%; left: 12%; right: auto;"/>
</div>
<!-- NAVIGATION SECTION -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px; margin-top: 300px;">
<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 of 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 to see the Overview of Payroll, here You can see the details of Attendance, Leaves, Payslip contracts, etc.
</div>
<div class="alert alert-primary mt-4">
<hr/>
You must need module - Odoo 16 Payroll in your system
<br/>
<div class="col-lg-12 d-flex justify-content-center align-items-center"
style="margin: 2rem 0;">
<p class=" mt8" style="font-family:Roboto ; color: #280135;">Odoo 16 Payroll - link to download : <a href="https://apps.odoo.com/apps/modules/16.0/hr_payroll_community/"> https://apps.odoo.com/apps/modules/16.0/hr_payroll_community/</a> </p>
</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: 40px; margin-bottom: 40px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Payslip Details</span>
</div>
<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;">Contract Details </span>
</div>
<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;">Leave Request Details
</span>
</div>
<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;">Attendance Details
</span>
</div>
<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;">Salary Rules Details
</span>
</div>
<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;">Salary Structures Details
</span>
</div>
</div>
</div>
<!-- END OF FEATURES SECTION -->
<!-- SCREENSHOTS SECTION -->
<div class="row" id="screenshots">
<div class="col-md-12"
style="border-bottom: 1px solid #d5d5d5 !important; margin: 2rem 0 !important">
<h2
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;">
<i class="fa fa-image mr-2"></i>Screenshots
</h2>
</div>
<div class="col-lg-12 my-2">
<h4 class="mt-2"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Open the Payroll Module to view the Payroll Dashboard</h4>
<img src="assets/screenshots/1.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-3">
<h4 class="mt-2"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Dashboard Tiles</h4>
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
The Tiles display a list of the Attendance orders,Leaves,
Payslips,Contracts,Salary Rules, and Salary Structures. </p>
<img src="assets/screenshots/2.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-3">
<h4 class="mt-3"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Monthly Expense Analysis</h4>
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Monthly expense can be analysed from the chart.
</p>
<img src="assets/screenshots/3.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-3">
<h4 class="mt-3"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
My Leave Analysis</h4>
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
User Leave Analysis
</p>
<img src="assets/screenshots/4.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-3">
<h4 class="mt-3"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Monthly Leave Analysis</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Monthly leave analysis can be view here.
</p>
<img src="assets/screenshots/5.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-3">
<h4 class="mt-3"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Details in Charts</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Payslip, Contract and Time off analysis can be done from the chart.
</p>
<img src="assets/screenshots/6.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<!-- END OF SCREENSHOTS SECTION -->
<!-- RELATED PRODUCTS -->
<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/categories.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Related
Products
</h2>
</div>
<div class="row">
<div class="col-sm-12">
<div id="demo1" class="row carousel slide" data-ride="carousel">
<!-- The slideshow -->
<div class="carousel-inner" style="padding: 30px;">
<div class="carousel-item" style="min-height: 198.656px;">
<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/odoo_dynamic_dashboard/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/11.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/16.0/crm_dashboard/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/12.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/16.0/dashboard_pos/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/13.png">
</div>
</a>
</div>
</div>
<div class="carousel-item active"
style="min-height: 198.656px;">
<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/inventory_stock_dashboard_odoo/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/14.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/16.0/project_dashboard_odoo/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/15.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/16.0/portal_dashboard/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/16.png">
</div>
</a>
</div>
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1"
data-slide="prev"
style="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="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 RELATED 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 OF 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"
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"
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 -->
</div>
</div>

1023
hr_payroll_dashboard/static/src/css/dashboard.css

File diff suppressed because it is too large

426
hr_payroll_dashboard/static/src/css/lib/nv.d3.css

@ -0,0 +1,426 @@
.nvd3 .nv-axis {
pointer-events:none;
opacity: 1;
}
.nvd3 .nv-axis path {
fill: none;
stroke: #000;
stroke-opacity: .75;
shape-rendering: crispEdges;
}
.nvd3 .nv-axis path.domain {
stroke-opacity: .75;
}
.nvd3 .nv-axis.nv-x path.domain {
stroke-opacity: 0;
}
.nvd3 .nv-axis line {
fill: none;
stroke: #e5e5e5;
shape-rendering: crispEdges;
}
.nvd3 .nv-axis .zero line,
/*this selector may not be necessary*/ .nvd3 .nv-axis line.zero {
stroke-opacity: .75;
}
.nvd3 .nv-axis .nv-axisMaxMin text {
font-weight: bold;
}
.nvd3 .x .nv-axis .nv-axisMaxMin text,
.nvd3 .x2 .nv-axis .nv-axisMaxMin text,
.nvd3 .x3 .nv-axis .nv-axisMaxMin text {
text-anchor: middle
}
.nvd3 .nv-axis.nv-disabled {
opacity: 0;
}
/* scatter */
.nvd3 .nv-groups .nv-point.hover {
stroke-width: 20px;
stroke-opacity: .5;
}
.nvd3 .nv-scatter .nv-point.hover {
fill-opacity: 1;
}
.nv-noninteractive {
pointer-events: none;
}
.nv-distx, .nv-disty {
pointer-events: none;
}
.nvtooltip {
position: absolute;
background-color: rgba(255,255,255,1.0);
color: rgba(0,0,0,1.0);
padding: 1px;
border: 1px solid rgba(0,0,0,.2);
z-index: 10000;
display: block;
font-family: Arial;
font-size: 13px;
text-align: left;
pointer-events: none;
white-space: nowrap;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.nvtooltip {
background: rgba(255,255,255, 0.8);
border: 1px solid rgba(0,0,0,0.5);
border-radius: 4px;
}
/*Give tooltips that old fade in transition by
putting a "with-transitions" class on the container div.
*/
.nvtooltip.with-transitions, .with-transitions .nvtooltip {
transition: opacity 50ms linear;
-moz-transition: opacity 50ms linear;
-webkit-transition: opacity 50ms linear;
transition-delay: 200ms;
-moz-transition-delay: 200ms;
-webkit-transition-delay: 200ms;
}
.nvtooltip.x-nvtooltip,
.nvtooltip.y-nvtooltip {
padding: 8px;
}
.nvtooltip h3 {
margin: 0;
padding: 4px 14px;
line-height: 18px;
font-weight: normal;
background-color: rgba(247,247,247,0.75);
color: rgba(0,0,0,1.0);
text-align: center;
border-bottom: 1px solid #ebebeb;
-webkit-border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
border-radius: 5px 5px 0 0;
}
.nvtooltip p {
margin: 0;
padding: 5px 14px;
text-align: center;
}
.nvtooltip span {
display: inline-block;
margin: 2px 0;
}
.nvtooltip table {
margin: 6px;
border-spacing:0;
}
.nvtooltip table td {
padding: 2px 9px 2px 0;
vertical-align: middle;
}
.nvtooltip table td.key {
font-weight: normal;
}
.nvtooltip table td.key.total {
font-weight: bold;
}
.nvtooltip table td.value {
text-align: right;
font-weight: bold;
}
.nvtooltip table tr.highlight td {
padding: 1px 9px 1px 0;
border-bottom-style: solid;
border-bottom-width: 1px;
border-top-style: solid;
border-top-width: 1px;
}
.nvtooltip table td.legend-color-guide div {
width: 8px;
height: 8px;
vertical-align: middle;
}
.nvtooltip table td.legend-color-guide div {
width: 12px;
height: 12px;
border: 1px solid #999;
}
.nvtooltip .footer {
padding: 3px;
text-align: center;
}
.nvtooltip-pending-removal {
pointer-events: none;
display: none;
}
/****
Interactive Layer
*/
.nvd3 .nv-interactiveGuideLine {
pointer-events:none;
}
.nvd3 line.nv-guideline {
stroke: #ccc;
}
.nvd3 .nv-bars rect {
fill-opacity: .75;
transition: fill-opacity 250ms linear;
-moz-transition: fill-opacity 250ms linear;
-webkit-transition: fill-opacity 250ms linear;
}
.nvd3 .nv-bars rect.hover {
fill-opacity: 1;
}
.nvd3 .nv-bars .hover rect {
fill: lightblue;
}
.nvd3 .nv-bars text {
fill: rgba(0,0,0,0);
}
.nvd3 .nv-bars .hover text {
fill: rgba(0,0,0,1);
}
.nvd3 .nv-multibar .nv-groups rect,
.nvd3 .nv-multibarHorizontal .nv-groups rect,
.nvd3 .nv-discretebar .nv-groups rect {
stroke-opacity: 0;
transition: fill-opacity 250ms linear;
-moz-transition: fill-opacity 250ms linear;
-webkit-transition: fill-opacity 250ms linear;
}
.nvd3 .nv-multibar .nv-groups rect:hover,
.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,
.nvd3 .nv-candlestickBar .nv-ticks rect:hover,
.nvd3 .nv-discretebar .nv-groups rect:hover {
fill-opacity: 1;
}
.nvd3 .nv-discretebar .nv-groups text,
.nvd3 .nv-multibarHorizontal .nv-groups text {
font-weight: bold;
fill: rgba(0,0,0,1);
stroke: rgba(0,0,0,0);
}
.nvd3 .nv-groups path.nv-line {
fill: none;
}
.nvd3 .nv-groups path.nv-area {
stroke: none;
}
.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point {
fill-opacity: 0;
stroke-opacity: 0;
}
.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point {
fill-opacity: .5 !important;
stroke-opacity: .5 !important;
}
.with-transitions .nvd3 .nv-groups .nv-point {
transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
-moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
-webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
}
.nvd3.nv-scatter .nv-groups .nv-point.hover,
.nvd3 .nv-groups .nv-point.hover {
stroke-width: 7px;
fill-opacity: .95 !important;
stroke-opacity: .95 !important;
}
.nvd3 .nv-point-paths path {
stroke: #aaa;
stroke-opacity: 0;
fill: #eee;
fill-opacity: 0;
}
.nvd3 .nv-indexLine {
cursor: ew-resize;
}
/********************
* SVG CSS
*/
/********************
Default CSS for an svg element nvd3 used
*/
svg.nvd3-svg {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-ms-user-select: none;
-moz-user-select: none;
user-select: none;
display: block;
width:100%;
height:100%;
}
/********************
Box shadow and border radius styling
*/
.nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip {
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.nvd3 text {
font: normal 12px Arial;
}
.nvd3 .title {
font: bold 14px Arial;
}
.nvd3 .nv-background {
fill: white;
fill-opacity: 0;
}
.nvd3.nv-noData {
font-size: 18px;
font-weight: bold;
}
/**********
* Brush
*/
.nv-brush .extent {
fill-opacity: .125;
shape-rendering: crispEdges;
}
.nv-brush .resize path {
fill: #eee;
stroke: #666;
}
/**********
* Legend
*/
.nvd3 .nv-legend .nv-series {
cursor: pointer;
}
.nvd3 .nv-legend .nv-disabled circle {
fill-opacity: 0;
}
/* focus */
.nvd3 .nv-brush .extent {
fill-opacity: 0 !important;
}
.nvd3 .nv-brushBackground rect {
stroke: #000;
stroke-width: .4;
fill: #fff;
fill-opacity: .7;
}
.nvd3.nv-pie path {
stroke-opacity: 0;
transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
-moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
-webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
}
.nvd3.nv-pie .nv-pie-title {
font-size: 24px;
fill: rgba(19, 196, 249, 0.59);
}
.nvd3.nv-pie .nv-slice text {
stroke: #000;
stroke-width: 0;
}
.nvd3.nv-pie path {
stroke: #fff;
stroke-width: 1px;
stroke-opacity: 1;
}
.nvd3.nv-pie path {
fill-opacity: .7;
}
.nvd3.nv-pie .hover path {
fill-opacity: 1;
}
.nvd3.nv-pie .nv-label {
pointer-events: none;
}
.nvd3.nv-pie .nv-label rect {
fill-opacity: 0;
stroke-opacity: 0;
}

431
hr_payroll_dashboard/static/src/css/style.scss

@ -0,0 +1,431 @@
#chartdiv {
width : 100%;
height : 500px;
font-size : 11px;
}
.card-title {
float: left;
font-size: 1.1rem;
font-weight: 400;
margin: 0;
text-transform: uppercase;
}
.col-md-3, .col-sm-12, .col-md-8, .col-md-4, .col-6, .col-4,.col-2 {
position: relative;
padding-right: 7.5px !important;
padding-left: 7.5px !important;
}
.card-header {
background-color:
transparent;
border-bottom: 1px solid
rgba(0,0,0,.125);
padding: .75rem 1.25rem;
position: relative;
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
}
.container-fluid.o_in_dashboard {
padding: 0px !important;
}
.o_action_manager{
overflow-y: scroll !important;
max-width:100%;
}
// new tile
.o_dashboards {
color: #2a2a2a;
background-color: #f2f2f2 !important;
}
.dash-header {
margin: 15px 0px 12px 0 !important;
display: block;
padding: 7px 25px 7px 0;
color: #0e1319;
font-size: 2rem;
font-weight: 400;
background-color:
rgba(255, 255, 255, 0.9) !important;
color:#212529;
padding: 1.5rem;
border-radius: 3px;
box-shadow: 0 0px 10px 0px
rgba(0, 0, 0, 0.05) !important;
display: flex;
justify-content: space-between;
align-items: center;
}
h1.dashboard-h1 {
display: block;
padding: 7px 25px 7px 0;
color: #0e1319;
font-size: 2rem;
font-weight: 400;
color:
#212529;
float: left;
margin-bottom: 0;
}
.card {
position: relative !important;
border-top: 0 !important;
margin-bottom: 30px !important;
width: 100% !important;
background-color: #ffffff !important;
border-radius: 0.25rem !important;
padding: 0px !important;
-webkit-transition: .5s !important;
transition: .5s !important;
display: -ms-flexbox !important;
display: flex !important;
-ms-flex-direction: column !important;
flex-direction: column !important;
box-shadow: 0 0px 10px 0px rgba(0, 0, 0, 0.05) !important;
border-radius: 0.25rem;
}
.card-header {
border: 0;
padding: 0;
}
.card-header > .card-tools {
float: right;
margin-right: 0.375rem;
margin-top: 5px;
margin-bottom: 10px;
}
.card-header i.fa {
font-size: 1.3rem;
display: inline-block;
padding: 0 0px;
margin: 0 0px;
color: #57769c;
opacity: .8;
-webkit-transition: 0.3s linear;
transition: 0.3s linear;
}
.main-title {
display: block;
margin-bottom: 5px;
font-size: 13px;
font-weight: 600;
color: #fff !important;
text-transform: uppercase;
padding: 1rem;
border-radius: 5px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.card-body {
background-color: rgba(255, 255, 255, 0.9) !important;
color: #212529;
padding-top: 0;
}
.tile.wide.invoice {
margin-bottom: 27px;
-webkit-box-shadow: 1px 5px 24px 0 rgba(68, 102, 242, 0.05);
box-shadow: 1px 5px 24px 0 rgba(68, 102, 242, 0);
background-color: #ffffff;
border-radius: 5px;
position: relative;
width: 100%;
padding: 0rem 0rem;
border: 1px solid rgba(0, 0, 0, 0.07);
}
.box-1 .main-title {
background: #67b7dc;
color: #fff;
}
.box-2 .main-title {
background: #6794dc !important;
color: #fff;
}
.box-3 .main-title {
background:#8067dc;
color: #fff;
}
.box-4 .main-title {
background: #c767dc;
color: #fff;
}
.count {
margin-bottom: 1rem;
}
.count > span > sapn {
font-size: 20px !important;
}
span#total_invoices_ span, span#total_invoices_last span, span#total_incomes_ span, span#total_incomes_last span, span#total_expenses_ span, span#total_expense_last span, span#unreconciled_items_ span, span#unreconciled_items_last span,span#unreconciled_counts_last_year span,span#unreconciled_counts_this_year span,span#total_expense_last_year span,span#total_expense_this_year span, span#total_incomes_last_year span,span#total_incomes_this_year span,span#total_invoices_last_year span,span#total_invoices_this_year span,span#net_profit_current_months span,span#net_profit_current_year span {
padding-right: 8px;
font-size: 16px;
font-weight: 600;
}
span#total_invoices_, span#total_invoices_last, span#total_incomes_, span#total_incomes_last, span#total_expenses_, span#total_expense_last, span#unreconciled_items_, span#unreconciled_items_last,span#unreconciled_counts_last_year,span#unreconciled_counts_this_year,span#total_expense_last_year,span#total_expense_this_year, span#total_incomes_last_year,span#total_incomes_this_year,span#total_invoices_last_year,span#total_invoices_this_year,span#net_profit_current_months,span#net_profit_current_year {
display: -webkit-box;
display: -webkit-flex;
display: flex;
flex-direction: column;
color: #455e7b !important;
}
.main-title~div {
display: flex;
justify-content: space-between;
margin-top: 1rem;
padding: 1rem;
background:#fff;
}
.card-header {
color: #0e1319 !important;
display: block !important;
padding: 1.5rem 1.5rem !important;
position: relative !important;
border-bottom: 1px solid rgba(0, 0, 0, 0.07) !important;
border-top-left-radius: 0.25rem !important;
border-top-right-radius: 0.25rem !important;
}
.card-header i.fa {
font-size: 1rem;
display: inline-block;
padding: 0 0px;
margin: 0 0px;
color: #57769c;
opacity: .8;
-webkit-transition: 0.3s linear;
transition: 0.3s linear;
}
.card-header > .card-tools {
float: right;
margin-right: 0;
margin-top: 0px !important;
margin-bottom: 0;
}
h3, .h3 {
margin: 0;
}
.card-tools .btn {
padding: 0 10px;
margin: 0;
line-height: normal !important;
}
#col-graph .card {
height: 366px;
}
#top_10_customers_this_month{
padding:0;
}
#top_10_customers_this_month li {
list-style: none;
padding-top: 6px;
padding-bottom: 6px;
font-size: 13px;
color:#455e7b !important;
border-bottom: 1px solid
rgba(0, 0, 0, 0.07) !important;
padding-left: 2rem;
display: flex;
justify-content: space-between;
padding-right: 2rem;
}
#top_10_customers_this_month li a{
color:
#455e7b !important;
}
#top_10_customers{
padding:0;
}
#top_10_customers li {
list-style: none;
padding-top: 6px;
padding-bottom: 6px;
font-size: 13px;
color:#455e7b !important;
border-bottom: 1px solid
rgba(0, 0, 0, 0.07) !important;
padding-left: 2rem;
display: flex;
justify-content: space-between;
padding-right: 2rem;
}
#top_10_customers li a{
color:
#455e7b !important;
}
progress, progress[role] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border: none;
background-size: auto;
height: 20px;
width: 100%;
background-color: #8067dc;
}
// The unordered list
.skill-list {
list-style: none;
margin: 0;
padding: 1em;
}
// The list item
.skill {
margin-bottom: 1em;
position: relative;
h3 {
color: #000;
left: 1em;
line-height: 1;
position: absolute;
top: 1em;
}
::-webkit-progress-value {
-webkit-animation: bar-fill 2s;
width: 0px;
}
}
// Style the bar colors
.skill-1::-webkit-progress-value {
background: #c767dc;
}
.skill-1::-moz-progress-bar {
background: #c767dc;
}
// Animation Keyframes
@-webkit-keyframes bar-fill {
0% { width: 0; }
}
@keyframes bar-fill {
0% { width: 0; }
}
#total_supplier_invoice{
color: #696969;
}
#total_customer_invoice_names{
color: #696969;
}
#total_customer_invoice{
color: #696969;
}
#total_invoice_difference, #total_supplier_difference{
color: #4ecdc4;
}
progress {
border: 0;
border-radius: 20px;
}
progress::-webkit-progress-bar {
border: 0;
border-radius: 20px;
}
progress::-webkit-progress-value {
border: 0;
border-radius: 20px;
}
progress::-moz-progress-bar {
border: 0;
border-radius: 20px;
}
#total_customer_invoice_paid .logo, #total_customer_invoice .logo, #total_supplier_invoice_paid .logo, #total_supplier_invoice .logo,
#total_customer_invoice_paid_current_year .logo, #total_customer_invoice_current_year .logo, #total_supplier_invoice_paid_current_year .logo, #total_supplier_invoice_current_year .logo,
#total_customer_invoice_paid_current_month .logo, #total_customer_invoice_current_month .logo, #total_supplier_invoice_paid_current_month .logo, #total_supplier_invoice_current_month .logo {
display: -webkit-box;
display: -webkit-flex;
display: flex;
justify-content: left;
flex-direction: column-reverse;
color:#455e7b !important;
}
#total_customer_invoice_paid .logo span:nth-child(2), #total_customer_invoice .logo span:nth-child(2), #total_supplier_invoice_paid .logo span:nth-child(2), #total_supplier_invoice .logo span:nth-child(2),
#total_customer_invoice_paid_current_year .logo span:nth-child(2), #total_customer_invoice_current_year .logo span:nth-child(2), #total_supplier_invoice_paid_current_year .logo span:nth-child(2), #total_supplier_invoice_current_year .logo span:nth-child(2),
#total_customer_invoice_paid_current_month .logo span:nth-child(2), #total_customer_invoice_current_month .logo span:nth-child(2), #total_supplier_invoice_paid_current_month .logo span:nth-child(2), #total_supplier_invoice_current_month .logo span:nth-child(2) {
font-weight: 600;
}
.toggle-on.btn {
padding-right: 7px;
right: 45%;
}
.toggle.btn.btn-default.off {
border: 1px solid #aaa;
background: #fff;
font-weight: 600 !important;
}
.toggle-off.btn {
padding-left: 9px;
}
#canvas {
height: 277px !important;
width: 100% !important;
}
#collection {
height: 277px !important;
width: 100% !important;
}
#hiring {
height: 260px !important;
width: 100% !important;
}
#payroll_status {
height: 260px !important;
width: 100% !important;
}
#evm {
height: 277px !important;
width: 100% !important;
}
.custom-h1 {
font-size: 1em;
margin-bottom: 0rem;
}
.custom-h3 {
font-size: 1em;
margin: 0;
}

748
hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js

@ -0,0 +1,748 @@
odoo.define('hr_payroll_dashboard.PayrollDashboard', function (require) {
"use strict";
var AbstractAction = require('web.AbstractAction');
var core = require('web.core');
var rpc = require('web.rpc');
var _t = core._t;
var QWeb = core.qweb;
var PayrollDashboard = AbstractAction.extend({
template: 'PayrollDashboardMain',
events: {
'click .o_payslips': '_onclickPayslips',
'click .o_contracts': '_onclickContracts',
'click .o_attendances': '_onclickAttendances',
'click .o_leave': '_onclickLeave',
'click .o_salary_structures': '_onClickSalaryStructure',
'click .o_salary_rules': '_onClickSalaryRules',
},
init: function(parent, context) {
this._super(parent, context);
this.login_employee = [];
this.dashboards_templates = ['PayrollManagerDashboard','EmployeeDetails','ManagerLeaveDashboard', 'PayrollChart'];
},
start: function() {
var self = this;
this.set("title", 'Dashboard');
self.update_leave_trend();
return this._super().then(function() {
self.render_dashboards();
self.render_graphs();
});
},
willStart: function() {
var self = this;
this.login_employee = {};
return this._super()
.then(function() {
var emp_details = self._rpc({
model: 'hr.employee',
method: 'get_user_employee_info'
}).then(function(result) {
self.login_employee = result[0];
});
return $.when(emp_details);
});
},
render_dashboards: function() {
var self = this;
if (this.login_employee){
var templates = []
if( self.login_employee.is_manager == true){templates = ['EmployeeDetails','ManagerLeaveDashboard','PayrollManagerDashboard'];
}
else{
templates = ['EmployeeDetails'];}
_.each(templates, function(template) {
self.$('.o_payroll_dashboard').append(QWeb.render(template, {widget: self}));
});
}
else{
self.$('.o_payroll_dashboard').append(QWeb.render('EmployeeWarning', {widget: self}));
}
},
_onclickPayslips: function(){
/*
* Open the Employee Payslips window.
*/
var self = this;
var domain = []
if( self.login_employee.is_manager == false){
domain = [['employee_id','=', this.login_employee.id]]
}
return this.do_action({
name: _t("Employee Payslips"),
type: 'ir.actions.act_window',
res_model: 'hr.payslip',
view_mode: 'tree,form,calendar',
views: [[false, 'list'],[false, 'form']],
domain: domain,
target: 'current'
});
},
_onclickContracts: function(){
/*
* Open the Employee Contracts window.
*/
var self = this;
var domain = []
if( self.login_employee.is_manager == false){
domain = [['employee_id','=', this.login_employee.id]]
}
return self.do_action({
name: _t("Employee Contracts"),
type: 'ir.actions.act_window',
res_model: 'hr.contract',
view_mode: 'tree,form',
views: [[false, 'list'],[false, 'form']],
context: {"create": false},
domain: domain,
});
},
_onclickAttendances: function(){
/*
* Open the Attendance window.
*/
var self = this;
var today = new Date();
var dd = String(today.getDate()).padStart(2, '0');
var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
var yyyy = today.getFullYear();
today = yyyy + '-' + mm + '-' + dd;
var domain = [['attendance_date', '=', today]]
if( self.login_employee.is_manager == false){
domain = [['employee_id','=', this.login_employee.id],['attendance_date', '=', today]]
}
return self.do_action({
name: _t("Employee Attendances"),
type: 'ir.actions.act_window',
res_model: 'hr.attendance',
view_mode: 'tree,form',
views: [[false, 'list'],[false, 'form']],
context: {"create": false},
domain: domain,
});
},
_onclickLeave: function(){
/*
* Open the Employee Leave window.
*/
var today = new Date();
var dd = String(today.getDate()).padStart(2, '0');
var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
var yyyy = today.getFullYear();
today = yyyy + '-' + mm + '-' + dd;
var self = this;
var domain = [['request_date_from', '=', today]]
if( self.login_employee.is_manager == false){
domain = [['employee_id','=', this.login_employee.id],['request_date_from', '=', today]]
}
return self.do_action({
name: _t("Employee Leave"),
type: 'ir.actions.act_window',
res_model: 'hr.leave',
view_mode: 'tree,form',
views: [[false, 'list'],[false, 'form']],
context: {"create": false},
domain: domain,
});
},
_onClickSalaryRules: function(){
/*
* Open the Salary Rules window.
*/
var self = this;
return self.do_action({
name: _t("Salary Rules"),
type: 'ir.actions.act_window',
res_model: 'hr.salary.rule',
view_mode: 'tree,form',
views: [[false, 'list'],[false, 'form']],
context: {"create": false},
});
},
_onClickSalaryStructure: function(){
/*
* Open the Salary Structures window.
*/
var self = this;
return self.do_action({
name: _t("Salary Structures"),
type: 'ir.actions.act_window',
res_model: 'hr.payroll.structure',
view_mode: 'tree,form',
views: [[false, 'list'],[false, 'form']],
context: {"create": false},
});
},
render_expense_graph:function(){
/**
* Render the expense graph.
* This function retrieves employee expense data and renders it as a graph using D3.js.
* The graph displays the monthly expenses of employees.
*/
var elem = this.$('.expense_graph');
var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139',
'#ffa433', '#ffc25b', '#f8e54b'];
var color = d3.scale.ordinal().range(colors);
rpc.query({
model: "hr.expense",
method: "get_employee_expense",
}).then(function (data) {
if(data){
data.forEach(function(d) {
d.values.forEach(function(d) {
d.l_month = d.l_month;
d.count = +d.count;
});
});
var margin = {top: 30, right: 10, bottom: 30, left: 30},
width = 400 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom
// Set the ranges
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], 1);
var y = d3.scale.linear()
.range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom");
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
x.domain(data[0].values.map(function(d) { return d.l_month; }));
y.domain([0, d3.max(data[0].values, d => d.count)])
var svg = d3.select(elem[0]).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var line = d3.svg.line()
.x(function(d) {return x(d.l_month); })
.y(function(d) {return y(d.count); });
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('class', 'line-group')
.append('path')
.attr('class', 'line')
.attr('d', function(d) { return line(d.values); })
.style('stroke', (d, i) => color(i));
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.selectAll("circle")
.data(function(d) { return d.values;}).enter()
.append("g")
.attr("class", "circle")
.append("circle")
.attr("cx", function(d) { return x(d.l_month)})
.attr("cy", function(d) { return y(d.count)})
.attr("r", 3);
}
});
},
render_leave_graph:function(){
/**
* Render the leave graph.
* This function retrieves department leave data and renders it as a graph using D3.js.
* The graph displays the total leave taken by each department over a period of time.
*/
var self = this;
var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139',
'#ffa433', '#ffc25b', '#f8e54b'];
var color = d3.scale.ordinal().range(colors);
rpc.query({
model: "hr.employee",
method: "get_department_leave",
}).then(function (data) {
if (data){
var fData = data[0];
var dept = data[1];
var id = self.$('.leave_graph')[0];
var barColor = '#ff618a';
fData.forEach(function(d){
var total = 0;
for (var dpt in dept){
total += d.leave[dept[dpt]];
}
d.total=total;
});
// function to handle histogram.
function histoGram(fD){
var hG={}, hGDim = {t: 60, r: 0, b: 30, l: 0};
hGDim.w = 350 - hGDim.l - hGDim.r,
hGDim.h = 200 - hGDim.t - hGDim.b;
//create svg for histogram.
var hGsvg = d3.select(id).append("svg")
.attr("width", hGDim.w + hGDim.l + hGDim.r)
.attr("height", hGDim.h + hGDim.t + hGDim.b).append("g")
.attr("transform", "translate(" + hGDim.l + "," + hGDim.t + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, hGDim.w], 0.1)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
hGsvg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + hGDim.h + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
// Create function for y-axis map.
var y = d3.scale.linear().range([hGDim.h, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
// Create bars for histogram to contain rectangles and freq labels.
var bars = hGsvg.selectAll(".bar").data(fD).enter()
.append("g").attr("class", "bar");
//create the rectangles.
bars.append("rect")
.attr("x", function(d) { return x(d[0]); })
.attr("y", function(d) { return y(d[1]); })
.attr("width", x.rangeBand())
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr('fill',barColor)
.on("mouseover",mouseover)// mouseover is defined below.
.on("mouseout",mouseout);// mouseout is defined below.
//Create the frequency labels above the rectangles.
bars.append("text").text(function(d){ return d3.format(",")(d[1])})
.attr("x", function(d) { return x(d[0])+x.rangeBand()/2; })
.attr("y", function(d) { return y(d[1])-5; })
.attr("text-anchor", "middle");
function mouseover(d){ // utility function to be called on mouseover.
// filter for selected state.
var st = fData.filter(function(s){ return s.l_month == d[0];})[0],
nD = d3.keys(st.leave).map(function(s){ return {type:s, leave:st.leave[s]};});
// call update functions of pie-chart and legend.
pC.update(nD);
leg.update(nD);
}
function mouseout(d){ // utility function to be called on mouseout.
// reset the pie-chart and legend.
pC.update(tF);
leg.update(tF);
}
// create function to update the bars. This will be used by pie-chart.
hG.update = function(nD, color){
// update the domain of the y-axis map to reflect change in frequencies.
y.domain([0, d3.max(nD, function(d) { return d[1]; })]);
// Attach the new data to the bars.
var bars = hGsvg.selectAll(".bar").data(nD);
// transition the height and color of rectangles.
bars.select("rect").transition().duration(500)
.attr("y", function(d) {return y(d[1]); })
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr("fill", color);
// transition the frequency labels location and change value.
bars.select("text").transition().duration(500)
.text(function(d){ return d3.format(",")(d[1])})
.attr("y", function(d) {return y(d[1])-5; });
}
return hG;
}
// function to handle pieChart.
function pieChart(pD){
var pC ={}, pieDim ={w:250, h: 250};
pieDim.r = Math.min(pieDim.w, pieDim.h) / 2;
// create svg for pie chart.
var piesvg = d3.select(id).append("svg")
.attr("width", pieDim.w).attr("height", pieDim.h).append("g")
.attr("transform", "translate("+pieDim.w/2+","+pieDim.h/2+")");
// create function to draw the arcs of the pie slices.
var arc = d3.svg.arc().outerRadius(pieDim.r - 10).innerRadius(0);
// create a function to compute the pie slice angles.
var pie = d3.layout.pie().sort(null).value(function(d) { return d.leave; });
// Draw the pie slices.
piesvg.selectAll("path").data(pie(pD)).enter().append("path").attr("d", arc)
.each(function(d) { this._current = d; })
.attr("fill", function(d, i){return color(i);})
.on("mouseover",mouseover).on("mouseout",mouseout);
// create function to update pie-chart. This will be used by histogram.
pC.update = function(nD){
piesvg.selectAll("path").data(pie(nD)).transition().duration(500)
.attrTween("d", arcTween);
}
// Utility function to be called on mouseover a pie slice.
function mouseover(d, i){
// call the update function of histogram with new data.
hG.update(fData.map(function(v){
return [v.l_month,v.leave[d.data.type]];}),color(i));
}
//Utility function to be called on mouseout a pie slice.
function mouseout(d){
// call the update function of histogram with all data.
hG.update(fData.map(function(v){
return [v.l_month,v.total];}), barColor);
}
// Animating the pie-slice requiring a custom function which specifies
// how the intermediate paths should be drawn.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) { return arc(i(t)); };
}
return pC;
}
// function to handle legend.
function legend(lD){
var leg = {};
// create table for legend.
var legend = d3.select(id).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(lD).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill", function(d, i){return color(i);})
// create the second column for each segment.
tr.append("td").text(function(d){ return d.type;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq')
.text(function(d){ return d.l_month;});
// create the fourth column for each segment.
tr.append("td").attr("class",'legendPerc')
.text(function(d){ return getLegend(d,lD);});
// Utility function to be used to update the legend.
leg.update = function(nD){
// update the data attached to the row elements.
var l = legend.select("tbody").selectAll("tr").data(nD);
// update the frequencies.
l.select(".legendFreq").text(function(d){ return d3.format(",")(d.leave);});
// update the percentage column.
l.select(".legendPerc").text(function(d){ return getLegend(d,nD);});
}
function getLegend(d,aD){ // Utility function to compute percentage.
var perc = (d.leave/d3.sum(aD.map(function(v){ return v.leave; })));
if (isNaN(perc)){
return d3.format("%")(0);
}
else{
return d3.format("%")(d.leave/d3.sum(aD.map(function(v){ return v.leave; })));
}
}
return leg;
}
// calculate total frequency by segment for all state.
var tF = dept.map(function(d){
return {type:d, leave: d3.sum(fData.map(function(t){ return t.leave[d];}))};
});
// calculate total frequency by state for all segment.
var sF = fData.map(function(d){return [d.l_month,d.total];});
var hG = histoGram(sF), // create the histogram.
pC = pieChart(tF), // create the pie-chart.
leg= legend(tF); // create the legend.
}
});
},
render_graphs: function(){
/**
* Render various graphs related to employee data.
* This function renders different graphs including employee contracts, time off, payslips,
* leave, and expenses. It checks if the current user is logged in as an employee and then
* calls individual functions to render each graph.
*/
var self = this;
if (this.login_employee){
self.render_employee_contracts_graph();
self.render_employee_time_off_graph();
self.render_employee_payslips_graph();
self.render_leave_graph();
self.render_expense_graph();
}
},
render_employee_time_off_graph(){
/**
* Render the employee time off graph.
*/
var self = this;
var w = 200;
var h = 200;
var r = h/2;
var elem = this.$('.time_off_graph');
var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139',
'#ffa433', '#ffc25b', '#f8e54b'];
var color = d3.scale.ordinal().range(colors);
rpc.query({
model: "hr.leave",
method: "get_employee_time_off",
}).then(function (data) {
if (data){
var segColor = {};
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function(d){return d.value;});
var arc = d3.svg.arc().outerRadius(r);
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
var legend = d3.select(elem[0]).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d, i){ return color(i) });
// create the second column for each segment.
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;")
.text(function(d){ return d.value;});
}
});
},
render_employee_payslips_graph(){
/**
* Render the Employee payslips graph.
*/
var self = this;
var w = 200;
var h = 200;
var r = h/2;
var elem = this.$('.emp_payslips_graph');
var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139',
'#ffa433', '#ffc25b', '#f8e54b'];
var color = d3.scale.ordinal().range(colors);
rpc.query({
model: "hr.payslip",
method: "get_employee_payslips",
}).then(function (data) {
if(data){
var segColor = {};
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function(d){return d.value;});
var arc = d3.svg.arc().outerRadius(r);
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
var legend = d3.select(elem[0]).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d, i){ return color(i) });
// create the second column for each segment.
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;")
.text(function(d){ return d.value;});
}
});
},
render_employee_contracts_graph(){
/**
* Render the employee contracts graph.
*/
var self = this;
var w = 200;
var h = 200;
var r = h/2;
var elem = this.$('.emp_contracts_graph');
var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139',
'#ffa433', '#ffc25b', '#f8e54b'];
var color = d3.scale.ordinal().range(colors);
rpc.query({
model: "hr.contract",
method: "get_employee_contract",
}).then(function (data) {
if(data){
var segColor = {};
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function(d){return d.value;});
var arc = d3.svg.arc().outerRadius(r);
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
var legend = d3.select(elem[0]).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d, i){ return color(i) });
// create the second column for each segment.
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;")
.text(function(d){ return d.value;});
}
});
},
update_leave_trend: function(){
/**
* Update the leave trend graph.
*/
var self = this;
rpc.query({
model: "hr.employee",
method: "employee_leave_trend",
}).then(function (data) {
if(data){
var elem = self.$('.leave_trend');
var margin = {top: 30, right: 20, bottom: 30, left: 80},
width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// Set the ranges
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], 1);
var y = d3.scale.linear()
.range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom");
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) { return x(d.l_month); })
.y(function(d) { return y(d.leave); });
var svg = d3.select(elem[0]).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(data.map(function(d) { return d.l_month; }));
y.domain([0, d3.max(data, function(d) { return d.leave; })]);
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3)
.attr("cx", function(d) { return x(d.l_month); })
.attr("cy", function(d) { return y(d.leave); })
// .on('mouseover', function() { d3.select(this).transition().duration(500).ease("elastic").attr('r', 3 * 2) })
// .on('mouseout', function() { d3.select(this).transition().duration(500).ease("in-out").attr('r', 3) });
.on("mouseover", function() { tooltip.style("display", null);
d3.select(this).transition().duration(500).ease("elastic").attr('r', 3 * 2)
})
.on("mouseout", function() { tooltip.style("display", "none");
d3.select(this).transition().duration(500).ease("in-out").attr('r', 3)
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.leave);
});
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "black")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
}
});
},
});
core.action_registry.add('payroll_dashboard', PayrollDashboard);
return PayrollDashboard;
});

7828
hr_payroll_dashboard/static/src/js/lib/d3.min.js

File diff suppressed because it is too large

268
hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml

@ -0,0 +1,268 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Payroll Dashboard View-->
<templates id="template" xml:space="preserve">
<t t-name="PayrollDashboardMain">
<div class="oh_dashboards">
<div class="container-fluid o_payroll_dashboard">
</div>
</div>
</t>
<t t-name="EmployeeWarning">
<div class="row" style="margin-top:1%;margin-left:40%;">
<div class="col-md-12 ">
<div>
<div class="rounded mx-auto d-block">
<div>
<p style="color:red;">Error : Could not find employee linked to user</p>
<p style="color:red;">Please contact system admin for the setup</p>
</div>
</div>
</div>
</div>
</div>
</t>
<t t-name="EmployeeDetails">
<link rel="stylesheet"
href="/hr_payroll_dashboard/static/src/css/dashboard.css"/>
<div class="row main-section">
<div class="col-md-4 col-sm-6 oh-payroll">
<div class="oh-card o_attendances" style="width: 288px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#5bcbd0">
<i class="fa fa-calendar"/>
</div>
<div class="stat-content">
<div class="stat-head">Attendances</div>
<div class="stat_count">
<t t-esc="widget.login_employee['emp_timesheets']"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4 col-sm-6 oh-payroll">
<div class="oh-card o_leave" style="width: 288px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#645bd0">
<i class="fa fa-calendar-minus-o"/>
</div>
<div class="stat-content">
<div class="stat-head">Leave Requests</div>
<div class="stat_count">
<t t-esc="widget.login_employee['emp_leave']"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4 col-sm-6 oh-payroll">
<div class="oh-card o_payslips" style="width: 350px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#85d05b">
<i class="fa fa-money"/>
</div>
<div class="stat-content">
<div class="stat-head">Payslips</div>
<div class="stat_count">
<t t-esc="widget.login_employee['payslip_count']"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4 col-sm-6 oh-payroll">
<div class="oh-card o_contracts" style="width: 288px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#d05bb8">
<i class="fa fa-handshake-o"/>
</div>
<div class="stat-content">
<div class="stat-head">Contracts</div>
<div class="stat_count">
<t t-esc="widget.login_employee['emp_contracts_count']"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4 col-sm-6 oh-payroll">
<div class="oh-card o_salary_rules" style="width: 288px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#FCF030">
<i class='fa fa-money'/>
</div>
<div class="stat-content">
<div class="stat-head">Salary Rules</div>
<div class="stat_count">
<t t-esc="widget.login_employee['salary_rule_count']"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4 col-sm-6 oh-payroll">
<div class="oh-card o_salary_structures" style="width: 350px;">
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#FFA742">
<i class='fa fa-money'/>
</div>
<div class="stat-content">
<div class="stat-head">Salary Structures</div>
<div class="stat_count">
<t t-esc="widget.login_employee['salary_structure_count']"/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<t t-name="PayrollChart">
<div class="col-xs-12 col-sm-12 col-lg-12 col-md-12">
<div class="row" style="margin:0px;">
<div class="col-md-6 monthly_expense_trend">
<div class="card">
<div class="card-header">
<div class="card-title">
<b>
<h3 class="custom-h3">
Monthly Expense Analysis
</h3>
</b>
</div>
</div>
<div class="card-body">
<div class="expense_graph"/>
</div>
</div>
</div>
<div class="col-md-6 my_leave_graph">
<div class="card">
<div class="card-header">
<div class="card-title">
<b>
<h3 class="custom-h3">
My Leave Analysis
</h3>
</b>
</div>
</div>
<div class="card-body">
<div class="leave_trend"/>
</div>
</div>
</div>
</div>
</div>
</t>
</t>
<t t-name="ManagerLeaveDashboard">
<div class="row" style="margin:0px;">
<div class="col-md-12 monthly_leave_graph_view">
<div class="card">
<div class="card-header">
<div class="card-title">
<b>
<h3 class="custom-h3">
Monthly Leave Analysis
</h3>
</b>
</div>
</div>
<div class="card-body">
<div class="leave_graph justify-content-center"/>
</div>
</div>
</div>
</div>
</t>
<t t-name="PayrollManagerDashboard">
<br/>
<br/>
<div class="row" style="margin:0px;">
<div class="col-md-4" id="col-graph">
<div class="card">
<div class="card-header">
<div class="custom-h3 card-title">
<b>
<span style="font-weight:bold;">
PAYSLIPS ANALYSIS
</span>
</b>
</div>
</div>
<div class="card-body mt-3" id="in_ex_body_hide">
<div class="row">
<div class="col-md-12">
<div class="chart">
<div style="text-align: center;"
class="emp_payslips_graph"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4" id="col-graph">
<div class="card">
<div class="card-header">
<div class="custom-h3 card-title">
<b>
<span style="font-weight:bold;">
CONTRACT ANALYSIS
</span>
</b>
</div>
</div>
<div class="card-body mt-3" id="in_ex_body_hide">
<div class="row">
<div class="col-md-12">
<div class="chart">
<div style="text-align: center;"
class="emp_contracts_graph"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4" id="col-graph">
<div class="card">
<div class="card-header">
<div class="custom-h3 card-title">
<b>
<span style="font-weight:bold;">
Time Off Analysis
</span>
</b>
</div>
</div>
<div class="card-body mt-3" id="in_ex_body_hide">
<div class="row">
<div class="col-md-12">
<div class="chart">
<div style="text-align: center;"
class="time_off_graph"/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</t>
</templates>

12
hr_payroll_dashboard/views/dashboard_views.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="action_hr_payroll_dashboard_menu" model="ir.actions.client">
<field name="name">Dashboard</field>
<field name="tag">payroll_dashboard</field>
</record>
<menuitem id="hr_payroll_dashboard_menu"
name="Dashboard"
action="action_hr_payroll_dashboard_menu"
parent="hr_payroll_community.menu_hr_payroll_community_root"
sequence="1"/>
</odoo>
Loading…
Cancel
Save