Browse Source

[ADD] Initial Commit 'hr_payroll_dashboard'

pull/216/head
AjmalCybro 3 years ago
parent
commit
0233fe2edf
  1. 18
      hr_payroll_dashboard/README.rst
  2. 22
      hr_payroll_dashboard/__init__.py
  3. 44
      hr_payroll_dashboard/__manifest__.py
  4. 6
      hr_payroll_dashboard/doc/RELEASE_NOTES.md
  5. 23
      hr_payroll_dashboard/models/__init__.py
  6. 440
      hr_payroll_dashboard/models/employee.py
  7. 75
      hr_payroll_dashboard/models/hr_payroll.py
  8. BIN
      hr_payroll_dashboard/static/description/assets/icons/check.png
  9. BIN
      hr_payroll_dashboard/static/description/assets/icons/chevron.png
  10. BIN
      hr_payroll_dashboard/static/description/assets/icons/cogs.png
  11. BIN
      hr_payroll_dashboard/static/description/assets/icons/consultation.png
  12. BIN
      hr_payroll_dashboard/static/description/assets/icons/ecom-black.png
  13. BIN
      hr_payroll_dashboard/static/description/assets/icons/education-black.png
  14. BIN
      hr_payroll_dashboard/static/description/assets/icons/hotel-black.png
  15. BIN
      hr_payroll_dashboard/static/description/assets/icons/license.png
  16. BIN
      hr_payroll_dashboard/static/description/assets/icons/lifebuoy.png
  17. BIN
      hr_payroll_dashboard/static/description/assets/icons/manufacturing-black.png
  18. BIN
      hr_payroll_dashboard/static/description/assets/icons/pos-black.png
  19. BIN
      hr_payroll_dashboard/static/description/assets/icons/puzzle.png
  20. BIN
      hr_payroll_dashboard/static/description/assets/icons/restaurant-black.png
  21. BIN
      hr_payroll_dashboard/static/description/assets/icons/service-black.png
  22. BIN
      hr_payroll_dashboard/static/description/assets/icons/trading-black.png
  23. BIN
      hr_payroll_dashboard/static/description/assets/icons/training.png
  24. BIN
      hr_payroll_dashboard/static/description/assets/icons/update.png
  25. BIN
      hr_payroll_dashboard/static/description/assets/icons/user.png
  26. BIN
      hr_payroll_dashboard/static/description/assets/icons/wrench.png
  27. BIN
      hr_payroll_dashboard/static/description/assets/modules/approval_image.png
  28. BIN
      hr_payroll_dashboard/static/description/assets/modules/dynamic_image.png
  29. BIN
      hr_payroll_dashboard/static/description/assets/modules/list_view_image.png
  30. BIN
      hr_payroll_dashboard/static/description/assets/modules/multiple_ref_image.png
  31. BIN
      hr_payroll_dashboard/static/description/assets/modules/print_image.png
  32. BIN
      hr_payroll_dashboard/static/description/assets/modules/product_return_image.png
  33. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/attendances_details.png
  34. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/dashboard(1).png
  35. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/dashboard(2.2).png
  36. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/dashboard(3.2).png
  37. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/dashboard(4).png
  38. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/dashboard(4.2).png
  39. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/dashboard(5.2).png
  40. BIN
      hr_payroll_dashboard/static/description/assets/screenshots/hero.png
  41. BIN
      hr_payroll_dashboard/static/description/banner.png
  42. BIN
      hr_payroll_dashboard/static/description/icon.png
  43. 657
      hr_payroll_dashboard/static/description/index.html
  44. 1028
      hr_payroll_dashboard/static/src/css/dashboard.css
  45. 426
      hr_payroll_dashboard/static/src/css/lib/nv.d3.css
  46. 432
      hr_payroll_dashboard/static/src/css/style.scss
  47. BIN
      hr_payroll_dashboard/static/src/img/attendance.png
  48. BIN
      hr_payroll_dashboard/static/src/img/contract.png
  49. BIN
      hr_payroll_dashboard/static/src/img/leave.png
  50. BIN
      hr_payroll_dashboard/static/src/img/payslips.png
  51. 770
      hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js
  52. 7828
      hr_payroll_dashboard/static/src/js/lib/d3.min.js
  53. 253
      hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml
  54. 16
      hr_payroll_dashboard/views/assets.xml
  55. 14
      hr_payroll_dashboard/views/dashboard_view.xml

18
hr_payroll_dashboard/README.rst

@ -0,0 +1,18 @@
HR Payroll Dashboard v14
=================
HR Payroll Dashboard
Installation
============
- www.odoo.com/documentation/14.0/setup/install.html
- Install our custom addon
Configuration
=============
No additional configurations needed
Credits
=======
Developer: Aiswarya J P v14 @ cybrosys, Contact: odoo@cybrosys.com

22
hr_payroll_dashboard/__init__.py

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

44
hr_payroll_dashboard/__manifest__.py

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2021-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author:Cybrosys Techno Solutions(odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
{
'name': "HR Payroll Dashboard",
'version': '14.0.1.0.0',
'summary': """HR Payroll Dashboard""",
'description': """HR Payroll Dashboard""",
'category': 'Human Resource',
'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/assets.xml',
'views/dashboard_view.xml',
],
'license': "AGPL-3",
'installable': True,
'application': True,
'images': ['static/description/banner.png'],
'qweb': [
"static/src/xml/payroll_dashboard.xml",
],
}

6
hr_payroll_dashboard/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <hr_payroll_dashboard>
#### 18.07.2022
#### Version 14.0.1.0.0
##### ADD
- Initial commit for HR Payroll Dashboard

23
hr_payroll_dashboard/models/__init__.py

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

440
hr_payroll_dashboard/models/employee.py

@ -0,0 +1,440 @@
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author:Cybrosys Techno Solutions(odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from datetime import timedelta, datetime, date
from collections import defaultdict
from dateutil.relativedelta import relativedelta
import pandas as pd
from pytz import utc
from odoo.tools import float_utils
from odoo import models, fields, api, _
from odoo.http import request
ROUNDING_FACTOR = 16
class Employee(models.Model):
_inherit = 'hr.employee'
is_manager = fields.Boolean(compute='_compute_is_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_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_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_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_id.id)])
payslips = self.env['hr.payslip'].sudo().search([
('employee_id', '=', employee_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_id.is_manager \
else len(leave_request_count)
payslip_count = len(payslips) if not employee_id.is_manager \
else len(self.env['hr.payslip'].sudo().search([]))
emp_contracts_count = len(employee_contracts) \
if not employee_id.is_manager else len(
self.env['hr.contract'].sudo().search([]))
attendance_today = len(manager_attendance_count) \
if employee_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):
"""To get the work days count"""
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(from_full, to_full,
resource)
day_total = defaultdict(float)
for start, stop, meta in intervals:
day_total[start.date()] += (stop - start).total_seconds() / 3600
if compute_leaves:
intervals = calendar._work_intervals(from_datetime, to_datetime,
resource, domain)
else:
intervals = calendar._attendance_intervals(from_datetime,
to_datetime, resource)
day_hours = defaultdict(float)
for start, stop, meta in intervals:
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 wise leave details"""
month_list = []
graph_result = []
uid = request.session.uid
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
for i in range(5, -1, -1):
last_month = datetime.now() - relativedelta(months=i)
text = format(last_month, '%B %Y')
month_list.append(text)
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]
for month in month_list:
leave = {}
for dept in departments:
leave[dept['name']] = 0
vals = {
'l_month': month,
'leave': leave
}
graph_result.append(vals)
employee_id = self.env['hr.employee'].browse(employee[0]['id'])
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 = []
for line in results:
employee = self.browse(line['employee_id'])
from_dt = fields.Datetime.from_string(line['date_from'])
to_dt = fields.Datetime.from_string(line['date_to'])
days = employee.get_work_days_dashboard(from_dt, to_dt)
line['days'] = days
vals = {
'department': line['department_id'],
'l_month': line['month_year'],
'days': days
}
leave_lines.append(vals)
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 get_employee_expense(self):
"""return employee expense details"""
month_list = []
graph_result = []
uid = request.session.uid
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
for i in range(5, -1, -1):
last_month = datetime.now() - relativedelta(months=i)
text = format(last_month, '%B %Y')
month_list.append(text)
self.env.cr.execute("""select id, name from hr_employee
where active=True""")
departments = self.env.cr.dictfetchall()
department_list = [x['name'] for x in departments]
for month in month_list:
leave = {}
for dept in departments:
leave[dept['name']] = 0
vals = {
'l_month': month,
'leave': leave
}
graph_result.append(vals)
employee_id = self.env['hr.employee'].browse(employee[0]['id'])
sql = """
SELECT h.id, h.employee_id,h.date,
extract('month' FROM h.date)::int AS leave_month,
to_char(h.date, 'Month YYYY') as month_year
FROM (select * from hr_expense where state = 'approved') h
"""
self.env.cr.execute(sql, (employee[0]['id'],))
results = self.env.cr.dictfetchall()
leave_lines = []
for line in results:
employee = self.browse(line['employee_id'])
from_dt = fields.Datetime.from_string(line['date'])
to_dt = fields.Datetime.from_string(line['date'])
days = employee.get_work_days_dashboard(from_dt, to_dt)
line['days'] = days
vals = {
'department': line['employee_id'],
'l_month': line['month_year'],
'days': days
}
leave_lines.append(vals)
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"""
leave_lines = []
month_list = []
graph_result = []
for i in range(5, -1, -1):
last_month = datetime.now() - relativedelta(months=i)
text = format(last_month, '%B %Y')
month_list.append(text)
uid = request.session.uid
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
for month in month_list:
vals = {
'l_month': month,
'leave': 0
}
graph_result.append(vals)
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
"""
self.env.cr.execute(sql, (employee[0]['id'],))
results = self.env.cr.dictfetchall()
for line in results:
employee = self.browse(line['employee_id'])
from_dt = fields.Datetime.from_string(line['date_from'])
to_dt = fields.Datetime.from_string(line['date_to'])
days = employee.get_work_days_dashboard(from_dt, to_dt)
line['days'] = days
vals = {
'l_month': line['month_year'],
'days': days
}
leave_lines.append(vals)
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
class Contract(models.Model):
_inherit = 'hr.contract'
state_label = fields.Char(compute="compute_state_label", store=True)
@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 = []
for i in range(0, len(dat)):
data.append({'label': dat[i][0], 'value': dat[i][1]})
return data
class HrExpense(models.Model):
_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 = []
approved_trend = []
for i in range(11, -1, -1):
last_month = datetime.now() - relativedelta(months=i)
text = format(last_month, '%B %Y')
month_list.append(text)
for month in month_list:
vals = {
'l_month': month,
'count': 0
}
approved_trend.append(vals)
uid = request.session.uid
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
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
class HrAttendance(models.Model):
_inherit = 'hr.attendance'
attendance_date = fields.Date(compute="compute_attendance_date",
store=True)
@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()

75
hr_payroll_dashboard/models/hr_payroll.py

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author:Cybrosys Techno Solutions(odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo import models, fields, api
class HrPayroll(models.Model):
_inherit = 'hr.payslip'
_description = 'Employee Payroll'
payslip_state = fields.Char(compute="compute_payslip_state", store=True)
@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 = []
for i in range(0, len(dat)):
if dat[i][0] is not None:
data.append({'label': dat[i][0], 'value': dat[i][1]})
return data
class HrLeave(models.Model):
_inherit = 'hr.leave'
state_string = fields.Char(compute="compute_state_string", store=True)
@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 = []
for i in range(0, len(dat)):
data.append({'label': dat[i][0], 'value': dat[i][1]})
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/modules/approval_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/dashboard(1).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/dashboard(2.2).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/dashboard(3.2).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/dashboard(4).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/dashboard(4.2).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
hr_payroll_dashboard/static/description/assets/screenshots/dashboard(5.2).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
hr_payroll_dashboard/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
hr_payroll_dashboard/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

657
hr_payroll_dashboard/static/description/index.html

@ -0,0 +1,657 @@
<div class="container" style="padding: 4rem 1.5rem !important">
<div class="row" style="height: 900px !important;">
<div class="col-sm-12 col-md-12 col-lg-12"
style="padding: 4rem 1rem !important; background-color: #714B67 !important; height: 600px !important; border-radius: 20px !important;">
<h1
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #FFFFFF !important; font-size: 3.5rem !important; text-align: center !important;">
Hr Payroll Dashboard</h1>
<p
style="font-family: 'Montserrat', sans-serif !important; font-weight: 300 !important; color: #FFFFFF !important; font-size: 1.4rem !important; text-align: center !important;">
Detailed Dashboard View for Payroll Module
</p>
<img src="./assets/screenshots/hero.png" class="img-responsive" width="100%" height="auto" />
</div>
</div>
<div class="row">
<div class="col-md-12" style="border-bottom: 1px solid #d5d5d5 !important; margin-bottom: 2rem !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-compass mr-2"></i>Explore this module
</h2>
</div>
<div class="col-md-6">
<a href="#overview" style="text-decoration: none !important;">
<div class="row"
style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;">
<div class="col-8">
<h3
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;">
Overview</h3>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;">
Learn more about this module</p>
</div>
<div class="col-4 text-right d-flex justify-content-end align-items-center">
<i class="fa fa-chevron-right" style="color: #714B67 !important;"></i>
</div>
</div>
</a>
</div>
<div class="col-md-6">
<a href="#features" style="text-decoration: none !important;">
<div class="row"
style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;">
<div class="col-8">
<h3
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;">
Features</h3>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;">
View features of this module</p>
</div>
<div class="col-4 text-right d-flex justify-content-end align-items-center">
<i class="fa fa-chevron-right" style="color: #714B67 !important;"></i>
</div>
</div>
</a>
</div>
<div class="col-md-6">
<a href="#screenshots" style="text-decoration: none !important;">
<div class="row"
style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;">
<div class="col-8">
<h3
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;">
Screenshots</h3>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;">
See key screenshots of this module</p>
</div>
<div class="col-4 text-right d-flex justify-content-end align-items-center">
<i class="fa fa-chevron-right" style="color: #714B67 !important;"></i>
</div>
</div>
</a>
</div>
</div>
<div class="row" id="overview">
<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-pie-chart mr-2"></i>Overview
</h2>
</div>
<div class="col-mg-12 pl-3">
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important; line-height: 30px !important;">
This module helps you to see the Overview of Payroll, here You can see the details of Attendance, Leaves, Payslip
contracts, etc.</p>
</div>
</div>
<div class="row" id="features">
<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-star mr-2"></i>Features
</h2>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Payslip Details</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Shows payslip count and analysis.</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Contract Details</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Shows Contract count and analysis.</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Leave Request Details</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Shows Leave request details and analysis.</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Attendance Details</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
shows attendance details.</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Salary Rules Details</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
shows salary rules details.</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Salary Structures Details</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
shows salary structures details.</p>
</div>
</div>
</div>
<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 Payroll</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
After installation, open Payroll Module</p>
<img src="hr_payroll_dashboard/static/description/assets/screenshots/dashboard(1).png" class="img-responsive img-thumbnail border" width="100%"
height="auto" />
</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;">
Payslip, Attendance, Contract, Leaves Details.</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Attendance, Leaves, Payslips, Contracts, Salary Rules, and Salary structures details of employees is displayed in dashboard.
Each card is clickable.
</p>
<img src="assets/screenshots/dashboard(2.2).png" class="img-responsive img-thumbnail border" width="100%"
height="auto" />
</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;">
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/dashboard(3.2).png" class="img-responsive img-thumbnail border" width="100%"
height="auto" />
</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;">
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/dashboard(4.2).png" class="img-responsive img-thumbnail border" width="100%"
height="auto" />
</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;">
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 bbe done.</p>
<img src="assets/screenshots/dashboard(5.2).png" class="img-responsive img-thumbnail border" width="100%"
height="auto" />
</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;">
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/dashboard(4).png" class="img-responsive img-thumbnail border" width="100%"
height="auto" />
</div>
</div>
<!-- SUGGESTED PRODUCTS -->
<div class="row">
<div class="col-lg-12 d-flex flex-column justify-content-center"
style="text-align: center; padding: 2.5rem 1rem !important;">
<h2 style="color: #212529 !important;">Suggested Products</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" />
<div id="demo1" class="row carousel slide" data-ride="carousel">
<!-- The slideshow -->
<div class="carousel-inner">
<div class="carousel-item active" style="min-height:0px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/14.0/dynamic_accounts_report/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/dynamic_image.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/14.0/product_return_pos/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/product_return_image.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/14.0/product_approval_management/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/approval_image.png">
</div>
</a>
</div>
</div>
<div class="carousel-item" style="min-height:0px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left">
<a href="https://apps.odoo.com/apps/modules/14.0/mrp_work_order_print/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/print_image.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/14.0/list_view_sticky_header/" target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/list_view_image.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/14.0/multiple_reference_per_product/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/multiple_ref_image.png">
</div>
</a>
</div>
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1" data-slide="prev"
style="left:-25px;width: 35px;color: #000;"> <span class="carousel-control-prev-icon"><i
class="fa fa-chevron-left" style="font-size:24px"></i></span> </a> <a
class="carousel-control-next" href="#demo1" data-slide="next"
style="right:-25px;width: 35px;color: #000;">
<span class="carousel-control-next-icon"><i class="fa fa-chevron-right"
style="font-size:24px"></i></span>
</a>
</div>
</div>
</div>
<!-- END OF SUGGESTED PRODUCTS -->
<!-- OUR SERVICES -->
<section class="container" style="margin-top: 6rem !important;">
<div class="row">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
<h2 style="color: #212529 !important;">Our Services</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" />
</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: #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>
</section>
<!-- END OF END OF OUR SERVICES -->
<!-- OUR INDUSTRIES -->
<section class="container" style="margin-top: 6rem !important;">
<div class="row">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
<h2 style="color: #212529 !important;">Our Industries</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" />
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; 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: 10px; 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: 10px; 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: 10px; 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: 10px; 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: 10px; 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: 10px; 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: 10px; 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>
</section>
<!-- END OF END OF OUR INDUSTRIES -->
<!-- FOOTER -->
<!-- Footer Section -->
<section class="container" style="margin: 5rem auto 2rem;">
<div class="row" style="max-width:1540px;">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
<h2 style="color: #212529 !important;">Need Help?</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" />
</div>
</div>
<!-- Contact Cards -->
<div class="row d-flex justify-content-center align-items-center"
style="max-width:1540px; margin: 0 auto 2rem auto;">
<div class="col-lg-12" style="padding: 0rem 3rem 2rem; border-radius: 10px; margin-right: 3rem; ">
<div class="row mt-4">
<div class="col-lg-4">
<a href="mailto:odoo@cybrosys.com" target="_blank" class="btn btn-block mb-2 deep_hover"
style="text-decoration: none; background-color: #4d4d4d; color: #FFF; border-radius: 4px;"><i
class="fa fa-envelope mr-2"></i>odoo@cybrosys.com</a>
</div>
<div class="col-lg-4">
<a href="https://api.WhatsApp.com/send?phone=918606827707" target="_blank"
class="btn btn-block mb-2 deep_hover"
style="text-decoration: none; background-color: #25D366; color: #FFF; border-radius: 4px;"><i
class="fa fa-WhatsApp mr-2"></i>WhatsApp</a>
</div>
<div class="col-lg-4">
<a href="mailto:info@cybrosys.com" target="_blank" class="btn btn-block deep_hover"
style="text-decoration: none; background-color: #4d4d4d; color: #FFF; border-radius: 4px;"><i
class="fa fa-envelope mr-2"></i>info@cybrosys.com</a>
</div>
</div>
</div>
</div>
<!-- End of Contact Cards -->
</section>
<!-- Footer -->
<section class="oe_container" style="padding: 2rem 3rem 1rem;">
<div class="row" style="max-width:1540px; margin: 0 auto; margin-right: 3rem; ">
<!-- Logo -->
<div class="col-lg-12 d-flex justify-content-center align-items-center" style="margin-top: 3rem;">
<img src="https://www.cybrosys.com/images/logo.png" width="200px" height="auto" />
</div>
<!-- End of Logo -->
<div class="col-lg-12">
<hr
style="margin-top: 3rem;background: linear-gradient(90deg, rgba(2,0,36,0) 0%, rgba(229,229,229,1) 33%, rgba(229,229,229,1) 58%, rgba(0,212,255,0) 100%); height: 2px; border-style: none;">
<!-- End of Footer Section -->
</div>
</div>
</section>
<!-- END OF FOOTER -->
</div>

1028
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;
}

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

@ -0,0 +1,432 @@
#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;
width: 100%;
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;
}

BIN
hr_payroll_dashboard/static/src/img/attendance.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
hr_payroll_dashboard/static/src/img/contract.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
hr_payroll_dashboard/static/src/img/leave.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
hr_payroll_dashboard/static/src/img/payslips.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

770
hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js

@ -0,0 +1,770 @@
odoo.define('hr_payroll_dashboard.PayrollDashboard', function (require) {
"use strict";
var AbstractAction = require('web.AbstractAction');
var ajax = require('web.ajax');
var core = require('web.core');
var rpc = require('web.rpc');
var session = require('web.session');
var web_client = require('web.web_client');
var _t = core._t;
var QWeb = core.qweb;
var PayrollDashboard = AbstractAction.extend({
template: 'PayrollDashboardMain',
cssLibs: [
'/hr_payroll_dashboard/static/src/css/lib/nv.d3.css'
],
jsLibs: [
'/hr_payroll_dashboard/static/src/js/lib/d3.min.js'
],
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(){
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(){
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',
views: [[false, 'list'],],
context: {"create": false},
domain: domain,
});
},
_onclickAttendances: function(){
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',
views: [[false, 'list'],],
context: {"create": false},
domain: domain,
});
},
_onclickLeave: function(){
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',
views: [[false, 'list'],],
context: {"create": false},
domain: domain,
});
},
_onClickSalaryRules: function(){
var self = this;
return self.do_action({
name: _t("Salary Rules"),
type: 'ir.actions.act_window',
res_model: 'hr.salary.rule',
view_mode: 'tree',
views: [[false, 'list'],],
context: {"create": false},
});
},
_onClickSalaryStructure: function(){
var self = this;
return self.do_action({
name: _t("Salary Structures"),
type: 'ir.actions.act_window',
res_model: 'hr.payroll.structure',
view_mode: 'tree',
views: [[false, 'list'],],
context: {"create": false},
});
},
update_attendance: function () {
var self = this;
this._rpc({
model: 'hr.employee',
method: 'attendance_manual',
args: [[self.login_employee.id], 'hr_attendance.hr_attendance_action_my_attendances'],
})
.then(function(result) {
var attendance_state =self.login_employee.attendance_state;
var message = ''
var action_client = {
type: "ir.actions.client",
name: _t('Dashboard'),
tag: 'payroll_dashboard',
};
self.do_action(action_client, {clear_breadcrumbs: true});
if (attendance_state == 'checked_in'){
message = 'Checked Out'
}
else if (attendance_state == 'checked_out'){
message = 'Checked In'
}
self.trigger_up('show_effect', {
message: _t("Successfully " + message),
type: 'rainbow_man'
});
setTimeout('location.reload()',7);
});
},
render_expense_graph:function(){
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) {
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(){
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) {
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(){
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(){
var self = this;
var w = 200;
var h = 200;
var r = h/2;
var elem = this.$('.time_off_graph');
// var colors = ['#ff8762', '#5ebade', '#b298e1', '#70cac1', '#cf2030'];
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) {
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(){
var self = this;
var w = 200;
var h = 200;
var r = h/2;
var elem = this.$('.emp_payslips_graph');
// var colors = ['#ff8762', '#5ebade', '#b298e1', '#70cac1', '#cf2030'];
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) {
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(){
var self = this;
var w = 200;
var h = 200;
var r = h/2;
var elem = this.$('.emp_contracts_graph');
// var colors = ['#ff8762', '#5ebade', '#b298e1', '#70cac1', '#cf2030'];
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) {
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(){
var self = this;
rpc.query({
model: "hr.employee",
method: "employee_leave_trend",
}).then(function (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

253
hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml

@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<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="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 o_attendances oh-payroll">
<div class="oh-card" 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 o_leave oh-payroll">
<div class="oh-card" 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 o_payslips oh-payroll">
<div class="oh-card" 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 o_contracts oh-payroll">
<div class="oh-card" 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 o_salary_rules oh-payroll">
<div class="oh-card" 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'></i>
</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 o_salary_structures oh-payroll">
<div class="oh-card" 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'></i>
</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>
<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>

16
hr_payroll_dashboard/views/assets.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<template id="assets_hr_payroll_dashboard" name="Payroll Dashboard"
inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript"
src="/hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js"/>
<link rel="stylesheet" type="text/scss"
href="/hr_payroll_dashboard/static/src/css/style.scss"/>
<link rel="stylesheet"
href="/hr_payroll_dashboard/static/src/css/dashboard.css"/>
</xpath>
</template>
</data>
</odoo>

14
hr_payroll_dashboard/views/dashboard_view.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<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"/>
</data>
</odoo>
Loading…
Cancel
Save