mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-24 20:46:38 +00:00
fix(stock): ignore future stock during batch reservation (#58303)
This commit is contained in:
@@ -9,9 +9,9 @@ from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder import Case
|
||||
from frappe.query_builder.functions import Max, Min, Sum
|
||||
from frappe.utils import cint, flt, nowdate, nowtime, parse_json
|
||||
from frappe.utils import cint, flt, get_datetime, now_datetime, nowdate, nowtime, parse_json
|
||||
|
||||
from erpnext.stock.utils import get_or_make_bin, get_stock_balance
|
||||
from erpnext.stock.utils import get_combine_datetime, get_or_make_bin, get_stock_balance
|
||||
|
||||
|
||||
class StockReservationEntry(Document):
|
||||
@@ -298,11 +298,13 @@ class StockReservationEntry(Document):
|
||||
|
||||
self.reservation_based_on = "Serial and Batch"
|
||||
self.sb_entries.clear()
|
||||
|
||||
kwargs = frappe._dict(
|
||||
{
|
||||
"item_code": self.item_code,
|
||||
"warehouse": self.warehouse,
|
||||
"qty": abs(self.reserved_qty) or 0,
|
||||
"posting_datetime": self.get_voucher_posting_datetime(),
|
||||
"based_on": based_on
|
||||
or frappe.get_single_value("Stock Settings", "pick_serial_and_batch_based_on"),
|
||||
}
|
||||
@@ -341,6 +343,37 @@ class StockReservationEntry(Document):
|
||||
},
|
||||
)
|
||||
|
||||
def get_voucher_posting_datetime(self):
|
||||
reservation_datetime = now_datetime()
|
||||
meta = frappe.get_meta(self.voucher_type)
|
||||
if meta.has_field("posting_datetime"):
|
||||
if posting_datetime := frappe.db.get_value(
|
||||
self.voucher_type, self.voucher_no, "posting_datetime"
|
||||
):
|
||||
return min(get_datetime(posting_datetime), reservation_datetime)
|
||||
|
||||
for date_field, time_field in (
|
||||
("posting_date", "posting_time"),
|
||||
("transaction_date", "transaction_time"),
|
||||
):
|
||||
if not meta.has_field(date_field):
|
||||
continue
|
||||
|
||||
fields = [date_field]
|
||||
if meta.has_field(time_field):
|
||||
fields.append(time_field)
|
||||
|
||||
values = frappe.db.get_value(self.voucher_type, self.voucher_no, fields, as_dict=True)
|
||||
if not values or not values.get(date_field):
|
||||
continue
|
||||
|
||||
posting_datetime = get_combine_datetime(
|
||||
values.get(date_field), values.get(time_field) or "23:59:59.999999"
|
||||
)
|
||||
return min(posting_datetime, reservation_datetime)
|
||||
|
||||
return reservation_datetime
|
||||
|
||||
def validate_reservation_based_on_serial_and_batch(self) -> None:
|
||||
"""Validates `Reserved Qty`, `Serial and Batch Nos` when `Reservation Based On` is `Serial and Batch`."""
|
||||
if self.voucher_type in ["Work Order", "Subcontracting Order"]:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from random import randint
|
||||
|
||||
import frappe
|
||||
from frappe.utils import flt, today
|
||||
from frappe.utils import add_days, flt, today
|
||||
|
||||
from erpnext.selling.doctype.sales_order.mapper import create_pick_list, make_delivery_note
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
@@ -717,6 +717,52 @@ class TestStockReservationEntry(ERPNextTestSuite):
|
||||
# Test - 9: After Delivery Note cancellation, SB Entry Delivered Qty should be `0`.
|
||||
self.assertEqual(sb_entry.delivered_qty, 0)
|
||||
|
||||
@ERPNextTestSuite.change_settings(
|
||||
"Stock Settings",
|
||||
{
|
||||
"allow_negative_stock": 0,
|
||||
"enable_stock_reservation": 1,
|
||||
"auto_reserve_serial_and_batch": 1,
|
||||
"pick_serial_and_batch_based_on": "LIFO",
|
||||
},
|
||||
)
|
||||
def test_auto_reserve_batch_ignores_future_stock(self) -> None:
|
||||
item = make_batch_item()
|
||||
voucher_date = add_days(today(), -1)
|
||||
|
||||
available_batch = frappe.get_doc(doctype="Batch", item=item.name).insert().name
|
||||
make_stock_entry(
|
||||
item_code=item.name,
|
||||
qty=1,
|
||||
to_warehouse=self.warehouse,
|
||||
rate=100,
|
||||
batch_no=available_batch,
|
||||
posting_date=voucher_date,
|
||||
posting_time="23:59:00",
|
||||
)
|
||||
|
||||
future_batch = frappe.get_doc(doctype="Batch", item=item.name).insert().name
|
||||
make_stock_entry(
|
||||
item_code=item.name,
|
||||
qty=1,
|
||||
to_warehouse=self.warehouse,
|
||||
rate=100,
|
||||
batch_no=future_batch,
|
||||
posting_date=add_days(today(), 1),
|
||||
posting_time="00:01:00",
|
||||
)
|
||||
|
||||
so = make_sales_order(
|
||||
item_code=item.name,
|
||||
warehouse=self.warehouse,
|
||||
qty=1,
|
||||
transaction_date=voucher_date,
|
||||
)
|
||||
so.db_set("transaction_time", None)
|
||||
so.create_stock_reservation_entries()
|
||||
|
||||
self.assertSetEqual(get_reserved_batch_nos(so.name), {available_batch})
|
||||
|
||||
@ERPNextTestSuite.change_settings(
|
||||
"Stock Settings",
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user