Skip to content

Commit

Permalink
feat: provision to close SCO (backport #39127) (#39144)
Browse files Browse the repository at this point in the history
* feat: provision to close SCO

(cherry picked from commit 5e2669f)

* fix: don't allow to submit/cancel SCR against a closed SCO

(cherry picked from commit 9e97347)

* fix: don't allow to submit/cancel SE against a closed SCO

(cherry picked from commit 5bc2035)

* fix(ux): filter closed SCO in `Get Items From` dialog

(cherry picked from commit bb839b2)

* fix: don't close PO on SCO close

(cherry picked from commit 0d01bd8)

* fix: update qty on SCO status change

(cherry picked from commit 245effc)

* fix: don't allow to reopen SCO if PO is closed

(cherry picked from commit 784b6dc)

* fix: auto close and reopen SCO based on PO status

(cherry picked from commit 0819675)

* fix(text): test_update_status

(cherry picked from commit cdd5441)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
  • Loading branch information
mergify[bot] and s-aga-r authored Jan 4, 2024
1 parent 2db1e1a commit b192ddd
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 45 deletions.
12 changes: 12 additions & 0 deletions erpnext/buying/doctype/purchase_order/purchase_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ def update_status(self, status):
self.update_requested_qty()
self.update_ordered_qty()
self.update_reserved_qty_for_subcontract()
self.update_subcontracting_order_status()
self.notify_update()
clear_doctype_notifications(self)

Expand Down Expand Up @@ -613,6 +614,17 @@ def auto_create_subcontracting_order(self):
if frappe.db.get_single_value("Buying Settings", "auto_create_subcontracting_order"):
make_subcontracting_order(self.name, save=True, notify=True)

def update_subcontracting_order_status(self):
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
update_subcontracting_order_status as update_sco_status,
)

if self.is_subcontracted and not self.is_old_subcontracting_flow:
sco = frappe.db.get_value("Subcontracting Order", {"purchase_order": self.name, "docstatus": 1})

if sco:
update_sco_status(sco, "Closed" if self.status == "Closed" else None)


def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0):
"""get last purchase rate for an item"""
Expand Down
20 changes: 6 additions & 14 deletions erpnext/stock/doctype/stock_entry/stock_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import erpnext
from erpnext.accounts.general_ledger import process_gl_map
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
from erpnext.manufacturing.doctype.bom.bom import (
add_additional_cost,
Expand Down Expand Up @@ -208,7 +209,6 @@ def validate(self):
self.validate_bom()
self.set_process_loss_qty()
self.validate_purchase_order()
self.validate_subcontracting_order()

if self.purpose in ("Manufacture", "Repack"):
self.mark_finished_and_scrap_items()
Expand Down Expand Up @@ -274,6 +274,7 @@ def is_enqueue_action(self, force=False) -> bool:
return False

def on_submit(self):
self.validate_closed_subcontracting_order()
self.update_stock_ledger()
self.update_work_order()
self.validate_subcontract_order()
Expand All @@ -294,6 +295,7 @@ def on_submit(self):
self.set_material_request_transfer_status("Completed")

def on_cancel(self):
self.validate_closed_subcontracting_order()
self.update_subcontract_order_supplied_items()
self.update_subcontracting_order_status()

Expand Down Expand Up @@ -1197,19 +1199,9 @@ def validate_purchase_order(self):
)
)

def validate_subcontracting_order(self):
if self.get("subcontracting_order") and self.purpose in [
"Send to Subcontractor",
"Material Transfer",
]:
sco_status = frappe.db.get_value("Subcontracting Order", self.subcontracting_order, "status")

if sco_status == "Closed":
frappe.throw(
_("Cannot create Stock Entry against a closed Subcontracting Order {0}.").format(
self.subcontracting_order
)
)
def validate_closed_subcontracting_order(self):
if self.get("subcontracting_order"):
check_on_hold_or_closed_status("Subcontracting Order", self.subcontracting_order)

def mark_finished_and_scrap_items(self):
if self.purpose != "Repack" and any(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,32 @@ frappe.ui.form.on('Subcontracting Order', {
},

refresh: function (frm) {
if (frm.doc.docstatus == 1 && frm.has_perm("submit")) {
if (frm.doc.status == "Closed") {
frm.add_custom_button(__('Re-open'), () => frm.events.update_subcontracting_order_status(frm), __("Status"));
} else if(flt(frm.doc.per_received, 2) < 100) {
frm.add_custom_button(__('Close'), () => frm.events.update_subcontracting_order_status(frm, "Closed"), __("Status"));
}
}

frm.trigger('get_materials_from_supplier');
},

update_subcontracting_order_status(frm, status) {
frappe.call({
method: "erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.update_subcontracting_order_status",
args: {
sco: frm.doc.name,
status: status,
},
callback: function (r) {
if (!r.exc) {
frm.reload_doc();
}
},
});
},

get_materials_from_supplier: function (frm) {
let sco_rm_details = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
"options": "Draft\nOpen\nPartially Received\nCompleted\nMaterial Transferred\nPartial Material Transferred\nCancelled",
"options": "Draft\nOpen\nPartially Received\nCompleted\nMaterial Transferred\nPartial Material Transferred\nCancelled\nClosed",
"print_hide": 1,
"read_only": 1,
"reqd": 1,
Expand Down Expand Up @@ -454,7 +454,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2023-06-03 16:18:17.782538",
"modified": "2024-01-03 20:56:04.670380",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Order",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from frappe.utils import flt

from erpnext.buying.doctype.purchase_order.purchase_order import is_subcontracting_order_created
from erpnext.buying.doctype.purchase_order.purchase_order import update_status as update_po_status
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.controllers.subcontracting_controller import SubcontractingController
from erpnext.stock.stock_balance import update_bin_qty
from erpnext.stock.utils import get_bin
Expand Down Expand Up @@ -68,6 +68,7 @@ class SubcontractingOrder(SubcontractingController):
"Material Transferred",
"Partial Material Transferred",
"Cancelled",
"Closed",
]
supplied_items: DF.Table[SubcontractingOrderSuppliedItem]
supplier: DF.Link
Expand Down Expand Up @@ -112,16 +113,10 @@ def validate(self):

def on_submit(self):
self.update_prevdoc_status()
self.update_requested_qty()
self.update_ordered_qty_for_subcontracting()
self.update_reserved_qty_for_subcontracting()
self.update_status()

def on_cancel(self):
self.update_prevdoc_status()
self.update_requested_qty()
self.update_ordered_qty_for_subcontracting()
self.update_reserved_qty_for_subcontracting()
self.update_status()

def validate_purchase_order_for_subcontracting(self):
Expand Down Expand Up @@ -277,6 +272,9 @@ def populate_items_table(self):
self.set_missing_values()

def update_status(self, status=None, update_modified=True):
if self.status == "Closed" and self.status != status:
check_on_hold_or_closed_status("Purchase Order", self.purchase_order)

if self.docstatus >= 1 and not status:
if self.docstatus == 1:
if self.status == "Draft":
Expand All @@ -285,11 +283,6 @@ def update_status(self, status=None, update_modified=True):
status = "Completed"
elif self.per_received > 0 and self.per_received < 100:
status = "Partially Received"
for item in self.supplied_items:
if not item.returned_qty or (item.supplied_qty - item.consumed_qty - item.returned_qty) > 0:
break
else:
status = "Closed"
else:
total_required_qty = total_supplied_qty = 0
for item in self.supplied_items:
Expand All @@ -304,13 +297,12 @@ def update_status(self, status=None, update_modified=True):
elif self.docstatus == 2:
status = "Cancelled"

if status:
frappe.db.set_value(
"Subcontracting Order", self.name, "status", status, update_modified=update_modified
)
if status and self.status != status:
self.db_set("status", status, update_modified=update_modified)

if status == "Closed":
update_po_status("Closed", self.purchase_order)
self.update_requested_qty()
self.update_ordered_qty_for_subcontracting()
self.update_reserved_qty_for_subcontracting()


@frappe.whitelist()
Expand Down Expand Up @@ -357,8 +349,8 @@ def update_item(source, target, source_parent):


@frappe.whitelist()
def update_subcontracting_order_status(sco):
def update_subcontracting_order_status(sco, status=None):
if isinstance(sco, str):
sco = frappe.get_doc("Subcontracting Order", sco)

sco.update_status()
sco.update_status(status)
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ frappe.listview_settings['Subcontracting Order'] = {
"Completed": "green",
"Partial Material Transferred": "purple",
"Material Transferred": "blue",
"Closed": "red",
"Closed": "green",
"Cancelled": "red",
};
return [__(doc.status), status_colors[doc.status], "status,=," + doc.status];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ def test_update_status(self):
self.assertEqual(sco.status, "Partially Received")

# Closed
ste = get_materials_from_supplier(sco.name, [d.name for d in sco.supplied_items])
ste.save()
ste.submit()
sco.load_from_db()
sco.update_status("Closed")
self.assertEqual(sco.status, "Closed")
ste.cancel()
sco.load_from_db()
scr = make_subcontracting_receipt(sco.name)
scr.save()
self.assertRaises(frappe.exceptions.ValidationError, scr.submit)
sco.update_status()
self.assertEqual(sco.status, "Partially Received")
scr.cancel()

# Completed
scr = make_subcontracting_receipt(sco.name)
Expand Down Expand Up @@ -564,7 +564,6 @@ def test_get_materials_from_supplier(self):

sco.load_from_db()

self.assertEqual(sco.status, "Closed")
self.assertEqual(sco.supplied_items[0].returned_qty, 5)

def test_ordered_qty_for_subcontracting_order(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ frappe.ui.form.on('Subcontracting Receipt', {
get_query_filters: {
docstatus: 1,
per_received: ['<', 100],
company: frm.doc.company
company: frm.doc.company,
status: ['!=', 'Closed'],
}
});
}, __('Get Items From'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import erpnext
from erpnext.accounts.utils import get_account_currency
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.controllers.subcontracting_controller import SubcontractingController
from erpnext.stock.stock_ledger import get_valuation_rate

Expand Down Expand Up @@ -142,6 +143,7 @@ def validate(self):
self.get_current_stock()

def on_submit(self):
self.validate_closed_subcontracting_order()
self.validate_available_qty_for_consumption()
self.update_status_updater_args()
self.update_prevdoc_status()
Expand All @@ -165,6 +167,7 @@ def on_cancel(self):
"Repost Item Valuation",
"Serial and Batch Bundle",
)
self.validate_closed_subcontracting_order()
self.update_status_updater_args()
self.update_prevdoc_status()
self.set_consumed_qty_in_subcontract_order()
Expand All @@ -175,6 +178,11 @@ def on_cancel(self):
self.update_status()
self.delete_auto_created_batches()

def validate_closed_subcontracting_order(self):
for item in self.items:
if item.subcontracting_order:
check_on_hold_or_closed_status("Subcontracting Order", item.subcontracting_order)

def validate_items_qty(self):
for item in self.items:
if not (item.qty or item.rejected_qty):
Expand Down

0 comments on commit b192ddd

Please sign in to comment.