diff --git a/hr_payroll_dashboard/__manifest__.py b/hr_payroll_dashboard/__manifest__.py index b21a87a1c..484a7ab9e 100644 --- a/hr_payroll_dashboard/__manifest__.py +++ b/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', diff --git a/hr_payroll_dashboard/models/employee.py b/hr_payroll_dashboard/models/employee.py index 8ef9bdd78..2e0ce3bf4 100644 --- a/hr_payroll_dashboard/models/employee.py +++ b/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): diff --git a/hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js b/hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js index c31cc3546..39a84887b 100644 --- a/hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js +++ b/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"); + } }); }, diff --git a/hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml b/hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml index e000f4cbe..2247b1b29 100644 --- a/hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml +++ b/hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml @@ -6,6 +6,20 @@ + +
+
+
+
+
+

Error : Could not find employee linked to user

+

Please contact system admin for the setup

+
+
+
+
+
+
@@ -83,7 +97,7 @@
- +
Salary Rules
@@ -100,7 +114,7 @@
- +
Salary Structures
@@ -175,8 +189,8 @@ -

-

+
+