mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-12 14:11:46 +00:00
feat: recalculate valuation rate and stock value from Bin
Renames the Recalculate Bin Qty button to Recalculate Values and sets
valuation_rate and stock_value from the last SLE (0 when none exists).
(cherry picked from commit df79e85f53)
This commit is contained in:
@@ -3,17 +3,17 @@
|
|||||||
|
|
||||||
frappe.ui.form.on("Bin", {
|
frappe.ui.form.on("Bin", {
|
||||||
refresh(frm) {
|
refresh(frm) {
|
||||||
frm.trigger("recalculate_bin_quantity");
|
frm.trigger("recalculate_values");
|
||||||
},
|
},
|
||||||
|
|
||||||
recalculate_bin_quantity(frm) {
|
recalculate_values(frm) {
|
||||||
frm.add_custom_button(__("Recalculate Bin Qty"), () => {
|
frm.add_custom_button(__("Recalculate Values"), () => {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "recalculate_qty",
|
method: "recalculate_values",
|
||||||
freeze: true,
|
freeze: true,
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function (r) {
|
callback: function (r) {
|
||||||
frappe.show_alert(__("Bin Qty Recalculated"), 2);
|
frappe.show_alert(__("Bin Values Recalculated"), 2);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class Bin(Document):
|
|||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def recalculate_qty(self):
|
def recalculate_values(self):
|
||||||
from erpnext.manufacturing.doctype.work_order.work_order import get_reserved_qty_for_production
|
from erpnext.manufacturing.doctype.work_order.work_order import get_reserved_qty_for_production
|
||||||
from erpnext.stock.stock_balance import (
|
from erpnext.stock.stock_balance import (
|
||||||
get_indented_qty,
|
get_indented_qty,
|
||||||
@@ -46,7 +46,10 @@ class Bin(Document):
|
|||||||
get_reserved_qty,
|
get_reserved_qty,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.actual_qty = get_actual_qty(self.item_code, self.warehouse)
|
last_sle = get_last_sle_values(self.item_code, self.warehouse)
|
||||||
|
self.actual_qty = last_sle.qty_after_transaction
|
||||||
|
self.valuation_rate = last_sle.valuation_rate
|
||||||
|
self.stock_value = last_sle.stock_value
|
||||||
self.planned_qty = get_planned_qty(self.item_code, self.warehouse)
|
self.planned_qty = get_planned_qty(self.item_code, self.warehouse)
|
||||||
self.indented_qty = get_indented_qty(self.item_code, self.warehouse)
|
self.indented_qty = get_indented_qty(self.item_code, self.warehouse)
|
||||||
self.ordered_qty = get_ordered_qty(self.item_code, self.warehouse)
|
self.ordered_qty = get_ordered_qty(self.item_code, self.warehouse)
|
||||||
@@ -302,20 +305,23 @@ def update_qty(bin_name, args):
|
|||||||
|
|
||||||
|
|
||||||
def get_actual_qty(item_code, warehouse):
|
def get_actual_qty(item_code, warehouse):
|
||||||
|
return get_last_sle_values(item_code, warehouse).qty_after_transaction
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_sle_values(item_code, warehouse):
|
||||||
sle = frappe.qb.DocType("Stock Ledger Entry")
|
sle = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
|
|
||||||
last_sle_qty = (
|
last_sle = (
|
||||||
frappe.qb.from_(sle)
|
frappe.qb.from_(sle)
|
||||||
.select(sle.qty_after_transaction)
|
.select(sle.qty_after_transaction, sle.valuation_rate, sle.stock_value)
|
||||||
.where((sle.item_code == item_code) & (sle.warehouse == warehouse) & (sle.is_cancelled == 0))
|
.where((sle.item_code == item_code) & (sle.warehouse == warehouse) & (sle.is_cancelled == 0))
|
||||||
.orderby(sle.posting_datetime, order=Order.desc)
|
.orderby(sle.posting_datetime, order=Order.desc)
|
||||||
.orderby(sle.creation, order=Order.desc)
|
.orderby(sle.creation, order=Order.desc)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.run()
|
.run(as_dict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
actual_qty = 0.0
|
if last_sle:
|
||||||
if last_sle_qty:
|
return last_sle[0]
|
||||||
actual_qty = last_sle_qty[0][0]
|
|
||||||
|
|
||||||
return actual_qty
|
return frappe._dict(qty_after_transaction=0.0, valuation_rate=0.0, stock_value=0.0)
|
||||||
|
|||||||
@@ -26,6 +26,35 @@ class TestBin(ERPNextTestSuite):
|
|||||||
bin = _create_bin(item_code, warehouse)
|
bin = _create_bin(item_code, warehouse)
|
||||||
self.assertEqual(bin.item_code, item_code)
|
self.assertEqual(bin.item_code, item_code)
|
||||||
|
|
||||||
|
def test_recalculate_values(self):
|
||||||
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
|
||||||
|
item_code = make_item("_TestBinRecalculateValues").name
|
||||||
|
warehouse = "_Test Warehouse - _TC"
|
||||||
|
make_stock_entry(item_code=item_code, target=warehouse, qty=10, rate=100)
|
||||||
|
|
||||||
|
bin = frappe.get_doc("Bin", {"item_code": item_code, "warehouse": warehouse})
|
||||||
|
bin.db_set({"actual_qty": 0, "valuation_rate": 0, "stock_value": 0})
|
||||||
|
bin.reload()
|
||||||
|
bin.recalculate_values()
|
||||||
|
|
||||||
|
self.assertEqual(bin.actual_qty, 10)
|
||||||
|
self.assertEqual(bin.valuation_rate, 100)
|
||||||
|
self.assertEqual(bin.stock_value, 1000)
|
||||||
|
|
||||||
|
def test_recalculate_values_without_sle(self):
|
||||||
|
item_code = make_item("_TestBinRecalculateValuesNoSLE").name
|
||||||
|
warehouse = "_Test Warehouse - _TC"
|
||||||
|
|
||||||
|
bin = _create_bin(item_code, warehouse)
|
||||||
|
bin.db_set({"actual_qty": 5, "valuation_rate": 50, "stock_value": 250})
|
||||||
|
bin.reload()
|
||||||
|
bin.recalculate_values()
|
||||||
|
|
||||||
|
self.assertEqual(bin.actual_qty, 0)
|
||||||
|
self.assertEqual(bin.valuation_rate, 0)
|
||||||
|
self.assertEqual(bin.stock_value, 0)
|
||||||
|
|
||||||
def test_index_exists(self):
|
def test_index_exists(self):
|
||||||
indexes = frappe.db.sql("show index from tabBin where Non_unique = 0", as_dict=1)
|
indexes = frappe.db.sql("show index from tabBin where Non_unique = 0", as_dict=1)
|
||||||
if not any(index.get("Key_name") == "unique_item_warehouse" for index in indexes):
|
if not any(index.get("Key_name") == "unique_item_warehouse" for index in indexes):
|
||||||
|
|||||||
Reference in New Issue
Block a user