feat(analytics): comply erpnext with utm methodology

This commit is contained in:
David
2024-06-29 00:45:39 +02:00
parent 7a21574cfb
commit 96a6673510
46 changed files with 631 additions and 416 deletions

View File

@@ -8,18 +8,19 @@
"document_type": "Lead",
"dynamic_filters_json": "[[\"Lead\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[]",
"group_by_based_on": "source",
"group_by_based_on": "utm_source",
"group_by_type": "Count",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"last_synced_on": "2020-07-22 16:11:14.170636",
"modified": "2020-07-22 16:13:38.696710",
"last_synced_on": "2023-10-09 17:32:34.504416",
"modified": "2024-06-28 12:18:19.477467",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead Source",
"number_of_groups": 0,
"owner": "Administrator",
"roles": [],
"timeseries": 0,
"type": "Donut",
"use_report_chart": 0,

View File

@@ -8,18 +8,19 @@
"document_type": "Opportunity",
"dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[]",
"group_by_based_on": "campaign",
"group_by_based_on": "utm_campaign",
"group_by_type": "Count",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"last_synced_on": "2020-07-22 15:45:32.572011",
"modified": "2020-07-22 16:10:02.497726",
"last_synced_on": "2024-06-28 12:17:20.081440",
"modified": "2024-06-28 12:17:53.934536",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunities via Campaigns",
"number_of_groups": 0,
"owner": "Administrator",
"roles": [],
"timeseries": 0,
"type": "Pie",
"use_report_chart": 0,

View File

@@ -14,7 +14,7 @@ frappe.ui.form.on("Campaign", {
frm.add_custom_button(
__("View Leads"),
function () {
frappe.route_options = { source: "Campaign", campaign_name: frm.doc.name };
frappe.route_options = { utm_source: "Campaign", utm_campaign: frm.doc.name };
frappe.set_route("List", "Lead");
},
"fa fa-list",

View File

@@ -25,6 +25,26 @@ class Campaign(Document):
naming_series: DF.Literal["SAL-CAM-.YYYY.-"]
# end: auto-generated types
def after_insert(self):
try:
mc = frappe.get_doc("UTM Campaign", self.campaign_name)
except frappe.DoesNotExistError:
mc = frappe.new_doc("UTM Campaign")
mc.name = self.campaign_name
mc.campaign_description = self.description
mc.crm_campaign = self.campaign_name
mc.save(ignore_permissions=True)
def on_change(self):
try:
mc = frappe.get_doc("UTM Campaign", self.campaign_name)
except frappe.DoesNotExistError:
mc = frappe.new_doc("UTM Campaign")
mc.name = self.campaign_name
mc.campaign_description = self.description
mc.crm_campaign = self.campaign_name
mc.save(ignore_permissions=True)
def autoname(self):
if frappe.defaults.get_global_default("campaign_naming_by") != "Naming Series":
self.name = self.campaign_name

View File

@@ -19,7 +19,6 @@
"lead_name",
"job_title",
"gender",
"source",
"col_break123",
"lead_owner",
"status",
@@ -53,13 +52,19 @@
"country",
"column_break2",
"contact_html",
"section_break_analytics",
"utm_source",
"utm_content",
"column_break_gkxo",
"utm_campaign",
"column_break_gqka",
"utm_medium",
"qualification_tab",
"qualification_status",
"column_break_64",
"qualified_by",
"qualified_on",
"other_info_tab",
"campaign_name",
"company",
"column_break_22",
"language",
@@ -160,14 +165,6 @@
"label": "Gender",
"options": "Gender"
},
{
"fieldname": "source",
"fieldtype": "Link",
"label": "Source",
"oldfieldname": "source",
"oldfieldtype": "Select",
"options": "Lead Source"
},
{
"depends_on": "eval:doc.source == 'Existing Customer'",
"fieldname": "customer",
@@ -178,14 +175,6 @@
"oldfieldtype": "Link",
"options": "Customer"
},
{
"fieldname": "campaign_name",
"fieldtype": "Link",
"label": "Campaign Name",
"oldfieldname": "campaign_name",
"oldfieldtype": "Link",
"options": "Campaign"
},
{
"fieldname": "image",
"fieldtype": "Attach Image",
@@ -510,13 +499,54 @@
"fieldtype": "Tab Break",
"label": "Connections",
"show_dashboard": 1
},
{
"fieldname": "column_break_gkxo",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_gqka",
"fieldtype": "Column Break"
},
{
"fieldname": "utm_content",
"print_hide": 1,
"fieldtype": "Data",
"label": "Content"
},
{
"fieldname": "utm_source",
"fieldtype": "Link",
"label": "Source",
"oldfieldname": "source",
"oldfieldtype": "Select",
"options": "UTM Source"
},
{
"fieldname": "utm_medium",
"fieldtype": "Link",
"label": "Medium",
"options": "UTM Medium"
},
{
"fieldname": "utm_campaign",
"fieldtype": "Link",
"label": "Campaign",
"oldfieldname": "campaign_name",
"oldfieldtype": "Link",
"options": "UTM Campaign"
},
{
"fieldname": "section_break_analytics",
"fieldtype": "Section Break",
"label": "Analytics"
}
],
"icon": "fa fa-user",
"idx": 5,
"image_field": "image",
"links": [],
"modified": "2024-03-27 13:09:59.818450",
"modified": "2024-06-28 10:29:32.676323",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
@@ -584,4 +614,4 @@
"states": [],
"subject_field": "title",
"title_field": "title"
}
}

View File

@@ -32,7 +32,6 @@ class Lead(SellingController, CRMNote):
annual_revenue: DF.Currency
blog_subscriber: DF.Check
campaign_name: DF.Link | None
city: DF.Data | None
company: DF.Link | None
company_name: DF.Data | None
@@ -63,7 +62,6 @@ class Lead(SellingController, CRMNote):
qualified_on: DF.Date | None
request_type: DF.Literal["", "Product Enquiry", "Request for Information", "Suggestions", "Other"]
salutation: DF.Link | None
source: DF.Link | None
state: DF.Data | None
status: DF.Literal[
"Lead",
@@ -80,6 +78,10 @@ class Lead(SellingController, CRMNote):
title: DF.Data | None
type: DF.Literal["", "Client", "Channel Partner", "Consultant"]
unsubscribed: DF.Check
utm_campaign: DF.Link | None
utm_content: DF.Data | None
utm_medium: DF.Link | None
utm_source: DF.Link | None
website: DF.Data | None
whatsapp_no: DF.Data | None
# end: auto-generated types
@@ -101,7 +103,7 @@ class Lead(SellingController, CRMNote):
def before_insert(self):
self.contact_doc = None
if frappe.db.get_single_value("CRM Settings", "auto_creation_of_contact"):
if self.source == "Existing Customer" and self.customer:
if self.utm_source == "Existing Customer" and self.customer:
contact = frappe.db.get_value(
"Dynamic Link",
{"link_doctype": "Customer", "parenttype": "Contact", "link_name": self.customer},
@@ -369,7 +371,6 @@ def make_opportunity(source_name, target_doc=None):
"Lead": {
"doctype": "Opportunity",
"field_map": {
"campaign_name": "campaign",
"doctype": "opportunity_from",
"name": "party_name",
"lead_name": "contact_display",

View File

@@ -1,7 +0,0 @@
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Lead Source", {
// refresh: function(frm) {
// }
});

View File

@@ -1,65 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:source_name",
"creation": "2016-09-16 01:47:47.382372",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"source_name",
"details"
],
"fields": [
{
"fieldname": "source_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Source Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "details",
"fieldtype": "Text Editor",
"label": "Details"
}
],
"links": [],
"modified": "2024-03-27 13:10:00.097850",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead Source",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"translated_doctype": 1
}

View File

@@ -1,22 +0,0 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class LeadSource(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
details: DF.TextEditor | None
source_name: DF.Data
# end: auto-generated types
pass

View File

@@ -1,9 +0,0 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
import unittest
class TestLeadSource(unittest.TestCase):
pass

View File

@@ -19,7 +19,6 @@
"status",
"column_break0",
"opportunity_type",
"source",
"opportunity_owner",
"column_break_10",
"sales_stage",
@@ -45,9 +44,15 @@
"column_break_17",
"opportunity_amount",
"base_opportunity_amount",
"section_break_analytics",
"utm_source",
"utm_content",
"column_break_emai",
"utm_campaign",
"column_break_whcu",
"utm_medium",
"more_info",
"company",
"campaign",
"transaction_date",
"column_break1",
"language",
@@ -317,24 +322,6 @@
"oldfieldtype": "Section Break",
"options": "fa fa-file-text"
},
{
"fieldname": "source",
"fieldtype": "Link",
"label": "Source",
"oldfieldname": "source",
"oldfieldtype": "Select",
"options": "Lead Source"
},
{
"depends_on": "eval: doc.source==\"Campaign\"",
"description": "Enter name of campaign if source of enquiry is campaign",
"fieldname": "campaign",
"fieldtype": "Link",
"label": "Campaign",
"oldfieldname": "campaign",
"oldfieldtype": "Link",
"options": "Campaign"
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
@@ -620,6 +607,50 @@
"fieldtype": "Link",
"label": "Country",
"options": "Country"
},
{
"fieldname": "column_break_emai",
"fieldtype": "Column Break"
},
{
"fieldname": "utm_source",
"print_hide": 1,
"fieldtype": "Link",
"label": "Source",
"oldfieldname": "source",
"oldfieldtype": "Select",
"options": "UTM Source"
},
{
"fieldname": "utm_campaign",
"print_hide": 1,
"fieldtype": "Link",
"label": "Campaign",
"oldfieldname": "campaign",
"oldfieldtype": "Link",
"options": "UTM Campaign"
},
{
"fieldname": "section_break_analytics",
"fieldtype": "Section Break",
"label": "Analytics"
},
{
"fieldname": "column_break_whcu",
"fieldtype": "Column Break"
},
{
"fieldname": "utm_medium",
"print_hide": 1,
"fieldtype": "Link",
"label": "Medium",
"options": "UTM Medium"
},
{
"fieldname": "utm_content",
"print_hide": 1,
"fieldtype": "Data",
"label": "Content"
}
],
"icon": "fa fa-info-sign",
@@ -668,4 +699,4 @@
"title_field": "title",
"track_seen": 1,
"track_views": 1
}
}

View File

@@ -45,7 +45,6 @@ class Opportunity(TransactionBase, CRMNote):
annual_revenue: DF.Currency
base_opportunity_amount: DF.Currency
base_total: DF.Currency
campaign: DF.Link | None
city: DF.Data | None
company: DF.Link
competitors: DF.TableMultiSelect[CompetitorDetail]
@@ -80,13 +79,16 @@ class Opportunity(TransactionBase, CRMNote):
phone_ext: DF.Data | None
probability: DF.Percent
sales_stage: DF.Link | None
source: DF.Link | None
state: DF.Data | None
status: DF.Literal["Open", "Quotation", "Converted", "Lost", "Replied", "Closed"]
territory: DF.Link | None
title: DF.Data | None
total: DF.Currency
transaction_date: DF.Date
utm_campaign: DF.Link | None
utm_content: DF.Data | None
utm_medium: DF.Link | None
utm_source: DF.Link | None
website: DF.Data | None
whatsapp: DF.Data | None
# end: auto-generated types

View File

@@ -9,8 +9,8 @@ from frappe.utils import flt
def execute(filters=None):
columns, data = [], []
columns = get_columns("Campaign Name")
data = get_lead_data(filters or {}, "Campaign Name")
columns = get_columns("utm_campaign")
data = get_lead_data(filters or {}, "utm_campaign")
return columns, data

View File

@@ -44,7 +44,7 @@ frappe.query_reports["Opportunity Summary by Sales Stage"] = {
fieldname: "opportunity_source",
label: __("Opportunity Source"),
fieldtype: "Link",
options: "Lead Source",
options: "UTM Source",
},
{
fieldname: "opportunity_type",

View File

@@ -36,9 +36,9 @@ class OpportunitySummaryBySalesStage:
self.columns.append(
{
"label": _("Source"),
"fieldname": "source",
"fieldname": "utm_source",
"fieldtype": "Link",
"options": "Lead Source",
"options": "UTM Source",
"width": 200,
}
)
@@ -69,7 +69,7 @@ class OpportunitySummaryBySalesStage:
based_on = {
"Opportunity Owner": "_assign",
"Source": "source",
"Source": "utm_source",
"Opportunity Type": "opportunity_type",
}[self.filters.get("based_on")]
@@ -128,7 +128,7 @@ class OpportunitySummaryBySalesStage:
for based_on, data in self.formatted_data.items():
row_based_on = {
"Opportunity Owner": "opportunity_owner",
"Source": "source",
"Source": "utm_source",
"Opportunity Type": "opportunity_type",
}[self.filters.get("based_on")]
@@ -148,7 +148,7 @@ class OpportunitySummaryBySalesStage:
based_on = {
"Opportunity Owner": "_assign",
"Source": "source",
"Source": "utm_source",
"Opportunity Type": "opportunity_type",
}[self.filters.get("based_on")]
@@ -188,7 +188,7 @@ class OpportunitySummaryBySalesStage:
filters.append({"opportunity_type": self.filters.get("opportunity_type")})
if self.filters.get("opportunity_source"):
filters.append({"source": self.filters.get("opportunity_source")})
filters.append({"utm_source": self.filters.get("opportunity_source")})
if self.filters.get("status"):
filters.append({"status": ("in", self.filters.get("status"))})

View File

@@ -40,7 +40,7 @@ class TestOpportunitySummaryBySalesStage(unittest.TestCase):
report = execute(filters)
expected_data = [{"source": "Cold Calling", "Prospecting": 1}]
expected_data = [{"utm_source": "Cold Calling", "Prospecting": 1}]
self.assertEqual(expected_data, report[1])

View File

@@ -57,7 +57,7 @@ frappe.query_reports["Sales Pipeline Analytics"] = {
fieldname: "opportunity_source",
label: __("Opportunity Source"),
fieldtype: "Link",
options: "Lead Source",
options: "UTM Source",
},
{
fieldname: "opportunity_type",

View File

@@ -142,7 +142,7 @@ class SalesPipelineAnalytics:
conditions = []
if self.filters.get("opportunity_source"):
conditions.append({"source": self.filters.get("opportunity_source")})
conditions.append({"utm_source": self.filters.get("opportunity_source")})
if self.filters.get("opportunity_type"):
conditions.append({"opportunity_type": self.filters.get("opportunity_type")})

View File

@@ -207,7 +207,7 @@ def create_opportunity():
customer_name = frappe.db.get_value("Customer", {"customer_name": "_Test NC"}, ["customer_name"])
doc.party_name = customer_name
doc.opportunity_amount = 150000
doc.source = "Cold Calling"
doc.utm_source = "Cold Calling"
doc.currency = "INR"
doc.expected_closing = "2021-08-31"
doc.company = "Best Test"

View File

@@ -163,84 +163,6 @@
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Masters",
"link_count": 7,
"onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Territory",
"link_count": 0,
"link_to": "Territory",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Customer Group",
"link_count": 0,
"link_to": "Customer Group",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Contact",
"link_count": 0,
"link_to": "Contact",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Prospect",
"link_count": 0,
"link_to": "Prospect",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Person",
"link_count": 0,
"link_to": "Sales Person",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Stage",
"link_count": 0,
"link_to": "Sales Stage",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Lead Source",
"link_count": 0,
"link_to": "Lead Source",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
@@ -417,9 +339,88 @@
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Masters",
"link_count": 7,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Territory",
"link_count": 0,
"link_to": "Territory",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Customer Group",
"link_count": 0,
"link_to": "Customer Group",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Contact",
"link_count": 0,
"link_to": "Contact",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Prospect",
"link_count": 0,
"link_to": "Prospect",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Person",
"link_count": 0,
"link_to": "Sales Person",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Stage",
"link_count": 0,
"link_to": "Sales Stage",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Lead Source",
"link_count": 0,
"link_to": "UTM Source",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2023-09-14 12:11:03.968048",
"modified": "2024-09-12 04:43:41.406488",
"modified_by": "Administrator",
"module": "CRM",
"name": "CRM",