mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-16 02:11:39 +00:00
fix(postgres): use portable GroupConcat in Lost Opportunity report
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.query_builder import DocType
|
from frappe.query_builder import DocType
|
||||||
from frappe.query_builder.custom import GROUP_CONCAT
|
from frappe.query_builder.functions import Date, GroupConcat
|
||||||
from frappe.query_builder.functions import Date
|
|
||||||
|
|
||||||
Opportunity = DocType("Opportunity")
|
Opportunity = DocType("Opportunity")
|
||||||
OpportunityLostReasonDetail = DocType("Opportunity Lost Reason Detail")
|
OpportunityLostReasonDetail = DocType("Opportunity Lost Reason Detail")
|
||||||
@@ -72,6 +71,9 @@ def get_columns():
|
|||||||
|
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
|
# db-aware GROUP_CONCAT (MariaDB) / STRING_AGG (postgres) with a ", " separator
|
||||||
|
lost_reasons = GroupConcat(OpportunityLostReasonDetail.lost_reason, ", ", alias="lost_reason")
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
frappe.qb.from_(Opportunity)
|
frappe.qb.from_(Opportunity)
|
||||||
.left_join(OpportunityLostReasonDetail)
|
.left_join(OpportunityLostReasonDetail)
|
||||||
@@ -85,7 +87,7 @@ def get_data(filters):
|
|||||||
Opportunity.party_name,
|
Opportunity.party_name,
|
||||||
Opportunity.customer_name,
|
Opportunity.customer_name,
|
||||||
Opportunity.opportunity_type,
|
Opportunity.opportunity_type,
|
||||||
GROUP_CONCAT(OpportunityLostReasonDetail.lost_reason, alias="lost_reason").separator(", "),
|
lost_reasons,
|
||||||
Opportunity.sales_stage,
|
Opportunity.sales_stage,
|
||||||
Opportunity.territory,
|
Opportunity.territory,
|
||||||
)
|
)
|
||||||
|
|||||||
22
erpnext/crm/report/lost_opportunity/test_lost_opportunity.py
Normal file
22
erpnext/crm/report/lost_opportunity/test_lost_opportunity.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.utils import add_days, today
|
||||||
|
|
||||||
|
from erpnext.crm.report.lost_opportunity.lost_opportunity import execute
|
||||||
|
from erpnext.tests.utils import ERPNextTestSuite
|
||||||
|
|
||||||
|
|
||||||
|
class TestLostOpportunity(ERPNextTestSuite):
|
||||||
|
def test_report_aggregates_lost_reasons(self):
|
||||||
|
# Exercises the db-aware GROUP_CONCAT (MariaDB) / STRING_AGG (postgres) aggregation of the
|
||||||
|
# child "Opportunity Lost Reason Detail" rows. The MySQL-only GROUP_CONCAT term would fail to
|
||||||
|
# compile on postgres, so simply running the report query guards the portability fix on both
|
||||||
|
# databases.
|
||||||
|
company = frappe.db.get_value("Company", {}, "name")
|
||||||
|
columns, data = execute(
|
||||||
|
frappe._dict({"company": company, "from_date": add_days(today(), -365), "to_date": today()})
|
||||||
|
)
|
||||||
|
self.assertTrue(columns)
|
||||||
|
self.assertIsInstance(data, list)
|
||||||
Reference in New Issue
Block a user