From 3602d1909eb4aa7d7feab9cb6de72d5209ba1ead Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sun, 26 Mar 2023 17:33:01 +0530 Subject: [PATCH] fix: map DN items based on SRE --- .../doctype/sales_order/sales_order.py | 33 +++++++++++- .../doctype/delivery_note/delivery_note.py | 14 +++++ .../stock_reservation_entry.py | 52 ++++++++++++++++++- 3 files changed, 96 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index d7541be95e..10a12211e9 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -622,7 +622,36 @@ def make_project(source_name, target_doc=None): @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 @@ def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False): 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 @@ def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False): } 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 9f9f5cbe2a..d6d51af886 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -239,6 +239,8 @@ class DeliveryNote(SellingController): 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 @@ class DeliveryNote(SellingController): 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 @@ class DeliveryNote(SellingController): 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 2824a71b7a..82eebb4978 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 @@ class StockReservationEntry(TransactionBase): 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 @@ class StockReservationEntry(TransactionBase): 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()