Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(minor): Stock Reservation Entry #37185

Merged
merged 2 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions erpnext/stock/dashboard/item_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from frappe.utils import cint, flt

from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock,
get_sre_reserved_qty_for_items_and_warehouses as get_reserved_stock_details,
)


Expand Down Expand Up @@ -61,7 +61,10 @@ def get_data(
limit_page_length=21,
)

sre_reserved_stock_details = get_reserved_stock(item_code, warehouse)
item_code_list = [item_code] if item_code else [i.item_code for i in items]
warehouse_list = [warehouse] if warehouse else [i.warehouse for i in items]

sre_reserved_stock_details = get_reserved_stock_details(item_code_list, warehouse_list)
precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))

for item in items:
Expand All @@ -75,7 +78,8 @@ def get_data(
"reserved_qty_for_production": flt(item.reserved_qty_for_production, precision),
"reserved_qty_for_sub_contract": flt(item.reserved_qty_for_sub_contract, precision),
"actual_qty": flt(item.actual_qty, precision),
"reserved_stock": sre_reserved_stock_details,
"reserved_stock": flt(sre_reserved_stock_details.get((item.item_code, item.warehouse))),
}
)

return items
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ def validate_reserved_stock(self) -> None:
"""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 = [], []
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
});
Expand All @@ -45,16 +45,16 @@ frappe.ui.form.on("Stock Reservation Entry", {
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,
}

Expand All @@ -63,7 +63,7 @@ frappe.ui.form.on("Stock Reservation Entry", {
return row.batch_no;
});

filters.name = ["not in", selected_batch_nos];
filters.name = ['not in', selected_batch_nos];
}

return { filters: filters }
Expand All @@ -74,53 +74,53 @@ frappe.ui.form.on("Stock Reservation Entry", {
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()
}
},

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);
}
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def validate(self) -> None:

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()
Expand Down Expand Up @@ -74,7 +74,7 @@ def validate_mandatory(self) -> None:
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"):
Expand Down Expand Up @@ -544,10 +544,36 @@ def get_available_serial_nos_to_reserve(
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 = (
Expand All @@ -557,29 +583,20 @@ def get_sre_reserved_qty_for_item_and_warehouse(
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:
Expand Down Expand Up @@ -711,7 +728,7 @@ def get_serial_batch_entries_for_voucher(sre_name: str) -> list[dict]:
).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"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];
},
};
2 changes: 1 addition & 1 deletion erpnext/stock/report/stock_balance/stock_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def get_item_warehouse_map(self):

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 = [], []
Expand Down
Loading