From 36c5e8a14fd9cebac5c5f22cf062ca3a47a416f0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 21 Apr 2022 12:33:38 +0530 Subject: [PATCH] feat: Pick list from SO with Product Bundle --- .../doctype/sales_order/sales_order.py | 31 +++++++++++++++++-- .../doctype/packed_item/packed_item.json | 9 +++++- .../stock/doctype/packed_item/packed_item.py | 6 +++- .../stock/doctype/pick_list/test_pick_list.py | 8 +++-- .../pick_list_item/pick_list_item.json | 13 +++++++- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index d3b4286be54..1d172ad0bf3 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1232,10 +1232,27 @@ def make_inter_company_purchase_order(source_name, target_doc=None): @frappe.whitelist() def create_pick_list(source_name, target_doc=None): - def update_item_quantity(source, target, source_parent): + from erpnext.stock.doctype.packed_item.packed_item import is_product_bundle + + def update_item_quantity(source, target, source_parent) -> None: target.qty = flt(source.qty) - flt(source.delivered_qty) target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor) + def update_packed_item_qty(source, target, source_parent) -> None: + qty = flt(source.qty) + for item in source_parent.items: + if source.parent_detail_docname == item.name: + pending_percent = (item.qty - item.delivered_qty) / item.qty + target.qty = target.stock_qty = qty * pending_percent + return + + def should_pick_order_item(item) -> bool: + return ( + abs(item.delivered_qty) < abs(item.qty) + and item.delivered_by_supplier != 1 + and not is_product_bundle(item.item_code) + ) + doc = get_mapped_doc( "Sales Order", source_name, @@ -1245,8 +1262,16 @@ def create_pick_list(source_name, target_doc=None): "doctype": "Pick List Item", "field_map": {"parent": "sales_order", "name": "sales_order_item"}, "postprocess": update_item_quantity, - "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) - and doc.delivered_by_supplier != 1, + "condition": should_pick_order_item, + }, + "Packed Item": { + "doctype": "Pick List Item", + "field_map": { + "parent": "sales_order", + "name": "sales_order_item", + "parent_detail_docname": "product_bundle_item", + }, + "postprocess": update_packed_item_qty, }, }, target_doc, diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json index e94c34d7adc..4e67c84a0bc 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.json +++ b/erpnext/stock/doctype/packed_item/packed_item.json @@ -29,6 +29,7 @@ "ordered_qty", "column_break_16", "incoming_rate", + "picked_qty", "page_break", "prevdoc_doctype", "parent_detail_docname" @@ -234,13 +235,19 @@ "label": "Ordered Qty", "no_copy": 1, "read_only": 1 + }, + { + "depends_on": "picked_qty", + "fieldname": "picked_qty", + "fieldtype": "Float", + "label": "Picked Qty" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-03-10 15:42:00.265915", + "modified": "2022-04-21 08:05:29.785362", "modified_by": "Administrator", "module": "Stock", "name": "Packed Item", diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py index 026dd4e122a..4d05d7a345c 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.py +++ b/erpnext/stock/doctype/packed_item/packed_item.py @@ -32,7 +32,7 @@ def make_packing_list(doc): reset = reset_packing_list(doc) for item_row in doc.get("items"): - if frappe.db.exists("Product Bundle", {"new_item_code": item_row.item_code}): + if is_product_bundle(item_row.item_code): for bundle_item in get_product_bundle_items(item_row.item_code): pi_row = add_packed_item_row( doc=doc, @@ -54,6 +54,10 @@ def make_packing_list(doc): set_product_bundle_rate_amount(doc, parent_items_price) # set price in bundle item +def is_product_bundle(item_code: str) -> bool: + return bool(frappe.db.exists("Product Bundle", {"new_item_code": item_code})) + + def get_indexed_packed_items_table(doc): """ Create dict from stale packed items table like: diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 1ba46f74148..d1a9472f1f6 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -10,6 +10,7 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde from erpnext.stock.doctype.item.test_item import create_item, make_item from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import ( EmptyStockReconciliationItemsError, ) @@ -582,16 +583,19 @@ class TestPickList(FrappeTestCase): def test_picklist_with_bundles(self): # from test_records.json + warehouse = "_Test Warehouse - _TC" bundle = "_Test Product Bundle Item" bundle_items = {"_Test Item": 5, "_Test Item Home Desktop 100": 2} + for item in bundle_items: + make_stock_entry(item=item, to_warehouse=warehouse, qty=10, rate=10) - so = make_sales_order(item_code=bundle, qty=1) + so = make_sales_order(item_code=bundle, qty=3) pl = create_pick_list(so.name) pl.save() self.assertEqual(len(pl.locations), 2) for item in pl.locations: - self.assertEqual(item.stock_qty, bundle_items[item.item_code]) + self.assertEqual(item.stock_qty, bundle_items[item.item_code] * 3) # def test_pick_list_skips_items_in_expired_batch(self): # pass diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json index 805286ddcc0..a96ebfcdee6 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -27,6 +27,7 @@ "column_break_15", "sales_order", "sales_order_item", + "product_bundle_item", "material_request", "material_request_item" ], @@ -146,6 +147,7 @@ { "fieldname": "sales_order_item", "fieldtype": "Data", + "hidden": 1, "label": "Sales Order Item", "read_only": 1 }, @@ -177,11 +179,19 @@ "fieldtype": "Data", "label": "Item Group", "read_only": 1 + }, + { + "description": "product bundle item row's name in sales order. Also indicates that picked item is to be used for a product bundle", + "fieldname": "product_bundle_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Product Bundle Item", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2021-09-28 12:02:16.923056", + "modified": "2022-04-22 05:27:38.497997", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", @@ -190,5 +200,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file