diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.js b/erpnext/selling/doctype/product_bundle/product_bundle.js index 5b74f2d9f8f..763fbcb56c9 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.js +++ b/erpnext/selling/doctype/product_bundle/product_bundle.js @@ -13,50 +13,16 @@ frappe.ui.form.on("Product Bundle", { // A submitted bundle is immutable. To change it, create a new version // (a fresh draft copied from this one) and submit that instead. if (frm.doc.docstatus === 1) { - frm.add_custom_button(__("Create New Version"), () => { - frappe.model.open_mapped_doc({ - method: "erpnext.selling.doctype.product_bundle.product_bundle.make_new_version", - frm: frm, - }); - }); + frm.add_custom_button( + __("New Version"), + () => { + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.product_bundle.product_bundle.make_new_version", + frm: frm, + }); + }, + __("Actions") + ); } - - show_supersede_hint(frm); - }, - - new_item_code: function (frm) { - show_supersede_hint(frm); }, }); - -function show_supersede_hint(frm) { - // Warn (non-blocking) when the chosen Parent Item already has an active bundle: - // submitting this draft will create a new version and deactivate that one. - frm.set_intro(""); - if (frm.doc.docstatus !== 0 || !frm.doc.new_item_code) { - return; - } - - frappe.db - .get_value( - "Product Bundle", - { - new_item_code: frm.doc.new_item_code, - is_active: 1, - docstatus: 1, - }, - "name" - ) - .then((r) => { - const active = r.message && r.message.name; - if (active && active !== frm.doc.name) { - frm.set_intro( - __( - "Item {0} already has an active Product Bundle ({1}). Submitting this will create a new version and deactivate {1}.", - [frm.doc.new_item_code, active] - ), - "orange" - ); - } - }); -} diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.json b/erpnext/selling/doctype/product_bundle/product_bundle.json index 365e9e4cce3..f5c15efb13a 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.json +++ b/erpnext/selling/doctype/product_bundle/product_bundle.json @@ -28,6 +28,7 @@ "fieldtype": "Link", "in_global_search": 1, "in_list_view": 1, + "in_standard_filter": 1, "label": "Parent Item", "no_copy": 1, "oldfieldname": "new_item_code", @@ -145,6 +146,7 @@ "write": 1 } ], + "search_fields": "new_item_code,description", "sort_field": "creation", "sort_order": "ASC", "states": [] diff --git a/erpnext/selling/doctype/product_bundle/product_bundle_list.js b/erpnext/selling/doctype/product_bundle/product_bundle_list.js index 7c1fa7d9161..282985528c5 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle_list.js +++ b/erpnext/selling/doctype/product_bundle/product_bundle_list.js @@ -12,6 +12,8 @@ frappe.listview_settings["Product Bundle"] = { if (doc.docstatus === 1 && doc.is_active) { return [__("Active"), "green", "is_active,=,1|disabled,=,0|docstatus,=,1"]; } - // inactive submitted versions keep the default "Submitted" indicator + if (doc.docstatus === 1 && !doc.is_active) { + return [__("Inactive"), "gray", "is_active,=,0|disabled,=,0|docstatus,=,1"]; + } }, }; diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py index fa268c295a4..59925106a88 100644 --- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py @@ -129,12 +129,32 @@ class TestProductBundle(ERPNextTestSuite): self.assertTrue(rows) self.assertEqual(rows[0].disabled, 1) self.assertEqual(rows[0].is_active, 1) + self.assertEqual(rows[0].stock_quantity, rows[0].quantity) + self.assertEqual(rows[0].stock_uom, rows[0].uom) _, parent_rows = execute({"item": self.parent, "section": "References"}) rows = [r for r in parent_rows if r.document_name == bundle.name] self.assertTrue(rows) self.assertEqual(rows[0].disabled, 1) + def test_item_where_used_report_hides_internal_and_empty_columns(self): + from erpnext.stock.report.item_where_used.item_where_used import execute + + bundle = make_product_bundle(self.parent, ["_Test PB Child A"]) + + columns, rows = execute({"item": "_Test PB Child A", "section": "Where Used"}) + fieldnames = [column["fieldname"] for column in columns] + + self.assertIn("stock_quantity", fieldnames) + self.assertIn("stock_uom", fieldnames) + self.assertNotIn("matched_field", fieldnames) + self.assertNotIn("company", fieldnames) + + rows = [r for r in rows if r.document_name == bundle.name] + self.assertTrue(rows) + self.assertEqual(rows[0].stock_quantity, rows[0].quantity) + self.assertEqual(rows[0].stock_uom, rows[0].uom) + def test_child_cannot_be_active_bundle(self): make_product_bundle(self.parent, ["_Test PB Child A"]) outer = make_item("_Test PB Outer", {"is_stock_item": 0, "is_sales_item": 1}).name diff --git a/erpnext/stock/report/item_where_used/item_where_used.py b/erpnext/stock/report/item_where_used/item_where_used.py index 24784ed9180..d7475e0c8a8 100644 --- a/erpnext/stock/report/item_where_used/item_where_used.py +++ b/erpnext/stock/report/item_where_used/item_where_used.py @@ -10,16 +10,16 @@ REFERENCES_SECTION = "References" def execute(filters=None): filters = frappe._dict(filters or {}) - columns = get_columns() + data = [] - if not filters.get("item"): - return columns, [] + if filters.get("item"): + data = get_data(filters) - return columns, get_data(filters) + return get_columns(data), data -def get_columns(): - return [ +def get_columns(data=None): + columns = [ { "fieldname": "section", "label": _("Section"), @@ -52,12 +52,6 @@ def get_columns(): "options": "Item", "width": 180, }, - { - "fieldname": "matched_field", - "label": _("Matched Field"), - "fieldtype": "Data", - "width": 180, - }, { "fieldname": "row_index", "label": _("Row"), @@ -123,6 +117,8 @@ def get_columns(): }, ] + return hide_empty_optional_columns(columns, data or []) + def get_data(filters): data = [] @@ -283,6 +279,8 @@ def get_product_bundle_component_rows(item): row_index=row.idx, quantity=row.qty, uom=row.uom, + stock_quantity=row.qty, + stock_uom=row.uom, is_active=bundle.is_active, disabled=bundle.disabled, ) @@ -473,6 +471,29 @@ def build_row(**kwargs): return frappe._dict(kwargs) +def hide_empty_optional_columns(columns, data): + optional_fields = {"stock_quantity", "stock_uom", "company", "is_default", "details"} + + if not data: + return columns + + fields_with_values = set() + for row in data: + for fieldname in optional_fields: + if field_has_value(row, fieldname): + fields_with_values.add(fieldname) + + return [column for column in columns if column["fieldname"] not in optional_fields - fields_with_values] + + +def field_has_value(row, fieldname): + if fieldname not in row: + return False + + value = row.get(fieldname) + return value is not None and value != "" + + def get_unique_names(names): unique_names = [] seen = set()