fix: Incorrect creation time at the time cancelling an entry causing an issue especially same posting datetime (backport #57380) (#57396)

* fix: Incorrect creation time at the time cancelling an entry causing an issue especially same posting datetime  (#57380)

* fix: shift same-timestamp sibling SLEs when cancelling an entry

update_qty_in_future_sle compared against the reversal SLE's own
creation and skipped same-posting_datetime siblings on cancel, leaving
their qty_after_transaction stale and causing false negative stock
errors.

* fix: revert update_qty_in_future_sle cancel tie-break, it double-counted

(cherry picked from commit 8c0ec3c179)

# Conflicts:
#	erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py

* chore: fix conflicts

Fix test cases related to stock ledger entry cancellations and ensure correct handling of same timestamp entries.

* test: use named item in test_cancel_shifts_same_timestamp_delivery_notes

* chore: fix typo

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
This commit is contained in:
mergify[bot]
2026-07-27 17:22:46 +00:00
committed by GitHub
parent c70cf8e554
commit ae0cd164f3
2 changed files with 84 additions and 0 deletions

View File

@@ -1357,6 +1357,81 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin):
# receipt2 now sits on a zero base -> 10 (not 0 from a double shift, nor a negative-stock error).
self.assertEqual(qty_after(receipt2), 10)
def test_cancel_shifts_same_timestamp_delivery_notes(self):
item = make_item("Test Shifts Same Timestamp Multiple DNs").name
warehouse = "_Test Warehouse - _TC"
posting_date = today()
posting_time = "10:00:00"
make_stock_entry(
item_code=item,
to_warehouse=warehouse,
qty=100,
rate=10,
posting_date=posting_date,
posting_time="09:00:00",
)
dns = []
for i in range(5):
dns.append(
create_delivery_note(
item_code=item,
warehouse=warehouse,
qty=20,
rate=10 * i,
posting_date=posting_date,
posting_time=posting_time,
)
)
time.sleep(1)
dn = dns[2]
dn.cancel()
expected_qty_after_transaction_of_dns3 = 40
qty_after_transaction_of_dns3 = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_no": dns[3].name, "is_cancelled": 0},
"qty_after_transaction",
)
self.assertEqual(expected_qty_after_transaction_of_dns3, qty_after_transaction_of_dns3)
def test_cancel_updates_bin_stock_value_when_no_sle_shares_timestamp(self):
item = make_item("Test Cancel Bin Stock Value Lone Timestamp").name
warehouse = "_Test Warehouse - _TC"
posting_date = today()
make_stock_entry(
item_code=item,
to_warehouse=warehouse,
qty=100,
rate=10,
posting_date=posting_date,
posting_time="09:00:00",
)
dn = create_delivery_note(
item_code=item,
warehouse=warehouse,
qty=20,
rate=10,
posting_date=posting_date,
posting_time="10:00:00",
)
dn.cancel()
# Nothing else sits on the delivery note's posting datetime, so the cancellation leaves no
# live SLE to reprocess. The bin must still fall back to the receipt's stock value.
bin_qty, bin_stock_value = frappe.db.get_value(
"Bin", {"item_code": item, "warehouse": warehouse}, ["actual_qty", "stock_value"]
)
self.assertEqual(bin_qty, 100)
self.assertEqual(bin_stock_value, 1000)
def test_get_next_stock_reco_respects_creation_order(self):
# A stock reco sharing the exact posting timestamp of the current entry must only count as the
# "next" reco when it was created after that entry. A reco created before it actually precedes

View File

@@ -799,9 +799,18 @@ class update_entries_after:
def process_sle_against_current_timestamp(self):
sl_entries = get_sle_against_current_voucher(self.args)
if self.args.get("cancelled") and sl_entries:
self.seed_previous_sle_for_cancellation(sl_entries[0])
for sle in sl_entries:
self.process_sle(sle)
def seed_previous_sle_for_cancellation(self, anchor_sle):
args = frappe._dict(anchor_sle)
args["sle_id"] = args.name
prev_sle = get_previous_sle_of_current_voucher(args)
if prev_sle:
self.prev_sle_dict[(anchor_sle.item_code, anchor_sle.warehouse)] = prev_sle
def get_future_entries_to_fix(self):
# includes current entry!
args = self.data[self.args.warehouse].previous_sle or frappe._dict(