| @ -0,0 +1,18 @@ | |||
| HR Payroll Dashboard v14 | |||
| ================= | |||
| HR Payroll Dashboard | |||
| 
 | |||
| Installation | |||
| ============ | |||
| 	- www.odoo.com/documentation/14.0/setup/install.html | |||
| 	- Install our custom addon | |||
| 
 | |||
| Configuration | |||
| ============= | |||
| 
 | |||
|     No additional configurations needed | |||
| 
 | |||
| Credits | |||
| ======= | |||
| Developer: Aiswarya J P v14 @ cybrosys, Contact: odoo@cybrosys.com | |||
| 
 | |||
| @ -0,0 +1,22 @@ | |||
| # -*- coding: utf-8 -*- | |||
| ############################################################################# | |||
| # | |||
| #    Cybrosys Technologies Pvt. Ltd. | |||
| # | |||
| #    Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |||
| #    Author:Cybrosys Techno Solutions(odoo@cybrosys.com) | |||
| # | |||
| #    You can modify it under the terms of the GNU AFFERO | |||
| #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | |||
| # | |||
| #    This program is distributed in the hope that it will be useful, | |||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |||
| #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | |||
| # | |||
| #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
| #    (AGPL v3) along with this program. | |||
| #    If not, see <http://www.gnu.org/licenses/>. | |||
| # | |||
| ############################################################################# | |||
| from . import models | |||
| @ -0,0 +1,44 @@ | |||
| # -*- coding: utf-8 -*- | |||
| ############################################################################# | |||
| # | |||
| #    Cybrosys Technologies Pvt. Ltd. | |||
| # | |||
| #    Copyright (C) 2021-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |||
| #    Author:Cybrosys Techno Solutions(odoo@cybrosys.com) | |||
| # | |||
| #    You can modify it under the terms of the GNU AFFERO | |||
| #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | |||
| # | |||
| #    This program is distributed in the hope that it will be useful, | |||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |||
| #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | |||
| # | |||
| #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
| #    (AGPL v3) along with this program. | |||
| #    If not, see <http://www.gnu.org/licenses/>. | |||
| # | |||
| ############################################################################# | |||
| { | |||
|     'name': "HR Payroll Dashboard", | |||
|     'version': '14.0.1.0.0', | |||
|     'summary': """HR Payroll Dashboard""", | |||
|     'description': """HR Payroll Dashboard""", | |||
|     'category': 'Human Resource', | |||
|     'author': 'Cybrosys Techno Solutions', | |||
|     'company': 'Cybrosys Techno Solutions', | |||
|     'maintainer': 'Cybrosys Techno Solutions', | |||
|     'website': "https://www.cybrosys.com", | |||
|     'depends': ['hr_payroll_community', 'hr_attendance', 'hr_expense'], | |||
|     'data': [ | |||
|         'views/assets.xml', | |||
|         'views/dashboard_view.xml', | |||
|     ], | |||
|     'license': "AGPL-3", | |||
|     'installable': True, | |||
|     'application': True, | |||
|     'images': ['static/description/banner.png'], | |||
|     'qweb': [ | |||
|         "static/src/xml/payroll_dashboard.xml", | |||
|     ], | |||
| } | |||
| @ -0,0 +1,6 @@ | |||
| ## Module <hr_payroll_dashboard> | |||
| 
 | |||
| #### 18.07.2022 | |||
| #### Version 14.0.1.0.0 | |||
| ##### ADD | |||
| - Initial commit for HR Payroll Dashboard | |||
| @ -0,0 +1,23 @@ | |||
| # -*- coding: utf-8 -*- | |||
| ############################################################################# | |||
| # | |||
| #    Cybrosys Technologies Pvt. Ltd. | |||
| # | |||
| #    Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |||
| #    Author:Cybrosys Techno Solutions(odoo@cybrosys.com) | |||
| # | |||
| #    You can modify it under the terms of the GNU AFFERO | |||
| #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | |||
| # | |||
| #    This program is distributed in the hope that it will be useful, | |||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |||
| #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | |||
| # | |||
| #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
| #    (AGPL v3) along with this program. | |||
| #    If not, see <http://www.gnu.org/licenses/>. | |||
| # | |||
| ############################################################################# | |||
| from . import employee | |||
| from . import hr_payroll | |||
| @ -0,0 +1,440 @@ | |||
| ############################################################################# | |||
| # | |||
| #    Cybrosys Technologies Pvt. Ltd. | |||
| # | |||
| #    Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |||
| #    Author:Cybrosys Techno Solutions(odoo@cybrosys.com) | |||
| # | |||
| #    You can modify it under the terms of the GNU AFFERO | |||
| #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | |||
| # | |||
| #    This program is distributed in the hope that it will be useful, | |||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |||
| #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | |||
| # | |||
| #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
| #    (AGPL v3) along with this program. | |||
| #    If not, see <http://www.gnu.org/licenses/>. | |||
| # | |||
| ############################################################################# | |||
| from datetime import timedelta, datetime, date | |||
| from collections import defaultdict | |||
| from dateutil.relativedelta import relativedelta | |||
| import pandas as pd | |||
| from pytz import utc | |||
| from odoo.tools import float_utils | |||
| from odoo import models, fields, api, _ | |||
| from odoo.http import request | |||
| 
 | |||
| ROUNDING_FACTOR = 16 | |||
| 
 | |||
| 
 | |||
| class Employee(models.Model): | |||
|     _inherit = 'hr.employee' | |||
| 
 | |||
|     is_manager = fields.Boolean(compute='_compute_is_manager') | |||
| 
 | |||
|     def _compute_is_manager(self): | |||
|         """Compute function for checking whether it is a manager or not""" | |||
|         for rec in self: | |||
|             if rec.env.user.has_group('hr_payroll_community.group_hr_payroll_community_manager'): | |||
|                 rec.is_manager = True | |||
|             else: | |||
|                 rec.is_manager = False | |||
| 
 | |||
|     @api.model | |||
|     def get_user_employee_info(self): | |||
|         """To get the employee information""" | |||
|         uid = request.session.uid | |||
|         employee_id = self.env['hr.employee'].sudo().search([ | |||
|             ('user_id', '=', uid)], limit=1) | |||
|         employee = self.env['hr.employee'].sudo().search_read([ | |||
|             ('user_id', '=', uid)], limit=1) | |||
|         attendance_count = self.env['hr.attendance'].sudo().search( | |||
|             [('employee_id', '=', employee_id.id), | |||
|              ('attendance_date', '=', date.today())]) | |||
|         manager_attendance_count = self.env['hr.attendance'].sudo().search( | |||
|             [('attendance_date', '=', date.today())]) | |||
|         leave_request_count = self.env['hr.leave'].sudo().search( | |||
|             [('employee_id', '=', employee_id.id), | |||
|              ('request_date_from', '=', date.today())]) | |||
|         manager_leave_request = self.env['hr.leave'].sudo().search( | |||
|             [('request_date_from', '=', date.today())]) | |||
|         employee_contracts = self.env['hr.contract'].sudo().search([ | |||
|             ('employee_id', '=', employee_id.id)]) | |||
|         payslips = self.env['hr.payslip'].sudo().search([ | |||
|             ('employee_id', '=', employee_id.id)]) | |||
|         salary_rules = self.env['hr.salary.rule'].sudo().search([]) | |||
|         salary_structures = self.env['hr.payroll.structure'].sudo().search([]) | |||
|         salary_rule_count = len(salary_rules) | |||
|         salary_structure_count = len(salary_structures) | |||
|         emp_leave = len(manager_leave_request) if employee_id.is_manager \ | |||
|             else len(leave_request_count) | |||
|         payslip_count = len(payslips) if not employee_id.is_manager \ | |||
|             else len(self.env['hr.payslip'].sudo().search([])) | |||
|         emp_contracts_count = len(employee_contracts) \ | |||
|             if not employee_id.is_manager else len( | |||
|                 self.env['hr.contract'].sudo().search([])) | |||
|         attendance_today = len(manager_attendance_count) \ | |||
|             if employee_id.is_manager else len(attendance_count) | |||
|         if employee: | |||
|             data = { | |||
|                 'emp_timesheets': attendance_today, | |||
|                 'emp_leave': emp_leave, | |||
|                 'emp_contracts_count': emp_contracts_count, | |||
|                 'payslip_count': payslip_count, | |||
|                 'leave_requests': leave_request_count, | |||
|                 'salary_rule_count': salary_rule_count, | |||
|                 'salary_structure_count': salary_structure_count, | |||
|                 'attendance_state': employee[0]['attendance_state'], | |||
|             } | |||
|             employee[0].update(data) | |||
|         return employee | |||
| 
 | |||
|     def get_work_days_dashboard(self, from_datetime, to_datetime, | |||
|                                 compute_leaves=False, calendar=None, | |||
|                                 domain=None): | |||
|         """To get the work days count""" | |||
|         resource = self.resource_id | |||
|         calendar = calendar or self.resource_calendar_id | |||
| 
 | |||
|         if not from_datetime.tzinfo: | |||
|             from_datetime = from_datetime.replace(tzinfo=utc) | |||
|         if not to_datetime.tzinfo: | |||
|             to_datetime = to_datetime.replace(tzinfo=utc) | |||
|         from_full = from_datetime - timedelta(days=1) | |||
|         to_full = to_datetime + timedelta(days=1) | |||
|         intervals = calendar._attendance_intervals(from_full, to_full, | |||
|                                                    resource) | |||
|         day_total = defaultdict(float) | |||
|         for start, stop, meta in intervals: | |||
|             day_total[start.date()] += (stop - start).total_seconds() / 3600 | |||
|         if compute_leaves: | |||
|             intervals = calendar._work_intervals(from_datetime, to_datetime, | |||
|                                                  resource, domain) | |||
|         else: | |||
|             intervals = calendar._attendance_intervals(from_datetime, | |||
|                                                        to_datetime, resource) | |||
|         day_hours = defaultdict(float) | |||
|         for start, stop, meta in intervals: | |||
|             day_hours[start.date()] += (stop - start).total_seconds() / 3600 | |||
|         days = sum( | |||
|             float_utils.round(ROUNDING_FACTOR * day_hours[day] / day_total[day]) / ROUNDING_FACTOR | |||
|             for day in day_hours | |||
|         ) | |||
|         return days | |||
| 
 | |||
|     @api.model | |||
|     def get_department_leave(self): | |||
|         """return department wise leave details""" | |||
|         month_list = [] | |||
|         graph_result = [] | |||
|         uid = request.session.uid | |||
|         employee = self.env['hr.employee'].sudo().search_read([ | |||
|             ('user_id', '=', uid)], limit=1) | |||
| 
 | |||
|         for i in range(5, -1, -1): | |||
|             last_month = datetime.now() - relativedelta(months=i) | |||
|             text = format(last_month, '%B %Y') | |||
|             month_list.append(text) | |||
|         self.env.cr.execute("""select id, name from hr_department  | |||
|             where active=True""") | |||
|         departments = self.env.cr.dictfetchall() | |||
|         department_list = [x['name'] for x in departments] | |||
|         for month in month_list: | |||
|             leave = {} | |||
|             for dept in departments: | |||
|                 leave[dept['name']] = 0 | |||
|             vals = { | |||
|                 'l_month': month, | |||
|                 'leave': leave | |||
|             } | |||
|             graph_result.append(vals) | |||
|         employee_id = self.env['hr.employee'].browse(employee[0]['id']) | |||
| 
 | |||
|         sql = """ | |||
|             SELECT h.id, h.employee_id,h.department_id | |||
|                  , extract('month' FROM y)::int AS leave_month | |||
|                  , to_char(y, 'Month YYYY') as month_year | |||
|                  , GREATEST(y                    , h.date_from) AS date_from | |||
|                  , LEAST   (y + interval '1 month', h.date_to)   AS date_to | |||
|             FROM  (select * from hr_leave where state = 'validate') h | |||
|                  , generate_series(date_trunc('month', date_from::timestamp) | |||
|                                  , date_trunc('month', date_to::timestamp) | |||
|                                  , interval '1 month') y | |||
|             where date_trunc('month', GREATEST(y , h.date_from)) >=  | |||
|             date_trunc('month', now()) - interval '6 month' and | |||
|             date_trunc('month', GREATEST(y , h.date_from)) <=  | |||
|             date_trunc('month', now()) | |||
|             and h.department_id is not null | |||
|             """ | |||
|         self.env.cr.execute(sql) | |||
|         results = self.env.cr.dictfetchall() | |||
|         leave_lines = [] | |||
|         for line in results: | |||
|             employee = self.browse(line['employee_id']) | |||
|             from_dt = fields.Datetime.from_string(line['date_from']) | |||
|             to_dt = fields.Datetime.from_string(line['date_to']) | |||
|             days = employee.get_work_days_dashboard(from_dt, to_dt) | |||
|             line['days'] = days | |||
|             vals = { | |||
|                 'department': line['department_id'], | |||
|                 'l_month': line['month_year'], | |||
|                 'days': days | |||
|             } | |||
|             leave_lines.append(vals) | |||
|         if leave_lines: | |||
|             df = pd.DataFrame(leave_lines) | |||
|             rf = df.groupby(['l_month', 'department']).sum() | |||
|             result_lines = rf.to_dict('index') | |||
|             for month in month_list: | |||
|                 for line in result_lines: | |||
|                     if month.replace(' ', '') == line[0].replace(' ', ''): | |||
|                         match = list(filter(lambda d: d['l_month'] in [month], | |||
|                                             graph_result))[0]['leave'] | |||
|                         dept_name = self.env['hr.department'].browse( | |||
|                             line[1]).name | |||
|                         if match: | |||
|                             match[dept_name] = result_lines[line]['days'] | |||
|         for result in graph_result: | |||
|             result['l_month'] = result['l_month'].split(' ')[:1][0].strip()[:3] \ | |||
|                                 + " " + result['l_month'].split(' ')[1:2][0] | |||
|         return graph_result, department_list | |||
| 
 | |||
|     @api.model | |||
|     def get_employee_expense(self): | |||
|         """return employee expense details""" | |||
|         month_list = [] | |||
|         graph_result = [] | |||
|         uid = request.session.uid | |||
|         employee = self.env['hr.employee'].sudo().search_read([ | |||
|             ('user_id', '=', uid)], limit=1) | |||
| 
 | |||
|         for i in range(5, -1, -1): | |||
|             last_month = datetime.now() - relativedelta(months=i) | |||
|             text = format(last_month, '%B %Y') | |||
|             month_list.append(text) | |||
|         self.env.cr.execute("""select id, name from hr_employee  | |||
|         where active=True""") | |||
|         departments = self.env.cr.dictfetchall() | |||
|         department_list = [x['name'] for x in departments] | |||
|         for month in month_list: | |||
|             leave = {} | |||
|             for dept in departments: | |||
|                 leave[dept['name']] = 0 | |||
|             vals = { | |||
|                 'l_month': month, | |||
|                 'leave': leave | |||
|             } | |||
|             graph_result.append(vals) | |||
|         employee_id = self.env['hr.employee'].browse(employee[0]['id']) | |||
| 
 | |||
|         sql = """ | |||
|                 SELECT h.id, h.employee_id,h.date, | |||
|                 extract('month' FROM h.date)::int AS leave_month, | |||
|                 to_char(h.date, 'Month YYYY') as month_year  | |||
|                 FROM  (select * from hr_expense where state = 'approved') h  | |||
|                 """ | |||
|         self.env.cr.execute(sql, (employee[0]['id'],)) | |||
| 
 | |||
|         results = self.env.cr.dictfetchall() | |||
|         leave_lines = [] | |||
|         for line in results: | |||
|             employee = self.browse(line['employee_id']) | |||
|             from_dt = fields.Datetime.from_string(line['date']) | |||
|             to_dt = fields.Datetime.from_string(line['date']) | |||
|             days = employee.get_work_days_dashboard(from_dt, to_dt) | |||
|             line['days'] = days | |||
|             vals = { | |||
|                 'department': line['employee_id'], | |||
|                 'l_month': line['month_year'], | |||
|                 'days': days | |||
|             } | |||
|             leave_lines.append(vals) | |||
|         if leave_lines: | |||
|             df = pd.DataFrame(leave_lines) | |||
|             rf = df.groupby(['l_month', 'department']).sum() | |||
|             result_lines = rf.to_dict('index') | |||
|             for month in month_list: | |||
|                 for line in result_lines: | |||
|                     if month.replace(' ', '') == line[0].replace(' ', ''): | |||
|                         match = list(filter(lambda d: d['l_month'] in [month], | |||
|                                             graph_result))[0]['leave'] | |||
|                         dept_name = self.env['hr.department'].browse( | |||
|                             line[1]).name | |||
|                         if match: | |||
|                             match[dept_name] = result_lines[line]['days'] | |||
|         for result in graph_result: | |||
|             result['l_month'] = result['l_month'].split(' ')[:1][0].strip()[:3] \ | |||
|                                 + " " + result['l_month'].split(' ')[1:2][0] | |||
|         return graph_result, department_list | |||
| 
 | |||
|     @api.model | |||
|     def employee_leave_trend(self): | |||
|         """return employee monthly leave details""" | |||
|         leave_lines = [] | |||
|         month_list = [] | |||
|         graph_result = [] | |||
|         for i in range(5, -1, -1): | |||
|             last_month = datetime.now() - relativedelta(months=i) | |||
|             text = format(last_month, '%B %Y') | |||
|             month_list.append(text) | |||
|         uid = request.session.uid | |||
|         employee = self.env['hr.employee'].sudo().search_read([ | |||
|             ('user_id', '=', uid)], limit=1) | |||
|         for month in month_list: | |||
|             vals = { | |||
|                 'l_month': month, | |||
|                 'leave': 0 | |||
|             } | |||
|             graph_result.append(vals) | |||
|         sql = """ | |||
|             SELECT h.id, h.employee_id | |||
|                  , extract('month' FROM y)::int AS leave_month | |||
|                  , to_char(y, 'Month YYYY') as month_year | |||
|                  , GREATEST(y                    , h.date_from) AS date_from | |||
|                  , LEAST   (y + interval '1 month', h.date_to)   AS date_to | |||
|             FROM  (select * from hr_leave where state = 'validate') h | |||
|                  , generate_series(date_trunc('month', date_from::timestamp) | |||
|                                  , date_trunc('month', date_to::timestamp) | |||
|                                  , interval '1 month') y | |||
|             where date_trunc('month', GREATEST(y , h.date_from)) >=  | |||
|             date_trunc('month', now()) - interval '6 month' and | |||
|             date_trunc('month', GREATEST(y , h.date_from)) <=  | |||
|             date_trunc('month', now()) | |||
|             and h.employee_id = %s | |||
|             """ | |||
|         self.env.cr.execute(sql, (employee[0]['id'],)) | |||
|         results = self.env.cr.dictfetchall() | |||
|         for line in results: | |||
|             employee = self.browse(line['employee_id']) | |||
|             from_dt = fields.Datetime.from_string(line['date_from']) | |||
|             to_dt = fields.Datetime.from_string(line['date_to']) | |||
|             days = employee.get_work_days_dashboard(from_dt, to_dt) | |||
|             line['days'] = days | |||
|             vals = { | |||
|                 'l_month': line['month_year'], | |||
|                 'days': days | |||
|             } | |||
|             leave_lines.append(vals) | |||
|         if leave_lines: | |||
|             df = pd.DataFrame(leave_lines) | |||
|             rf = df.groupby(['l_month']).sum() | |||
|             result_lines = rf.to_dict('index') | |||
|             for line in result_lines: | |||
|                 match = list(filter(lambda d: d['l_month'].replace( | |||
|                     ' ', '') == line.replace(' ', ''), graph_result)) | |||
|                 if match: | |||
|                     match[0]['leave'] = result_lines[line]['days'] | |||
|         for result in graph_result: | |||
|             result['l_month'] = result['l_month'].split(' ')[:1][0].strip()[:3] \ | |||
|                                 + " " + result['l_month'].split(' ')[1:2][0] | |||
|         return graph_result | |||
| 
 | |||
| 
 | |||
| class Contract(models.Model): | |||
|     _inherit = 'hr.contract' | |||
| 
 | |||
|     state_label = fields.Char(compute="compute_state_label", store=True) | |||
| 
 | |||
|     @api.depends('state') | |||
|     def compute_state_label(self): | |||
|         """Compute to get the label value of the contract state""" | |||
|         for record in self: | |||
|             record.state_label = dict(self._fields['state'].selection).get( | |||
|                 record.state) | |||
| 
 | |||
|     @api.model | |||
|     def get_employee_contract(self): | |||
|         """return employees contract details""" | |||
|         cr = self._cr | |||
|         cr.execute("""SELECT hr_contract.state_label,count(*)  | |||
|         FROM hr_contract  | |||
|         JOIN hr_employee ON hr_employee.id=hr_contract.employee_id  | |||
|         GROUP BY hr_contract.state_label""") | |||
|         dat = cr.fetchall() | |||
|         data = [] | |||
|         for i in range(0, len(dat)): | |||
|             data.append({'label': dat[i][0], 'value': dat[i][1]}) | |||
|         return data | |||
| 
 | |||
| 
 | |||
| class HrExpense(models.Model): | |||
|     _inherit = 'hr.expense' | |||
| 
 | |||
|     state_label = fields.Char(compute="compute_state_label", store=True) | |||
| 
 | |||
|     @api.depends('state') | |||
|     def compute_state_label(self): | |||
|         """Compute function for the expense state label""" | |||
|         for record in self: | |||
|             record.state_label = dict(self._fields['state'].selection).get( | |||
|                 record.state) | |||
| 
 | |||
|     @api.model | |||
|     def get_employee_expense(self): | |||
|         """return employee expense details""" | |||
|         cr = self._cr | |||
|         month_list = [] | |||
|         approved_trend = [] | |||
| 
 | |||
|         for i in range(11, -1, -1): | |||
|             last_month = datetime.now() - relativedelta(months=i) | |||
|             text = format(last_month, '%B %Y') | |||
|             month_list.append(text) | |||
|         for month in month_list: | |||
|             vals = { | |||
|                 'l_month': month, | |||
|                 'count': 0 | |||
|             } | |||
|             approved_trend.append(vals) | |||
|         uid = request.session.uid | |||
|         employee = self.env['hr.employee'].sudo().search_read([ | |||
|             ('user_id', '=', uid)], limit=1) | |||
|         employee_id = self.env['hr.employee'].browse(employee[0]['id']) | |||
|         if not employee_id.is_manager: | |||
|             sql = ('''select to_char(date, 'Month YYYY') as l_month,  | |||
|                     count(id) from hr_expense | |||
|                     WHERE date BETWEEN CURRENT_DATE - INTERVAL '12 months' | |||
|                     AND CURRENT_DATE + interval '1 month - 1 day'  | |||
|                     AND hr_expense.employee_id = %s | |||
|                     group by l_month''') | |||
|             self.env.cr.execute(sql, (employee[0]['id'],)) | |||
|         else: | |||
|             sql = ('''select to_char(date, 'Month YYYY') as l_month,  | |||
|             count(id) from hr_expense WHERE date  | |||
|             BETWEEN CURRENT_DATE - INTERVAL  | |||
|             '12 months' AND CURRENT_DATE + interval '1 month - 1 day'  | |||
|             group by l_month''') | |||
|             self.env.cr.execute(sql) | |||
|         approved_data = cr.fetchall() | |||
|         for line in approved_data: | |||
|             match = list(filter(lambda d: d['l_month'].replace( | |||
|                 ' ', '') == line[0].replace(' ', ''), approved_trend)) | |||
|             if match: | |||
|                 match[0]['count'] = line[1] | |||
| 
 | |||
|         for expense in approved_trend: | |||
|             expense['l_month'] = expense[ | |||
|                                      'l_month'].split(' ')[:1][0].strip()[:3] | |||
| 
 | |||
|         graph_result = [{ | |||
| 
 | |||
|             'values': approved_trend | |||
|         }] | |||
|         return graph_result | |||
| 
 | |||
| 
 | |||
| class HrAttendance(models.Model): | |||
|     _inherit = 'hr.attendance' | |||
| 
 | |||
|     attendance_date = fields.Date(compute="compute_attendance_date", | |||
|                                   store=True) | |||
| 
 | |||
|     @api.depends('check_in') | |||
|     def compute_attendance_date(self): | |||
|         """Compute function for the attendance date""" | |||
|         for rec in self: | |||
|             if rec.check_in: | |||
|                 rec.attendance_date = rec.check_in.date() | |||
| @ -0,0 +1,75 @@ | |||
| # -*- coding: utf-8 -*- | |||
| ############################################################################# | |||
| # | |||
| #    Cybrosys Technologies Pvt. Ltd. | |||
| # | |||
| #    Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |||
| #    Author:Cybrosys Techno Solutions(odoo@cybrosys.com) | |||
| # | |||
| #    You can modify it under the terms of the GNU AFFERO | |||
| #    GENERAL PUBLIC LICENSE (AGPL v3), Version 3. | |||
| # | |||
| #    This program is distributed in the hope that it will be useful, | |||
| #    but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |||
| #    GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. | |||
| # | |||
| #    You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE | |||
| #    (AGPL v3) along with this program. | |||
| #    If not, see <http://www.gnu.org/licenses/>. | |||
| # | |||
| ############################################################################# | |||
| from odoo import models, fields, api | |||
| 
 | |||
| 
 | |||
| class HrPayroll(models.Model): | |||
|     _inherit = 'hr.payslip' | |||
|     _description = 'Employee Payroll' | |||
| 
 | |||
|     payslip_state = fields.Char(compute="compute_payslip_state", store=True) | |||
| 
 | |||
|     @api.depends('state') | |||
|     def compute_payslip_state(self): | |||
|         """Compute the label value of the payslip state""" | |||
|         for rec in self: | |||
|             rec.payslip_state = dict(self._fields[ | |||
|                                             'state'].selection).get(rec.state) | |||
| 
 | |||
|     @api.model | |||
|     def get_employee_payslips(self): | |||
|         """return employee payslip details""" | |||
|         self._cr.execute( | |||
|             """SELECT hr_payslip.payslip_state,count(*) FROM hr_employee  | |||
|             JOIN hr_payslip ON hr_payslip.employee_id=hr_employee.id  | |||
|             GROUP BY hr_payslip.payslip_state | |||
|             """) | |||
|         dat = self._cr.fetchall() | |||
|         data = [] | |||
|         for i in range(0, len(dat)): | |||
|             if dat[i][0] is not None: | |||
|                 data.append({'label': dat[i][0], 'value': dat[i][1]}) | |||
|         return data | |||
| 
 | |||
| 
 | |||
| class HrLeave(models.Model): | |||
|     _inherit = 'hr.leave' | |||
|     state_string = fields.Char(compute="compute_state_string", store=True) | |||
| 
 | |||
|     @api.depends('state') | |||
|     def compute_state_string(self): | |||
|         """Compute the label of the leave state""" | |||
|         for rec in self: | |||
|             rec.state_string = dict(self._fields[ | |||
|                                            'state'].selection).get(rec.state) | |||
| 
 | |||
|     @api.model | |||
|     def get_employee_time_off(self): | |||
|         """return employee time off details""" | |||
|         self._cr.execute("""SELECT hr_leave.state_string, count(*)  | |||
|         FROM hr_employee JOIN hr_leave ON hr_leave.employee_id=hr_employee.id  | |||
|         GROUP BY hr_leave.state_string""") | |||
|         dat = self._cr.fetchall() | |||
|         data = [] | |||
|         for i in range(0, len(dat)): | |||
|             data.append({'label': dat[i][0], 'value': dat[i][1]}) | |||
|         return data | |||
| After Width: | Height: | Size: 3.6 KiB | 
| After Width: | Height: | Size: 310 B | 
| After Width: | Height: | Size: 1.3 KiB | 
| After Width: | Height: | Size: 1.4 KiB | 
| After Width: | Height: | Size: 576 B | 
| After Width: | Height: | Size: 733 B | 
| After Width: | Height: | Size: 911 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 673 B | 
| After Width: | Height: | Size: 878 B | 
| After Width: | Height: | Size: 653 B | 
| After Width: | Height: | Size: 905 B | 
| After Width: | Height: | Size: 839 B | 
| After Width: | Height: | Size: 427 B | 
| After Width: | Height: | Size: 627 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 988 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 51 KiB | 
| After Width: | Height: | Size: 58 KiB | 
| After Width: | Height: | Size: 58 KiB | 
| After Width: | Height: | Size: 59 KiB | 
| After Width: | Height: | Size: 59 KiB | 
| After Width: | Height: | Size: 58 KiB | 
| After Width: | Height: | Size: 50 KiB | 
| After Width: | Height: | Size: 87 KiB | 
| After Width: | Height: | Size: 87 KiB | 
| After Width: | Height: | Size: 102 KiB | 
| After Width: | Height: | Size: 42 KiB | 
| After Width: | Height: | Size: 99 KiB | 
| After Width: | Height: | Size: 33 KiB | 
| After Width: | Height: | Size: 78 KiB | 
| After Width: | Height: | Size: 58 KiB | 
| After Width: | Height: | Size: 17 KiB | 
| @ -0,0 +1,657 @@ | |||
| <div class="container" style="padding: 4rem 1.5rem !important"> | |||
|     <div class="row" style="height: 900px !important;"> | |||
|         <div class="col-sm-12 col-md-12 col-lg-12" | |||
|             style="padding: 4rem 1rem !important; background-color: #714B67 !important; height: 600px !important; border-radius: 20px !important;"> | |||
|             <h1 | |||
|                 style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #FFFFFF !important;  font-size: 3.5rem !important; text-align: center !important;"> | |||
|                 Hr Payroll Dashboard</h1> | |||
|             <p | |||
|                 style="font-family: 'Montserrat', sans-serif !important; font-weight: 300 !important; color: #FFFFFF !important;  font-size: 1.4rem !important; text-align: center !important;"> | |||
|                 Detailed Dashboard View for Payroll Module | |||
|             </p> | |||
|             <img src="./assets/screenshots/hero.png" class="img-responsive" width="100%" height="auto" /> | |||
|         </div> | |||
|     </div> | |||
| 
 | |||
|     <div class="row"> | |||
|         <div class="col-md-12" style="border-bottom: 1px solid #d5d5d5 !important; margin-bottom: 2rem !important"> | |||
|             <h2 | |||
|                 style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;"> | |||
|                 <i class="fa fa-compass mr-2"></i>Explore this module | |||
|             </h2> | |||
|         </div> | |||
|         <div class="col-md-6"> | |||
|             <a href="#overview" style="text-decoration: none !important;"> | |||
|                 <div class="row" | |||
|                     style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;"> | |||
|                     <div class="col-8"> | |||
|                         <h3 | |||
|                             style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;"> | |||
|                             Overview</h3> | |||
|                         <p | |||
|                             style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;"> | |||
|                             Learn more about this module</p> | |||
|                     </div> | |||
|                     <div class="col-4 text-right d-flex justify-content-end align-items-center"> | |||
|                         <i class="fa fa-chevron-right" style="color: #714B67 !important;"></i> | |||
|                     </div> | |||
|                 </div> | |||
|             </a> | |||
|         </div> | |||
| 
 | |||
|         <div class="col-md-6"> | |||
|             <a href="#features" style="text-decoration: none !important;"> | |||
|                 <div class="row" | |||
|                     style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;"> | |||
|                     <div class="col-8"> | |||
|                         <h3 | |||
|                             style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;"> | |||
|                             Features</h3> | |||
|                         <p | |||
|                             style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;"> | |||
|                             View features of this module</p> | |||
|                     </div> | |||
|                     <div class="col-4 text-right d-flex justify-content-end align-items-center"> | |||
|                         <i class="fa fa-chevron-right" style="color: #714B67 !important;"></i> | |||
|                     </div> | |||
|                 </div> | |||
|             </a> | |||
|         </div> | |||
|         <div class="col-md-6"> | |||
|             <a href="#screenshots" style="text-decoration: none !important;"> | |||
|                 <div class="row" | |||
|                     style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;"> | |||
|                     <div class="col-8"> | |||
|                         <h3 | |||
|                             style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;"> | |||
|                             Screenshots</h3> | |||
|                         <p | |||
|                             style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;"> | |||
|                             See key screenshots of this module</p> | |||
|                     </div> | |||
|                     <div class="col-4 text-right d-flex justify-content-end align-items-center"> | |||
|                         <i class="fa fa-chevron-right" style="color: #714B67 !important;"></i> | |||
|                     </div> | |||
|                 </div> | |||
|             </a> | |||
|         </div> | |||
| 
 | |||
|     </div> | |||
| 
 | |||
| 
 | |||
|     <div class="row" id="overview"> | |||
|         <div class="col-md-12" style="border-bottom: 1px solid #d5d5d5 !important; margin: 2rem 0 !important"> | |||
|             <h2 | |||
|                 style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;"> | |||
|                 <i class="fa fa-pie-chart mr-2"></i>Overview | |||
|             </h2> | |||
|         </div> | |||
| 
 | |||
|         <div class="col-mg-12 pl-3"> | |||
|             <p | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important; line-height: 30px !important;"> | |||
|                 This module helps you to see the Overview of Payroll, here You can see the details of Attendance, Leaves, Payslip | |||
|                 contracts, etc.</p> | |||
| 
 | |||
|         </div> | |||
|     </div> | |||
| 
 | |||
|     <div class="row" id="features"> | |||
|         <div class="col-md-12" style="border-bottom: 1px solid #d5d5d5 !important; margin: 2rem 0 !important"> | |||
|             <h2 | |||
|                 style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;"> | |||
|                 <i class="fa fa-star mr-2"></i>Features | |||
|             </h2> | |||
|         </div> | |||
| 
 | |||
|         <div class="col-md-6 pl-3 py-3 d-flex"> | |||
|             <div> | |||
|                 <img src="assets/icons/check.png"> | |||
|             </div> | |||
|             <div> | |||
|                 <h4 | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                     Payslip Details</h4> | |||
|                 <p | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                     Shows payslip count and analysis.</p> | |||
|             </div> | |||
|         </div> | |||
| 
 | |||
|         <div class="col-md-6 pl-3 py-3 d-flex"> | |||
|             <div> | |||
|                 <img src="assets/icons/check.png"> | |||
|             </div> | |||
|             <div> | |||
|                 <h4 | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                     Contract Details</h4> | |||
|                 <p | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                     Shows Contract count and analysis.</p> | |||
|             </div> | |||
|         </div> | |||
| 
 | |||
|         <div class="col-md-6 pl-3 py-3 d-flex"> | |||
|             <div> | |||
|                 <img src="assets/icons/check.png"> | |||
|             </div> | |||
|             <div> | |||
|                 <h4 | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                     Leave Request Details</h4> | |||
|                 <p | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                     Shows Leave request details and analysis.</p> | |||
|             </div> | |||
|         </div> | |||
|         <div class="col-md-6 pl-3 py-3 d-flex"> | |||
|             <div> | |||
|                 <img src="assets/icons/check.png"> | |||
|             </div> | |||
|             <div> | |||
|                 <h4 | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                     Attendance Details</h4> | |||
|                 <p | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                     shows attendance details.</p> | |||
|             </div> | |||
|         </div> | |||
|         <div class="col-md-6 pl-3 py-3 d-flex"> | |||
|             <div> | |||
|                 <img src="assets/icons/check.png"> | |||
|             </div> | |||
|             <div> | |||
|                 <h4 | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                     Salary Rules Details</h4> | |||
|                 <p | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                     shows salary rules details.</p> | |||
|             </div> | |||
|         </div> | |||
|         <div class="col-md-6 pl-3 py-3 d-flex"> | |||
|             <div> | |||
|                 <img src="assets/icons/check.png"> | |||
|             </div> | |||
|             <div> | |||
|                 <h4 | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                     Salary Structures Details</h4> | |||
|                 <p | |||
|                     style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                     shows salary structures details.</p> | |||
|             </div> | |||
|         </div> | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
| 
 | |||
|     </div> | |||
| 
 | |||
|     <div class="row" id="screenshots"> | |||
|         <div class="col-md-12" style="border-bottom: 1px solid #d5d5d5 !important; margin: 2rem 0 !important"> | |||
|             <h2 | |||
|                 style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;"> | |||
|                 <i class="fa fa-image mr-2"></i>Screenshots | |||
|             </h2> | |||
|         </div> | |||
| 
 | |||
|         <div class="col-lg-12 my-2"> | |||
|             <h4 class="mt-2" | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                 Open Payroll</h4> | |||
|             <p | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                 After installation, open Payroll Module</p> | |||
| 
 | |||
|             <img src="hr_payroll_dashboard/static/description/assets/screenshots/dashboard(1).png" class="img-responsive img-thumbnail border" width="100%" | |||
|                 height="auto" /> | |||
|         </div> | |||
| 
 | |||
|         <div class="col-lg-12 my-2"> | |||
|             <h4 class="mt-2" | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                 Payslip, Attendance, Contract, Leaves Details.</h4> | |||
|             <p | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                Attendance, Leaves, Payslips, Contracts, Salary Rules, and Salary structures details of employees is displayed in dashboard. | |||
|                Each card is clickable. | |||
|             </p> | |||
| 
 | |||
|             <img src="assets/screenshots/dashboard(2.2).png" class="img-responsive img-thumbnail border" width="100%" | |||
|                 height="auto" /> | |||
|         </div> | |||
| 
 | |||
| 
 | |||
| 
 | |||
|         <div class="col-lg-12 my-2"> | |||
|             <h4 class="mt-2" | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                 Monthly Expense Analysis</h4> | |||
|             <p | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                 Monthly expense can be analysed from the chart.</p> | |||
|             <img src="assets/screenshots/dashboard(3.2).png" class="img-responsive img-thumbnail border" width="100%" | |||
|                 height="auto" /> | |||
|         </div> | |||
|         <div class="col-lg-12 my-2"> | |||
|             <h4 class="mt-2" | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                 My Leave Analysis</h4> | |||
|             <p | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                 User Leave Analysis.</p> | |||
|             <img src="assets/screenshots/dashboard(4.2).png" class="img-responsive img-thumbnail border" width="100%" | |||
|                 height="auto" /> | |||
|         </div> | |||
|         <div class="col-lg-12 my-2"> | |||
|             <h4 class="mt-2" | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                 Monthly Leave Analysis</h4> | |||
|             <p | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                 Monthly leave analysis can bbe done.</p> | |||
|             <img src="assets/screenshots/dashboard(5.2).png" class="img-responsive img-thumbnail border" width="100%" | |||
|                 height="auto" /> | |||
|         </div> | |||
|         <div class="col-lg-12 my-2"> | |||
|             <h4 class="mt-2" | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> | |||
|                 Details in Charts</h4> | |||
|             <p | |||
|                 style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> | |||
|                 Payslip, Contract and Time off analysis can be done  from the chart.</p> | |||
|             <img src="assets/screenshots/dashboard(4).png" class="img-responsive img-thumbnail border" width="100%" | |||
|                 height="auto" /> | |||
|         </div> | |||
| 
 | |||
| 
 | |||
| 
 | |||
|     </div> | |||
| 
 | |||
|     <!-- SUGGESTED PRODUCTS --> | |||
|     <div class="row"> | |||
|         <div class="col-lg-12 d-flex flex-column justify-content-center" | |||
|             style="text-align: center; padding: 2.5rem 1rem !important;"> | |||
|             <h2 style="color: #212529 !important;">Suggested Products</h2> | |||
|             <hr | |||
|                 style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" /> | |||
| 
 | |||
|             <div id="demo1" class="row carousel slide" data-ride="carousel"> | |||
|                 <!-- The slideshow --> | |||
|                 <div class="carousel-inner"> | |||
|                     <div class="carousel-item active" style="min-height:0px"> | |||
|                         <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left"> | |||
|                             <a href="https://apps.odoo.com/apps/modules/14.0/dynamic_accounts_report/" target="_blank"> | |||
|                                 <div style="border-radius:10px"> | |||
|                                     <img class="img img-responsive center-block" | |||
|                                         style="border-top-left-radius:10px; border-top-right-radius:10px" | |||
|                                         src="./assets/modules/dynamic_image.png"> | |||
|                                 </div> | |||
|                             </a> | |||
|                         </div> | |||
|                         <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left"> | |||
|                             <a href="https://apps.odoo.com/apps/modules/14.0/product_return_pos/" target="_blank"> | |||
|                                 <div style="border-radius:10px"> | |||
|                                     <img class="img img-responsive center-block" | |||
|                                         style="border-top-left-radius:10px; border-top-right-radius:10px" | |||
|                                         src="./assets/modules/product_return_image.png"> | |||
|                                 </div> | |||
|                             </a> | |||
|                         </div> | |||
|                         <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left"> | |||
|                             <a href="https://apps.odoo.com/apps/modules/14.0/product_approval_management/" | |||
|                                 target="_blank"> | |||
|                                 <div style="border-radius:10px"> | |||
|                                     <img class="img img-responsive center-block" | |||
|                                         style="border-top-left-radius:10px; border-top-right-radius:10px" | |||
|                                         src="./assets/modules/approval_image.png"> | |||
|                                 </div> | |||
|                             </a> | |||
|                         </div> | |||
|                     </div> | |||
|                     <div class="carousel-item" style="min-height:0px"> | |||
|                         <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left"> | |||
|                             <a href="https://apps.odoo.com/apps/modules/14.0/mrp_work_order_print/" target="_blank"> | |||
|                                 <div style="border-radius:10px"> | |||
|                                     <img class="img img-responsive center-block" | |||
|                                         style="border-top-left-radius:10px; border-top-right-radius:10px" | |||
|                                         src="./assets/modules/print_image.png"> | |||
|                                 </div> | |||
|                             </a> | |||
|                         </div> | |||
|                         <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left"> | |||
|                             <a href="https://apps.odoo.com/apps/modules/14.0/list_view_sticky_header/" target="_blank"> | |||
|                                 <div style="border-radius:10px"> | |||
|                                     <img class="img img-responsive center-block" | |||
|                                         style="border-top-left-radius:10px; border-top-right-radius:10px" | |||
|                                         src="./assets/modules/list_view_image.png"> | |||
|                                 </div> | |||
|                             </a> | |||
|                         </div> | |||
|                         <div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left"> | |||
|                             <a href="https://apps.odoo.com/apps/modules/14.0/multiple_reference_per_product/" | |||
|                                 target="_blank"> | |||
|                                 <div style="border-radius:10px"> | |||
|                                     <img class="img img-responsive center-block" | |||
|                                         style="border-top-left-radius:10px; border-top-right-radius:10px" | |||
|                                         src="./assets/modules/multiple_ref_image.png"> | |||
|                                 </div> | |||
|                             </a> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|                 <!-- Left and right controls --> | |||
|                 <a class="carousel-control-prev" href="#demo1" data-slide="prev" | |||
|                     style="left:-25px;width: 35px;color: #000;"> <span class="carousel-control-prev-icon"><i | |||
|                             class="fa fa-chevron-left" style="font-size:24px"></i></span> </a> <a | |||
|                     class="carousel-control-next" href="#demo1" data-slide="next" | |||
|                     style="right:-25px;width: 35px;color: #000;"> | |||
|                     <span class="carousel-control-next-icon"><i class="fa fa-chevron-right" | |||
|                             style="font-size:24px"></i></span> | |||
|                 </a> | |||
|             </div> | |||
|         </div> | |||
|     </div> | |||
|     <!-- END OF SUGGESTED PRODUCTS --> | |||
| 
 | |||
|     <!-- OUR SERVICES --> | |||
|     <section class="container" style="margin-top: 6rem !important;"> | |||
|         <div class="row"> | |||
|             <div class="col-lg-12 d-flex flex-column justify-content-center align-items-center"> | |||
|                 <h2 style="color: #212529 !important;">Our Services</h2> | |||
|                 <hr | |||
|                     style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" /> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/cogs.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Odoo | |||
|                     Customization</h6> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/wrench.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Odoo | |||
|                     Implementation</h6> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/lifebuoy.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Odoo | |||
|                     Support</h6> | |||
|             </div> | |||
| 
 | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/user.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Hire | |||
|                     Odoo | |||
|                     Developer</h6> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #54a0ff  !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/puzzle.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Odoo | |||
|                     Integration</h6> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/update.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Odoo | |||
|                     Migration</h6> | |||
|             </div> | |||
| 
 | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/consultation.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Odoo | |||
|                     Consultancy</h6> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/training.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Odoo | |||
|                     Implementation</h6> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> | |||
|                 <div class="d-flex justify-content-center align-items-center mx-3 my-3" | |||
|                     style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;"> | |||
|                     <img src="assets/icons/license.png" class="img-responsive" height="48px" width="48px"> | |||
|                 </div> | |||
|                 <h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> | |||
|                     Odoo | |||
|                     Licensing Consultancy</h6> | |||
|             </div> | |||
|         </div> | |||
|     </section> | |||
|     <!-- END OF END OF OUR SERVICES --> | |||
| 
 | |||
|     <!-- OUR INDUSTRIES --> | |||
|     <section class="container" style="margin-top: 6rem !important;"> | |||
|         <div class="row"> | |||
|             <div class="col-lg-12 d-flex flex-column justify-content-center align-items-center"> | |||
|                 <h2 style="color: #212529 !important;">Our Industries</h2> | |||
|                 <hr | |||
|                     style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" /> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-3"> | |||
|                 <div class="my-4 d-flex flex-column justify-content-center" | |||
|                     style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;"> | |||
|                     <img src="./assets/icons/trading-black.png" class="img-responsive mb-3" height="48px" width="48px"> | |||
|                     <h5 | |||
|                         style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> | |||
|                         Trading | |||
|                     </h5> | |||
|                     <p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> | |||
|                         Easily procure | |||
|                         and | |||
|                         sell your products</p> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-3"> | |||
|                 <div class="my-4 d-flex flex-column justify-content-center" | |||
|                     style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;"> | |||
|                     <img src="./assets/icons/pos-black.png" class="img-responsive mb-3" height="48px" width="48px"> | |||
|                     <h5 | |||
|                         style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> | |||
|                         POS | |||
|                     </h5> | |||
|                     <p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> | |||
|                         Easy | |||
|                         configuration | |||
|                         and convivial experience</p> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-3"> | |||
|                 <div class="my-4 d-flex flex-column justify-content-center" | |||
|                     style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;"> | |||
|                     <img src="./assets/icons/education-black.png" class="img-responsive mb-3" height="48px" | |||
|                         width="48px"> | |||
|                     <h5 | |||
|                         style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> | |||
|                         Education | |||
|                     </h5> | |||
|                     <p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> | |||
|                         A platform for | |||
|                         educational management</p> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-3"> | |||
|                 <div class="my-4 d-flex flex-column justify-content-center" | |||
|                     style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;"> | |||
|                     <img src="./assets/icons/manufacturing-black.png" class="img-responsive mb-3" height="48px" | |||
|                         width="48px"> | |||
|                     <h5 | |||
|                         style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> | |||
|                         Manufacturing | |||
|                     </h5> | |||
|                     <p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> | |||
|                         Plan, track and | |||
|                         schedule your operations</p> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-3"> | |||
|                 <div class="my-4 d-flex flex-column justify-content-center" | |||
|                     style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;"> | |||
|                     <img src="./assets/icons/ecom-black.png" class="img-responsive mb-3" height="48px" width="48px"> | |||
|                     <h5 | |||
|                         style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> | |||
|                         E-commerce & Website | |||
|                     </h5> | |||
|                     <p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> | |||
|                         Mobile | |||
|                         friendly, | |||
|                         awe-inspiring product pages</p> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-3"> | |||
|                 <div class="my-4 d-flex flex-column justify-content-center" | |||
|                     style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;"> | |||
|                     <img src="./assets/icons/service-black.png" class="img-responsive mb-3" height="48px" width="48px"> | |||
|                     <h5 | |||
|                         style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> | |||
|                         Service Management | |||
|                     </h5> | |||
|                     <p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> | |||
|                         Keep track of | |||
|                         services and invoice</p> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-3"> | |||
|                 <div class="my-4 d-flex flex-column justify-content-center" | |||
|                     style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;"> | |||
|                     <img src="./assets/icons/restaurant-black.png" class="img-responsive mb-3" height="48px" | |||
|                         width="48px"> | |||
|                     <h5 | |||
|                         style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> | |||
|                         Restaurant | |||
|                     </h5> | |||
|                     <p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> | |||
|                         Run your bar or | |||
|                         restaurant methodically</p> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-lg-3"> | |||
|                 <div class="my-4 d-flex flex-column justify-content-center" | |||
|                     style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;"> | |||
|                     <img src="./assets/icons/hotel-black.png" class="img-responsive mb-3" height="48px" width="48px"> | |||
|                     <h5 | |||
|                         style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> | |||
|                         Hotel Management | |||
|                     </h5> | |||
|                     <p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> | |||
|                         An | |||
|                         all-inclusive | |||
|                         hotel management application</p> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|         </div> | |||
|     </section> | |||
| 
 | |||
|     <!-- END OF END OF OUR INDUSTRIES --> | |||
| 
 | |||
|     <!-- FOOTER --> | |||
|     <!-- Footer Section --> | |||
|     <section class="container" style="margin: 5rem auto 2rem;"> | |||
|         <div class="row" style="max-width:1540px;"> | |||
|             <div class="col-lg-12 d-flex flex-column justify-content-center align-items-center"> | |||
|                 <h2 style="color: #212529 !important;">Need Help?</h2> | |||
|                 <hr | |||
|                     style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;" /> | |||
|             </div> | |||
|         </div> | |||
| 
 | |||
|         <!-- Contact Cards --> | |||
|         <div class="row d-flex justify-content-center align-items-center" | |||
|             style="max-width:1540px; margin: 0 auto 2rem auto;"> | |||
| 
 | |||
|             <div class="col-lg-12" style="padding: 0rem 3rem 2rem; border-radius: 10px; margin-right: 3rem; "> | |||
| 
 | |||
|                 <div class="row mt-4"> | |||
|                     <div class="col-lg-4"> | |||
|                         <a href="mailto:odoo@cybrosys.com" target="_blank" class="btn btn-block mb-2 deep_hover" | |||
|                             style="text-decoration: none;  background-color: #4d4d4d; color: #FFF;  border-radius: 4px;"><i | |||
|                                 class="fa fa-envelope mr-2"></i>odoo@cybrosys.com</a> | |||
|                     </div> | |||
|                     <div class="col-lg-4"> | |||
|                         <a href="https://api.WhatsApp.com/send?phone=918606827707" target="_blank" | |||
|                             class="btn btn-block mb-2 deep_hover" | |||
|                             style="text-decoration: none;  background-color: #25D366; color: #FFF;  border-radius: 4px;"><i | |||
|                                 class="fa fa-WhatsApp mr-2"></i>WhatsApp</a> | |||
|                     </div> | |||
|                     <div class="col-lg-4"> | |||
|                         <a href="mailto:info@cybrosys.com" target="_blank" class="btn btn-block deep_hover" | |||
|                             style="text-decoration: none; background-color: #4d4d4d; color: #FFF;  border-radius: 4px;"><i | |||
|                                 class="fa fa-envelope mr-2"></i>info@cybrosys.com</a> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|         </div> | |||
|         <!-- End of Contact Cards --> | |||
|     </section> | |||
|     <!-- Footer --> | |||
|     <section class="oe_container" style="padding: 2rem 3rem 1rem;"> | |||
|         <div class="row" style="max-width:1540px; margin: 0 auto; margin-right: 3rem; "> | |||
|             <!-- Logo --> | |||
|             <div class="col-lg-12 d-flex justify-content-center align-items-center" style="margin-top: 3rem;"> | |||
|                 <img src="https://www.cybrosys.com/images/logo.png" width="200px" height="auto" /> | |||
|             </div> | |||
|             <!-- End of Logo --> | |||
|             <div class="col-lg-12"> | |||
|                 <hr | |||
|                     style="margin-top: 3rem;background: linear-gradient(90deg, rgba(2,0,36,0) 0%, rgba(229,229,229,1) 33%, rgba(229,229,229,1) 58%, rgba(0,212,255,0) 100%); height: 2px; border-style: none;"> | |||
|                 <!-- End of Footer Section --> | |||
|             </div> | |||
|         </div> | |||
|     </section> | |||
|     <!-- END OF FOOTER --> | |||
| 
 | |||
| </div> | |||
| @ -0,0 +1,426 @@ | |||
| .nvd3 .nv-axis { | |||
|     pointer-events:none; | |||
|     opacity: 1; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-axis path { | |||
|     fill: none; | |||
|     stroke: #000; | |||
|     stroke-opacity: .75; | |||
|     shape-rendering: crispEdges; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-axis path.domain { | |||
|     stroke-opacity: .75; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-axis.nv-x path.domain { | |||
|     stroke-opacity: 0; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-axis line { | |||
|     fill: none; | |||
|     stroke: #e5e5e5; | |||
|     shape-rendering: crispEdges; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-axis .zero line, | |||
|     /*this selector may not be necessary*/ .nvd3 .nv-axis line.zero { | |||
|     stroke-opacity: .75; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-axis .nv-axisMaxMin text { | |||
|     font-weight: bold; | |||
| } | |||
| 
 | |||
| .nvd3 .x  .nv-axis .nv-axisMaxMin text, | |||
| .nvd3 .x2 .nv-axis .nv-axisMaxMin text, | |||
| .nvd3 .x3 .nv-axis .nv-axisMaxMin text { | |||
|     text-anchor: middle | |||
| } | |||
| 
 | |||
| .nvd3 .nv-axis.nv-disabled { | |||
|     opacity: 0; | |||
| } | |||
| 
 | |||
| /* scatter */ | |||
| .nvd3 .nv-groups .nv-point.hover { | |||
|     stroke-width: 20px; | |||
|     stroke-opacity: .5; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-scatter .nv-point.hover { | |||
|     fill-opacity: 1; | |||
| } | |||
| .nv-noninteractive { | |||
|     pointer-events: none; | |||
| } | |||
| 
 | |||
| .nv-distx, .nv-disty { | |||
|     pointer-events: none; | |||
| } | |||
| 
 | |||
| 
 | |||
| 
 | |||
| .nvtooltip { | |||
|     position: absolute; | |||
|     background-color: rgba(255,255,255,1.0); | |||
|     color: rgba(0,0,0,1.0); | |||
|     padding: 1px; | |||
|     border: 1px solid rgba(0,0,0,.2); | |||
|     z-index: 10000; | |||
|     display: block; | |||
| 
 | |||
|     font-family: Arial; | |||
|     font-size: 13px; | |||
|     text-align: left; | |||
|     pointer-events: none; | |||
| 
 | |||
|     white-space: nowrap; | |||
| 
 | |||
|     -webkit-touch-callout: none; | |||
|     -webkit-user-select: none; | |||
|     -khtml-user-select: none; | |||
|     -moz-user-select: none; | |||
|     -ms-user-select: none; | |||
|     user-select: none; | |||
| } | |||
| 
 | |||
| .nvtooltip { | |||
|     background: rgba(255,255,255, 0.8); | |||
|     border: 1px solid rgba(0,0,0,0.5); | |||
|     border-radius: 4px; | |||
| } | |||
| 
 | |||
| /*Give tooltips that old fade in transition by | |||
|     putting a "with-transitions" class on the container div. | |||
| */ | |||
| .nvtooltip.with-transitions, .with-transitions .nvtooltip { | |||
|     transition: opacity 50ms linear; | |||
|     -moz-transition: opacity 50ms linear; | |||
|     -webkit-transition: opacity 50ms linear; | |||
| 
 | |||
|     transition-delay: 200ms; | |||
|     -moz-transition-delay: 200ms; | |||
|     -webkit-transition-delay: 200ms; | |||
| } | |||
| 
 | |||
| .nvtooltip.x-nvtooltip, | |||
| .nvtooltip.y-nvtooltip { | |||
|     padding: 8px; | |||
| } | |||
| 
 | |||
| .nvtooltip h3 { | |||
|     margin: 0; | |||
|     padding: 4px 14px; | |||
|     line-height: 18px; | |||
|     font-weight: normal; | |||
|     background-color: rgba(247,247,247,0.75); | |||
|     color: rgba(0,0,0,1.0); | |||
|     text-align: center; | |||
| 
 | |||
|     border-bottom: 1px solid #ebebeb; | |||
| 
 | |||
|     -webkit-border-radius: 5px 5px 0 0; | |||
|     -moz-border-radius: 5px 5px 0 0; | |||
|     border-radius: 5px 5px 0 0; | |||
| } | |||
| 
 | |||
| .nvtooltip p { | |||
|     margin: 0; | |||
|     padding: 5px 14px; | |||
|     text-align: center; | |||
| } | |||
| 
 | |||
| .nvtooltip span { | |||
|     display: inline-block; | |||
|     margin: 2px 0; | |||
| } | |||
| 
 | |||
| .nvtooltip table { | |||
|     margin: 6px; | |||
|     border-spacing:0; | |||
| } | |||
| 
 | |||
| 
 | |||
| .nvtooltip table td { | |||
|     padding: 2px 9px 2px 0; | |||
|     vertical-align: middle; | |||
| } | |||
| 
 | |||
| .nvtooltip table td.key { | |||
|     font-weight: normal; | |||
| } | |||
| .nvtooltip table td.key.total { | |||
|     font-weight: bold; | |||
| } | |||
| .nvtooltip table td.value { | |||
|     text-align: right; | |||
|     font-weight: bold; | |||
| } | |||
| 
 | |||
| .nvtooltip table tr.highlight td { | |||
|     padding: 1px 9px 1px 0; | |||
|     border-bottom-style: solid; | |||
|     border-bottom-width: 1px; | |||
|     border-top-style: solid; | |||
|     border-top-width: 1px; | |||
| } | |||
| 
 | |||
| .nvtooltip table td.legend-color-guide div { | |||
|     width: 8px; | |||
|     height: 8px; | |||
|     vertical-align: middle; | |||
| } | |||
| 
 | |||
| .nvtooltip table td.legend-color-guide div { | |||
|     width: 12px; | |||
|     height: 12px; | |||
|     border: 1px solid #999; | |||
| } | |||
| 
 | |||
| .nvtooltip .footer { | |||
|     padding: 3px; | |||
|     text-align: center; | |||
| } | |||
| 
 | |||
| .nvtooltip-pending-removal { | |||
|     pointer-events: none; | |||
|     display: none; | |||
| } | |||
| 
 | |||
| 
 | |||
| /**** | |||
| Interactive Layer | |||
| */ | |||
| .nvd3 .nv-interactiveGuideLine { | |||
|     pointer-events:none; | |||
| } | |||
| .nvd3 line.nv-guideline { | |||
|     stroke: #ccc; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-bars rect { | |||
|     fill-opacity: .75; | |||
| 
 | |||
|     transition: fill-opacity 250ms linear; | |||
|     -moz-transition: fill-opacity 250ms linear; | |||
|     -webkit-transition: fill-opacity 250ms linear; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-bars rect.hover { | |||
|     fill-opacity: 1; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-bars .hover rect { | |||
|     fill: lightblue; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-bars text { | |||
|     fill: rgba(0,0,0,0); | |||
| } | |||
| 
 | |||
| .nvd3 .nv-bars .hover text { | |||
|     fill: rgba(0,0,0,1); | |||
| } | |||
| 
 | |||
| .nvd3 .nv-multibar .nv-groups rect, | |||
| .nvd3 .nv-multibarHorizontal .nv-groups rect, | |||
| .nvd3 .nv-discretebar .nv-groups rect { | |||
|     stroke-opacity: 0; | |||
| 
 | |||
|     transition: fill-opacity 250ms linear; | |||
|     -moz-transition: fill-opacity 250ms linear; | |||
|     -webkit-transition: fill-opacity 250ms linear; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-multibar .nv-groups rect:hover, | |||
| .nvd3 .nv-multibarHorizontal .nv-groups rect:hover, | |||
| .nvd3 .nv-candlestickBar .nv-ticks rect:hover, | |||
| .nvd3 .nv-discretebar .nv-groups rect:hover { | |||
|     fill-opacity: 1; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-discretebar .nv-groups text, | |||
| .nvd3 .nv-multibarHorizontal .nv-groups text { | |||
|     font-weight: bold; | |||
|     fill: rgba(0,0,0,1); | |||
|     stroke: rgba(0,0,0,0); | |||
| } | |||
| 
 | |||
| .nvd3 .nv-groups path.nv-line { | |||
|     fill: none; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-groups path.nv-area { | |||
|     stroke: none; | |||
| } | |||
| 
 | |||
| .nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point { | |||
|     fill-opacity: 0; | |||
|     stroke-opacity: 0; | |||
| } | |||
| 
 | |||
| .nvd3.nv-scatter.nv-single-point .nv-groups .nv-point { | |||
|     fill-opacity: .5 !important; | |||
|     stroke-opacity: .5 !important; | |||
| } | |||
| 
 | |||
| 
 | |||
| .with-transitions .nvd3 .nv-groups .nv-point { | |||
|     transition: stroke-width 250ms linear, stroke-opacity 250ms linear; | |||
|     -moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear; | |||
|     -webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear; | |||
| 
 | |||
| } | |||
| 
 | |||
| .nvd3.nv-scatter .nv-groups .nv-point.hover, | |||
| .nvd3 .nv-groups .nv-point.hover { | |||
|     stroke-width: 7px; | |||
|     fill-opacity: .95 !important; | |||
|     stroke-opacity: .95 !important; | |||
| } | |||
| 
 | |||
| 
 | |||
| .nvd3 .nv-point-paths path { | |||
|     stroke: #aaa; | |||
|     stroke-opacity: 0; | |||
|     fill: #eee; | |||
|     fill-opacity: 0; | |||
| } | |||
| 
 | |||
| 
 | |||
| 
 | |||
| .nvd3 .nv-indexLine { | |||
|     cursor: ew-resize; | |||
| } | |||
| 
 | |||
| /******************** | |||
|  * SVG CSS | |||
|  */ | |||
| 
 | |||
| /******************** | |||
|   Default CSS for an svg element nvd3 used | |||
| */ | |||
| svg.nvd3-svg { | |||
|     -webkit-touch-callout: none; | |||
|     -webkit-user-select: none; | |||
|     -khtml-user-select: none; | |||
|     -ms-user-select: none; | |||
|     -moz-user-select: none; | |||
|     user-select: none; | |||
|     display: block; | |||
|     width:100%; | |||
|     height:100%; | |||
| } | |||
| 
 | |||
| /******************** | |||
|   Box shadow and border radius styling | |||
| */ | |||
| .nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip { | |||
|     -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); | |||
|     -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); | |||
|     box-shadow: 0 5px 10px rgba(0,0,0,.2); | |||
| 
 | |||
|     -webkit-border-radius: 5px; | |||
|     -moz-border-radius: 5px; | |||
|     border-radius: 5px; | |||
| } | |||
| 
 | |||
| 
 | |||
| .nvd3 text { | |||
|     font: normal 12px Arial; | |||
| } | |||
| 
 | |||
| .nvd3 .title { | |||
|     font: bold 14px Arial; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-background { | |||
|     fill: white; | |||
|     fill-opacity: 0; | |||
| } | |||
| 
 | |||
| .nvd3.nv-noData { | |||
|     font-size: 18px; | |||
|     font-weight: bold; | |||
| } | |||
| 
 | |||
| 
 | |||
| /********** | |||
| *  Brush | |||
| */ | |||
| 
 | |||
| .nv-brush .extent { | |||
|     fill-opacity: .125; | |||
|     shape-rendering: crispEdges; | |||
| } | |||
| 
 | |||
| .nv-brush .resize path { | |||
|     fill: #eee; | |||
|     stroke: #666; | |||
| } | |||
| 
 | |||
| 
 | |||
| /********** | |||
| *  Legend | |||
| */ | |||
| 
 | |||
| .nvd3 .nv-legend .nv-series { | |||
|     cursor: pointer; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-legend .nv-disabled circle { | |||
|     fill-opacity: 0; | |||
| } | |||
| 
 | |||
| /* focus */ | |||
| .nvd3 .nv-brush .extent { | |||
|     fill-opacity: 0 !important; | |||
| } | |||
| 
 | |||
| .nvd3 .nv-brushBackground rect { | |||
|     stroke: #000; | |||
|     stroke-width: .4; | |||
|     fill: #fff; | |||
|     fill-opacity: .7; | |||
| } | |||
| 
 | |||
| 
 | |||
| .nvd3.nv-pie path { | |||
|     stroke-opacity: 0; | |||
|     transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; | |||
|     -moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; | |||
|     -webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; | |||
| 
 | |||
| } | |||
| 
 | |||
| .nvd3.nv-pie .nv-pie-title { | |||
|     font-size: 24px; | |||
|     fill: rgba(19, 196, 249, 0.59); | |||
| } | |||
| 
 | |||
| .nvd3.nv-pie .nv-slice text { | |||
|     stroke: #000; | |||
|     stroke-width: 0; | |||
| } | |||
| 
 | |||
| .nvd3.nv-pie path { | |||
|     stroke: #fff; | |||
|     stroke-width: 1px; | |||
|     stroke-opacity: 1; | |||
| } | |||
| 
 | |||
| .nvd3.nv-pie path { | |||
|     fill-opacity: .7; | |||
| } | |||
| .nvd3.nv-pie .hover path { | |||
|     fill-opacity: 1; | |||
| } | |||
| .nvd3.nv-pie .nv-label { | |||
|     pointer-events: none; | |||
| } | |||
| .nvd3.nv-pie .nv-label rect { | |||
|     fill-opacity: 0; | |||
|     stroke-opacity: 0; | |||
| } | |||
| @ -0,0 +1,432 @@ | |||
| #chartdiv { | |||
| 	width		: 100%; | |||
| 	height		: 500px; | |||
| 	font-size	: 11px; | |||
| } | |||
| .card-title { | |||
|     float: left; | |||
|     font-size: 1.1rem; | |||
|     font-weight: 400; | |||
|     margin: 0; | |||
|     text-transform: uppercase; | |||
| } | |||
| 
 | |||
| .col-md-3, .col-sm-12, .col-md-8, .col-md-4, .col-6, .col-4,.col-2  { | |||
|     position: relative; | |||
|     width: 100%; | |||
|     padding-right: 7.5px !important; | |||
|     padding-left: 7.5px !important; | |||
| } | |||
| 
 | |||
| 
 | |||
| .card-header { | |||
|     background-color: | |||
|     transparent; | |||
|     border-bottom: 1px solid | |||
|     rgba(0,0,0,.125); | |||
|     padding: .75rem 1.25rem; | |||
|     position: relative; | |||
|     border-top-left-radius: .25rem; | |||
|     border-top-right-radius: .25rem; | |||
| } | |||
| 
 | |||
| 
 | |||
| .container-fluid.o_in_dashboard { | |||
|     padding: 0px !important; | |||
| } | |||
| .o_action_manager{ | |||
|     overflow-y: scroll !important; | |||
|     max-width:100%; | |||
| } | |||
| 
 | |||
| // new tile | |||
| 
 | |||
| 
 | |||
| .o_dashboards { | |||
|     color: #2a2a2a; | |||
|     background-color: #f2f2f2 !important; | |||
| } | |||
| .dash-header { | |||
| 
 | |||
|     margin: 15px 0px 12px 0 !important; | |||
|     display: block; | |||
|     padding: 7px 25px 7px 0; | |||
|     color: #0e1319; | |||
|     font-size: 2rem; | |||
|     font-weight: 400; | |||
|     background-color: | |||
|     rgba(255, 255, 255, 0.9) !important; | |||
|     color:#212529; | |||
|     padding: 1.5rem; | |||
|     border-radius: 3px; | |||
|     box-shadow: 0 0px 10px 0px | |||
|     rgba(0, 0, 0, 0.05) !important; | |||
|     display: flex; | |||
|     justify-content: space-between; | |||
|     align-items: center; | |||
| 
 | |||
| } | |||
| h1.dashboard-h1 { | |||
| 
 | |||
|     display: block; | |||
|     padding: 7px 25px 7px 0; | |||
|     color: #0e1319; | |||
|     font-size: 2rem; | |||
|     font-weight: 400; | |||
|     color: | |||
| 
 | |||
|     #212529; | |||
|     float: left; | |||
|     margin-bottom: 0; | |||
| 
 | |||
| } | |||
| .card { | |||
|     position: relative !important; | |||
|     border-top: 0 !important; | |||
|     margin-bottom: 30px !important; | |||
|     width: 100% !important; | |||
|     background-color: #ffffff  !important; | |||
|     border-radius: 0.25rem !important; | |||
|     padding: 0px !important; | |||
|     -webkit-transition: .5s !important; | |||
|     transition: .5s !important; | |||
|     display: -ms-flexbox !important; | |||
|     display: flex !important; | |||
|     -ms-flex-direction: column !important; | |||
|     flex-direction: column !important; | |||
|     box-shadow: 0 0px 10px 0px rgba(0, 0, 0, 0.05) !important; | |||
|     border-radius: 0.25rem; | |||
| } | |||
| .card-header { | |||
|     border: 0; | |||
|     padding: 0; | |||
| } | |||
| .card-header > .card-tools { | |||
|     float: right; | |||
|     margin-right: 0.375rem; | |||
|     margin-top: 5px; | |||
|     margin-bottom: 10px; | |||
| } | |||
| .card-header i.fa { | |||
|     font-size: 1.3rem; | |||
|     display: inline-block; | |||
|     padding: 0 0px; | |||
|     margin: 0 0px; | |||
|     color: #57769c; | |||
|     opacity: .8; | |||
|     -webkit-transition: 0.3s linear; | |||
|     transition: 0.3s linear; | |||
| } | |||
| 
 | |||
| .main-title { | |||
|     display: block; | |||
|     margin-bottom: 5px; | |||
|     font-size: 13px; | |||
|     font-weight: 600; | |||
|     color: #fff !important; | |||
|     text-transform: uppercase; | |||
|     padding: 1rem; | |||
|     border-radius: 5px; | |||
|     border-bottom-left-radius: 0; | |||
|     border-bottom-right-radius: 0; | |||
| } | |||
| .card-body { | |||
|     background-color: rgba(255, 255, 255, 0.9) !important; | |||
|     color: #212529; | |||
|     padding-top: 0; | |||
| } | |||
| .tile.wide.invoice { | |||
|     margin-bottom: 27px; | |||
|     -webkit-box-shadow: 1px 5px 24px 0 rgba(68, 102, 242, 0.05); | |||
|     box-shadow: 1px 5px 24px 0 rgba(68, 102, 242, 0); | |||
|     background-color: #ffffff; | |||
|     border-radius: 5px; | |||
|     position: relative; | |||
|     width: 100%; | |||
|     padding: 0rem 0rem; | |||
|     border: 1px solid rgba(0, 0, 0, 0.07); | |||
| } | |||
| .box-1 .main-title { | |||
|     background: #67b7dc; | |||
|     color: #fff; | |||
| } | |||
|     .box-2 .main-title { | |||
|     background: #6794dc !important; | |||
|     color: #fff; | |||
| } | |||
| .box-3 .main-title { | |||
|     background:#8067dc; | |||
|     color: #fff; | |||
| } | |||
|     .box-4 .main-title { | |||
|     background: #c767dc; | |||
|     color: #fff; | |||
| } | |||
| .count { | |||
|     margin-bottom: 1rem; | |||
| } | |||
| 
 | |||
| .count > span > sapn { | |||
|     font-size: 20px !important; | |||
| } | |||
| 
 | |||
| span#total_invoices_ span, span#total_invoices_last span, span#total_incomes_ span, span#total_incomes_last span, span#total_expenses_ span, span#total_expense_last span, span#unreconciled_items_ span, span#unreconciled_items_last span,span#unreconciled_counts_last_year span,span#unreconciled_counts_this_year span,span#total_expense_last_year span,span#total_expense_this_year span, span#total_incomes_last_year span,span#total_incomes_this_year span,span#total_invoices_last_year span,span#total_invoices_this_year span,span#net_profit_current_months span,span#net_profit_current_year  span { | |||
|     padding-right: 8px; | |||
|     font-size: 16px; | |||
|     font-weight: 600; | |||
| } | |||
| 
 | |||
| span#total_invoices_, span#total_invoices_last, span#total_incomes_, span#total_incomes_last, span#total_expenses_, span#total_expense_last, span#unreconciled_items_, span#unreconciled_items_last,span#unreconciled_counts_last_year,span#unreconciled_counts_this_year,span#total_expense_last_year,span#total_expense_this_year, span#total_incomes_last_year,span#total_incomes_this_year,span#total_invoices_last_year,span#total_invoices_this_year,span#net_profit_current_months,span#net_profit_current_year { | |||
|     display: -webkit-box; | |||
|     display: -webkit-flex; | |||
|     display: flex; | |||
|     flex-direction: column; | |||
|     color: #455e7b !important; | |||
| } | |||
| .main-title~div { | |||
|     display: flex; | |||
|     justify-content: space-between; | |||
|     margin-top: 1rem; | |||
|     padding: 1rem; | |||
|     background:#fff; | |||
| } | |||
| .card-header { | |||
|     color: #0e1319 !important; | |||
|     display: block !important; | |||
|     padding: 1.5rem 1.5rem !important; | |||
|     position: relative !important; | |||
|     border-bottom: 1px solid rgba(0, 0, 0, 0.07) !important; | |||
|     border-top-left-radius: 0.25rem !important; | |||
|     border-top-right-radius: 0.25rem !important; | |||
| } | |||
| .card-header i.fa { | |||
|     font-size: 1rem; | |||
|     display: inline-block; | |||
|     padding: 0 0px; | |||
|     margin: 0 0px; | |||
|     color: #57769c; | |||
|     opacity: .8; | |||
|     -webkit-transition: 0.3s linear; | |||
|     transition: 0.3s linear; | |||
| } | |||
| .card-header > .card-tools { | |||
|     float: right; | |||
|     margin-right: 0; | |||
|     margin-top: 0px !important; | |||
|     margin-bottom: 0; | |||
| } | |||
| h3, .h3 { | |||
|     margin: 0; | |||
| } | |||
| .card-tools .btn { | |||
|     padding: 0 10px; | |||
|     margin: 0; | |||
|     line-height: normal !important; | |||
| } | |||
| 
 | |||
| 
 | |||
| 
 | |||
| #col-graph .card { | |||
|     height: 366px; | |||
| } | |||
| 
 | |||
| #top_10_customers_this_month{ | |||
| padding:0; | |||
| } | |||
| #top_10_customers_this_month li { | |||
|     list-style: none; | |||
|     padding-top: 6px; | |||
|     padding-bottom: 6px; | |||
|     font-size: 13px; | |||
|     color:#455e7b !important; | |||
|     border-bottom: 1px solid | |||
|     rgba(0, 0, 0, 0.07) !important; | |||
|     padding-left: 2rem; | |||
|     display: flex; | |||
|     justify-content: space-between; | |||
|     padding-right: 2rem; | |||
| } | |||
| #top_10_customers_this_month li a{ | |||
| color: | |||
| #455e7b !important; | |||
| } | |||
| 
 | |||
| #top_10_customers{ | |||
| padding:0; | |||
| } | |||
| #top_10_customers li { | |||
|     list-style: none; | |||
|     padding-top: 6px; | |||
|     padding-bottom: 6px; | |||
|     font-size: 13px; | |||
|     color:#455e7b !important; | |||
|     border-bottom: 1px solid | |||
|     rgba(0, 0, 0, 0.07) !important; | |||
|     padding-left: 2rem; | |||
|     display: flex; | |||
|     justify-content: space-between; | |||
|     padding-right: 2rem; | |||
| } | |||
| #top_10_customers li a{ | |||
| color: | |||
| #455e7b !important; | |||
| } | |||
| 
 | |||
| progress, progress[role] { | |||
|   -webkit-appearance: none; | |||
|   -moz-appearance: none; | |||
|   appearance: none; | |||
|   border: none; | |||
|   background-size: auto; | |||
|   height: 20px; | |||
|   width: 100%; | |||
|   background-color: #8067dc; | |||
| } | |||
| 
 | |||
| // The unordered list | |||
| .skill-list { | |||
|   list-style: none; | |||
|   margin: 0; | |||
|   padding: 1em; | |||
| } | |||
| 
 | |||
| // The list item | |||
| .skill { | |||
|   margin-bottom: 1em; | |||
|   position: relative; | |||
|   h3 { | |||
|     color: #000; | |||
|     left: 1em; | |||
|     line-height: 1; | |||
|     position: absolute; | |||
|     top: 1em; | |||
|   } | |||
|   ::-webkit-progress-value { | |||
|     -webkit-animation: bar-fill 2s; | |||
|     width: 0px; | |||
|   } | |||
| } | |||
| 
 | |||
| // Style the bar colors | |||
| .skill-1::-webkit-progress-value { | |||
|   background: #c767dc; | |||
| } | |||
| 
 | |||
| .skill-1::-moz-progress-bar { | |||
|   background: #c767dc; | |||
| } | |||
| 
 | |||
| // Animation Keyframes | |||
| @-webkit-keyframes bar-fill { | |||
|   0% { width: 0; } | |||
| } | |||
| 
 | |||
| @keyframes bar-fill { | |||
|   0% { width: 0; } | |||
| } | |||
| 
 | |||
| #total_supplier_invoice{ | |||
| color: #696969; | |||
| } | |||
| 
 | |||
| #total_customer_invoice_names{ | |||
| color: #696969; | |||
| } | |||
| 
 | |||
| #total_customer_invoice{ | |||
| color: #696969; | |||
| } | |||
| 
 | |||
| #total_invoice_difference, #total_supplier_difference{ | |||
| color: #4ecdc4; | |||
| } | |||
| progress { | |||
|     border: 0; | |||
|     border-radius: 20px; | |||
| } | |||
| progress::-webkit-progress-bar { | |||
|     border: 0; | |||
|     border-radius: 20px; | |||
| } | |||
| progress::-webkit-progress-value { | |||
|     border: 0; | |||
|     border-radius: 20px; | |||
| } | |||
| progress::-moz-progress-bar { | |||
|     border: 0; | |||
|     border-radius: 20px; | |||
| } | |||
| #total_customer_invoice_paid .logo, #total_customer_invoice .logo, #total_supplier_invoice_paid .logo, #total_supplier_invoice .logo, | |||
|  #total_customer_invoice_paid_current_year .logo, #total_customer_invoice_current_year .logo, #total_supplier_invoice_paid_current_year .logo, #total_supplier_invoice_current_year .logo, | |||
|  #total_customer_invoice_paid_current_month .logo, #total_customer_invoice_current_month .logo, #total_supplier_invoice_paid_current_month .logo, #total_supplier_invoice_current_month .logo { | |||
| 
 | |||
|     display: -webkit-box; | |||
|     display: -webkit-flex; | |||
|     display: flex; | |||
|     justify-content: left; | |||
|     flex-direction: column-reverse; | |||
|     color:#455e7b !important; | |||
| 
 | |||
| } | |||
| 
 | |||
| #total_customer_invoice_paid .logo span:nth-child(2), #total_customer_invoice .logo span:nth-child(2), #total_supplier_invoice_paid .logo span:nth-child(2), #total_supplier_invoice .logo span:nth-child(2), | |||
| #total_customer_invoice_paid_current_year .logo span:nth-child(2), #total_customer_invoice_current_year .logo span:nth-child(2), #total_supplier_invoice_paid_current_year .logo span:nth-child(2), #total_supplier_invoice_current_year .logo span:nth-child(2), | |||
| #total_customer_invoice_paid_current_month .logo span:nth-child(2), #total_customer_invoice_current_month .logo span:nth-child(2), #total_supplier_invoice_paid_current_month .logo span:nth-child(2), #total_supplier_invoice_current_month .logo span:nth-child(2) { | |||
| 
 | |||
|     font-weight: 600; | |||
| 
 | |||
| } | |||
| 
 | |||
| .toggle-on.btn { | |||
| 
 | |||
|     padding-right: 7px; | |||
|     right: 45%; | |||
| 
 | |||
| } | |||
| 
 | |||
| .toggle.btn.btn-default.off { | |||
| 
 | |||
|     border: 1px solid #aaa; | |||
| 
 | |||
| background: #fff; | |||
|     font-weight: 600 !important; | |||
| 
 | |||
| } | |||
| .toggle-off.btn { | |||
| 
 | |||
|     padding-left: 9px; | |||
| 
 | |||
| } | |||
| 
 | |||
| #canvas { | |||
|     height: 277px !important; | |||
|     width: 100% !important; | |||
| } | |||
| #collection { | |||
|     height: 277px !important; | |||
|     width: 100% !important; | |||
| } | |||
| #hiring { | |||
|     height: 260px !important; | |||
|     width: 100% !important; | |||
| } | |||
| 
 | |||
| #payroll_status { | |||
|     height: 260px !important; | |||
|     width: 100% !important; | |||
| } | |||
| 
 | |||
| #evm { | |||
|     height: 277px !important; | |||
|     width: 100% !important; | |||
| } | |||
| 
 | |||
| .custom-h1 { | |||
|     font-size: 1em; | |||
|     margin-bottom: 0rem; | |||
| } | |||
| 
 | |||
| .custom-h3 { | |||
|     font-size: 1em; | |||
|     margin: 0; | |||
| } | |||
| After Width: | Height: | Size: 14 KiB | 
| After Width: | Height: | Size: 21 KiB | 
| After Width: | Height: | Size: 12 KiB | 
| After Width: | Height: | Size: 34 KiB | 
| @ -0,0 +1,770 @@ | |||
| odoo.define('hr_payroll_dashboard.PayrollDashboard', function (require) { | |||
| "use strict"; | |||
| var AbstractAction = require('web.AbstractAction'); | |||
| var ajax = require('web.ajax'); | |||
| var core = require('web.core'); | |||
| var rpc = require('web.rpc'); | |||
| var session = require('web.session'); | |||
| var web_client = require('web.web_client'); | |||
| var _t = core._t; | |||
| var QWeb = core.qweb; | |||
| 
 | |||
| var PayrollDashboard = AbstractAction.extend({ | |||
|     template: 'PayrollDashboardMain', | |||
| 
 | |||
|     cssLibs: [ | |||
|     '/hr_payroll_dashboard/static/src/css/lib/nv.d3.css' | |||
|     ], | |||
|     jsLibs: [ | |||
|     '/hr_payroll_dashboard/static/src/js/lib/d3.min.js' | |||
| 
 | |||
|     ], | |||
|     events: { | |||
|         'click .o_payslips': '_onclickPayslips', | |||
|         'click .o_contracts': '_onclickContracts', | |||
|         'click .o_attendances': '_onclickAttendances', | |||
|         'click .o_leave': '_onclickLeave', | |||
|         'click .o_salary_structures': '_onClickSalaryStructure', | |||
|         'click  .o_salary_rules': '_onClickSalaryRules', | |||
|     }, | |||
| 
 | |||
|     init: function(parent, context) { | |||
|         this._super(parent, context); | |||
|         this.login_employee = []; | |||
|         this.dashboards_templates = ['PayrollManagerDashboard','EmployeeDetails','ManagerLeaveDashboard', 'PayrollChart']; | |||
| 
 | |||
|     }, | |||
| 
 | |||
|     start: function() { | |||
|         var self = this; | |||
|         this.set("title", 'Dashboard'); | |||
|         self.update_leave_trend(); | |||
|         return this._super().then(function() { | |||
|             self.render_dashboards(); | |||
|             self.render_graphs(); | |||
| 
 | |||
|         }); | |||
|     }, | |||
| 
 | |||
|     willStart: function() { | |||
|         var self = this; | |||
|         this.login_employee = {}; | |||
|         return this._super() | |||
|         .then(function() { | |||
|         var emp_details =  self._rpc({ | |||
|                     model: 'hr.employee', | |||
|                     method: 'get_user_employee_info' | |||
|             }).then(function(result) { | |||
|                 self.login_employee =  result[0]; | |||
|             }); | |||
|         return $.when(emp_details); | |||
|         }); | |||
|     }, | |||
|     render_dashboards: function() { | |||
|         var self = this; | |||
|         if (this.login_employee){ | |||
|             var templates = [] | |||
|             if( self.login_employee.is_manager == true){templates = ['EmployeeDetails','ManagerLeaveDashboard','PayrollManagerDashboard']; | |||
|             } | |||
|             else{ | |||
|              templates = ['EmployeeDetails'];} | |||
|             _.each(templates, function(template) { | |||
|                 self.$('.o_payroll_dashboard').append(QWeb.render(template, {widget: self})); | |||
|             }); | |||
|         } | |||
|         else{ | |||
|                 self.$('.o_payroll_dashboard').append(QWeb.render('EmployeeWarning', {widget: self})); | |||
|             } | |||
|     }, | |||
|     _onclickPayslips: function(){ | |||
|         var self = this; | |||
|         var domain = [] | |||
|         if( self.login_employee.is_manager == false){ | |||
|             domain = [['employee_id','=', this.login_employee.id]] | |||
| 
 | |||
|         } | |||
|        return this.do_action({ | |||
|                 name: _t("Employee Payslips"), | |||
|                 type: 'ir.actions.act_window', | |||
|                 res_model: 'hr.payslip', | |||
|                 view_mode: 'tree,form,calendar', | |||
|                 views: [[false, 'list'],[false, 'form']], | |||
|                 domain: domain, | |||
|                 target: 'current' | |||
|             }); | |||
|     }, | |||
|     _onclickContracts: function(){ | |||
|         var self = this; | |||
|         var domain = [] | |||
|         if( self.login_employee.is_manager == false){ | |||
|             domain = [['employee_id','=', this.login_employee.id]] | |||
| 
 | |||
|         } | |||
|         return self.do_action({ | |||
|             name: _t("Employee Contracts"), | |||
|             type: 'ir.actions.act_window', | |||
|             res_model: 'hr.contract', | |||
|             view_mode: 'tree', | |||
|             views: [[false, 'list'],], | |||
|             context: {"create": false}, | |||
|             domain: domain, | |||
|         }); | |||
|     }, | |||
|     _onclickAttendances: function(){ | |||
|         var self = this; | |||
|          var today = new Date(); | |||
|         var dd = String(today.getDate()).padStart(2, '0'); | |||
|         var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
 | |||
|         var yyyy = today.getFullYear(); | |||
| 
 | |||
|         today = yyyy + '-' + mm + '-' + dd; | |||
|        var domain = [['attendance_date', '=', today]] | |||
|         if( self.login_employee.is_manager == false){ | |||
|             domain = [['employee_id','=', this.login_employee.id],['attendance_date', '=', today]] | |||
| 
 | |||
|         } | |||
|         return self.do_action({ | |||
|             name: _t("Employee Attendances"), | |||
|             type: 'ir.actions.act_window', | |||
|             res_model: 'hr.attendance', | |||
|             view_mode: 'tree', | |||
|             views: [[false, 'list'],], | |||
|             context: {"create": false}, | |||
|             domain: domain, | |||
|         }); | |||
|     }, | |||
|     _onclickLeave: function(){ | |||
|         var today = new Date(); | |||
|         var dd = String(today.getDate()).padStart(2, '0'); | |||
|         var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
 | |||
|         var yyyy = today.getFullYear(); | |||
| 
 | |||
|         today = yyyy + '-' + mm + '-' + dd; | |||
|         var self = this; | |||
|         var domain = [['request_date_from', '=', today]] | |||
|         if( self.login_employee.is_manager == false){ | |||
|             domain = [['employee_id','=', this.login_employee.id],['request_date_from', '=', today]] | |||
| 
 | |||
|         } | |||
|         return self.do_action({ | |||
|             name: _t("Employee Leave"), | |||
|             type: 'ir.actions.act_window', | |||
|             res_model: 'hr.leave', | |||
|             view_mode: 'tree', | |||
|             views: [[false, 'list'],], | |||
|             context: {"create": false}, | |||
|             domain: domain, | |||
|         }); | |||
|     }, | |||
|     _onClickSalaryRules: function(){ | |||
|         var self = this; | |||
|         return self.do_action({ | |||
|             name: _t("Salary Rules"), | |||
|             type: 'ir.actions.act_window', | |||
|             res_model: 'hr.salary.rule', | |||
|             view_mode: 'tree', | |||
|             views: [[false, 'list'],], | |||
|             context: {"create": false}, | |||
|         }); | |||
|     }, | |||
|     _onClickSalaryStructure: function(){ | |||
|         var self = this; | |||
|         return self.do_action({ | |||
|             name: _t("Salary Structures"), | |||
|             type: 'ir.actions.act_window', | |||
|             res_model: 'hr.payroll.structure', | |||
|             view_mode: 'tree', | |||
|             views: [[false, 'list'],], | |||
|             context: {"create": false}, | |||
|         }); | |||
|     }, | |||
| 
 | |||
|     update_attendance: function () { | |||
|         var self = this; | |||
|         this._rpc({ | |||
|             model: 'hr.employee', | |||
|             method: 'attendance_manual', | |||
|             args: [[self.login_employee.id], 'hr_attendance.hr_attendance_action_my_attendances'], | |||
|         }) | |||
|         .then(function(result) { | |||
|             var attendance_state =self.login_employee.attendance_state; | |||
|             var message = '' | |||
|             var action_client = { | |||
|                 type: "ir.actions.client", | |||
|                 name: _t('Dashboard'), | |||
|                 tag: 'payroll_dashboard', | |||
|             }; | |||
|             self.do_action(action_client, {clear_breadcrumbs: true}); | |||
|             if (attendance_state == 'checked_in'){ | |||
|                 message = 'Checked Out' | |||
|             } | |||
|             else if (attendance_state == 'checked_out'){ | |||
|                 message = 'Checked In' | |||
|             } | |||
|             self.trigger_up('show_effect', { | |||
|                 message: _t("Successfully " + message), | |||
|                 type: 'rainbow_man' | |||
| 
 | |||
|             }); | |||
|             setTimeout('location.reload()',7); | |||
| 
 | |||
| 
 | |||
|         }); | |||
| 
 | |||
| 
 | |||
|     }, | |||
| 
 | |||
|     render_expense_graph:function(){ | |||
|        var elem = this.$('.expense_graph'); | |||
|         var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139', | |||
|         '#ffa433', '#ffc25b', '#f8e54b']; | |||
|         var color = d3.scale.ordinal().range(colors); | |||
|         rpc.query({ | |||
|             model: "hr.expense", | |||
|             method: "get_employee_expense", | |||
|         }).then(function (data) { | |||
|             data.forEach(function(d) { | |||
|               d.values.forEach(function(d) { | |||
|                 d.l_month = d.l_month; | |||
|                 d.count = +d.count; | |||
|               }); | |||
|             }); | |||
|             var margin = {top: 30, right: 10, bottom: 30, left: 30}, | |||
|                 width = 400 - margin.left - margin.right, | |||
|                 height = 250 - margin.top - margin.bottom; | |||
| 
 | |||
|             // Set the ranges
 | |||
|             var x = d3.scale.ordinal() | |||
|                 .rangeRoundBands([0, width], 1); | |||
| 
 | |||
|             var y = d3.scale.linear() | |||
|                 .range([height, 0]); | |||
| 
 | |||
|             // Define the axes
 | |||
|             var xAxis = d3.svg.axis().scale(x) | |||
|                 .orient("bottom"); | |||
| 
 | |||
|             var yAxis = d3.svg.axis().scale(y) | |||
|                 .orient("left").ticks(5); | |||
| 
 | |||
|             x.domain(data[0].values.map(function(d) { return d.l_month; })); | |||
|             y.domain([0, d3.max(data[0].values, d => d.count)]) | |||
| 
 | |||
|             var svg = d3.select(elem[0]).append("svg") | |||
|                 .attr("width", width + margin.left + margin.right) | |||
|                 .attr("height", height + margin.top + margin.bottom) | |||
|                 .append("g") | |||
|                 .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |||
| 
 | |||
|             // Add the X Axis
 | |||
|             svg.append("g") | |||
|                 .attr("class", "x axis") | |||
|                 .attr("transform", "translate(0," + height + ")") | |||
|                 .call(xAxis); | |||
| 
 | |||
|             // Add the Y Axis
 | |||
|             svg.append("g") | |||
|                 .attr("class", "y axis") | |||
|                 .call(yAxis); | |||
| 
 | |||
| 
 | |||
|             var line = d3.svg.line() | |||
|                 .x(function(d) {return x(d.l_month); }) | |||
|                 .y(function(d) {return y(d.count); }); | |||
| 
 | |||
|             let lines = svg.append('g') | |||
|               .attr('class', 'lines'); | |||
| 
 | |||
|             lines.selectAll('.line-group') | |||
|                 .data(data).enter() | |||
|                 .append('g') | |||
|                 .attr('class', 'line-group') | |||
|                 .append('path') | |||
|                 .attr('class', 'line') | |||
|                 .attr('d', function(d) { return line(d.values); }) | |||
|                 .style('stroke', (d, i) => color(i)); | |||
| 
 | |||
|             lines.selectAll("circle-group") | |||
|                 .data(data).enter() | |||
|                 .append("g") | |||
|                 .selectAll("circle") | |||
|                 .data(function(d) { return d.values;}).enter() | |||
|                 .append("g") | |||
|                 .attr("class", "circle") | |||
|                 .append("circle") | |||
|                 .attr("cx", function(d) { return x(d.l_month)}) | |||
|                 .attr("cy", function(d) { return y(d.count)}) | |||
|                 .attr("r", 3); | |||
| 
 | |||
| 
 | |||
|         }); | |||
|     }, | |||
| 
 | |||
|     render_leave_graph:function(){ | |||
|         var self = this; | |||
|         var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139', | |||
|         '#ffa433', '#ffc25b', '#f8e54b']; | |||
|         var color = d3.scale.ordinal().range(colors); | |||
|         rpc.query({ | |||
|                 model: "hr.employee", | |||
|                 method: "get_department_leave", | |||
|             }).then(function (data) { | |||
|                 var fData = data[0]; | |||
|                 var dept = data[1]; | |||
|                 var id = self.$('.leave_graph')[0]; | |||
|                 var barColor = '#ff618a'; | |||
|                 fData.forEach(function(d){ | |||
|                     var total = 0; | |||
|                     for (var dpt in dept){ | |||
|                         total += d.leave[dept[dpt]]; | |||
|                     } | |||
|                 d.total=total; | |||
|                 }); | |||
| 
 | |||
|                 // function to handle histogram.
 | |||
|                 function histoGram(fD){ | |||
|                     var hG={},    hGDim = {t: 60, r: 0, b: 30, l: 0}; | |||
|                     hGDim.w = 350 - hGDim.l - hGDim.r, | |||
|                     hGDim.h = 200 - hGDim.t - hGDim.b; | |||
| 
 | |||
|                     //create svg for histogram.
 | |||
|                     var hGsvg = d3.select(id).append("svg") | |||
|                         .attr("width", hGDim.w + hGDim.l + hGDim.r) | |||
|                         .attr("height", hGDim.h + hGDim.t + hGDim.b).append("g") | |||
|                         .attr("transform", "translate(" + hGDim.l + "," + hGDim.t + ")"); | |||
| 
 | |||
|                     // create function for x-axis mapping.
 | |||
|                     var x = d3.scale.ordinal().rangeRoundBands([0, hGDim.w], 0.1) | |||
|                             .domain(fD.map(function(d) { return d[0]; })); | |||
| 
 | |||
|                     // Add x-axis to the histogram svg.
 | |||
|                     hGsvg.append("g").attr("class", "x axis") | |||
|                         .attr("transform", "translate(0," + hGDim.h + ")") | |||
|                         .call(d3.svg.axis().scale(x).orient("bottom")); | |||
| 
 | |||
|                     // Create function for y-axis map.
 | |||
|                     var y = d3.scale.linear().range([hGDim.h, 0]) | |||
|                             .domain([0, d3.max(fD, function(d) { return d[1]; })]); | |||
| 
 | |||
|                     // Create bars for histogram to contain rectangles and freq labels.
 | |||
|                     var bars = hGsvg.selectAll(".bar").data(fD).enter() | |||
|                             .append("g").attr("class", "bar"); | |||
| 
 | |||
|                     //create the rectangles.
 | |||
|                     bars.append("rect") | |||
|                         .attr("x", function(d) { return x(d[0]); }) | |||
|                         .attr("y", function(d) { return y(d[1]); }) | |||
|                         .attr("width", x.rangeBand()) | |||
|                         .attr("height", function(d) { return hGDim.h - y(d[1]); }) | |||
|                         .attr('fill',barColor) | |||
|                         .on("mouseover",mouseover)// mouseover is defined below.
 | |||
|                         .on("mouseout",mouseout);// mouseout is defined below.
 | |||
| 
 | |||
|                     //Create the frequency labels above the rectangles.
 | |||
|                     bars.append("text").text(function(d){ return d3.format(",")(d[1])}) | |||
|                         .attr("x", function(d) { return x(d[0])+x.rangeBand()/2; }) | |||
|                         .attr("y", function(d) { return y(d[1])-5; }) | |||
|                         .attr("text-anchor", "middle"); | |||
| 
 | |||
|                     function mouseover(d){  // utility function to be called on mouseover.
 | |||
|                         // filter for selected state.
 | |||
|                         var st = fData.filter(function(s){ return s.l_month == d[0];})[0], | |||
|                             nD = d3.keys(st.leave).map(function(s){ return {type:s, leave:st.leave[s]};}); | |||
| 
 | |||
|                         // call update functions of pie-chart and legend.
 | |||
|                         pC.update(nD); | |||
|                         leg.update(nD); | |||
|                     } | |||
| 
 | |||
|                     function mouseout(d){    // utility function to be called on mouseout.
 | |||
|                         // reset the pie-chart and legend.
 | |||
|                         pC.update(tF); | |||
|                         leg.update(tF); | |||
|                     } | |||
| 
 | |||
|                     // create function to update the bars. This will be used by pie-chart.
 | |||
|                     hG.update = function(nD, color){ | |||
|                         // update the domain of the y-axis map to reflect change in frequencies.
 | |||
|                         y.domain([0, d3.max(nD, function(d) { return d[1]; })]); | |||
| 
 | |||
|                         // Attach the new data to the bars.
 | |||
|                         var bars = hGsvg.selectAll(".bar").data(nD); | |||
| 
 | |||
|                         // transition the height and color of rectangles.
 | |||
|                         bars.select("rect").transition().duration(500) | |||
|                             .attr("y", function(d) {return y(d[1]); }) | |||
|                             .attr("height", function(d) { return hGDim.h - y(d[1]); }) | |||
|                             .attr("fill", color); | |||
| 
 | |||
|                         // transition the frequency labels location and change value.
 | |||
|                         bars.select("text").transition().duration(500) | |||
|                             .text(function(d){ return d3.format(",")(d[1])}) | |||
|                             .attr("y", function(d) {return y(d[1])-5; }); | |||
|                     } | |||
|                     return hG; | |||
|                 } | |||
| 
 | |||
|                 // function to handle pieChart.
 | |||
|                 function pieChart(pD){ | |||
|                     var pC ={},    pieDim ={w:250, h: 250}; | |||
|                     pieDim.r = Math.min(pieDim.w, pieDim.h) / 2; | |||
| 
 | |||
|                     // create svg for pie chart.
 | |||
|                     var piesvg = d3.select(id).append("svg") | |||
|                         .attr("width", pieDim.w).attr("height", pieDim.h).append("g") | |||
|                         .attr("transform", "translate("+pieDim.w/2+","+pieDim.h/2+")"); | |||
| 
 | |||
|                     // create function to draw the arcs of the pie slices.
 | |||
|                     var arc = d3.svg.arc().outerRadius(pieDim.r - 10).innerRadius(0); | |||
| 
 | |||
|                     // create a function to compute the pie slice angles.
 | |||
|                     var pie = d3.layout.pie().sort(null).value(function(d) { return d.leave; }); | |||
| 
 | |||
|                     // Draw the pie slices.
 | |||
|                     piesvg.selectAll("path").data(pie(pD)).enter().append("path").attr("d", arc) | |||
|                         .each(function(d) { this._current = d; }) | |||
|                         .attr("fill", function(d, i){return color(i);}) | |||
|                         .on("mouseover",mouseover).on("mouseout",mouseout); | |||
| 
 | |||
|                     // create function to update pie-chart. This will be used by histogram.
 | |||
|                     pC.update = function(nD){ | |||
|                         piesvg.selectAll("path").data(pie(nD)).transition().duration(500) | |||
|                             .attrTween("d", arcTween); | |||
|                     } | |||
|                     // Utility function to be called on mouseover a pie slice.
 | |||
|                     function mouseover(d, i){ | |||
|                         // call the update function of histogram with new data.
 | |||
|                         hG.update(fData.map(function(v){ | |||
|                             return [v.l_month,v.leave[d.data.type]];}),color(i)); | |||
|                     } | |||
|                     //Utility function to be called on mouseout a pie slice.
 | |||
|                     function mouseout(d){ | |||
|                         // call the update function of histogram with all data.
 | |||
|                         hG.update(fData.map(function(v){ | |||
|                             return [v.l_month,v.total];}), barColor); | |||
| 
 | |||
|                     } | |||
|                     // Animating the pie-slice requiring a custom function which specifies
 | |||
|                     // how the intermediate paths should be drawn.
 | |||
|                     function arcTween(a) { | |||
|                         var i = d3.interpolate(this._current, a); | |||
|                         this._current = i(0); | |||
|                         return function(t) { return arc(i(t));    }; | |||
|                     } | |||
|                     return pC; | |||
|                 } | |||
| 
 | |||
| //                 function to handle legend.
 | |||
|                 function legend(lD){ | |||
|                     var leg = {}; | |||
| 
 | |||
|                     // create table for legend.
 | |||
|                     var legend = d3.select(id).append("table").attr('class','legend'); | |||
| 
 | |||
|                     // create one row per segment.
 | |||
|                     var tr = legend.append("tbody").selectAll("tr").data(lD).enter().append("tr"); | |||
| 
 | |||
|                     // create the first column for each segment.
 | |||
|                     tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect") | |||
|                         .attr("width", '16').attr("height", '16') | |||
|                         .attr("fill", function(d, i){return color(i);}) | |||
| 
 | |||
|                     // create the second column for each segment.
 | |||
|                     tr.append("td").text(function(d){ return d.type;}); | |||
| 
 | |||
|                     // create the third column for each segment.
 | |||
|                     tr.append("td").attr("class",'legendFreq') | |||
|                         .text(function(d){ return d.l_month;}); | |||
| 
 | |||
|                     // create the fourth column for each segment.
 | |||
|                     tr.append("td").attr("class",'legendPerc') | |||
|                         .text(function(d){ return getLegend(d,lD);}); | |||
| 
 | |||
|                     // Utility function to be used to update the legend.
 | |||
|                     leg.update = function(nD){ | |||
|                         // update the data attached to the row elements.
 | |||
|                         var l = legend.select("tbody").selectAll("tr").data(nD); | |||
| 
 | |||
|                         // update the frequencies.
 | |||
|                         l.select(".legendFreq").text(function(d){ return d3.format(",")(d.leave);}); | |||
| 
 | |||
|                         // update the percentage column.
 | |||
|                         l.select(".legendPerc").text(function(d){ return getLegend(d,nD);}); | |||
|                     } | |||
| 
 | |||
|                     function getLegend(d,aD){ // Utility function to compute percentage.
 | |||
|                         var perc = (d.leave/d3.sum(aD.map(function(v){ return v.leave; }))); | |||
|                         if (isNaN(perc)){ | |||
|                             return d3.format("%")(0); | |||
|                             } | |||
|                         else{ | |||
|                             return d3.format("%")(d.leave/d3.sum(aD.map(function(v){ return v.leave; }))); | |||
|                             } | |||
|                     } | |||
| 
 | |||
|                     return leg; | |||
|                 } | |||
|                 // calculate total frequency by segment for all state.
 | |||
|                 var tF = dept.map(function(d){ | |||
|                     return {type:d, leave: d3.sum(fData.map(function(t){ return t.leave[d];}))}; | |||
|                 }); | |||
| 
 | |||
|                 // calculate total frequency by state for all segment.
 | |||
|                 var sF = fData.map(function(d){return [d.l_month,d.total];}); | |||
| 
 | |||
|                 var hG = histoGram(sF), // create the histogram.
 | |||
|                     pC = pieChart(tF), // create the pie-chart.
 | |||
|                     leg= legend(tF);  // create the legend.
 | |||
|         }); | |||
|     }, | |||
|     render_graphs: function(){ | |||
|         var self = this; | |||
|         if (this.login_employee){ | |||
|             self.render_employee_contracts_graph(); | |||
|             self.render_employee_time_off_graph(); | |||
|             self.render_employee_payslips_graph(); | |||
|             self.render_leave_graph(); | |||
|             self.render_expense_graph(); | |||
| 
 | |||
|         } | |||
|     }, | |||
|      render_employee_time_off_graph(){ | |||
|         var self = this; | |||
|         var w = 200; | |||
|         var h = 200; | |||
|         var r = h/2; | |||
|         var elem = this.$('.time_off_graph'); | |||
| //        var colors = ['#ff8762', '#5ebade', '#b298e1', '#70cac1', '#cf2030'];
 | |||
|         var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139', | |||
|         '#ffa433', '#ffc25b', '#f8e54b']; | |||
|         var color = d3.scale.ordinal().range(colors); | |||
|         rpc.query({ | |||
|             model: "hr.leave", | |||
|             method: "get_employee_time_off", | |||
| 
 | |||
|         }).then(function (data) { | |||
|             var segColor = {}; | |||
|             var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")"); | |||
|             var pie = d3.layout.pie().value(function(d){return d.value;}); | |||
|             var arc = d3.svg.arc().outerRadius(r); | |||
|             var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice"); | |||
|             arcs.append("svg:path") | |||
|                 .attr("fill", function(d, i){ | |||
|                     return color(i); | |||
|                 }) | |||
|                 .attr("d", function (d) { | |||
|                     return arc(d); | |||
|                 }); | |||
| 
 | |||
|             var legend = d3.select(elem[0]).append("table").attr('class','legend'); | |||
| 
 | |||
|             // create one row per segment.
 | |||
|             var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr"); | |||
| 
 | |||
|             // create the first column for each segment.
 | |||
|             tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect") | |||
|                 .attr("width", '16').attr("height", '16') | |||
|                 .attr("fill",function(d, i){ return color(i) }); | |||
| 
 | |||
|             // create the second column for each segment.
 | |||
|             tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;}); | |||
| 
 | |||
|             // create the third column for each segment.
 | |||
|             tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;") | |||
|                 .text(function(d){ return d.value;}); | |||
| 
 | |||
|         }); | |||
| 
 | |||
|     }, | |||
|     render_employee_payslips_graph(){ | |||
|         var self = this; | |||
|         var w = 200; | |||
|         var h = 200; | |||
|         var r = h/2; | |||
|         var elem = this.$('.emp_payslips_graph'); | |||
| //        var colors = ['#ff8762', '#5ebade', '#b298e1', '#70cac1', '#cf2030'];
 | |||
|         var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139', | |||
|         '#ffa433', '#ffc25b', '#f8e54b']; | |||
|         var color = d3.scale.ordinal().range(colors); | |||
|         rpc.query({ | |||
|             model: "hr.payslip", | |||
|             method: "get_employee_payslips", | |||
|         }).then(function (data) { | |||
|             var segColor = {}; | |||
|             var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")"); | |||
|             var pie = d3.layout.pie().value(function(d){return d.value;}); | |||
|             var arc = d3.svg.arc().outerRadius(r); | |||
|             var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice"); | |||
|             arcs.append("svg:path") | |||
|                 .attr("fill", function(d, i){ | |||
|                     return color(i); | |||
|                 }) | |||
|                 .attr("d", function (d) { | |||
|                     return arc(d); | |||
|                 }); | |||
| 
 | |||
|             var legend = d3.select(elem[0]).append("table").attr('class','legend'); | |||
| 
 | |||
|             // create one row per segment.
 | |||
|             var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr"); | |||
| 
 | |||
|             // create the first column for each segment.
 | |||
|             tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect") | |||
|                 .attr("width", '16').attr("height", '16') | |||
|                 .attr("fill",function(d, i){ return color(i) }); | |||
| 
 | |||
|             // create the second column for each segment.
 | |||
|             tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;}); | |||
| 
 | |||
|             // create the third column for each segment.
 | |||
|             tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;") | |||
|                 .text(function(d){ return d.value;}); | |||
|         }); | |||
| 
 | |||
|     }, | |||
|     render_employee_contracts_graph(){ | |||
|         var self = this; | |||
|         var w = 200; | |||
|         var h = 200; | |||
|         var r = h/2; | |||
|         var elem = this.$('.emp_contracts_graph'); | |||
| //        var colors = ['#ff8762', '#5ebade', '#b298e1', '#70cac1', '#cf2030'];
 | |||
|         var colors = ['#70cac1', '#659d4e', '#208cc2', '#4d6cb1', '#584999', '#8e559e', '#cf3650', '#f65337', '#fe7139', | |||
|         '#ffa433', '#ffc25b', '#f8e54b']; | |||
|         var color = d3.scale.ordinal().range(colors); | |||
|         rpc.query({ | |||
|             model: "hr.contract", | |||
|             method: "get_employee_contract", | |||
|         }).then(function (data) { | |||
|             var segColor = {}; | |||
|             var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")"); | |||
|             var pie = d3.layout.pie().value(function(d){return d.value;}); | |||
|             var arc = d3.svg.arc().outerRadius(r); | |||
|             var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice"); | |||
|             arcs.append("svg:path") | |||
|                 .attr("fill", function(d, i){ | |||
|                     return color(i); | |||
|                 }) | |||
|                 .attr("d", function (d) { | |||
|                     return arc(d); | |||
|                 }); | |||
| 
 | |||
|             var legend = d3.select(elem[0]).append("table").attr('class','legend'); | |||
| 
 | |||
|             // create one row per segment.
 | |||
|             var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr"); | |||
| 
 | |||
|             // create the first column for each segment.
 | |||
|             tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect") | |||
|                 .attr("width", '16').attr("height", '16') | |||
|                 .attr("fill",function(d, i){ return color(i) }); | |||
| 
 | |||
|             // create the second column for each segment.
 | |||
|             tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;}); | |||
| 
 | |||
|             // create the third column for each segment.
 | |||
|             tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;") | |||
|                 .text(function(d){ return d.value;}); | |||
|         }); | |||
| 
 | |||
|     }, | |||
|     update_leave_trend: function(){ | |||
|         var self = this; | |||
|         rpc.query({ | |||
|             model: "hr.employee", | |||
|             method: "employee_leave_trend", | |||
|         }).then(function (data) { | |||
|             var elem = self.$('.leave_trend'); | |||
|             var margin = {top: 30, right: 20, bottom: 30, left: 80}, | |||
|                 width = 500 - margin.left - margin.right, | |||
|                 height = 250 - margin.top - margin.bottom; | |||
| 
 | |||
|             // Set the ranges
 | |||
|             var x = d3.scale.ordinal() | |||
|                 .rangeRoundBands([0, width], 1); | |||
| 
 | |||
|             var y = d3.scale.linear() | |||
|                 .range([height, 0]); | |||
| 
 | |||
|             // Define the axes
 | |||
|             var xAxis = d3.svg.axis().scale(x) | |||
|                 .orient("bottom"); | |||
| 
 | |||
|             var yAxis = d3.svg.axis().scale(y) | |||
|                 .orient("left").ticks(5); | |||
| 
 | |||
|             var valueline = d3.svg.line() | |||
|                 .x(function(d) { return x(d.l_month); }) | |||
|                 .y(function(d) { return y(d.leave); }); | |||
| 
 | |||
| 
 | |||
|             var svg = d3.select(elem[0]).append("svg") | |||
|                 .attr("width", width + margin.left + margin.right) | |||
|                 .attr("height", height + margin.top + margin.bottom) | |||
|                 .append("g") | |||
|                 .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |||
| 
 | |||
|             x.domain(data.map(function(d) { return d.l_month; })); | |||
|             y.domain([0, d3.max(data, function(d) { return d.leave; })]); | |||
| 
 | |||
|             // Add the X Axis
 | |||
|             svg.append("g") | |||
|                 .attr("class", "x axis") | |||
|                 .attr("transform", "translate(0," + height + ")") | |||
|                 .call(xAxis); | |||
| 
 | |||
|             // Add the Y Axis
 | |||
|             svg.append("g") | |||
|                 .attr("class", "y axis") | |||
|                 .call(yAxis); | |||
| 
 | |||
|             svg.append("path") | |||
|                 .attr("class", "line") | |||
|                 .attr("d", valueline(data)); | |||
| 
 | |||
|             // Add the scatterplot
 | |||
|             svg.selectAll("dot") | |||
|                 .data(data) | |||
|                 .enter().append("circle") | |||
|                 .attr("r", 3) | |||
|                 .attr("cx", function(d) { return x(d.l_month); }) | |||
|                 .attr("cy", function(d) { return y(d.leave); }) | |||
| //                .on('mouseover', function() { d3.select(this).transition().duration(500).ease("elastic").attr('r', 3 * 2) })
 | |||
| //                .on('mouseout', function() { d3.select(this).transition().duration(500).ease("in-out").attr('r', 3) });
 | |||
|                 .on("mouseover", function() { tooltip.style("display", null); | |||
|                     d3.select(this).transition().duration(500).ease("elastic").attr('r', 3 * 2) | |||
|                  }) | |||
|                 .on("mouseout", function() { tooltip.style("display", "none"); | |||
|                     d3.select(this).transition().duration(500).ease("in-out").attr('r', 3) | |||
|                 }) | |||
|                 .on("mousemove", function(d) { | |||
|                     var xPosition = d3.mouse(this)[0] - 15; | |||
|                     var yPosition = d3.mouse(this)[1] - 25; | |||
|                     tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")"); | |||
|                     tooltip.select("text").text(d.leave); | |||
|                 }); | |||
| 
 | |||
|             var tooltip = svg.append("g") | |||
|                   .attr("class", "tooltip") | |||
|                   .style("display", "none"); | |||
| 
 | |||
|                 tooltip.append("rect") | |||
|                   .attr("width", 30) | |||
|                   .attr("height", 20) | |||
|                   .attr("fill", "black") | |||
|                   .style("opacity", 0.5); | |||
| 
 | |||
|                 tooltip.append("text") | |||
|                   .attr("x", 15) | |||
|                   .attr("dy", "1.2em") | |||
|                   .style("text-anchor", "middle") | |||
|                   .attr("font-size", "12px") | |||
|                   .attr("font-weight", "bold"); | |||
| 
 | |||
|         }); | |||
|     }, | |||
| 
 | |||
| }); | |||
| core.action_registry.add('payroll_dashboard', PayrollDashboard); | |||
| return PayrollDashboard; | |||
| }); | |||
| @ -0,0 +1,253 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <templates id="template" xml:space="preserve"> | |||
|     <t t-name="PayrollDashboardMain"> | |||
|          <div class="oh_dashboards"> | |||
|             <div class="container-fluid o_payroll_dashboard"> | |||
|             </div> | |||
|         </div> | |||
|     </t> | |||
|     <t t-name="EmployeeDetails"> | |||
|         <link rel="stylesheet" | |||
|               href="/hr_payroll_dashboard/static/src/css/dashboard.css"/> | |||
|         <div class="row main-section"> | |||
|             <div class="col-md-4 col-sm-6 o_attendances oh-payroll"> | |||
|                 <div class="oh-card" style="width: 288px;"> | |||
|                     <div class="oh-card-body"> | |||
|                         <div class="stat-widget-one"> | |||
|                             <div class="stat-icon" style="background:#5bcbd0"> | |||
|                                 <i class="fa fa-calendar"/> | |||
|                             </div> | |||
|                             <div class="stat-content"> | |||
|                                 <div class="stat-head">Attendances</div> | |||
|                                 <div class="stat_count"> | |||
|                                     <t t-esc="widget.login_employee['emp_timesheets']"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|             <div class="col-md-4 col-sm-6 o_leave oh-payroll"> | |||
|                 <div class="oh-card" style="width: 288px;"> | |||
|                     <div class="oh-card-body"> | |||
|                         <div class="stat-widget-one"> | |||
|                             <div class="stat-icon" style="background:#645bd0"> | |||
|                                 <i class="fa fa-calendar-minus-o"/> | |||
|                             </div> | |||
|                             <div class="stat-content"> | |||
|                                 <div class="stat-head">Leave Requests</div> | |||
|                                 <div class="stat_count"> | |||
|                                     <t t-esc="widget.login_employee['emp_leave']"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|             <div class="col-md-4 col-sm-6 o_payslips oh-payroll"> | |||
|                 <div class="oh-card" style="width: 350px;"> | |||
|                     <div class="oh-card-body"> | |||
|                         <div class="stat-widget-one"> | |||
|                             <div class="stat-icon" style="background:#85d05b"> | |||
|                                 <i class="fa fa-money"/> | |||
|                             </div> | |||
|                             <div class="stat-content"> | |||
|                                 <div class="stat-head">Payslips</div> | |||
|                                 <div class="stat_count"> | |||
|                                     <t t-esc="widget.login_employee['payslip_count']"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|             <div class="col-md-4 col-sm-6 o_contracts oh-payroll"> | |||
|                 <div class="oh-card" style="width: 288px;"> | |||
|                     <div class="oh-card-body"> | |||
|                         <div class="stat-widget-one"> | |||
|                             <div class="stat-icon" style="background:#d05bb8"> | |||
|                                 <i class="fa fa-handshake-o"/> | |||
|                             </div> | |||
|                             <div class="stat-content"> | |||
|                                 <div class="stat-head">Contracts</div> | |||
|                                 <div class="stat_count"> | |||
|                                     <t t-esc="widget.login_employee['emp_contracts_count']"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|             <div class="col-md-4 col-sm-6 o_salary_rules oh-payroll"> | |||
|                 <div class="oh-card" style="width: 288px;"> | |||
|                     <div class="oh-card-body"> | |||
|                         <div class="stat-widget-one"> | |||
|                             <div class="stat-icon" style="background:#FCF030"> | |||
|                                 <i class='fa fa-money'></i> | |||
|                             </div> | |||
|                             <div class="stat-content"> | |||
|                                 <div class="stat-head">Salary Rules</div> | |||
|                                 <div class="stat_count"> | |||
|                                     <t t-esc="widget.login_employee['salary_rule_count']"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|             <div class="col-md-4 col-sm-6 o_salary_structures oh-payroll"> | |||
|                 <div class="oh-card" style="width: 350px;"> | |||
|                     <div class="oh-card-body"> | |||
|                         <div class="stat-widget-one"> | |||
|                             <div class="stat-icon" style="background:#FFA742"> | |||
|                                 <i class='fa fa-money'></i> | |||
|                             </div> | |||
|                             <div class="stat-content"> | |||
|                                 <div class="stat-head">Salary Structures</div> | |||
|                                 <div class="stat_count"> | |||
|                                     <t t-esc="widget.login_employee['salary_structure_count']"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|         </div> | |||
| 
 | |||
|         <t t-name="PayrollChart"> | |||
|             <div class="col-xs-12 col-sm-12 col-lg-12 col-md-12"> | |||
|                 <div class="row" style="margin:0px;"> | |||
|                     <div class="col-md-6 monthly_expense_trend"> | |||
|                         <div class="card"> | |||
|                             <div class="card-header"> | |||
|                                 <div class="card-title"> | |||
|                                     <b> | |||
|                                         <h3 class="custom-h3"> | |||
|                                             Monthly Expense Analysis | |||
|                                         </h3> | |||
|                                     </b> | |||
|                                 </div> | |||
|                             </div> | |||
|                             <div class="card-body"> | |||
|                                 <div class="expense_graph"/> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                     <div class="col-md-6 my_leave_graph"> | |||
|                         <div class="card"> | |||
|                             <div class="card-header"> | |||
|                                 <div class="card-title"> | |||
|                                     <b> | |||
|                                         <h3 class="custom-h3"> | |||
|                                             My Leave Analysis | |||
|                                         </h3> | |||
|                                     </b> | |||
|                                 </div> | |||
|                             </div> | |||
|                             <div class="card-body"> | |||
|                                 <div class="leave_trend"/> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|         </t> | |||
|     </t> | |||
|     <t t-name="ManagerLeaveDashboard"> | |||
|         <div class="row" style="margin:0px;"> | |||
|             <div class="col-md-12 monthly_leave_graph_view"> | |||
|                 <div class="card"> | |||
|                     <div class="card-header"> | |||
|                         <div class="card-title"> | |||
|                             <b> | |||
|                                 <h3 class="custom-h3"> | |||
|                                     Monthly Leave Analysis | |||
|                                 </h3> | |||
|                             </b> | |||
|                         </div> | |||
|                     </div> | |||
|                     <div class="card-body"> | |||
|                         <div class="leave_graph justify-content-center"/> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|         </div> | |||
|     </t> | |||
| 
 | |||
|     <t t-name="PayrollManagerDashboard"> | |||
|         <br></br> | |||
|         <br></br> | |||
|         <div class="row" style="margin:0px;"> | |||
|             <div class="col-md-4" id="col-graph"> | |||
|                 <div class="card"> | |||
|                     <div class="card-header"> | |||
|                         <div class="custom-h3 card-title"> | |||
|                             <b> | |||
|                                 <span style="font-weight:bold;"> | |||
|                                     PAYSLIPS ANALYSIS | |||
|                                 </span> | |||
|                             </b> | |||
|                         </div> | |||
|                     </div> | |||
|                     <div class="card-body mt-3" id="in_ex_body_hide"> | |||
|                         <div class="row"> | |||
|                             <div class="col-md-12"> | |||
|                                 <div class="chart"> | |||
|                                     <div style="text-align: center;" | |||
|                                          class="emp_payslips_graph"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|             <div class="col-md-4" id="col-graph"> | |||
|                 <div class="card"> | |||
|                     <div class="card-header"> | |||
|                         <div class="custom-h3 card-title"> | |||
|                             <b> | |||
|                                 <span style="font-weight:bold;"> | |||
|                                     CONTRACT ANALYSIS | |||
|                                 </span> | |||
|                             </b> | |||
|                         </div> | |||
|                     </div> | |||
|                     <div class="card-body mt-3" id="in_ex_body_hide"> | |||
|                         <div class="row"> | |||
|                             <div class="col-md-12"> | |||
|                                 <div class="chart"> | |||
|                                     <div style="text-align: center;" | |||
|                                          class="emp_contracts_graph"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
| 
 | |||
|             <div class="col-md-4" id="col-graph"> | |||
|                 <div class="card"> | |||
|                     <div class="card-header"> | |||
|                         <div class="custom-h3 card-title"> | |||
|                             <b> | |||
|                                 <span style="font-weight:bold;"> | |||
|                                     Time Off Analysis | |||
|                                 </span> | |||
|                             </b> | |||
|                         </div> | |||
|                     </div> | |||
|                     <div class="card-body mt-3" id="in_ex_body_hide"> | |||
|                         <div class="row"> | |||
|                             <div class="col-md-12"> | |||
|                                 <div class="chart"> | |||
|                                     <div style="text-align: center;" | |||
|                                          class="time_off_graph"/> | |||
|                                 </div> | |||
|                             </div> | |||
|                         </div> | |||
|                     </div> | |||
|                 </div> | |||
|             </div> | |||
|         </div> | |||
|     </t> | |||
| </templates> | |||
| @ -0,0 +1,16 @@ | |||
| <?xml version="1.0" encoding="utf-8" ?> | |||
| <odoo> | |||
|     <data> | |||
|         <template id="assets_hr_payroll_dashboard" name="Payroll Dashboard" | |||
|                   inherit_id="web.assets_backend"> | |||
|             <xpath expr="." position="inside"> | |||
|                 <script type="text/javascript" | |||
|                         src="/hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js"/> | |||
|                 <link rel="stylesheet" type="text/scss" | |||
|                       href="/hr_payroll_dashboard/static/src/css/style.scss"/> | |||
|                 <link rel="stylesheet" | |||
|                       href="/hr_payroll_dashboard/static/src/css/dashboard.css"/> | |||
|             </xpath> | |||
|         </template> | |||
|     </data> | |||
| </odoo> | |||
| @ -0,0 +1,14 @@ | |||
| <?xml version="1.0" encoding="UTF-8" ?> | |||
| <odoo> | |||
|     <data> | |||
|         <record id="action_hr_payroll_dashboard_menu" model="ir.actions.client"> | |||
|             <field name="name">Dashboard</field> | |||
|             <field name="tag">payroll_dashboard</field> | |||
|         </record> | |||
|         <menuitem id="hr_payroll_dashboard_menu" | |||
|                   name="Dashboard" | |||
|                   action="action_hr_payroll_dashboard_menu" | |||
|                   parent="hr_payroll_community.menu_hr_payroll_community_root" | |||
|                   sequence="1"/> | |||
|     </data> | |||
| </odoo> | |||