mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-26 13:25:20 +00:00
Merge pull request #58391 from frappe/mergify/bp/version-16-hotfix/pr-58362
fix(stock): reset bin when no stock ledger entries remain (backport #58362)
This commit is contained in:
@@ -55,6 +55,68 @@ class TestBin(ERPNextTestSuite):
|
||||
self.assertEqual(bin.valuation_rate, 0)
|
||||
self.assertEqual(bin.stock_value, 0)
|
||||
|
||||
def test_repost_resets_bin_without_sle(self):
|
||||
"""A repost must zero the bin when the ledger is empty, e.g. after entries were deleted."""
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.stock_ledger import update_entries_after
|
||||
|
||||
item_code = make_item().name
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
make_stock_entry(item_code=item_code, target=warehouse, qty=10, rate=100)
|
||||
|
||||
# deleting a transaction with `delete_linked_ledger_entries` on drops its entries outright
|
||||
frappe.db.delete("Stock Ledger Entry", {"item_code": item_code, "warehouse": warehouse})
|
||||
|
||||
update_entries_after(
|
||||
{
|
||||
"item_code": item_code,
|
||||
"warehouse": warehouse,
|
||||
"posting_date": "1900-01-01",
|
||||
"posting_time": "00:01",
|
||||
}
|
||||
)
|
||||
|
||||
bin = frappe.get_doc("Bin", {"item_code": item_code, "warehouse": warehouse})
|
||||
self.assertEqual(bin.actual_qty, 0)
|
||||
self.assertEqual(bin.valuation_rate, 0)
|
||||
self.assertEqual(bin.stock_value, 0)
|
||||
|
||||
def test_cancelling_last_entry_resets_bin(self):
|
||||
"""Cancelling the only voucher must clear stock value, not just quantity."""
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
item_code = make_item().name
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
se = make_stock_entry(item_code=item_code, target=warehouse, qty=10, rate=100)
|
||||
|
||||
se.cancel()
|
||||
|
||||
bin = frappe.get_doc("Bin", {"item_code": item_code, "warehouse": warehouse})
|
||||
self.assertEqual(bin.actual_qty, 0)
|
||||
self.assertEqual(bin.valuation_rate, 0)
|
||||
self.assertEqual(bin.stock_value, 0)
|
||||
|
||||
def test_deleting_last_voucher_resets_bin(self):
|
||||
"""Deleting the only voucher wipes its ledger entries outright, the bin must still be cleared."""
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
item_code = make_item().name
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
delete_entries = frappe.get_single_value("Accounts Settings", "delete_linked_ledger_entries")
|
||||
frappe.db.set_single_value("Accounts Settings", "delete_linked_ledger_entries", 1)
|
||||
|
||||
try:
|
||||
se = make_stock_entry(item_code=item_code, target=warehouse, qty=10, rate=100)
|
||||
se.cancel()
|
||||
frappe.delete_doc("Stock Entry", se.name, force=1)
|
||||
finally:
|
||||
frappe.db.set_single_value("Accounts Settings", "delete_linked_ledger_entries", delete_entries)
|
||||
|
||||
bin = frappe.get_doc("Bin", {"item_code": item_code, "warehouse": warehouse})
|
||||
self.assertEqual(bin.actual_qty, 0)
|
||||
self.assertEqual(bin.valuation_rate, 0)
|
||||
self.assertEqual(bin.stock_value, 0)
|
||||
|
||||
def test_index_exists(self):
|
||||
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):
|
||||
|
||||
@@ -587,16 +587,6 @@ class update_entries_after:
|
||||
previous_sle = get_previous_sle_of_current_voucher(args)
|
||||
if previous_sle:
|
||||
self.prev_sle_dict[(args.get("item_code"), args.get("warehouse"))] = previous_sle
|
||||
else:
|
||||
self.prev_sle_dict[(args.get("item_code"), args.get("warehouse"))] = frappe._dict(
|
||||
{
|
||||
"qty_after_transaction": 0.0,
|
||||
"valuation_rate": 0.0,
|
||||
"stock_value": 0.0,
|
||||
"prev_stock_value": 0.0,
|
||||
"stock_queue": [],
|
||||
}
|
||||
)
|
||||
|
||||
warehouse_dict.previous_sle = previous_sle
|
||||
|
||||
@@ -1800,6 +1790,30 @@ class update_entries_after:
|
||||
|
||||
frappe.db.set_value("Bin", bin_name, updated_values, update_modified=True)
|
||||
|
||||
self.reset_bin_without_stock_ledger_entries()
|
||||
|
||||
def reset_bin_without_stock_ledger_entries(self):
|
||||
"""Reset the bin when its ledger has no entries left, prev_sle_dict never covers that case."""
|
||||
item_code, warehouse = self.args.get("item_code"), self.args.get("warehouse")
|
||||
if not item_code or not warehouse or (item_code, warehouse) in self.prev_sle_dict:
|
||||
return
|
||||
|
||||
if frappe.db.exists(
|
||||
"Stock Ledger Entry", {"item_code": item_code, "warehouse": warehouse, "is_cancelled": 0}
|
||||
):
|
||||
return
|
||||
|
||||
bin_name = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse})
|
||||
if not bin_name:
|
||||
return
|
||||
|
||||
frappe.db.set_value(
|
||||
"Bin",
|
||||
bin_name,
|
||||
{"actual_qty": 0.0, "stock_value": 0.0, "valuation_rate": 0.0},
|
||||
update_modified=True,
|
||||
)
|
||||
|
||||
|
||||
def get_sle_against_current_voucher(kwargs):
|
||||
kwargs["posting_datetime"] = get_combine_datetime(kwargs.posting_date, kwargs.posting_time)
|
||||
|
||||
Reference in New Issue
Block a user