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
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							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}
 | |
| 
 |