feat: reserve stock for SO on PR submission

This commit is contained in:
s-aga-r 2023-10-19 14:48:13 +05:30
parent 188175be84
commit 64497c9228
4 changed files with 62 additions and 35 deletions

View File

@ -541,7 +541,7 @@ class SalesOrder(SellingController):
create_stock_reservation_entries_for_so_items as create_stock_reservation_entries,
)
create_stock_reservation_entries(so=self, items_details=items_details, notify=notify)
create_stock_reservation_entries(sales_order=self, items_details=items_details, notify=notify)
@frappe.whitelist()
def cancel_stock_reservation_entries(self, sre_list=None, notify=True) -> None:

View File

@ -242,7 +242,7 @@ class PickList(Document):
for so, locations in so_details.items():
so_doc = frappe.get_doc("Sales Order", so)
create_stock_reservation_entries_for_so_items(
so=so_doc, items_details=locations, against_pick_list=True, notify=notify
sales_order=so_doc, items_details=locations, against_pick_list=True, notify=notify
)
@frappe.whitelist()

View File

@ -264,6 +264,7 @@ class PurchaseReceipt(BuyingController):
self.make_gl_entries()
self.repost_future_sle_and_gle()
self.set_consumed_qty_in_subcontract_order()
self.reserve_stock_for_sales_order()
def check_next_docstatus(self):
submit_rv = frappe.db.sql(
@ -829,6 +830,31 @@ class PurchaseReceipt(BuyingController):
self.load_from_db()
def reserve_stock_for_sales_order(self):
if self.is_return or not cint(
frappe.db.get_single_value("Stock Settings", "auto_reserve_stock_for_sales_order_on_purchase")
):
return
self.reload() # reload to get the Serial and Batch Bundle Details
so_items_details_map = {}
for item in self.items:
if item.sales_order and item.sales_order_item:
item_details = {
"name": item.sales_order_item,
"item_code": item.item_code,
"warehouse": item.warehouse,
"qty_to_reserve": item.stock_qty,
"serial_and_batch_bundle": item.get("serial_and_batch_bundle"),
}
so_items_details_map.setdefault(item.sales_order, []).append(item_details)
if so_items_details_map:
for so, items_details in so_items_details_map.items():
so_doc = frappe.get_doc("Sales Order", so)
so_doc.create_stock_reservation_entries(items_details)
def update_billed_amount_based_on_po(po_details, update_modified=True):
po_billed_amt_details = get_billed_amount_against_po(po_details)

View File

@ -761,7 +761,7 @@ def has_reserved_stock(voucher_type: str, voucher_no: str, voucher_detail_no: st
def create_stock_reservation_entries_for_so_items(
so: object,
sales_order: object,
items_details: list[dict] = None,
against_pick_list: bool = False,
notify=True,
@ -771,15 +771,17 @@ def create_stock_reservation_entries_for_so_items(
from erpnext.selling.doctype.sales_order.sales_order import get_unreserved_qty
if not against_pick_list and (
so.get("_action") == "submit"
and so.set_warehouse
and cint(frappe.get_cached_value("Warehouse", so.set_warehouse, "is_group"))
sales_order.get("_action") == "submit"
and sales_order.set_warehouse
and cint(frappe.get_cached_value("Warehouse", sales_order.set_warehouse, "is_group"))
):
return frappe.msgprint(
_("Stock cannot be reserved in the group warehouse {0}.").format(frappe.bold(so.set_warehouse))
_("Stock cannot be reserved in the group warehouse {0}.").format(
frappe.bold(sales_order.set_warehouse)
)
)
validate_stock_reservation_settings(so)
validate_stock_reservation_settings(sales_order)
allow_partial_reservation = frappe.db.get_single_value(
"Stock Settings", "allow_partial_reservation"
@ -787,29 +789,28 @@ def create_stock_reservation_entries_for_so_items(
items = []
if items_details:
item_field = "sales_order_item" if against_pick_list else "name"
for item in items_details:
so_item = frappe.get_doc(
"Sales Order Item", item.get("sales_order_item") if against_pick_list else item.get("name")
)
so_item.reserve_stock = 1
so_item = frappe.get_doc("Sales Order Item", item.get(item_field))
so_item.warehouse = item.get("warehouse")
so_item.qty_to_reserve = (
item.get("picked_qty") - item.get("stock_reserved_qty", 0)
if against_pick_list
else (flt(item.get("qty_to_reserve")) * flt(so_item.conversion_factor, 1))
)
so_item.serial_and_batch_bundle = item.get("serial_and_batch_bundle")
if against_pick_list:
so_item.pick_list = item.get("parent")
so_item.pick_list_item = item.get("name")
so_item.pick_list_sbb = item.get("serial_and_batch_bundle")
items.append(so_item)
sre_count = 0
reserved_qty_details = get_sre_reserved_qty_details_for_voucher("Sales Order", so.name)
reserved_qty_details = get_sre_reserved_qty_details_for_voucher("Sales Order", sales_order.name)
for item in items if items_details else so.get("items"):
for item in items if items_details else sales_order.get("items"):
# Skip if `Reserved Stock` is not checked for the item.
if not item.get("reserve_stock"):
continue
@ -817,9 +818,9 @@ def create_stock_reservation_entries_for_so_items(
# Stock should be reserved from the Pick List if has Picked Qty.
if not against_pick_list and flt(item.picked_qty) > 0:
frappe.throw(
_(
"Row #{0}: Item {1} has been picked, please create a Stock Reservation from the Pick List."
).format(item.idx, frappe.bold(item.item_code))
_("Row #{0}: Item {1} has been picked, please reserve stock from the Pick List.").format(
item.idx, frappe.bold(item.item_code)
)
)
is_stock_item, has_serial_no, has_batch_no = frappe.get_cached_value(
@ -915,33 +916,33 @@ def create_stock_reservation_entries_for_so_items(
sre.warehouse = item.warehouse
sre.has_serial_no = has_serial_no
sre.has_batch_no = has_batch_no
sre.voucher_type = so.doctype
sre.voucher_no = so.name
sre.voucher_type = sales_order.doctype
sre.voucher_no = sales_order.name
sre.voucher_detail_no = item.name
sre.available_qty = available_qty_to_reserve
sre.voucher_qty = item.stock_qty
sre.reserved_qty = qty_to_be_reserved
sre.company = so.company
sre.company = sales_order.company
sre.stock_uom = item.stock_uom
sre.project = so.project
sre.project = sales_order.project
if against_pick_list:
sre.against_pick_list = item.pick_list
sre.against_pick_list_item = item.pick_list_item
if item.pick_list_sbb:
sbb = frappe.get_doc("Serial and Batch Bundle", item.pick_list_sbb)
sre.reservation_based_on = "Serial and Batch"
for entry in sbb.entries:
sre.append(
"sb_entries",
{
"serial_no": entry.serial_no,
"batch_no": entry.batch_no,
"qty": 1 if has_serial_no else abs(entry.qty),
"warehouse": entry.warehouse,
},
)
if item.serial_and_batch_bundle:
sbb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
sre.reservation_based_on = "Serial and Batch"
for entry in sbb.entries:
sre.append(
"sb_entries",
{
"serial_no": entry.serial_no,
"batch_no": entry.batch_no,
"qty": 1 if has_serial_no else abs(entry.qty),
"warehouse": entry.warehouse,
},
)
sre.save()
sre.submit()