@ -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 |
|||
|
@ -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 |
@ -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", |
|||
], |
|||
} |
@ -0,0 +1,6 @@ |
|||
## Module <hr_payroll_dashboard> |
|||
|
|||
#### 18.07.2022 |
|||
#### Version 14.0.1.0.0 |
|||
##### ADD |
|||
- Initial commit for HR Payroll Dashboard |
@ -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 |
@ -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() |
@ -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 |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 17 KiB |
@ -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 & 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> |
@ -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; |
|||
} |
@ -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; |
|||
} |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 34 KiB |
@ -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; |
|||
}); |
@ -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> |
@ -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> |
@ -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> |