diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 74775b98e39..ba906261159 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -683,6 +683,102 @@ class TestStockLedgerEntry(FrappeTestCase): # original amount self.assertEqual(50, _get_stock_credit(final_consumption)) +<<<<<<< HEAD +======= + def test_tie_breaking(self): + frappe.flags.dont_execute_stock_reposts = True + self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts") + + item = make_item().name + warehouse = "_Test Warehouse - _TC" + + posting_date = "2022-01-01" + posting_time = "00:00:01" + sle = frappe.qb.DocType("Stock Ledger Entry") + + def ordered_qty_after_transaction(): + return ( + frappe.qb.from_(sle) + .select("qty_after_transaction") + .where((sle.item_code == item) & (sle.warehouse == warehouse) & (sle.is_cancelled == 0)) + .orderby(CombineDatetime(sle.posting_date, sle.posting_time)) + .orderby(sle.creation) + ).run(pluck=True) + + first = make_stock_entry( + item_code=item, + to_warehouse=warehouse, + qty=10, + posting_time=posting_time, + posting_date=posting_date, + do_not_submit=True, + ) + second = make_stock_entry( + item_code=item, + to_warehouse=warehouse, + qty=1, + posting_date=posting_date, + posting_time=posting_time, + do_not_submit=True, + ) + + first.submit() + second.submit() + + self.assertEqual([10, 11], ordered_qty_after_transaction()) + + first.cancel() + self.assertEqual([1], ordered_qty_after_transaction()) + + backdated = make_stock_entry( + item_code=item, + to_warehouse=warehouse, + qty=1, + posting_date="2021-01-01", + posting_time=posting_time, + ) + self.assertEqual([1, 2], ordered_qty_after_transaction()) + + backdated.cancel() + self.assertEqual([1], ordered_qty_after_transaction()) + + def test_timestamp_clash(self): + + item = make_item().name + warehouse = "_Test Warehouse - _TC" + + reciept = make_stock_entry( + item_code=item, + to_warehouse=warehouse, + qty=100, + rate=10, + posting_date="2021-01-01", + posting_time="01:00:00", + ) + + consumption = make_stock_entry( + item_code=item, + from_warehouse=warehouse, + qty=50, + posting_date="2021-01-01", + posting_time="02:00:00.1234", # ms are possible when submitted without editing posting time + ) + + backdated_receipt = make_stock_entry( + item_code=item, + to_warehouse=warehouse, + qty=100, + posting_date="2021-01-01", + rate=10, + posting_time="02:00:00", # same posting time as consumption but ms part stripped + ) + + try: + backdated_receipt.cancel() + except Exception as e: + self.fail("Double processing of qty for clashing timestamp.") + +>>>>>>> 7c839c4503 (fix: double future qty updates) def create_repack_entry(**args): args = frappe._dict(args) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index d2c10018ba5..c3f9561a1cc 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1241,6 +1241,8 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): datetime_limit_condition = "" qty_shift = args.actual_qty + args["time_format"] = "%H:%i:%s" + # find difference/shift in qty caused by stock reconciliation if args.voucher_type == "Stock Reconciliation": qty_shift = get_stock_reco_qty_shift(args) @@ -1261,12 +1263,8 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): and warehouse = %(warehouse)s and voucher_no != %(voucher_no)s and is_cancelled = 0 - and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s) - or ( - timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s) - and creation > %(creation)s - ) - ) + and timestamp(posting_date, time_format(posting_time, %(time_format)s)) + > timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) {datetime_limit_condition} """.format( qty_shift=qty_shift, datetime_limit_condition=datetime_limit_condition