diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index 248b2f85ba4..6033765f755 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -355,6 +355,43 @@ class TestJobCard(ERPNextTestSuite): self.assertEqual(transfer_entry.fg_completed_qty, 1) self.assertEqual(job_card.transferred_qty, 1) + def test_material_request_stock_entry_uses_job_card_coverage(self): + from erpnext.stock.doctype.material_request.mapper import make_stock_entry + + self.transfer_material_against = "Job Card" + self.source_warehouse = "Stores - _TC" + job_card = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name}) + mr = make_material_request(job_card.name) + mr.schedule_date = today() + for row in mr.items: + row.qty = flt(row.qty) / 2 + row.stock_qty = flt(row.stock_qty) / 2 + mr.submit() + + stock_entry = make_stock_entry(mr.name) + self.assertEqual(stock_entry.fg_completed_qty, job_card.for_quantity / 2) + + selected_row = mr.items[0] + try: + frappe.flags.selected_children = {"items": [selected_row.name]} + selected_stock_entry = make_stock_entry(mr.name) + finally: + frappe.flags.selected_children = None + + self.assertEqual( + [row.job_card_item for row in selected_stock_entry.items], [selected_row.job_card_item] + ) + self.assertEqual(selected_stock_entry.fg_completed_qty, 0) + + for row in mr.items: + transferred_qty = flt(row.stock_qty) / 2 + frappe.db.set_value("Job Card Item", row.job_card_item, "transferred_qty", transferred_qty) + frappe.db.set_value(row.doctype, row.name, "ordered_qty", transferred_qty) + mr.reload() + + repeated_stock_entry = make_stock_entry(mr.name) + self.assertEqual(repeated_stock_entry.fg_completed_qty, job_card.for_quantity / 4) + @ERPNextTestSuite.change_settings("Manufacturing Settings", {"job_card_excess_transfer": 1}) def test_job_card_excess_material_transfer(self): "Test transferring more than required RM against Job Card." @@ -1017,6 +1054,7 @@ class TestJobCard(ERPNextTestSuite): self.assertEqual(ste.job_card, job_card_name) self.assertEqual(ste.from_bom, 1.0) self.assertEqual(ste.bom_no, work_order.bom_no) + self.assertEqual(ste.fg_completed_qty, frappe.get_value("Job Card", job_card_name, "for_quantity")) def test_job_card_material_transfer_via_pick_list(self): from erpnext.stock.doctype.material_request.mapper import create_pick_list diff --git a/erpnext/stock/doctype/material_request/mapper.py b/erpnext/stock/doctype/material_request/mapper.py index ea6fd98fdca..3a98a606005 100644 --- a/erpnext/stock/doctype/material_request/mapper.py +++ b/erpnext/stock/doctype/material_request/mapper.py @@ -385,8 +385,16 @@ def make_stock_entry(source_name: str, target_doc: str | dict | Document | None target.bom_no = work_order_details.bom_no target.use_multi_level_bom = work_order_details.use_multi_level_bom target.from_bom = 1 - # not fg-qty-driven, mirrors the Pick List -> Stock Entry transfer for this Work Order - target.fg_completed_qty = 0 + if not source.job_card: + # not fg-qty-driven, mirrors the Pick List -> Stock Entry transfer for this Work Order + target.fg_completed_qty = 0 + + if source.job_card: + from erpnext.stock.doctype.stock_entry.services.material_transfer import ( + MaterialTransferForManufactureStockEntry, + ) + + MaterialTransferForManufactureStockEntry(target).cap_completed_qty_to_material_coverage() doclist = get_mapped_doc( "Material Request", diff --git a/erpnext/stock/doctype/stock_entry/services/material_transfer.py b/erpnext/stock/doctype/stock_entry/services/material_transfer.py index 87376b86cb5..8012393faaa 100644 --- a/erpnext/stock/doctype/stock_entry/services/material_transfer.py +++ b/erpnext/stock/doctype/stock_entry/services/material_transfer.py @@ -193,6 +193,9 @@ class MaterialTransferForManufactureStockEntry(BaseMaterialTransferStockEntry): if not self._is_overproduction_allowed(flt(self.wo_doc.qty)): return + self.cap_completed_qty_to_material_coverage() + + def cap_completed_qty_to_material_coverage(self): required_qty, transferred_qty, target_qty, precision = self._get_material_coverage_data() if not required_qty: return @@ -206,7 +209,7 @@ class MaterialTransferForManufactureStockEntry(BaseMaterialTransferStockEntry): material_reference = row.original_item or row.item_code transferred = flt(row.qty) * flt(row.conversion_factor or 1) - if row.s_warehouse and material_reference in required_qty: + if material_reference in required_qty and (self.doc.job_card or row.s_warehouse): transferred_qty[material_reference] += transferred covered_after = self._get_covered_qty(required_qty, transferred_qty, target_qty, precision)