From f9713eeb5690f9e71e01c6c570012f561a633c73 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 22 Nov 2023 15:22:42 +0530 Subject: [PATCH 1/8] fix: skip fixed assets in parent --- .../doctype/product_bundle/product_bundle.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py index ac83c0f046..4b401e7e67 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/product_bundle.py @@ -59,6 +59,8 @@ class ProductBundle(Document): """Validates, main Item is not a stock item""" if frappe.db.get_value("Item", self.new_item_code, "is_stock_item"): frappe.throw(_("Parent Item {0} must not be a Stock Item").format(self.new_item_code)) + if frappe.db.get_value("Item", self.new_item_code, "is_fixed_asset"): + frappe.throw(_("Parent Item {0} must not be a Fixed Asset").format(self.new_item_code)) def validate_child_items(self): for item in self.items: @@ -73,12 +75,17 @@ class ProductBundle(Document): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): - from erpnext.controllers.queries import get_match_cond - - return frappe.db.sql( - """select name, item_name, description from tabItem - where is_stock_item=0 and name not in (select name from `tabProduct Bundle`) - and %s like %s %s limit %s offset %s""" - % (searchfield, "%s", get_match_cond(doctype), "%s", "%s"), - ("%%%s%%" % txt, page_len, start), - ) + product_bundles = frappe.db.get_list("Product Bundle", pluck="name") + item = frappe.qb.DocType("Item") + return ( + frappe.qb.from_(item) + .select("*") + .where( + (item.is_stock_item == 0) + & (item.is_fixed_asset == 0) + & (item.name.notin(product_bundles)) + & (item[searchfield].like(f"%{txt}%")) + ) + .limit(page_len) + .offset(start) + ).run() From ee76af76812fe04d86653fd1955ad133c8cb5df8 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 22 Nov 2023 15:23:17 +0530 Subject: [PATCH 2/8] feat: add disabled field in product bundle --- .../product_bundle/product_bundle.json | 398 +++++------------- 1 file changed, 101 insertions(+), 297 deletions(-) diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.json b/erpnext/selling/doctype/product_bundle/product_bundle.json index 56155fb750..c4f21b61b9 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.json +++ b/erpnext/selling/doctype/product_bundle/product_bundle.json @@ -1,315 +1,119 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "beta": 0, - "creation": "2013-06-20 11:53:21", - "custom": 0, - "description": "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have \"Is Stock Item\" as \"No\" and \"Is Sales Item\" as \"Yes\".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials", - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "creation": "2013-06-20 11:53:21", + "description": "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have \"Is Stock Item\" as \"No\" and \"Is Sales Item\" as \"Yes\".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "basic_section", + "new_item_code", + "description", + "column_break_eonk", + "disabled", + "item_section", + "items", + "section_break_4", + "about" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "basic_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "basic_section", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "new_item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Parent Item", - "length": 0, - "no_copy": 1, - "oldfieldname": "new_item_code", - "oldfieldtype": "Data", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "new_item_code", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "label": "Parent Item", + "no_copy": 1, + "oldfieldname": "new_item_code", + "oldfieldtype": "Data", + "options": "Item", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Description" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "List items that form the package.", - "fieldname": "item_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "description": "List items that form the package.", + "fieldname": "item_section", + "fieldtype": "Section Break", + "label": "Items" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "oldfieldname": "sales_bom_items", - "oldfieldtype": "Table", - "options": "Product Bundle Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "oldfieldname": "sales_bom_items", + "oldfieldtype": "Table", + "options": "Product Bundle Item", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "about", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "

About Product Bundle

\n\n

Aggregate group of Items into another Item. This is useful if you are bundling a certain Items into a package and you maintain stock of the packed Items and not the aggregate Item.

\n

The package Item will have Is Stock Item as No and Is Sales Item as Yes.

\n

Example:

\n

If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.

", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "about", + "fieldtype": "HTML", + "options": "

About Product Bundle

\n\n

Aggregate group of Items into another Item. This is useful if you are bundling a certain Items into a package and you maintain stock of the packed Items and not the aggregate Item.

\n

The package Item will have Is Stock Item as No and Is Sales Item as Yes.

\n

Example:

\n

If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.

" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "column_break_eonk", + "fieldtype": "Column Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-sitemap", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "Selling", - "name": "Product Bundle", - "owner": "Administrator", + ], + "icon": "fa fa-sitemap", + "idx": 1, + "links": [], + "modified": "2023-11-22 15:20:46.805114", + "modified_by": "Administrator", + "module": "Selling", + "name": "Product Bundle", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User" + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "ASC", + "states": [] } \ No newline at end of file From 874774fe6c81c854fb3ade03e93f003c0fc8dd8a Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 22 Nov 2023 15:24:56 +0530 Subject: [PATCH 3/8] fix: filter bundle items based on disabled check --- erpnext/stock/doctype/packed_item/packed_item.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py index a9e9ad1a63..8b6f7158e0 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.py +++ b/erpnext/stock/doctype/packed_item/packed_item.py @@ -111,7 +111,7 @@ def get_product_bundle_items(item_code): product_bundle_item.uom, product_bundle_item.description, ) - .where(product_bundle.new_item_code == item_code) + .where((product_bundle.new_item_code == item_code) & (product_bundle.disabled == 0)) .orderby(product_bundle_item.idx) ) return query.run(as_dict=True) From 3543f86c639cf7383c37a5ed9a61d5787c9ebad4 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 22 Nov 2023 16:04:05 +0530 Subject: [PATCH 4/8] fix: has_product_bundle util to only check for enabled bundles --- erpnext/controllers/selling_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index d34fbeb0da..c3211b1d1c 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -352,7 +352,7 @@ class SellingController(StockController): def has_product_bundle(self, item_code): return frappe.db.sql( """select name from `tabProduct Bundle` - where new_item_code=%s and docstatus != 2""", + where new_item_code=%s and disabled=0""", item_code, ) From 67f43d37df9e77c3e59562117fa44ea76273a536 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 22 Nov 2023 16:10:49 +0530 Subject: [PATCH 5/8] fix: validation for existing bundles --- erpnext/selling/doctype/product_bundle/product_bundle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py index 4b401e7e67..2fd9cc1301 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/product_bundle.py @@ -64,7 +64,7 @@ class ProductBundle(Document): def validate_child_items(self): for item in self.items: - if frappe.db.exists("Product Bundle", item.item_code): + if frappe.db.exists("Product Bundle", {"name": item.item_code, "disabled": 0}): frappe.throw( _( "Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save" @@ -75,7 +75,7 @@ class ProductBundle(Document): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): - product_bundles = frappe.db.get_list("Product Bundle", pluck="name") + product_bundles = frappe.db.get_list("Product Bundle", {"disabled": 0}, pluck="name") item = frappe.qb.DocType("Item") return ( frappe.qb.from_(item) From 8bdb61cb87802723a0db1aaa29c30c15c740ec6b Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 22 Nov 2023 16:17:23 +0530 Subject: [PATCH 6/8] fix: condition in other bundle utils --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 2 +- erpnext/selling/doctype/sales_order/sales_order.py | 8 +++++--- erpnext/stock/get_item_details.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index e36e97bc4b..9091a77f99 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -556,7 +556,7 @@ def get_stock_availability(item_code, warehouse): return bin_qty - pos_sales_qty, is_stock_item else: is_stock_item = True - if frappe.db.exists("Product Bundle", item_code): + if frappe.db.exists("Product Bundle", {"name": item_code, "disabled": 0}): return get_bundle_availability(item_code, warehouse), is_stock_item else: is_stock_item = False diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index a97198aa78..a23599b180 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -688,7 +688,9 @@ def make_material_request(source_name, target_doc=None): "Sales Order Item": { "doctype": "Material Request Item", "field_map": {"name": "sales_order_item", "parent": "sales_order"}, - "condition": lambda item: not frappe.db.exists("Product Bundle", item.item_code) + "condition": lambda item: not frappe.db.exists( + "Product Bundle", {"name": item.item_code, "disabled": 0} + ) and get_remaining_qty(item) > 0, "postprocess": update_item, }, @@ -1309,7 +1311,7 @@ def set_delivery_date(items, sales_order): def is_product_bundle(item_code): - return frappe.db.exists("Product Bundle", item_code) + return frappe.db.exists("Product Bundle", {"name": item_code, "disabled": 0}) @frappe.whitelist() @@ -1521,7 +1523,7 @@ def get_work_order_items(sales_order, for_raw_material_request=0): product_bundle_parents = [ pb.new_item_code for pb in frappe.get_all( - "Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"] + "Product Bundle", {"new_item_code": ["in", item_codes], "disabled": 0}, ["new_item_code"] ) ] diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index c766cab0a1..d1a9cf26ac 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -149,7 +149,7 @@ def remove_standard_fields(details): def set_valuation_rate(out, args): - if frappe.db.exists("Product Bundle", args.item_code, cache=True): + if frappe.db.exists("Product Bundle", {"name": args.item_code, "disabled": 0}, cache=True): valuation_rate = 0.0 bundled_items = frappe.get_doc("Product Bundle", args.item_code) From 362f377f6127e9262ab7829a427d0a2051a77b2f Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 22 Nov 2023 16:26:09 +0530 Subject: [PATCH 7/8] fix: skip disabled bundles for non-report utils --- erpnext/stock/doctype/delivery_note/delivery_note.py | 4 ++-- erpnext/stock/doctype/item/item.py | 8 ++++++-- erpnext/stock/doctype/packed_item/packed_item.py | 2 +- erpnext/stock/doctype/pick_list/pick_list.py | 8 ++++++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 66dd33a400..f240136e9c 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -615,7 +615,7 @@ class DeliveryNote(SellingController): items_list = [item.item_code for item in self.items] return frappe.db.get_all( "Product Bundle", - filters={"new_item_code": ["in", items_list]}, + filters={"new_item_code": ["in", items_list], "disabled": 0}, pluck="name", ) @@ -938,7 +938,7 @@ def make_packing_slip(source_name, target_doc=None): }, "postprocess": update_item, "condition": lambda item: ( - not frappe.db.exists("Product Bundle", {"new_item_code": item.item_code}) + not frappe.db.exists("Product Bundle", {"new_item_code": item.item_code, "disabled": 0}) and flt(item.packed_qty) < flt(item.qty) ), }, diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d8935fe203..cb34497f28 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -512,8 +512,12 @@ class Item(Document): def validate_duplicate_product_bundles_before_merge(self, old_name, new_name): "Block merge if both old and new items have product bundles." - old_bundle = frappe.get_value("Product Bundle", filters={"new_item_code": old_name}) - new_bundle = frappe.get_value("Product Bundle", filters={"new_item_code": new_name}) + old_bundle = frappe.get_value( + "Product Bundle", filters={"new_item_code": old_name, "disabled": 0} + ) + new_bundle = frappe.get_value( + "Product Bundle", filters={"new_item_code": new_name, "disabled": 0} + ) if old_bundle and new_bundle: bundle_link = get_link_to_form("Product Bundle", old_bundle) diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py index 8b6f7158e0..35701c90de 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.py +++ b/erpnext/stock/doctype/packed_item/packed_item.py @@ -55,7 +55,7 @@ def make_packing_list(doc): def is_product_bundle(item_code: str) -> bool: - return bool(frappe.db.exists("Product Bundle", {"new_item_code": item_code})) + return bool(frappe.db.exists("Product Bundle", {"new_item_code": item_code, "disabled": 0})) def get_indexed_packed_items_table(doc): diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index ed20209577..644df3d29a 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -368,7 +368,9 @@ class PickList(Document): frappe.throw("Row #{0}: Item Code is Mandatory".format(item.idx)) if not cint( frappe.get_cached_value("Item", item.item_code, "is_stock_item") - ) and not frappe.db.exists("Product Bundle", {"new_item_code": item.item_code}): + ) and not frappe.db.exists( + "Product Bundle", {"new_item_code": item.item_code, "disabled": 0} + ): continue item_code = item.item_code reference = item.sales_order_item or item.material_request_item @@ -507,7 +509,9 @@ class PickList(Document): # bundle_item_code: Dict[component, qty] product_bundle_qty_map = {} for bundle_item_code in bundles: - bundle = frappe.get_last_doc("Product Bundle", {"new_item_code": bundle_item_code}) + bundle = frappe.get_last_doc( + "Product Bundle", {"new_item_code": bundle_item_code, "disabled": 0} + ) product_bundle_qty_map[bundle_item_code] = {item.item_code: item.qty for item in bundle.items} return product_bundle_qty_map From 16573378870bb9c6e80f39e80fa43e5b2cc0a69f Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 22 Nov 2023 16:45:14 +0530 Subject: [PATCH 8/8] chore: linting issues --- erpnext/controllers/selling_controller.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index c3211b1d1c..5575a24b35 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -350,11 +350,12 @@ class SellingController(StockController): return il def has_product_bundle(self, item_code): - return frappe.db.sql( - """select name from `tabProduct Bundle` - where new_item_code=%s and disabled=0""", - item_code, - ) + product_bundle = frappe.qb.DocType("Product Bundle") + return ( + frappe.qb.from_(product_bundle) + .select(product_bundle.name) + .where((product_bundle.new_item_code == item_code) & (product_bundle.disabled == 0)) + ).run() def get_already_delivered_qty(self, current_docname, so, so_detail): delivered_via_dn = frappe.db.sql(