diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 5ab291bb56a..63ead3d4e26 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -207,9 +207,9 @@ frappe.ui.form.on("Production Plan", {
set_field_options("projected_qty_formula", projected_qty_formula);
},
- has_unreserved_stock(frm, table) {
+ has_unreserved_stock(frm, table, qty_field = "required_qty") {
let has_unreserved_stock = frm.doc[table].some(
- (item) => flt(item.qty) > flt(item.stock_reserved_qty)
+ (item) => flt(item[qty_field]) > flt(item.stock_reserved_qty)
);
return has_unreserved_stock;
@@ -249,7 +249,7 @@ frappe.ui.form.on("Production Plan", {
setup_stock_reservation_for_raw_materials(frm) {
if (frm.doc.docstatus === 1 && frm.doc.reserve_stock) {
- if (frm.events.has_unreserved_stock(frm, "mr_items")) {
+ if (frm.events.has_unreserved_stock(frm, "mr_items", "required_bom_qty")) {
frm.add_custom_button(
__("Reserve for Raw Materials"),
() => erpnext.stock_reservation.make_entries(frm, "mr_items"),
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 899e28e0a1e..8e40804e761 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -1619,18 +1619,20 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx")))
if bom_no:
- if data.get("include_exploded_items") and doc.get("skip_available_sub_assembly_item"):
- item_details = {}
- if doc.get("sub_assembly_items"):
- item_details = get_raw_materials_of_sub_assembly_items(
- so_item_details[doc.get("sales_order")].keys() if so_item_details else [],
- item_details,
- company,
- bom_no,
- include_non_stock_items,
- sub_assembly_items,
- planned_qty=planned_qty,
- )
+ if (
+ data.get("include_exploded_items")
+ and doc.get("skip_available_sub_assembly_item")
+ and doc.get("sub_assembly_items")
+ ):
+ item_details = get_raw_materials_of_sub_assembly_items(
+ so_item_details[doc.get("sales_order")].keys() if so_item_details else [],
+ item_details,
+ company,
+ bom_no,
+ include_non_stock_items,
+ sub_assembly_items,
+ planned_qty=planned_qty,
+ )
elif data.get("include_exploded_items") and include_subcontracted_items:
# fetch exploded items from BOM
@@ -2089,7 +2091,7 @@ def get_reserved_qty_for_sub_assembly(item_code, warehouse):
def make_stock_reservation_entries(doc, items=None, table_name=None, notify=False):
if isinstance(doc, str):
doc = parse_json(doc)
- doc = frappe.get_doc("Work Order", doc.get("name"))
+ doc = frappe.get_doc("Production Plan", doc.get("name"))
if items and isinstance(items, str):
items = parse_json(items)
@@ -2113,9 +2115,22 @@ def make_stock_reservation_entries(doc, items=None, table_name=None, notify=Fals
sre = StockReservation(doc, items=items, kwargs=mapper[child_table_name], notify=notify)
if doc.docstatus == 1:
- sre.make_stock_reservation_entries()
- frappe.msgprint(_("Stock Reservation Entries Created"), alert=True)
+ sre_created = sre.make_stock_reservation_entries()
+ if sre_created:
+ frappe.msgprint(_("Stock Reservation Entries Created"), alert=True)
elif doc.docstatus == 2:
sre.cancel_stock_reservation_entries()
doc.reload()
+
+
+@frappe.whitelist()
+def cancel_stock_reservation_entries(doc, sre_list):
+ if isinstance(doc, str):
+ doc = parse_json(doc)
+ doc = frappe.get_doc("Production Plan", doc.get("name"))
+
+ sre = StockReservation(doc)
+ sre.cancel_stock_reservation_entries(sre_list)
+
+ doc.reload()
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 15342f9d6be..2010161170b 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -1415,8 +1415,12 @@ class TestProductionPlan(IntegrationTestCase):
do_not_submit=1,
skip_available_sub_assembly_item=1,
warehouse="_Test Warehouse - _TC",
+ sub_assembly_warehouse="_Test Warehouse - _TC",
)
+ plan.get_sub_assembly_items()
+ plan.save()
+
items = get_items_for_material_requests(
plan.as_dict(), warehouses=[{"warehouse": "_Test Warehouse - _TC"}]
)
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 5a0af932bf0..10254256026 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -1426,7 +1426,7 @@ class WorkOrder(Document):
return
item_list = list(items.values())
- make_stock_reservation_entries(self, item_list, notify=True)
+ make_stock_reservation_entries(self, item_list, is_transfer=False, notify=True)
def get_list_of_materials_for_reservation(self, stock_entry):
items = frappe._dict()
@@ -1648,7 +1648,7 @@ class WorkOrder(Document):
@frappe.whitelist()
-def make_stock_reservation_entries(doc, items=None, table_name=None, notify=False):
+def make_stock_reservation_entries(doc, items=None, table_name=None, is_transfer=True, notify=False):
if isinstance(doc, str):
doc = parse_json(doc)
doc = frappe.get_doc("Work Order", doc.get("name"))
@@ -1658,14 +1658,15 @@ def make_stock_reservation_entries(doc, items=None, table_name=None, notify=Fals
sre = StockReservation(doc, items=items, notify=notify)
if doc.docstatus == 1:
- if doc.production_plan:
+ if doc.production_plan and is_transfer:
sre.transfer_reservation_entries_to(
doc.production_plan, from_doctype="Production Plan", to_doctype="Work Order"
)
else:
- sre.make_stock_reservation_entries()
+ sre_created = sre.make_stock_reservation_entries()
+ if sre_created:
+ frappe.msgprint(_("Stock Reservation Entries Created"), alert=True)
- frappe.msgprint(_("Stock Reservation Entries Created"), alert=True)
elif doc.docstatus == 2:
sre.cancel_stock_reservation_entries()
diff --git a/erpnext/public/js/stock_reservation.js b/erpnext/public/js/stock_reservation.js
index 854212ee5ae..4af158a132b 100644
--- a/erpnext/public/js/stock_reservation.js
+++ b/erpnext/public/js/stock_reservation.js
@@ -30,15 +30,15 @@ $.extend(erpnext.stock_reservation, {
params["qty_field"] = {
"Sales Order": "stock_qty",
"Work Order": "required_qty",
+ "Production Plan": "required_qty",
}[frm.doc.doctype];
if (frm.doc.doctype === "Production Plan") {
if (table_name === "sub_assembly_items") {
- params["qty_field"] = "qty";
params["item_code_field"] = "production_item";
params["warehouse_field"] = "fg_warehouse";
} else {
- params["qty_field"] = "quantity";
+ params["qty_field"] = "required_bom_qty";
}
}
@@ -50,6 +50,8 @@ $.extend(erpnext.stock_reservation, {
params["method"] = {
"Sales Order": "delivered_qty",
+ "Production Plan":
+ "erpnext.manufacturing.doctype.production_plan.production_plan.make_stock_reservation_entries",
"Work Order":
"erpnext.manufacturing.doctype.work_order.work_order.make_stock_reservation_entries",
}[frm.doc.doctype];
@@ -141,6 +143,10 @@ $.extend(erpnext.stock_reservation, {
},
render_items(frm, parms) {
+ if (!frm.doc.reserve_stock) {
+ return;
+ }
+
let dialog = erpnext.stock_reservation.dialog;
let field = frappe.scrub(parms.child_doctype);
@@ -155,25 +161,23 @@ $.extend(erpnext.stock_reservation, {
let warehouse_field = parms.warehouse_field || "warehouse";
frm.doc[parms.table_name].forEach((item) => {
- if (frm.doc.reserve_stock) {
- let unreserved_qty =
- (flt(item[qty_field]) -
- (item.stock_reserved_qty
- ? flt(item.stock_reserved_qty)
- : flt(item[dispatch_qty_field]) * flt(item.conversion_factor || 1))) /
- flt(item.conversion_factor || 1);
+ let unreserved_qty =
+ (flt(item[qty_field]) -
+ (item.stock_reserved_qty
+ ? flt(item.stock_reserved_qty)
+ : flt(item[dispatch_qty_field]) * flt(item.conversion_factor || 1))) /
+ flt(item.conversion_factor || 1);
- if (unreserved_qty > 0) {
- let args = {
- __checked: 1,
- item_code: item[item_code_field] || item.item_code,
- warehouse: item[warehouse_field] || item.warehouse || item.source_warehouse,
- };
+ if (unreserved_qty > 0) {
+ let args = {
+ __checked: 1,
+ item_code: item[item_code_field] || item.item_code,
+ warehouse: item[warehouse_field] || item.warehouse || item.source_warehouse,
+ };
- args[field] = item.name;
- args[qty_field] = unreserved_qty;
- dialog.fields_dict.items.df.data.push(args);
- }
+ args[field] = item.name;
+ args[qty_field] = unreserved_qty;
+ dialog.fields_dict.items.df.data.push(args);
}
});
@@ -257,11 +261,17 @@ $.extend(erpnext.stock_reservation, {
},
cancel_stock_reservation(dialog, frm) {
- var data = { sr_entries: dialog.fields_dict.sr_entries.grid.get_selected_children() };
+ let data = { sr_entries: dialog.fields_dict.sr_entries.grid.get_selected_children() };
+ let method = "erpnext.manufacturing.doctype.work_order.work_order.cancel_stock_reservation_entries";
+
+ if (frm.doc.doctype === "Production Plan") {
+ method =
+ "erpnext.manufacturing.doctype.production_plan.production_plan.cancel_stock_reservation_entries";
+ }
if (data.sr_entries?.length > 0) {
frappe.call({
- method: "erpnext.manufacturing.doctype.work_order.work_order.cancel_stock_reservation_entries",
+ method: method,
args: {
doc: frm.doc,
sre_list: data.sr_entries.map((item) => item.sre),
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index a764a006b0c..68be8499ad2 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -16,11 +16,11 @@ frappe.ui.form.on("Item", {
let msg = __(
"Changing the valuation method to Moving Average will affect new transactions. If backdated entries are added, earlier FIFO-based entries will be reposted, which may change closing balances."
);
- msg += "
";
+ msg += "
";
msg += __(
"Also you can't switch back to FIFO after setting the valuation method to Moving Average for this item."
);
- msg += "
";
+ msg += "
";
msg += __("Do you want to change valuation method?");
frappe.confirm(
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 11f9ee932ee..eb1658509bd 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -1908,19 +1908,33 @@ def get_reserved_voucher_details(kwargs):
value = {
"Delivery Note": ["Delivery Note Item", "against_sales_order"],
- }.get(kwargs.get("voucher_type"))
+ "Stock Entry": ["Stock Entry", "work_order"],
+ "Work Order": ["Work Order", "production_plan"],
+ }.get(kwargs.get("sabb_voucher_type"))
if not value or not kwargs.get("sabb_voucher_no"):
return reserved_voucher_details
- reserved_voucher_details = frappe.get_all(
- value[0],
- pluck=value[1],
- filters={
+ voucher_based_filters = {
+ "Delivery Note": {
"name": kwargs.get("sabb_voucher_detail_no"),
"parent": kwargs.get("sabb_voucher_no"),
"docstatus": 1,
},
+ "Stock Entry": {
+ "name": kwargs.get("sabb_voucher_no"),
+ "docstatus": 1,
+ },
+ "Work Order": {
+ "name": kwargs.get("sabb_voucher_no"),
+ "docstatus": 1,
+ },
+ }.get(kwargs.get("sabb_voucher_type"))
+
+ reserved_voucher_details = frappe.get_all(
+ value[0],
+ pluck=value[1],
+ filters=voucher_based_filters,
)
return reserved_voucher_details
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
index b9a5593afc1..81047bd6d09 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
@@ -1044,6 +1044,7 @@ class StockReservation:
child_doctype = frappe.scrub(self.doc.doctype + " Item")
+ is_sre_created = False
for item in items:
sre = frappe.new_doc("Stock Reservation Entry")
if isinstance(item, dict):
@@ -1106,6 +1107,9 @@ class StockReservation:
self.set_serial_batch(sre, item.serial_and_batch_bundles)
sre.submit()
+ is_sre_created = True
+
+ return is_sre_created
def set_serial_batch(self, sre, serial_batch_bundles):
bundle_details = frappe.get_all(