mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-24 13:15:03 +00:00
fix: seed cancelled voucher replay from before its posting datetime
On cancel, update_entries_after replays every live SLE at the voucher's posting datetime, but get_previous_sle_of_current_voucher seeded the replay with the reversal SLE's creation, which resolves to the bucket's own closing row. The bucket's net qty got double-counted into every same-datetime row, so later submissions passed negative stock validation against inflated balances, and the queued repost then rewrote correct values with allow_negative_stock forced on, silently creating negative stock. Backports the missing guard fromeca71dce54. (cherry picked from commit1ff297e9da)
This commit is contained in:
@@ -2751,6 +2751,50 @@ class TestStockEntry(FrappeTestCase):
|
||||
|
||||
self.assertEqual(se.process_loss_qty, 50)
|
||||
|
||||
@change_settings("Stock Settings", {"allow_negative_stock": 0})
|
||||
def test_cancel_seeds_replay_from_before_posting_datetime_bucket(self):
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
|
||||
item = make_item().name
|
||||
warehouse = create_warehouse("Cancel Replay Warehouse", company="_Test Company")
|
||||
|
||||
def stock_entry(qty, posting_date, **kwargs):
|
||||
return make_stock_entry(
|
||||
item_code=item,
|
||||
qty=qty,
|
||||
company="_Test Company",
|
||||
posting_date=posting_date,
|
||||
posting_time="10:00:00",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
stock_entry(25, add_days(today(), -3), to_warehouse=warehouse, rate=10)
|
||||
|
||||
bucket_date = add_days(today(), -2)
|
||||
stock_entry(20, bucket_date, from_warehouse=warehouse)
|
||||
receipt = stock_entry(75, bucket_date, to_warehouse=warehouse, rate=10)
|
||||
duplicate_issue = stock_entry(20, bucket_date, from_warehouse=warehouse)
|
||||
|
||||
frappe.flags.dont_execute_stock_reposts = True
|
||||
self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts")
|
||||
|
||||
duplicate_issue.reload()
|
||||
duplicate_issue.cancel()
|
||||
|
||||
self.assertEqual(
|
||||
flt(
|
||||
frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{"voucher_no": receipt.name, "is_cancelled": 0},
|
||||
"qty_after_transaction",
|
||||
)
|
||||
),
|
||||
80,
|
||||
)
|
||||
|
||||
overdraw = stock_entry(100, add_days(today(), -1), from_warehouse=warehouse, do_not_submit=True)
|
||||
self.assertRaises(NegativeStockError, overdraw.submit)
|
||||
|
||||
|
||||
def make_serialized_item(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -1762,7 +1762,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc
|
||||
voucher_no = args.get("voucher_no")
|
||||
voucher_condition = f"and voucher_no != '{voucher_no}'"
|
||||
|
||||
elif args.get("creation") and args.get("sle_id"):
|
||||
elif args.get("creation") and args.get("sle_id") and not args.get("cancelled"):
|
||||
creation = args.get("creation")
|
||||
operator = "<="
|
||||
voucher_condition = f"and creation < '{creation}'"
|
||||
|
||||
Reference in New Issue
Block a user