Browse Source

Jan 11 : [FIX] Bug Fixed 'hr_payroll_dashboard'

pull/238/head
AjmalCybro 2 years ago
parent
commit
66e66dc925
  1. 2
      hr_payroll_dashboard/__manifest__.py
  2. 394
      hr_payroll_dashboard/models/employee.py
  3. 880
      hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js
  4. 22
      hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml

2
hr_payroll_dashboard/__manifest__.py

@ -21,7 +21,7 @@
#############################################################################
{
'name': "HR Payroll Dashboard",
'version': '14.0.1.0.0',
'version': '14.0.1.1.1',
'summary': """HR Payroll Dashboard""",
'description': """HR Payroll Dashboard""",
'category': 'Human Resource',

394
hr_payroll_dashboard/models/employee.py

@ -47,37 +47,38 @@ class Employee(models.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_user_id = self.env['hr.employee'].sudo().search([
('user_id', '=', uid)
], limit=1)
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
attendance_count = self.env['hr.attendance'].sudo().search(
[('employee_id', '=', employee_id.id),
[('employee_id', '=', employee_user_id.id),
('attendance_date', '=', date.today())])
manager_attendance_count = self.env['hr.attendance'].sudo().search(
[('attendance_date', '=', date.today())])
leave_request_count = self.env['hr.leave'].sudo().search(
[('employee_id', '=', employee_id.id),
[('employee_id', '=', employee_user_id.id),
('request_date_from', '=', date.today())])
manager_leave_request = self.env['hr.leave'].sudo().search(
[('request_date_from', '=', date.today())])
employee_contracts = self.env['hr.contract'].sudo().search([
('employee_id', '=', employee_id.id)])
('employee_id', '=', employee_user_id.id)])
payslips = self.env['hr.payslip'].sudo().search([
('employee_id', '=', employee_id.id)])
('employee_id', '=', employee_user_id.id)])
salary_rules = self.env['hr.salary.rule'].sudo().search([])
salary_structures = self.env['hr.payroll.structure'].sudo().search([])
salary_rule_count = len(salary_rules)
salary_structure_count = len(salary_structures)
emp_leave = len(manager_leave_request) if employee_id.is_manager \
emp_leave = len(manager_leave_request) if employee_user_id.is_manager \
else len(leave_request_count)
payslip_count = len(payslips) if not employee_id.is_manager \
payslip_count = len(payslips) if not employee_user_id.is_manager \
else len(self.env['hr.payslip'].sudo().search([]))
emp_contracts_count = len(employee_contracts) \
if not employee_id.is_manager else len(
if not employee_user_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_user_id.is_manager else len(attendance_count)
if employee:
data = {
'emp_timesheets': attendance_today,
@ -128,20 +129,34 @@ class Employee(models.Model):
@api.model
def get_department_leave(self):
"""return department wise leave details"""
employee = False
month_list = []
graph_result = []
uid = request.session.uid
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
employee_user = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
employees = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee_user:
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
employee_id = self.env['hr.employee'].browse(
employee[0]['id'])
elif employees:
employee = self.env['hr.employee'].sudo().search_read([], limit=1)
employee_id = self.env['hr.employee'].browse(
employee[0]['id'])
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:
@ -151,65 +166,81 @@ class Employee(models.Model):
'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
if employee:
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
else:
return False
@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)
employee = False
employee_user = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
employees = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee_user:
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
elif employees:
employees = self.env['hr.employee'].sudo().search_read([], limit=1)
for i in range(5, -1, -1):
last_month = datetime.now() - relativedelta(months=i)
@ -228,47 +259,50 @@ class Employee(models.Model):
'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'],))
if employee:
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
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
else:
return False
@api.model
def employee_leave_trend(self):
@ -281,8 +315,19 @@ class Employee(models.Model):
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)
employee = False
employee_user = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
employees = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee_user:
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
elif employees:
employee = self.env['hr.employee'].sudo().search_read([], limit=1)
for month in month_list:
vals = {
'l_month': month,
@ -305,32 +350,35 @@ class Employee(models.Model):
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
if employee:
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
else:
return False
class Contract(models.Model):
@ -383,47 +431,63 @@ class HrExpense(models.Model):
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'],))
employee = False
employee_user = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
employees = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee_user:
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
elif employees:
employee = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee:
employee_id = self.env['hr.employee'].browse(employee[0]['id'])
if not employee_id.is_manager:
sql = ('''select to_char(date, 'Month YYYY') as l_month,
count(id) from hr_expense
WHERE date BETWEEN CURRENT_DATE - INTERVAL '12 months'
AND CURRENT_DATE + interval '1 month - 1 day'
AND hr_expense.employee_id = %s
group by l_month''')
self.env.cr.execute(sql, (employee[0]['id'],))
else:
sql = ('''select to_char(date, 'Month YYYY') as l_month,
count(id) from hr_expense WHERE date
BETWEEN CURRENT_DATE - INTERVAL
'12 months' AND CURRENT_DATE + interval '1 month - 1 day'
group by l_month''')
self.env.cr.execute(sql)
approved_data = cr.fetchall()
for line in approved_data:
match = list(filter(lambda d: d['l_month'].replace(
' ', '') == line[0].replace(' ', ''), approved_trend))
if match:
match[0]['count'] = line[1]
for expense in approved_trend:
expense['l_month'] = expense[
'l_month'].split(' ')[:1][0].strip()[:3]
graph_result = [{
'values': approved_trend
}]
return graph_result
else:
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
return False
class HrAttendance(models.Model):

880
hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js

@ -223,80 +223,80 @@ var PayrollDashboard = AbstractAction.extend({
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);
if(data){
data.forEach(function(d) {
d.values.forEach(function(d) {
d.l_month = d.l_month;
d.count = +d.count;
});
});
var margin = {top: 30, right: 10, bottom: 30, left: 30},
width = 400 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// Set the ranges
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], 1);
var y = d3.scale.linear()
.range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom");
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
x.domain(data[0].values.map(function(d) { return d.l_month; }));
y.domain([0, d3.max(data[0].values, d => d.count)])
var svg = d3.select(elem[0]).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var line = d3.svg.line()
.x(function(d) {return x(d.l_month); })
.y(function(d) {return y(d.count); });
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('class', 'line-group')
.append('path')
.attr('class', 'line')
.attr('d', function(d) { return line(d.values); })
.style('stroke', (d, i) => color(i));
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.selectAll("circle")
.data(function(d) { return d.values;}).enter()
.append("g")
.attr("class", "circle")
.append("circle")
.attr("cx", function(d) { return x(d.l_month)})
.attr("cy", function(d) { return y(d.count)})
.attr("r", 3);
}
});
},
@ -309,212 +309,214 @@ var PayrollDashboard = AbstractAction.extend({
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]); })
if (data){
var fData = data[0];
var dept = data[1];
var id = self.$('.leave_graph')[0];
var barColor = '#ff618a';
fData.forEach(function(d){
var total = 0;
for (var dpt in dept){
total += d.leave[dept[dpt]];
}
d.total=total;
});
// function to handle histogram.
function histoGram(fD){
var hG={}, hGDim = {t: 60, r: 0, b: 30, l: 0};
hGDim.w = 350 - hGDim.l - hGDim.r,
hGDim.h = 200 - hGDim.t - hGDim.b;
//create svg for histogram.
var hGsvg = d3.select(id).append("svg")
.attr("width", hGDim.w + hGDim.l + hGDim.r)
.attr("height", hGDim.h + hGDim.t + hGDim.b).append("g")
.attr("transform", "translate(" + hGDim.l + "," + hGDim.t + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, hGDim.w], 0.1)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
hGsvg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + hGDim.h + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
// Create function for y-axis map.
var y = d3.scale.linear().range([hGDim.h, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
// Create bars for histogram to contain rectangles and freq labels.
var bars = hGsvg.selectAll(".bar").data(fD).enter()
.append("g").attr("class", "bar");
//create the rectangles.
bars.append("rect")
.attr("x", function(d) { return x(d[0]); })
.attr("y", function(d) { return y(d[1]); })
.attr("width", x.rangeBand())
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr("fill", 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; });
.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;
}
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));
// 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;
}
//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 = {};
// function to handle legend.
function legend(lD){
var leg = {};
// create table for legend.
var legend = d3.select(id).append("table").attr('class','legend');
// 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 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 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 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 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);});
// 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);
// 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 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);});
}
// 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; })));
}
}
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];}))};
});
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];});
// 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.
var hG = histoGram(sF), // create the histogram.
pC = pieChart(tF), // create the pie-chart.
leg= legend(tF); // create the legend.
}
});
},
render_graphs: function(){
@ -543,36 +545,37 @@ var PayrollDashboard = AbstractAction.extend({
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;});
if (data){
var segColor = {};
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function(d){return d.value;});
var arc = d3.svg.arc().outerRadius(r);
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
var legend = d3.select(elem[0]).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d, i){ return color(i) });
// create the second column for each segment.
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;")
.text(function(d){ return d.value;});
}
});
},
@ -590,35 +593,37 @@ var PayrollDashboard = AbstractAction.extend({
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;});
if(data){
var segColor = {};
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function(d){return d.value;});
var arc = d3.svg.arc().outerRadius(r);
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
var legend = d3.select(elem[0]).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d, i){ return color(i) });
// create the second column for each segment.
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;")
.text(function(d){ return d.value;});
}
});
},
@ -636,35 +641,37 @@ var PayrollDashboard = AbstractAction.extend({
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;});
if(data){
var segColor = {};
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function(d){return d.value;});
var arc = d3.svg.arc().outerRadius(r);
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
var legend = d3.select(elem[0]).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d, i){ return color(i) });
// create the second column for each segment.
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;")
.text(function(d){ return d.value;});
}
});
},
@ -674,93 +681,94 @@ var PayrollDashboard = AbstractAction.extend({
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");
if(data){
var elem = self.$('.leave_trend');
var margin = {top: 30, right: 20, bottom: 30, left: 80},
width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// Set the ranges
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], 1);
var y = d3.scale.linear()
.range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom");
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) { return x(d.l_month); })
.y(function(d) { return y(d.leave); });
var svg = d3.select(elem[0]).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(data.map(function(d) { return d.l_month; }));
y.domain([0, d3.max(data, function(d) { return d.leave; })]);
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3)
.attr("cx", function(d) { return x(d.l_month); })
.attr("cy", function(d) { return y(d.leave); })
// .on('mouseover', function() { d3.select(this).transition().duration(500).ease("elastic").attr('r', 3 * 2) })
// .on('mouseout', function() { d3.select(this).transition().duration(500).ease("in-out").attr('r', 3) });
.on("mouseover", function() { tooltip.style("display", null);
d3.select(this).transition().duration(500).ease("elastic").attr('r', 3 * 2)
})
.on("mouseout", function() { tooltip.style("display", "none");
d3.select(this).transition().duration(500).ease("in-out").attr('r', 3)
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.leave);
});
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "black")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
}
});
},

22
hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml

@ -6,6 +6,20 @@
</div>
</div>
</t>
<t t-name="EmployeeWarning">
<div class="row" style="margin-top:1%;margin-left:40%;">
<div class="col-md-12 ">
<div>
<div class="rounded mx-auto d-block">
<div>
<p style="color:red;">Error : Could not find employee linked to user</p>
<p style="color:red;">Please contact system admin for the setup</p>
</div>
</div>
</div>
</div>
</div>
</t>
<t t-name="EmployeeDetails">
<link rel="stylesheet"
href="/hr_payroll_dashboard/static/src/css/dashboard.css"/>
@ -83,7 +97,7 @@
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#FCF030">
<i class='fa fa-money'></i>
<i class='fa fa-money'/>
</div>
<div class="stat-content">
<div class="stat-head">Salary Rules</div>
@ -100,7 +114,7 @@
<div class="oh-card-body">
<div class="stat-widget-one">
<div class="stat-icon" style="background:#FFA742">
<i class='fa fa-money'></i>
<i class='fa fa-money'/>
</div>
<div class="stat-content">
<div class="stat-head">Salary Structures</div>
@ -175,8 +189,8 @@
</t>
<t t-name="PayrollManagerDashboard">
<br></br>
<br></br>
<br/>
<br/>
<div class="row" style="margin:0px;">
<div class="col-md-4" id="col-graph">
<div class="card">

Loading…
Cancel
Save