diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index d5b2d42b8f5..49f1b24a743 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1026,7 +1026,14 @@ class TestDeliveryNote(IntegrationTestCase): def test_sales_invoice_qty_after_return(self): from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return - dn = create_delivery_note(qty=10) + item = make_item( + "Test Sales Invoice Qty After Return", + properties={"is_stock_item": 1, "stock_uom": "Nos"}, + ) + + make_stock_entry(item_code=item.name, target="_Test Warehouse - _TC", qty=10, basic_rate=100) + + dn = create_delivery_note(item_code=item.name, qty=10) dnr1 = make_sales_return(dn.name) dnr1.get("items")[0].qty = -3 @@ -1042,8 +1049,8 @@ class TestDeliveryNote(IntegrationTestCase): self.assertEqual(si.get("items")[0].qty, 5) si.reload().cancel().delete() - dnr1.reload().cancel().delete() dnr2.reload().cancel().delete() + dnr1.reload().cancel().delete() dn.reload().cancel().delete() def test_dn_billing_status_case3(self): diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 53df3d1d6f7..1801aeb2f9e 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -8,6 +8,7 @@ import json import frappe from frappe import _, bold, scrub from frappe.model.meta import get_field_precision +from frappe.query_builder import Order from frappe.query_builder.functions import Sum from frappe.utils import ( add_to_date, @@ -67,8 +68,8 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc from erpnext.controllers.stock_controller import future_sle_exists if sl_entries: - cancel = sl_entries[0].get("is_cancelled") - if cancel: + cancelled = sl_entries[0].get("is_cancelled") + if cancelled: validate_cancellation(sl_entries) set_as_cancel(sl_entries[0].get("voucher_type"), sl_entries[0].get("voucher_no")) @@ -79,7 +80,7 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc if sle.serial_no and not via_landed_cost_voucher: validate_serial_no(sle) - if cancel: + if cancelled: sle["actual_qty"] = -flt(sle.get("actual_qty")) if sle["actual_qty"] < 0 and not sle.get("outgoing_rate"): @@ -108,7 +109,9 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc if is_stock_item: bin_name = get_or_make_bin(args.get("item_code"), args.get("warehouse")) args.reserved_stock = flt(frappe.db.get_value("Bin", bin_name, "reserved_stock")) - repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher) + repost_current_voucher( + args, allow_negative_stock, via_landed_cost_voucher, cancelled=cancelled + ) update_bin_qty(bin_name, args) else: frappe.msgprint( @@ -116,7 +119,7 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc ) -def repost_current_voucher(args, allow_negative_stock=False, via_landed_cost_voucher=False): +def repost_current_voucher(args, allow_negative_stock=False, via_landed_cost_voucher=False, cancelled=False): if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation": if not args.get("posting_date"): args["posting_date"] = nowdate() @@ -135,6 +138,7 @@ def repost_current_voucher(args, allow_negative_stock=False, via_landed_cost_vou "sle_id": args.get("name"), "creation": args.get("creation"), "reserved_stock": args.get("reserved_stock"), + "cancelled": cancelled, }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher, @@ -667,33 +671,31 @@ class update_entries_after: def process_sle_against_current_timestamp(self): sl_entries = self.get_sle_against_current_voucher() for sle in sl_entries: + sle["timestamp"] = sle.posting_datetime self.process_sle(sle) def get_sle_against_current_voucher(self): self.args["posting_datetime"] = get_combine_datetime(self.args.posting_date, self.args.posting_time) + doctype = frappe.qb.DocType("Stock Ledger Entry") - return frappe.db.sql( - """ - select - *, posting_datetime as "timestamp" - from - `tabStock Ledger Entry` - where - item_code = %(item_code)s - and warehouse = %(warehouse)s - and is_cancelled = 0 - and ( - posting_datetime = %(posting_datetime)s - ) - and creation = %(creation)s - order by - creation ASC - for update - """, - self.args, - as_dict=1, + query = ( + frappe.qb.from_(doctype) + .select("*") + .where( + (doctype.item_code == self.args.item_code) + & (doctype.warehouse == self.args.warehouse) + & (doctype.is_cancelled == 0) + & (doctype.posting_datetime == self.args.posting_datetime) + ) + .orderby(doctype.creation, order=Order.asc) + .for_update() ) + if not self.args.get("cancelled"): + query = query.where(doctype.creation == self.args.creation) + + return query.run(as_dict=True) + def get_future_entries_to_fix(self): # includes current entry! args = self.data[self.args.warehouse].previous_sle or frappe._dict( @@ -1715,7 +1717,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}'"