From 1138effd7c4d69b2c02c03877bcd982e5353146a Mon Sep 17 00:00:00 2001 From: Dipen Gala Date: Wed, 17 Jun 2026 13:12:54 +0530 Subject: [PATCH] feat: add Purchase Person reports mirroring Sales Person reports Three new Script Reports in the Buying module: - Purchase Person-wise Transaction Summary (mirrors Sales Person-wise) - Purchase Person Commission Summary (mirrors Sales Person Commission) - Purchase Person Target Variance Based On Item Group (mirrors Sales Person Target) The shared `item_group_wise_sales_target_variance.get_actual_data` helper gains a `purchase_person` branch that joins `Purchase Team` the same way the existing `sales_person` branch joins `Sales Team`. All three reports are added to the Buying workspace under a Purchase Person card. Co-Authored-By: Claude Sonnet 4.6 --- .../__init__.py | 0 .../purchase_person_commission_summary.js | 51 ++++ .../purchase_person_commission_summary.json | 26 ++ .../purchase_person_commission_summary.py | 143 +++++++++ .../__init__.py | 0 ...son_target_variance_based_on_item_group.js | 60 ++++ ...n_target_variance_based_on_item_group.json | 33 +++ ...son_target_variance_based_on_item_group.py | 11 + .../__init__.py | 0 ...urchase_person_wise_transaction_summary.js | 70 +++++ ...chase_person_wise_transaction_summary.json | 31 ++ ...urchase_person_wise_transaction_summary.py | 276 ++++++++++++++++++ erpnext/buying/workspace/buying/buying.json | 83 ++++-- .../item_group_wise_sales_target_variance.py | 7 + 14 files changed, 771 insertions(+), 20 deletions(-) create mode 100644 erpnext/buying/report/purchase_person_commission_summary/__init__.py create mode 100644 erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.js create mode 100644 erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.json create mode 100644 erpnext/buying/report/purchase_person_commission_summary/purchase_person_commission_summary.py create mode 100644 erpnext/buying/report/purchase_person_target_variance_based_on_item_group/__init__.py create mode 100644 erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.js create mode 100644 erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.json create mode 100644 erpnext/buying/report/purchase_person_target_variance_based_on_item_group/purchase_person_target_variance_based_on_item_group.py create mode 100644 erpnext/buying/report/purchase_person_wise_transaction_summary/__init__.py create mode 100644 erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.js create mode 100644 erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.json create mode 100644 erpnext/buying/report/purchase_person_wise_transaction_summary/purchase_person_wise_transaction_summary.py 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