From f24b55633600facf3eba3f76b7cd7c682d323317 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 07:40:14 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20do=20not=20rely=20on=20client=20side=20t?= =?UTF-8?q?o=20update=20quantities=20during=20partial=20d=E2=80=A6=20(back?= =?UTF-8?q?port=20#54804)=20(#54821)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: do not rely on client side to update quantities during partial d… (#54804) fix: do not rely on client side to update quantities during partial dropship (cherry picked from commit 03acbc3dc9af07ebaa900e443c5dc1497270e789) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.py * chore: resolve conflicts --------- Co-authored-by: Mihir Kandoi --- .../doctype/purchase_order/purchase_order.js | 73 +++++-------------- .../doctype/purchase_order/purchase_order.py | 46 +++++++++++- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index bb6e261c22d..86e7c790b7a 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -813,61 +813,26 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( }, ], primary_action: (values) => { - const data = values.items.filter((item) => item.__checked); - if (!data.length) { - frappe.throw(__("Please select at least one item to update delivered quantity.")); - } - - data.forEach((item) => { - if (!item.qty_change) { - frappe.throw( - __( - "Item {0} has no changes in delivered quantity. Please unselect the row if you do not wish to update its quantity.", - [item.item_code.bold()] - ) - ); - } - if (item.qty_change < 0 && Math.abs(item.qty_change) > item.delivered_qty) { - frappe.throw( - __("Delivered Qty cannot be reduced by more than {0} for item {1}", [ - item.delivered_qty, - item.item_code.bold(), - ]) - ); - } - if (item.qty_change > 0 && item.delivered_qty + item.qty_change > item.qty) { - frappe.throw( - __("Delivered Qty cannot be increased by more than {0} for item {1}", [ - item.qty - item.delivered_qty, - item.item_code.bold(), - ]) - ); - } - }); - - data.forEach((item) => { - frappe.model.set_value( - "Purchase Order Item", - item.name, - "received_qty", - item.delivered_qty + item.qty_change - ); - }); - const frm = this.frm; - frm.dirty(); - frm.save("Update", () => { - frappe.call({ - doc: frm.doc, - method: "update_receiving_percentage", - callback: function (r) { - if (!r.exc) { - dialog.hide(); - frappe.toast(__("Quantities updated successfully.")); - frm.reload_doc(); - } - }, - }); + frappe.call({ + doc: frm.doc, + method: "update_dropship_received_qty", + args: { + data: values.items + .filter((item) => item.__checked) + .map((item) => ({ + name: item.name, + current_qty: item.delivered_qty, + qty_change: item.qty_change, + })), + }, + callback: function (r) { + if (!r.exc) { + frm.reload_doc(); + frappe.toast(__("Quantities updated successfully.")); + dialog.hide(); + } + }, }); }, }); diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 140eef0ba38..aad447a8e7e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -575,6 +575,51 @@ class PurchaseOrder(BuyingController): def has_drop_ship_item(self): return any(d.delivered_by_supplier for d in self.items) + @frappe.whitelist() + def update_dropship_received_qty(self, data: list[dict]): + if not data: + frappe.throw(_("Please select at least one item to update delivered quantity.")) + + for d in data: + item = next((item for item in self.items if item.name == d.get("name")), None) + + if not item: + frappe.throw( + _("Item with name {0} not found in the Purchase Order").format(frappe.bold(d.get("name"))) + ) + + if not item.has_permlevel_access_to("received_qty", permission_type="write"): + frappe.throw( + _("You don't have permission to update Received Qty DocField for item {0}").format( + frappe.bold(item.item_code) + ) + ) + + if not d.get("qty_change"): + frappe.throw( + _( + "Item {0} has no changes in delivered quantity. Please unselect the row if you do not wish to update its quantity." + ).format(frappe.bold(item.item_code)) + ) + + if d.get("qty_change") < 0 and abs(d.get("qty_change")) > item.received_qty: + frappe.throw( + _("Delivered Qty cannot be reduced by more than {0} for item {1}").format( + item.received_qty, frappe.bold(item.item_code) + ) + ) + + if d.get("qty_change") > 0 and item.received_qty + d.get("qty_change") > item.qty: + frappe.throw( + _("Delivered Qty cannot be increased by more than {0} for item {1}").format( + item.qty - item.received_qty, frappe.bold(item.item_code) + ) + ) + + item.received_qty += d.get("qty_change") + self.update_receiving_percentage() + self.save() + def is_against_so(self): return any(d.sales_order for d in self.items if d.sales_order) @@ -588,7 +633,6 @@ class PurchaseOrder(BuyingController): stock_bin = get_bin(d.rm_item_code, d.reserve_warehouse) stock_bin.update_reserved_qty_for_sub_contracting(subcontract_doctype="Purchase Order") - @frappe.whitelist() def update_receiving_percentage(self): total_qty, received_qty = 0.0, 0.0 for item in self.items: