refactor(buying): extract Purchase Order StatusService

Move status transitions and receiving progress into
buying/doctype/purchase_order/services/status.py:

- update_status (module-level whitelisted wrapper + list view) and
  update_receiving_percentage (called by child_item_update) stay as
  controller delegators
- check_modified_date moves into the service (internal to update_status)

No behaviour change.
This commit is contained in:
Nabin Hait
2026-06-07 09:46:28 +05:30
parent f7460f7be3
commit 3df8e7bfe6
2 changed files with 52 additions and 27 deletions

View File

@@ -6,9 +6,8 @@ import json
import frappe
from frappe import _
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.document import Document
from frappe.utils import cint, cstr, flt
from frappe.utils import cint, flt
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
unlink_inter_company_doc,
@@ -17,6 +16,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
)
from erpnext.accounts.party import get_party_account_currency
from erpnext.buying.doctype.purchase_order.services.drop_ship import DropShipService
from erpnext.buying.doctype.purchase_order.services.status import StatusService
from erpnext.buying.doctype.purchase_order.services.subcontracting import SubcontractingService
from erpnext.buying.utils import validate_for_items
from erpnext.controllers.buying_controller import BuyingController
@@ -365,24 +365,8 @@ class PurchaseOrder(BuyingController):
for item_code, warehouse in item_wh_list:
update_bin_qty(item_code, warehouse, {"ordered_qty": get_ordered_qty(item_code, warehouse)})
def check_modified_date(self):
modified_in_db = frappe.db.get_value("Purchase Order", self.name, "modified")
if modified_in_db and cstr(modified_in_db) != cstr(self.modified):
frappe.msgprint(
_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name),
raise_exception=True,
)
def update_status(self, status):
self.check_modified_date()
self.set_status(update=True, status=status)
self.update_requested_qty()
self.update_ordered_qty()
SubcontractingService(self).update_subcontracting_order_status()
self.update_blanket_order()
self.notify_update()
clear_doctype_notifications(self)
StatusService(self).update_status(status)
def on_submit(self):
super().on_submit()
@@ -500,14 +484,7 @@ class PurchaseOrder(BuyingController):
return any(d.production_plan for d in self.items if d.production_plan)
def update_receiving_percentage(self):
total_qty, received_qty = 0.0, 0.0
for item in self.items:
received_qty += min(item.received_qty, item.qty)
total_qty += item.qty
if total_qty and received_qty:
self.db_set("per_received", flt(received_qty / total_qty) * 100, update_modified=False)
else:
self.db_set("per_received", 0, update_modified=False)
StatusService(self).update_receiving_percentage()
def set_service_items_for_finished_goods(self):
SubcontractingService(self).set_service_items_for_finished_goods()

View File

@@ -0,0 +1,48 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
"""Status transitions and receiving progress for Purchase Order."""
import frappe
from frappe import _
from frappe.desk.notifications import clear_doctype_notifications
from frappe.utils import cstr, flt
from erpnext.buying.doctype.purchase_order.services.subcontracting import SubcontractingService
class StatusService:
def __init__(self, doc):
self.doc = doc
def update_status(self, status: str) -> None:
doc = self.doc
self.check_modified_date()
doc.set_status(update=True, status=status)
doc.update_requested_qty()
doc.update_ordered_qty()
SubcontractingService(doc).update_subcontracting_order_status()
doc.update_blanket_order()
doc.notify_update()
clear_doctype_notifications(doc)
def check_modified_date(self) -> None:
doc = self.doc
modified_in_db = frappe.db.get_value("Purchase Order", doc.name, "modified")
if modified_in_db and cstr(modified_in_db) != cstr(doc.modified):
frappe.msgprint(
_("{0} {1} has been modified. Please refresh.").format(doc.doctype, doc.name),
raise_exception=True,
)
def update_receiving_percentage(self) -> None:
doc = self.doc
total_qty, received_qty = 0.0, 0.0
for item in doc.items:
received_qty += min(item.received_qty, item.qty)
total_qty += item.qty
if total_qty and received_qty:
doc.db_set("per_received", flt(received_qty / total_qty) * 100, update_modified=False)
else:
doc.db_set("per_received", 0, update_modified=False)