refactor(minor): `Stock Reservation Entry`
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 26ca012..e36d576 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -346,7 +346,7 @@
"""Raises an exception if there is any reserved stock for the items in the Stock Reconciliation."""
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
- get_sre_reserved_qty_for_item_and_warehouse as get_sre_reserved_qty_details,
+ get_sre_reserved_qty_for_items_and_warehouses as get_sre_reserved_qty_details,
)
item_code_list, warehouse_list = [], []
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.js b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.js
index 4d96636..c5df319 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.js
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.js
@@ -1,42 +1,42 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
-frappe.ui.form.on("Stock Reservation Entry", {
+frappe.ui.form.on('Stock Reservation Entry', {
refresh(frm) {
- frm.trigger("set_queries");
- frm.trigger("toggle_read_only_fields");
- frm.trigger("hide_rate_related_fields");
- frm.trigger("hide_primary_action_button");
- frm.trigger("make_sb_entries_warehouse_read_only");
+ frm.trigger('set_queries');
+ frm.trigger('toggle_read_only_fields');
+ frm.trigger('hide_rate_related_fields');
+ frm.trigger('hide_primary_action_button');
+ frm.trigger('make_sb_entries_warehouse_read_only');
},
has_serial_no(frm) {
- frm.trigger("toggle_read_only_fields");
+ frm.trigger('toggle_read_only_fields');
},
has_batch_no(frm) {
- frm.trigger("toggle_read_only_fields");
+ frm.trigger('toggle_read_only_fields');
},
warehouse(frm) {
if (frm.doc.warehouse) {
frm.doc.sb_entries.forEach((row) => {
- frappe.model.set_value(row.doctype, row.name, "warehouse", frm.doc.warehouse);
+ frappe.model.set_value(row.doctype, row.name, 'warehouse', frm.doc.warehouse);
});
}
},
set_queries(frm) {
- frm.set_query("warehouse", () => {
+ frm.set_query('warehouse', () => {
return {
filters: {
- "is_group": 0,
- "company": frm.doc.company,
+ 'is_group': 0,
+ 'company': frm.doc.company,
}
};
});
- frm.set_query("serial_no", "sb_entries", function(doc, cdt, cdn) {
+ frm.set_query('serial_no', 'sb_entries', function(doc, cdt, cdn) {
var selected_serial_nos = doc.sb_entries.map(row => {
return row.serial_no;
});
@@ -45,16 +45,16 @@
filters: {
item_code: doc.item_code,
warehouse: row.warehouse,
- status: "Active",
- name: ["not in", selected_serial_nos],
+ status: 'Active',
+ name: ['not in', selected_serial_nos],
}
}
});
- frm.set_query("batch_no", "sb_entries", function(doc, cdt, cdn) {
+ frm.set_query('batch_no', 'sb_entries', function(doc, cdt, cdn) {
let filters = {
item: doc.item_code,
- batch_qty: [">", 0],
+ batch_qty: ['>', 0],
disabled: 0,
}
@@ -63,7 +63,7 @@
return row.batch_no;
});
- filters.name = ["not in", selected_batch_nos];
+ filters.name = ['not in', selected_batch_nos];
}
return { filters: filters }
@@ -74,37 +74,37 @@
if (frm.doc.has_serial_no) {
frm.doc.sb_entries.forEach(row => {
if (row.qty !== 1) {
- frappe.model.set_value(row.doctype, row.name, "qty", 1);
+ frappe.model.set_value(row.doctype, row.name, 'qty', 1);
}
})
}
frm.fields_dict.sb_entries.grid.update_docfield_property(
- "serial_no", "read_only", !frm.doc.has_serial_no
+ 'serial_no', 'read_only', !frm.doc.has_serial_no
);
frm.fields_dict.sb_entries.grid.update_docfield_property(
- "batch_no", "read_only", !frm.doc.has_batch_no
+ 'batch_no', 'read_only', !frm.doc.has_batch_no
);
// Qty will always be 1 for Serial No.
frm.fields_dict.sb_entries.grid.update_docfield_property(
- "qty", "read_only", frm.doc.has_serial_no
+ 'qty', 'read_only', frm.doc.has_serial_no
);
- frm.set_df_property("sb_entries", "allow_on_submit", frm.doc.against_pick_list ? 0 : 1);
+ frm.set_df_property('sb_entries', 'allow_on_submit', frm.doc.against_pick_list ? 0 : 1);
},
hide_rate_related_fields(frm) {
- ["incoming_rate", "outgoing_rate", "stock_value_difference", "is_outward", "stock_queue"].forEach(field => {
+ ['incoming_rate', 'outgoing_rate', 'stock_value_difference', 'is_outward', 'stock_queue'].forEach(field => {
frm.fields_dict.sb_entries.grid.update_docfield_property(
- field, "hidden", 1
+ field, 'hidden', 1
);
});
},
hide_primary_action_button(frm) {
- // Hide "Amend" button on cancelled document
+ // Hide 'Amend' button on cancelled document
if (frm.doc.docstatus == 2) {
frm.page.btn_primary.hide()
}
@@ -112,15 +112,15 @@
make_sb_entries_warehouse_read_only(frm) {
frm.fields_dict.sb_entries.grid.update_docfield_property(
- "warehouse", "read_only", 1
+ 'warehouse', 'read_only', 1
);
},
});
-frappe.ui.form.on("Serial and Batch Entry", {
+frappe.ui.form.on('Serial and Batch Entry', {
sb_entries_add(frm, cdt, cdn) {
if (frm.doc.warehouse) {
- frappe.model.set_value(cdt, cdn, "warehouse", frm.doc.warehouse);
+ frappe.model.set_value(cdt, cdn, 'warehouse', frm.doc.warehouse);
}
},
});
\ No newline at end of file
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 bd7bb66..936be3f 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
@@ -14,7 +14,7 @@
self.validate_amended_doc()
self.validate_mandatory()
- self.validate_for_group_warehouse()
+ self.validate_group_warehouse()
validate_disabled_warehouse(self.warehouse)
validate_warehouse_company(self.warehouse, self.company)
self.validate_uom_is_integer()
@@ -74,7 +74,7 @@
msg = _("{0} is required").format(self.meta.get_label(d))
frappe.throw(msg)
- def validate_for_group_warehouse(self) -> None:
+ def validate_group_warehouse(self) -> None:
"""Raises an exception if `Warehouse` is a Group Warehouse."""
if frappe.get_cached_value("Warehouse", self.warehouse, "is_group"):
@@ -544,10 +544,36 @@
return available_serial_nos_list
-def get_sre_reserved_qty_for_item_and_warehouse(
- item_code: str | list, warehouse: str | list = None
-) -> float | dict:
- """Returns `Reserved Qty` for Item and Warehouse combination OR a dict like {("item_code", "warehouse"): "reserved_qty", ... }."""
+def get_sre_reserved_qty_for_item_and_warehouse(item_code: str, warehouse: str = None) -> float:
+ """Returns current `Reserved Qty` for Item and Warehouse combination."""
+
+ sre = frappe.qb.DocType("Stock Reservation Entry")
+ query = (
+ frappe.qb.from_(sre)
+ .select(Sum(sre.reserved_qty - sre.delivered_qty).as_("reserved_qty"))
+ .where(
+ (sre.docstatus == 1)
+ & (sre.item_code == item_code)
+ & (sre.status.notin(["Delivered", "Cancelled"]))
+ )
+ .groupby(sre.item_code, sre.warehouse)
+ )
+
+ if warehouse:
+ query = query.where(sre.warehouse == warehouse)
+
+ reserved_qty = query.run(as_list=True)
+
+ return flt(reserved_qty[0][0]) if reserved_qty else 0.0
+
+
+def get_sre_reserved_qty_for_items_and_warehouses(
+ item_code_list: list, warehouse_list: list = None
+) -> dict:
+ """Returns a dict like {("item_code", "warehouse"): "reserved_qty", ... }."""
+
+ if not item_code_list:
+ return {}
sre = frappe.qb.DocType("Stock Reservation Entry")
query = (
@@ -557,29 +583,20 @@
sre.warehouse,
Sum(sre.reserved_qty - sre.delivered_qty).as_("reserved_qty"),
)
- .where((sre.docstatus == 1) & (sre.status.notin(["Delivered", "Cancelled"])))
+ .where(
+ (sre.docstatus == 1)
+ & sre.item_code.isin(item_code_list)
+ & (sre.status.notin(["Delivered", "Cancelled"]))
+ )
.groupby(sre.item_code, sre.warehouse)
)
- query = (
- query.where(sre.item_code.isin(item_code))
- if isinstance(item_code, list)
- else query.where(sre.item_code == item_code)
- )
-
- if warehouse:
- query = (
- query.where(sre.warehouse.isin(warehouse))
- if isinstance(warehouse, list)
- else query.where(sre.warehouse == warehouse)
- )
+ if warehouse_list:
+ query = query.where(sre.warehouse.isin(warehouse_list))
data = query.run(as_dict=True)
- if isinstance(item_code, str) and isinstance(warehouse, str):
- return data[0]["reserved_qty"] if data else 0.0
- else:
- return {(d["item_code"], d["warehouse"]): d["reserved_qty"] for d in data} if data else {}
+ return {(d["item_code"], d["warehouse"]): d["reserved_qty"] for d in data} if data else {}
def get_sre_reserved_qty_details_for_voucher(voucher_type: str, voucher_no: str) -> dict:
@@ -711,7 +728,7 @@
).run(as_dict=True)
-def get_ssb_bundle_for_voucher(sre: dict) -> object | None:
+def get_ssb_bundle_for_voucher(sre: dict) -> object:
"""Returns a new `Serial and Batch Bundle` against the provided SRE."""
sb_entries = get_serial_batch_entries_for_voucher(sre["name"])
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry_list.js b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry_list.js
index 442ac39..5b390f7 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry_list.js
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry_list.js
@@ -4,13 +4,14 @@
frappe.listview_settings['Stock Reservation Entry'] = {
get_indicator: function (doc) {
const status_colors = {
- 'Draft': 'red',
- 'Partially Reserved': 'orange',
- 'Reserved': 'blue',
- 'Partially Delivered': 'purple',
- 'Delivered': 'green',
- 'Cancelled': 'red',
+ 'Draft': 'red',
+ 'Partially Reserved': 'orange',
+ 'Reserved': 'blue',
+ 'Partially Delivered': 'purple',
+ 'Delivered': 'green',
+ 'Cancelled': 'red',
};
- return [__(doc.status), status_colors[doc.status], 'status,=,' + doc.status];
+
+ return [__(doc.status), status_colors[doc.status], 'status,=,' + doc.status];
},
};
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 337b0ea..a59f9de 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -165,7 +165,7 @@
def get_sre_reserved_qty_details(self) -> dict:
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
- get_sre_reserved_qty_for_item_and_warehouse as get_reserved_qty_details,
+ get_sre_reserved_qty_for_items_and_warehouses as get_reserved_qty_details,
)
item_code_list, warehouse_list = [], []