* fix: only validate qty for main non-subassy items (cherry picked from commit3d43c437ad) * fix: subassembly items linked to temporary name Production Plan tables for po_items and sub_assembly_items are prepared client side so both dont exist at time of first save or modifying and hence any "links" created are invalid. This change retains temporary name so it can be relinked server side after naming is performed. Co-Authored-By: Marica <maricadsouza221197@gmail.com> (cherry picked from commit5b1d6055e6) * test: dont resubmit WO [skip ci] Co-authored-by: Ankush Menat <ankush@frappe.io>
This commit is contained in:
@@ -2,6 +2,13 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Production Plan', {
|
||||
|
||||
before_save: function(frm) {
|
||||
// preserve temporary names on production plan item to re-link sub-assembly items
|
||||
frm.doc.po_items.forEach(item => {
|
||||
item.temporary_name = item.name;
|
||||
});
|
||||
},
|
||||
setup: function(frm) {
|
||||
frm.custom_make_buttons = {
|
||||
'Work Order': 'Work Order / Subcontract PO',
|
||||
|
||||
@@ -32,6 +32,7 @@ class ProductionPlan(Document):
|
||||
self.set_pending_qty_in_row_without_reference()
|
||||
self.calculate_total_planned_qty()
|
||||
self.set_status()
|
||||
self._rename_temporary_references()
|
||||
|
||||
def set_pending_qty_in_row_without_reference(self):
|
||||
"Set Pending Qty in independent rows (not from SO or MR)."
|
||||
@@ -57,6 +58,18 @@ class ProductionPlan(Document):
|
||||
if not flt(d.planned_qty):
|
||||
frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx))
|
||||
|
||||
def _rename_temporary_references(self):
|
||||
""" po_items and sub_assembly_items items are both constructed client side without saving.
|
||||
|
||||
Attempt to fix linkages by using temporary names to map final row names.
|
||||
"""
|
||||
new_name_map = {d.temporary_name: d.name for d in self.po_items if d.temporary_name}
|
||||
actual_names = set(new_name_map.values())
|
||||
|
||||
for sub_assy in self.sub_assembly_items:
|
||||
if sub_assy.production_plan_item not in actual_names:
|
||||
sub_assy.production_plan_item = new_name_map.get(sub_assy.production_plan_item)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_open_sales_orders(self):
|
||||
""" Pull sales orders which are pending to deliver based on criteria selected"""
|
||||
|
||||
@@ -617,6 +617,39 @@ class TestProductionPlan(FrappeTestCase):
|
||||
wo_doc.submit()
|
||||
self.assertEqual(wo_doc.qty, 0.55)
|
||||
|
||||
def test_temporary_name_relinking(self):
|
||||
|
||||
pp = frappe.new_doc("Production Plan")
|
||||
|
||||
# this can not be unittested so mocking data that would be expected
|
||||
# from client side.
|
||||
for _ in range(10):
|
||||
po_item = pp.append("po_items", {
|
||||
"name": frappe.generate_hash(length=10),
|
||||
"temporary_name": frappe.generate_hash(length=10),
|
||||
})
|
||||
pp.append("sub_assembly_items", {
|
||||
"production_plan_item": po_item.temporary_name
|
||||
})
|
||||
pp._rename_temporary_references()
|
||||
|
||||
for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items):
|
||||
self.assertEqual(po_item.name, subassy_item.production_plan_item)
|
||||
|
||||
# bad links should be erased
|
||||
pp.append("sub_assembly_items", {
|
||||
"production_plan_item": frappe.generate_hash(length=16)
|
||||
})
|
||||
pp._rename_temporary_references()
|
||||
self.assertIsNone(pp.sub_assembly_items[-1].production_plan_item)
|
||||
pp.sub_assembly_items.pop()
|
||||
|
||||
# reattempting on same doc shouldn't change anything
|
||||
pp._rename_temporary_references()
|
||||
for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items):
|
||||
self.assertEqual(po_item.name, subassy_item.production_plan_item)
|
||||
|
||||
|
||||
def create_production_plan(**args):
|
||||
"""
|
||||
sales_order (obj): Sales Order Doc Object
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"material_request",
|
||||
"material_request_item",
|
||||
"product_bundle_item",
|
||||
"item_reference"
|
||||
"item_reference",
|
||||
"temporary_name"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -204,17 +205,25 @@
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Item Reference"
|
||||
},
|
||||
{
|
||||
"fieldname": "temporary_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "temporary name"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-28 18:31:06.822168",
|
||||
"modified": "2022-03-24 04:54:09.940224",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Plan Item",
|
||||
"naming_rule": "Random",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
}
|
||||
@@ -352,7 +352,6 @@ class TestWorkOrder(FrappeTestCase):
|
||||
wo_order = make_wo_order_test_record(planned_start_date=now(),
|
||||
sales_order=so.name, qty=3)
|
||||
|
||||
wo_order.submit()
|
||||
self.assertEqual(wo_order.docstatus, 1)
|
||||
|
||||
allow_overproduction("overproduction_percentage_for_sales_order", 0)
|
||||
|
||||
@@ -457,7 +457,8 @@ class WorkOrder(Document):
|
||||
mr_obj.update_requested_qty([self.material_request_item])
|
||||
|
||||
def update_ordered_qty(self):
|
||||
if self.production_plan and self.production_plan_item:
|
||||
if self.production_plan and self.production_plan_item \
|
||||
and not self.production_plan_sub_assembly_item:
|
||||
qty = frappe.get_value("Production Plan Item", self.production_plan_item, "ordered_qty") or 0.0
|
||||
|
||||
if self.docstatus == 1:
|
||||
@@ -640,9 +641,13 @@ class WorkOrder(Document):
|
||||
if not self.qty > 0:
|
||||
frappe.throw(_("Quantity to Manufacture must be greater than 0."))
|
||||
|
||||
if self.production_plan and self.production_plan_item:
|
||||
if self.production_plan and self.production_plan_item \
|
||||
and not self.production_plan_sub_assembly_item:
|
||||
qty_dict = frappe.db.get_value("Production Plan Item", self.production_plan_item, ["planned_qty", "ordered_qty"], as_dict=1)
|
||||
|
||||
if not qty_dict:
|
||||
return
|
||||
|
||||
allowance_qty = flt(frappe.db.get_single_value("Manufacturing Settings",
|
||||
"overproduction_percentage_for_work_order"))/100 * qty_dict.get("planned_qty", 0)
|
||||
|
||||
|
||||
@@ -352,6 +352,6 @@ erpnext.patches.v13_0.update_reserved_qty_closed_wo
|
||||
erpnext.patches.v13_0.amazon_mws_deprecation_warning
|
||||
erpnext.patches.v13_0.set_work_order_qty_in_so_from_mr
|
||||
erpnext.patches.v13_0.update_accounts_in_loan_docs
|
||||
erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items
|
||||
erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
|
||||
erpnext.patches.v13_0.rename_non_profit_fields
|
||||
erpnext.patches.v13_0.enable_ksa_vat_docs #1
|
||||
|
||||
Reference in New Issue
Block a user