@ -0,0 +1,42 @@ |
|||||
|
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
||||
|
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
HR Payroll Dashboard |
||||
|
==================== |
||||
|
* Detailed Dashboard View for Payroll Module |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
General Public License, Version 3 (AGPL v3). |
||||
|
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
||||
|
|
||||
|
Company |
||||
|
------- |
||||
|
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Credits |
||||
|
------- |
||||
|
* Developer: (V16)Anfas Faisal K, Contact: odoo@cybrosys.com |
||||
|
|
||||
|
Contacts |
||||
|
-------- |
||||
|
* Mail Contact : odoo@cybrosys.com |
||||
|
* Website : https://cybrosys.com |
||||
|
|
||||
|
Bug Tracker |
||||
|
----------- |
||||
|
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
||||
|
|
||||
|
Maintainer |
||||
|
========== |
||||
|
.. image:: https://cybrosys.com/images/logo.png |
||||
|
:target: https://cybrosys.com |
||||
|
|
||||
|
This module is maintained by Cybrosys Technologies. |
||||
|
|
||||
|
For support and more information, please visit https://www.cybrosys.com |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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,54 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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': '16.0.1.0.0', |
||||
|
'category': 'Human Resource', |
||||
|
'summary': """Detailed Dashboard View for Payroll Module""", |
||||
|
'description': "This module helps you to see the Overview of Payroll, " |
||||
|
"here You can see the details of Attendance, Leaves, " |
||||
|
"Payslip contracts, etc.", |
||||
|
'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/dashboard_views.xml', |
||||
|
], |
||||
|
'assets': { |
||||
|
'web.assets_backend': [ |
||||
|
'hr_payroll_dashboard/static/src/js/hr_payroll_dashboard.js', |
||||
|
'hr_payroll_dashboard/static/src/css/lib/nv.d3.css', |
||||
|
'hr_payroll_dashboard/static/src/css/dashboard.css', |
||||
|
'hr_payroll_dashboard/static/src/css/style.scss', |
||||
|
'hr_payroll_dashboard/static/src/js/lib/d3.min.js', |
||||
|
'hr_payroll_dashboard/static/src/xml/payroll_dashboard.xml' |
||||
|
], |
||||
|
}, |
||||
|
'images': ['static/description/banner.png'], |
||||
|
"external_dependencies": {"python": ["pandas"]}, |
||||
|
'license': "AGPL-3", |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': True, |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
## Module <hr_payroll_dashboard> |
||||
|
|
||||
|
#### 03.06.2024 |
||||
|
#### Version 16.0.1.0.0 |
||||
|
##### ADD |
||||
|
|
||||
|
- Initial commit for HR Payroll Dashboard |
@ -0,0 +1,27 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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 hr_attendance |
||||
|
from . import hr_contract |
||||
|
from . import hr_employee |
||||
|
from . import hr_expense |
||||
|
from . import hr_leave |
||||
|
from . import hr_payslip |
@ -0,0 +1,42 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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 api, fields, models |
||||
|
|
||||
|
|
||||
|
class HrAttendance(models.Model): |
||||
|
""" |
||||
|
This class extends the HR Attendance model to include additional fields |
||||
|
and functionalities. |
||||
|
""" |
||||
|
_inherit = 'hr.attendance' |
||||
|
|
||||
|
attendance_date = fields.Date(compute="_compute_attendance_date", |
||||
|
store=True, |
||||
|
help="The date of attendance based on the " |
||||
|
"check-in time.") |
||||
|
|
||||
|
@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,52 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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 api, fields, models |
||||
|
|
||||
|
|
||||
|
class Contract(models.Model): |
||||
|
""" |
||||
|
This class extends the HR Contract model to include additional fields |
||||
|
and functionalities. |
||||
|
""" |
||||
|
_inherit = 'hr.contract' |
||||
|
|
||||
|
state_label = fields.Char(compute="_compute_state_label", store=True, |
||||
|
help="A representation of the contract state.") |
||||
|
|
||||
|
@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 = [{'label': d[0], 'value': d[1]} for d in dat] |
||||
|
return data |
@ -0,0 +1,260 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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/>. |
||||
|
# |
||||
|
################################################################################ |
||||
|
import pandas as pd |
||||
|
from datetime import timedelta, datetime, date |
||||
|
from collections import defaultdict |
||||
|
from dateutil.relativedelta import relativedelta |
||||
|
from pytz import utc |
||||
|
from odoo.tools import float_utils |
||||
|
from odoo import api, fields, models |
||||
|
from odoo.http import request |
||||
|
|
||||
|
ROUNDING_FACTOR = 16 |
||||
|
|
||||
|
|
||||
|
class Employee(models.Model): |
||||
|
""" |
||||
|
This class extends the HR Employee model to include additional fields |
||||
|
and functionalities |
||||
|
""" |
||||
|
_inherit = 'hr.employee' |
||||
|
|
||||
|
is_manager = fields.Boolean(compute='_compute_is_manager', |
||||
|
help="Flag indicating whether the employee is a" |
||||
|
"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_user_id = self.env['hr.employee'].sudo().search([ |
||||
|
('user_id', '=', uid) |
||||
|
], limit=1) |
||||
|
employee = self.env['hr.employee'].sudo().search_read([ |
||||
|
('user_id', '=', uid)], limit=1) |
||||
|
attendance_count = self.env['hr.attendance'].sudo().search( |
||||
|
[('employee_id', '=', employee_user_id.id), |
||||
|
('attendance_date', '=', date.today())]) |
||||
|
manager_attendance_count = self.env['hr.attendance'].sudo().search( |
||||
|
[('attendance_date', '=', date.today())]) |
||||
|
leave_request_count = self.env['hr.leave'].sudo().search( |
||||
|
[('employee_id', '=', employee_user_id.id), |
||||
|
('request_date_from', '=', date.today())]) |
||||
|
manager_leave_request = self.env['hr.leave'].sudo().search( |
||||
|
[('request_date_from', '=', date.today())]) |
||||
|
employee_contracts = self.env['hr.contract'].sudo().search([ |
||||
|
('employee_id', '=', employee_user_id.id)]) |
||||
|
payslips = self.env['hr.payslip'].sudo().search([ |
||||
|
('employee_id', '=', employee_user_id.id)]) |
||||
|
salary_rules = self.env['hr.salary.rule'].sudo().search([]) |
||||
|
salary_structures = self.env['hr.payroll.structure'].sudo().search([]) |
||||
|
salary_rule_count = len(salary_rules) |
||||
|
salary_structure_count = len(salary_structures) |
||||
|
emp_leave = len(manager_leave_request) if employee_user_id.is_manager \ |
||||
|
else len(leave_request_count) |
||||
|
payslip_count = len(payslips) if not employee_user_id.is_manager \ |
||||
|
else len(self.env['hr.payslip'].sudo().search([])) |
||||
|
emp_contracts_count = len(employee_contracts) \ |
||||
|
if not employee_user_id.is_manager else len( |
||||
|
self.env['hr.contract'].sudo().search([])) |
||||
|
attendance_today = len(manager_attendance_count) \ |
||||
|
if employee_user_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): |
||||
|
""" |
||||
|
Calculate the total work days between two datetimes. |
||||
|
""" |
||||
|
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_batch(from_full, to_full, |
||||
|
resource) |
||||
|
day_total = defaultdict(float) |
||||
|
for start, stop, meta in intervals[resource.id]: |
||||
|
day_total[start.date()] += (stop - start).total_seconds() / 3600 |
||||
|
if compute_leaves: |
||||
|
intervals = calendar._work_intervals_batch(from_datetime, |
||||
|
to_datetime, resource, |
||||
|
domain) |
||||
|
else: |
||||
|
intervals = calendar._attendance_intervals_batch(from_datetime, |
||||
|
to_datetime, |
||||
|
resource) |
||||
|
day_hours = defaultdict(float) |
||||
|
for start, stop, meta in intervals[resource.id]: |
||||
|
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 leave details.""" |
||||
|
month_list = [format(datetime.now() - relativedelta(months=i), '%B %Y') |
||||
|
for i in range(5, -1, -1)] |
||||
|
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] |
||||
|
graph_result = [{ |
||||
|
'l_month': month, |
||||
|
'leave': {dept['name']: 0 for dept in departments} |
||||
|
} for month in month_list] |
||||
|
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 = [{ |
||||
|
'department': line['department_id'], |
||||
|
'l_month': line['month_year'], |
||||
|
'days': self.browse(line['employee_id']).get_work_days_dashboard( |
||||
|
fields.Datetime.from_string(line['date_from']), |
||||
|
fields.Datetime.from_string(line['date_to']) |
||||
|
) |
||||
|
} for line in results] |
||||
|
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""" |
||||
|
month_list = [format(datetime.now() - relativedelta(months=i), '%B %Y') |
||||
|
for i in range(5, -1, -1)] |
||||
|
uid = request.session.uid |
||||
|
employee = False |
||||
|
employee_user = self.env['hr.employee'].sudo().search_read([ |
||||
|
('user_id', '=', uid) |
||||
|
], limit=1) |
||||
|
employees = self.env['hr.employee'].sudo().search_read([], limit=1) |
||||
|
if employee_user: |
||||
|
employee = self.env['hr.employee'].sudo().search_read([ |
||||
|
('user_id', '=', uid) |
||||
|
], limit=1) |
||||
|
elif employees: |
||||
|
employee = self.env['hr.employee'].sudo().search_read([], limit=1) |
||||
|
graph_result = [{ |
||||
|
'l_month': month, |
||||
|
'leave': 0 |
||||
|
} for month in month_list] |
||||
|
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 |
||||
|
""" |
||||
|
if employee: |
||||
|
self.env.cr.execute(sql, (employee[0]['id'],)) |
||||
|
results = self.env.cr.dictfetchall() |
||||
|
leave_lines = [{ |
||||
|
'l_month': line['month_year'], |
||||
|
'days': self.browse( |
||||
|
line['employee_id']).get_work_days_dashboard( |
||||
|
fields.Datetime.from_string(line['date_from']), |
||||
|
fields.Datetime.from_string(line['date_to']) |
||||
|
) |
||||
|
} for line in results] |
||||
|
if leave_lines: |
||||
|
df = pd.DataFrame(leave_lines) |
||||
|
rf = df.groupby(['l_month']).sum() |
||||
|
result_lines = rf.to_dict('index') |
||||
|
for line in result_lines: |
||||
|
match = list(filter(lambda d: d['l_month'].replace( |
||||
|
' ', '') == line.replace(' ', ''), graph_result)) |
||||
|
if match: |
||||
|
match[0]['leave'] = result_lines[line]['days'] |
||||
|
for result in graph_result: |
||||
|
result['l_month'] = result['l_month'].split(' ')[:1][0].strip()[ |
||||
|
:3] \ |
||||
|
+ " " + result['l_month'].split(' ')[1:2][0] |
||||
|
return graph_result |
||||
|
else: |
||||
|
return False |
@ -0,0 +1,96 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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 datetime |
||||
|
from dateutil.relativedelta import relativedelta |
||||
|
from odoo import api, fields, models |
||||
|
from odoo.http import request |
||||
|
|
||||
|
|
||||
|
class HrExpense(models.Model): |
||||
|
""" |
||||
|
This class extends the HR Expense model to include additional fields |
||||
|
and functionalities. |
||||
|
""" |
||||
|
_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 = [format(datetime.now() - relativedelta(months=i), '%B %Y') |
||||
|
for i in range(11, -1, -1)] |
||||
|
approved_trend = [{'l_month': month, 'count': 0} |
||||
|
for month in month_list] |
||||
|
uid = request.session.uid |
||||
|
employee = False |
||||
|
employee_user = self.env['hr.employee'].sudo().search_read([ |
||||
|
('user_id', '=', uid) |
||||
|
], limit=1) |
||||
|
employees = self.env['hr.employee'].sudo().search_read([], limit=1) |
||||
|
if employee_user: |
||||
|
employee = self.env['hr.employee'].sudo().search_read([ |
||||
|
('user_id', '=', uid) |
||||
|
], limit=1) |
||||
|
elif employees: |
||||
|
employee = self.env['hr.employee'].sudo().search_read([], limit=1) |
||||
|
if employee: |
||||
|
employee_id = self.env['hr.employee'].browse(employee[0]['id']) |
||||
|
if not employee_id.is_manager: |
||||
|
sql = ('''select to_char(date, 'Month YYYY') as l_month, |
||||
|
count(id) from hr_expense |
||||
|
WHERE date BETWEEN CURRENT_DATE - INTERVAL '12 months' |
||||
|
AND CURRENT_DATE + interval '1 month - 1 day' |
||||
|
AND hr_expense.employee_id = %s |
||||
|
group by l_month''') |
||||
|
self.env.cr.execute(sql, (employee[0]['id'],)) |
||||
|
else: |
||||
|
sql = ('''select to_char(date, 'Month YYYY') as l_month, |
||||
|
count(id) from hr_expense WHERE date |
||||
|
BETWEEN CURRENT_DATE - INTERVAL |
||||
|
'12 months' AND CURRENT_DATE + interval '1 month - 1 day' |
||||
|
group by l_month''') |
||||
|
self.env.cr.execute(sql) |
||||
|
approved_data = cr.fetchall() |
||||
|
for line in approved_data: |
||||
|
match = list(filter(lambda d: d['l_month'].replace( |
||||
|
' ', '') == line[0].replace(' ', ''), approved_trend)) |
||||
|
if match: |
||||
|
match[0]['count'] = line[1] |
||||
|
for expense in approved_trend: |
||||
|
expense['l_month'] = expense[ |
||||
|
'l_month'].split(' ')[:1][0].strip()[ |
||||
|
:3] |
||||
|
graph_result = [{ |
||||
|
'values': approved_trend |
||||
|
}] |
||||
|
return graph_result |
||||
|
else: |
||||
|
return False |
@ -0,0 +1,50 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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 api, fields, models |
||||
|
|
||||
|
|
||||
|
class HrLeave(models.Model): |
||||
|
""" |
||||
|
This class extends the HR Leave model to include additional fields |
||||
|
and functionalities specific to the requirements of the application. |
||||
|
""" |
||||
|
_inherit = 'hr.leave' |
||||
|
|
||||
|
state_string = fields.Char(compute="_compute_state_string", store=True, |
||||
|
help="A representation of the leave state.") |
||||
|
|
||||
|
@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 = [{'label': d[0], 'value': d[1]} for d in dat] |
||||
|
return data |
@ -0,0 +1,53 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Anfas Faisal K (odoo@cybrosys.info) |
||||
|
# |
||||
|
# 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 api ,fields, models |
||||
|
|
||||
|
|
||||
|
class HrPayslip(models.Model): |
||||
|
""" |
||||
|
This class extends the Hr Payslip model to include additional fields |
||||
|
and functionalities. |
||||
|
""" |
||||
|
_inherit = 'hr.payslip' |
||||
|
_description = 'Employee Payroll' |
||||
|
|
||||
|
payslip_state = fields.Char(compute="_compute_payslip_state", store=True, |
||||
|
help="A representation of the payslip state.") |
||||
|
|
||||
|
@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 = [{'label': d[0], 'value': d[1]} for d in dat if d[0] is not None] |
||||
|
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: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 158 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,699 @@ |
|||||
|
<div style="background-color: #714B67; min-height: 600px; width: 100%; padding: 15px; position: relative;"> |
||||
|
<!-- TITLE BAR --> |
||||
|
<div |
||||
|
style="border-bottom: 1px solid #875A7B; padding: 15px; display: flex; justify-content: space-between; align-items: center;"> |
||||
|
<img src="assets/misc/cybrosys-logo.png" width="42" height="42" |
||||
|
style="width: 42px; height: 42px;"/> |
||||
|
<div> |
||||
|
<div style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;" |
||||
|
class="mr-2"> |
||||
|
<i class="fa fa-check mr-1"></i>Community |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- END OF TITLE BAR --> |
||||
|
|
||||
|
<!-- APP HERO --> |
||||
|
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;"> |
||||
|
Hr Payroll Dashboard |
||||
|
</h1> |
||||
|
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;"> |
||||
|
Detailed Dashboard View for Payroll Module. |
||||
|
</p> |
||||
|
<!-- END OF APP HERO --> |
||||
|
<img src="assets/screenshots/v16-hero.gif" |
||||
|
style="width: 75%; height: auto; position: absolute; margin-left: auto; margin-right: auto; top: 45%; left: 12%; right: auto;"/> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<!-- NAVIGATION SECTION --> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="border-bottom: 2px solid #714B67; padding: 15px 0px; margin-top: 300px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center mr-2" |
||||
|
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
||||
|
<img src="assets/misc/compass.png"/> |
||||
|
</div> |
||||
|
<h2 class="mt-2" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
||||
|
Explore This |
||||
|
Module</h2> |
||||
|
</div> |
||||
|
<div class="row my-4" style="font-family: 'Montserrat', sans-serif;"> |
||||
|
<div class="col-sm-12 col-md-6 my-3"> |
||||
|
<a href="#overview"> |
||||
|
<div class="d-flex justify-content-between align-items-center" |
||||
|
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
||||
|
<div> |
||||
|
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Overview</span> |
||||
|
<span |
||||
|
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">Learn |
||||
|
more about this |
||||
|
module</span> |
||||
|
</div> |
||||
|
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-sm-12 col-md-6 my-3"> |
||||
|
<a href="#features"> |
||||
|
<div class="d-flex justify-content-between align-items-center" |
||||
|
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
||||
|
<div> |
||||
|
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Features</span> |
||||
|
<span |
||||
|
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View |
||||
|
features of this |
||||
|
module</span> |
||||
|
</div> |
||||
|
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-sm-12 col-md-6 my-3"> |
||||
|
<a href="#screenshots"> |
||||
|
<div class="d-flex justify-content-between align-items-center" |
||||
|
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
||||
|
<div> |
||||
|
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Screenshots</span> |
||||
|
<span |
||||
|
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View |
||||
|
screenshots of this |
||||
|
module</span> |
||||
|
</div> |
||||
|
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- END OF NAVIGATION SECTION --> |
||||
|
|
||||
|
<!-- OVERVIEW SECTION --> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" |
||||
|
id="overview"> |
||||
|
<div class="d-flex justify-content-center align-items-center mr-2" |
||||
|
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
||||
|
<img src="assets/misc/pie-chart.png"/> |
||||
|
</div> |
||||
|
<h2 class="mt-2" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
||||
|
Overview |
||||
|
</h2> |
||||
|
</div> |
||||
|
<div class="row" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;"> |
||||
|
<div class="col-sm-12 py-4"> |
||||
|
This module helps you to see the Overview of Payroll, here You can see the details of Attendance, Leaves, Payslip contracts, etc. |
||||
|
</div> |
||||
|
<div class="alert alert-primary mt-4"> |
||||
|
<hr/> |
||||
|
You must need module - Odoo 16 Payroll in your system |
||||
|
<br/> |
||||
|
<div class="col-lg-12 d-flex justify-content-center align-items-center" |
||||
|
style="margin: 2rem 0;"> |
||||
|
<p class=" mt8" style="font-family:Roboto ; color: #280135;">Odoo 16 Payroll - link to download : <a href="https://apps.odoo.com/apps/modules/16.0/hr_payroll_community/"> https://apps.odoo.com/apps/modules/16.0/hr_payroll_community/</a> </p> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- END OF OVERVIEW SECTION --> |
||||
|
|
||||
|
<!-- FEATURES SECTION --> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" |
||||
|
id="features"> |
||||
|
<div class="d-flex justify-content-center align-items-center mr-2" |
||||
|
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
||||
|
<img src="assets/misc/features.png"/> |
||||
|
</div> |
||||
|
<h2 class="mt-2" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
||||
|
Features |
||||
|
</h2> |
||||
|
</div> |
||||
|
<div class="row" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;"> |
||||
|
<div class="col-sm-12 col-md-6"> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="margin-top: 40px; margin-bottom: 40px"> |
||||
|
<img src="assets/misc/check-box.png" class="mr-2"/> |
||||
|
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Payslip Details</span> |
||||
|
</div> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="margin-top: 30px; margin-bottom: 30px"> |
||||
|
<img src="assets/misc/check-box.png" class="mr-2"/> |
||||
|
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Contract Details </span> |
||||
|
</div> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="margin-top: 30px; margin-bottom: 30px"> |
||||
|
<img src="assets/misc/check-box.png" class="mr-2"/> |
||||
|
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Leave Request Details |
||||
|
</span> |
||||
|
</div> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="margin-top: 30px; margin-bottom: 30px"> |
||||
|
<img src="assets/misc/check-box.png" class="mr-2"/> |
||||
|
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Attendance Details |
||||
|
</span> |
||||
|
</div> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="margin-top: 30px; margin-bottom: 30px"> |
||||
|
<img src="assets/misc/check-box.png" class="mr-2"/> |
||||
|
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Salary Rules Details |
||||
|
</span> |
||||
|
</div> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="margin-top: 30px; margin-bottom: 30px"> |
||||
|
<img src="assets/misc/check-box.png" class="mr-2"/> |
||||
|
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Salary Structures Details |
||||
|
</span> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- END OF FEATURES SECTION --> |
||||
|
|
||||
|
<!-- SCREENSHOTS SECTION --> |
||||
|
<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 the Payroll Module to view the Payroll Dashboard</h4> |
||||
|
|
||||
|
<img src="assets/screenshots/1.png" |
||||
|
class="img-responsive img-thumbnail border" width="100%" |
||||
|
height="auto"/> |
||||
|
</div> |
||||
|
<div class="col-lg-12 my-3"> |
||||
|
<h4 class="mt-2" |
||||
|
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;"> |
||||
|
Dashboard Tiles</h4> |
||||
|
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> |
||||
|
The Tiles display a list of the Attendance orders,Leaves, |
||||
|
Payslips,Contracts,Salary Rules, and Salary Structures. </p> |
||||
|
<img src="assets/screenshots/2.png" |
||||
|
class="img-responsive img-thumbnail border" width="100%" |
||||
|
height="auto"/> |
||||
|
</div> |
||||
|
<div class="col-lg-12 my-3"> |
||||
|
<h4 class="mt-3" |
||||
|
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/3.png" |
||||
|
class="img-responsive img-thumbnail border" width="100%" |
||||
|
height="auto"/> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-lg-12 my-3"> |
||||
|
<h4 class="mt-3" |
||||
|
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/4.png" |
||||
|
class="img-responsive img-thumbnail border" width="100%" |
||||
|
height="auto"/> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-lg-12 my-3"> |
||||
|
<h4 class="mt-3" |
||||
|
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 be view here. |
||||
|
</p> |
||||
|
<img src="assets/screenshots/5.png" |
||||
|
class="img-responsive img-thumbnail border" width="100%" |
||||
|
height="auto"/> |
||||
|
</div> |
||||
|
<div class="col-lg-12 my-3"> |
||||
|
<h4 class="mt-3" |
||||
|
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/6.png" |
||||
|
class="img-responsive img-thumbnail border" width="100%" |
||||
|
height="auto"/> |
||||
|
</div> |
||||
|
<!-- END OF SCREENSHOTS SECTION --> |
||||
|
<!-- RELATED PRODUCTS --> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center mr-2" |
||||
|
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
||||
|
<img src="assets/misc/categories.png"/> |
||||
|
</div> |
||||
|
<h2 class="mt-2" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
||||
|
Related |
||||
|
Products |
||||
|
</h2> |
||||
|
</div> |
||||
|
<div class="row"> |
||||
|
<div class="col-sm-12"> |
||||
|
<div id="demo1" class="row carousel slide" data-ride="carousel"> |
||||
|
<!-- The slideshow --> |
||||
|
<div class="carousel-inner" style="padding: 30px;"> |
||||
|
<div class="carousel-item" style="min-height: 198.656px;"> |
||||
|
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
||||
|
style="float:left"> |
||||
|
<a href="https://apps.odoo.com/apps/modules/16.0/odoo_dynamic_dashboard/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/11.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/16.0/crm_dashboard/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/12.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/16.0/dashboard_pos/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/13.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="carousel-item active" |
||||
|
style="min-height: 198.656px;"> |
||||
|
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
||||
|
style="float:left"> |
||||
|
<a href="https://apps.odoo.com/apps/modules/16.0/inventory_stock_dashboard_odoo/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/14.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/16.0/project_dashboard_odoo/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/15.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/16.0/portal_dashboard/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/16.png"> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- Left and right controls --> |
||||
|
<a class="carousel-control-prev" href="#demo1" |
||||
|
data-slide="prev" |
||||
|
style="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="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 RELATED PRODUCTS --> |
||||
|
|
||||
|
<!-- OUR SERVICES --> |
||||
|
|
||||
|
<div class="d-flex align-items-center" |
||||
|
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center mr-2" |
||||
|
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
||||
|
<img src="assets/misc/star.png"/> |
||||
|
</div> |
||||
|
<h2 class="mt-2" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
||||
|
Our Services |
||||
|
</h2> |
||||
|
</div> |
||||
|
|
||||
|
<div class="container my-5"> |
||||
|
<div class="row"> |
||||
|
<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> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<!-- END OF OUR SERVICES --> |
||||
|
|
||||
|
<!-- OUR INDUSTRIES --> |
||||
|
|
||||
|
<div class="d-flex align-items-center" |
||||
|
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center mr-2" |
||||
|
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
||||
|
<img src="assets/misc/corporate.png"/> |
||||
|
</div> |
||||
|
<h2 class="mt-2" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
||||
|
Our |
||||
|
Industries |
||||
|
</h2> |
||||
|
</div> |
||||
|
|
||||
|
<div class="container my-5"> |
||||
|
<div class="row"> |
||||
|
<div class="col-lg-3"> |
||||
|
<div class="my-4 d-flex flex-column justify-content-center" |
||||
|
style="background-color: #f6f8f9 !important; border-radius: 0px; 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: 0px; 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: 0px; 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: 0px; 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: 0px; 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: 0px; 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: 0px; 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: 0px; 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> |
||||
|
</div> |
||||
|
|
||||
|
<!-- END OF OUR INDUSTRIES --> |
||||
|
|
||||
|
<!-- SUPPORT --> |
||||
|
<div class="d-flex align-items-center" |
||||
|
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center mr-2" |
||||
|
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
||||
|
<img src="assets/misc/customer-support.png"/> |
||||
|
</div> |
||||
|
<h2 class="mt-2" |
||||
|
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
||||
|
Support |
||||
|
</h2> |
||||
|
</div> |
||||
|
<div class="container mt-5"> |
||||
|
<div class="row"> |
||||
|
<div class="col-sm-12 col-md-6"> |
||||
|
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;"> |
||||
|
<div class="mr-4" |
||||
|
style="background-color: #714B67; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;"> |
||||
|
<img src="assets/misc/support.png" height="48" |
||||
|
width="48" style="width: 42px; height: 42px;"/> |
||||
|
</div> |
||||
|
<div> |
||||
|
<h4>Need Help?</h4> |
||||
|
<p style="line-height: 100%;">Got questions or need |
||||
|
help? Get in touch.</p> |
||||
|
<a href="mailto:odoo@cybrosys.com"> |
||||
|
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;"> |
||||
|
odoo@cybrosys.com</p> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-sm-12 col-md-6"> |
||||
|
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;"> |
||||
|
<div class="mr-4" |
||||
|
style="background-color: #2AC44D; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;"> |
||||
|
<img src="assets/misc/whatsapp.png" height="52" |
||||
|
width="52" style="width: 52px; height: 52px;"/> |
||||
|
</div> |
||||
|
<div> |
||||
|
<h4>WhatsApp</h4> |
||||
|
<p style="line-height: 100%;">Say hi to us on |
||||
|
WhatsApp!</p> |
||||
|
<a href="https://api.whatsapp.com/send?phone=918606827707"> |
||||
|
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;"> |
||||
|
+91 86068 |
||||
|
27707</p> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="row"> |
||||
|
<div class="col-sm-12 my-5 d-flex justify-content-center align-items-center"> |
||||
|
<img src="assets/misc/logo.png" width="144" height="31" |
||||
|
style="width:144px; height: 31px; margin-top: 40px;"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- END OF SUPPORT --> |
||||
|
</div> |
||||
|
</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,431 @@ |
|||||
|
#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; |
||||
|
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; |
||||
|
} |
@ -0,0 +1,748 @@ |
|||||
|
odoo.define('hr_payroll_dashboard.PayrollDashboard', function (require) { |
||||
|
"use strict"; |
||||
|
var AbstractAction = require('web.AbstractAction'); |
||||
|
var core = require('web.core'); |
||||
|
var rpc = require('web.rpc'); |
||||
|
var _t = core._t; |
||||
|
var QWeb = core.qweb; |
||||
|
|
||||
|
var PayrollDashboard = AbstractAction.extend({ |
||||
|
template: 'PayrollDashboardMain', |
||||
|
|
||||
|
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(){ |
||||
|
/* |
||||
|
* Open the Employee Payslips window. |
||||
|
*/ |
||||
|
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(){ |
||||
|
/* |
||||
|
* Open the Employee Contracts window. |
||||
|
*/ |
||||
|
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,form', |
||||
|
views: [[false, 'list'],[false, 'form']], |
||||
|
context: {"create": false}, |
||||
|
domain: domain, |
||||
|
}); |
||||
|
}, |
||||
|
_onclickAttendances: function(){ |
||||
|
/* |
||||
|
* Open the Attendance window. |
||||
|
*/ |
||||
|
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,form', |
||||
|
views: [[false, 'list'],[false, 'form']], |
||||
|
context: {"create": false}, |
||||
|
domain: domain, |
||||
|
}); |
||||
|
}, |
||||
|
_onclickLeave: function(){ |
||||
|
/* |
||||
|
* Open the Employee Leave window. |
||||
|
*/ |
||||
|
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,form', |
||||
|
views: [[false, 'list'],[false, 'form']], |
||||
|
context: {"create": false}, |
||||
|
domain: domain, |
||||
|
}); |
||||
|
}, |
||||
|
_onClickSalaryRules: function(){ |
||||
|
/* |
||||
|
* Open the Salary Rules window. |
||||
|
*/ |
||||
|
var self = this; |
||||
|
return self.do_action({ |
||||
|
name: _t("Salary Rules"), |
||||
|
type: 'ir.actions.act_window', |
||||
|
res_model: 'hr.salary.rule', |
||||
|
view_mode: 'tree,form', |
||||
|
views: [[false, 'list'],[false, 'form']], |
||||
|
context: {"create": false}, |
||||
|
}); |
||||
|
}, |
||||
|
_onClickSalaryStructure: function(){ |
||||
|
/* |
||||
|
* Open the Salary Structures window. |
||||
|
*/ |
||||
|
var self = this; |
||||
|
return self.do_action({ |
||||
|
name: _t("Salary Structures"), |
||||
|
type: 'ir.actions.act_window', |
||||
|
res_model: 'hr.payroll.structure', |
||||
|
view_mode: 'tree,form', |
||||
|
views: [[false, 'list'],[false, 'form']], |
||||
|
context: {"create": false}, |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
render_expense_graph:function(){ |
||||
|
/** |
||||
|
* Render the expense graph. |
||||
|
* This function retrieves employee expense data and renders it as a graph using D3.js. |
||||
|
* The graph displays the monthly expenses of employees. |
||||
|
*/ |
||||
|
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) { |
||||
|
if(data){ |
||||
|
data.forEach(function(d) { |
||||
|
d.values.forEach(function(d) { |
||||
|
d.l_month = d.l_month; |
||||
|
d.count = +d.count; |
||||
|
}); |
||||
|
}); |
||||
|
var margin = {top: 30, right: 10, bottom: 30, left: 30}, |
||||
|
width = 400 - margin.left - margin.right, |
||||
|
height = 250 - margin.top - margin.bottom |
||||
|
// Set the ranges
|
||||
|
var x = d3.scale.ordinal() |
||||
|
.rangeRoundBands([0, width], 1); |
||||
|
var y = d3.scale.linear() |
||||
|
.range([height, 0]); |
||||
|
// Define the axes
|
||||
|
var xAxis = d3.svg.axis().scale(x) |
||||
|
.orient("bottom"); |
||||
|
var yAxis = d3.svg.axis().scale(y) |
||||
|
.orient("left").ticks(5); |
||||
|
x.domain(data[0].values.map(function(d) { return d.l_month; })); |
||||
|
y.domain([0, d3.max(data[0].values, d => d.count)]) |
||||
|
var svg = d3.select(elem[0]).append("svg") |
||||
|
.attr("width", width + margin.left + margin.right) |
||||
|
.attr("height", height + margin.top + margin.bottom) |
||||
|
.append("g") |
||||
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
||||
|
// Add the X Axis
|
||||
|
svg.append("g") |
||||
|
.attr("class", "x axis") |
||||
|
.attr("transform", "translate(0," + height + ")") |
||||
|
.call(xAxis); |
||||
|
// Add the Y Axis
|
||||
|
svg.append("g") |
||||
|
.attr("class", "y axis") |
||||
|
.call(yAxis); |
||||
|
var line = d3.svg.line() |
||||
|
.x(function(d) {return x(d.l_month); }) |
||||
|
.y(function(d) {return y(d.count); }); |
||||
|
let lines = svg.append('g') |
||||
|
.attr('class', 'lines'); |
||||
|
lines.selectAll('.line-group') |
||||
|
.data(data).enter() |
||||
|
.append('g') |
||||
|
.attr('class', 'line-group') |
||||
|
.append('path') |
||||
|
.attr('class', 'line') |
||||
|
.attr('d', function(d) { return line(d.values); }) |
||||
|
.style('stroke', (d, i) => color(i)); |
||||
|
lines.selectAll("circle-group") |
||||
|
.data(data).enter() |
||||
|
.append("g") |
||||
|
.selectAll("circle") |
||||
|
.data(function(d) { return d.values;}).enter() |
||||
|
.append("g") |
||||
|
.attr("class", "circle") |
||||
|
.append("circle") |
||||
|
.attr("cx", function(d) { return x(d.l_month)}) |
||||
|
.attr("cy", function(d) { return y(d.count)}) |
||||
|
.attr("r", 3); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
render_leave_graph:function(){ |
||||
|
/** |
||||
|
* Render the leave graph. |
||||
|
* This function retrieves department leave data and renders it as a graph using D3.js. |
||||
|
* The graph displays the total leave taken by each department over a period of time. |
||||
|
*/ |
||||
|
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) { |
||||
|
if (data){ |
||||
|
var fData = data[0]; |
||||
|
var dept = data[1]; |
||||
|
var id = self.$('.leave_graph')[0]; |
||||
|
var barColor = '#ff618a'; |
||||
|
fData.forEach(function(d){ |
||||
|
var total = 0; |
||||
|
for (var dpt in dept){ |
||||
|
total += d.leave[dept[dpt]]; |
||||
|
} |
||||
|
d.total=total; |
||||
|
}); |
||||
|
// function to handle histogram.
|
||||
|
function histoGram(fD){ |
||||
|
var hG={}, hGDim = {t: 60, r: 0, b: 30, l: 0}; |
||||
|
hGDim.w = 350 - hGDim.l - hGDim.r, |
||||
|
hGDim.h = 200 - hGDim.t - hGDim.b; |
||||
|
//create svg for histogram.
|
||||
|
var hGsvg = d3.select(id).append("svg") |
||||
|
.attr("width", hGDim.w + hGDim.l + hGDim.r) |
||||
|
.attr("height", hGDim.h + hGDim.t + hGDim.b).append("g") |
||||
|
.attr("transform", "translate(" + hGDim.l + "," + hGDim.t + ")"); |
||||
|
// create function for x-axis mapping.
|
||||
|
var x = d3.scale.ordinal().rangeRoundBands([0, hGDim.w], 0.1) |
||||
|
.domain(fD.map(function(d) { return d[0]; })); |
||||
|
// Add x-axis to the histogram svg.
|
||||
|
hGsvg.append("g").attr("class", "x axis") |
||||
|
.attr("transform", "translate(0," + hGDim.h + ")") |
||||
|
.call(d3.svg.axis().scale(x).orient("bottom")); |
||||
|
// Create function for y-axis map.
|
||||
|
var y = d3.scale.linear().range([hGDim.h, 0]) |
||||
|
.domain([0, d3.max(fD, function(d) { return d[1]; })]); |
||||
|
// Create bars for histogram to contain rectangles and freq labels.
|
||||
|
var bars = hGsvg.selectAll(".bar").data(fD).enter() |
||||
|
.append("g").attr("class", "bar"); |
||||
|
//create the rectangles.
|
||||
|
bars.append("rect") |
||||
|
.attr("x", function(d) { return x(d[0]); }) |
||||
|
.attr("y", function(d) { return y(d[1]); }) |
||||
|
.attr("width", x.rangeBand()) |
||||
|
.attr("height", function(d) { return hGDim.h - y(d[1]); }) |
||||
|
.attr('fill',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(){ |
||||
|
/** |
||||
|
* Render various graphs related to employee data. |
||||
|
* This function renders different graphs including employee contracts, time off, payslips, |
||||
|
* leave, and expenses. It checks if the current user is logged in as an employee and then |
||||
|
* calls individual functions to render each graph. |
||||
|
*/ |
||||
|
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(){ |
||||
|
/** |
||||
|
* Render the 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 = ['#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) { |
||||
|
if (data){ |
||||
|
var segColor = {}; |
||||
|
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")"); |
||||
|
var pie = d3.layout.pie().value(function(d){return d.value;}); |
||||
|
var arc = d3.svg.arc().outerRadius(r); |
||||
|
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice"); |
||||
|
arcs.append("svg:path") |
||||
|
.attr("fill", function(d, i){ |
||||
|
return color(i); |
||||
|
}) |
||||
|
.attr("d", function (d) { |
||||
|
return arc(d); |
||||
|
}); |
||||
|
|
||||
|
var legend = d3.select(elem[0]).append("table").attr('class','legend'); |
||||
|
// create one row per segment.
|
||||
|
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr"); |
||||
|
// create the first column for each segment.
|
||||
|
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect") |
||||
|
.attr("width", '16').attr("height", '16') |
||||
|
.attr("fill",function(d, i){ return color(i) }); |
||||
|
// create the second column for each segment.
|
||||
|
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;}); |
||||
|
// create the third column for each segment.
|
||||
|
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;") |
||||
|
.text(function(d){ return d.value;}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
render_employee_payslips_graph(){ |
||||
|
/** |
||||
|
* Render the Employee payslips graph. |
||||
|
*/ |
||||
|
var self = this; |
||||
|
var w = 200; |
||||
|
var h = 200; |
||||
|
var r = h/2; |
||||
|
var elem = this.$('.emp_payslips_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.payslip", |
||||
|
method: "get_employee_payslips", |
||||
|
}).then(function (data) { |
||||
|
if(data){ |
||||
|
var segColor = {}; |
||||
|
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")"); |
||||
|
var pie = d3.layout.pie().value(function(d){return d.value;}); |
||||
|
var arc = d3.svg.arc().outerRadius(r); |
||||
|
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice"); |
||||
|
arcs.append("svg:path") |
||||
|
.attr("fill", function(d, i){ |
||||
|
return color(i); |
||||
|
}) |
||||
|
.attr("d", function (d) { |
||||
|
return arc(d); |
||||
|
}); |
||||
|
|
||||
|
var legend = d3.select(elem[0]).append("table").attr('class','legend'); |
||||
|
|
||||
|
// create one row per segment.
|
||||
|
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr"); |
||||
|
|
||||
|
// create the first column for each segment.
|
||||
|
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect") |
||||
|
.attr("width", '16').attr("height", '16') |
||||
|
.attr("fill",function(d, i){ return color(i) }); |
||||
|
|
||||
|
// create the second column for each segment.
|
||||
|
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;}); |
||||
|
|
||||
|
// create the third column for each segment.
|
||||
|
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;") |
||||
|
.text(function(d){ return d.value;}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
render_employee_contracts_graph(){ |
||||
|
/** |
||||
|
* Render the employee contracts graph. |
||||
|
*/ |
||||
|
var self = this; |
||||
|
var w = 200; |
||||
|
var h = 200; |
||||
|
var r = h/2; |
||||
|
var elem = this.$('.emp_contracts_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.contract", |
||||
|
method: "get_employee_contract", |
||||
|
}).then(function (data) { |
||||
|
if(data){ |
||||
|
var segColor = {}; |
||||
|
var vis = d3.select(elem[0]).append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")"); |
||||
|
var pie = d3.layout.pie().value(function(d){return d.value;}); |
||||
|
var arc = d3.svg.arc().outerRadius(r); |
||||
|
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice"); |
||||
|
arcs.append("svg:path") |
||||
|
.attr("fill", function(d, i){ |
||||
|
return color(i); |
||||
|
}) |
||||
|
.attr("d", function (d) { |
||||
|
return arc(d); |
||||
|
}); |
||||
|
|
||||
|
var legend = d3.select(elem[0]).append("table").attr('class','legend'); |
||||
|
|
||||
|
// create one row per segment.
|
||||
|
var tr = legend.append("tbody").selectAll("tr").data(data).enter().append("tr"); |
||||
|
|
||||
|
// create the first column for each segment.
|
||||
|
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect") |
||||
|
.attr("width", '16').attr("height", '16') |
||||
|
.attr("fill",function(d, i){ return color(i) }); |
||||
|
|
||||
|
// create the second column for each segment.
|
||||
|
tr.append("td").attr("style","font-weight: bold;").text(function(d){ return d.label;}); |
||||
|
|
||||
|
// create the third column for each segment.
|
||||
|
tr.append("td").attr("class",'legendFreq').attr("style","border: 5px solid transparent; font-weight: bold;") |
||||
|
.text(function(d){ return d.value;}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
}, |
||||
|
update_leave_trend: function(){ |
||||
|
/** |
||||
|
* Update the leave trend graph. |
||||
|
*/ |
||||
|
var self = this; |
||||
|
rpc.query({ |
||||
|
model: "hr.employee", |
||||
|
method: "employee_leave_trend", |
||||
|
}).then(function (data) { |
||||
|
if(data){ |
||||
|
var elem = self.$('.leave_trend'); |
||||
|
var margin = {top: 30, right: 20, bottom: 30, left: 80}, |
||||
|
width = 500 - margin.left - margin.right, |
||||
|
height = 250 - margin.top - margin.bottom; |
||||
|
|
||||
|
// Set the ranges
|
||||
|
var x = d3.scale.ordinal() |
||||
|
.rangeRoundBands([0, width], 1); |
||||
|
|
||||
|
var y = d3.scale.linear() |
||||
|
.range([height, 0]); |
||||
|
|
||||
|
// Define the axes
|
||||
|
var xAxis = d3.svg.axis().scale(x) |
||||
|
.orient("bottom"); |
||||
|
|
||||
|
var yAxis = d3.svg.axis().scale(y) |
||||
|
.orient("left").ticks(5); |
||||
|
|
||||
|
var valueline = d3.svg.line() |
||||
|
.x(function(d) { return x(d.l_month); }) |
||||
|
.y(function(d) { return y(d.leave); }); |
||||
|
|
||||
|
|
||||
|
var svg = d3.select(elem[0]).append("svg") |
||||
|
.attr("width", width + margin.left + margin.right) |
||||
|
.attr("height", height + margin.top + margin.bottom) |
||||
|
.append("g") |
||||
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
||||
|
|
||||
|
x.domain(data.map(function(d) { return d.l_month; })); |
||||
|
y.domain([0, d3.max(data, function(d) { return d.leave; })]); |
||||
|
|
||||
|
// Add the X Axis
|
||||
|
svg.append("g") |
||||
|
.attr("class", "x axis") |
||||
|
.attr("transform", "translate(0," + height + ")") |
||||
|
.call(xAxis); |
||||
|
|
||||
|
// Add the Y Axis
|
||||
|
svg.append("g") |
||||
|
.attr("class", "y axis") |
||||
|
.call(yAxis); |
||||
|
|
||||
|
svg.append("path") |
||||
|
.attr("class", "line") |
||||
|
.attr("d", valueline(data)); |
||||
|
|
||||
|
// Add the scatterplot
|
||||
|
svg.selectAll("dot") |
||||
|
.data(data) |
||||
|
.enter().append("circle") |
||||
|
.attr("r", 3) |
||||
|
.attr("cx", function(d) { return x(d.l_month); }) |
||||
|
.attr("cy", function(d) { return y(d.leave); }) |
||||
|
// .on('mouseover', function() { d3.select(this).transition().duration(500).ease("elastic").attr('r', 3 * 2) })
|
||||
|
// .on('mouseout', function() { d3.select(this).transition().duration(500).ease("in-out").attr('r', 3) });
|
||||
|
.on("mouseover", function() { tooltip.style("display", null); |
||||
|
d3.select(this).transition().duration(500).ease("elastic").attr('r', 3 * 2) |
||||
|
}) |
||||
|
.on("mouseout", function() { tooltip.style("display", "none"); |
||||
|
d3.select(this).transition().duration(500).ease("in-out").attr('r', 3) |
||||
|
}) |
||||
|
.on("mousemove", function(d) { |
||||
|
var xPosition = d3.mouse(this)[0] - 15; |
||||
|
var yPosition = d3.mouse(this)[1] - 25; |
||||
|
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")"); |
||||
|
tooltip.select("text").text(d.leave); |
||||
|
}); |
||||
|
|
||||
|
var tooltip = svg.append("g") |
||||
|
.attr("class", "tooltip") |
||||
|
.style("display", "none"); |
||||
|
|
||||
|
tooltip.append("rect") |
||||
|
.attr("width", 30) |
||||
|
.attr("height", 20) |
||||
|
.attr("fill", "black") |
||||
|
.style("opacity", 0.5); |
||||
|
|
||||
|
tooltip.append("text") |
||||
|
.attr("x", 15) |
||||
|
.attr("dy", "1.2em") |
||||
|
.style("text-anchor", "middle") |
||||
|
.attr("font-size", "12px") |
||||
|
.attr("font-weight", "bold"); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
core.action_registry.add('payroll_dashboard', PayrollDashboard); |
||||
|
return PayrollDashboard; |
||||
|
}); |
@ -0,0 +1,268 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!--Payroll Dashboard View--> |
||||
|
<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="EmployeeWarning"> |
||||
|
<div class="row" style="margin-top:1%;margin-left:40%;"> |
||||
|
<div class="col-md-12 "> |
||||
|
<div> |
||||
|
<div class="rounded mx-auto d-block"> |
||||
|
<div> |
||||
|
<p style="color:red;">Error : Could not find employee linked to user</p> |
||||
|
<p style="color:red;">Please contact system admin for the setup</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
<t t-name="EmployeeDetails"> |
||||
|
<link rel="stylesheet" |
||||
|
href="/hr_payroll_dashboard/static/src/css/dashboard.css"/> |
||||
|
<div class="row main-section"> |
||||
|
<div class="col-md-4 col-sm-6 oh-payroll"> |
||||
|
<div class="oh-card o_attendances" 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 oh-payroll"> |
||||
|
<div class="oh-card o_leave" 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 oh-payroll"> |
||||
|
<div class="oh-card o_payslips" 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 oh-payroll"> |
||||
|
<div class="oh-card o_contracts" 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 oh-payroll"> |
||||
|
<div class="oh-card o_salary_rules" 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'/> |
||||
|
</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 oh-payroll"> |
||||
|
<div class="oh-card o_salary_structures" 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'/> |
||||
|
</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/> |
||||
|
<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,12 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<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"/> |
||||
|
</odoo> |