diff --git a/crm_dashboard/README.rst b/crm_dashboard/README.rst new file mode 100755 index 000000000..7d62a334a --- /dev/null +++ b/crm_dashboard/README.rst @@ -0,0 +1,45 @@ +.. 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 + +CRM Dashboard +================== +* Visual report of CRM through Dashboard. + +Configuration +============= +* No additional configurations needed + +Company +------- +* `Cybrosys Techno Solutions `__ + +License +------- +Affero General Public License, Version 3 (AGPL v3). +(https://www.gnu.org/licenses/agpl-3.0-standalone.html) + +Credits +------- +* Developer: (V17) Mruthul Raj, Contact:odoo@cybrosys.com + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : https://cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com + +This module is maintained by Cybrosys Technologies. +For support and more information, please visit `Our Website `__ + +Further information +=================== +HTML Description: ``__ diff --git a/crm_dashboard/__init__.py b/crm_dashboard/__init__.py new file mode 100644 index 000000000..2fd8b0743 --- /dev/null +++ b/crm_dashboard/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Mruthul Raj (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 . +# +################################################################################ +from . import models diff --git a/crm_dashboard/__manifest__.py b/crm_dashboard/__manifest__.py new file mode 100644 index 000000000..6ad0f17d3 --- /dev/null +++ b/crm_dashboard/__manifest__.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Mruthul Raj (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 . +# +################################################################################ +{ + 'name': "CRM Dashboard", + 'version': '17.0.1.0.0', + 'category': 'Extra Tools', + 'summary': """Get a visual report of CRM through a Dashboard in CRM """, + 'description': """CRM dashboard module brings a multipurpose graphical + dashboard for CRM module and making the relationship management + better and easier""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'depends': ['crm', 'sale_management'], + 'data': ['views/crm_team_views.xml', + 'views/res_users_views.xml', + 'views/utm_campaign_views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'crm_dashboard/static/src/css/dashboard.css', + 'crm_dashboard/static/src/css/style.scss', + 'crm_dashboard/static/src/css/material-gauge.css', + 'crm_dashboard/static/src/js/crm_dashboard.js', + 'crm_dashboard/static/src/js/lib/highcharts.js', + 'crm_dashboard/static/src/js/lib/Chart.bundle.js', + 'crm_dashboard/static/src/js/lib/funnel.js', + 'crm_dashboard/static/src/js/lib/d3.min.js', + 'crm_dashboard/static/src/js/lib/material-gauge.js', + 'crm_dashboard/static/src/js/lib/columnHeatmap.min.js', + 'crm_dashboard/static/src/js/lib/columnHeatmap.js', + 'crm_dashboard/static/src/xml/dashboard_templates.xml', + ], + }, + 'images': ['static/description/banner.jpg'], + 'license': 'AGPL-3', + 'installable': True, + 'application': False, + 'auto_install': False, +} diff --git a/crm_dashboard/doc/RELEASE_NOTES.md b/crm_dashboard/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..9ad6d8980 --- /dev/null +++ b/crm_dashboard/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 28.12.2023 +#### Version 17.0.1.0.0 +#### ADD +- Initial commit for CRM Dashboard diff --git a/crm_dashboard/models/__init__.py b/crm_dashboard/models/__init__.py new file mode 100644 index 000000000..cea389218 --- /dev/null +++ b/crm_dashboard/models/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Mruthul Raj (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 . +# +################################################################################ +from . import crm_lead +from . import crm_team +from . import res_user +from . import sale_order +from . import utm_campaign diff --git a/crm_dashboard/models/crm_lead.py b/crm_dashboard/models/crm_lead.py new file mode 100644 index 000000000..8b9d6c1de --- /dev/null +++ b/crm_dashboard/models/crm_lead.py @@ -0,0 +1,1181 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Mruthul Raj (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 . +# +################################################################################ +import calendar +from datetime import datetime + +from dateutil.relativedelta import relativedelta +from odoo import api, fields, models +from odoo.http import request +from odoo.tools import date_utils + + +class CRMLead(models.Model): + """Extends crm.lead for adding more functions in it""" + _inherit = 'crm.lead' + + monthly_goal = fields.Float(string="Monthly Goal", help="Lead monthly goal") + achievement_amount = fields.Float(string="Monthly Achievement", + help="Achievement for the month") + + @api.model + def _get_currency(self): + """Get the currency symbol and position for the current user's company. + Returns: + list: A list containing the currency symbol and its position.""" + currency_array = [self.env.user.company_id.currency_id.symbol, + self.env.user.company_id.currency_id.position] + return currency_array + + @api.model + def check_user_group(self, kwargs): + """Checking a user group""" + user = self.env.user + if user.has_group('sales_team.group_sale_manager'): + return True + else: + return False + + @api.model + def get_lead_stage_data(self, kwargs): + """funnel chart""" + stage_ids = self.env["crm.stage"].search([]) + crm_list = [] + for stage in stage_ids: + leads = self.search_count([("stage_id", "=", stage.id)]) + crm_list.append((stage.name, int(leads))) + return crm_list + + @api.model + def get_lead_month_pie(self, kwargs): + """pie chart""" + month_count = [] + month_value = [] + leads = self.env['crm.lead'].search([]) + for rec in leads: + month = rec.create_date.month + if month not in month_value: + month_value.append(month) + month_count.append(month) + month_val = [{'label': calendar.month_name[month], + 'value': month_count.count(month)} for month in + month_value] + names = [record['label'] for record in month_val] + counts = [record['value'] for record in month_val] + month = [counts, names] + return month + + @api.model + def get_the_sales_activity(self, kwargs): + """Sales Activity Pie""" + self._cr.execute('''SELECT mail_activity_type.name, COUNT(*) FROM + mail_activity + INNER JOIN mail_activity_type ON + mail_activity.activity_type_id = mail_activity_type.id + WHERE mail_activity.res_model = 'crm.lead' + GROUP BY mail_activity_type.name''') + data = self._cr.dictfetchall() + names = [record['name']['en_US'] for record in data] + counts = [record['count'] for record in data] + return [counts, names] + + @api.model + def get_the_annual_target(self, kwargs): + """Annual Target: Year To Date Graph""" + session_user_id = self.env.uid + self._cr.execute('''SELECT res_users.id, res_users.sales, res_users.sale_team_id, + (SELECT crm_team.invoiced_target FROM crm_team + WHERE crm_team.id = res_users.sale_team_id) as invoiced_target + FROM res_users WHERE res_users.sales IS NOT NULL + AND res_users.id=%s AND res_users.sale_team_id IS NOT NULL;''' % session_user_id) + data2 = self._cr.dictfetchall() + sales = [rec['sales'] for rec in data2] + inv_target = [ + rec['invoiced_target'] if rec['invoiced_target'] is not None else 0 + for rec in data2] + team_id = data2[-1]['sale_team_id'] if data2 else 0 + target_annual = sum(sales) + sum(inv_target) + if self.env.user.has_group('sales_team.group_sale_manager'): + self._cr.execute('''SELECT res_users.id,res_users.sales, + res_users.sale_team_id, (SELECT crm_team.invoiced_target FROM + crm_team WHERE crm_team.id = res_users.sale_team_id) FROM res_users + WHERE res_users.id = %s AND res_users.sales is not null;''' + % session_user_id) + data3 = self._cr.dictfetchall() + sales = [] + inv_target = [] + for rec in data3: + sales.append(rec['sales']) + inv_target.append(rec['invoiced_target']) + if inv_target == [None]: + inv_target = [0] + ytd_target = (sum(sales) + sum(inv_target)) + self._cr.execute('''select sum(expected_revenue) from crm_lead + where stage_id=4 and team_id=%s AND Extract(Year FROM date_closed)= + Extract(Year FROM DATE(NOW()))''' % team_id) + achieved_won_data = self._cr.dictfetchall() + achieved_won = [item['sum'] for item in achieved_won_data] + else: + self._cr.execute( + '''SELECT res_users.id,res_users.sales FROM res_users WHERE + res_users.id = %s AND res_users.sales is not null;''' % + session_user_id) + data4 = self._cr.dictfetchall() + sales = [] + for rec in data4: + sales.append(rec['sales']) + ytd_target = (sum(sales)) + self._cr.execute('''select sum(expected_revenue) from crm_lead + where stage_id=4 and user_id=%s AND + Extract(Year FROM date_closed)=Extract(Year FROM DATE(NOW()))''' + % session_user_id) + achieved_won_data = self._cr.dictfetchall() + achieved_won = [item['sum'] for item in achieved_won_data] + won = achieved_won[0] + if won is None: + won = 0 + value = [target_annual, ytd_target, won] + name = ["Annual Target", "YtD target", "Won"] + final = [value, name] + return final + + @api.model + def get_the_campaign_pie(self, kwargs): + """Leads Group By Campaign Pie""" + self._cr.execute('''SELECT campaign_id, COUNT(*), + (SELECT name FROM utm_campaign + WHERE utm_campaign.id = crm_lead.campaign_id) + FROM crm_lead WHERE campaign_id IS NOT NULL GROUP BY + campaign_id''') + data = self._cr.dictfetchall() + names = [record.get('name') for record in data] + counts = [record.get('count') for record in data] + final = [counts, names] + return final + + @api.model + def get_the_source_pie(self, kwargs): + """Leads Group By Source Pie""" + self._cr.execute('''SELECT source_id, COUNT(*), + (SELECT name FROM utm_source + WHERE utm_source.id = crm_lead.source_id) + FROM crm_lead WHERE source_id IS NOT NULL GROUP BY + source_id''') + data = self._cr.dictfetchall() + names = [record.get('name') for record in data] + counts = [record.get('count') for record in data] + final = [counts, names] + return final + + @api.model + def get_the_medium_pie(self, kwargs): + """Leads Group By Medium Pie""" + self._cr.execute('''SELECT medium_id, COUNT(*), + (SELECT name FROM utm_medium + WHERE utm_medium.id = crm_lead.medium_id) + FROM crm_lead WHERE medium_id IS NOT NULL GROUP BY medium_id''') + data = self._cr.dictfetchall() + names = [record.get('name') for record in data] + counts = [record.get('count') for record in data] + final = [counts, names] + return final + + @api.model + def revenue_count_pie(self, kwargs): + """Total expected revenue and count Pie""" + session_user_id = self.env.uid + + def fetch_total_revenue(query): + self._cr.execute(query) + total_rev_data = self._cr.dictfetchall() + total_rev = total_rev_data[0]['revenue'] if total_rev_data and \ + total_rev_data[0][ + 'revenue'] else 0 + return total_rev + + queries = [ + f"SELECT sum(expected_revenue) as revenue FROM crm_lead WHERE user_id={session_user_id} AND type='opportunity' AND active='true'", + f"SELECT sum(expected_revenue) as revenue FROM crm_lead WHERE user_id={session_user_id} AND type='opportunity' AND active='false' AND stage_id='4'", + f"SELECT sum(expected_revenue) as revenue FROM crm_lead WHERE user_id={session_user_id} AND type='opportunity' AND active='false' AND probability='0' AND active='false'" + ] + total_expected_revenue, total_won_rev, total_lost_rev = [ + fetch_total_revenue(query) for query in queries] + exp_revenue_without_won = total_expected_revenue - total_won_rev + revenue_pie_count = [exp_revenue_without_won, total_won_rev, + total_lost_rev] + revenue_pie_title = ['Expected without Won', 'Won', 'Lost'] + revenue_data = [revenue_pie_count, revenue_pie_title] + return revenue_data + + @api.model + def get_upcoming_events(self, kwargs): + """Upcoming Activities Table""" + today = fields.date.today() + session_user_id = self.env.uid + self._cr.execute('''select mail_activity.activity_type_id, + mail_activity.date_deadline, mail_activity.summary, + mail_activity.res_name,(SELECT mail_activity_type.name + FROM mail_activity_type WHERE mail_activity_type.id = + mail_activity.activity_type_id), mail_activity.user_id FROM + mail_activity WHERE res_model = 'crm.lead' AND + mail_activity.date_deadline >= '%s' and user_id = %s GROUP BY + mail_activity.activity_type_id, mail_activity.date_deadline, + mail_activity.summary,mail_activity.res_name,mail_activity.user_id + order by mail_activity.date_deadline asc''' % (today, session_user_id)) + data = self._cr.fetchall() + events = [[record[0], record[1], record[2], record[3], + record[4] if record[4] else '', + self.env['res.users'].browse(record[5]).name if record[ + 5] else '' + ] for record in data] + return { + 'event': events, + 'cur_lang': self.env.context.get('lang') + } + + @api.model + def get_top_deals(self, kwargs): + """Top 10 Deals Table""" + self._cr.execute('''SELECT crm_lead.user_id,crm_lead.id, + crm_lead.expected_revenue, crm_lead.name,crm_lead.company_id, + (SELECT crm_team.name FROM crm_team + WHERE crm_lead.team_id = crm_team.id) from crm_lead where + crm_lead.expected_revenue is not null and crm_lead.type = 'opportunity' + GROUP BY crm_lead.user_id, + crm_lead.id,crm_lead.expected_revenue,crm_lead.name,crm_lead.company_id + order by crm_lead.expected_revenue DESC limit 10''') + data1 = self._cr.fetchall() + deals = [[self.env['res.users'].browse(rec[0]).name, + rec[1], rec[2], rec[3], + self.env['res.company'].browse(rec[4]).currency_id.symbol, + rec[5], index + 1] for index, rec in enumerate(data1)] + return {'deals': deals} + + @api.model + def get_monthly_goal(self, kwargs): + """Monthly Goal Gauge""" + uid = request.session.uid + leads = self.env['crm.lead'].search([ + ('date_deadline', '!=', False), ('user_id', '=', uid), + ('type', '=', 'opportunity')]) + leads_won = self.env['crm.lead'].search([ + ('date_closed', '!=', False), ('stage_id', '=', 4), + ('user_id', '=', uid), ('type', '=', 'opportunity')]) + currency_symbol = self.env.company.currency_id.symbol + achievement = sum(won.expected_revenue for won in leads_won.filtered( + lambda a: a.date_closed.month == fields.date.today().month and + a.date_closed.year == fields.date.today().year)) + total = sum(rec.expected_revenue for rec in leads.filtered( + lambda t: t.date_deadline.month == fields.date.today().month and + t.date_deadline.year == fields.date.today().year)) + self.monthly_goal = total + self.achievement_amount = achievement + percent = (achievement * 100 / total) / 100 if total > 0 else 0 + goals = [achievement, total, currency_symbol, percent] + return {'goals': goals} + + @api.model + def get_top_sp_revenue(self, kwargs): + """Top 10 Salesperson revenue Table""" + user = self.env.user + self._cr.execute('''SELECT user_id, id, expected_revenue, name, company_id + FROM crm_lead + WHERE expected_revenue IS NOT NULL AND user_id = %s + GROUP BY user_id, id + ORDER BY expected_revenue DESC + LIMIT 10''' % user.id) + data1 = self._cr.fetchall() + top_revenue = [ + [self.env['res.users'].browse(rec[0]).name, rec[1], rec[2], + rec[3], self.env['res.company'].browse(rec[4]).currency_id.symbol] + for rec in data1] + return {'top_revenue': top_revenue} + + @api.model + def get_country_revenue(self, kwargs): + """Top 10 Country Wise Revenue - Heat Map""" + company_id = self.env.company.id + self._cr.execute('''SELECT country_id, sum(expected_revenue) + FROM crm_lead + WHERE expected_revenue IS NOT NULL + AND country_id IS NOT NULL + GROUP BY country_id + ORDER BY sum(expected_revenue) DESC + LIMIT 10''') + data1 = self._cr.fetchall() + country_revenue = [[self.env['res.country'].browse(rec[0]).name, + rec[1], self.env['res.company'].browse( + company_id).currency_id.symbol] for rec in data1] + return {'country_revenue': country_revenue} + + @api.model + def get_country_count(self, kwargs): + """Top 10 Country Wise Count - Heat Map""" + self._cr.execute('''SELECT country_id, COUNT(*) + FROM crm_lead + WHERE country_id IS NOT NULL + GROUP BY country_id + ORDER BY COUNT(*) DESC + LIMIT 10''') + data1 = self._cr.fetchall() + country_count = [[self.env['res.country'].browse(rec[0]).name, rec[1]] + for rec in data1] + return {'country_count': country_count} + + @api.model + def get_total_lost_crm(self, option): + """Lost Opportunity or Lead Graph""" + month_dict = {} + for i in range(int(option) - 1, -1, -1): + last_month = fields.Datetime.now() - relativedelta(months=i) + text = format(last_month, '%B') + month_dict[text] = 0 + if option == '1': + day_dict = {} + last_day = date_utils.end_of(fields.Date.today(), + "month").strftime("%d") + for i in range(1, int(last_day), 1): + day_dict[i] = 0 + self._cr.execute('''select create_date::date,count(id) from crm_lead + where probability=0 and active=false and create_date between + (now() - interval '1 month') and now() + group by create_date order by create_date;''') + data = self._cr.dictfetchall() + for rec in data: + day_dict[int(rec['create_date'].strftime("%d"))] = rec['count'] + + test = {'month': list(day_dict.keys()), + 'count': list(day_dict.values())} + else: + month_string = str(int(option)) + ' Months' + self._cr.execute('''select extract(month from create_date),count(id) + from crm_lead where probability=0 and active=false and + create_date between (now() - interval '%s') and now() + group by extract(month from create_date) order by extract( + month from create_date);''' % month_string) + data = self._cr.dictfetchall() + for rec in data: + datetime_object = datetime.strptime( + str(int(rec['date_part'])), "%m") + month_name = datetime_object.strftime("%B") + month_dict[month_name] = rec['count'] + test = {'month': list(month_dict.keys()), + 'count': list(month_dict.values())} + return test + + @api.model + def get_ratio_based_country(self, kwargs): + """Top 5 Won vs Lost Ratio based on Country""" + self._cr.execute('''SELECT (SELECT name FROM res_country WHERE id=country_id), + COUNT(country_id)FROM crm_lead + WHERE probability=100 AND active=true + GROUP BY country_id + ORDER BY COUNT(country_id) DESC''') + data_won = self._cr.fetchall() + self._cr.execute('''SELECT (SELECT name FROM res_country WHERE id=country_id), + COUNT(country_id) + FROM crm_lead + WHERE probability=0 AND active=false + GROUP BY country_id + ORDER BY COUNT(country_id) DESC''') + data_lost = self._cr.fetchall() + country_wise_ratio = [[won[0], won[1], str(round(won[1] / next( + (lost[1] for lost in data_lost if lost[0] == won[0]), 1), + 2))] for won in + data_won[:5]] + return {'country_wise_ratio': country_wise_ratio} + + @api.model + def get_ratio_based_sp(self, kwargs): + """Top 5 Won vs Lost Ratio based on Sales Person""" + self._cr.execute('''select user_id,count(user_id) from crm_lead where + probability=100 and active=true group by user_id order by + count(user_id) desc''') + data_won = self._cr.fetchall() + self._cr.execute('''select user_id,count(user_id) from crm_lead where + probability=0 and active=false group by user_id order by + count(user_id) desc''') + data_lost = self._cr.fetchall() + won = [[user_id_obj.name, rec[1]] for rec in data_won for user_id_obj in + self.env['res.users'].browse(rec[0])] + for won_list in won: + won_list.append(next((lose[1] for lose in data_lost if + self.env['res.users'].browse( + data_won[won.index(won_list)][0]).name == + self.env['res.users'].browse(lose[0]).name), + 0)) + salesperson_wise_ratio = [[data[0], data[1], data[2], + round(data[1] / data[2], 2) if data[ + 2] != 0 else 0] + for data in won if len(data) == 2] + salesperson_wise_ratio = sorted(salesperson_wise_ratio, + key=lambda x: x[3], reverse=True)[:5] + return {'salesperson_wise_ratio': salesperson_wise_ratio} + + @api.model + def get_ratio_based_sales_team(self, kwargs): + """Top 5 Won vs Lost Ratio based on Sales Team""" + self._cr.execute('''select (SELECT name FROM crm_team WHERE crm_team.id + = team_id), count(user_id) from crm_lead where probability=100 and + active=true group by team_id order by count(team_id) desc''') + data_won = self._cr.fetchall() + self._cr.execute('''select (SELECT name FROM crm_team WHERE crm_team.id + = team_id), count(user_id) from crm_lead where probability=0 and + active=false group by team_id order by count(team_id) desc''') + data_lost = self._cr.fetchall() + won = [[rec[0], rec[1]] for rec in data_won] + for won_list in won: + won_list.append( + next((lose[1] for lose in data_lost if lose[0] == won_list[0]), + 0)) + sales_team_wise_ratio = [[data[0], data[1], data[2], + round(data[1] / data[2], 2) if data[ + 2] != 0 else 0] + for data in won if len(data) == 3] + sales_team_wise_ratio = sorted(sales_team_wise_ratio, + key=lambda x: x[3], reverse=True)[:5] + return {'sales_team_wise_ratio': sales_team_wise_ratio} + + @api.model + def get_lost_lead_by_reason_pie(self, kwargs): + """Lost Leads by Lost Reason Pie""" + self._cr.execute('''select lost_reason_id, count(*), (SELECT name FROM + crm_lost_reason WHERE id = lost_reason_id) from crm_lead where + probability=0 and active=false and type='lead' group by lost_reason_id''') + data1 = self._cr.dictfetchall() + name = [rec["name"] if rec["name"] is not None else "Undefined" for rec + in data1] + count = [rec["count"] for rec in data1] + lost_leads = [count, name] + return lost_leads + + @api.model + def get_lost_lead_by_stage_pie(self, kwargs): + """Lost Leads by Stage Pie""" + self._cr.execute('''select stage_id, count(*),(SELECT name FROM + crm_stage WHERE id = stage_id) from crm_lead where probability=0 and + active=false and type='lead' group by stage_id''') + data1 = self._cr.dictfetchall() + name = [rec["name"] for rec in data1] + count = [rec["count"] for rec in data1] + lost_leads_stage = [count, name] + return lost_leads_stage + + @api.model + def get_recent_activities(self, kwargs): + """Recent Activities Table""" + today = fields.date.today() + recent_week = today - relativedelta(days=7) + self._cr.execute('''select mail_activity.activity_type_id, + mail_activity.date_deadline, mail_activity.summary, + mail_activity.res_name,(SELECT mail_activity_type.name + FROM mail_activity_type WHERE mail_activity_type.id = + mail_activity.activity_type_id), mail_activity.user_id FROM + mail_activity WHERE res_model = 'crm.lead' AND + mail_activity.date_deadline between '%s' and '%s' GROUP BY + mail_activity.activity_type_id, mail_activity.date_deadline, + mail_activity.summary,mail_activity.res_name,mail_activity.user_id + order by mail_activity.date_deadline desc''' % (recent_week, today)) + data = self._cr.fetchall() + activities = [[*record[:5], self.env['res.users'].browse(record[5]).name + ] for record in data] + return {'activities': activities} + + @api.model + def get_count_unassigned(self, kwargs): + """Unassigned Leads Count Card""" + count_unassigned = self.env['crm.lead'].search_count( + [('user_id', '=', False), ('type', '=', 'lead')]) + return {'count_unassigned': count_unassigned} + + @api.model + @api.model + def get_top_sp_by_invoice(self, kwargs): + """Top 10 Sales Person by Invoice Table""" + self._cr.execute('''select user_id,sum(amount_total) as total + from sale_order where invoice_status='invoiced' + group by user_id order by total desc limit 10''') + data1 = self._cr.fetchall() + sales_person_invoice = [[ + self.env['res.users'].browse(rec[0]).name, rec[1], + self.env['res.users'].browse(rec[0]).company_id.currency_id.symbol, + idx + 1, ] for idx, rec in enumerate(data1)] + return {'sales_person_invoice': sales_person_invoice} + + @api.model + def lead_details_user(self, kwargs): + """Cards Count and Detail based on User""" + session_user_id = self.env.uid + month_count = [] + month_value = [] + leads = self.env['crm.lead'].search([]) + for rec in leads: + month = rec.create_date.month + if month not in month_value: + month_value.append(month) + month_count.append(month) + value = [] + for index in range(len(month_value)): + value = month_count.count(month_value[index]) + self._cr.execute('''SELECT res_users.id,res_users.sales, + res_users.sale_team_id,(SELECT crm_team.invoiced_target FROM crm_team + WHERE crm_team.id = res_users.sale_team_id) + FROM res_users WHERE res_users.sales is not null and res_users.id=%s + AND res_users.sale_team_id is not null;''' % session_user_id) + data2 = self._cr.dictfetchall() + sales = [] + inv_target = [] + team_id = 0 + for rec in data2: + sales.append(rec['sales']) + inv_target.append(rec['invoiced_target']) + if inv_target == [None]: + inv_target = [0] + team_id = rec['sale_team_id'] + target_annual = (sum(sales) + sum(inv_target)) + if self.env.user.has_group('sales_team.group_sale_manager'): + self._cr.execute('''SELECT res_users.id,res_users.sales, + res_users.sale_team_id,(SELECT crm_team.invoiced_target FROM + crm_team WHERE crm_team.id = res_users.sale_team_id) FROM + res_users WHERE res_users.id = %s AND res_users.sales is not + null;''' % session_user_id) + data3 = self._cr.dictfetchall() + sales = [] + inv_target = [] + for rec in data3: + sales.append(rec['sales']) + inv_target.append(rec['invoiced_target']) + if inv_target == [None]: + inv_target = [0] + ytd_target = (sum(sales) + sum(inv_target)) + self._cr.execute('''select sum(expected_revenue) from crm_lead where + stage_id=4 and team_id=%s AND Extract(Year FROM date_closed)= + Extract(Year FROM DATE(NOW()))''' % team_id) + achieved_won_data = self._cr.dictfetchall() + achieved_won = [item['sum'] for item in achieved_won_data] + else: + self._cr.execute('''SELECT res_users.id,res_users.sales FROM + res_users WHERE res_users.id = %s AND res_users.sales is not null; + ''' % session_user_id) + data4 = self._cr.dictfetchall() + sales = [] + for rec in data4: + sales.append(rec['sales']) + ytd_target = (sum(sales)) + self._cr.execute('''select sum(expected_revenue) from crm_lead where + stage_id=4 and user_id=%s AND Extract(Year FROM date_closed)= + Extract(Year FROM DATE(NOW()))''' % session_user_id) + achieved_won_data = self._cr.dictfetchall() + achieved_won = [item['sum'] for item in achieved_won_data] + won = achieved_won[0] + if won is None: + won = 0 + difference = target_annual - won + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = '%s' AND Extract(MONTH FROM crm_lead.date_deadline) + = Extract( MONTH FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW())) + ''' % session_user_id) + record = self._cr.dictfetchall() + rec_ids = [item['count'] for item in record] + crm_lead_value = rec_ids[0] + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = %s AND crm_lead.type = 'opportunity' AND + Extract(MONTH FROM crm_lead.date_deadline) = Extract(MONTH FROM + DATE(NOW())) AND Extract(Year FROM crm_lead.date_deadline + ) = Extract(Year FROM DATE(NOW( )))''' % session_user_id) + opportunity_data = self._cr.dictfetchall() + opportunity_data_value = [item['count'] for item in opportunity_data] + opportunity_value = opportunity_data_value[0] + self._cr.execute('''select SUM(crm_lead.expected_revenue) from crm_lead + WHERE crm_lead.user_id = %s and type='opportunity' and active='true' + AND Extract(MONTH FROM crm_lead.date_deadline) = + Extract(MONTH FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW()))''' + % session_user_id) + exp_revenue_data = self._cr.dictfetchall() + exp_revenue_data_value = [item['sum'] for item in exp_revenue_data] + exp_revenue_value = exp_revenue_data_value[0] + if exp_revenue_value is None: + exp_revenue_value = 0 + self._cr.execute('''select SUM(crm_lead.expected_revenue) from crm_lead + WHERE crm_lead.user_id = %s and type='opportunity' and active='true' + and stage_id=4 AND Extract(MONTH FROM crm_lead.date_closed) = + Extract(MONTH FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_closed) = Extract(Year FROM DATE(NOW())) + ''' % session_user_id) + revenue_data = self._cr.dictfetchall() + revenue_data_value = [item['sum'] for item in revenue_data] + revenue_value = revenue_data_value[0] + if revenue_value is None: + revenue_value = 0 + ratio_value = [] + if revenue_value == 0: + ratio_value = 0 + if revenue_value > 0: + self._cr.execute('''select case when b.count_two = 0 then 0 else ( + CAST(a.count_one as float) / CAST(b.count_two as float))end as + final_count from (select COUNT(id) as count_one from crm_lead WHERE + crm_lead.user_id = '%s' AND crm_lead.active = True AND + crm_lead.probability = 100 AND Extract(MONTH FROM + crm_lead.date_deadline) = Extract(MONTH FROM DATE(NOW())) + AND Extract(Year FROM crm_lead.date_open) = Extract(Year + FROM DATE(NOW())))a,(select COUNT(id) as count_two from crm_lead + WHERE crm_lead.user_id = '%s' AND crm_lead.active = False AND + crm_lead.probability = 0 AND Extract(MONTH FROM + crm_lead.date_deadline) = Extract(MONTH FROM DATE(NOW())) AND + Extract(Year FROM crm_lead.date_deadline) = Extract(Year FROM + DATE(NOW())))b''' % (session_user_id, session_user_id)) + ratio_data_value = [row[0] for row in self._cr.fetchall()] + ratio_value = str(ratio_data_value)[1:-1] + self._cr.execute('''SELECT active,count(active) FROM crm_lead where + type='opportunity' and active = true and probability = 100 and + user_id=%s AND Extract(MONTH FROM date_closed) = Extract(MONTH FROM + DATE(NOW())) AND Extract(Year FROM date_closed) = Extract( + Year FROM DATE(NOW())) or type='opportunity' and active = false and + probability = 0 and user_id=%s AND Extract(MONTH FROM date_deadline) = + Extract(MONTH FROM DATE(NOW())) AND Extract(Year FROM date_deadline) = + Extract(Year FROM DATE(NOW())) GROUP BY active + ''' % (session_user_id, session_user_id)) + record_opportunity = dict(self._cr.fetchall()) + opportunity_ratio_value = 0.0 + if record_opportunity == {}: + opportunity_ratio_value = 0.0 + else: + total_opportunity_won = record_opportunity.get(False) + total_opportunity_lost = record_opportunity.get(True) + if total_opportunity_won is None: + total_opportunity_won = 0 + if total_opportunity_lost is None: + total_opportunity_lost = 0 + opportunity_ratio_value = 0.0 + if total_opportunity_lost > 0: + opportunity_ratio_value = round( + total_opportunity_won / total_opportunity_lost, 2) + avg = 0 + if crm_lead_value == 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data = self._cr.dictfetchall() + for rec in data: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + if crm_lead_value > 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data1 = self._cr.dictfetchall() + for rec in data1: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + avg_time = 0 if crm_lead_value == 0 else round(avg / crm_lead_value) + data = { + 'record': crm_lead_value, + 'record_op': opportunity_value, + 'record_rev_exp': exp_revenue_value, + 'record_rev': revenue_value, + 'record_ratio': ratio_value, + 'opportunity_ratio_value': str(opportunity_ratio_value), + 'avg_time': avg_time, + 'count': value, + 'target': target_annual, + 'ytd_target': ytd_target, + 'difference': difference, + 'won': won, + } + return data + + @api.model + def crm_year(self, kwargs): + """Year CRM Dropdown Filter""" + session_user_id = self.env.uid + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = '%s' AND Extract(Year FROM crm_lead.date_deadline) = + Extract(Year FROM DATE(NOW()))''' % session_user_id) + record = self._cr.dictfetchall() + rec_ids = [item['count'] for item in record] + crm_lead_value = rec_ids[0] + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = %s AND crm_lead.type = 'opportunity' AND + Extract(Year FROM crm_lead.date_deadline + ) = Extract(Year FROM DATE(NOW()))''' % session_user_id) + opportunity_data = self._cr.dictfetchall() + opportunity_data_value = [item['count'] for item in opportunity_data] + opportunity_value = opportunity_data_value[0] + self._cr.execute('''select SUM(crm_lead.expected_revenue) from crm_lead + WHERE crm_lead.user_id = %s and type='opportunity' and active='true' AND + Extract(Year FROM crm_lead.date_deadline) = Extract(Year FROM + DATE(NOW()))''' % session_user_id) + exp_revenue_data = self._cr.dictfetchall() + exp_revenue_data_value = [item['sum'] for item in exp_revenue_data] + exp_revenue_value = exp_revenue_data_value[0] + if exp_revenue_value is None: + exp_revenue_value = 0 + self._cr.execute('''select SUM(crm_lead.expected_revenue) from crm_lead + WHERE crm_lead.user_id = %s and type='opportunity' and active='true' and + stage_id=4 AND Extract(Year FROM crm_lead.date_closed) = Extract(Year + FROM DATE(NOW()))''' % session_user_id) + revenue_data = self._cr.dictfetchall() + revenue_data_value = [item['sum'] for item in revenue_data] + revenue_value = revenue_data_value[0] + if revenue_value is None: + revenue_value = 0 + ratio_value = [] + if revenue_value == 0: + ratio_value = 0 + if revenue_value > 0: + self._cr.execute('''select case when b.count_two = 0 then 0 else ( + CAST(a.count_one as float) / CAST(b.count_two as float))end as + final_count from (select COUNT(id) as count_one from crm_lead + WHERE crm_lead.user_id = '%s' AND crm_lead.active = True AND + crm_lead.probability = 100 AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW())))a, + (select COUNT(id) as count_two from crm_lead WHERE crm_lead.user_id + = '%s' AND crm_lead.active = False AND crm_lead.probability + = 0 AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW())))b + ''' % (session_user_id, session_user_id)) + ratio_value = [row[0] for row in self._cr.fetchall()] + ratio_value = str(ratio_value)[1:-1] + self._cr.execute('''SELECT active,count(active) FROM crm_lead + where type='opportunity' and active = true and probability = 100 and + user_id=%s AND Extract(Year FROM date_closed) = Extract(Year + FROM DATE(NOW())) or type='opportunity' and active = false and + probability = 0 and user_id=%s + AND Extract(Year FROM date_deadline) = Extract(Year FROM DATE(NOW())) + GROUP BY active''' % (session_user_id, session_user_id)) + record_opportunity = dict(self._cr.fetchall()) + opportunity_ratio_value = 0.0 + if record_opportunity == {}: + opportunity_ratio_value = 0.0 + else: + total_opportunity_won = record_opportunity.get(False) + total_opportunity_lost = record_opportunity.get(True) + if total_opportunity_won is None: + total_opportunity_won = 0 + if total_opportunity_lost is None: + total_opportunity_lost = 0 + opportunity_ratio_value = 0.0 + if total_opportunity_lost > 0: + opportunity_ratio_value = round( + total_opportunity_won / total_opportunity_lost, 2) + avg = 0 + if crm_lead_value == 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data = self._cr.dictfetchall() + for rec in data: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + if crm_lead_value > 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data1 = self._cr.dictfetchall() + for rec in data1: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + if crm_lead_value == 0: + record_avg_time = 0 + else: + record_avg_time = round(avg / crm_lead_value) + data_year = { + 'record': crm_lead_value, + 'record_op': opportunity_value, + 'record_rev_exp': exp_revenue_value, + 'record_rev': revenue_value, + 'record_ratio': ratio_value, + 'opportunity_ratio_value': str(opportunity_ratio_value), + 'avg_time': record_avg_time, + } + return data_year + + @api.model + def crm_quarter(self, kwargs): + """Quarter CRM Dropdown Filter""" + session_user_id = self.env.uid + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = '%s' AND Extract(QUARTER FROM crm_lead.date_deadline) + = Extract(QUARTER FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW())) + ''' % session_user_id) + record = self._cr.dictfetchall() + rec_ids = [item['count'] for item in record] + crm_lead_value = rec_ids[0] + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = %s AND crm_lead.type = 'opportunity' AND + Extract(QUARTER FROM crm_lead.date_deadline) = Extract(QUARTER FROM + DATE(NOW())) AND Extract(Year FROM crm_lead.date_deadline + ) = Extract(Year FROM DATE(NOW( )))''' % session_user_id) + opportunity_data = self._cr.dictfetchall() + opportunity_data_value = [item['count'] for item in opportunity_data] + opportunity_value = opportunity_data_value[0] + self._cr.execute('''select SUM(crm_lead.expected_revenue) from crm_lead + WHERE crm_lead.user_id = %s and type='opportunity' and active='true' + AND Extract(QUARTER FROM crm_lead.date_deadline) = + Extract(QUARTER FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW()))''' + % session_user_id) + exp_revenue_data = self._cr.dictfetchall() + exp_revenue_data_value = [item['sum'] for item in exp_revenue_data] + exp_revenue_value = exp_revenue_data_value[0] + if exp_revenue_value is None: + exp_revenue_value = 0 + self._cr.execute('''select SUM(crm_lead.expected_revenue) from crm_lead + WHERE crm_lead.user_id = %s and type='opportunity' and active='true' + and stage_id=4 AND Extract(QUARTER FROM crm_lead.date_closed) = + Extract(QUARTER FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_closed) = Extract(Year FROM DATE(NOW()))''' + % session_user_id) + revenue_data = self._cr.dictfetchall() + revenue_data_value = [item['sum'] for item in revenue_data] + revenue_value = revenue_data_value[0] + if revenue_value is None: + revenue_value = 0 + ratio_value = [] + if revenue_value == 0: + ratio_value = 0 + if revenue_value > 0: + self._cr.execute('''select case when b.count_two = 0 then 0 else ( + CAST(a.count_one as float) / CAST(b.count_two as float))end as + final_count from (select COUNT(id) as count_one from crm_lead + WHERE crm_lead.user_id = '%s' AND crm_lead.active = True AND + crm_lead.probability = 100 AND Extract(QUARTER FROM + crm_lead.date_deadline) = Extract(QUARTER FROM DATE(NOW())) + AND Extract(Year FROM crm_lead.date_open) = Extract(Year + FROM DATE(NOW())))a,(select COUNT(id) as count_two from crm_lead + WHERE crm_lead.user_id = '%s' AND crm_lead.active = False AND + crm_lead.probability = 0 AND Extract(QUARTER FROM + crm_lead.date_deadline) = Extract(QUARTER FROM DATE(NOW())) + AND Extract(Year FROM crm_lead.date_deadline) = Extract(Year + FROM DATE(NOW())))b''' % (session_user_id, session_user_id)) + ratio_value = [row[0] for row in self._cr.fetchall()] + ratio_value = str(ratio_value)[1:-1] + self._cr.execute('''SELECT active,count(active) FROM crm_lead + where type='opportunity' and active = true and probability = 100 and + user_id=%s AND Extract(QUARTER FROM date_closed) = + Extract(QUARTER FROM DATE(NOW())) AND Extract(Year FROM date_closed) + = Extract(Year FROM DATE(NOW())) or type='opportunity' and active = + false and probability = 0 and user_id=%s AND Extract(QUARTER FROM + date_deadline) = Extract(QUARTER FROM DATE(NOW())) + AND Extract(Year FROM date_deadline) = Extract(Year FROM DATE(NOW())) + GROUP BY active''' % (session_user_id, session_user_id)) + record_opportunity = dict(self._cr.fetchall()) + opportunity_ratio_value = 0.0 + if record_opportunity == {}: + opportunity_ratio_value = 0.0 + else: + total_opportunity_won = record_opportunity.get(False) + total_opportunity_lost = record_opportunity.get(True) + if total_opportunity_won is None: + total_opportunity_won = 0 + if total_opportunity_lost is None: + total_opportunity_lost = 0 + opportunity_ratio_value = 0.0 + if total_opportunity_lost > 0: + opportunity_ratio_value = round( + total_opportunity_won / total_opportunity_lost, 2) + avg = 0 + if crm_lead_value == 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data = self._cr.dictfetchall() + for rec in data: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + if crm_lead_value > 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data1 = self._cr.dictfetchall() + for rec in data1: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + if crm_lead_value == 0: + record_avg_time = 0 + else: + record_avg_time = round(avg / crm_lead_value) + data_quarter = { + 'record': crm_lead_value, + 'record_op': opportunity_value, + 'record_rev_exp': exp_revenue_value, + 'record_rev': revenue_value, + 'record_ratio': ratio_value, + 'opportunity_ratio_value': str(opportunity_ratio_value), + 'avg_time': record_avg_time, + } + return data_quarter + + @api.model + def crm_month(self, kwargs): + """Month CRM Dropdown Filter""" + session_user_id = self.env.uid + + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = '%s' AND Extract(MONTH FROM crm_lead.date_deadline) + = Extract(MONTH FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW()))''' + % session_user_id) + record = self._cr.dictfetchall() + rec_ids = [item['count'] for item in record] + crm_lead_value = rec_ids[0] + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = %s AND crm_lead.type = 'opportunity' AND + Extract(MONTH FROM crm_lead.date_deadline) = + Extract(MONTH FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW( )))''' + % session_user_id) + opportunity_data = self._cr.dictfetchall() + opportunity_data_value = [item['count'] for item in opportunity_data] + opportunity_value = opportunity_data_value[0] + self._cr.execute('''select SUM(crm_lead.expected_revenue) from crm_lead + WHERE crm_lead.user_id = %s and type='opportunity' and active='true' + AND Extract(MONTH FROM crm_lead.date_deadline) = + Extract(MONTH FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW()))''' % + session_user_id) + exp_revenue_data = self._cr.dictfetchall() + exp_revenue_data_value = [item['sum'] for item in exp_revenue_data] + exp_revenue_value = exp_revenue_data_value[0] + if exp_revenue_value is None: + exp_revenue_value = 0 + self._cr.execute('''select SUM(crm_lead.expected_revenue) from + crm_lead WHERE crm_lead.user_id = %s and type='opportunity' and + active='true' and stage_id=4 AND Extract(MONTH FROM + crm_lead.date_closed) = Extract(MONTH FROM DATE(NOW())) + AND Extract(Year FROM crm_lead.date_closed) = Extract(Year + FROM DATE(NOW()))''' % session_user_id) + revenue_data = self._cr.dictfetchall() + revenue_data_value = [item['sum'] for item in revenue_data] + revenue_value = revenue_data_value[0] + if revenue_value is None: + revenue_value = 0 + ratio_value = [] + if revenue_value == 0: + ratio_value = 0 + if revenue_value > 0: + self._cr.execute('''select case when b.count_two = 0 then 0 else ( + CAST(a.count_one as float) / CAST(b.count_two as float))end as + final_count from (select COUNT(id) as count_one from crm_lead + WHERE crm_lead.user_id = '%s' AND crm_lead.active = True AND + crm_lead.probability = 100 AND Extract(MONTH FROM + crm_lead.date_deadline) = Extract(MONTH FROM DATE(NOW())) + AND Extract(Year FROM crm_lead.date_open) = + Extract(Year FROM DATE(NOW())))a,(select COUNT(id) as + count_two from crm_lead WHERE crm_lead.user_id = '%s' + AND crm_lead.active = False AND crm_lead.probability = 0 + AND Extract(MONTH FROM crm_lead.date_deadline) = + Extract(MONTH FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW())))b''' + % (session_user_id, session_user_id)) + ratio_value = [row[0] for row in self._cr.fetchall()] + ratio_value = str(ratio_value)[1:-1] + self._cr.execute('''SELECT active,count(active) FROM crm_lead + where type='opportunity' and active = true and probability = 100 and + user_id=%s AND Extract(MONTH FROM date_closed) = + Extract(MONTH FROM DATE(NOW())) AND Extract(Year FROM date_closed) + = Extract(Year FROM DATE(NOW())) or type='opportunity' + and active = false and probability = 0 and user_id=%s + AND Extract(MONTH FROM date_deadline) = Extract(MONTH FROM DATE(NOW())) + AND Extract(Year FROM date_deadline) = Extract(Year FROM DATE(NOW())) + GROUP BY active''' % (session_user_id, session_user_id)) + record_opportunity = dict(self._cr.fetchall()) + opportunity_ratio_value = 0.0 + if record_opportunity == {}: + opportunity_ratio_value = 0.0 + else: + total_opportunity_won = record_opportunity.get(False) + total_opportunity_lost = record_opportunity.get(True) + if total_opportunity_won is None: + total_opportunity_won = 0 + if total_opportunity_lost is None: + total_opportunity_lost = 0 + opportunity_ratio_value = 0.0 + if total_opportunity_lost > 0: + opportunity_ratio_value = round( + total_opportunity_won / total_opportunity_lost, 2) + avg = 0 + if crm_lead_value == 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data = self._cr.dictfetchall() + for rec in data: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + if crm_lead_value > 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data1 = self._cr.dictfetchall() + for rec in data1: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + record_avg_time = 0 if crm_lead_value == 0 else round( + avg / crm_lead_value) + data_month = { + 'record': crm_lead_value, + 'record_op': opportunity_value, + 'record_rev_exp': exp_revenue_value, + 'record_rev': revenue_value, + 'record_ratio': ratio_value, + 'opportunity_ratio_value': str(opportunity_ratio_value), + 'avg_time': record_avg_time, + } + return data_month + + @api.model + def crm_week(self, kwargs): + """Week CRM Dropdown Filter""" + session_user_id = self.env.uid + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = '%s' AND Extract(MONTH FROM + crm_lead.date_deadline) = Extract(MONTH FROM DATE(NOW())) + AND Extract(Week FROM crm_lead.date_deadline) = + Extract(Week FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW()))''' + % session_user_id) + record = self._cr.dictfetchall() + rec_ids = [item['count'] for item in record] + crm_lead_value = rec_ids[0] + self._cr.execute('''select COUNT(id) from crm_lead WHERE + crm_lead.user_id = %s AND crm_lead.type = 'opportunity' AND + Extract(MONTH FROM crm_lead.date_deadline) = Extract(MONTH + FROM DATE(NOW())) AND Extract(Week FROM crm_lead.date_deadline + ) = Extract(Week FROM DATE(NOW())) AND Extract(Year + FROM crm_lead.date_deadline) = Extract(Year FROM DATE(NOW()))''' + % session_user_id) + opportunity_data = self._cr.dictfetchall() + opportunity_data_value = [item['count'] for item in opportunity_data] + opportunity_value = opportunity_data_value[0] + self._cr.execute('''select SUM(crm_lead.expected_revenue) from crm_lead + WHERE crm_lead.user_id = %s and type='opportunity' + and active='true' AND Extract(MONTH FROM crm_lead.date_deadline) + = Extract(MONTH FROM DATE(NOW())) AND Extract(Week FROM + crm_lead.date_deadline) = Extract(Week FROM DATE(NOW())) AND + Extract(Year FROM crm_lead.date_deadline) = + Extract(Year FROM DATE(NOW()))''' % session_user_id) + exp_revenue_data = self._cr.dictfetchall() + exp_revenue_data_value = [item['sum'] for item in exp_revenue_data] + exp_revenue_value = exp_revenue_data_value[0] + if exp_revenue_value is None: + exp_revenue_value = 0 + self._cr.execute('''select SUM(crm_lead.expected_revenue) from + crm_lead WHERE crm_lead.user_id = %s and type='opportunity' + and active='true' and stage_id=4 AND Extract( + MONTH FROM crm_lead.date_closed) = Extract(MONTH FROM DATE(NOW())) + AND Extract(Week FROM crm_lead.date_closed) = + Extract(Week FROM DATE(NOW())) AND Extract(Year + FROM crm_lead.date_closed) = Extract(Year FROM DATE(NOW()))''' + % session_user_id) + revenue_data = self._cr.dictfetchall() + revenue_data_value = [item['sum'] for item in revenue_data] + revenue_value = revenue_data_value[0] + if revenue_value is None: + revenue_value = 0 + ratio_value = [] + if revenue_value == 0: + ratio_value = 0 + if revenue_value > 0: + self._cr.execute('''select case when b.count_two = 0 then 0 else ( + CAST(a.count_one as float) / CAST(b.count_two as float))end as + final_count from (select COUNT(id) as count_one from crm_lead + WHERE crm_lead.user_id = '%s' AND crm_lead.active = True AND + crm_lead.probability = 100 AND + Extract(MONTH FROM crm_lead.date_deadline) = + Extract(MONTH FROM DATE(NOW())) AND + Extract(Week FROM crm_lead.date_deadline) = + Extract(Week FROM DATE(NOW())) AND + Extract(Year FROM crm_lead.date_open + ) = Extract(Year FROM DATE(NOW())))a, + (select COUNT(id) as count_two from crm_lead WHERE + crm_lead.user_id = '%s' AND crm_lead.active = False AND + crm_lead.probability = 0 AND Extract(MONTH + FROM crm_lead.date_deadline) = Extract(MONTH FROM DATE(NOW())) + AND Extract(Week FROM crm_lead.date_deadline) = + Extract(Week FROM DATE(NOW())) AND Extract(Year FROM + crm_lead.date_deadline) = Extract(Year FROM DATE(NOW())))b + ''' % (session_user_id, session_user_id)) + ratio_value = [row[0] for row in self._cr.fetchall()] + ratio_value = str(ratio_value)[1:-1] + self._cr.execute('''SELECT active,count(active) FROM crm_lead + where type='opportunity' and active = true and probability = 100 and + user_id=%s AND Extract(MONTH FROM crm_lead.date_closed) = + Extract(MONTH FROM DATE(NOW())) AND Extract(Week FROM + crm_lead.date_closed) = Extract(Week FROM DATE(NOW())) AND + Extract(Year FROM crm_lead.date_closed) = Extract(Year FROM DATE(NOW())) + or type='opportunity' and active = false and probability = 0 + and user_id=%s AND Extract(MONTH FROM crm_lead.date_deadline) + = Extract(MONTH FROM DATE(NOW())) AND + Extract(Week FROM crm_lead.date_deadline) + = Extract(Week FROM DATE(NOW())) AND + Extract(Year FROM crm_lead.date_deadline) + = Extract(Year FROM DATE(NOW()))GROUP BY active''' + % (session_user_id, session_user_id)) + record_opportunity = dict(self._cr.fetchall()) + opportunity_ratio_value = 0.0 + if record_opportunity == {}: + opportunity_ratio_value = 0.0 + else: + total_opportunity_won = record_opportunity.get(False) + total_opportunity_lost = record_opportunity.get(True) + if total_opportunity_won is None: + total_opportunity_won = 0 + if total_opportunity_lost is None: + total_opportunity_lost = 0 + opportunity_ratio_value = 0.0 + if total_opportunity_lost > 0: + opportunity_ratio_value = round( + total_opportunity_won / total_opportunity_lost, 2) + avg = 0 + if crm_lead_value == 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data = self._cr.dictfetchall() + for rec in data: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + if crm_lead_value > 1: + self._cr.execute('''SELECT id, date_conversion, create_date + FROM crm_lead WHERE date_conversion IS NOT NULL;''') + data1 = self._cr.dictfetchall() + for rec in data1: + date_close = rec['date_conversion'] + date_create = rec['create_date'] + avg = (date_close - date_create).seconds + if crm_lead_value == 0: + record_avg_time = 0 + else: + record_avg_time = round(avg / crm_lead_value) + data_week = { + 'record': crm_lead_value, + 'record_op': opportunity_value, + 'record_rev_exp': exp_revenue_value, + 'record_rev': revenue_value, + 'record_ratio': ratio_value, + 'opportunity_ratio_value': str(opportunity_ratio_value), + 'avg_time': record_avg_time, + } + return data_week diff --git a/crm_dashboard/models/crm_team.py b/crm_dashboard/models/crm_team.py new file mode 100644 index 000000000..fd17b59bd --- /dev/null +++ b/crm_dashboard/models/crm_team.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Mruthul Raj (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 . +# +################################################################################ +from odoo import fields, models + + +class CRMSalesTeam(models.Model): + """CRMSalesTeam model extends the base crm.team model to add a field, + crm_lead_state_id, which represents the default CRM Lead stage for + leads associated with this sales team. + """ + _inherit = 'crm.team' + + crm_lead_state_id = fields.Many2one("crm.stage", string="CRM Lead", + store=True, + help="CRM Lead stage for leads " + "associated with this sales team.") diff --git a/crm_dashboard/models/res_user.py b/crm_dashboard/models/res_user.py new file mode 100644 index 000000000..256f7955d --- /dev/null +++ b/crm_dashboard/models/res_user.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Mruthul Raj (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 . +# +################################################################################ +from odoo import fields, models + + +class ResUser(models.Model): + """ResUser model extends the base res. Users model to add a field 'sales' + that represents the target for the salesperson.""" + _inherit = 'res.users' + + sales = fields.Float(string="Target", help="The target value for the " + "salesperson.") diff --git a/crm_dashboard/models/sale_order.py b/crm_dashboard/models/sale_order.py new file mode 100644 index 000000000..bcb54b79c --- /dev/null +++ b/crm_dashboard/models/sale_order.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Mruthul Raj (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 . +# +################################################################################ +from odoo import models + + +class SalesOrder(models.Model): + """Extends sale order for overriding action confirm function""" + _inherit = 'sale.order' + + def action_confirm(self): + """Override the action_confirm method to change CRM Stage. + Returns: + dict: A dictionary containing the result of the original + action_confirm method.""" + res = super(SalesOrder, self).action_confirm() + self.opportunity_id.stage_id = self.team_id.crm_lead_state_id + return res diff --git a/crm_dashboard/models/utm_campaign.py b/crm_dashboard/models/utm_campaign.py new file mode 100644 index 000000000..3b1e11ca0 --- /dev/null +++ b/crm_dashboard/models/utm_campaign.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies(). +# Author: Mruthul Raj (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 . +# +################################################################################ +from odoo import fields, models + + +class CampaignSmartButton(models.Model): + """Extends the UTM Campaign model with a Smart Button to calculate and + display the Win Loss Ratio.""" + _inherit = 'utm.campaign' + + total_ratio = fields.Float(compute='_compute_ratio', + help="Total lead ratio") + + def get_ratio(self): + """Open the Win Loss Ratio window upon clicking the Smart Button. + Returns: + dict: A dictionary specifying the action to be taken upon button + click.""" + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': 'Win Loss Ratio', + 'view_mode': 'kanban', + 'res_model': 'crm.lead', + 'domain': [['user_id', '=', self.env.uid], "|", + "&", ["active", "=", True], ["probability", '=', 100], + "&", ["active", "=", False], ["probability", '=', 0] + ], + 'context': "{'create': False,'records_draggable': False}" + } + + def _compute_ratio(self): + """Compute the Win Loss Ratio based on CRM lead statistics.""" + total_won = self.env['crm.lead'].search_count( + [('active', '=', True), ('probability', '=', 100), + ('user_id', '=', self.env.uid)]) + total_lose = self.env['crm.lead'].search_count( + [('active', '=', False), ('probability', '=', 0), + ('user_id', '=', self.env.uid)]) + + if total_lose == 0: + ratio = 0 + else: + ratio = round(total_won / total_lose, 2) + self.total_ratio = ratio diff --git a/crm_dashboard/static/description/assets/icons/capture (1).png b/crm_dashboard/static/description/assets/icons/capture (1).png new file mode 100644 index 000000000..8824deafc Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/capture (1).png differ diff --git a/crm_dashboard/static/description/assets/icons/check.png b/crm_dashboard/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/check.png differ diff --git a/crm_dashboard/static/description/assets/icons/chevron.png b/crm_dashboard/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/chevron.png differ diff --git a/crm_dashboard/static/description/assets/icons/cogs.png b/crm_dashboard/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/cogs.png differ diff --git a/crm_dashboard/static/description/assets/icons/consultation.png b/crm_dashboard/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/consultation.png differ diff --git a/crm_dashboard/static/description/assets/icons/ecom-black.png b/crm_dashboard/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/ecom-black.png differ diff --git a/crm_dashboard/static/description/assets/icons/education-black.png b/crm_dashboard/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/education-black.png differ diff --git a/crm_dashboard/static/description/assets/icons/hotel-black.png b/crm_dashboard/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/hotel-black.png differ diff --git a/crm_dashboard/static/description/assets/icons/img.png b/crm_dashboard/static/description/assets/icons/img.png new file mode 100644 index 000000000..70197f477 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/img.png differ diff --git a/crm_dashboard/static/description/assets/icons/license.png b/crm_dashboard/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/license.png differ diff --git a/crm_dashboard/static/description/assets/icons/lifebuoy.png b/crm_dashboard/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/lifebuoy.png differ diff --git a/crm_dashboard/static/description/assets/icons/manufacturing-black.png b/crm_dashboard/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/manufacturing-black.png differ diff --git a/crm_dashboard/static/description/assets/icons/photo-capture.png b/crm_dashboard/static/description/assets/icons/photo-capture.png new file mode 100644 index 000000000..06c111758 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/photo-capture.png differ diff --git a/crm_dashboard/static/description/assets/icons/pos-black.png b/crm_dashboard/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/pos-black.png differ diff --git a/crm_dashboard/static/description/assets/icons/puzzle.png b/crm_dashboard/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/puzzle.png differ diff --git a/crm_dashboard/static/description/assets/icons/restaurant-black.png b/crm_dashboard/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/restaurant-black.png differ diff --git a/crm_dashboard/static/description/assets/icons/service-black.png b/crm_dashboard/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/service-black.png differ diff --git a/crm_dashboard/static/description/assets/icons/trading-black.png b/crm_dashboard/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/trading-black.png differ diff --git a/crm_dashboard/static/description/assets/icons/training.png b/crm_dashboard/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/training.png differ diff --git a/crm_dashboard/static/description/assets/icons/update.png b/crm_dashboard/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/update.png differ diff --git a/crm_dashboard/static/description/assets/icons/user.png b/crm_dashboard/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/user.png differ diff --git a/crm_dashboard/static/description/assets/icons/wrench.png b/crm_dashboard/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/crm_dashboard/static/description/assets/icons/wrench.png differ diff --git a/crm_dashboard/static/description/assets/misc/Cybrosys R.png b/crm_dashboard/static/description/assets/misc/Cybrosys R.png new file mode 100644 index 000000000..da4058087 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/Cybrosys R.png differ diff --git a/crm_dashboard/static/description/assets/misc/categories.png b/crm_dashboard/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/categories.png differ diff --git a/crm_dashboard/static/description/assets/misc/check-box.png b/crm_dashboard/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/check-box.png differ diff --git a/crm_dashboard/static/description/assets/misc/compass.png b/crm_dashboard/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/compass.png differ diff --git a/crm_dashboard/static/description/assets/misc/corporate.png b/crm_dashboard/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/corporate.png differ diff --git a/crm_dashboard/static/description/assets/misc/customer-support.png b/crm_dashboard/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/customer-support.png differ diff --git a/crm_dashboard/static/description/assets/misc/cybrosys-logo.png b/crm_dashboard/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/cybrosys-logo.png differ diff --git a/crm_dashboard/static/description/assets/misc/email.svg b/crm_dashboard/static/description/assets/misc/email.svg new file mode 100644 index 000000000..15291cdc3 --- /dev/null +++ b/crm_dashboard/static/description/assets/misc/email.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crm_dashboard/static/description/assets/misc/features.png b/crm_dashboard/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/features.png differ diff --git a/crm_dashboard/static/description/assets/misc/logo.png b/crm_dashboard/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/logo.png differ diff --git a/crm_dashboard/static/description/assets/misc/phone.svg b/crm_dashboard/static/description/assets/misc/phone.svg new file mode 100644 index 000000000..b7bd7f251 --- /dev/null +++ b/crm_dashboard/static/description/assets/misc/phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/crm_dashboard/static/description/assets/misc/pictures.png b/crm_dashboard/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/pictures.png differ diff --git a/crm_dashboard/static/description/assets/misc/pie-chart.png b/crm_dashboard/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/pie-chart.png differ diff --git a/crm_dashboard/static/description/assets/misc/right-arrow.png b/crm_dashboard/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/right-arrow.png differ diff --git a/crm_dashboard/static/description/assets/misc/star (1) 2.svg b/crm_dashboard/static/description/assets/misc/star (1) 2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/crm_dashboard/static/description/assets/misc/star (1) 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/crm_dashboard/static/description/assets/misc/star.png b/crm_dashboard/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/star.png differ diff --git a/crm_dashboard/static/description/assets/misc/support (1) 1.svg b/crm_dashboard/static/description/assets/misc/support (1) 1.svg new file mode 100644 index 000000000..7d37a8f30 --- /dev/null +++ b/crm_dashboard/static/description/assets/misc/support (1) 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/crm_dashboard/static/description/assets/misc/support-email.svg b/crm_dashboard/static/description/assets/misc/support-email.svg new file mode 100644 index 000000000..eb70370d6 --- /dev/null +++ b/crm_dashboard/static/description/assets/misc/support-email.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/crm_dashboard/static/description/assets/misc/support.png b/crm_dashboard/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/support.png differ diff --git a/crm_dashboard/static/description/assets/misc/tick-mark.svg b/crm_dashboard/static/description/assets/misc/tick-mark.svg new file mode 100644 index 000000000..2dbb40187 --- /dev/null +++ b/crm_dashboard/static/description/assets/misc/tick-mark.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/crm_dashboard/static/description/assets/misc/whatsapp 1.svg b/crm_dashboard/static/description/assets/misc/whatsapp 1.svg new file mode 100644 index 000000000..0bfaf8fc6 --- /dev/null +++ b/crm_dashboard/static/description/assets/misc/whatsapp 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/crm_dashboard/static/description/assets/misc/whatsapp.png b/crm_dashboard/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/crm_dashboard/static/description/assets/misc/whatsapp.png differ diff --git a/crm_dashboard/static/description/assets/misc/whatsapp.svg b/crm_dashboard/static/description/assets/misc/whatsapp.svg new file mode 100644 index 000000000..b618aea1d --- /dev/null +++ b/crm_dashboard/static/description/assets/misc/whatsapp.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crm_dashboard/static/description/assets/modules/1.png b/crm_dashboard/static/description/assets/modules/1.png new file mode 100644 index 000000000..d0f36b007 Binary files /dev/null and b/crm_dashboard/static/description/assets/modules/1.png differ diff --git a/crm_dashboard/static/description/assets/modules/l2.png b/crm_dashboard/static/description/assets/modules/l2.png new file mode 100644 index 000000000..f40a0756d Binary files /dev/null and b/crm_dashboard/static/description/assets/modules/l2.png differ diff --git a/crm_dashboard/static/description/assets/modules/l3.png b/crm_dashboard/static/description/assets/modules/l3.png new file mode 100644 index 000000000..5738a486e Binary files /dev/null and b/crm_dashboard/static/description/assets/modules/l3.png differ diff --git a/crm_dashboard/static/description/assets/modules/l4.png b/crm_dashboard/static/description/assets/modules/l4.png new file mode 100644 index 000000000..8d99e8c68 Binary files /dev/null and b/crm_dashboard/static/description/assets/modules/l4.png differ diff --git a/crm_dashboard/static/description/assets/modules/l5.png b/crm_dashboard/static/description/assets/modules/l5.png new file mode 100644 index 000000000..3415917c2 Binary files /dev/null and b/crm_dashboard/static/description/assets/modules/l5.png differ diff --git a/crm_dashboard/static/description/assets/modules/l6.png b/crm_dashboard/static/description/assets/modules/l6.png new file mode 100644 index 000000000..c7ea331ee Binary files /dev/null and b/crm_dashboard/static/description/assets/modules/l6.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/1.png b/crm_dashboard/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..ee7da787d Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/1.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/2.png b/crm_dashboard/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..ec414ce2b Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/2.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/3.png b/crm_dashboard/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..2ca24c603 Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/3.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/4.png b/crm_dashboard/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..ecfd99d5c Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/4.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/5.png b/crm_dashboard/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..cffd04a7c Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/5.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/6.png b/crm_dashboard/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..81c4daeae Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/6.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/7.png b/crm_dashboard/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..c09a88887 Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/7.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/8.png b/crm_dashboard/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..26652e549 Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/8.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/9.png b/crm_dashboard/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..69453476a Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/9.png differ diff --git a/crm_dashboard/static/description/assets/screenshots/hero.gif b/crm_dashboard/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..8258630e6 Binary files /dev/null and b/crm_dashboard/static/description/assets/screenshots/hero.gif differ diff --git a/crm_dashboard/static/description/banner.jpg b/crm_dashboard/static/description/banner.jpg new file mode 100644 index 000000000..5bf8e7b03 Binary files /dev/null and b/crm_dashboard/static/description/banner.jpg differ diff --git a/crm_dashboard/static/description/icon.png b/crm_dashboard/static/description/icon.png new file mode 100644 index 000000000..05fdb7768 Binary files /dev/null and b/crm_dashboard/static/description/icon.png differ diff --git a/crm_dashboard/static/description/index.html b/crm_dashboard/static/description/index.html new file mode 100644 index 000000000..8a4ab211f --- /dev/null +++ b/crm_dashboard/static/description/index.html @@ -0,0 +1,840 @@ + + + + + + + Odoo App 3 Index + + + + + + + + +
+
+
+
+
+ +
+
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+
+
+
+

+ CRM Dashboard

+

+ Detailed Dashboard View for CRM. +

+
+ +
+
+
+
+
+

+ Key Highlights +

+
+
+
+
+
+ +
+
+

+ Dashboard view for CRM module

+
+
+
+
+
+
+ +
+
+

+ Different Types of Reports in the Form of + Graphs

+
+
+
+
+
+
+ +
+
+

+ Dedicated + Views + for Users and Admins.

+
+
+
+
+
+
+ +
+
+

+ Top deals + and + Monthly revenue goals.

+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+

+ Clickable + Dashboard Cards

+

You can + click + on + the respective Cards which will direct the + users to the respective information + and aspects of the CRM operations with + Odoo.

+
+
+
+
+
+
+ +
+
+

+ Target, Lead + and + Activity analysis

+

A dedicated + Funnel Chart provides you with complete + insight into the Leads which are in + operation.

+
+
+
+
+
+
+ +
+
+

+ Set up + Individual + user targets

+
+
+
+
+
+
+ +
+
+

+ A dedicated + Listing of Top Deals and the Monthly Goal + Gauge motivates users.

+
+
+
+
+
+
+ +
+
+

+ Upcoming + Activities and Total Revenue.

+
+
+
+
+
+
+ +
+
+

+ Top Salespersons as well as Top Country + Revenues.

+
+
+
+
+
+
+ +
+
+

+ Lost + Analysis, + Recent Activities, and Top Salesperson.

+
+
+
+
+
+
+ +
+
+

+ Dashboard + View of + the User.

+
+
+
+
+
+
+ +
+
+
+
+
+
+
    +
  • + Activity + monitoring. +
  • +
  • + Year to + Date + bar graphTarget Field in User Settings. +
  • +
  • + Top deals + and + Monthly revenue goals. +
  • +
  • + The users + will + have different authorization based on + the employment level. +
  • + +
+
+
+
+
+
+
Version + 17.0.1.0.0|Released on:28th Dec 2023 +
+

+ Initial Commit for CRM Dashboard.

+
+
+
+
+
+
+
+

+ Related Products

+
+
+ +
+
+

+ Our Services

+ +
+
+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Customization

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Implementation

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Support

+
+
+
+
+
+
+ service-icon +
+
+

Hire + Odoo Developer

+
+
+
+
+ +
+
+ service-icon +
+
+

Odoo + Integration

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Migration

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Consultancy

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Implementation

+
+
+
+
+
+
+ service-icon +
+
+

Odoo + Licensing Consultancy

+
+
+
+
+
+
+

+ Our Industries

+ +
+
+
+
+
+
+ +

Trading

+

Easily procure and sell your products

+
+
+
+
+ +

POS

+

Easy configuration and convivial experience

+
+
+
+
+ +

+ Education

+

A platform for educational management

+
+
+
+
+ +

+ Manufacturing

+

Plan, track and schedule your operations

+
+
+
+
+ +

E-commerce & + Website

+

Mobile friendly, awe-inspiring product pages

+
+
+
+
+ +

Service + Management

+

Keep track of services and invoice

+
+
+
+
+ +

+ Restaurant

+

Run your bar or restaurant methodically

+
+
+
+
+ +

Hotel + Management

+

An all-inclusive hotel management application

+
+
+
+
+
+
+

+ Support

+
+
+
+
+
+
+
+ +
+ Need + Help? +

Got + questions or need help? Get in touch.

+
odoo@cybrosys.com +
+
+
+
+
+
+
+
+ +
+ WhatsApp +

Say hi to + us on WhatsApp!

+
+91 + 99456767686 +
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/crm_dashboard/static/src/css/dashboard.css b/crm_dashboard/static/src/css/dashboard.css new file mode 100644 index 000000000..335e5831e --- /dev/null +++ b/crm_dashboard/static/src/css/dashboard.css @@ -0,0 +1,34 @@ +p, span, a, ul, li, button { + font-size: inherit; + font-weight: inherit; + line-height: inherit; +} + +strong { + font-weight: 600; +} + +h1, h2, h3, h4, h5, h6 { + line-height: 1.5em; + font-weight: 300; +} + +strong { + font-weight: 400; +} + +.sub_title { + font-size: 14px; +} + +.sub_title div span { + font-weight: 600; +} + +.chart #canvas_graph { + height: 400px !important; +} + +.highcharts-background { + fill: none; +} \ No newline at end of file diff --git a/crm_dashboard/static/src/css/material-gauge.css b/crm_dashboard/static/src/css/material-gauge.css new file mode 100644 index 000000000..7f553df9a --- /dev/null +++ b/crm_dashboard/static/src/css/material-gauge.css @@ -0,0 +1,194 @@ +/* + * #### Gauge Component + * + * The standard markup for the component is: + * + *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * No + * + * Yes + *
+ *
+ */ + +/* + * First define all of the relevant rules that aren't dependent + * on the size of the gauge. We want to collect the size-depenent + * rules in one place to make it easier to adjust the size. + */ + +.gauge { + position: relative; +} + +.gauge__container { + margin: 0; + padding: 0; + position: absolute; + left: 50%; + overflow: hidden; + text-align: center; + -webkit-transform: translateX(-50%); + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + -o-transform: translateX(-50%); + transform: translateX(-50%); +} + +.gauge__background { + z-index: 0; + position: absolute; + background-color: #d8f0de; + top: 0; + border-radius: 300px 300px 0 0; +} + +.gauge__data { + z-index: 1; + position: absolute; + background-color: #00c29d; + margin-left: auto; + margin-right: auto; + border-radius: 300px 300px 0 0; + -webkit-transform-origin: center bottom; + -moz-transform-origin: center bottom; + -ms-transform-origin: center bottom; + -o-transform-origin: center bottom; + transform-origin: center bottom; +} + +.gauge__center { + z-index: 2; + position: absolute; + background-color: #f9f9f9; + margin-right: auto; + border-radius: 300px 300px 0 0; +} + +.gauge__marker { + z-index: 3; + background-color: #fff; + position: absolute; + width: 1px; +} + +.gauge__needle { + z-index: 4; + background-color: #21242c; + height: 3px; + position: absolute; + -webkit-transform-origin: left center; + -moz-transform-origin: left center; + -ms-transform-origin: left center; + -o-transform-origin: left center; + transform-origin: left center; +} + +.gauge__labels { + display: table; + margin: 0 auto; + position: relative; +} + +.gauge__label--low { + display: table-cell; + text-align: center; + color: #00c29d; +} + +.gauge__label--spacer { + display: table-cell; +} + +.gauge__label--high { + display: table-cell; + text-align: center; + color: #979f99; +} + +/* + * Now define the rules that depend on the size of + * the gauge. We start with sizing for a small mobile + * device. + */ + +.gauge { height: calc(120px + 3em); } +.gauge__container { width: 240px; height: 120px; } +.gauge__marker { height: 120px; left: 119.5px; } +.gauge__background { width: 240px; height: 120px; } +.gauge__center { width: 144px; height: 72px; top: 48px; margin-left: 48px; } +.gauge__data { width: 240px; height: 120px; } +.gauge__needle { left: 120px; top: 117px; width: 120px; } +.gauge__labels { top: 120px; width: 240px; } +.gauge__label--low { width: 48px; } +.gauge__label--spacer { width: 144px; } +.gauge__label--high { width: 48px; } + +/* + * Increase the gauge size slightly on larger viewports. + */ + + @media only screen and (min-width: 400px) { + .gauge { height: calc(150px + 3em); } + .gauge__container { width: 300px; height: 150px; } + .gauge__marker { height: 150px; left: 149.5px; } + .gauge__background { width: 300px; height: 150px; } + .gauge__center { width: 180px; height: 90px; top: 60px; margin-left: 60px; } + .gauge__data { width: 300px; height: 150px; } + .gauge__needle { left: 150px; top: 147px; width: 150px; } + .gauge__labels { top: 160px; width: 300px; font-size: 20px;} + .gauge__label--low { width: 60px; } + .gauge__label--spacer { width: 180px; } + .gauge__label--high { width: 60px; } +} + +/* + * As an option, the `gauge--liveupdate` class can be added + * to the main gauge element. When this class is present, + * we add a transition that animates any changes to the gauge + * value. Currently, the app does not use this option because + * all the inputs that can change gauge values are present + * on tab panels that are different from the gauge itself. + * Therefore, users won't be able to see any gauge changes + * when they make input changes. The code is available, though, + * should this change. + */ + +.gauge--liveupdate .gauge__data, +.gauge--liveupdate .gauge__needle { + -webkit-transition: all 1s ease-in-out; + -moz-transition: all 1s ease-in-out; + -ms-transition: all 1s ease-in-out; + -o-transition: all 1s ease-in-out; + transition: all 1s ease-in-out; +} + +/* + * For a given gauge value, x, ranging from 0.0 to 1.0, set + * the `transform: rotate()` property according to the + * following equation: `-0.5 + 0.5x turns` The default + * properties below represent an x value of 0. + */ + +.gauge__data { + -webkit-transform: rotate(-.50turn); + -moz-transform: rotate(-.50turn); + -ms-transform: rotate(-.50turn); + -o-transform: rotate(-.50turn); + transform: rotate(-.50turn); +} +.gauge__needle { + -webkit-transform: rotate(-.50turn); + -moz-transform: rotate(-.50turn); + -ms-transform: rotate(-.50turn); + -o-transform: rotate(-.50turn); + transform: rotate(-.50turn); +} diff --git a/crm_dashboard/static/src/css/style.scss b/crm_dashboard/static/src/css/style.scss new file mode 100644 index 000000000..7ddad814e --- /dev/null +++ b/crm_dashboard/static/src/css/style.scss @@ -0,0 +1,237 @@ +:root { + /* Primary */ + --mauve: #7D7EAF; + --pink-dark: #BD85BA; + --pink: #F78EAD; + --peach: #FFA48E; + --orange: #FFCA71; + --gold: #CEA716; + --green: #1EC198; + --grey: #a0a0a0; + /* Light */ + --mauve-light: #e5e5ef; + --pink-dark-light: #f2e7f1; + --pink-light: #fde8ef; + --peach-light: #ffede8; + --orange-light: #fff4e3; + --gold-light: #faf6e8; + --green-light: #e9f9f5; + --grey-light: #e0e0e0; + + /*Lighter*/ + --grey-lighter: #fafafa; + --grey-dark-lighter: #f3f3f3; +} + +/* Background */ +.bg-mauve-light { + background-color: var(--mauve-light); +} + +.bg-pink-dark-light { + background-color: var(--pink-dark-light); +} + +.bg-pink-light { + background-color: var(--pink-light); +} + +.bg-peach-light { + background-color: var(--peach-light); +} + +.bg-orange-light { + background-color: var(--orange-light); +} + +.bg-gold-light { + background-color: var(--gold-light); +} + +.bg-green-light { + background-color: var(--green-light); +} + +/* Text */ +.text-mauve { + color: var(--mauve); +} + +.text-pink-dark { + color: var(--pink-dark); +} + +.text-pink { + color: var(--pink); +} + +.text-peach { + color: var(--peach); +} + +.text-orange { + color: var(--orange); +} + +.text-gold { + color: var(--gold); +} + +.text-green { + color: var(--green); +} + +/* Cards */ + +.dashboard-card { + border-radius: 0.3rem; + display: flex; + justify-content: center; + margin: 1rem auto; + height: 90px; +} + +.dashboard-card__icon-container { + height: 50px; + width: 50px; + border-radius: 50%; +} + +.dashboard-card__icon-container i { + font-size: 20px; +} + +.dashboard-card__details { + margin-left: 1rem; + max-width: 120px; +} + +.dashboard-card__details h3 { + font-weight: 700; + font-size: 1.5rem; +} + +.dashboard-card__details h4 { + font-weight: 700; + font-size: 0.7rem; + color: var(--grey); + margin-top: -5px; +} + +h2.section-header { + font-weight: 700; + font-size: 1.5rem; +} + +.chart-container { + border-radius: 0.3rem; + padding: 1rem; + margin: 1rem auto; +} + +.chart-container.card-shadow { + height: 100%; +} + +.half_chart.chart-container.card-shadow { + height: 49%; +} + +.chart-container h2 { + font-weight: 700; + font-size: 1.125rem; +} + +.item-container { + background-color: var(--grey-lighter); + border-radius: 0.3rem; + padding: 1.2rem 1rem; + margin: 1rem auto; +} + +.item-container:hover { + background-color: var(--grey-dark-lighter); + transition: all 0.3s ease-in-out; + cursor: pointer; +} + +.count-container { + font-weight: 700; + font-size: 1.7rem; + background-color: var(--mauve-light); + color: var(--mauve); + height: 50px; + width: 50px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; +} + +.item-header { + padding:inherit; + display: flex; + align-items: flex-start; +} + +.item-title h3 { + font-size: 1.3rem; + font-weight: 700; +} + +.item-content ul { + list-style: none; + padding-left: 0px; +} + +.item-content ul>li { + font-size: 0.9rem; + color: var(--grey); + font-weight: 700; +} + +/* Misc */ +.card-shadow { + -webkit-box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); + -moz-box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); + box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); +} + +/* Table */ +thead { + background-color: #e9ecf0; + border-bottom: none; +} + +.table thead th { + border-bottom: none; +} + +.table td, +.table th { + border-top: 1px solid #eceff2; +} + +.crm_scroll_table { + max-height: 395px; + overflow-y: auto; +} +.recent_activity_div .crm_scroll_table { + max-height: 435px; +} + +.crm_scroll_table thead { + position: sticky; + top: 0; +} + +.crm_scroll_table .count-container { + height: 45px; + width: 162px; + border-radius: 50px; + margin-right: 10px; +} + +.crm_scroll_table .item-content ul > li { + font-size: 1.1rem; +} \ No newline at end of file diff --git a/crm_dashboard/static/src/js/crm_dashboard.js b/crm_dashboard/static/src/js/crm_dashboard.js new file mode 100644 index 000000000..e34283978 --- /dev/null +++ b/crm_dashboard/static/src/js/crm_dashboard.js @@ -0,0 +1,1460 @@ +/** @odoo-module */ +import { registry} from '@web/core/registry'; +import { useService } from "@web/core/utils/hooks"; +const { Component, onWillStart, onMounted} = owl +import { jsonrpc } from "@web/core/network/rpc_service"; +import { _t } from "@web/core/l10n/translation"; +import { session } from "@web/session"; +import { WebClient } from "@web/webclient/webclient"; +export class CRMDashboard extends Component { + /** + * Sets up the CRM Dashboard component. + * Initializes required services and lifecycle hooks. + */ + setup() { + this.action = useService("action"); + this.orm = useService("orm"); + onWillStart(this.onWillStart); + onMounted(this.onMounted); + } + /** + * Function triggered before the component starts. + * Perform necessary setup or operations here. + */ + async onWillStart() { + var self = this; + this.login_employee = {}; + var def0 = jsonrpc('/web/dataset/call_kw/crm.lead/check_user_group', { + model: 'crm.lead', + method: 'check_user_group', + args: [{}], + kwargs: {}, + }).then(function(result) { + if (result == true) { + self.is_manager = true; + } else { + self.is_manager = false; + } + }); + var def1 = jsonrpc('/web/dataset/call_kw/crm.lead/get_upcoming_events', { + model: "crm.lead", + method: "get_upcoming_events", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.upcoming_events = res['event']; + self.current_lang = res['cur_lang']; + }); + var def2 = jsonrpc('/web/dataset/call_kw/crm.lead/get_top_deals', { + model: "crm.lead", + method: "get_top_deals", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.top_deals = res['deals']; + }); + var def3 = jsonrpc('/web/dataset/call_kw/crm.lead/get_monthly_goal', { + model: "crm.lead", + method: "get_monthly_goal", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.monthly_goals = res['goals']; + }); + var def4 = jsonrpc('/web/dataset/call_kw/crm.lead/get_top_sp_revenue', { + model: "crm.lead", + method: "get_top_sp_revenue", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.top_sp_revenue = res['top_revenue']; + }); + var def5 = jsonrpc('/web/dataset/call_kw/crm.lead/get_country_revenue', { + model: "crm.lead", + method: "get_country_revenue", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.top_country_revenue = res['country_revenue']; + }); + var def6 = jsonrpc('/web/dataset/call_kw/crm.lead/get_country_count', { + model: "crm.lead", + method: "get_country_count", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.top_country_count = res['country_count']; + }); + var def8 = jsonrpc('/web/dataset/call_kw/crm.lead/get_ratio_based_country', { + model: "crm.lead", + method: "get_ratio_based_country", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.top_country_wise_ratio = res['country_wise_ratio']; + }); + var def9 = jsonrpc('/web/dataset/call_kw/crm.lead/get_ratio_based_sp', { + model: "crm.lead", + method: "get_ratio_based_sp", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.top_salesperson_wise_ratio = res['salesperson_wise_ratio']; + }); + var def10 = jsonrpc('/web/dataset/call_kw/crm.lead/get_ratio_based_sales_team', { + model: "crm.lead", + method: "get_ratio_based_sales_team", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.top_sales_team_wise_ratio = res['sales_team_wise_ratio']; + }); + var def11 = jsonrpc('/web/dataset/call_kw/crm.lead/get_recent_activities', { + model: "crm.lead", + method: "get_recent_activities", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.recent_activities = res['activities']; + }); + var def12 = jsonrpc('/web/dataset/call_kw/crm.lead/get_count_unassigned', { + model: "crm.lead", + method: "get_count_unassigned", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.get_count_unassigned = res['count_unassigned']; + }); + var def13 = jsonrpc('/web/dataset/call_kw/crm.lead/get_top_sp_by_invoice', { + model: "crm.lead", + method: "get_top_sp_by_invoice", + args: [{}], + kwargs: {}, + }) + .then(function(res) { + self.top_sp_by_invoice = res['sales_person_invoice']; + }); + return $.when(def0, def1, def2, def3, def4, def5, def6, def8, def9, def10, def11, def12, def13); + } + /** + * Handles the change event for income and expense values. + * @param {Event} e - The event object. + */ + on_change_income_expense_values(e) { + e.stopPropagation(); + var $target = $(e.target); + var value = $target.val(); + if (value == "this_year") { + this.onclick_this_year($target.val()); + } else if (value == "this_quarter") { + this.onclick_this_quarter($target.val()); + } else if (value == "this_month") { + this.onclick_this_month($target.val()); + } else if (value == "this_week") { + this.onclick_this_week($target.val()); + } + } + /** + * Handles the click event for the 'this_week' option. + * Calls a JSON-RPC method to retrieve data for the current week. + * Updates the UI to display the relevant information. + * @param {Event} ev - The event object. + */ + onclick_this_week(ev) { + var self = this; + jsonrpc('/web/dataset/call_kw/crm.lead/crm_week', { + model: 'crm.lead', + method: 'crm_week', + args: [{}], + kwargs: {}, + }) + .then(function(result) { + $('#leads_this_year').hide(); + $('#opp_this_year').hide(); + $('#exp_rev_this_year').hide(); + $('#rev_this_year').hide(); + $('#ratio_this_year').hide(); + $('#avg_time_this_year').hide(); + $('#total_revenue_this_year').hide(); + $('#leads_this_quarter').hide(); + $('#opp_this_quarter').hide(); + $('#exp_rev_this_quarter').hide(); + $('#rev_this_quarter').hide(); + $('#ratio_this_quarter').hide(); + $('#avg_time_this_quarter').hide(); + $('#total_revenue_this_quarter').hide(); + $('#leads_this_month').hide(); + $('#opp_this_month').hide(); + $('#exp_rev_this_month').hide(); + $('#rev_this_month').hide(); + $('#ratio_this_month').hide(); + $('#avg_time_this_month').hide(); + $('#total_revenue_this_month').hide(); + $('#leads_this_week').show(); + $('#opp_this_week').show(); + $('#exp_rev_this_week').show(); + $('#rev_this_week').show(); + $('#ratio_this_week').show(); + $('#avg_time_this_week').show(); + $('#total_revenue_this_week').show(); + $('#leads_this_week').empty(); + $('#opp_this_week').empty(); + $('#exp_rev_this_week').empty(); + $('#rev_this_week').empty(); + $('#ratio_this_week').empty(); + $('#avg_time_this_week').empty(); + $('#total_revenue_this_week').empty(); + $('#leads_this_week').append('' + result.record + ''); + $('#opp_this_week').append('' + result.record_op + ''); + $('#exp_rev_this_week').append('' + self.monthly_goals[2] + ' ' + result.record_rev_exp + ''); + $('#rev_this_week').append('' + self.monthly_goals[2] + ' ' + result.record_rev + ''); + $('#ratio_this_week').append('' + result.record_ratio + ''); + $('#avg_time_this_week').append('' + result.avg_time + ' sec' + ''); + $('#total_revenue_this_week').append('' + result.opportunity_ratio_value + ''); + }) + } + /** + * Handles the click event for the 'this_month' option. + * Calls a JSON-RPC method to retrieve data for the current month. + * Updates the UI to display the relevant information. + * @param {Event} ev - The event object. + */ + onclick_this_month(ev) { + var self = this; + jsonrpc('/web/dataset/call_kw/crm.lead/crm_month', { + model: 'crm.lead', + method: 'crm_month', + args: [{}], + kwargs: {}, + }) + .then(function(result) { + $('#leads_this_year').hide(); + $('#opp_this_year').hide(); + $('#exp_rev_this_year').hide(); + $('#rev_this_year').hide(); + $('#ratio_this_year').hide(); + $('#avg_time_this_year').hide(); + $('#total_revenue_this_year').hide(); + $('#leads_this_quarter').hide(); + $('#opp_this_quarter').hide(); + $('#exp_rev_this_quarter').hide(); + $('#rev_this_quarter').hide(); + $('#ratio_this_quarter').hide(); + $('#avg_time_this_quarter').hide(); + $('#total_revenue_this_quarter').hide(); + $('#leads_this_week').hide(); + $('#opp_this_week').hide(); + $('#exp_rev_this_week').hide(); + $('#rev_this_week').hide(); + $('#ratio_this_week').hide(); + $('#avg_time_this_week').hide(); + $('#total_revenue_this_week').hide(); + $('#leads_this_month').show(); + $('#opp_this_month').show(); + $('#exp_rev_this_month').show(); + $('#rev_this_month').show(); + $('#ratio_this_month').show(); + $('#avg_time_this_month').show(); + $('#total_revenue_this_month').show(); + $('#leads_this_month').empty(); + $('#opp_this_month').empty(); + $('#exp_rev_this_month').empty(); + $('#rev_this_month').empty(); + $('#ratio_this_month').empty(); + $('#avg_time_this_month').empty(); + $('#total_revenue_this_month').empty(); + $('#leads_this_month').append('' + result.record + ''); + $('#opp_this_month').append('' + result.record_op + ''); + $('#exp_rev_this_month').append('' + self.monthly_goals[2] + ' ' + result.record_rev_exp + ''); + $('#rev_this_month').append('' + self.monthly_goals[2] + ' ' + result.record_rev + ''); + $('#ratio_this_month').append('' + result.record_ratio + ''); + $('#avg_time_this_month').append('' + result.avg_time + ' sec' + ''); + $('#total_revenue_this_month').append('' + result.opportunity_ratio_value + ''); + }) + } + /** + * Handles the click event for the 'this_quarter' option. + * Calls a JSON-RPC method to retrieve data for the current quarter. + * Updates the UI to display the relevant information. + * @param {Event} ev - The event object. + */ + onclick_this_quarter(ev) { + var self = this; + jsonrpc('/web/dataset/call_kw/crm.lead/crm_quarter', { + model: 'crm.lead', + method: 'crm_quarter', + args: [{}], + kwargs: {}, + }) + .then(function(result) { + $('#leads_this_year').hide(); + $('#opp_this_year').hide(); + $('#exp_rev_this_year').hide(); + $('#rev_this_year').hide(); + $('#ratio_this_year').hide(); + $('#avg_time_this_year').hide(); + $('#total_revenue_this_year').hide(); + $('#leads_this_month').hide(); + $('#opp_this_month').hide(); + $('#exp_rev_this_month').hide(); + $('#rev_this_month').hide(); + $('#ratio_this_month').hide(); + $('#avg_time_this_month').hide(); + $('#total_revenue_this_month').hide(); + $('#leads_this_week').hide(); + $('#opp_this_week').hide(); + $('#exp_rev_this_week').hide(); + $('#rev_this_week').hide(); + $('#ratio_this_week').hide(); + $('#avg_time_this_week').hide(); + $('#total_revenue_this_week').hide(); + $('#leads_this_quarter').show(); + $('#opp_this_quarter').show(); + $('#exp_rev_this_quarter').show(); + $('#rev_this_quarter').show(); + $('#ratio_this_quarter').show(); + $('#avg_time_this_quarter').show(); + $('#total_revenue_this_quarter').show(); + $('#leads_this_quarter').empty(); + $('#opp_this_quarter').empty(); + $('#exp_rev_this_quarter').empty(); + $('#rev_this_quarter').empty(); + $('#ratio_this_quarter').empty(); + $('#avg_time_this_quarter').empty(); + $('#total_revenue_this_quarter').empty(); + $('#leads_this_quarter').append('' + result.record + ''); + $('#opp_this_quarter').append('' + result.record_op + ''); + $('#exp_rev_this_quarter').append('' + self.monthly_goals[2] + ' ' + result.record_rev_exp + ''); + $('#rev_this_quarter').append('' + self.monthly_goals[2] + ' ' + result.record_rev + ''); + $('#ratio_this_quarter').append('' + result.record_ratio + ''); + $('#avg_time_this_quarter').append('' + result.avg_time + ' sec' + ''); + $('#total_revenue_this_quarter').append('' + result.opportunity_ratio_value + ''); + }) + } + /** + * Handles the click event for the 'this_year' option. + * Calls a JSON-RPC method to retrieve data for the current year. + * Updates the UI to display the relevant information. + * @param {Event} ev - The event object. + */ + onclick_this_year(ev) { + var self = this; + jsonrpc('/web/dataset/call_kw/crm.lead/crm_year', { + model: 'crm.lead', + method: 'crm_year', + args: [{}], + kwargs: {}, + }) + .then(function(result) { + $('#leads_this_quarter').hide(); + $('#opp_this_quarter').hide(); + $('#exp_rev_this_quarter').hide(); + $('#rev_this_quarter').hide(); + $('#ratio_this_quarter').hide(); + $('#avg_time_this_quarter').hide(); + $('#total_revenue_this_quarter').hide(); + $('#leads_this_month').hide(); + $('#opp_this_month').hide(); + $('#exp_rev_this_month').hide(); + $('#rev_this_month').hide(); + $('#ratio_this_month').hide(); + $('#avg_time_this_month').hide(); + $('#total_revenue_this_month').hide(); + $('#leads_this_week').hide(); + $('#opp_this_week').hide(); + $('#exp_rev_this_week').hide(); + $('#rev_this_week').hide(); + $('#ratio_this_week').hide(); + $('#avg_time_this_week').hide(); + $('#total_revenue_this_week').hide(); + $('#leads_this_year').show(); + $('#opp_this_year').show(); + $('#exp_rev_this_year').show(); + $('#rev_this_year').show(); + $('#ratio_this_year').show(); + $('#avg_time_this_year').show(); + $('#total_revenue_this_year').show(); + $('#leads_this_year').empty(); + $('#opp_this_year').empty(); + $('#exp_rev_this_year').empty(); + $('#rev_this_year').empty(); + $('#ratio_this_year').empty(); + $('#avg_time_this_year').empty(); + $('#total_revenue_this_year').empty(); + $('#leads_this_year').append('' + result.record + ''); + $('#opp_this_year').append('' + result.record_op + ''); + $('#exp_rev_this_year').append('' + self.monthly_goals[2] + ' ' + result.record_rev_exp + ''); + $('#rev_this_year').append('' + self.monthly_goals[2] + ' ' + result.record_rev + ''); + $('#ratio_this_year').append('' + result.record_ratio + ''); + $('#avg_time_this_year').append('' + result.avg_time + ' sec' + ''); + $('#total_revenue_this_year').append('' + result.opportunity_ratio_value + ''); + }) + } + /** + * Handles the revenue card action. + * Initiates an action to display revenue-related data. + * @param {Event} e - The event object. + */ + revenue_card(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.action.doAction({ + name: _t("Revenue"), + type: 'ir.actions.act_window', + res_model: 'crm.lead', + view_mode: 'tree,form,calendar', + views: [ + [false, 'list'], + [false, 'form'] + ], + domain: [ + ['user_id', '=', session.uid], + ['type', '=', 'opportunity'], + ['stage_id', '=', 4] + ], + target: 'current', + }, options) + } + /** + * Initiates an action to display expected revenue data. + * @param {Event} e - The event object. + */ + exp_revenue(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.action.doAction({ + name: _t("Expected Revenue"), + type: 'ir.actions.act_window', + res_model: 'crm.lead', + view_mode: 'tree,form,calendar', + views: [ + [false, 'list'], + [false, 'form'] + ], + domain: [ + ['user_id', '=', session.uid], + ['type', '=', 'opportunity'], + ['active', '=', true] + ], + target: 'current', + }, options) + } + /** + * Initiates an action to display unassigned leads data. + * @param {Event} e - The event object. + */ + unassigned_leads(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.action.doAction({ + name: _t("Unassigned Leads"), + type: 'ir.actions.act_window', + res_model: 'crm.lead', + view_mode: 'tree,form,calendar', + views: [ + [false, 'list'], + [false, 'form'] + ], + domain: [ + ['user_id', '=', false], + ['type', '=', 'lead'] + ], + context: { + 'group_by': 'team_id' + }, + target: 'current', + }, options) + } + /** + * Initiates an action to display opportunity data. + * @param {Event} e - The event object. + */ + opportunity(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.action.doAction({ + name: _t("Opportunity"), + type: 'ir.actions.act_window', + res_model: 'crm.lead', + view_mode: 'tree,form,calendar', + views: [ + [false, 'list'], + [false, 'form'] + ], + domain: [ + ['user_id', '=', session.uid], + ['type', '=', 'opportunity'] + ], + target: 'current', + }) + } + /** + * Initiates an action to display leads assigned to the current user. + * @param {Event} e - The event object. + */ + my_lead(e) { + var self = this; + e.stopPropagation(); + e.preventDefault(); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.action.doAction({ + name: _t("My Leads"), + type: 'ir.actions.act_window', + res_model: 'crm.lead', + view_mode: 'tree,form,calendar', + views: [ + [false, 'list'], + [false, 'form'] + ], + domain: [ + ['user_id', '=', session.uid] + ], + target: 'current', + }, options) + } + /** + * Handles the reverse breadcrumb action. + * Updates the breadcrumb, fetches updated data, and reloads the dashboard. + */ + on_reverse_breadcrumb() { + var self = this; + WebClient.do_push_state({}); + this.update_cp(); + this.fetch_data().then(function() { + self.$('.o_hr_dashboard').reload(); + self.render_dashboards(); + }); + } + /** + * Lifecycle hook triggered when the component is mounted. + * Renders various charts and graphs upon mounting. + */ + async onMounted() { + this.renderElement(); + this.funnel_chart(); + this.render_annual_chart_graph(); + this.render_sales_activity_graph(); + this.render_leads_month_graph(); + this.render_revenue_count_pie(); + this.render_campaign_leads_graph(); + this.render_medium_leads_graph(); + this.render_source_leads_graph(); + this.onclick_lost_last_12months(); + this.render_lost_leads_by_stage_graph(); + } + /** + * Renders a doughnut chart to display lost leads categorized by stage. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_lost_leads_by_stage_graph() { + var self = this + var ctx = $(".lost_leads_by_stage_graph"); + jsonrpc('/web/dataset/call_kw/crm.lead/get_lost_lead_by_stage_pie', { + model: "crm.lead", + method: "get_lost_lead_by_stage_pie", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + legend: { + display: true, + position: "bottom", + labels: { + fontColor: "#333", + fontSize: 16 + } + }, + scales: { + yAxes: [{ + gridLines: { + color: "rgba(0, 0, 0, 0)", + display: false, + }, + ticks: { + min: 0, + display: false, + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "doughnut", + data: data, + options: options + }); + }); + } + /** + * Handles the change event for the total lost CRM selection. + * Determines the selected timeframe and triggers the respective method. + * @param {Event} e - The event object. + */ + change_total_loosed_crm(e) { + e.stopPropagation(); + var $target = $(e.target); + var value = $target.val(); + if (value == "lost_last_12months") { + this.onclick_lost_last_12months($target.val()); + } else if (value == "lost_last_6months") { + this.onclick_lost_last_6months($target.val()); + } else if (value == "lost_last_month") { + this.onclick_lost_last_month($target.val()); + } + } + /** + * Handles the click event for the 'lost_last_month' option. + * Calls a JSON-RPC method to retrieve total lost CRM data for the last month. + * Generates a bar chart to represent the lost CRM data. + * @param {Event} ev - The event object. + */ + onclick_lost_last_month(ev) { + var self = this; + self.initial_render = true; + jsonrpc('/web/dataset/call_kw/crm.lead/get_total_lost_crm', { + model: "crm.lead", + method: "get_total_lost_crm", + args: ['1'], + kwargs: {}, + }).then(function(result) { + var ctx = document.getElementById("canvas").getContext('2d'); + // Define the data + var lost_reason = result.month // Add data values to array + var count = result.count; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: lost_reason, //x axis + datasets: [{ + label: 'Count', // Name the series + data: count, // Specify the data values array + backgroundColor: '#66aecf', + borderColor: '#66aecf', + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, // Specify bar border width + type: 'bar', // Set this data to a line chart + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } + }, + responsive: true, // Instruct chart js to respond nicely. + maintainAspectRatio: false, // Add to prevent default behaviour of full-width/height + } + }); + }); + } + /** + * Handles the click event for displaying total lost CRM data for the last 6 months. + * Retrieves and displays the data using Chart.js in a bar chart format. + * @param {Event} ev - The event object. + */ + onclick_lost_last_6months(ev) { + var self = this; + self.initial_render = true; + jsonrpc('/web/dataset/call_kw/crm.lead/get_total_lost_crm', { + model: "crm.lead", + method: "get_total_lost_crm", + args: ['6'], + kwargs: {}, + }).then(function(result) { + var ctx = document.getElementById("canvas").getContext('2d'); + // Define the data + var lost_reason = result.month // Add data values to array + var count = result.count; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: lost_reason, //x axis + datasets: [{ + label: 'Count', // Name the series + data: count, // Specify the data values array + backgroundColor: '#66aecf', + borderColor: '#66aecf', + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, // Specify bar border width + type: 'bar', // Set this data to a line chart + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } + }, + responsive: true, // Instruct chart js to respond nicely. + maintainAspectRatio: false, // Add to prevent default behaviour of full-width/height + } + }); + }); + } + /** + * Handles the click event for displaying total lost CRM data for the last 12 months. + * Retrieves and displays the data using Chart.js in a bar chart format. + * If the user is a manager, it fetches data for the last 12 months. + * @param {Event} ev - The event object. + */ + onclick_lost_last_12months(ev) { + var self = this; + if (self.is_manager == true) { + self.initial_render = true; + jsonrpc('/web/dataset/call_kw/crm.lead/get_total_lost_crm', { + model: "crm.lead", + method: "get_total_lost_crm", + args: ['12'], + kwargs: {}, + }).then(function(result) { + var ctx = document.getElementById("canvas").getContext('2d'); + // Define the data + var lost_reason = result.month // Add data values to array + var count = result.count; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: lost_reason, //x axis + datasets: [{ + label: 'Count', // Name the series + data: count, // Specify the data values array + backgroundColor: '#66aecf', + borderColor: '#66aecf', + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, // Specify bar border width + type: 'bar', // Set this data to a line chart + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, // Instruct chart js to respond nicely. + maintainAspectRatio: false, // Add to prevent default behaviour of full-width/height + } + }); + }); + }; + } + /** + * Renders a doughnut chart to display lost leads categorized by reason. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_lost_leads_graph() { + var self = this; + var ctx = $(".lost_leads_graph"); + console.log(ctx) + jsonrpc('/web/dataset/call_kw/crm.lead/get_lost_lead_by_reason_pie', { + model: "crm.lead", + method: "get_lost_lead_by_reason_pie", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + legend: { + display: true, + position: "bottom", + labels: { + fontColor: "#333", + fontSize: 16 + } + }, + scales: { + yAxes: [{ + gridLines: { + color: "rgba(0, 0, 0, 0)", + display: false, + }, + ticks: { + min: 0, + display: false, + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "doughnut", + data: data, + options: options + }); + }); + } + /** + * Renders a doughnut chart to display leads categorized by their source. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_source_leads_graph() { + var self = this + var ctx = $(".source_lead"); + jsonrpc('/web/dataset/call_kw/crm.lead/get_the_source_pie', { + model: "crm.lead", + method: "get_the_source_pie", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + legend: { + display: true, + position: "right", + labels: { + fontColor: "#333", + fontSize: 14 + } + }, + scales: { + yAxes: [{ + gridLines: { + color: "rgba(0, 0, 0, 0)", + display: false, + }, + ticks: { + min: 0, + display: false, + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "doughnut", + data: data, + options: options + }); + }); + } + /** + * Renders a doughnut chart to display leads categorized by their medium. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_medium_leads_graph() { + var self = this + var ctx = $(".medium_leads"); + jsonrpc('/web/dataset/call_kw/crm.lead/get_the_medium_pie', { + model: "crm.lead", + method: "get_the_medium_pie", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + legend: { + display: true, + position: "right", + labels: { + fontColor: "#333", + fontSize: 14 + } + }, + scales: { + yAxes: [{ + gridLines: { + color: "rgba(0, 0, 0, 0)", + display: false, + }, + ticks: { + min: 0, + display: false, + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "doughnut", + data: data, + options: options + }); + }); + } + /** + * Renders a doughnut chart to display leads categorized by their campaign source. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_campaign_leads_graph() { + var self = this + var ctx = $(".campaign_source"); + jsonrpc('/web/dataset/call_kw/crm.lead/get_the_campaign_pie', { + model: "crm.lead", + method: "get_the_campaign_pie", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + legend: { + display: true, + position: "bottom", + labels: { + fontColor: "#333", + fontSize: 14 + } + }, + scales: { + yAxes: [{ + gridLines: { + color: "rgba(0, 0, 0, 0)", + display: false, + }, + ticks: { + min: 0, + display: false, + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "doughnut", + data: data, + options: options + }); + }); + } + /** + * Renders a doughnut chart to display revenue count data. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_revenue_count_pie() { + var self = this; + var ctx = $(".revenue_count_pie_canvas"); + jsonrpc('/web/dataset/call_kw/crm.lead/revenue_count_pie', { + model: "crm.lead", + method: "revenue_count_pie", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#ff7c43", + "#f95d6a" + ], + borderColor: [ + "#003f5c", + "#ff7c43", + "#f95d6a" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + legend: { + display: true, + position: "bottom", + labels: { + fontColor: "#333", + fontSize: 16 + } + }, + scales: { + yAxes: [{ + gridLines: { + color: "rgba(0, 0, 0, 0)", + display: false, + }, + ticks: { + min: 0, + display: false, + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "doughnut", + data: data, + options: options + }); + }); + } + /** + * Renders a doughnut chart to display leads created each month. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_leads_month_graph() { + var self = this + var ctx = $(".lead_month"); + jsonrpc('/web/dataset/call_kw/crm.lead/get_lead_month_pie', { + model: "crm.lead", + method: "get_lead_month_pie", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + legend: { + display: true, + position: "right", + labels: { + fontColor: "#333", + fontSize: 16 + } + }, + scales: { + yAxes: [{ + gridLines: { + color: "rgba(0, 0, 0, 0)", + display: false, + }, + ticks: { + min: 0, + display: false, + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "doughnut", + data: data, + options: options + }); + }); + } + /** + * Renders a doughnut chart to display sales activity data. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_sales_activity_graph() { + var self = this + var ctx = $(".sales_activity"); + jsonrpc('/web/dataset/call_kw/crm.lead/get_the_sales_activity', { + model: "crm.lead", + method: "get_the_sales_activity", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderColor: [ + "#003f5c", + "#2f4b7c", + "#f95d6a", + "#665191", + "#d45087", + "#ff7c43", + "#ffa600", + "#a05195", + "#6d5c16" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + legend: { + display: true, + position: "right", + labels: { + fontColor: "#333", + fontSize: 16 + } + }, + scales: { + yAxes: [{ + gridLines: { + color: "rgba(0, 0, 0, 0)", + display: false, + }, + ticks: { + min: 0, + display: false, + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "doughnut", + data: data, + options: options + }); + }); + } + /** + * Renders a bar chart to display annual target data. + * Fetches data via JSON-RPC and generates a chart using Chart.js. + */ + render_annual_chart_graph() { + var self = this + var ctx = $(".annual_target"); + jsonrpc('/web/dataset/call_kw/crm.lead/get_the_annual_target', { + model: "crm.lead", + method: "get_the_annual_target", + args: [{}], + kwargs: {}, + }).then(function(arrays) { + var data = { + labels: arrays[1], + datasets: [{ + label: "", + data: arrays[0], + backgroundColor: [ + "#003f5c", + "#f95d6a", + "#ff7c43", + "#6d5c16" + ], + borderColor: [ + "#003f5c", + "#f95d6a", + "#ff7c43", + "#6d5c16" + ], + borderWidth: 1 + }, ] + }; + //options + var options = { + responsive: true, + title: false, + scales: { + yAxes: [{ + ticks: { + min: 0 + } + }] + } + }; + //create Chart class object + var chart = new Chart(ctx, { + type: "bar", + data: data, + options: { + responsive: true, + maintainAspectRatio: false, + legend: { + display: false //This will do the task + }, + } + }); + }); + } + /** + * Renders a funnel chart displaying lead stage data. + * Fetches data via JSON-RPC and generates a chart using Highcharts. + */ + funnel_chart() { + jsonrpc('/web/dataset/call_kw/crm.lead/get_lead_stage_data', { + model: "crm.lead", + method: "get_lead_stage_data", + args: [{}], + kwargs: {}, + }).then(function(callbacks) { + Highcharts.chart("container", { + chart: { + type: "funnel", + }, + title: false, + credits: { + enabled: false + }, + plotOptions: { + series: { + dataLabels: { + enabled: true, + softConnector: true + }, + center: ['45%', '50%'], + neckWidth: '40%', + neckHeight: '35%', + width: '90%', + height: '80%' + } + }, + series: [{ + name: "Number Of Leads", + data: callbacks, + }], + }); + }); + } + /** + * Fetches lead details via JSON-RPC and appends the data to specific HTML elements. + */ + renderElement(ev) { + var self = this; + jsonrpc('/web/dataset/call_kw/crm.lead/lead_details_user', { + model: "crm.lead", + method: "lead_details_user", + args: [{}], + kwargs: {}, + }).then(function(result) { + $('#leads_this_month').append('' + result.record + ''); + $('#opp_this_month').append('' + result.record_op + ''); + $('#exp_rev_this_month').append('' + self.monthly_goals[2] + ' ' + result.record_rev_exp + ''); + $('#rev_this_month').append('' + self.monthly_goals[2] + ' ' + result.record_rev + ''); + $('#ratio_this_month').append('' + result.record_ratio + ''); + $('#avg_time_this_month').append('' + result.avg_time + ' sec' + ''); + $('#total_revenue_this_month').append('' + result.opportunity_ratio_value + ''); + $('#target').append('' + result.target + ''); + $('#ytd_target').append('' + result.ytd_target + ''); + $('#difference').append('' + result.difference + ''); + $('#won').append('' + result.won + ''); + }) + } +} +CRMDashboard.template = "CRMdashboard" +registry.category("actions").add("crm_dashboard", CRMDashboard) diff --git a/crm_dashboard/static/src/xml/dashboard_templates.xml b/crm_dashboard/static/src/xml/dashboard_templates.xml new file mode 100644 index 000000000..1d912d2a5 --- /dev/null +++ b/crm_dashboard/static/src/xml/dashboard_templates.xml @@ -0,0 +1,642 @@ + + + + +
+
+ + + +
+
+
+ + +
+
+
+
+
+

CRM Dashboard +

+
+
+
+ +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+ +
+
+

+ +
+
+
+
+ +

+

My Leads

+
+
+
+
+
+
+ +
+
+

+ +
+
+
+
+ +

+

My Opportunities

+
+
+
+
+
+
+ +
+
+

+ +
+
+
+
+ +

+

Expected Revenue

+
+
+
+
+
+
+ +
+
+

+ +
+
+
+
+ +

+

Revenue

+
+
+
+
+
+
+ +
+
+

+ +
+
+
+
+ +

+

Win Ratio

+
+
+
+
+
+
+ +
+
+

+ +
+
+
+
+ +

+

Average Closing Time

+
+
+
+
+
+
+ +
+
+

+ +
+
+
+
+ +

+

Opportunity Win Loss Ratio

+
+
+
+
+
+
+ +
+
+

+ + + +

+

Unassigned Leads Count

+
+
+
+
+
+
+
+

Funnel Chart

+
+
+
+
+
+
+
+
+

Year to Date

+
+
+ +
+
+
Annual Target : +
+
+
YtD Target : +
+
+
Achieved Won : +
+
+
Difference : +
+
+
+
+
+ +
+
+

Leads by Month

+
+
+ +
+
+
+

CRM Activity

+
+
+ +
+
+
+
+
+
+
+

Upcoming Activities

+
+
+
+
+ +
+
+ +
+
+
+
    +
  • + + + Activity: + + + + +
  • +
  • + Name: + + + +
  • +
  • + Summary: + + + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+

Total Revenue by Salesperson

+
+
+ +
+
+
+
+
+
+ + +
+
+
+
+

Top Deals

+
+
+
+ +
+
+ +
+
+

+ . + + +

+
+
    +
  • + Sales Person: + + ,Team: + +
  • +
+
+
+
+
+
+
+
+
+
+
+

Monthly Goal

+
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + +
+
+
+
+

Leads group by Campaign

+
+
+ +
+
+
+
+
+

Leads group by Medium

+
+
+ +
+
+
+

Leads group by Source

+
+
+ +
+
+
+
+
+
+
+

Top Salesperson Revenue

+
+ + + + + + + + + + + + + + + +
OpportunityRevenue
+ + + + +
+
+
+
+
+

Top Country Wise Revenue

+
+ + + + + + + + + + + + + + + +
CountryRevenue
+ + + + +
+
+
+
+
+

Top Country Wise Count

+
+ + + + + + + + + + + + + + + +
CountryCount
+ + + +
+
+
+
+
+
+
+

Recent Activities

+
+
+
+
+ +
+
+ +
+
+
+
    +
  • + + + Activity: + + + + +
  • +
  • Name: + + + +
  • +
  • + Summary: + + + +
  • +
  • + Sales Rep.: + +
  • +
+
+
+
+
+
+
+
+
+
+
+
+

Lost Opportunity/Lead Graph

+
+
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+

Top Sales Person by Invoice

+
+
+
+ +
+
+ +
+
+

+

+
+
    +
  • +

    + + +

    +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/crm_dashboard/views/crm_team_views.xml b/crm_dashboard/views/crm_team_views.xml new file mode 100644 index 000000000..712cfb2fb --- /dev/null +++ b/crm_dashboard/views/crm_team_views.xml @@ -0,0 +1,23 @@ + + + + + crm.team.view.form.inherit.crm.dashboard + crm.team + + + + + + + + + + CRM + crm_dashboard + + + + diff --git a/crm_dashboard/views/res_users_views.xml b/crm_dashboard/views/res_users_views.xml new file mode 100644 index 000000000..9e2120ecd --- /dev/null +++ b/crm_dashboard/views/res_users_views.xml @@ -0,0 +1,18 @@ + + + + + res.users.view.form.inherit.crm.dashboard + res.users + + + + + + + + + + diff --git a/crm_dashboard/views/utm_campaign_views.xml b/crm_dashboard/views/utm_campaign_views.xml new file mode 100644 index 000000000..99664b547 --- /dev/null +++ b/crm_dashboard/views/utm_campaign_views.xml @@ -0,0 +1,18 @@ + + + + + utm.campaign.view.form.inherit.crm.dashboard + utm.campaign + + +
+ +
+
+
+