@ -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: (V18) Nihala KP, 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: Nihala KP @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: Nihala KP @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': '18.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,379 @@ |
|||||
|
# -*- 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 = [] |
||||
|
if request.env.user.has_group('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.""" |
||||
|
if request.env.user.has_group('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] |
||||
|
task_so_ids = list(set(task_so_ids)) |
||||
|
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.""" |
||||
|
if request.env.user.has_group('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.""" |
||||
|
if request.env.user.has_group('project.group_project_manager'): |
||||
|
request._cr.execute('''select project_task.name as task_name, |
||||
|
project_task.id, |
||||
|
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, task.id] for task |
||||
|
in |
||||
|
all_tasks] |
||||
|
return { |
||||
|
'project': task_project |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
## Module <project_dashboard_odoo> |
||||
|
|
||||
|
#### 4.10.2024 |
||||
|
#### Version 18.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: Nihala KP @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: Nihala KP @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: 9.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 628 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 272 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 912 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 207 KiB |
After Width: | Height: | Size: 207 KiB |
After Width: | Height: | Size: 233 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 197 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 880 KiB |
After Width: | Height: | Size: 91 KiB |
@ -0,0 +1,841 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"/> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
||||
|
<title> Project Dashboard</title> |
||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" |
||||
|
rel="stylesheet"/> |
||||
|
<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=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" |
||||
|
rel="stylesheet"> |
||||
|
<link rel="stylesheet" |
||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"/> |
||||
|
<link rel="stylesheet" |
||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css"/> |
||||
|
<style> |
||||
|
:root { |
||||
|
--primary-color: #7f54b3; |
||||
|
--bg-white: #fff; |
||||
|
--text-color: #121212; |
||||
|
--text-color-light: #64728f; |
||||
|
} |
||||
|
body{ |
||||
|
font-family: "Montserrat", sans-serif; |
||||
|
} |
||||
|
.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { |
||||
|
color: #121212; |
||||
|
font-family: Montserrat; |
||||
|
font-size: 16px !important; |
||||
|
font-weight: 500 !important; |
||||
|
border-radius: 30px; |
||||
|
line-height: normal; |
||||
|
text-transform: capitalize; |
||||
|
background-color: #F5F5F5; |
||||
|
border: none; |
||||
|
margin-bottom: 0; |
||||
|
padding: 12px 24px; |
||||
|
} |
||||
|
|
||||
|
.nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { |
||||
|
border-color: transparent; |
||||
|
isolation: isolate; |
||||
|
} |
||||
|
|
||||
|
.nav-tabs .nav-link:focus-visible { |
||||
|
border-color: transparent; |
||||
|
box-shadow: none; |
||||
|
} |
||||
|
|
||||
|
/* owl-carosel */ |
||||
|
.owl-carousel .owl-nav { |
||||
|
position: absolute; |
||||
|
top: 42%; |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
transform: translateY(-42%); |
||||
|
} |
||||
|
|
||||
|
.owl-carousel .owl-nav button.owl-prev { |
||||
|
position: absolute; |
||||
|
right: -36px; |
||||
|
font-size: 28px; |
||||
|
background-color: #e4e4e4; |
||||
|
border-radius: 20px; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.owl-carousel .owl-nav button.owl-next { |
||||
|
position: absolute; |
||||
|
left: -36px; |
||||
|
font-size: 28px; |
||||
|
background-color: #e4e4e4; |
||||
|
border-radius: 20px; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<!-- overview --> |
||||
|
<div class="container"> |
||||
|
<div class="my-5"> |
||||
|
<!-- button tab --> |
||||
|
<!-- --> |
||||
|
<!-- version support --> |
||||
|
<div class="my-3 d-flex align-items-center justify-content-end"> |
||||
|
<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 class="tab-content" id="myTabContent"> |
||||
|
<!-- description --> |
||||
|
<div class="tab-pane fade show active" id="home" role="tabpanel" |
||||
|
aria-labelledby="home-tab"> |
||||
|
<div class="position-relative" |
||||
|
style="border-radius: 16px; background: #f8f8f8; padding: 20px 0;"> |
||||
|
<div class="row " style=" |
||||
|
padding: 2rem 0rem 0 !important; |
||||
|
"> |
||||
|
<div class="col-lg-8 mx-auto gap-4 d-flex flex-column align-items-center"> |
||||
|
<p class="my-1 text-center text-uppercase" |
||||
|
style=" |
||||
|
letter-spacing: 4px !important; |
||||
|
color: #7f54b3; |
||||
|
font-weight: bold; |
||||
|
text-align: center; |
||||
|
font-size: 14px; |
||||
|
font-weight: 600; |
||||
|
line-height: 15.96px; |
||||
|
text-transform: uppercase; |
||||
|
"> |
||||
|
In This Dashboard, You Can Get a Detailed View for Project. |
||||
|
</p> |
||||
|
<h1 class="text-center text-uppercase my-0" |
||||
|
style=" |
||||
|
color: #121212; |
||||
|
font-size: 46px; |
||||
|
font-weight: 700; |
||||
|
line-height: normal; |
||||
|
"> Project Dashboard</span> |
||||
|
</h1> |
||||
|
</div> |
||||
|
<div class="col-lg-12 d-flex justify-content-center align-items-center" |
||||
|
style="margin: 3rem 0;"> |
||||
|
<img src="./assets/icons/brand-pair.svg" |
||||
|
width="100%" |
||||
|
height="auto" |
||||
|
style="width: 50%" |
||||
|
class="img-responsive"/> |
||||
|
</div> |
||||
|
<div class="col-md-12 text-center"> |
||||
|
<a href="mailto:odoo@cybrosys.com" |
||||
|
target="_blank" |
||||
|
style="background-color: transparent;border-radius: 35px; |
||||
|
font-family: Montserrat; |
||||
|
display: inline-block; |
||||
|
padding: 7px 33px; |
||||
|
border: 1px solid #7f54b3; |
||||
|
color: #7f54b3; |
||||
|
text-decoration: none; |
||||
|
" |
||||
|
class="mx-1 mb-2 deep-1 deep_hover"> |
||||
|
<img class="img" |
||||
|
style="width: 24px" |
||||
|
src="./assets/icons/mail.svg"/> |
||||
|
<span class="pl-2" |
||||
|
style=" font-size: 16px; vertical-align: middle" |
||||
|
>Email Us</span |
||||
|
> |
||||
|
</a> |
||||
|
<a href="skype:cybroopenerp?chat" |
||||
|
target="_blank" |
||||
|
style=" |
||||
|
background-color: #7f289b; |
||||
|
font-family: Montserrat; |
||||
|
display: inline-block; |
||||
|
padding: 7px 33px; |
||||
|
border: 1px solid #7f289b; |
||||
|
border-radius: 35px; |
||||
|
text-decoration: none; |
||||
|
" |
||||
|
class="mx-1 mb-2 deep-1 deep_hover"> |
||||
|
<img |
||||
|
class="img" |
||||
|
style="width: 24px" |
||||
|
src="./assets/icons/skype-fill.svg" |
||||
|
/> |
||||
|
<span |
||||
|
class="pl-2" |
||||
|
style="color: #fff; font-size: 16px; vertical-align: middle" |
||||
|
>Skype Us</span |
||||
|
> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="d-flex justify-content-center mt-2"> |
||||
|
<img src="./assets/icons/hero.gif" |
||||
|
class="w-100" |
||||
|
style="z-index: 3; height: auto;"> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="position-absolute bottom-0" |
||||
|
style="z-index: 1; width: 100%;"> |
||||
|
<img src="./assets/icons/banner-bg.svg" |
||||
|
class="img-fluid w-100"> |
||||
|
</div> |
||||
|
<div class="position-absolute bottom-0 end-0" |
||||
|
style=" z-index: 2;"> |
||||
|
<img src="./assets/icons/patter.svg"> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- key-highlight --> |
||||
|
<div class="" style="border-radius: 16px; |
||||
|
padding: 60px 40px; |
||||
|
border: 1px solid #EBEEF2; |
||||
|
background: #F5F5F7; |
||||
|
box-shadow: 0px 5px 20px -11px rgba(0, 0, 0, 0.25); "> |
||||
|
<div class="row"> |
||||
|
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center"> |
||||
|
<h2 style=" color: #121212; |
||||
|
text-align: center; |
||||
|
font-size: 40px; |
||||
|
font-weight: 700; |
||||
|
text-transform: uppercase; padding-bottom: 50px;">Key |
||||
|
Highlights</h2> |
||||
|
</div> |
||||
|
<div class="col-lg-4"> |
||||
|
<div class="mb-4 d-flex flex-column justify-content-center gap-3" |
||||
|
style="border-radius: 12px; border: 1px solid #B6BCCD; |
||||
|
background: #FFF;padding:32px "> |
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#7847D9 !important; border-radius:8px !important; height:42px; width:42px"> |
||||
|
<img src="./assets/icons/feature-icon.svg" |
||||
|
class="img-responsive" height="26px" |
||||
|
width="26px"> |
||||
|
</div> |
||||
|
<h5 class="m-0" |
||||
|
style="color:#000 !important; font-weight:bold"> |
||||
|
Dashboard view for Project module |
||||
|
</h5> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-lg-4"> |
||||
|
<div class="mb-4 d-flex flex-column justify-content-center gap-3" |
||||
|
style="border-radius: 12px; |
||||
|
border: 1px solid #B6BCCD; |
||||
|
background: #FFF;padding:32px "> |
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#7847D9 !important; border-radius:8px !important; height:42px; width:42px"> |
||||
|
<img src="./assets/icons/feature-icon.svg" |
||||
|
class="img-responsive" height="26px" |
||||
|
width="26px"> |
||||
|
</div> |
||||
|
<h5 class="m-0" |
||||
|
style="color:#000 !important; font-weight:bold"> |
||||
|
Graphs Are Included in the view |
||||
|
</h5> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!--code --> |
||||
|
<div class="my-5"> |
||||
|
<div class="position-relative" style=" padding: 5rem 4rem 5rem 4rem; background-color: #0A1425; border-radius: 12px;"> |
||||
|
<div class="d-flex flex-column gap-4"> |
||||
|
<span class="wrapper-subtitle" |
||||
|
style="font-size: 40px; font-weight: 700; color: #fff;line-height: 60px; text-transform: capitalize; width: 450px; font-family: Montserrat;">Project Dashboard</span> |
||||
|
<h3 class="wrapper-details" |
||||
|
style="font-size: 20px; font-weight: 400; color: #fff; line-height: 32px; "> |
||||
|
Are you ready to make your business more |
||||
|
organized? |
||||
|
<br> Improve now! |
||||
|
</h3> |
||||
|
<div class="d-flex gap-3"> |
||||
|
<a href="mailto:odoo@cybrosys.com" |
||||
|
class="shop-btn" style="cursor: pointer; border-radius: 16px; display: flex; justify-content: center; align-items: center; gap: 7px; |
||||
|
border: 1px solid #ffffff33; |
||||
|
background-color: #ffffff14; |
||||
|
backdrop-filter: blur(10px); color: #fff; padding: 12px 16px 12px 16px; text-decoration: none;"> |
||||
|
<span style="border-radius: 12px; |
||||
|
background-color: #ffffff1a; |
||||
|
backdrop-filter: blur(6px);padding: 12px; "> |
||||
|
<img src="./assets/icons/banner-mail.svg"> |
||||
|
</span> |
||||
|
<span style="font-weight: 500;font-family: Montserrat;">odoo@cybrosys.com</span> |
||||
|
</a> |
||||
|
<a href="tel:+91 9074270811" class="shop-btn" |
||||
|
style="cursor: pointer; border-radius: 16px; display: flex; justify-content: center; align-items: center; gap: 7px; |
||||
|
border: 1px solid #ffffff33; |
||||
|
background-color: #ffffff14; |
||||
|
backdrop-filter: blur(10px); color: #fff; padding: 12px 22px 12px 18px; text-decoration: none;"> |
||||
|
<span style="border-radius: 12px; |
||||
|
background-color: #ffffff1a; |
||||
|
backdrop-filter: blur(6px);padding: 12px;"> |
||||
|
<img src="./assets/icons/banner-call.svg"> |
||||
|
</span> |
||||
|
<span style="font-weight: 500;font-family: Montserrat;">+91 9074270811</span> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="position-absolute bottom-0 end-0"> |
||||
|
<img src="./assets/icons/banner-pattern.svg" |
||||
|
style="width: 540px;"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- end-code --> |
||||
|
<!-- --> |
||||
|
<!-- screenshot and other --> |
||||
|
|
||||
|
<div class="mb-4 bg-white" |
||||
|
style=" border: 1px solid #EBEEF2; border-radius: 6px; box-shadow: 0px 8px 20px -4px rgba(0, 0, 0, 0.10); border: 1px solid #EBEEF2;"> |
||||
|
<div> |
||||
|
<ul class="nav nav-tabs justify-content-center bg-white py-2" |
||||
|
id="myTab" role="tablist" |
||||
|
style="border-radius: 6px 6px 0 0;"> |
||||
|
<li class="nav-item"> |
||||
|
<a aria-controls="overview" |
||||
|
aria-bs-selected="true" |
||||
|
class="nav-link active" data-bs-toggle="tab" |
||||
|
href="#overview" id="overview-tab" role="tab" |
||||
|
style="color:#121212; font-weight:500; font-size:16px"> |
||||
|
Screenshots</a> |
||||
|
</li> |
||||
|
<li class="nav-item"> |
||||
|
<a aria-controls="feature" |
||||
|
aria-bs-selected="false" |
||||
|
class="nav-link py-2" data-bs-toggle="tab" |
||||
|
href="#feature" id="feature-tab" role="tab" |
||||
|
style="color:#121212; font-weight:500; font-size:16px">Features</a> |
||||
|
</li> |
||||
|
|
||||
|
<li class="nav-item"> |
||||
|
<a aria-controls="releases" |
||||
|
aria-bs-selected="false" class="nav-link" |
||||
|
data-bs-toggle="tab" href="#releases" |
||||
|
id="releases-tab" role="tab" |
||||
|
style="color:#121212; font-weight:500; font-size:16px">Releases</a> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="tab-content p-md-5 p-2 py-3" id="myTabContent"> |
||||
|
<div aria-labelledby="overview-tab" |
||||
|
class="tab-pane fade show active" id="overview" |
||||
|
role="tabpanel"> |
||||
|
<div class="position-relative mb-4" |
||||
|
style="border-radius:10px"> |
||||
|
<img alt="acc_bg" |
||||
|
class="w-100 h-100 position-absolute img-fluid left_0" |
||||
|
loading="lazy" |
||||
|
src="//apps.odoocdn.com/apps/assets/17.0/ks_dashboard_ninja/ai-img/o3.png?007008f" |
||||
|
style=""> |
||||
|
</div> |
||||
|
<!-- screenshots section--> |
||||
|
<div class="position-relative mb-4" |
||||
|
style="border-radius:10px; background-color:#f4f4f4"> |
||||
|
<div class="p-md-5 p-3 position-relative"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px; |
||||
|
font-weight: 700;"> |
||||
|
<span style="color:#121212; font-size:calc(1.1rem + 1vw)">Different Types of Graphs. |
||||
|
</span> |
||||
|
<!-- <span style="color: var(--primary-color); font-size:calc(1.1rem + 1vw)"> Menu</span>--> |
||||
|
</h1> |
||||
|
</div> |
||||
|
<div class="col-md-12 mb-4"> |
||||
|
<p style="font-weight:400; font-size:16px; line-height:150%; text-align:center; color:var(--text-color-light)"> |
||||
|
Project Dashboard has different types of Graphs that will give you a complete analyzing of the Project module. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="col-md-12 text-center"> |
||||
|
<div class="d-inline-block p-3 shadow-sm" |
||||
|
style="background-color:#fff; border-radius:10px"> |
||||
|
<img alt="" class="img-fluid" |
||||
|
loading="lazy" |
||||
|
src="./assets/screenshots/1.png" |
||||
|
style="min-height: 1px;"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="position-relative mb-4" |
||||
|
style="border-radius:10px; background-color:#f4f4f4"> |
||||
|
<div class="p-md-5 p-3 position-relative"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-12 text-center"> |
||||
|
<div class="d-inline-block p-3 shadow-sm" |
||||
|
style="background-color:#fff; border-radius:10px"> |
||||
|
<img alt="" class="img-fluid" |
||||
|
loading="lazy" |
||||
|
src="./assets/screenshots/2.png" |
||||
|
style="min-height: 1px;"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="position-relative mb-4" |
||||
|
style="border-radius:10px; background-color:#f4f4f4"> |
||||
|
<div class="p-md-5 p-3 position-relative"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px; |
||||
|
font-weight: 700;"> |
||||
|
|
||||
|
</h1> |
||||
|
</div> |
||||
|
<div class="col-md-12 mb-4"> |
||||
|
</div> |
||||
|
<div class="col-md-12 text-center"> |
||||
|
<div class="d-inline-block p-3 shadow-sm" |
||||
|
style="background-color:#fff; border-radius:10px"> |
||||
|
<img alt="" class="img-fluid" |
||||
|
loading="lazy" |
||||
|
src="./assets/screenshots/3.png" |
||||
|
style="min-height: 1px;"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="position-relative mb-4" |
||||
|
style="border-radius:10px; background-color:#f4f4f4"> |
||||
|
<div class="p-md-5 p-3 position-relative"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px; |
||||
|
font-weight: 700;"> |
||||
|
<span style="color:#121212; font-size:calc(1.1rem + 1vw)">Project Table |
||||
|
</span> |
||||
|
</h1> |
||||
|
</div> |
||||
|
<div class="col-md-12 mb-4"> |
||||
|
<p style="font-weight:400; font-size:16px; line-height:150%; text-align:center; color:var(--text-color-light)"> |
||||
|
User can see all Project ad its current Status. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="col-md-12 text-center"> |
||||
|
<div class="d-inline-block p-3 shadow-sm" |
||||
|
style="background-color:#fff; border-radius:10px"> |
||||
|
<img alt="" class="img-fluid" |
||||
|
loading="lazy" |
||||
|
src="./assets/screenshots/6.png" |
||||
|
style="min-height: 1px;"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div aria-labelledby="feature-tab" |
||||
|
class="tab-pane fade show py-1" id="feature" |
||||
|
role="tabpanel"> |
||||
|
<div class="row py-4"> |
||||
|
<!-- Features Section --> |
||||
|
<div class="col-md-6 col-sm-12 p-3"> |
||||
|
<div class="d-flex flex-column align-items-start h-100" |
||||
|
style="padding:30px; border-radius:12px; background-color:#faf8ff"> |
||||
|
<div class="d-flex align-items-center justify-content-center"> |
||||
|
<div class="d-flex align-items-center justify-content-center " |
||||
|
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px"> |
||||
|
<i class="fa fa-star " |
||||
|
style="color:#fff; font-size:14px"></i> |
||||
|
</div> |
||||
|
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px"> |
||||
|
User can see all details about Project and Task through Graphs. </p> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6 col-sm-12 p-3"> |
||||
|
<div class="d-flex flex-column align-items-start h-100" |
||||
|
style="padding:30px; border-radius:12px; background-color:#faf8ff"> |
||||
|
<div class="d-flex align-items-center justify-content-center"> |
||||
|
<div class="d-flex align-items-center justify-content-center " |
||||
|
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px"> |
||||
|
<i class="fa fa-star " |
||||
|
style="color:#fff; font-size:14px"></i> |
||||
|
</div> |
||||
|
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px"> |
||||
|
User can see all details about Timesheet through Graphs. </p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6 col-sm-12 p-3"> |
||||
|
<div class="d-flex flex-column align-items-start h-100" |
||||
|
style="padding:30px; border-radius:12px; background-color:#faf8ff"> |
||||
|
<div class="d-flex align-items-center justify-content-center"> |
||||
|
<div class="d-flex align-items-center justify-content-center " |
||||
|
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px"> |
||||
|
<i class="fa fa-star " |
||||
|
style="color:#fff; font-size:14px"></i> |
||||
|
</div> |
||||
|
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px"> |
||||
|
User can see all Projects with Stages. </p> |
||||
|
</div> |
||||
|
<div class="ms-5"> |
||||
|
<p class="m-0" |
||||
|
style="color:#718096"> |
||||
|
</p> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6 col-sm-12 p-3"> |
||||
|
<div class="d-flex flex-column align-items-start h-100" |
||||
|
style="padding:30px; border-radius:12px; background-color:#faf8ff"> |
||||
|
<div class="d-flex align-items-center justify-content-center"> |
||||
|
<div class="d-flex align-items-center justify-content-center " |
||||
|
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px"> |
||||
|
<i class="fa fa-star " |
||||
|
style="color:#fff; font-size:14px"></i> |
||||
|
</div> |
||||
|
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px"> |
||||
|
User can use filter based on the Employee, Project and Dates. </p> |
||||
|
</div> |
||||
|
<div class="ms-5"> |
||||
|
<p class="m-0" |
||||
|
style="color:#718096"> |
||||
|
</p> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div aria-labelledby="releases-tab" |
||||
|
class="tab-pane fade show" id="releases" |
||||
|
role="tabpanel"> |
||||
|
<!-- Release Notes --> |
||||
|
<div class="row pt-5 m-0"> |
||||
|
<div class="col-md-3"> |
||||
|
<h4 style="font-size:16px; font-weight:600; color:#514F4F; margin:0; line-height:26px;"> |
||||
|
Latest Release 18.0.1.0.0 |
||||
|
</h4> |
||||
|
<span style="font-size:14px; color:#7A7979; display:block; margin-bottom:20px;"> |
||||
|
4th October, 2024 |
||||
|
</span> |
||||
|
</div> |
||||
|
<div class="col-md-8"> |
||||
|
<div style="padding:0 0 40px"> |
||||
|
<div style="margin:0 0 10px"> |
||||
|
<div style="display:inline-block; padding:0px 8px; color:#514F4F; background-color:#FFD8D8; border-radius:20px"> |
||||
|
Add |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="d-flex m-0" |
||||
|
style="color:#7A7979;"> |
||||
|
<ul class="pl-3 mb-0"> |
||||
|
<li> |
||||
|
Initial commit for Project Dashboard |
||||
|
</li> |
||||
|
|
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div style="padding:0 0 0; border-bottom:1px solid #E3E3E3"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- --> |
||||
|
<!-- related post --> |
||||
|
<!-- --> |
||||
|
<section class="oe_container mt32"> |
||||
|
<h2 style="color: #091E42;font-family: "Montserrat";text-align: center;margin: 25px auto;text-transform: uppercase;" class="oe_slogan"> |
||||
|
<b>Related Products</b> |
||||
|
</h2> |
||||
|
<div id="demo" class="row carousel slide mt64 mb32" data-bs-ride="carousel"> |
||||
|
<!-- The slideshow --> |
||||
|
<div class="carousel-inner"> |
||||
|
<div class="carousel-item active"> |
||||
|
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
||||
|
<a href="https://apps.odoo.com/apps/modules/17.0/base_accounting_kit" target="_blank" style="color: #000; text-decoration: none;"> |
||||
|
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
||||
|
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/1.gif" /> |
||||
|
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
||||
|
Odoo 17 Full Accounting Kit</h4> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
||||
|
<a href="https://apps.odoo.com/apps/modules/17.0/ohrms_core" target="_blank" style="color: #000; text-decoration: none;"> |
||||
|
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
||||
|
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/2.gif" /> |
||||
|
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
||||
|
Open HRMS Core</h4> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
||||
|
<a href="https://apps.odoo.com/apps/modules/17.0/invoice_format_editor" target="_blank" style="color: #000; text-decoration: none;"> |
||||
|
<div style="border-radius: 6px;padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
||||
|
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/3.png"/> |
||||
|
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
||||
|
Odoo17 Invoice Format Editor </h4> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="carousel-item"> |
||||
|
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
||||
|
<a href="https://apps.odoo.com/apps/modules/17.0/login_user_detail" target="_blank" style="color: #000; text-decoration: none;"> |
||||
|
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
||||
|
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/4.png" /> |
||||
|
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
||||
|
User Log Details</h4> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
||||
|
<a href="https://apps.odoo.com/apps/modules/17.0/product_barcode" target="_blank" style="color: #000; text-decoration: none;"> |
||||
|
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
||||
|
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/5.png" /> |
||||
|
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
||||
|
Odoo17 Product Barcode Generator </h4> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
||||
|
<a href="https://apps.odoo.com/apps/modules/17.0/whatsapp_redirect" target="_blank" style="color: #000; text-decoration: none;"> |
||||
|
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
||||
|
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/6.jpg" /> |
||||
|
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
||||
|
Send Whatsapp Message Odoo17</h4> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- Left and right controls --> |
||||
|
<a class="carousel-control-prev" href="#demo" data-bs-slide="prev" style="margin-left: -30px;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="#demo" data-bs-slide="next" style="margin-right: -30px;width: 35px;color: #000;"> |
||||
|
<span class="carousel-control-next-icon"> |
||||
|
<i class="fa fa-chevron-right" style="font-size:24px"></i> |
||||
|
</span> |
||||
|
</a> |
||||
|
</div> |
||||
|
</section> |
||||
|
<!-- service-section --> |
||||
|
|
||||
|
<section id="services" class="mt-5" style="border-radius: 16px; |
||||
|
border: 1px solid #EBEEF2; |
||||
|
background: var(--Neutral-N0, #FFF); |
||||
|
padding: 60px 40px; |
||||
|
box-shadow: 0px 5px 20px -11px rgba(0, 0, 0, 0.25);"> |
||||
|
<div class="text-center mt-4"><h3 class="mb-0" style="color: #000; |
||||
|
text-align: center; |
||||
|
font-family: Montserrat; |
||||
|
font-size: 40px; |
||||
|
font-style: normal; |
||||
|
font-weight: 700; |
||||
|
line-height: normal; |
||||
|
text-transform: uppercase; |
||||
|
padding-bottom: 50px;"> |
||||
|
Our Services</h3></div> |
||||
|
<div class="row mt-3"> |
||||
|
<div class="col-lg-3 col-sm-12 mb-3"> |
||||
|
<a href="#" style="text-decoration:none"> |
||||
|
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
||||
|
style="font-size:25px; font-weight:bold;background-color:#FFE2E5; margin:auto; gap: 16px; border-radius: 8px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#FA5A7D; border-radius:50%; height:56px; width:56px"> |
||||
|
<img src="./assets/icons/gear.svg" |
||||
|
class="img-responsive" |
||||
|
height="28px" width="28px"> |
||||
|
</div> |
||||
|
<span style="font-size: 18px; |
||||
|
color: var(--text-color); |
||||
|
font-weight: 600;"> Odoo Customization</span> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-lg-3 col-sm-12 mb-3"> |
||||
|
<a href="#" style="text-decoration:none"> |
||||
|
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
||||
|
style="font-size:25px; font-weight:bold;background-color:#FFF4DE; margin:auto; gap: 16px; border-radius: 8px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#FF947A; border-radius:50%; height:56px; width:56px"> |
||||
|
<img src="./assets/icons/wrench-icon.svg" |
||||
|
class="img-responsive" |
||||
|
height="28px" width="28px"> |
||||
|
</div> |
||||
|
<span style="font-size: 18px; |
||||
|
color: var(--text-color); |
||||
|
font-weight: 600;"> Odoo Implementation</span> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-lg-3 col-sm-12 mb-3"> |
||||
|
<a href="#" style="text-decoration:none"> |
||||
|
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
||||
|
style="font-size:25px; font-weight:bold;background-color:#DCFCE7; margin:auto; gap: 16px; border-radius: 8px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#3CD856; border-radius:50%; height:56px; width:56px"> |
||||
|
<img src="./assets/icons/life-ring-icon.svg" |
||||
|
class="img-responsive" |
||||
|
height="28px" width="28px"> |
||||
|
</div> |
||||
|
<span style="font-size: 18px; |
||||
|
color: var(--text-color); |
||||
|
font-weight: 600;">Odoo Support</span> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-lg-3 col-sm-12 mb-3"> |
||||
|
<a href="#" style="text-decoration:none"> |
||||
|
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
||||
|
style="font-size:25px; font-weight:bold;background-color:#F3E8FF; margin:auto; gap: 16px; border-radius: 8px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#BF83FF; border-radius:50%; height:56px; width:56px"> |
||||
|
<img src="./assets/icons/arrows-repeat.svg" |
||||
|
class="img-responsive" |
||||
|
height="28px" width="28px"> |
||||
|
</div> |
||||
|
<span style="font-size: 18px; |
||||
|
color: var(--text-color); |
||||
|
font-weight: 600;">Odoo Migration</span> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-lg-3 col-sm-12 mb-3"> |
||||
|
<a href="#" style="text-decoration:none"> |
||||
|
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
||||
|
style="font-size:25px; font-weight:bold;background-color:#F1F9FF; margin:auto; gap: 16px; border-radius: 8px;"> |
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#01649C; border-radius:50%; height:56px; width:56px"> |
||||
|
<img src="./assets/icons/puzzle-piece-icon.svg" |
||||
|
class="img-responsive" |
||||
|
height="28px" width="28px"> |
||||
|
</div> |
||||
|
<span style="font-size: 18px; |
||||
|
color: var(--text-color); |
||||
|
font-weight: 600;">Odoo integration</span> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-lg-3 col-sm-12 mb-3"> |
||||
|
<a href="#" style="text-decoration:none"> |
||||
|
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
||||
|
style="font-size:25px; font-weight:bold;background-color:#EDF8ED; margin:auto; gap: 16px; border-radius: 8px;"> |
||||
|
|
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#69CC70; border-radius:50%; height:56px; width:56px"> |
||||
|
<img src="./assets/icons/odoo-consultancy.svg" |
||||
|
class="img-responsive" |
||||
|
height="28px" width="28px"> |
||||
|
</div> |
||||
|
<span style="font-size: 18px; |
||||
|
color: var(--text-color); |
||||
|
font-weight: 600;">Odoo Consultancy</span> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-lg-3 col-sm-12 mb-3"> |
||||
|
<a href="#" style="text-decoration:none"> |
||||
|
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
||||
|
style="font-size:25px; font-weight:bold;background-color:#F1F6FF; margin:auto; gap: 16px; border-radius: 8px;"> |
||||
|
|
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#2E4556; border-radius:50%; height:56px; width:56px"> |
||||
|
<img src="./assets/icons/odoo-licencing.svg" |
||||
|
class="img-responsive" |
||||
|
height="28px" width="28px"> |
||||
|
</div> |
||||
|
<span style="font-size: 18px; |
||||
|
color: var(--text-color); |
||||
|
font-weight: 600;">Odoo Licensing</span> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="col-lg-3 col-sm-12 mb-3"> |
||||
|
<a href="#" style="text-decoration:none"> |
||||
|
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
||||
|
style="font-size:25px; font-weight:bold;background-color:#FAF6EA; margin:auto; gap: 16px; border-radius: 8px;"> |
||||
|
|
||||
|
<div class="d-flex justify-content-center align-items-center" |
||||
|
style="background-color:#FCD12C; border-radius:50%; height:56px; width:56px"> |
||||
|
<img src="./assets/icons/hire-odoo.svg" |
||||
|
class="img-responsive" |
||||
|
height="28px" width="28px"> |
||||
|
</div> |
||||
|
<span style="font-size: 18px; |
||||
|
color: var(--text-color); |
||||
|
font-weight: 600;">Hire Odoo Developer</span> |
||||
|
</div> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- licence --> |
||||
|
<div class="tab-pane fade" id="profile" role="tabpanel" |
||||
|
aria-labelledby="profile-tab"> |
||||
|
<div class="px-5"> |
||||
|
.... |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- --> |
||||
|
</div> |
||||
|
</section> |
||||
|
<!-- --> |
||||
|
</div> |
||||
|
</div> |
||||
|
</body> |
||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script> |
||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" |
||||
|
integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" |
||||
|
crossorigin="anonymous" referrerpolicy="no-referrer"></script> |
||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js"></script> |
||||
|
<script> |
||||
|
$('.owl-carousel').owlCarousel({ |
||||
|
rtl: true, |
||||
|
loop: true, |
||||
|
margin: 10, |
||||
|
nav: true, |
||||
|
responsive: { |
||||
|
0: { |
||||
|
items: 1 |
||||
|
}, |
||||
|
600: { |
||||
|
items: 3 |
||||
|
}, |
||||
|
1000: { |
||||
|
items: 3 |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
</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: 50px 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,460 @@ |
|||||
|
/** @odoo-module */ |
||||
|
import { registry} from '@web/core/registry'; |
||||
|
import { useService } from "@web/core/utils/hooks"; |
||||
|
const { Component, onWillStart} = owl |
||||
|
import { rpc } from "@web/core/network/rpc"; |
||||
|
import { _t } from "@web/core/l10n/translation"; |
||||
|
import { onMounted, useRef, useState} from "@odoo/owl"; |
||||
|
|
||||
|
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.project_doughnut = useRef("project_doughnut"); |
||||
|
this.project_selection = useRef("project_selection"); |
||||
|
this.start_date = useRef("start_date"); |
||||
|
this.end_date = useRef("end_date"); |
||||
|
this.tot_project = useRef("tot_project"); |
||||
|
this.tot_employee = useRef("tot_employee"); |
||||
|
this.tot_hrs = useRef("tot_hrs"); |
||||
|
this.tot_margin = useRef("tot_margin"); |
||||
|
this.total_task = useRef("tot_task"); |
||||
|
this.total_so = useRef("tot_so"); |
||||
|
this.employee_selection = useRef("employee_selection"); |
||||
|
this.top_selling_employees = useRef("top_selling_employees"); |
||||
|
this.state = useState({ |
||||
|
projects : '', |
||||
|
employees: "", |
||||
|
}); |
||||
|
this.rpc = this.env.services.rpc |
||||
|
onWillStart(async () => { |
||||
|
await this.willStart(); |
||||
|
}); |
||||
|
onMounted(async () => { |
||||
|
await this.mounted() |
||||
|
}); |
||||
|
} |
||||
|
/** |
||||
|
* Event handler for the 'onWillStart' event. |
||||
|
*/ |
||||
|
async willStart() { |
||||
|
await this.fetch_data(); |
||||
|
} |
||||
|
/** |
||||
|
* Event handler for the 'onMounted' event. |
||||
|
* Renders various components and charts after fetching data. |
||||
|
*/ |
||||
|
async mounted() { |
||||
|
// 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() { |
||||
|
var datas = await rpc("/project/task/count") |
||||
|
var ctx = this.project_doughnut; |
||||
|
const chart = new Chart(this.project_doughnut.el, { |
||||
|
type: "doughnut", |
||||
|
data: { |
||||
|
labels: datas.project, |
||||
|
datasets: [{ |
||||
|
backgroundColor: datas.color, |
||||
|
data: datas.task |
||||
|
}] |
||||
|
}, |
||||
|
options: { |
||||
|
legend: { |
||||
|
position: 'left' |
||||
|
}, |
||||
|
cutoutPercentage: 40, |
||||
|
responsive: true, |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
/** |
||||
|
function for getting values to employee graph |
||||
|
*/ |
||||
|
async render_top_employees_graph() { |
||||
|
var ctx = this.top_selling_employees |
||||
|
var arrays = await rpc('/employee/timesheet') |
||||
|
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.el, { |
||||
|
type: 'bar', |
||||
|
data: data, |
||||
|
options: options |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
/** |
||||
|
* Function for getting employees for filter. |
||||
|
*/ |
||||
|
async render_filter() { |
||||
|
var data = await rpc('/project/filter') |
||||
|
this.state.projects = data[0] |
||||
|
this.state.employees = data[1] |
||||
|
} |
||||
|
/** |
||||
|
* Event handler to apply filters based on user selections and update the dashboard data accordingly. |
||||
|
*/ |
||||
|
async _onchangeFilter(ev) { |
||||
|
this.flag = 1 |
||||
|
var start_date = this.start_date.el.value; |
||||
|
var end_date = this.end_date.el.value; |
||||
|
var employee_selection = this.employee_selection.el.value; |
||||
|
var project_selection = this.project_selection.el.value; |
||||
|
if (!start_date) { |
||||
|
start_date = "null" |
||||
|
} |
||||
|
if (!end_date) { |
||||
|
end_date = "null" |
||||
|
} |
||||
|
if (!employee_selection) { |
||||
|
employee_selection = "null" |
||||
|
} |
||||
|
if (!project_selection) { |
||||
|
project_selection = "null" |
||||
|
} |
||||
|
var data = await rpc('/project/filter-apply', { |
||||
|
'data': { |
||||
|
'start_date': start_date, |
||||
|
'end_date': end_date, |
||||
|
'project': project_selection, |
||||
|
'employee': employee_selection |
||||
|
} |
||||
|
}) |
||||
|
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'] |
||||
|
this.tot_project.el.innerHTML = data['total_project'].length |
||||
|
this.tot_employee.el.innerHTML = data['total_emp'].length |
||||
|
this.total_task.el.innerHTML = data['total_task'].length |
||||
|
this.tot_hrs.el.innerHTML = data['hours_recorded'] |
||||
|
this.tot_margin.el.innerHTML = data['total_margin'] |
||||
|
this.total_so.el.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 = rpc('/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 = rpc('/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 = rpc('/get/task/data') |
||||
|
.then(function(res) { |
||||
|
self.task_data = res['project']; |
||||
|
}); |
||||
|
return Promise.all([def1, def3, def4]) |
||||
|
.then(() => { |
||||
|
console.log('All data has been fetched successfully.'); |
||||
|
}) |
||||
|
.catch((error) => { |
||||
|
console.error('An error occurred while fetching data:', error); |
||||
|
}); |
||||
|
} |
||||
|
/** |
||||
|
* 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,419 @@ |
|||||
|
<?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"> |
||||
|
<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-ref="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-ref="end_date" |
||||
|
t-on-change="(ev) => this._onchangeFilter(ev)"/> |
||||
|
</p> |
||||
|
<p>Project :</p> |
||||
|
<p> |
||||
|
<select class="inner_select" id="project_selection" |
||||
|
t-ref="project_selection" |
||||
|
t-on-change="(ev) => this._onchangeFilter(ev)"> |
||||
|
<option value="null">All Projects</option> |
||||
|
<option t-foreach="state.projects" t-as="project" |
||||
|
t-key="project.id" |
||||
|
t-att-value="project.id"> |
||||
|
<t t-esc="project.name"/> |
||||
|
</option> |
||||
|
</select> |
||||
|
</p> |
||||
|
<p>Employees :</p> |
||||
|
<p> |
||||
|
<select class="inner_select" id="employee_selection" |
||||
|
t-ref="employee_selection" |
||||
|
t-on-change="(ev) => this._onchangeFilter(ev)"> |
||||
|
<option value="null">All Employees</option> |
||||
|
<option t-foreach="state.employees" t-as="employee" |
||||
|
t-key="employee.id" |
||||
|
t-att-value="employee.id"> |
||||
|
<t t-esc="employee.name"/> |
||||
|
</option> |
||||
|
</select> |
||||
|
</p> |
||||
|
<button class="btn btn-danger g-col-6 p-2" |
||||
|
onclick="location.reload()" |
||||
|
style="margin-left:18px; height: 37px;"> |
||||
|
Reset |
||||
|
</button> |
||||
|
</div> |
||||
|
<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-ref="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-ref="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-ref="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-ref="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-ref="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_sale(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-ref="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" |
||||
|
t-ref="project_doughnut" |
||||
|
style="background:#fff;" width="200" |
||||
|
height="115"/> |
||||
|
</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" |
||||
|
t-ref="top_selling_employees" |
||||
|
style="background:#fff;" width="200" |
||||
|
height="108"/> |
||||
|
</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[1]"> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<t t-if="flag_user == 1"> |
||||
|
<t t-esc="proj[2]['en_US']"/> |
||||
|
</t> |
||||
|
<t t-else=""> |
||||
|
<t t-esc="proj[2]"/> |
||||
|
</t> |
||||
|
</td> |
||||
|
<td> |
||||
|
<t t-esc="proj[0]"/> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</t> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<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 class="stage_wise_total"> |
||||
|
<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="data['name']"> |
||||
|
<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> |
||||
|
</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> |