mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-16 03:29:16 +00:00
feat(subcontracting): Added provision to create multiple Subcontracting Orders against a single Purchase Order (#44711)
* feat(subcontracting): Added provision to create multiple Subcontracting Orders from a single Subcontracted Purchase Order * refactor(new_sc_flow_2): Fixed error thrown by semgrep
This commit is contained in:
@@ -5,10 +5,38 @@ frappe.provide("erpnext.buying");
|
||||
|
||||
erpnext.landed_cost_taxes_and_charges.setup_triggers("Subcontracting Order");
|
||||
|
||||
// client script for Subcontracting Order Item is not necessarily required as the server side code will do everything that is necessary.
|
||||
// this is just so that the user does not get potentially confused
|
||||
frappe.ui.form.on("Subcontracting Order Item", {
|
||||
qty(frm, cdt, cdn) {
|
||||
const row = locals[cdt][cdn];
|
||||
frappe.model.set_value(cdt, cdn, "amount", row.qty * row.rate);
|
||||
const service_item = frm.doc.service_items[row.idx - 1];
|
||||
frappe.model.set_value(
|
||||
service_item.doctype,
|
||||
service_item.name,
|
||||
"qty",
|
||||
row.qty * row.sc_conversion_factor
|
||||
);
|
||||
frappe.model.set_value(service_item.doctype, service_item.name, "fg_item_qty", row.qty);
|
||||
frappe.model.set_value(
|
||||
service_item.doctype,
|
||||
service_item.name,
|
||||
"amount",
|
||||
row.qty * row.sc_conversion_factor * service_item.rate
|
||||
);
|
||||
},
|
||||
before_items_remove(frm, cdt, cdn) {
|
||||
const row = locals[cdt][cdn];
|
||||
frm.toggle_enable(["service_items"], true);
|
||||
frm.get_field("service_items").grid.grid_rows[row.idx - 1].remove();
|
||||
frm.toggle_enable(["service_items"], false);
|
||||
},
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Subcontracting Order", {
|
||||
setup: (frm) => {
|
||||
frm.get_field("items").grid.cannot_add_rows = true;
|
||||
frm.get_field("items").grid.only_sortable();
|
||||
frm.trigger("set_queries");
|
||||
|
||||
frm.set_indicator_formatter("item_code", (doc) => (doc.qty <= doc.received_qty ? "green" : "orange"));
|
||||
|
||||
@@ -6,7 +6,6 @@ from frappe import _
|
||||
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.utils import check_on_hold_or_closed_status
|
||||
from erpnext.controllers.subcontracting_controller import SubcontractingController
|
||||
from erpnext.stock.stock_balance import update_bin_qty
|
||||
@@ -120,20 +119,15 @@ class SubcontractingOrder(SubcontractingController):
|
||||
def on_submit(self):
|
||||
self.update_prevdoc_status()
|
||||
self.update_status()
|
||||
self.update_sco_qty_in_po()
|
||||
|
||||
def on_cancel(self):
|
||||
self.update_prevdoc_status()
|
||||
self.update_status()
|
||||
self.update_sco_qty_in_po(cancel=True)
|
||||
|
||||
def validate_purchase_order_for_subcontracting(self):
|
||||
if self.purchase_order:
|
||||
if is_subcontracting_order_created(self.purchase_order):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Only one Subcontracting Order can be created against a Purchase Order, cancel the existing Subcontracting Order to create a new one."
|
||||
)
|
||||
)
|
||||
|
||||
po = frappe.get_doc("Purchase Order", self.purchase_order)
|
||||
|
||||
if not po.is_subcontracted:
|
||||
@@ -154,10 +148,23 @@ class SubcontractingOrder(SubcontractingController):
|
||||
frappe.throw(_("Please select a Subcontracting Purchase Order."))
|
||||
|
||||
def validate_service_items(self):
|
||||
for item in self.service_items:
|
||||
if frappe.get_value("Item", item.item_code, "is_stock_item"):
|
||||
msg = f"Service Item {item.item_name} must be a non-stock item."
|
||||
frappe.throw(_(msg))
|
||||
purchase_order_items = [item.purchase_order_item for item in self.items]
|
||||
self.service_items = [
|
||||
service_item
|
||||
for service_item in self.service_items
|
||||
if service_item.purchase_order_item in purchase_order_items
|
||||
]
|
||||
|
||||
for service_item in self.service_items:
|
||||
if frappe.get_value("Item", service_item.item_code, "is_stock_item"):
|
||||
frappe.throw(_("Service Item {0} must be a non-stock item.").format(service_item.item_code))
|
||||
|
||||
item = next(
|
||||
item for item in self.items if item.purchase_order_item == service_item.purchase_order_item
|
||||
)
|
||||
service_item.qty = item.qty * item.sc_conversion_factor
|
||||
service_item.fg_item_qty = item.qty
|
||||
service_item.amount = service_item.qty * service_item.rate
|
||||
|
||||
def validate_supplied_items(self):
|
||||
if self.supplier_warehouse:
|
||||
@@ -241,6 +248,18 @@ class SubcontractingOrder(SubcontractingController):
|
||||
for si in self.service_items:
|
||||
if si.fg_item:
|
||||
item = frappe.get_doc("Item", si.fg_item)
|
||||
|
||||
po_item = frappe.get_doc("Purchase Order Item", si.purchase_order_item)
|
||||
available_qty = po_item.qty - po_item.sco_qty
|
||||
|
||||
if available_qty == 0:
|
||||
continue
|
||||
|
||||
si.qty = available_qty
|
||||
conversion_factor = po_item.qty / po_item.fg_item_qty
|
||||
si.fg_item_qty = available_qty / conversion_factor
|
||||
si.amount = available_qty * si.rate
|
||||
|
||||
bom = (
|
||||
frappe.db.get_value(
|
||||
"Subcontracting BOM",
|
||||
@@ -257,6 +276,7 @@ class SubcontractingOrder(SubcontractingController):
|
||||
"schedule_date": self.schedule_date,
|
||||
"description": item.description,
|
||||
"qty": si.fg_item_qty,
|
||||
"sc_conversion_factor": conversion_factor,
|
||||
"stock_uom": item.stock_uom,
|
||||
"bom": bom,
|
||||
"purchase_order_item": si.purchase_order_item,
|
||||
@@ -310,6 +330,12 @@ class SubcontractingOrder(SubcontractingController):
|
||||
self.update_ordered_qty_for_subcontracting()
|
||||
self.update_reserved_qty_for_subcontracting()
|
||||
|
||||
def update_sco_qty_in_po(self, cancel=False):
|
||||
for service_item in self.service_items:
|
||||
doc = frappe.get_doc("Purchase Order Item", service_item.purchase_order_item)
|
||||
doc.sco_qty = (doc.sco_qty + service_item.qty) if not cancel else (doc.sco_qty - service_item.qty)
|
||||
doc.save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_subcontracting_receipt(source_name, target_doc=None):
|
||||
|
||||
@@ -49,12 +49,6 @@ class TestSubcontractingOrder(IntegrationTestCase):
|
||||
make_service_items()
|
||||
make_bom_for_subcontracted_items()
|
||||
|
||||
def test_populate_items_table(self):
|
||||
sco = get_subcontracting_order()
|
||||
sco.items = None
|
||||
sco.populate_items_table()
|
||||
self.assertEqual(len(sco.service_items), len(sco.items))
|
||||
|
||||
def test_set_missing_values(self):
|
||||
sco = get_subcontracting_order()
|
||||
before = {sco.total_qty, sco.total, sco.total_additional_costs}
|
||||
|
||||
@@ -54,7 +54,8 @@
|
||||
"column_break_nfod",
|
||||
"section_break_34",
|
||||
"purchase_order_item",
|
||||
"page_break"
|
||||
"page_break",
|
||||
"sc_conversion_factor"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -147,8 +148,8 @@
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Quantity",
|
||||
"non_negative": 1,
|
||||
"print_width": "60px",
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"width": "60px"
|
||||
},
|
||||
@@ -400,13 +401,20 @@
|
||||
{
|
||||
"fieldname": "column_break_nfod",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "sc_conversion_factor",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 1,
|
||||
"label": "SC Conversion Factor",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-12-06 15:23:05.252346",
|
||||
"modified": "2024-12-13 13:35:28.935898",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Order Item",
|
||||
|
||||
@@ -42,6 +42,7 @@ class SubcontractingOrderItem(Document):
|
||||
received_qty: DF.Float
|
||||
returned_qty: DF.Float
|
||||
rm_cost_per_qty: DF.Currency
|
||||
sc_conversion_factor: DF.Float
|
||||
schedule_date: DF.Date | None
|
||||
service_cost_per_qty: DF.Currency
|
||||
stock_uom: DF.Link
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-27 13:10:46.534662",
|
||||
"modified": "2024-12-05 17:33:46.099601",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Order Service Item",
|
||||
|
||||
@@ -19,6 +19,8 @@ class SubcontractingOrderServiceItem(Document):
|
||||
fg_item_qty: DF.Float
|
||||
item_code: DF.Link
|
||||
item_name: DF.Data
|
||||
material_request: DF.Link | None
|
||||
material_request_item: DF.Data | None
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
|
||||
Reference in New Issue
Block a user