Multi Pick List to Single Work order

Self-hosted DO

erpnext: 15.21.2
frappe: 15.24.1
hrms: 15.20.0

As a Food & Beverage company, I have established a Bill of Materials (BOM) that includes both raw materials (RM) and packaging materials (PM). In the work order section, I initiate a work order and create associated job cards using the BOM. Subsequently, I generate a pick list to retrieve the necessary materials, then proceed to the manufacturing area to commence the job cards.

Currently, if I choose to request all materials simultaneously using the “create pick list” option, the process completes successfully, material transfers as required and allowing me to submit job cards and transfer finished goods to the store (warehouse).

However, I prefer to request RM and PM separately (multiple pick list connected to a single work order), as packaging materials are needed much later in the process (approximately 5-6 hours later). If I try to this currently, the erpnext system doesn’t allow me to and only allows to create one time pick list

Is there a inbuilt system to accomplish the following, else how to go about creating server and client scripts. Should I create buttons on work order itself or rather have a button on pick list to auto remove items based on their source warehouse and somehow override current limitation of one picklist.

Any pointers on this would be really helpful.

Python script for pick list to split based on source warehouse, to be more specific, in new doc event, duplicate current doc to create new one, in the current doc keep items of the same source warehouse, in the duplicate new one keep items of the other source warehouse. Use one extra new field to identify whether is duplicated doc by script avoid trigger the event again.

here is almost the same logic on stock entry

if doc.purpose == 'Material Transfer for Manufacture' and not doc.flags.split_ste:         #仅针对生产发料按发料仓拆单
    last_warehouse = doc.items[-1].s_warehouse                                             #获取最后明细行发料仓    
    to_split_rows = []
    for row in doc.items:
        if row.s_warehouse != last_warehouse:
            to_split_rows.append(row)                                                      #获取待拆分行:发料仓不等于最后明细行发料仓
    if to_split_rows:
        other_warehouses = {row.s_warehouse for row in to_split_rows}                      #获取其它发料仓
        for warehouse in other_warehouses:                                                 #拆分(遍历)其它发料仓    
            new_ste = frappe.copy_doc(doc,ignore_no_copy=False)
            items= []                                                                      #复制当前发料单到拆分后发料单
            for row in to_split_rows:
                if row.s_warehouse == warehouse:
                    items.append(row)                                                      #复制后仅保留对应发料仓明细行
            new_ste.items = items
            new_ste.flags.split_ste = True                                                 #设置标志位,触发此段代码时,不拆单
            new_ste.insert(ignore_permissions = True)
        items = [] 
        for row in doc.items:
            if row.s_warehouse == last_warehouse:
                items.append(row)  
        doc.items = items  
1 Like

Thanks for the response @szufisher

Your solution did provide an idea to create the split functionality buttons on pick list doctype which are working flawlessly, so thanks for that

Instead of creating pick lists automatically, I just wanna bypass/override the restriction of only able to transfer items once and restricted according to “Qty to Manufacture” in work order doctype. So, after creating a pick list once inside the limit and only for RM, on clicking the create pick list button on work order doctype I get

If I enter anything above zero I get

As I want the PM material requested very later I want the manufacturing department to send the pick list for PM pick list later

Do suggest some available routes to accomplish the aforementioned. I hope I was able to clearly explain my requirement

override the standard whitelist method, changes as following

issue created Multi Pick List to Single Work order · Issue #41664 · frappe/erpnext · GitHub

1 Like

Didn’t work out

  1. Created a custom app

  2. Created a overrides folder and in it inserted the following script

frappe-user/frappe-bench/apps/custom_app/custom_app/overrides/work_order_custom.py

@frappe.whitelist()
def custom_create_pick_list(source_name, target_doc=None, for_qty=None):
	for_qty = for_qty or json.loads(target_doc).get("for_qty")
	max_finished_goods_qty = frappe.db.get_value("Work Order", source_name, "qty")

	def update_item_quantity(source, target, source_parent):
		pending_to_issue = flt(source.required_qty) - flt(source.transferred_qty)
		desire_to_transfer = flt(source.required_qty) / max_finished_goods_qty * flt(for_qty)

		qty = 0
		if desire_to_transfer and desire_to_transfer <= pending_to_issue:
			qty = desire_to_transfer
		elif pending_to_issue > 0:
			qty = pending_to_issue

		if qty:
			target.qty = qty
			target.stock_qty = qty
			target.uom = frappe.get_value("Item", source.item_code, "stock_uom")
			target.stock_uom = target.uom
			target.conversion_factor = 1
		else:
			target.delete()

	doc = get_mapped_doc(
		"Work Order",
		source_name,
		{
			"Work Order": {"doctype": "Pick List", "validation": {"docstatus": ["=", 1]}},
			"Work Order Item": {
				"doctype": "Pick List Item",
				"postprocess": update_item_quantity,
				"condition": lambda doc: abs(doc.transferred_qty) > abs(doc.required_qty),
			},
		},
		target_doc,
	)

	doc.for_qty = for_qty

	doc.set_item_locations()

	return doc
  1. Added the path to hooks.py file

my work_order.py path was:
frappe-user/frappe-bench/apps/erpnext/erpnext/manufacturing/doctype/work_order/work_order.py

override_whitelisted_methods = {
    "erpnext.erpnext.manufacturing.doctype.work_order.work_order.create_pick_list": "custom_app.overrides.work_order_custom.custom_create_pick_list",
}

Finally using bench cleared cache, migrated and restarted, but this issue still persists.

looks like you followed my code exactly, suggest to debug and ensure your override works.
you can use frappe.errprint(‘your debug message’) in your code, then check the message in browser console.