You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

438 lines
18 KiB

################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Raneesha (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
import calendar
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.tools.safe_eval import datetime
def get_period_start_date(period):
"""Returns the start date for the given period"""
today = datetime.datetime.now()
if period == 'month':
start_date = today.replace(day=1)
elif period == 'quarter':
current_month = today.month
start_month = ((current_month - 1) // 3) * 3 + 1
start_date = today.replace(month=start_month, day=1)
elif period == 'year':
start_date = today.replace(month=1, day=1)
elif period == 'week':
start_date = today - datetime.timedelta(days=today.weekday())
else:
raise ValueError("Invalid period specified")
return start_date.date()
class CRMLead(models.Model):
"""Extends crm.lead for adding more functions in it"""
_inherit = 'crm.lead'
@api.model
def get_data(self, period):
"""Returns data to the dashboard tiles"""
period_days = get_period_start_date(period)
crm_model = self.search([('create_date', '>=', period_days)])
lead_count = 0
opportunity_count = 0
win_count = 0
active_lead_count = 0
active_opportunity_count = 0
won_opportunity_count = 0
total_seconds = 0
expected_revenue = 0
revenue = 0
unassigned_leads = 0
for record in crm_model:
if record.type == 'lead':
lead_count += 1
if not record.user_id:
unassigned_leads += 1
if record.type == 'opportunity':
opportunity_count += 1
expected_revenue += record.expected_revenue
if record.active:
if record.probability == 0:
active_opportunity_count += 1
elif record.probability == 100:
won_opportunity_count += 1
if record.stage_id.is_won:
revenue += record.expected_revenue
if record.active:
if record.probability == 0:
active_lead_count += 1
elif record.probability == 100:
win_count += 1
if record.date_conversion:
total_seconds += (
record.date_conversion - record.create_date).seconds
win_ratio = win_count / active_lead_count if active_lead_count else 0
opportunity_ratio = won_opportunity_count / active_opportunity_count if active_opportunity_count else 0
avg_close_time = round(total_seconds / len(crm_model.filtered(
lambda l: l.date_conversion))) if total_seconds else 0
return {
'leads': lead_count,
'opportunities': opportunity_count,
'exp_revenue': expected_revenue,
'revenue': revenue,
'win_ratio': win_ratio,
'opportunity_ratio': opportunity_ratio,
'avg_close_time': avg_close_time,
'unassigned_leads': unassigned_leads,
}
@api.model
def get_lead_stage_data(self, period):
"""funnel chart"""
period_days = get_period_start_date(period)
crm_model = self.search([('create_date', '>=', period_days)])
stage_lead_count = {}
for lead in crm_model:
stage_name = lead.stage_id.name
if stage_name in stage_lead_count:
stage_lead_count[stage_name] += 1
else:
stage_lead_count[stage_name] = 1
# Convert the dictionary into lists for stages and their counts
crm_stages = list(stage_lead_count.keys())
lead_count = list(stage_lead_count.values())
# Return the data in the expected format
return [lead_count, crm_stages]
@api.model
def get_lead_by_month(self):
"""pie chart"""
month_count = []
month_value = []
for rec in self.search([]):
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_crm_activities(self, period):
"""Sales Activity Pie"""
start_date = get_period_start_date(period)
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
INNER JOIN crm_lead
ON mail_activity.res_id = crm_lead.id
AND mail_activity.res_model = 'crm.lead'
WHERE crm_lead.create_date >= %s
GROUP BY mail_activity_type.name
''', (start_date,))
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_campaign_pie(self, period):
"""Leads Group By Campaign Pie"""
start_date = get_period_start_date(period)
self._cr.execute('''SELECT campaign_id, COUNT(*),
(SELECT name FROM utm_campaign
WHERE utm_campaign.id = crm_lead.campaign_id)
FROM crm_lead WHERE create_date >= %s AND campaign_id IS NOT NULL GROUP BY
campaign_id''', (start_date,))
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, period):
"""Leads Group By Source Pie"""
start_date = get_period_start_date(period)
self._cr.execute('''SELECT source_id, COUNT(*),
(SELECT name FROM utm_source
WHERE utm_source.id = crm_lead.source_id)
FROM crm_lead WHERE create_date >= %s AND source_id IS NOT NULL GROUP BY
source_id''', (start_date,))
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, period):
"""Leads Group By Medium Pie"""
start_date = get_period_start_date(period)
self._cr.execute('''SELECT medium_id, COUNT(*),
(SELECT name FROM utm_medium
WHERE utm_medium.id = crm_lead.medium_id)
FROM crm_lead WHERE create_date >= %s AND medium_id IS NOT NULL GROUP BY medium_id''',
(start_date,))
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_total_lost_crm(self, period):
"""Lost Opportunity or Lead Graph"""
month_dict = {}
# Format the start date to be used in the SQL query
start_date = get_period_start_date(period)
if period == 'year':
num_months = 12
elif period == 'quarter':
num_months = 3
else:
num_months = 1
# Initialize the dictionary with month names and counts
for i in range(num_months):
current_month = start_date + relativedelta(months=i)
month_name = current_month.strftime('%B')
month_dict[month_name] = 0
# Execute the SQL query to count lost opportunities
self._cr.execute('''SELECT TO_CHAR(create_date, 'Month') AS month,
COUNT(id)
FROM crm_lead
WHERE probability = 0
AND active = FALSE
AND create_date >= %s
GROUP BY TO_CHAR(create_date, 'Month')
ORDER BY TO_CHAR(create_date, 'Month')''',
(start_date,))
data = self._cr.dictfetchall()
# Update month_dict with the results from the query
for rec in data:
month_name = rec[
'month'].strip() # Strip the month name to remove extra spaces
if month_name in month_dict:
month_dict[month_name] = rec['count']
result = {
'month': list(month_dict.keys()),
'count': list(month_dict.values())
}
return result
@api.model
def get_upcoming_events(self):
"""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 total_revenue_by_sales(self, period):
"""Total expected revenue and count Pie"""
session_user_id = self.env.uid
start_date = get_period_start_date(period)
# SQL query template
query_template = """
SELECT sum(expected_revenue) as revenue
FROM crm_lead
WHERE user_id = %s
AND type = 'opportunity'
AND active = %s
{conditions}
"""
# Query conditions for different cases
conditions = [
"", # Active opportunities
"AND stage_id = '4'", # Won opportunities
"AND probability = '0'", # Lost opportunities
]
# Active status for each condition
active_status = ['true', 'false', 'false']
# Fetch total revenue for each condition
revenues = []
for cond, active in zip(conditions, active_status):
self._cr.execute(query_template.format(conditions=cond),
(session_user_id, active))
revenue = self._cr.fetchone()[0] or 0
revenues.append(revenue)
# Calculate expected revenue without won
exp_revenue_without_won = revenues[0] - revenues[1]
# Prepare the data for the pie chart
revenue_pie_count = [exp_revenue_without_won, revenues[1], revenues[2]]
revenue_pie_title = ['Expected without Won', 'Won', 'Lost']
return [revenue_pie_count, revenue_pie_title]
@api.model
def get_top_sp_revenue(self,period):
"""Top 10 Salesperson revenue Table"""
user = self.env.user
start_date = get_period_start_date(period)
self._cr.execute('''SELECT user_id, id, expected_revenue, name, company_id
FROM crm_lead
WHERE create_date >= '%s' AND expected_revenue IS NOT NULL AND user_id = %s
GROUP BY user_id, id
ORDER BY expected_revenue DESC
LIMIT 10''' % (start_date,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_top_country_revenue(self, period):
"""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_top_country_count(self, period):
"""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_recent_activities(self, kwargs):
"""Recent Activities Table"""
today = fields.Date.today()
recent_week = today - relativedelta(days=7)
current_user_id = self.env.user.id # Get the current logged-in user's ID
# Check if the current user is an administrator
is_admin = self.env.user.has_group('base.group_system')
# Build the SQL query with or without user filtering based on role
if is_admin:
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))
else:
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
AND mail_activity.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 DESC
''', (recent_week, today, current_user_id))
data = self._cr.fetchall()
activities = [
[*record[:5], self.env['res.users'].browse(record[5]).name] for
record in data]
return {'activities': activities}