Repository: frappe/frappe

Issue:
Need a Project Name in the Final Report
If we are filtering the Report with a Project Name and exporting it into Excel then the Project Name is not shown anywhere in Excel.
It should show at least in the Header in Excel.

![image](https://github.com/frappe/erpnext/assets/108865344/71bc9687-9dec-48c3-8707-d3220d82915f)





Discussion:

Resolving patch:
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -318,6 +318,7 @@ def export_query():
 	file_format_type = form_params.file_format_type
 	custom_columns = frappe.parse_json(form_params.custom_columns or "[]")
 	include_indentation = form_params.include_indentation
+	include_filters = form_params.include_filters
 	visible_idx = form_params.visible_idx
 
 	if isinstance(visible_idx, str):
@@ -327,6 +328,8 @@ def export_query():
 		report_name, form_params.filters, custom_columns=custom_columns, are_default_filters=False
 	)
 	data = frappe._dict(data)
+	data.filters = form_params.applied_filters
+
 	if not data.columns:
 		frappe.respond_as_web_page(
 			_("No data to export"),
@@ -335,7 +338,9 @@ def export_query():
 		return
 
 	format_duration_fields(data)
-	xlsx_data, column_widths = build_xlsx_data(data, visible_idx, include_indentation)
+	xlsx_data, column_widths = build_xlsx_data(
+		data, visible_idx, include_indentation, include_filters=include_filters
+	)
 
 	if file_format_type == "CSV":
 		content = get_csv_bytes(xlsx_data, csv_params)
@@ -360,7 +365,9 @@ def format_duration_fields(data: frappe._dict) -> None:
 				row[index] = format_duration(row[index])
 
 
-def build_xlsx_data(data, visible_idx, include_indentation, ignore_visible_idx=False):
+def build_xlsx_data(
+	data, visible_idx, include_indentation, include_filters=False, ignore_visible_idx=False
+):
 	EXCEL_TYPES = (
 		str,
 		bool,
@@ -380,17 +387,34 @@ def build_xlsx_data(data, visible_idx, include_indentation, ignore_visible_idx=F
 		# Note: converted for faster lookups
 		visible_idx = set(visible_idx)
 
-	result = [[]]
+	result = []
 	column_widths = []
 
+	if cint(include_filters):
+		filter_data = []
+		filters = data.filters
+		for filter_name, filter_value in filters.items():
+			if not filter_value:
+				continue
+			filter_value = (
+				", ".join([cstr(x) for x in filter_value])
+				if isinstance(filter_value, list)
+				else cstr(filter_value)
+			)
+			filter_data.append([cstr(filter_name), filter_value])
+		filter_data.append([])
+		result += filter_data
+
+	column_data = []
 	for column in data.columns:
 		if column.get("hidden"):
 			continue
-		result[0].append(_(column.get("label")))
+		column_data.append(_(column.get("label")))
 		column_width = cint(column.get("width", 0))
 		# to convert into scale accepted by openpyxl
 		column_width /= 10
 		column_widths.append(column_width)
+	result.append(column_data)
 
 	# build table from result
 	for row_idx, row in enumerate(data.result):
diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py
--- a/frappe/desk/reportview.py
+++ b/frappe/desk/reportview.py
@@ -217,6 +217,8 @@ def clean_params(data):
 def parse_json(data):
 	if (filters := data.get("filters")) and isinstance(filters, str):
 		data["filters"] = json.loads(filters)
+	if (applied_filters := data.get("applied_filters")) and isinstance(applied_filters, str):
+		data["applied_filters"] = json.loads(applied_filters)
 	if (or_filters := data.get("or_filters")) and isinstance(or_filters, str):
 		data["or_filters"] = json.loads(or_filters)
 	if (fields := data.get("fields")) and isinstance(fields, str):


Test patch:
diff --git a/frappe/tests/test_query_report.py b/frappe/tests/test_query_report.py
--- a/frappe/tests/test_query_report.py
+++ b/frappe/tests/test_query_report.py
@@ -22,18 +22,7 @@ def test_xlsx_data_with_multiple_datatypes(self):
 		"""Test exporting report using rows with multiple datatypes (list, dict)"""
 
 		# Create mock data
-		data = frappe._dict()
-		data.columns = [
-			{"label": "Column A", "fieldname": "column_a", "fieldtype": "Float"},
-			{"label": "Column B", "fieldname": "column_b", "width": 100, "fieldtype": "Float"},
-			{"label": "Column C", "fieldname": "column_c", "width": 150, "fieldtype": "Duration"},
-		]
-		data.result = [
-			[1.0, 3.0, 600],
-			{"column_a": 22.1, "column_b": 21.8, "column_c": 86412},
-			{"column_b": 5.1, "column_c": 53234, "column_a": 11.1},
-			[3.0, 1.5, 333],
-		]
+		data = create_mock_data()
 
 		# Define the visible rows
 		visible_idx = [0, 2, 3]
@@ -54,6 +43,27 @@ def test_xlsx_data_with_multiple_datatypes(self):
 			for cell in row:
 				self.assertIsInstance(cell, (int, float))
 
+	def test_xlsx_data_with_filters(self):
+		"""Test building xlsx data along with filters"""
+
+		# Create mock data
+		data = create_mock_data()
+		data.filters = {"Label 1": "Filter Value", "Label 2": None, "Label 3": list(range(5))}
+
+		# Define the visible rows
+		visible_idx = [0, 2, 3]
+
+		# Build the result
+		xlsx_data, column_widths = build_xlsx_data(
+			data, visible_idx, include_indentation=False, include_filters=True
+		)
+
+		# Check if unset filters are skipped | Rows - 2 filters + 1 empty + 1 column + 3 data
+		self.assertEqual(len(xlsx_data), 7)
+
+		# Check filter formatting
+		self.assertListEqual(xlsx_data[:2], [["Label 1", "Filter Value"], ["Label 3", "0, 1, 2, 3, 4"]])
+
 	def test_xlsx_export_with_composite_cell_value(self):
 		"""Test excel export using rows with composite cell value"""
 
@@ -236,3 +246,19 @@ def test_report_for_duplicate_column_names(self):
 		except Exception as e:
 			raise e
 			frappe.db.rollback()
+
+
+def create_mock_data():
+	data = frappe._dict()
+	data.columns = [
+		{"label": "Column A", "fieldname": "column_a", "fieldtype": "Float"},
+		{"label": "Column B", "fieldname": "column_b", "width": 100, "fieldtype": "Float"},
+		{"label": "Column C", "fieldname": "column_c", "width": 150, "fieldtype": "Duration"},
+	]
+	data.result = [
+		[1.0, 3.0, 600],
+		{"column_a": 22.1, "column_b": 21.8, "column_c": 86412},
+		{"column_b": 5.1, "column_c": 53234, "column_a": 11.1},
+		[3.0, 1.5, 333],
+	]
+	return data