feat: provision to close SCO (backport #39127) (#39144)

* feat: provision to close SCO

(cherry picked from commit 5e2669f4b6)

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

(cherry picked from commit 9e973476b2)

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

(cherry picked from commit 5bc2035bd0)

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

(cherry picked from commit bb839b2924)

* fix: don't close PO on SCO close

(cherry picked from commit 0d01bd8a5a)

* fix: update qty on SCO status change

(cherry picked from commit 245effcccd)

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

(cherry picked from commit 784b6dcfea)

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

(cherry picked from commit 0819675fce)

* fix(text): test_update_status

(cherry picked from commit cdd5441435)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
This commit is contained in:
mergify[bot]
2024-01-04 17:22:58 +05:30
committed by GitHub
parent 2db1e1a737
commit b192ddd13b
9 changed files with 72 additions and 45 deletions

View File

@@ -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 = [];

View File

@@ -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,
@@ -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",

View File

@@ -7,7 +7,7 @@ from frappe.model.mapper import get_mapped_doc
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
@@ -68,6 +68,7 @@ class SubcontractingOrder(SubcontractingController):
"Material Transferred",
"Partial Material Transferred",
"Cancelled",
"Closed",
]
supplied_items: DF.Table[SubcontractingOrderSuppliedItem]
supplier: DF.Link
@@ -112,16 +113,10 @@ class SubcontractingOrder(SubcontractingController):
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):
@@ -277,6 +272,9 @@ class SubcontractingOrder(SubcontractingController):
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":
@@ -285,11 +283,6 @@ class SubcontractingOrder(SubcontractingController):
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:
@@ -304,13 +297,12 @@ class SubcontractingOrder(SubcontractingController):
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()
@@ -357,8 +349,8 @@ def get_mapped_subcontracting_receipt(source_name, target_doc=None):
@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)

View File

@@ -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];

View File

@@ -95,14 +95,14 @@ class TestSubcontractingOrder(FrappeTestCase):
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)
@@ -564,7 +564,6 @@ class TestSubcontractingOrder(FrappeTestCase):
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):

View File

@@ -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'));

View File

@@ -8,6 +8,7 @@ from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate
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
@@ -142,6 +143,7 @@ class SubcontractingReceipt(SubcontractingController):
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()
@@ -165,6 +167,7 @@ class SubcontractingReceipt(SubcontractingController):
"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()
@@ -175,6 +178,11 @@ class SubcontractingReceipt(SubcontractingController):
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):