fix: auto batch not set for raw materials in subcontracting receipt

(cherry picked from commit 23f9d4c600)

# Conflicts:
#	erpnext/controllers/subcontracting_controller.py
This commit is contained in:
Rohit Waghchaure
2025-09-25 22:30:06 +05:30
committed by Mergify
parent b18afa8bdd
commit 6c8e8384d5
3 changed files with 105 additions and 5 deletions

View File

@@ -12,6 +12,12 @@ from frappe.utils import cint, flt, get_link_to_form
from erpnext.controllers.stock_controller import StockController
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
<<<<<<< HEAD
=======
combine_datetime,
get_auto_batch_nos,
get_available_serial_nos,
>>>>>>> 23f9d4c600 (fix: auto batch not set for raw materials in subcontracting receipt)
get_voucher_wise_serial_batch_from_bundle,
)
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -52,9 +58,42 @@ class SubcontractingController(StockController):
if self.doctype in ["Subcontracting Order", "Subcontracting Receipt"]:
self.validate_items()
self.create_raw_materials_supplied()
self.set_valuation_rate_for_rm()
else:
super().validate()
def set_valuation_rate_for_rm(self):
rate_changed = False
if self.doctype == "Subcontracting Receipt":
for row in self.supplied_items:
kwargs = frappe._dict(
{
"item_code": row.rm_item_code,
"warehouse": self.supplier_warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"qty": flt(row.consumed_qty) * (-1 if not self.is_return else 1),
"voucher_type": self.doctype,
"voucher_no": self.name,
"company": self.company,
"serial_and_batch_bundle": row.serial_and_batch_bundle,
"voucher_detail_no": row.name,
"batch_no": row.batch_no,
"serial_no": row.serial_no,
"use_serial_batch_fields": row.use_serial_batch_fields,
}
)
rate = get_incoming_rate(kwargs)
precision = frappe.get_precision("Subcontracting Receipt Supplied Item", "rate")
if flt(rate, precision) != flt(row.rate, precision):
row.rate = rate
row.amount = flt(row.consumed_qty) * flt(rate)
rate_changed = True
if rate_changed:
self.calculate_items_qty_and_amount()
def validate_rejected_warehouse(self):
for item in self.get("items"):
if flt(item.rejected_qty) and not item.rejected_warehouse:
@@ -610,6 +649,64 @@ class SubcontractingController(StockController):
self.set_rate_for_supplied_items(rm_obj, item_row)
elif self.backflush_based_on == "BOM":
self.update_rate_for_supplied_items()
self.set_batch_for_supplied_items()
def set_batch_for_supplied_items(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos_for_outward
from erpnext.stock.get_item_details import get_filtered_serial_nos
for row in self.supplied_items:
item_details = frappe.get_cached_value(
"Item", row.rm_item_code, ["has_batch_no", "has_serial_no"], as_dict=1
)
if not item_details.has_batch_no and not item_details.has_serial_no:
continue
if not row.use_serial_batch_fields:
continue
kwargs = frappe._dict(
{
"item_code": row.rm_item_code,
"warehouse": self.supplier_warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"qty": flt(row.consumed_qty),
}
)
if item_details.has_serial_no and not row.serial_and_batch_bundle and not row.serial_no:
serial_nos = get_available_serial_nos(kwargs)
if serial_nos:
serial_nos = [sn.get("serial_no") for sn in serial_nos]
serial_nos = get_filtered_serial_nos(serial_nos, self, "supplied_items")
row.serial_no = "\n".join(serial_nos)
elif item_details.has_batch_no and not row.serial_and_batch_bundle and not row.batch_no:
batches = get_auto_batch_nos(kwargs)
if batches:
consumed_qty = row.consumed_qty
for index, d in enumerate(batches):
if consumed_qty <= 0:
break
if index == 0:
row.batch_no = d.get("batch_no")
row.consumed_qty = d.get("qty")
consumed_qty -= d.get("qty")
else:
new_row = self.append("supplied_items", {})
new_row.update(frappe.copy_doc(row).as_dict())
new_row.update(
{
"consumed_qty": d.get("qty"),
"batch_no": d.get("batch_no"),
"rate": row.rate,
"amount": flt(d.get("qty")) * flt(row.rate),
}
)
consumed_qty -= d.get("qty")
def update_rate_for_supplied_items(self):
if self.doctype != "Subcontracting Receipt":

View File

@@ -260,10 +260,13 @@ def filter_batches(batches, doc):
del batches[row.get("batch_no")]
def get_filtered_serial_nos(serial_nos, doc):
def get_filtered_serial_nos(serial_nos, doc, table=None):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
for row in doc.get("items"):
if not table:
table = "items"
for row in doc.get(table):
if row.get("serial_no"):
for serial_no in get_serial_nos(row.get("serial_no")):
if serial_no in serial_nos:

View File

@@ -740,13 +740,13 @@ class TestSubcontractingReceipt(FrappeTestCase):
for row in scr.supplied_items:
self.assertEqual(row.rate, 300.00)
self.assertTrue(row.serial_and_batch_bundle)
auto_created_serial_batch = frappe.db.get_value(
serial_and_batch_bundle = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_no": scr.name, "voucher_detail_no": row.name},
"auto_created_serial_and_batch_bundle",
"serial_and_batch_bundle",
)
self.assertTrue(auto_created_serial_batch)
self.assertTrue(serial_and_batch_bundle)
self.assertEqual(scr.items[0].rm_cost_per_qty, 900)
self.assertEqual(scr.items[0].service_cost_per_qty, 100)