@ -0,0 +1,46 @@ |
|||
.. 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 |
|||
|
|||
Project Dashboard |
|||
================= |
|||
In this dashboard you can get Detailed Dashboard View for Project |
|||
|
|||
Configuration |
|||
============= |
|||
* No need of any configuration. |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (AGPL v3). |
|||
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
|||
|
|||
Credits |
|||
------- |
|||
* Developer: (V17) Mruthul Raj, 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 `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import controllers |
|||
from . import models |
@ -0,0 +1,48 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
{ |
|||
'name': 'Project Dashboard', |
|||
'version': '17.0.1.0.0', |
|||
'category': 'Extra Tools', |
|||
'summary': """Get a Detailed View for Project.""", |
|||
'description': """In this dashboard user can get the Detailed Information |
|||
about Project, Task, Employee, Hours recorded, Total Margin and Total |
|||
Sale Orders.""", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'depends': ['sale_management', 'project', 'sale_timesheet'], |
|||
'data': ['views/dashboard_views.xml'], |
|||
'assets': { |
|||
'web.assets_backend': [ |
|||
'project_dashboard_odoo/static/src/js/dashboard.js', |
|||
'project_dashboard_odoo/static/src/css/dashboard.css', |
|||
'project_dashboard_odoo/static/src/xml/dashboard_templates.xml', |
|||
'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js' |
|||
]}, |
|||
'images': ['static/description/banner.png'], |
|||
'license': 'AGPL-3', |
|||
'installable': True, |
|||
'application': False, |
|||
'auto_install': False, |
|||
} |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import project_dashboard_odoo |
@ -0,0 +1,380 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
import datetime |
|||
from odoo import http |
|||
from odoo.http import request |
|||
|
|||
|
|||
class ProjectFilter(http.Controller): |
|||
"""The ProjectFilter class provides the filter option to the js. |
|||
When applying the filter returns the corresponding data.""" |
|||
|
|||
@http.route('/project/task/count', auth='public', type='json') |
|||
def get_project_task_count(self): |
|||
"""Summary: |
|||
when the page is loaded, get the data from different models and |
|||
transfer to the js file. |
|||
Return a dictionary variable. |
|||
Return: |
|||
type:It is a dictionary variable. This dictionary contains data for |
|||
the project task graph.""" |
|||
project_name = [] |
|||
total_task = [] |
|||
colors = [] |
|||
user_employee = request.env.user.partner_id |
|||
if user_employee.user_has_groups('project.group_project_manager'): |
|||
project_ids = request.env['project.project'].search([]) |
|||
else: |
|||
project_ids = request.env['project.project'].search( |
|||
[('user_id', '=', request.env.uid)]) |
|||
for project_id in project_ids: |
|||
project_name.append(project_id.name) |
|||
task = request.env['project.task'].search_count( |
|||
[('project_id', '=', project_id.id)]) |
|||
total_task.append(task) |
|||
color_code = request.env['project.project'].get_color_code() |
|||
colors.append(color_code) |
|||
return { |
|||
'project': project_name, |
|||
'task': total_task, |
|||
'color': colors |
|||
} |
|||
|
|||
@http.route('/employee/timesheet', auth='public', type='json') |
|||
def get_top_timesheet_employees(self): |
|||
"""Summary: |
|||
when the page is loaded, get the data for the timesheet graph. |
|||
Return: |
|||
type:It is a list. This list contains data that affects the graph |
|||
of employees.""" |
|||
query = '''select hr_employee.name as employee,sum(unit_amount) as unit |
|||
from account_analytic_line |
|||
inner join hr_employee on hr_employee.id = |
|||
account_analytic_line.employee_id |
|||
group by hr_employee.id ORDER |
|||
BY unit DESC Limit 10 ''' |
|||
request._cr.execute(query) |
|||
top_product = request._cr.dictfetchall() |
|||
unit = [record.get('unit') for record in top_product] |
|||
employee = [record.get('employee') for record in top_product] |
|||
return [unit, employee] |
|||
|
|||
@http.route('/project/filter', auth='public', type='json') |
|||
def project_filter(self): |
|||
"""Summary: |
|||
transferring data to the selection field that works as a filter |
|||
Returns: |
|||
type:list of lists, it contains the data for the corresponding |
|||
filter.""" |
|||
project_list = [] |
|||
employee_list = [] |
|||
project_ids = request.env['project.project'].search([]) |
|||
employee_ids = request.env['hr.employee'].search([]) |
|||
# getting partner data |
|||
for employee_id in employee_ids: |
|||
dic = {'name': employee_id.name, |
|||
'id': employee_id.id} |
|||
employee_list.append(dic) |
|||
for project_id in project_ids: |
|||
dic = {'name': project_id.name, |
|||
'id': project_id.id} |
|||
project_list.append(dic) |
|||
return [project_list, employee_list] |
|||
|
|||
@http.route('/project/filter-apply', auth='public', type='json') |
|||
def project_filter_apply(self, **kw): |
|||
"""Summary: |
|||
transferring data after filter 9th applied |
|||
Args: |
|||
kw(dict):This parameter contains the value of selection field |
|||
Returns: |
|||
type:dict, it contains the data for the corresponding |
|||
filtrated transferring data to ui after filtration.""" |
|||
data = kw['data'] |
|||
# checking the employee selected or not |
|||
if data['employee'] == 'null': |
|||
emp_selected = [employee.id for employee in |
|||
request.env['hr.employee'].search([])] |
|||
else: |
|||
emp_selected = [int(data['employee'])] |
|||
start_date = data['start_date'] |
|||
end_date = data['end_date'] |
|||
# checking the dates are selected or not |
|||
if start_date != 'null' and end_date != 'null': |
|||
start_date = datetime.datetime.strptime(start_date, |
|||
"%Y-%m-%d").date() |
|||
end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d").date() |
|||
if data['project'] == 'null': |
|||
pro_selected = [project.id for project in |
|||
request.env['project.project'].search( |
|||
[('date_start', '>', start_date), |
|||
('date_start', '<', end_date)])] |
|||
else: |
|||
pro_selected = [int(data['project'])] |
|||
elif start_date == 'null' and end_date != 'null': |
|||
end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d").date() |
|||
if data['project'] == 'null': |
|||
pro_selected = [project.id for project in |
|||
request.env['project.project'].search( |
|||
[('date_start', '<', end_date)])] |
|||
else: |
|||
pro_selected = [int(data['project'])] |
|||
elif start_date != 'null' and end_date == 'null': |
|||
start_date = datetime.datetime.strptime(start_date, |
|||
"%Y-%m-%d").date() |
|||
if data['project'] == 'null': |
|||
pro_selected = [project.id for project in |
|||
request.env['project.project'].search( |
|||
[('date_start', '>', start_date)])] |
|||
else: |
|||
pro_selected = [int(data['project'])] |
|||
else: |
|||
if data['project'] == 'null': |
|||
pro_selected = [project.id for project in |
|||
request.env['project.project'].search([])] |
|||
else: |
|||
pro_selected = [int(data['project'])] |
|||
report_project = request.env['timesheets.analysis.report'].search( |
|||
[('project_id', 'in', pro_selected), |
|||
('employee_id', 'in', emp_selected)]) |
|||
analytic_project = request.env['account.analytic.line'].search( |
|||
[('project_id', 'in', pro_selected), |
|||
('employee_id', 'in', emp_selected)]) |
|||
margin = round(sum(report_project.mapped('margin')), 2) |
|||
sale_orders = [] |
|||
for rec in analytic_project: |
|||
if rec.order_id.id and rec.order_id.id not in sale_orders: |
|||
sale_orders.append(rec.order_id.id) |
|||
total_time = sum(analytic_project.mapped('unit_amount')) |
|||
return { |
|||
'total_project': pro_selected, |
|||
'total_emp': emp_selected, |
|||
'total_task': [rec.id for rec in request.env['project.task'].search( |
|||
[('project_id', 'in', pro_selected)])], |
|||
'hours_recorded': total_time, |
|||
'list_hours_recorded': [rec.id for rec in analytic_project], |
|||
'total_margin': margin, |
|||
'total_so': sale_orders |
|||
} |
|||
|
|||
@http.route('/get/tiles/data', auth='public', type='json') |
|||
def get_tiles_data(self): |
|||
"""Summary: |
|||
when the page is loaded, get the data from different models and |
|||
transfer to the js file. |
|||
Return a dictionary variable. |
|||
Return: |
|||
type:It is a dictionary variable. This dictionary contains data that |
|||
affects the dashboard view.""" |
|||
user_employee = request.env.user.partner_id |
|||
if user_employee.user_has_groups('project.group_project_manager'): |
|||
all_project = request.env['project.project'].search([]) |
|||
all_task = request.env['project.task'].search([]) |
|||
analytic_project = request.env['account.analytic.line'].search([]) |
|||
report_project = request.env['timesheets.analysis.report'].search( |
|||
[]) |
|||
margin = round(sum(report_project.mapped('margin')), 2) |
|||
total_time = sum(analytic_project.mapped('unit_amount')) |
|||
employees = request.env['hr.employee'].search([]) |
|||
task = request.env['project.task'].sudo().search_read([ |
|||
('sale_order_id', '!=', False) |
|||
], ['sale_order_id']) |
|||
task_so_ids = [o['sale_order_id'][0] for o in task] |
|||
sale_orders = request.env['sale.order'].browse(task_so_ids) |
|||
project_stage_ids = request.env['project.project.stage'].search([]) |
|||
project_stage_list = [] |
|||
for project_stage_id in project_stage_ids: |
|||
total_projects = request.env[ |
|||
'project.project'].sudo().search_count( |
|||
[('stage_id', '=', project_stage_id.id)]) |
|||
project_stage_list.append({'name': project_stage_id.name, |
|||
'projects': total_projects}) |
|||
return { |
|||
'total_projects': len(all_project), |
|||
'total_projects_ids': all_project.ids, |
|||
'total_tasks': len(all_task), |
|||
'total_tasks_ids': all_task.ids, |
|||
'total_hours': total_time, |
|||
'total_profitability': margin, |
|||
'total_employees': len(employees), |
|||
'total_sale_orders': len(sale_orders), |
|||
'sale_orders_ids': sale_orders.mapped('id'), |
|||
'project_stage_list': project_stage_list, |
|||
'flag': 1} |
|||
else: |
|||
all_project = request.env['project.project'].search( |
|||
[('user_id', '=', request.env.uid)]) |
|||
all_task = [] |
|||
for task in request.env['project.task'].search([]): |
|||
for assignee in task.user_ids: |
|||
if assignee.id == request.env.uid: |
|||
all_task.append(task.id) |
|||
analytic_project = request.env['account.analytic.line'].search( |
|||
[('project_id', 'in', all_project.ids)]) |
|||
total_time = sum(analytic_project.mapped('unit_amount')) |
|||
task = request.env['project.task'].sudo().search_read([ |
|||
('sale_order_id', '!=', False), |
|||
('project_id', 'in', all_project.ids) |
|||
], ['sale_order_id']) |
|||
task_so_ids = [o['sale_order_id'][0] for o in task] |
|||
sale_orders = request.mapped('sale_line_id.order_id') | request.env[ |
|||
'sale.order'].browse(task_so_ids) |
|||
project_stage_ids = request.env['project.project.stage'].search([]) |
|||
project_stage_list = [] |
|||
for project_stage_id in project_stage_ids: |
|||
total_projects = request.env['project.project'].search_count( |
|||
[('stage_id', '=', project_stage_id.id), |
|||
('id', 'in', all_project.ids)]) |
|||
project_stage_list.append({ |
|||
'name': project_stage_id.name, |
|||
'projects': total_projects |
|||
}) |
|||
return { |
|||
'total_projects': len(all_project), |
|||
'total_projects_ids': all_project.ids, |
|||
'total_tasks': len(all_task), |
|||
'total_tasks_ids': all_task, |
|||
'total_hours': total_time, |
|||
'total_sale_orders': len(sale_orders), |
|||
'sale_orders_ids': sale_orders.mapped('id'), |
|||
'project_stage_list': project_stage_list, |
|||
'flag': 2} |
|||
|
|||
@http.route('/get/hours', auth='public', type='json') |
|||
def get_hours_data(self): |
|||
"""Summary: |
|||
when the page is loaded get the data for the hour table. |
|||
Return: |
|||
type:It is a dictionary variable. This dictionary contains data that |
|||
hours table.""" |
|||
user_employee = request.env.user.partner_id |
|||
if user_employee.user_has_groups('project.group_project_manager'): |
|||
query = '''SELECT sum(unit_amount) as hour_recorded FROM |
|||
account_analytic_line WHERE |
|||
timesheet_invoice_type='non_billable_project' ''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
hour_recorded = [] |
|||
for record in data: |
|||
hour_recorded.append(record.get('hour_recorded')) |
|||
query = '''SELECT sum(unit_amount) as hour_recorde FROM |
|||
account_analytic_line WHERE |
|||
timesheet_invoice_type='billable_time' ''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
hour_recorde = [] |
|||
for record in data: |
|||
hour_recorde.append(record.get('hour_recorde')) |
|||
query = '''SELECT sum(unit_amount) as billable_fix FROM |
|||
account_analytic_line WHERE |
|||
timesheet_invoice_type='billable_fixed' ''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
billable_fix = [] |
|||
for record in data: |
|||
billable_fix.append(record.get('billable_fix')) |
|||
query = '''SELECT sum(unit_amount) as non_billable FROM |
|||
account_analytic_line WHERE timesheet_invoice_type='non_billable' |
|||
''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
non_billable = [] |
|||
for record in data: |
|||
non_billable.append(record.get('non_billable')) |
|||
query = '''SELECT sum(unit_amount) as total_hr FROM |
|||
account_analytic_line WHERE |
|||
timesheet_invoice_type='non_billable_project' or |
|||
timesheet_invoice_type='billable_time' or |
|||
timesheet_invoice_type='billable_fixed' or |
|||
timesheet_invoice_type='non_billable' ''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
total_hr = [] |
|||
for record in data: |
|||
total_hr.append(record.get('total_hr')) |
|||
return { |
|||
'hour_recorded': hour_recorded, |
|||
'hour_recorde': hour_recorde, |
|||
'billable_fix': billable_fix, |
|||
'non_billable': non_billable, |
|||
'total_hr': total_hr, |
|||
} |
|||
else: |
|||
all_project = request.env['project.project'].search( |
|||
[('user_id', '=', request.env.uid)]).ids |
|||
analytic_project = request.env['account.analytic.line'].search( |
|||
[('project_id', 'in', all_project)]) |
|||
all_hour_recorded = analytic_project.filtered( |
|||
lambda x: x.timesheet_invoice_type == 'non_billable_project') |
|||
all_hour_recorde = analytic_project.filtered( |
|||
lambda x: x.timesheet_invoice_type == 'billable_time') |
|||
all_billable_fix = analytic_project.filtered( |
|||
lambda x: x.timesheet_invoice_type == 'billable_fixed') |
|||
all_non_billable = analytic_project.filtered( |
|||
lambda x: x.timesheet_invoice_type == 'non_billable') |
|||
hour_recorded = [sum(all_hour_recorded.mapped('unit_amount'))] |
|||
hour_recorde = [sum(all_hour_recorde.mapped('unit_amount'))] |
|||
billable_fix = [sum(all_billable_fix.mapped('unit_amount'))] |
|||
non_billable = [sum(all_non_billable.mapped('unit_amount'))] |
|||
total_hr = [ |
|||
sum(hour_recorded + hour_recorde + billable_fix + non_billable)] |
|||
return { |
|||
'hour_recorded': hour_recorded, |
|||
'hour_recorde': hour_recorde, |
|||
'billable_fix': billable_fix, |
|||
'non_billable': non_billable, |
|||
'total_hr': total_hr, |
|||
} |
|||
|
|||
@http.route('/get/task/data', auth='public', type='json') |
|||
def get_task_data(self): |
|||
""" |
|||
Summary: |
|||
when the page is loaded, get the data from different models and |
|||
transfer to the js file. |
|||
Return a dictionary variable. |
|||
Return: |
|||
type:It is a dictionary variable. This dictionary contains data that |
|||
affecting project task table.""" |
|||
user_employee = request.env.user.partner_id |
|||
if user_employee.user_has_groups('project.group_project_manager'): |
|||
request._cr.execute('''select project_task.name as task_name, |
|||
pro.name as project_name from project_task |
|||
Inner join project_project as pro on project_task.project_id |
|||
= pro.id ORDER BY project_name ASC''') |
|||
data = request._cr.fetchall() |
|||
project_name = [] |
|||
for rec in data: |
|||
project_name.append(list(rec)) |
|||
return { |
|||
'project': project_name |
|||
} |
|||
else: |
|||
all_project = request.env['project.project'].search( |
|||
[('user_id', '=', request.env.uid)]).ids |
|||
all_tasks = request.env['project.task'].search( |
|||
[('project_id', 'in', all_project)]) |
|||
task_project = [[task.name, task.project_id.name] for task in |
|||
all_tasks] |
|||
return { |
|||
'project': task_project |
|||
} |
@ -0,0 +1,7 @@ |
|||
## Module <project_dashboard_odoo> |
|||
|
|||
#### 30.11.2023 |
|||
#### Version 17.0.1.0.0 |
|||
##### ADD |
|||
|
|||
- Initial Commit for Project Dashboard |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import project_project |
@ -0,0 +1,35 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
import random |
|||
from odoo import models |
|||
|
|||
|
|||
class Project(models.Model): |
|||
"""This class inherits from 'project.project' and adds custom functionality |
|||
to it.It provides methods to work with project data.""" |
|||
_inherit = 'project.project' |
|||
|
|||
def get_color_code(self): |
|||
"""Generate a random color code in hexadecimal format. |
|||
:return: A random color code in the format '#RRGGBB.'""" |
|||
color = f"#{random.randint(0, 0xFFFFFF):06x}" |
|||
return color |
@ -0,0 +1,35 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
import random |
|||
from odoo import models |
|||
|
|||
|
|||
class ProjectProject(models.Model): |
|||
"""This class inherits from 'project.project' and adds custom functionality |
|||
to it.It provides methods to work with project data.""" |
|||
_inherit = 'project.project' |
|||
|
|||
def get_color_code(self): |
|||
"""Generate a random color code in hexadecimal format. |
|||
:return: A random color code in the format '#RRGGBB.'""" |
|||
color = f"#{random.randint(0, 0xFFFFFF):06x}" |
|||
return color |
After Width: | Height: | Size: 36 KiB |
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.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 11 KiB |
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: 80 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: 3.2 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 565 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 912 KiB |
After Width: | Height: | Size: 228 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 237 KiB |
After Width: | Height: | Size: 314 KiB |
After Width: | Height: | Size: 331 KiB |
After Width: | Height: | Size: 196 KiB |
After Width: | Height: | Size: 194 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,654 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Odoo App 3 Index</title> |
|||
<!-- Bootstrap CSS --> |
|||
<link rel="stylesheet" |
|||
href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" |
|||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" |
|||
crossorigin="anonymous"> |
|||
<link rel="stylesheet" |
|||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css"> |
|||
<link rel="preconnect" href="https://fonts.googleapis.com"> |
|||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
|||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" |
|||
rel="stylesheet"> |
|||
</head> |
|||
<body> |
|||
<section> |
|||
<div class="container" |
|||
style="font-family: 'Inter', sans-serif !important;background-color: #fff !important;"> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-12 col-lg-12 d-flex justify-content-between flex-wrap align-items-sm-center" |
|||
style="border-bottom:1px solid rgba(0, 0, 0, 0.22)"> |
|||
<div class="my-3"> |
|||
<img src="assets/misc/Cybrosys R.png" |
|||
style="width:auto !important; height:40px !important"> |
|||
</div> |
|||
<div class="my-3 d-flex align-items-center"> |
|||
<div class="text-center" |
|||
style="background-color:#017E84 !important;font-size: 0.8rem !important; color:#fff !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width: 120px !important;"> |
|||
Community |
|||
</div> |
|||
<div class="text-center" |
|||
style="background-color:#875A7B !important; color:#fff !important;font-size: 0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important;min-width: 120px !important;"> |
|||
Enterprise |
|||
</div> |
|||
<div class="text-center" |
|||
style="background-color:#7C7BAD !important; color:#fff !important;font-size: 0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width: 120px !important;"> |
|||
Odoo.sh |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-12 col-lg-12 text-center d-flex align-items-center flex-column" |
|||
style="margin: 80px 0px !important;"> |
|||
<h1 style="font-size: 2.8rem;font-weight: 700; color: |
|||
#1A202C;"> |
|||
Project Dashboard</h1> |
|||
<p class="my-3 mb-4" |
|||
style="max-width: 80%; font-weight: 400 !important; line-height: 32px; color: #718096;"> |
|||
In This Dashboard, You Can Get a Detailed View for Project. |
|||
</p> |
|||
<div style="width: 80%; margin-top: 3rem;"> |
|||
<img src="assets/screenshots/hero.gif" |
|||
class="img-responsive" width="100%" height="auto"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container mt-5 mb-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4"> |
|||
<p class="m-0" |
|||
style="font-weight: 600; font-size: 24px; color:#714b67 !important"> |
|||
Key Highlights |
|||
</p> |
|||
</div> |
|||
<div class="row py-4"> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px; |
|||
background: #FFF; |
|||
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); "> |
|||
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67; |
|||
display: flex; justify-content: center; align-items: center; |
|||
margin-right: 10px; flex-shrink: 0;"> |
|||
<i class="fa-solid fa-star " |
|||
style="color: #fff;font-size:14px;"></i> |
|||
</div> |
|||
<div> |
|||
<p style="color: #1A202C;font-weight: 600; |
|||
font-size: 1.2rem; margin-bottom: 2px;"> |
|||
Dashboard view for Project module</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px; |
|||
background: #FFF; |
|||
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); "> |
|||
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67; |
|||
display: flex; justify-content: center; align-items: center; |
|||
margin-right: 10px; flex-shrink: 0;"> |
|||
<i class="fa-solid fa-star " |
|||
style="color: #fff;font-size:14px;"></i> |
|||
</div> |
|||
<div> |
|||
<p style="color: #1A202C;font-weight: 600; |
|||
font-size: 1.2rem; margin-bottom: 2px;"> |
|||
Graphs included</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container rounded"> |
|||
<ul class="nav nav-tabs d-flex" |
|||
style="width: fit-content;margin: 0 auto;gap: 1rem;"> |
|||
<li class="col text-center py-2 text-nowrap " |
|||
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;"> |
|||
<a |
|||
class="active show" data-toggle="tab" href="#tab1" |
|||
style="color: #fff;font-weight: 500; background-color: #714B67; text-decoration: none;"> |
|||
<i class="fa-regular fa-image pr-2" |
|||
style="color: #fff;"></i> |
|||
Screenshots</a></li> |
|||
<li class="col text-center py-2 text-nowrap " |
|||
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;"> |
|||
<a |
|||
data-toggle="tab" href="#tab2" |
|||
style="color: #fff;font-weight: 500; text-decoration: none;"><i |
|||
class="fa-solid fa-star pr-2" |
|||
style="color: #fff;"></i>Features</a></li> |
|||
<li class="col text-center py-2 text-nowrap " |
|||
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;"> |
|||
<a |
|||
data-toggle="tab" href="#tab3" |
|||
style="color: #fff;font-weight: 500; text-decoration: none; background-color: #714B67;"><i |
|||
class="fa-solid fa-book-open pr-2" |
|||
style="color: #fff;"></i>Released Notes</a></li> |
|||
</ul> |
|||
<div class="tab-content" |
|||
style="background-color: rgba(121, 113, 119, 0.04);"> |
|||
<div id="tab1" class="tab-pane fade in active show"> |
|||
<div class="col-lg-12 py-2" |
|||
style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/Screenshot.png" |
|||
class="img-responsive" width="100%" |
|||
height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
Different Types of Graphs.</h4> |
|||
<p class="m-0" style="color:#718096">Project |
|||
Dashboard has different types of Graphs that |
|||
will give you a complete analyzing of the Project |
|||
module.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" |
|||
style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/Screenshot2.png" |
|||
class="img-responsive" width="100%" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-12 py-2" |
|||
style="padding: 1rem 4rem !important;"> |
|||
<div |
|||
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="row justify-content-center p-3 w-100 m-0"> |
|||
<img src="assets/screenshots/Screenshot4.png" |
|||
class="img-responsive" width="100%" |
|||
height="auto"> |
|||
</div> |
|||
<div class="px-3"> |
|||
<h4 class="mt-2" |
|||
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> |
|||
Project Table</h4> |
|||
<p class="m-0" style="color:#718096"> User can |
|||
see all Project ad its current Status.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div id="tab2" class="tab-pane fade"> |
|||
<div class="col-mg-12" style="padding: 1rem 4rem;"> |
|||
<ul style="list-style: none; padding: 1rem 0;font-weight: 500;"> |
|||
<li class="py-3" |
|||
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<span style="margin-right: 12px;"><img |
|||
src="assets/misc/star (1) 2.svg" |
|||
alt="" |
|||
width="16px"></span>User can see |
|||
all details about Project and Task through |
|||
Graphs. |
|||
</li> |
|||
<li class="py-3" |
|||
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<span style="margin-right: 12px;"><img |
|||
src="assets/misc/star (1) 2.svg" |
|||
alt="" |
|||
width="16px"></span>User can see all |
|||
details about Timesheet through Graphs. |
|||
</li> |
|||
<li class="py-3" |
|||
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<span style="margin-right: 12px;"><img |
|||
src="assets/misc/star (1) 2.svg" |
|||
alt="" |
|||
width="16px"></span>User can see all |
|||
Projects with Stages. |
|||
</li> |
|||
<li class="py-3" |
|||
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<span style="margin-right: 12px;"><img |
|||
src="assets/misc/star (1) 2.svg" |
|||
alt="" |
|||
width="16px"></span>User can use |
|||
filter based on the Employee, Project and Dates. |
|||
</li> |
|||
|
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<div id="tab3" class="tab-pane fade"> |
|||
<div class="col-mg-12 active" style="padding: 1rem 4rem;"> |
|||
<div class="py-3" |
|||
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> |
|||
<div class="d-flex mb-3" |
|||
style="font-size: 0.8rem; font-weight: 500;"><span>Version |
|||
17.0.1.0.0</span><span |
|||
class="px-2">|</span><span |
|||
style="color: #714B67;font-weight: 600;">Released on:30th Nov 2023</span> |
|||
</div> |
|||
<p class="m-0" |
|||
style=" color:#718096!important; font-size:1rem !important;line-height: 28px;"> |
|||
Initial Commit for Project Dashboard.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-5"> |
|||
<p class="m-0" |
|||
style="font-weight: 600; font-size: 24px; color:#000 !important"> |
|||
Related Products</p> |
|||
</div> |
|||
</div> |
|||
<div id="myCarousel" class="carousel slide py-3" data-ride="carousel"> |
|||
<div class="carousel-inner"> |
|||
<div class="carousel-item active"> |
|||
<div class="row p-4"> |
|||
<div class="col"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/base_accounting_kit/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/1.gif" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Odoo 17 Full Accounting Kit</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="col"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/invoice_format_editor/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/2.png" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Invoice Format Editor</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="col"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/inventory_barcode_scanning/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/3.png" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Barcode scanning in Inventory</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="carousel-item"> |
|||
<div class="row p-4"> |
|||
<div class="col"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/whatsapp_redirect/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px; "> |
|||
<img src="assets/modules/4.jpg" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Send Whatsapp Message</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="col"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/base_account_budget/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px;"> |
|||
<img src="assets/modules/5.jpg" alt="" width="100%" height="auto"> |
|||
|
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Budget Management</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="col"> |
|||
<div class="p-3"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/product_barcode/" style="color: #000; text-decoration: none;"> |
|||
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;"> |
|||
<div style="width: 300px;"> |
|||
<img src="assets/modules/6.png" alt="" width="100%" height="auto"> |
|||
</div> |
|||
<p class="text-center pt-2 text-black font-weight-bold">Product Barcode Generator</p> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<a class="carousel-control-prev" href="#myCarousel" 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="#myCarousel" 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 class="container mt-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4"> |
|||
<p class="m-0" |
|||
style="font-weight: 600; font-size: 24px; color:#000 !important"> |
|||
Our Services</p> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="container my-5"> |
|||
<div class="row py-3"> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#13EA36 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/cogs.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo |
|||
Customization</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#DBC711; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/wrench.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo |
|||
Implementation</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#FF6B6B ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/lifebuoy.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo |
|||
Support</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#FFA801 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/user.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Hire |
|||
Odoo Developer</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;"> |
|||
|
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#54A0FF; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/puzzle.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo |
|||
Integration</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#6D7680 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/update.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo |
|||
Migration</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#786FA6 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/consultation.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo |
|||
Consultancy</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#F8A5C2 ; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/training.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo |
|||
Implementation</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 px-4 py-4"> |
|||
<div |
|||
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;"> |
|||
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);"> |
|||
<div style="background-color:#E6BE26; border-radius: 50%; padding: 15px; width: 68px; |
|||
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);"> |
|||
<img src="assets/icons/license.png" |
|||
alt="service-icon" width="38px" |
|||
height="auto"> |
|||
</div> |
|||
</div> |
|||
<p style="margin-top: 20px; font-weight: bold;">Odoo |
|||
Licensing Consultancy</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4"> |
|||
<p class="m-0" |
|||
style="font-weight: 600; font-size: 24px; color:#000 !important"> |
|||
Our Industries</p> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="container"> |
|||
<div class="row my-5 py-4"> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100 " |
|||
style="border-right: 1px solid rgb(209, 209, 209); border-bottom: 1px solid rgb(209, 209, 209); padding: 30px; box-shadow: 6px 0 10px rgba(228, 227, 227, 0.373);"> |
|||
<img src="assets/icons/trading-black.png" width="42px" |
|||
height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Trading</p> |
|||
<p>Easily procure and sell your products</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209);border-bottom: 1px solid rgb(209, 209, 209); padding: 30px;"> |
|||
<img src="assets/icons/pos-black.png" width="42px" |
|||
height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">POS</p> |
|||
<p>Easy configuration and convivial experience</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209);border-bottom: 1px solid rgba(0, 0, 0, 0.2); padding: 30px; box-shadow: 0 5px 10px rgba(228, 227, 227, 0.373)"> |
|||
<img src="assets/icons/education-black.png" width="42px" |
|||
height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;"> |
|||
Education</p> |
|||
<p>A platform for educational management</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-bottom: 1px solid rgb(209, 209, 209); padding: 30px; "> |
|||
<img src="assets/icons/manufacturing-black.png" |
|||
width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;"> |
|||
Manufacturing</p> |
|||
<p>Plan, track and schedule your operations</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px;"> |
|||
<img src="assets/icons/ecom-black.png" width="42px" |
|||
height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">E-commerce & |
|||
Website</p> |
|||
<p>Mobile friendly, awe-inspiring product pages</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px;box-shadow: 0 -5px 10px rgba(228, 227, 227, 0.373);"> |
|||
<img src="assets/icons/service-black.png" width="42px" |
|||
height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Service |
|||
Management</p> |
|||
<p>Keep track of services and invoice</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px; "> |
|||
<img src="assets/icons/restaurant-black.png" |
|||
width="42px" height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;"> |
|||
Restaurant</p> |
|||
<p>Run your bar or restaurant methodically</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3 col-sm-6 p-0"> |
|||
<div class="d-flex flex-column h-100" |
|||
style=" padding: 30px;box-shadow: -5px 0 10px rgba(228, 227, 227, 0.373);"> |
|||
<img src="assets/icons/hotel-black.png" width="42px" |
|||
height="auto" alt=""> |
|||
<p style="color: #714B67;font-weight: 600; margin-top: 10px; |
|||
font-size: 1.2rem; margin-bottom: 2px;">Hotel |
|||
Management</p> |
|||
<p>An all-inclusive hotel management application</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-5"> |
|||
<p class="m-0" |
|||
style="font-weight: 600; font-size: 24px; color:#000 !important"> |
|||
Support</p> |
|||
</div> |
|||
</div> |
|||
<div class="container my-5"> |
|||
<div class="row" style="background-color: #FFFAFE;"> |
|||
<div class="col-md-6 pb-4 d-flex align-items-center justify-content-center" |
|||
style="border-right: 1px solid #D9D9D9;"> |
|||
<div style="padding: 30px;"> |
|||
<div class="d-flex align-items-center"> |
|||
<img src="assets/misc/support (1) 1.svg" alt="" |
|||
width="60px" style="margin-right: 12px;"> |
|||
<div style="padding: 0px 8px;"> |
|||
<span |
|||
style="color: #714B67;font-size: 24px;font-weight: 600;padding-bottom: 1rem;">Need |
|||
Help?</span> |
|||
<p class="m-0" style="color:#718096;">Got |
|||
questions or need help? Get in touch.</p> |
|||
<div style="font-weight: 400;"><span><img |
|||
src="assets/misc/support-email.svg" |
|||
alt="" |
|||
width="18px" |
|||
style="filter: invert(1);margin-right: 0.8rem;"></span>odoo@cybrosys.com |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 pb-4 d-flex align-items-center justify-content-center"> |
|||
<div style="padding: 30px;"> |
|||
<div class="d-flex align-items-center"> |
|||
<img src="assets/misc/whatsapp 1.svg" alt="" |
|||
width="60px" style="margin-right: 12px;"> |
|||
<div> |
|||
<span style="color: #714B67;font-size: 24px;font-weight: 600;">WhatsApp</span> |
|||
<p class="m-0" style="color:#718096;">Say hi to |
|||
us on WhatsApp!</p> |
|||
<div style="font-weight: 400; font-size: 16px;"><span><img |
|||
src="assets/misc/phone.svg" |
|||
alt="" width="14px" |
|||
style="filter: invert(1); margin-right: 0.8rem;"></span>+91 |
|||
99456767686 |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
<!-- Optional JavaScript --> |
|||
<!-- jQuery first, then Popper.js, then Bootstrap JS --> |
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> |
|||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> |
|||
</body> |
|||
</html> |
@ -0,0 +1,426 @@ |
|||
.oh-card h4 { |
|||
font-size: 1.1rem; |
|||
} |
|||
|
|||
.stat-icon { |
|||
display: inline-block; |
|||
} |
|||
|
|||
.stat-widget-one .stat-icon { |
|||
vertical-align: top; |
|||
margin: auto; |
|||
width: 100%; |
|||
} |
|||
|
|||
.stat-widget-one .stat-icon i { |
|||
font-size: 30px; |
|||
font-weight: 900; |
|||
display: inline-block; |
|||
color: #01c490; |
|||
} |
|||
|
|||
.stat-widget-one .stat-text { |
|||
font-size: 14px; |
|||
color: #868e96; |
|||
font-weight: bold; |
|||
|
|||
} |
|||
|
|||
.stat-widget-one .stat-digit { |
|||
font-size: 24px; |
|||
color: #02448b; |
|||
} |
|||
|
|||
.stat_count { |
|||
font-size: 28px !important; |
|||
} |
|||
|
|||
body .text-color { |
|||
color: #00438b; |
|||
} |
|||
/* Leave graph */ |
|||
path { |
|||
stroke: #fff; |
|||
} |
|||
|
|||
path:hover { |
|||
opacity: 0.9; |
|||
} |
|||
|
|||
rect:hover { |
|||
fill: #934da5; |
|||
} |
|||
|
|||
.axis { |
|||
font: 10px sans-serif; |
|||
} |
|||
|
|||
.legend tr { |
|||
border-bottom: 1px solid grey; |
|||
} |
|||
|
|||
.legend tr:first-child { |
|||
border-top: 1px solid grey; |
|||
} |
|||
|
|||
.axis path, |
|||
.axis line { |
|||
fill: none; |
|||
stroke: #000; |
|||
shape-rendering: crispEdges; |
|||
} |
|||
|
|||
.x.axis path { |
|||
display: none; |
|||
} |
|||
|
|||
.legend { |
|||
border-collapse: collapse; |
|||
border-spacing: 0px; |
|||
display: inline-block; |
|||
} |
|||
|
|||
.legend td, |
|||
.legend .legend_col { |
|||
padding: 4px 5px; |
|||
vertical-align: bottom; |
|||
} |
|||
|
|||
.legendFreq, |
|||
.legendPerc { |
|||
align: right; |
|||
width: 50px; |
|||
} |
|||
|
|||
/* Leave broadfactor graph */ |
|||
|
|||
.broad_factor_graph .axis path, |
|||
.broad_factor_graph .axis line { |
|||
fill: none; |
|||
stroke: black; |
|||
shape-rendering: crispEdges; |
|||
} |
|||
|
|||
.broad_factor_graph .axis text { |
|||
font-family: sans-serif; |
|||
font-size: 11px; |
|||
} |
|||
|
|||
.broad_factor_graph rect { |
|||
-moz-transition: all 0.3s; |
|||
-webkit-transition: all 0.3s; |
|||
-o-transition: all 0.3s; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
.broad_factor_graph rect:hover { |
|||
fill: #ff618a; |
|||
} |
|||
|
|||
#broad_factor_pdf { |
|||
background-color: #ffffff; |
|||
border: 0; |
|||
color: #000000; |
|||
float: right; |
|||
} |
|||
|
|||
#broad_factor_pdf i { |
|||
color: red; |
|||
} |
|||
|
|||
|
|||
/*=====================New Dashboard===========================*/ |
|||
|
|||
.oh_dashboards { |
|||
background-color: #f8faff !important; |
|||
padding: 0px !important; |
|||
|
|||
} |
|||
|
|||
.container-fluid.o_hr_dashboard { |
|||
padding: 0px !important; |
|||
} |
|||
|
|||
.employee-prof { |
|||
|
|||
padding: 0px; |
|||
height: 100%; |
|||
background-color: #3e6282; |
|||
/*background-image: linear-gradient(180deg, #3e6282, #41666f);*/ |
|||
position: fixed; |
|||
/*z-index: 999;*/ |
|||
} |
|||
|
|||
.employee-prof .oh-card:hover { |
|||
|
|||
transform: none !important; |
|||
box-shadow: none !important; |
|||
|
|||
} |
|||
|
|||
.oh-card { |
|||
|
|||
padding-top: 0px; |
|||
padding: 0px; |
|||
margin-bottom: 1.5rem; |
|||
border-radius: 0px; |
|||
box-shadow: none; |
|||
background: none; |
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
|
|||
} |
|||
|
|||
.oh-card:hover { |
|||
|
|||
transform: translateY(-2px) translateZ(0) !important; |
|||
box-shadow: 0 10px 10px 0 rgba(62, 57, 107, 0.12), 0 0 0 transparent !important; |
|||
|
|||
} |
|||
|
|||
.media-body.employee-name { |
|||
|
|||
background: #466b8d; |
|||
float: left; |
|||
margin: 0; |
|||
width: 100% |
|||
} |
|||
|
|||
.oh-payslip { |
|||
|
|||
margin-top: 1.5%; |
|||
|
|||
} |
|||
|
|||
.oh-payslip .stat-icon { |
|||
|
|||
width: 30%; |
|||
height: 85px; |
|||
text-align: center; |
|||
padding-top: 2%; |
|||
color: #fff; |
|||
|
|||
} |
|||
|
|||
.oh-payslip .oh-card { |
|||
|
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.06); |
|||
|
|||
} |
|||
|
|||
.stat-widget-one .stat-text { |
|||
|
|||
font-size: 14px; |
|||
color: #ff8762; |
|||
margin-top: 2.3rem; |
|||
margin-left: 1rem; |
|||
|
|||
} |
|||
|
|||
.stat-widget-one .stat-digit { |
|||
|
|||
font-size: 17px; |
|||
color: #000; |
|||
margin-left: 1rem; |
|||
padding-left: 26px; |
|||
|
|||
} |
|||
|
|||
.stat-widget-one .stat-icon i { |
|||
font-size: 32px; |
|||
display: inline-block; |
|||
color: #000; |
|||
top: 16px; |
|||
position: relative; |
|||
} |
|||
|
|||
.stat-widget-one { |
|||
|
|||
background-color: white; |
|||
text-align: inherit !important; |
|||
|
|||
} |
|||
|
|||
.stat-widget-one { |
|||
width: 100%; |
|||
} |
|||
|
|||
.oh-payslip .stat-icon { |
|||
|
|||
width: 30%; |
|||
height: 85px; |
|||
text-align: center; |
|||
padding-top: 2%; |
|||
|
|||
} |
|||
|
|||
|
|||
h4 .stat-count { |
|||
font-size: 17px; |
|||
text-align: center; |
|||
color: #000 !important; |
|||
margin-top: 0px; |
|||
width: 100%; |
|||
float: left; |
|||
margin: 0; |
|||
} |
|||
|
|||
.hr-chart-1 { |
|||
margin: 15px 0px; |
|||
background: #fff; |
|||
padding: 0px !important; |
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.06); |
|||
} |
|||
|
|||
.hr-chart-1:hover { |
|||
transform: translateY(-2px) translateZ(0) !important; |
|||
box-shadow: 0 10px 10px 0 rgba(62, 57, 107, 0.12), 0 0 0 transparent !important; |
|||
} |
|||
|
|||
.stat-head { |
|||
text-align: center !important; |
|||
font-weight: 300; |
|||
font-size: 21px; |
|||
margin-bottom: 12px; |
|||
margin-left: 2px; |
|||
} |
|||
|
|||
|
|||
.hr_birthday { |
|||
font-size: 17px; |
|||
text-align: center; |
|||
padding: 20px 0; |
|||
color: #00438b; |
|||
font-weight: 300; |
|||
} |
|||
|
|||
.hr_notification img { |
|||
width: 40px; |
|||
height: 40px; |
|||
border-radius: 100%; |
|||
} |
|||
|
|||
.hr_notification { |
|||
background: #fff; |
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.06); |
|||
height: 757px; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.hr_notification .media { |
|||
border-bottom: 1px solid #e6e6e6; |
|||
padding-bottom: 6px; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
.hr_notification .text-color.display-6 { |
|||
margin: 0px 0 3px; |
|||
color: #2d2d2d; |
|||
} |
|||
|
|||
.hr_notification p { |
|||
margin: 0 0 1px; |
|||
color: #666; |
|||
font-size: 10px; |
|||
} |
|||
|
|||
.hr_notification_head { |
|||
font-size: 17px; |
|||
text-align: center; |
|||
padding: 12px 0; |
|||
color: #fff; |
|||
font-weight: 300; |
|||
background: #5ebade; |
|||
margin-bottom: 9px; |
|||
} |
|||
|
|||
.hr-chart-1 { |
|||
margin: 15px 0px; |
|||
background: #fff; |
|||
padding: 0px !important; |
|||
padding-top: 0px; |
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.06); |
|||
padding-top: 3px !important; |
|||
} |
|||
|
|||
/* width */ |
|||
.hr_notification::-webkit-scrollbar { |
|||
width: 4px; |
|||
} |
|||
|
|||
/* Track */ |
|||
.hr_notification::-webkit-scrollbar-track { |
|||
background: #f1f1f1; |
|||
} |
|||
|
|||
/* Handle */ |
|||
.hr_notification::-webkit-scrollbar-thumb { |
|||
background: #495057; |
|||
; |
|||
} |
|||
|
|||
/* Handle on hover */ |
|||
.hr_notification::-webkit-scrollbar-thumb:hover { |
|||
background: #598da1; |
|||
} |
|||
|
|||
.oh-card-body { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.text-align { |
|||
margin-left: 17px; |
|||
} |
|||
|
|||
.inner_select { |
|||
min-width: 150px |
|||
} |
|||
|
|||
#table_status { |
|||
width: 90%; |
|||
margin-left: 5%; |
|||
font-family: Arial, Helvetica, sans-serif; |
|||
} |
|||
|
|||
#table_status tr:nth-child(even) { |
|||
background-color: #f2f2f2; |
|||
} |
|||
|
|||
#table_status tr:hover { |
|||
background-color: #ddd; |
|||
} |
|||
|
|||
.fleet-pill { |
|||
align-items: center; |
|||
font-family: "Open Sans", Arial, Verdana, sans-serif; |
|||
font-weight: bold; |
|||
font-size: 11px; |
|||
display: inline-block; |
|||
height: 100%; |
|||
white-space: nowrap; |
|||
width: auto; |
|||
position: relative; |
|||
border-radius: 100px; |
|||
line-height: 1; |
|||
overflow: hidden; |
|||
padding: 0px 8px 0px 7px; |
|||
text-overflow: ellipsis; |
|||
line-height: 1.25rem; |
|||
color: #fff; |
|||
word-break: break-word; |
|||
background: #0253e8; |
|||
} |
|||
|
|||
.inner_select p { |
|||
margin-left: 20px |
|||
} |
@ -0,0 +1,447 @@ |
|||
/** @odoo-module */ |
|||
import { registry} from '@web/core/registry'; |
|||
import { useService } from "@web/core/utils/hooks"; |
|||
const { Component, onWillStart, onMounted} = owl |
|||
import { jsonrpc } from "@web/core/network/rpc_service"; |
|||
import { _t } from "@web/core/l10n/translation"; |
|||
|
|||
export class ProjectDashboard extends Component { |
|||
/** |
|||
* Setup method to initialize required services and register event handlers. |
|||
*/ |
|||
setup() { |
|||
this.action = useService("action"); |
|||
this.orm = useService("orm"); |
|||
this.rpc = this.env.services.rpc |
|||
onWillStart(this.onWillStart); |
|||
onMounted(this.onMounted); |
|||
} |
|||
/** |
|||
* Event handler for the 'onWillStart' event. |
|||
*/ |
|||
async onWillStart() { |
|||
await this.fetch_data(); |
|||
} |
|||
/** |
|||
* Event handler for the 'onMounted' event. |
|||
* Renders various components and charts after fetching data. |
|||
*/ |
|||
async onMounted() { |
|||
// Render other components after fetching data
|
|||
this.render_project_task(); |
|||
this.render_top_employees_graph(); |
|||
this.render_filter(); |
|||
} |
|||
/** |
|||
* Render the project task chart. |
|||
*/ |
|||
async render_project_task() { |
|||
await jsonrpc("/project/task/count").then(function(data) { |
|||
var ctx = $("#project_doughnut"); |
|||
new Chart(ctx, { |
|||
type: "doughnut", |
|||
data: { |
|||
labels: data.project, |
|||
datasets: [{ |
|||
backgroundColor: data.color, |
|||
data: data.task |
|||
}] |
|||
}, |
|||
options: { |
|||
legend: { |
|||
position: 'left' |
|||
}, |
|||
cutoutPercentage: 40, |
|||
responsive: true, |
|||
} |
|||
}); |
|||
}) |
|||
} |
|||
/** |
|||
function for getting values to employee graph |
|||
*/ |
|||
async render_top_employees_graph() { |
|||
var ctx = $(".top_selling_employees"); |
|||
await jsonrpc('/employee/timesheet').then(function(arrays) { |
|||
var data = { |
|||
labels: arrays[1], |
|||
datasets: [{ |
|||
label: "Hours Spent", |
|||
data: arrays[0], |
|||
backgroundColor: [ |
|||
"rgba(190, 27, 75,1)", |
|||
"rgba(31, 241, 91,1)", |
|||
"rgba(103, 23, 252,1)", |
|||
"rgba(158, 106, 198,1)", |
|||
"rgba(250, 217, 105,1)", |
|||
"rgba(255, 98, 31,1)", |
|||
"rgba(255, 31, 188,1)", |
|||
"rgba(75, 192, 192,1)", |
|||
"rgba(153, 102, 255,1)", |
|||
"rgba(10,20,30,1)" |
|||
], |
|||
borderColor: [ |
|||
"rgba(190, 27, 75, 0.2)", |
|||
"rgba(190, 223, 122, 0.2)", |
|||
"rgba(103, 23, 252, 0.2)", |
|||
"rgba(158, 106, 198, 0.2)", |
|||
"rgba(250, 217, 105, 0.2)", |
|||
"rgba(255, 98, 31, 0.2)", |
|||
"rgba(255, 31, 188, 0.2)", |
|||
"rgba(75, 192, 192, 0.2)", |
|||
"rgba(153, 102, 255, 0.2)", |
|||
"rgba(10,20,30,0.3)" |
|||
], |
|||
borderWidth: 1 |
|||
}, |
|||
|
|||
] |
|||
}; |
|||
//options
|
|||
var options = { |
|||
responsive: true, |
|||
title: { |
|||
display: true, |
|||
position: "top", |
|||
text: " Time by Employees", |
|||
fontSize: 18, |
|||
fontColor: "#111" |
|||
}, |
|||
legend: { |
|||
display: false, |
|||
}, |
|||
scales: { |
|||
yAxes: [{ |
|||
ticks: { |
|||
min: 0 |
|||
} |
|||
}] |
|||
} |
|||
}; |
|||
//create Chart class object
|
|||
var chart = new Chart(ctx, { |
|||
type: 'bar', |
|||
data: data, |
|||
options: options |
|||
}); |
|||
|
|||
}); |
|||
} |
|||
/** |
|||
* Function for getting employees for filter. |
|||
*/ |
|||
render_filter() { |
|||
jsonrpc('/project/filter').then(function(data) { |
|||
var projects = data[0] |
|||
var employees = data[1] |
|||
$(projects).each(function(project) { |
|||
$('#project_selection').append("<option value=" + projects[project].id + ">" + projects[project].name + "</option>"); |
|||
}); |
|||
$(employees).each(function(employee) { |
|||
$('#employee_selection').append("<option value=" + employees[employee].id + ">" + employees[employee].name + "</option>"); |
|||
}); |
|||
}) |
|||
} |
|||
/** |
|||
* Event handler to apply filters based on user selections and update the dashboard data accordingly. |
|||
*/ |
|||
_onchangeFilter(ev) { |
|||
this.flag = 1 |
|||
var start_date = $('#start_date').val(); |
|||
var end_date = $('#end_date').val(); |
|||
var employee_selection = $('#employee_selection').val(); |
|||
var project_selection = $('#project_selection').val(); |
|||
var self = this; |
|||
if (!start_date) { |
|||
start_date = "null" |
|||
} |
|||
if (!end_date) { |
|||
end_date = "null" |
|||
} |
|||
if (!employee_selection) { |
|||
employee_selection = "null" |
|||
} |
|||
if (!project_selection) { |
|||
project_selection = "null" |
|||
} |
|||
jsonrpc('/project/filter-apply', { |
|||
'data': { |
|||
'start_date': start_date, |
|||
'end_date': end_date, |
|||
'project': project_selection, |
|||
'employee': employee_selection |
|||
} |
|||
}).then(function(data) { |
|||
self.tot_hrs = data['list_hours_recorded'] |
|||
self.tot_employee = data['total_emp'] |
|||
self.tot_project = data['total_project'] |
|||
self.tot_task = data['total_task'] |
|||
self.tot_so = data['total_so'] |
|||
$('#tot_project')[0].innerHTML = data['total_project'].length |
|||
$('#tot_employee')[0].innerHTML = data['total_emp'].length |
|||
$("#tot_task")[0].innerHTML = data['total_task'].length |
|||
$("#tot_hrs")[0].innerHTML = data['hours_recorded'] |
|||
$("#tot_margin")[0].innerHTML = data['total_margin'] |
|||
$("#tot_so")[0].innerHTML = data['total_so'].length |
|||
}) |
|||
} |
|||
/** |
|||
* Event handler to open a list of employees and display them to the user. |
|||
*/ |
|||
tot_emp(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
if (this.flag == 0) { |
|||
this.action.doAction({ |
|||
name: _t("Employees"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'hr.employee', |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} else { |
|||
this.action.doAction({ |
|||
name: _t("Employees"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'hr.employee', |
|||
domain: [ |
|||
["id", "in", this.tot_employee] |
|||
], |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
|
|||
} |
|||
} |
|||
/** |
|||
function for getting values when page is loaded |
|||
*/ |
|||
fetch_data() { |
|||
this.flag = 0 |
|||
var self = this; |
|||
var def1 = jsonrpc('/get/tiles/data').then(function(result) { |
|||
if (result['flag'] == 1) { |
|||
self.total_projects = result['total_projects'] |
|||
self.total_tasks = result['total_tasks'] |
|||
self.tot_task = result['total_tasks_ids'] |
|||
self.total_hours = result['total_hours'] |
|||
self.total_profitability = result['total_profitability'] |
|||
self.total_employees = result['total_employees'] |
|||
self.total_sale_orders = result['total_sale_orders'] |
|||
self.project_stage_list = result['project_stage_list'] |
|||
self.tot_so = result['sale_orders_ids'] |
|||
self.flag_user = result['flag'] |
|||
self.total_projects_ids = result['total_projects_ids'] |
|||
} else { |
|||
self.tot_task = result['total_tasks_ids'] |
|||
self.total_projects = result['total_projects'] |
|||
self.total_tasks = result['total_tasks'] |
|||
self.total_hours = result['total_hours'] |
|||
self.total_sale_orders = result['total_sale_orders'] |
|||
self.project_stage_list = result['project_stage_list'] |
|||
self.flag_user = result['flag'] |
|||
self.tot_so = result['sale_orders_ids'] |
|||
self.total_projects_ids = result['total_projects_ids'] |
|||
} |
|||
}); |
|||
/** |
|||
function for getting values to hours table |
|||
*/ |
|||
var def3 = jsonrpc('/get/hours') |
|||
.then(function(res) { |
|||
self.hour_recorded = res['hour_recorded']; |
|||
self.hour_recorde = res['hour_recorde']; |
|||
self.billable_fix = res['billable_fix']; |
|||
self.non_billable = res['non_billable']; |
|||
self.total_hr = res['total_hr']; |
|||
}); |
|||
|
|||
var def4 = jsonrpc('/get/task/data') |
|||
.then(function(res) { |
|||
self.task_data = res['project']; |
|||
}); |
|||
return $.when(def1, def3, def4); |
|||
} |
|||
/** |
|||
* Event handler to open a list of projects and display them to the user. |
|||
*/ |
|||
tot_projects(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
if (this.flag == 0) { |
|||
this.action.doAction({ |
|||
name: _t("Projects"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'project.project', |
|||
domain: [ |
|||
["id", "in", this.total_projects_ids] |
|||
], |
|||
view_mode: 'kanban,form', |
|||
views: [ |
|||
[false, 'kanban'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} else { |
|||
if (this.tot_project) { |
|||
this.action.doAction({ |
|||
name: _t("Projects"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'project.project', |
|||
domain: [ |
|||
["id", "in", this.tot_project] |
|||
], |
|||
view_mode: 'kanban,form', |
|||
views: [ |
|||
[false, 'kanban'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
} |
|||
} |
|||
/** |
|||
* Event handler to open a list of tasks and display them to the user. |
|||
*/ |
|||
tot_tasks(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
this.action.doAction({ |
|||
name: _t("Tasks"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'project.task', |
|||
domain: [ |
|||
["id", "in", this.tot_task] |
|||
], |
|||
view_mode: 'tree,kanban,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
/** |
|||
for opening account analytic line view |
|||
*/ |
|||
hr_recorded(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
if (this.flag == 0) { |
|||
this.action.doAction({ |
|||
name: _t("Timesheets"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'account.analytic.line', |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} else { |
|||
if (this.tot_hrs) { |
|||
this.action.doAction({ |
|||
name: _t("Timesheets"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'account.analytic.line', |
|||
domain: [ |
|||
["id", "in", this.tot_hrs] |
|||
], |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
} |
|||
} |
|||
/** |
|||
for opening sale order view |
|||
*/ |
|||
tot_sale(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
this.action.doAction({ |
|||
name: _t("Sale Order"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'sale.order', |
|||
domain: [ |
|||
["id", "in", this.tot_so] |
|||
], |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
/** |
|||
* Event handler to view a list of employees. |
|||
* @param {Event} e - The click event. |
|||
*/ |
|||
tot_emp(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
if (this.flag == 0) { |
|||
this.action.doAction({ |
|||
name: _t("Employees"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'hr.employee', |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} else { |
|||
this.action.doAction({ |
|||
name: _t("Employees"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'hr.employee', |
|||
domain: [ |
|||
["id", "in", this.tot_employee] |
|||
], |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
|
|||
} |
|||
} |
|||
} |
|||
ProjectDashboard.template = "ProjectDashboard" |
|||
registry.category("actions").add("project_dashboard", ProjectDashboard) |
@ -0,0 +1,369 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- Template for the project dashboard --> |
|||
<templates id="template" xml:space="preserve"> |
|||
<t t-name="ProjectDashboard"> |
|||
<div class="oh_dashboards" |
|||
style="margin-top: 20px; overflow-y: scroll;vertical-align: middle;overflow-x: clip;max-height: -webkit-fill-available;"> |
|||
<div class="container-fluid o_pj_dashboard" |
|||
style="margin-left:4%;"> |
|||
<t t-call="DashboardProject"/> |
|||
<t t-call="DashboardChart"/> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<t t-name="DashboardProject"> |
|||
<!-- Template for filter items and cards --> |
|||
<div class="row main-section"> |
|||
<t t-if="flag_user == 1"> |
|||
<div class="inner_select" style="display: flex;"> |
|||
<p style="margin-left: 20px;">Start Date :</p> |
|||
<p> |
|||
<input type="date" class="inner_select" id="start_date" |
|||
name="start_date" t-on-change="(ev) => this._onchangeFilter(ev)"/> |
|||
</p> |
|||
<p>End Date :</p> |
|||
<p> |
|||
<input type="date" class="inner_select" id="end_date" |
|||
name="end_date" t-on-change="(ev) => this._onchangeFilter(ev)"/> |
|||
</p> |
|||
<p>Project :</p> |
|||
<p> |
|||
<select class="inner_select" id="project_selection" t-on-change="(ev) => this._onchangeFilter(ev)"> |
|||
<option value="null">All Projects</option> |
|||
</select> |
|||
</p> |
|||
<p>Employees :</p> |
|||
<p> |
|||
<select class="inner_select" id="employee_selection" t-on-change="(ev) => this._onchangeFilter(ev)"> |
|||
<option value="null">All Employees</option> |
|||
</select> |
|||
</p> |
|||
<button class="btn btn-danger g-col-6 p-2" onclick="location.reload()"> |
|||
Reset |
|||
</button> |
|||
</div> |
|||
</t> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_projects" t-on-click="(e) => this.tot_projects(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-puzzle-piece" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Total Project</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" |
|||
id="tot_project"> |
|||
<t t-esc="total_projects"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<t t-if="flag_user == 1"> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_emp" t-on-click="(e) => this.tot_emp(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-user" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Total Employees</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" |
|||
id="tot_employee"> |
|||
<t t-esc="total_employees"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_tasks" t-on-click="(e) => this.tot_tasks(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-tasks" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Total tasks</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" id="tot_task"> |
|||
<t t-esc="total_tasks"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<t t-if="flag_user == 1"> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body hr_recorded" t-on-click="(e) => this.hr_recorded(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-clock-o" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Hours Recorded</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" |
|||
id="tot_hrs"> |
|||
<t t-esc="total_hours"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<t t-if="flag_user == 1"> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_profitability" t-on-click="(e) => this.tot_sale(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-dollar" /></div> |
|||
<div class="stat-head" style="padding: 5%;width: 60%;">Total Margin</div> |
|||
<div class="stat_count" style="padding: 4%;width: 30%;display:inline-table;" |
|||
id="tot_margin"> |
|||
<t t-esc="total_profitability"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_sale" t-on-click="(e) => this.tot_emp(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-ticket" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Total Sale Orders</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" id="tot_so"> |
|||
<t t-esc="total_sale_orders"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<t t-name="DashboardChart"> |
|||
<!-- Template for charts --> |
|||
<div class="col-xs-12 col-sm-12 col-lg-12 col-md-12"> |
|||
<div class="row main-section"> |
|||
<div class="col-sm-7 col-lg-7"> |
|||
<div class="graph_view" style="box-shadow:5px 11px 30px;"> |
|||
<div class="text-color hr-chart-1"> |
|||
<div class="oh-card-body pb-0" |
|||
style="text-align:center;"> |
|||
<h2 style="margin-left:45%;padding-top:2%;">Project Task Analysis</h2> |
|||
</div> |
|||
<canvas id="project_doughnut" |
|||
style="background:#fff;" width="200" |
|||
height="120"/> |
|||
</div> |
|||
</div> |
|||
<div class="selling_product_graph_view" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="oh-card text-color"> |
|||
<canvas class="top_selling_employees" |
|||
style="background:#fff;" width="200" |
|||
height="120"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-lg-4" style="top: 82px;right: -100px;"> |
|||
<div class="hr_notification" style="background: #fff;transition: transform 0.2s ease, box-shadow 0.2s ease;will-change: transform, box-shadow;box-shadow: 0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62,57,107,0.06); |
|||
height: 763px;margin-bottom: 15px;margin-top: 15px;box-shadow:5px 11px 30px;"> |
|||
<div class="hr_notification_head" |
|||
style="font-size: 17px;text-align: center;padding: 12px 0;color: #fff;font-weight: 300;background: #000080;margin-bottom: 9px;"> |
|||
Project Task Details |
|||
</div> |
|||
<div class="col-sm-12 col-lg-12" style="padding:0;"> |
|||
<div class="text-color"> |
|||
<div class="media" |
|||
style="overflow-y: auto;height: 704px;"> |
|||
<div class="media-body"> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Project Name</th> |
|||
<th rowspan="14">Task Name</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="task_data" |
|||
t-as="proj" t-key="proj"> |
|||
<tr> |
|||
<td> |
|||
<t t-if="flag_user == 1"> |
|||
<t t-esc="proj[1]['en_US']"/> |
|||
</t> |
|||
<t t-els=""> |
|||
<t t-esc="proj[1]"/> |
|||
</t> |
|||
</td> |
|||
<td> |
|||
<t t-esc="proj[0]"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="hr_notification" style="background: #fff;transition: transform 0.2s ease, box-shadow 0.2s ease;will-change: transform, box-shadow;box-shadow: 0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62,57,107,0.06); |
|||
overflow-y: scroll;vertical-align: middle;overflow-x: clip;max-height: 37%;margin-bottom: 15px;box-shadow:5px 11px 30px;"> |
|||
<div class="hr_notification_head" |
|||
style="font-size: 17px;text-align: center;padding: 12px 0;color: #fff;font-weight: 300;background: #000080;margin-bottom: 9px;"> |
|||
Hours Recorded |
|||
</div> |
|||
<!-- Updated code with right-aligned values for all tables --> |
|||
<div class="col-sm-12 col-lg-12" style="padding: 0;"> |
|||
<div class="text-color"> |
|||
<div class=""> |
|||
<div class="media"> |
|||
<div class="media-body"> |
|||
<!-- Table 1: Billed on Timesheet --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Billed on Timesheet</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="hour_recorde" t-as="hour_recorde" t-key="hour_recorde"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="hour_recorde"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
<!-- Table 2: Billed at a Fixed Price --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Billed at a Fixed Price</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="billable_fix" t-as="billable_fix" t-key="billable_fix"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="billable_fix"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
|
|||
<!-- Table 3: No Task Found --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">No Task Found</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="hour_recorded" t-as="hour_recorded" t-key="hour_recorded"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="hour_recorded"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
|
|||
<!-- Table 4: Non Billable Tasks --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Non Billable Tasks</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="non_billable" t-as="non_billable" t-key="non_billable"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="non_billable"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
<!-- Table 5: Total --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Total:</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="total_hr" t-as="total_hr" t-key="total_hr"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="total_hr"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="hr_notification" style="background: #fff;transition: transform 0.2s ease, box-shadow 0.2s ease;will-change: transform, box-shadow;box-shadow: 0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62,57,107,0.06); |
|||
height: auto;padding-bottom: 15px;box-shadow:5px 11px 30px;"> |
|||
<div class="hr_notification_head" |
|||
style="font-size: 17px;text-align: center;padding: 12px 0;color: #fff;font-weight: 300;background: #000080;margin-bottom: 9px;"> |
|||
Stage Wise Total Projects |
|||
</div> |
|||
<table id="table_status" style="width"> |
|||
<tr> |
|||
<th/> |
|||
<th/> |
|||
</tr> |
|||
<t t-foreach="project_stage_list" |
|||
t-as="data" t-key="project_stage_list"> |
|||
<tr> |
|||
<td style="text-align:center;"> |
|||
<h4 t-esc="data['name']"/> |
|||
</td> |
|||
<td style="text-align:center;"> |
|||
<h4 class="fleet-pill" |
|||
t-esc="data['projects']"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
</templates> |
@ -0,0 +1,14 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Action for Project Dashboard--> |
|||
<record id="project_dashboard_action" model="ir.actions.client"> |
|||
<field name="name">Dashboard</field> |
|||
<field name="tag">project_dashboard</field> |
|||
</record> |
|||
<!-- Dashboard menu item --> |
|||
<menuitem id="project_menu_open_Dashboard" |
|||
name="Dashboard" |
|||
action="project_dashboard_action" |
|||
parent="project.menu_main_pm" |
|||
sequence="1"/> |
|||
</odoo> |