fix: map DN items based on SRE
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index d7541be..10a1221 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -622,7 +622,36 @@
@frappe.whitelist()
def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False):
+ from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
+ get_stock_reservation_entry_for_voucher,
+ has_reserved_stock,
+ )
+
def set_missing_values(source, target):
+ if not target.items and has_reserved_stock("Sales Order", source_name):
+ sre_list = get_stock_reservation_entry_for_voucher("Sales Order", source_name)
+ sre_dict = {d.pop("voucher_detail_no"): d for d in sre_list}
+
+ for item in source.get("items"):
+ if item.name in sre_dict:
+ qty_to_deliver = (
+ sre_dict[item.name]["reserved_qty"] - sre_dict[item.name]["delivered_qty"]
+ ) / item.conversion_factor
+
+ row = frappe.new_doc("Delivery Note Item")
+ row.against_sales_order = source.name
+ row.against_sre = sre_dict[item.name]["name"]
+ row.so_detail = item.name
+ row.item_code = item.item_code
+ row.item_name = item.item_name
+ row.description = item.description
+ row.qty = qty_to_deliver
+ row.stock_uom = item.stock_uom
+ row.uom = item.uom
+ row.conversion_factor = item.conversion_factor
+
+ target.append("items", row)
+
target.run_method("set_missing_values")
target.run_method("set_po_nos")
target.run_method("calculate_taxes_and_totals")
@@ -651,6 +680,9 @@
or item_group.get("buying_cost_center")
)
+ if has_reserved_stock("Sales Order", source_name):
+ skip_item_mapping = True
+
mapper = {
"Sales Order": {"doctype": "Delivery Note", "validation": {"docstatus": ["=", 1]}},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
@@ -678,7 +710,6 @@
}
target_doc = get_mapped_doc("Sales Order", source_name, mapper, target_doc, set_missing_values)
-
target_doc.set_onload("ignore_price_list", True)
return target_doc
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 9f9f5cb..d6d51af 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -239,6 +239,8 @@
self.update_prevdoc_status()
self.update_billing_status()
+ self.update_stock_reservation_entry()
+
if not self.is_return:
self.check_credit_limit()
elif self.issue_credit_note:
@@ -258,6 +260,8 @@
self.update_prevdoc_status()
self.update_billing_status()
+ self.update_stock_reservation_entry()
+
# Updating stock ledger should always be called after updating prevdoc status,
# because updating reserved qty in bin depends upon updated delivered qty in SO
self.update_stock_ledger()
@@ -268,6 +272,16 @@
self.repost_future_sle_and_gle()
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
+ def update_stock_reservation_entry(self):
+ if not self.is_return:
+ from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
+ update_delivered_qty,
+ )
+
+ for item in self.get("items"):
+ if item.against_sre:
+ update_delivered_qty(item.doctype, item.against_sre)
+
def check_credit_limit(self):
from erpnext.selling.doctype.customer.customer import check_credit_limit
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
index 2824a71..82eebb4 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
@@ -3,6 +3,7 @@
import frappe
from frappe import _
+from frappe.query_builder.functions import Sum
from erpnext.utilities.transaction_base import TransactionBase
@@ -62,8 +63,6 @@
frappe.db.set_value(self.doctype, self.name, "status", status, update_modified=update_modified)
def update_reserved_qty_in_voucher(self, update_modified=True):
- from frappe.query_builder.functions import Sum
-
sre = frappe.qb.DocType("Stock Reservation Entry")
reserved_qty = (
frappe.qb.from_(sre)
@@ -83,3 +82,52 @@
reserved_qty,
update_modified=update_modified,
)
+
+
+def get_stock_reservation_entry_for_voucher(voucher_type, voucher_no, voucher_detail_no=None):
+ sre = frappe.qb.DocType("Stock Reservation Entry")
+ query = (
+ frappe.qb.from_(sre)
+ .select(
+ sre.name,
+ sre.item_code,
+ sre.warehouse,
+ sre.voucher_detail_no,
+ sre.reserved_qty,
+ sre.delivered_qty,
+ sre.stock_uom,
+ )
+ .where(
+ (sre.docstatus == 1)
+ & (sre.voucher_type == voucher_type)
+ & (sre.voucher_no == voucher_no)
+ & (sre.status.notin(["Delivered", "Cancelled"]))
+ )
+ .orderby(sre.creation)
+ )
+
+ if voucher_detail_no:
+ query = query.where(sre.voucher_detail_no == voucher_detail_no)
+
+ return query.run(as_dict=True)
+
+
+def has_reserved_stock(voucher_type, voucher_no, voucher_detail_no=None):
+ if get_stock_reservation_entry_for_voucher(voucher_type, voucher_no, voucher_detail_no):
+ return True
+
+ return False
+
+
+def update_delivered_qty(doctype, sre_name, sre_field="against_sre", qty_field="stock_qty"):
+ table = frappe.qb.DocType(doctype)
+ delivered_qty = (
+ frappe.qb.from_(table)
+ .select(Sum(table[qty_field]))
+ .where((table.docstatus == 1) & (table[sre_field] == sre_name))
+ ).run(as_list=True)[0][0] or 0.0
+
+ sre_doc = frappe.get_doc("Stock Reservation Entry", sre_name)
+ sre_doc.delivered_qty = delivered_qty
+ sre_doc.db_update()
+ sre_doc.update_status()