mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-24 11:29:48 +00:00
fix: submittable product bundle issues
(cherry picked from commit a218b8db8c)
This commit is contained in:
@@ -26,6 +26,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",
|
||||
@@ -116,6 +117,7 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "new_item_code,description",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
def make_product_bundle(parent, items, qty=None):
|
||||
if frappe.db.exists("Product Bundle", parent):
|
||||
@@ -16,3 +19,67 @@ def make_product_bundle(parent, items, qty=None):
|
||||
product_bundle.insert()
|
||||
|
||||
return product_bundle
|
||||
|
||||
|
||||
class TestProductBundle(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
suffix = frappe.generate_hash(length=8)
|
||||
self.parent = make_item(f"_Test PB Parent {suffix}", {"is_stock_item": 0, "is_sales_item": 1}).name
|
||||
self.child = make_item(f"_Test PB Child {suffix}", {"is_stock_item": 1}).name
|
||||
|
||||
def test_item_where_used_report_shows_disabled_flag(self):
|
||||
from erpnext.stock.report.item_where_used.item_where_used import execute
|
||||
|
||||
bundle = make_product_bundle(self.parent, [self.child])
|
||||
bundle.disabled = 1
|
||||
bundle.save()
|
||||
|
||||
_, component_rows = execute({"item": self.child, "section": "Where Used"})
|
||||
rows = [r for r in component_rows if r.document_name == bundle.name]
|
||||
self.assertTrue(rows)
|
||||
self.assertEqual(rows[0].disabled, 1)
|
||||
self.assertEqual(rows[0].is_active, 0)
|
||||
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, [self.child])
|
||||
|
||||
columns, rows = execute({"item": self.child, "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_item_where_used_report_hides_false_check_columns(self):
|
||||
from erpnext.stock.report.item_where_used.item_where_used import get_columns
|
||||
|
||||
columns = get_columns([frappe._dict(stock_quantity=0, is_default=0)])
|
||||
fieldnames = [column["fieldname"] for column in columns]
|
||||
|
||||
self.assertIn("stock_quantity", fieldnames)
|
||||
self.assertNotIn("is_default", fieldnames)
|
||||
|
||||
def test_child_cannot_be_active_bundle(self):
|
||||
make_product_bundle(self.parent, [self.child])
|
||||
outer = make_item(
|
||||
f"_Test PB Outer {frappe.generate_hash(length=8)}", {"is_stock_item": 0, "is_sales_item": 1}
|
||||
).name
|
||||
|
||||
doc = frappe.get_doc({"doctype": "Product Bundle", "new_item_code": outer})
|
||||
doc.append("items", {"item_code": self.parent, "qty": 1})
|
||||
self.assertRaises(frappe.ValidationError, doc.insert)
|
||||
|
||||
@@ -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=0 if bundle.disabled else 1,
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user