fix: incorrect requested quantity for the subcontracting order (backport #38455) (#38471)

* fix: incorrect requested quantity for the subcontracting order

(cherry picked from commit 691e3bb24f)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.py
#	erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
#	erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
#	erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
This commit is contained in:
mergify[bot]
2023-11-30 20:53:52 +05:30
committed by GitHub
parent bec7fb5493
commit 497049b3fd
6 changed files with 309 additions and 154 deletions

View File

@@ -86,6 +86,10 @@ class PurchaseOrder(BuyingController):
self.reset_default_field_value("set_warehouse", "items", "warehouse") self.reset_default_field_value("set_warehouse", "items", "warehouse")
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
mri_compare_fields = [["project", "="], ["item_code", "="]]
if self.is_subcontracted:
mri_compare_fields = [["project", "="]]
super(PurchaseOrder, self).validate_with_previous_doc( super(PurchaseOrder, self).validate_with_previous_doc(
{ {
"Supplier Quotation": { "Supplier Quotation": {
@@ -108,7 +112,7 @@ class PurchaseOrder(BuyingController):
}, },
"Material Request Item": { "Material Request Item": {
"ref_dn_field": "material_request_item", "ref_dn_field": "material_request_item",
"compare_fields": [["project", "="], ["item_code", "="]], "compare_fields": mri_compare_fields,
"is_child_table": True, "is_child_table": True,
}, },
} }
@@ -282,23 +286,6 @@ class PurchaseOrder(BuyingController):
check_list.append(d.material_request) check_list.append(d.material_request)
check_on_hold_or_closed_status("Material Request", d.material_request) check_on_hold_or_closed_status("Material Request", d.material_request)
def update_requested_qty(self):
material_request_map = {}
for d in self.get("items"):
if d.material_request_item:
material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
for mr, mr_item_rows in material_request_map.items():
if mr and mr_item_rows:
mr_obj = frappe.get_doc("Material Request", mr)
if mr_obj.status in ["Stopped", "Cancelled"]:
frappe.throw(
_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError
)
mr_obj.update_requested_qty(mr_item_rows)
def update_ordered_qty(self, po_item_rows=None): def update_ordered_qty(self, po_item_rows=None):
"""update requested qty (before ordered_qty is updated)""" """update requested qty (before ordered_qty is updated)"""
item_wh_list = [] item_wh_list = []
@@ -340,7 +327,9 @@ class PurchaseOrder(BuyingController):
self.update_status_updater() self.update_status_updater()
self.update_prevdoc_status() self.update_prevdoc_status()
self.update_requested_qty() if not self.is_subcontracted or self.is_old_subcontracting_flow:
self.update_requested_qty()
self.update_ordered_qty() self.update_ordered_qty()
self.validate_budget() self.validate_budget()
self.update_reserved_qty_for_subcontract() self.update_reserved_qty_for_subcontract()
@@ -372,7 +361,9 @@ class PurchaseOrder(BuyingController):
# Must be called after updating ordered qty in Material Request # Must be called after updating ordered qty in Material Request
# bin uses Material Request Items to recalculate & update # bin uses Material Request Items to recalculate & update
self.update_requested_qty() if not self.is_subcontracted or self.is_old_subcontracting_flow:
self.update_requested_qty()
self.update_ordered_qty() self.update_ordered_qty()
self.update_blanket_order() self.update_blanket_order()
@@ -679,7 +670,10 @@ def get_mapped_subcontracting_order(source_name, target_doc=None):
}, },
"Purchase Order Item": { "Purchase Order Item": {
"doctype": "Subcontracting Order Service Item", "doctype": "Subcontracting Order Service Item",
"field_map": {}, "field_map": {
"material_request": "material_request",
"material_request_item": "material_request_item",
},
"field_no_map": [], "field_no_map": [],
}, },
}, },

View File

@@ -789,6 +789,23 @@ class SubcontractingController(StockController):
return self._sub_contracted_items return self._sub_contracted_items
def update_requested_qty(self):
material_request_map = {}
for d in self.get("items"):
if d.material_request_item:
material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
for mr, mr_item_rows in material_request_map.items():
if mr and mr_item_rows:
mr_obj = frappe.get_doc("Material Request", mr)
if mr_obj.status in ["Stopped", "Cancelled"]:
frappe.throw(
_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError
)
mr_obj.update_requested_qty(mr_item_rows)
def get_item_details(items): def get_item_details(items):
item = frappe.qb.DocType("Item") item = frappe.qb.DocType("Item")

View File

@@ -13,6 +13,23 @@ from erpnext.stock.utils import get_bin
class SubcontractingOrder(SubcontractingController): class SubcontractingOrder(SubcontractingController):
def __init__(self, *args, **kwargs):
super(SubcontractingOrder, self).__init__(*args, **kwargs)
self.status_updater = [
{
"source_dt": "Subcontracting Order Item",
"target_dt": "Material Request Item",
"join_field": "material_request_item",
"target_field": "ordered_qty",
"target_parent_dt": "Material Request",
"target_parent_field": "per_ordered",
"target_ref_field": "stock_qty",
"source_field": "qty",
"percent_join_field": "material_request",
}
]
def before_validate(self): def before_validate(self):
super(SubcontractingOrder, self).before_validate() super(SubcontractingOrder, self).before_validate()
@@ -26,11 +43,15 @@ class SubcontractingOrder(SubcontractingController):
self.reset_default_field_value("set_warehouse", "items", "warehouse") self.reset_default_field_value("set_warehouse", "items", "warehouse")
def on_submit(self): def on_submit(self):
self.update_prevdoc_status()
self.update_requested_qty()
self.update_ordered_qty_for_subcontracting() self.update_ordered_qty_for_subcontracting()
self.update_reserved_qty_for_subcontracting() self.update_reserved_qty_for_subcontracting()
self.update_status() self.update_status()
def on_cancel(self): def on_cancel(self):
self.update_prevdoc_status()
self.update_requested_qty()
self.update_ordered_qty_for_subcontracting() self.update_ordered_qty_for_subcontracting()
self.update_reserved_qty_for_subcontracting() self.update_reserved_qty_for_subcontracting()
self.update_status() self.update_status()
@@ -164,7 +185,9 @@ class SubcontractingOrder(SubcontractingController):
"qty": si.fg_item_qty, "qty": si.fg_item_qty,
"stock_uom": item.stock_uom, "stock_uom": item.stock_uom,
"bom": bom, "bom": bom,
}, "material_request": si.material_request,
"material_request_item": si.material_request_item,
}
) )
else: else:
frappe.throw( frappe.throw(

View File

@@ -628,6 +628,62 @@ class TestSubcontractingOrder(FrappeTestCase):
self.assertEqual(ordered_qty + 10, new_ordered_qty) self.assertEqual(ordered_qty + 10, new_ordered_qty)
def test_requested_qty_for_subcontracting_order(self):
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
requested_qty = frappe.db.get_value(
"Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "Subcontracted Item SA8"},
fieldname="indented_qty",
)
requested_qty = flt(requested_qty)
mr = make_material_request(
item_code="Subcontracted Item SA8",
material_request_type="Purchase",
qty=10,
)
self.assertTrue(mr.docstatus == 1)
new_requested_qty = frappe.db.get_value(
"Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "Subcontracted Item SA8"},
fieldname="indented_qty",
)
new_requested_qty = flt(new_requested_qty)
self.assertEqual(requested_qty + 10, new_requested_qty)
po = make_purchase_order(mr.name)
po.is_subcontracted = 1
po.supplier = "_Test Supplier"
po.items[0].fg_item = "Subcontracted Item SA8"
po.items[0].fg_item_qty = 10
po.items[0].item_code = "Subcontracted Service Item 8"
po.items[0].item_name = "Subcontracted Service Item 8"
po.items[0].qty = 10
po.supplier_warehouse = "_Test Warehouse 1 - _TC"
po.save()
po.submit()
self.assertTrue(po.items[0].material_request)
self.assertTrue(po.items[0].material_request_item)
sco = create_subcontracting_order(po_name=po.name)
self.assertTrue(sco.items[0].material_request)
self.assertTrue(sco.items[0].material_request_item)
new_requested_qty = frappe.db.get_value(
"Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "Subcontracted Item SA8"},
fieldname="indented_qty",
)
new_requested_qty = flt(new_requested_qty)
self.assertEqual(requested_qty, new_requested_qty)
def create_subcontracting_order(**args): def create_subcontracting_order(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@@ -40,6 +40,11 @@
"manufacture_section", "manufacture_section",
"manufacturer", "manufacturer",
"manufacturer_part_no", "manufacturer_part_no",
"column_break_impp",
"reference_section",
"material_request",
"column_break_fpyl",
"material_request_item",
"accounting_dimensions_section", "accounting_dimensions_section",
"cost_center", "cost_center",
"dimension_col_break", "dimension_col_break",
@@ -332,13 +337,44 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Project", "label": "Project",
"options": "Project" "options": "Project"
},
{
"fieldname": "column_break_impp",
"fieldtype": "Column Break"
},
{
"fieldname": "material_request",
"fieldtype": "Link",
"label": "Material Request",
"no_copy": 1,
"options": "Material Request",
"read_only": 1,
"search_index": 1
},
{
"fieldname": "material_request_item",
"fieldtype": "Data",
"label": "Material Request Item",
"no_copy": 1,
"read_only": 1,
"search_index": 1
},
{
"collapsible": 1,
"fieldname": "reference_section",
"fieldtype": "Section Break",
"label": "Reference"
},
{
"fieldname": "column_break_fpyl",
"fieldtype": "Column Break"
} }
], ],
"idx": 1, "idx": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2023-11-14 18:38:37.640677", "modified": "2023-11-30 15:29:43.744618",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Subcontracting", "module": "Subcontracting",
"name": "Subcontracting Order Item", "name": "Subcontracting Order Item",
@@ -351,4 +387,4 @@
"sort_order": "DESC", "sort_order": "DESC",
"states": [], "states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -1,131 +1,160 @@
{ {
"actions": [], "actions": [],
"autoname": "hash", "autoname": "hash",
"creation": "2022-04-01 19:23:05.728354", "creation": "2022-04-01 19:23:05.728354",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"item_code", "item_code",
"column_break_2", "column_break_2",
"item_name", "item_name",
"section_break_4", "section_break_4",
"qty", "qty",
"column_break_6", "column_break_6",
"rate", "rate",
"column_break_8", "column_break_8",
"amount", "amount",
"section_break_10", "section_break_10",
"fg_item", "fg_item",
"column_break_12", "column_break_12",
"fg_item_qty" "fg_item_qty",
], "section_break_kphn",
"fields": [ "material_request",
{ "column_break_piqi",
"bold": 1, "material_request_item"
"columns": 2, ],
"fieldname": "item_code", "fields": [
"fieldtype": "Link", {
"in_list_view": 1, "bold": 1,
"label": "Item Code", "columns": 2,
"options": "Item", "fieldname": "item_code",
"reqd": 1, "fieldtype": "Link",
"search_index": 1 "in_list_view": 1,
}, "label": "Item Code",
{ "options": "Item",
"fetch_from": "item_code.item_name", "reqd": 1,
"fieldname": "item_name", "search_index": 1
"fieldtype": "Data", },
"in_global_search": 1, {
"in_list_view": 1, "fetch_from": "item_code.item_name",
"label": "Item Name", "fieldname": "item_name",
"print_hide": 1, "fieldtype": "Data",
"reqd": 1 "in_global_search": 1,
}, "in_list_view": 1,
{ "label": "Item Name",
"bold": 1, "print_hide": 1,
"columns": 1, "reqd": 1
"fieldname": "qty", },
"fieldtype": "Float", {
"in_list_view": 1, "bold": 1,
"label": "Quantity", "columns": 1,
"print_width": "60px", "fieldname": "qty",
"reqd": 1, "fieldtype": "Float",
"width": "60px" "in_list_view": 1,
}, "label": "Quantity",
{ "print_width": "60px",
"bold": 1, "reqd": 1,
"columns": 2, "width": "60px"
"fetch_from": "item_code.standard_rate", },
"fetch_if_empty": 1, {
"fieldname": "rate", "bold": 1,
"fieldtype": "Currency", "columns": 2,
"in_list_view": 1, "fetch_from": "item_code.standard_rate",
"label": "Rate", "fetch_if_empty": 1,
"options": "currency", "fieldname": "rate",
"reqd": 1 "fieldtype": "Currency",
}, "in_list_view": 1,
{ "label": "Rate",
"columns": 2, "options": "currency",
"fieldname": "amount", "reqd": 1
"fieldtype": "Currency", },
"in_list_view": 1, {
"label": "Amount", "columns": 2,
"options": "currency", "fieldname": "amount",
"read_only": 1, "fieldtype": "Currency",
"reqd": 1 "in_list_view": 1,
}, "label": "Amount",
{ "options": "currency",
"fieldname": "fg_item", "read_only": 1,
"fieldtype": "Link", "reqd": 1
"label": "Finished Good Item", },
"options": "Item", {
"reqd": 1 "fieldname": "fg_item",
}, "fieldtype": "Link",
{ "label": "Finished Good Item",
"default": "1", "options": "Item",
"fieldname": "fg_item_qty", "reqd": 1
"fieldtype": "Float", },
"label": "Finished Good Item Quantity", {
"reqd": 1 "default": "1",
}, "fieldname": "fg_item_qty",
{ "fieldtype": "Float",
"fieldname": "column_break_2", "label": "Finished Good Item Quantity",
"fieldtype": "Column Break" "reqd": 1
}, },
{ {
"fieldname": "section_break_4", "fieldname": "column_break_2",
"fieldtype": "Section Break" "fieldtype": "Column Break"
}, },
{ {
"fieldname": "column_break_6", "fieldname": "section_break_4",
"fieldtype": "Column Break" "fieldtype": "Section Break"
}, },
{ {
"fieldname": "column_break_8", "fieldname": "column_break_6",
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"fieldname": "section_break_10", "fieldname": "column_break_8",
"fieldtype": "Section Break" "fieldtype": "Column Break"
}, },
{ {
"fieldname": "column_break_12", "fieldname": "section_break_10",
"fieldtype": "Column Break" "fieldtype": "Section Break"
} },
], {
"istable": 1, "fieldname": "column_break_12",
"links": [], "fieldtype": "Column Break"
"modified": "2022-04-07 11:43:43.094867", },
"modified_by": "Administrator", {
"module": "Subcontracting", "collapsible": 1,
"name": "Subcontracting Order Service Item", "fieldname": "section_break_kphn",
"naming_rule": "Random", "fieldtype": "Section Break",
"owner": "Administrator", "label": "Reference"
"permissions": [], },
"quick_entry": 1, {
"search_fields": "item_name", "fieldname": "material_request",
"sort_field": "modified", "fieldtype": "Link",
"sort_order": "DESC", "label": "Material Request",
"states": [] "no_copy": 1,
} "options": "Material Request",
"read_only": 1
},
{
"fieldname": "column_break_piqi",
"fieldtype": "Column Break"
},
{
"fieldname": "material_request_item",
"fieldtype": "Data",
"label": "Material Request Item",
"no_copy": 1,
"read_only": 1
}
],
"istable": 1,
"links": [],
"modified": "2023-11-30 13:29:31.017440",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Order Service Item",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"search_fields": "item_name",
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}