diff --git a/erpnext/buying/report/purchase_person_commission_summary/__init__.py b/erpnext/buying/report/purchase_person_commission_summary/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.js b/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.js
new file mode 100644
index 00000000000..478e9f7c408
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.js
@@ -0,0 +1,51 @@
+// Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.query_reports["Purchase Person Commission Summary"] = {
+ filters: [
+ {
+ fieldname: "purchase_person",
+ label: __("Purchase Person"),
+ fieldtype: "Link",
+ options: "Purchase Person",
+ },
+ {
+ fieldname: "doc_type",
+ label: __("Document Type"),
+ fieldtype: "Select",
+ options: "Purchase Order\nPurchase Receipt\nPurchase Invoice",
+ default: "Purchase Order",
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
+ },
+ {
+ fieldname: "to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.get_today(),
+ },
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ },
+ {
+ fieldname: "supplier",
+ label: __("Supplier"),
+ fieldtype: "Link",
+ options: "Supplier",
+ },
+ {
+ fieldname: "territory",
+ label: __("Territory"),
+ fieldtype: "Link",
+ options: "Territory",
+ },
+ ],
+};
diff --git a/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.json b/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.json
new file mode 100644
index 00000000000..f79924506a8
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 1,
+ "creation": "2026-06-17 00:00:00",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2026-06-17 00:00:00",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Person Commission Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Purchase Order",
+ "report_name": "Purchase Person Commission Summary",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Purchase Manager"
+ },
+ {
+ "role": "Accounts User"
+ }
+ ]
+}
diff --git a/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.py b/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.py
new file mode 100644
index 00000000000..b9fcf54153e
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.py
@@ -0,0 +1,143 @@
+# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from frappe import _, msgprint, qb
+from frappe.query_builder import Criterion
+
+
+def execute(filters=None):
+ if not filters:
+ filters = {}
+
+ columns = get_columns(filters)
+ entries = get_entries(filters)
+ data = []
+
+ for d in entries:
+ data.append(
+ [
+ d.name,
+ d.supplier,
+ d.territory,
+ d.posting_date,
+ d.base_net_amount,
+ d.purchase_person,
+ d.allocated_percentage,
+ d.commission_rate,
+ d.allocated_amount,
+ d.incentives,
+ ]
+ )
+
+ if data:
+ total_row = [""] * len(data[0])
+ data.append(total_row)
+
+ return columns, data
+
+
+def get_columns(filters):
+ if not filters.get("doc_type"):
+ msgprint(_("Please select the document type first"), raise_exception=1)
+
+ return [
+ {
+ "label": _(filters["doc_type"]),
+ "options": filters["doc_type"],
+ "fieldname": filters["doc_type"],
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {
+ "label": _("Supplier"),
+ "options": "Supplier",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {
+ "label": _("Territory"),
+ "options": "Territory",
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "width": 100,
+ },
+ {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
+ {"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120},
+ {
+ "label": _("Purchase Person"),
+ "options": "Purchase Person",
+ "fieldname": "purchase_person",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {
+ "label": _("Contribution %"),
+ "fieldname": "contribution_percentage",
+ "fieldtype": "Data",
+ "width": 110,
+ },
+ {
+ "label": _("Commission Rate %"),
+ "fieldname": "commission_rate",
+ "fieldtype": "Data",
+ "width": 100,
+ },
+ {
+ "label": _("Contribution Amount"),
+ "fieldname": "contribution_amount",
+ "fieldtype": "Currency",
+ "width": 120,
+ },
+ {"label": _("Incentives"), "fieldname": "incentives", "fieldtype": "Currency", "width": 120},
+ ]
+
+
+def get_entries(filters):
+ dt = qb.DocType(filters["doc_type"])
+ pt = qb.DocType("Purchase Team")
+ date_field = dt["transaction_date"] if filters["doc_type"] == "Purchase Order" else dt["posting_date"]
+
+ conditions = get_conditions(dt, pt, filters, date_field)
+ return (
+ qb.from_(dt)
+ .join(pt)
+ .on(pt.parent.eq(dt.name) & pt.parenttype.eq(filters["doc_type"]))
+ .select(
+ dt.name,
+ dt.supplier,
+ dt.territory,
+ date_field.as_("posting_date"),
+ dt.base_net_total.as_("base_net_amount"),
+ pt.commission_rate,
+ pt.purchase_person,
+ pt.allocated_percentage,
+ pt.allocated_amount,
+ pt.incentives,
+ )
+ .where(Criterion.all(conditions))
+ .orderby(dt.name, pt.purchase_person)
+ .run(as_dict=True)
+ )
+
+
+def get_conditions(dt, pt, filters, date_field):
+ conditions = [dt.docstatus.eq(1)]
+
+ from_dt = filters.get("from_date")
+ to_dt = filters.get("to_date")
+ if from_dt and to_dt:
+ conditions.append(date_field.between(from_dt, to_dt))
+ elif from_dt:
+ conditions.append(date_field.gte(from_dt))
+ elif to_dt:
+ conditions.append(date_field.lte(to_dt))
+
+ for field in ["company", "supplier", "territory"]:
+ if filters.get(field):
+ conditions.append(dt[field].eq(filters.get(field)))
+
+ if filters.get("purchase_person"):
+ conditions.append(pt["purchase_person"].eq(filters.get("purchase_person")))
+
+ return conditions
diff --git a/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/__init__.py b/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.js b/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.js
new file mode 100644
index 00000000000..f247c69141b
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.js
@@ -0,0 +1,60 @@
+// Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.query_reports["Purchase Person Target Variance Based On Item Group"] = {
+ filters: [
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ },
+ {
+ fieldname: "fiscal_year",
+ label: __("Fiscal Year"),
+ fieldtype: "Link",
+ options: "Fiscal Year",
+ default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
+ },
+ {
+ fieldname: "doctype",
+ label: __("Document Type"),
+ fieldtype: "Select",
+ options: "Purchase Order\nPurchase Receipt\nPurchase Invoice",
+ default: "Purchase Order",
+ },
+ {
+ fieldname: "period",
+ label: __("Period"),
+ fieldtype: "Select",
+ options: [
+ { value: "Monthly", label: __("Monthly") },
+ { value: "Quarterly", label: __("Quarterly") },
+ { value: "Half-Yearly", label: __("Half-Yearly") },
+ { value: "Yearly", label: __("Yearly") },
+ ],
+ default: "Monthly",
+ },
+ {
+ fieldname: "target_on",
+ label: __("Target On"),
+ fieldtype: "Select",
+ options: "Quantity\nAmount",
+ default: "Quantity",
+ },
+ ],
+ formatter: function (value, row, column, data, default_formatter) {
+ value = default_formatter(value, row, column, data);
+
+ if (column.fieldname.includes("variance")) {
+ if (data[column.fieldname] < 0) {
+ value = "" + value + "";
+ } else if (data[column.fieldname] > 0) {
+ value = "" + value + "";
+ }
+ }
+
+ return value;
+ },
+};
diff --git a/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.json b/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.json
new file mode 100644
index 00000000000..550423569c2
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.json
@@ -0,0 +1,33 @@
+{
+ "add_total_row": 0,
+ "creation": "2026-06-17 00:00:00",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2026-06-17 00:00:00",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Person Target Variance Based On Item Group",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Purchase Order",
+ "report_name": "Purchase Person Target Variance Based On Item Group",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Purchase User"
+ },
+ {
+ "role": "Purchase Manager"
+ },
+ {
+ "role": "Accounts User"
+ },
+ {
+ "role": "Stock User"
+ }
+ ]
+}
diff --git a/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.py b/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.py
new file mode 100644
index 00000000000..afea77a376a
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+
+from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import (
+ get_data_column,
+)
+
+
+def execute(filters=None):
+ return get_data_column(filters, "Purchase Person")
diff --git a/erpnext/buying/report/purchase_person_wise_transaction_summary/__init__.py b/erpnext/buying/report/purchase_person_wise_transaction_summary/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.js b/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.js
new file mode 100644
index 00000000000..84f6f8ea927
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.js
@@ -0,0 +1,70 @@
+// Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.query_reports["Purchase Person-wise Transaction Summary"] = {
+ filters: [
+ {
+ fieldname: "purchase_person",
+ label: __("Purchase Person"),
+ fieldtype: "Link",
+ options: "Purchase Person",
+ },
+ {
+ fieldname: "doc_type",
+ label: __("Document Type"),
+ fieldtype: "Select",
+ options: "Purchase Order\nPurchase Receipt\nPurchase Invoice",
+ default: "Purchase Order",
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
+ },
+ {
+ fieldname: "to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.get_today(),
+ },
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ reqd: 1,
+ },
+ {
+ fieldname: "item_group",
+ label: __("Item Group"),
+ fieldtype: "Link",
+ options: "Item Group",
+ },
+ {
+ fieldname: "brand",
+ label: __("Brand"),
+ fieldtype: "Link",
+ options: "Brand",
+ },
+ {
+ fieldname: "supplier",
+ label: __("Supplier"),
+ fieldtype: "Link",
+ options: "Supplier",
+ },
+ {
+ fieldname: "territory",
+ label: __("Territory"),
+ fieldtype: "Link",
+ options: "Territory",
+ },
+ {
+ fieldname: "show_return_entries",
+ label: __("Show Return Entries"),
+ fieldtype: "Check",
+ default: 0,
+ },
+ ],
+};
diff --git a/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.json b/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.json
new file mode 100644
index 00000000000..2ac85991690
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.json
@@ -0,0 +1,31 @@
+{
+ "add_total_row": 1,
+ "creation": "2026-06-17 00:00:00",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2026-06-17 00:00:00",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Person-wise Transaction Summary",
+ "owner": "Administrator",
+ "ref_doctype": "Purchase Order",
+ "report_name": "Purchase Person-wise Transaction Summary",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Purchase User"
+ },
+ {
+ "role": "Purchase Manager"
+ },
+ {
+ "role": "Accounts User"
+ },
+ {
+ "role": "Stock User"
+ }
+ ]
+}
diff --git a/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.py b/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.py
new file mode 100644
index 00000000000..5ac58512a4a
--- /dev/null
+++ b/erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.py
@@ -0,0 +1,276 @@
+# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+
+import frappe
+from frappe import _, msgprint, qb
+from frappe.query_builder import Case, Criterion
+
+from erpnext import get_company_currency
+
+
+def execute(filters=None):
+ if not filters:
+ filters = {}
+
+ validate_filters(filters)
+
+ columns = get_columns(filters)
+ entries = get_entries(filters)
+ item_details = get_item_details()
+ data = []
+
+ company_currency = get_company_currency(filters.get("company"))
+
+ for d in entries:
+ if d.stock_qty > 0 or filters.get("show_return_entries", 0):
+ data.append(
+ [
+ d.name,
+ d.supplier,
+ d.territory,
+ d.warehouse,
+ d.posting_date,
+ d.item_code,
+ item_details.get(d.item_code, {}).get("item_group"),
+ item_details.get(d.item_code, {}).get("brand"),
+ d.stock_qty,
+ d.base_net_amount,
+ d.purchase_person,
+ d.allocated_percentage,
+ (d.stock_qty * d.allocated_percentage / 100),
+ d.contribution_amt,
+ company_currency,
+ ]
+ )
+
+ if data:
+ total_row = [""] * len(data[0])
+ data.append(total_row)
+
+ return columns, data
+
+
+def validate_filters(filters):
+ ALLOWED_DOCTYPES = ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]
+
+ if not filters.get("doc_type"):
+ msgprint(_("Please select the document type first"), raise_exception=1)
+
+ if filters.get("doc_type") not in ALLOWED_DOCTYPES:
+ frappe.throw(_("{0}, {1} or {2} are the only allowed options.").format(*ALLOWED_DOCTYPES))
+
+
+def get_columns(filters):
+ return [
+ {
+ "label": _(filters["doc_type"]),
+ "options": filters["doc_type"],
+ "fieldname": frappe.scrub(filters["doc_type"]),
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {
+ "label": _("Supplier"),
+ "options": "Supplier",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {
+ "label": _("Territory"),
+ "options": "Territory",
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {
+ "label": _("Warehouse"),
+ "options": "Warehouse",
+ "fieldname": "warehouse",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 140},
+ {
+ "label": _("Item Code"),
+ "options": "Item",
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {
+ "label": _("Item Group"),
+ "options": "Item Group",
+ "fieldname": "item_group",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {
+ "label": _("Brand"),
+ "options": "Brand",
+ "fieldname": "brand",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {"label": _("Total Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 140},
+ {
+ "label": _("Amount"),
+ "options": "currency",
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "width": 140,
+ },
+ {
+ "label": _("Purchase Person"),
+ "options": "Purchase Person",
+ "fieldname": "purchase_person",
+ "fieldtype": "Link",
+ "width": 140,
+ },
+ {"label": _("Contribution %"), "fieldname": "contribution", "fieldtype": "Float", "width": 140},
+ {
+ "label": _("Contribution Qty"),
+ "fieldname": "contribution_qty",
+ "fieldtype": "Float",
+ "width": 140,
+ },
+ {
+ "label": _("Contribution Amount"),
+ "options": "currency",
+ "fieldname": "contribution_amt",
+ "fieldtype": "Currency",
+ "width": 140,
+ },
+ {
+ "label": _("Currency"),
+ "options": "Currency",
+ "fieldname": "currency",
+ "fieldtype": "Link",
+ "hidden": 1,
+ },
+ ]
+
+
+def get_entries(filters):
+ doc_type = filters["doc_type"]
+
+ date_field = "transaction_date" if doc_type == "Purchase Order" else "posting_date"
+ qty_field = "received_qty" if doc_type == "Purchase Order" else "qty"
+
+ dt = frappe.qb.DocType(doc_type)
+ dt_item = frappe.qb.DocType(f"{doc_type} Item")
+ pt = frappe.qb.DocType("Purchase Team")
+
+ calc_qty = dt_item[qty_field] * dt_item.conversion_factor
+ calc_net_amount = dt_item.base_net_rate * calc_qty
+
+ stock_qty_case = Case().when(dt.status == "Closed", calc_qty).else_(dt_item.stock_qty).as_("stock_qty")
+
+ base_net_amount_case = (
+ Case()
+ .when(dt.status == "Closed", calc_net_amount)
+ .else_(dt_item.base_net_amount)
+ .as_("base_net_amount")
+ )
+
+ contribution_amt_case = (
+ Case()
+ .when(dt.status == "Closed", (calc_net_amount * pt.allocated_percentage / 100))
+ .else_(dt_item.base_net_amount * pt.allocated_percentage / 100)
+ .as_("contribution_amt")
+ )
+
+ conditions = get_conditions(dt, pt, filters, date_field)
+
+ query = (
+ frappe.qb.from_(dt)
+ .join(dt_item)
+ .on(dt.name == dt_item.parent)
+ .join(pt)
+ .on(dt.name == pt.parent)
+ .select(
+ dt.name,
+ dt.supplier,
+ dt.territory,
+ dt[date_field].as_("posting_date"),
+ dt_item.item_code,
+ pt.purchase_person,
+ pt.allocated_percentage,
+ dt_item.warehouse,
+ stock_qty_case,
+ base_net_amount_case,
+ contribution_amt_case,
+ )
+ .where(pt.parenttype == doc_type)
+ .where(dt.docstatus == 1)
+ .where(Criterion.all(conditions))
+ .orderby(pt.purchase_person)
+ .orderby(dt.name, order=frappe.qb.desc)
+ )
+
+ return query.run(as_dict=True)
+
+
+def get_conditions(dt, pt, filters, date_field):
+ conditions = []
+
+ for field in ["company", "supplier", "territory"]:
+ if filters.get(field):
+ conditions.append(dt[field].eq(filters[field]))
+
+ if filters.get("purchase_person"):
+ lft, rgt = frappe.get_value("Purchase Person", filters.get("purchase_person"), ["lft", "rgt"])
+ purchase_person_tbl = frappe.qb.DocType("Purchase Person")
+ subquery = (
+ frappe.qb.from_(purchase_person_tbl)
+ .select(purchase_person_tbl.name)
+ .where(purchase_person_tbl.lft >= lft)
+ .where(purchase_person_tbl.rgt <= rgt)
+ )
+ conditions.append(pt.purchase_person.isin(subquery))
+
+ if filters.get("from_date"):
+ conditions.append(dt[date_field].gte(filters["from_date"]))
+
+ if filters.get("to_date"):
+ conditions.append(dt[date_field].lte(filters["to_date"]))
+
+ items = get_items(filters)
+ if items:
+ conditions.append(
+ frappe.qb.DocType(f"{filters['doc_type']} Item").item_code.isin([i[0] for i in items])
+ )
+ elif filters.get("item_group") or filters.get("brand"):
+ conditions.append(frappe.qb.terms.ValueWrapper(0).eq(1))
+
+ return conditions
+
+
+def get_items(filters):
+ item = qb.DocType("Item")
+
+ item_query_conditions = []
+ if filters.get("item_group"):
+ item_group = qb.DocType("Item Group")
+ lft, rgt = frappe.db.get_all(
+ "Item Group", filters={"name": filters.get("item_group")}, fields=["lft", "rgt"], as_list=True
+ )[0]
+ item_group_query = (
+ qb.from_(item_group)
+ .select(item_group.name)
+ .where((item_group.lft >= lft) & (item_group.rgt <= rgt))
+ )
+ item_query_conditions.append(item.item_group.isin(item_group_query))
+ if filters.get("brand"):
+ item_query_conditions.append(item.brand == filters.get("brand"))
+
+ if not item_query_conditions:
+ return []
+
+ return qb.from_(item).select(item.name).where(Criterion.all(item_query_conditions)).run()
+
+
+def get_item_details():
+ items = frappe.get_all("Item", fields=["name", "item_group", "brand"])
+ return {d.name: d for d in items}
diff --git a/erpnext/buying/workspace/buying/buying.json b/erpnext/buying/workspace/buying/buying.json
index 6d37ae3cbe8..6f8ce289fa9 100644
--- a/erpnext/buying/workspace/buying/buying.json
+++ b/erpnext/buying/workspace/buying/buying.json
@@ -23,6 +23,7 @@
"is_query_report": 0,
"label": "Buying",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -86,6 +87,7 @@
"is_query_report": 0,
"label": "Items & Pricing",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -171,6 +173,7 @@
"is_query_report": 0,
"label": "Settings",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -212,6 +215,7 @@
"is_query_report": 0,
"label": "Supplier",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -264,6 +268,7 @@
"is_query_report": 0,
"label": "Supplier Scorecard",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -316,6 +321,7 @@
"is_query_report": 0,
"label": "Key Reports",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -390,6 +396,7 @@
"is_query_report": 0,
"label": "Purchase Partner",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -415,25 +422,6 @@
"onboard": 0,
"type": "Link"
},
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Purchase Person",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Purchase Person",
- "link_count": 0,
- "link_to": "Purchase Person",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
{
"dependencies": "Purchase Partner",
"hidden": 0,
@@ -467,11 +455,65 @@
"onboard": 0,
"type": "Link"
},
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Purchase Person",
+ "link_count": 0,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Purchase Person",
+ "link_count": 0,
+ "link_to": "Purchase Person",
+ "link_type": "DocType",
+ "onboard": 1,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Purchase Person",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Purchase Person-wise Transaction Summary",
+ "link_count": 0,
+ "link_to": "Purchase Person-wise Transaction Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Purchase Person",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Purchase Person Commission Summary",
+ "link_count": 0,
+ "link_to": "Purchase Person Commission Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Purchase Person",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Purchase Person Target Variance Based On Item Group",
+ "link_count": 0,
+ "link_to": "Purchase Person Target Variance Based On Item Group",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
{
"hidden": 0,
"is_query_report": 0,
"label": "Other Reports",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -579,6 +621,7 @@
"is_query_report": 0,
"label": "Regional",
"link_count": 0,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -594,7 +637,7 @@
"type": "Link"
}
],
- "modified": "2026-01-02 14:55:59.078773",
+ "modified": "2026-06-17 13:01:33.296280",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying",
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
index b837d67e1c0..444fc121ba8 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
@@ -243,6 +243,13 @@ def get_actual_data(filters, sales_users_or_territory_data, date_field, sales_fi
sales_field_col = sales_team[sales_field]
query = query.inner_join(sales_team).on(sales_team.parent == parent_doc.name)
+ elif sales_field == "purchase_person":
+ purchase_team = frappe.qb.DocType("Purchase Team")
+ stock_qty = child_doc.stock_qty * purchase_team.allocated_percentage / 100
+ net_amount = child_doc.base_net_amount * purchase_team.allocated_percentage / 100
+ sales_field_col = purchase_team[sales_field]
+
+ query = query.inner_join(purchase_team).on(purchase_team.parent == parent_doc.name)
else:
stock_qty = child_doc.stock_qty
net_amount = child_doc.base_net_amount