mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-20 11:22:28 +00:00
fix(stock): link job card in stock entry created from pick list (backport #57031)
A Stock Entry created from a Pick List against a job card's Material Request never set job_card, job_card_item, fg_completed_qty or the 'Material Transfer for Manufacture' purpose, so the Job Card did not recognize the transfer and blocked submission. The WIP warehouse was also not populated. Route such pick lists through a job-card-aware branch mirroring the direct Material Request -> Stock Entry mapper, and set the purpose to 'Material Transfer for Manufacture' in the work order branch so the WO -> MR -> Pick List flow updates the work order too.
This commit is contained in:
@@ -662,6 +662,48 @@ class TestJobCard(ERPNextTestSuite):
|
||||
self.assertEqual(ste.from_bom, 1.0)
|
||||
self.assertEqual(ste.bom_no, work_order.bom_no)
|
||||
|
||||
def test_job_card_material_transfer_via_pick_list(self):
|
||||
from erpnext.stock.doctype.material_request.material_request import create_pick_list
|
||||
from erpnext.stock.doctype.pick_list.pick_list import (
|
||||
create_stock_entry as create_stock_entry_from_pick_list,
|
||||
)
|
||||
|
||||
create_bom_with_multiple_operations()
|
||||
work_order = make_wo_with_transfer_against_jc()
|
||||
|
||||
for item in work_order.required_items:
|
||||
make_stock_entry(
|
||||
item_code=item.item_code,
|
||||
target=item.source_warehouse,
|
||||
qty=item.required_qty * 2,
|
||||
basic_rate=100,
|
||||
)
|
||||
|
||||
job_card_name = frappe.db.get_value("Job Card", {"work_order": work_order.name}, "name")
|
||||
job_card = frappe.get_doc("Job Card", job_card_name)
|
||||
|
||||
mr = make_material_request(job_card_name)
|
||||
mr.schedule_date = today()
|
||||
mr.submit()
|
||||
|
||||
pick_list = create_pick_list(mr.name)
|
||||
pick_list.submit()
|
||||
|
||||
ste = frappe.get_doc(create_stock_entry_from_pick_list(pick_list.as_dict()))
|
||||
self.assertEqual(ste.purpose, "Material Transfer for Manufacture")
|
||||
self.assertEqual(ste.job_card, job_card_name)
|
||||
self.assertEqual(ste.work_order, work_order.name)
|
||||
self.assertEqual(ste.fg_completed_qty, job_card.for_quantity)
|
||||
for row in ste.items:
|
||||
self.assertEqual(row.t_warehouse, job_card.wip_warehouse)
|
||||
self.assertTrue(row.job_card_item)
|
||||
|
||||
ste.insert()
|
||||
ste.submit()
|
||||
|
||||
job_card.reload()
|
||||
self.assertEqual(job_card.transferred_qty, job_card.for_quantity)
|
||||
|
||||
def test_job_card_proccess_qty_and_completed_qty(self):
|
||||
from erpnext.manufacturing.doctype.routing.test_routing import (
|
||||
create_routing,
|
||||
|
||||
@@ -1585,15 +1585,22 @@ def create_stock_entry(pick_list: str | dict):
|
||||
stock_entry.pick_list = pick_list.get("name")
|
||||
stock_entry.purpose = pick_list.get("purpose")
|
||||
stock_entry.company = pick_list.get("company")
|
||||
stock_entry.set_stock_entry_type()
|
||||
|
||||
if pick_list.get("work_order"):
|
||||
job_card = pick_list.get("material_request") and frappe.db.get_value(
|
||||
"Material Request", pick_list.get("material_request"), "job_card"
|
||||
)
|
||||
|
||||
if job_card:
|
||||
stock_entry = update_stock_entry_based_on_job_card(pick_list, stock_entry, job_card)
|
||||
elif pick_list.get("work_order"):
|
||||
stock_entry = update_stock_entry_based_on_work_order(pick_list, stock_entry)
|
||||
elif pick_list.get("material_request"):
|
||||
stock_entry = update_stock_entry_based_on_material_request(pick_list, stock_entry)
|
||||
else:
|
||||
stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry)
|
||||
|
||||
stock_entry.set_stock_entry_type()
|
||||
|
||||
if not stock_entry.get("items"):
|
||||
return frappe.msgprint(_("All picked items have already been transferred against this Pick List"))
|
||||
|
||||
@@ -1684,9 +1691,57 @@ def stock_entry_exists(pick_list_name):
|
||||
return frappe.db.exists("Stock Entry", {"pick_list": pick_list_name})
|
||||
|
||||
|
||||
def update_stock_entry_based_on_job_card(pick_list, stock_entry, job_card):
|
||||
job_card = frappe.db.get_value(
|
||||
"Job Card",
|
||||
job_card,
|
||||
["name", "work_order", "bom_no", "semi_fg_bom", "for_quantity", "transferred_qty", "wip_warehouse"],
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
stock_entry.purpose = "Material Transfer for Manufacture"
|
||||
stock_entry.job_card = job_card.name
|
||||
stock_entry.work_order = job_card.work_order
|
||||
stock_entry.from_bom = 1
|
||||
stock_entry.bom_no = job_card.semi_fg_bom or job_card.bom_no
|
||||
stock_entry.fg_completed_qty = max(flt(job_card.for_quantity) - flt(job_card.transferred_qty), 0)
|
||||
stock_entry.to_warehouse = job_card.wip_warehouse
|
||||
|
||||
job_card_items = get_job_card_items_by_material_request_item(pick_list)
|
||||
|
||||
for location in pick_list.locations:
|
||||
if get_pending_transfer_stock_qty(location) <= 0:
|
||||
continue
|
||||
item = frappe._dict()
|
||||
update_common_item_properties(item, location)
|
||||
item.t_warehouse = job_card.wip_warehouse
|
||||
item.job_card_item = job_card_items.get(location.material_request_item)
|
||||
stock_entry.append("items", item)
|
||||
|
||||
return stock_entry
|
||||
|
||||
|
||||
def get_job_card_items_by_material_request_item(pick_list):
|
||||
material_request_items = [
|
||||
location.material_request_item for location in pick_list.locations if location.material_request_item
|
||||
]
|
||||
if not material_request_items:
|
||||
return {}
|
||||
|
||||
return dict(
|
||||
frappe.get_all(
|
||||
"Material Request Item",
|
||||
filters={"name": ["in", material_request_items]},
|
||||
fields=["name", "job_card_item"],
|
||||
as_list=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def update_stock_entry_based_on_work_order(pick_list, stock_entry):
|
||||
work_order = frappe.get_doc("Work Order", pick_list.get("work_order"))
|
||||
|
||||
stock_entry.purpose = "Material Transfer for Manufacture"
|
||||
stock_entry.work_order = work_order.name
|
||||
stock_entry.company = work_order.company
|
||||
stock_entry.from_bom = 1
|
||||
|
||||
Reference in New Issue
Block a user