Merge pull request #31353 from marination/so-to-wo-bom

fix: Pick Template BOM if variant BOM absent in WO popup from SO
This commit is contained in:
Marica 2022-06-15 14:37:15 +05:30 committed by GitHub
commit c83b4043b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 38 deletions

View File

@ -25,6 +25,7 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import (
from erpnext.selling.doctype.customer.customer import check_credit_limit from erpnext.selling.doctype.customer.customer import check_credit_limit
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.stock.doctype.item.item import get_item_defaults
from erpnext.stock.get_item_details import get_default_bom
from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty
form_grid_templates = {"items": "templates/form_grid/item_grid.html"} form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
@ -423,8 +424,9 @@ class SalesOrder(SellingController):
for table in [self.items, self.packed_items]: for table in [self.items, self.packed_items]:
for i in table: for i in table:
bom = get_default_bom_item(i.item_code) bom = get_default_bom(i.item_code)
stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
if not for_raw_material_request: if not for_raw_material_request:
total_work_order_qty = flt( total_work_order_qty = flt(
frappe.db.sql( frappe.db.sql(
@ -438,32 +440,19 @@ class SalesOrder(SellingController):
pending_qty = stock_qty pending_qty = stock_qty
if pending_qty and i.item_code not in product_bundle_parents: if pending_qty and i.item_code not in product_bundle_parents:
if bom: items.append(
items.append( dict(
dict( name=i.name,
name=i.name, item_code=i.item_code,
item_code=i.item_code, description=i.description,
description=i.description, bom=bom or "",
bom=bom, warehouse=i.warehouse,
warehouse=i.warehouse, pending_qty=pending_qty,
pending_qty=pending_qty, required_qty=pending_qty if for_raw_material_request else 0,
required_qty=pending_qty if for_raw_material_request else 0, sales_order_item=i.name,
sales_order_item=i.name,
)
)
else:
items.append(
dict(
name=i.name,
item_code=i.item_code,
description=i.description,
bom="",
warehouse=i.warehouse,
pending_qty=pending_qty,
required_qty=pending_qty if for_raw_material_request else 0,
sales_order_item=i.name,
)
) )
)
return items return items
def on_recurring(self, reference_doc, auto_repeat_doc): def on_recurring(self, reference_doc, auto_repeat_doc):
@ -1167,13 +1156,6 @@ def update_status(status, name):
so.update_status(status) so.update_status(status)
def get_default_bom_item(item_code):
bom = frappe.get_all("BOM", dict(item=item_code, is_active=True), order_by="is_default desc")
bom = bom[0].name if bom else None
return bom
@frappe.whitelist() @frappe.whitelist()
def make_raw_material_request(items, company, sales_order, project=None): def make_raw_material_request(items, company, sales_order, project=None):
if not frappe.has_permission("Sales Order", "write"): if not frappe.has_permission("Sales Order", "write"):

View File

@ -1380,6 +1380,59 @@ class TestSalesOrder(FrappeTestCase):
except Exception: except Exception:
self.fail("Can not cancel sales order with linked cancelled payment entry") self.fail("Can not cancel sales order with linked cancelled payment entry")
def test_work_order_pop_up_from_sales_order(self):
"Test `get_work_order_items` in Sales Order picks the right BOM for items to manufacture."
from erpnext.controllers.item_variant import create_variant
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
make_item( # template item
"Test-WO-Tshirt",
{
"has_variant": 1,
"variant_based_on": "Item Attribute",
"attributes": [{"attribute": "Test Colour"}],
},
)
make_item("Test-RM-Cotton") # RM for BOM
for colour in (
"Red",
"Green",
):
variant = create_variant("Test-WO-Tshirt", {"Test Colour": colour})
variant.save()
template_bom = make_bom(item="Test-WO-Tshirt", rate=100, raw_materials=["Test-RM-Cotton"])
red_var_bom = make_bom(item="Test-WO-Tshirt-R", rate=100, raw_materials=["Test-RM-Cotton"])
so = make_sales_order(
**{
"item_list": [
{
"item_code": "Test-WO-Tshirt-R",
"qty": 1,
"rate": 1000,
"warehouse": "_Test Warehouse - _TC",
},
{
"item_code": "Test-WO-Tshirt-G",
"qty": 1,
"rate": 1000,
"warehouse": "_Test Warehouse - _TC",
},
]
}
)
wo_items = so.get_work_order_items()
self.assertEqual(wo_items[0].get("item_code"), "Test-WO-Tshirt-R")
self.assertEqual(wo_items[0].get("bom"), red_var_bom.name)
# Must pick Template Item BOM for Test-WO-Tshirt-G as it has no BOM
self.assertEqual(wo_items[1].get("item_code"), "Test-WO-Tshirt-G")
self.assertEqual(wo_items[1].get("bom"), template_bom.name)
def test_request_for_raw_materials(self): def test_request_for_raw_materials(self):
item = make_item( item = make_item(
"_Test Finished Item", "_Test Finished Item",

View File

@ -1352,12 +1352,22 @@ def get_price_list_currency_and_exchange_rate(args):
@frappe.whitelist() @frappe.whitelist()
def get_default_bom(item_code=None): def get_default_bom(item_code=None):
if item_code: def _get_bom(item):
bom = frappe.db.get_value( bom = frappe.get_all(
"BOM", {"docstatus": 1, "is_default": 1, "is_active": 1, "item": item_code} "BOM", dict(item=item, is_active=True, is_default=True, docstatus=1), limit=1
) )
if bom: return bom[0].name if bom else None
return bom
if not item_code:
return
bom_name = _get_bom(item_code)
template_item = frappe.db.get_value("Item", item_code, "variant_of")
if not bom_name and template_item:
bom_name = _get_bom(template_item)
return bom_name
@frappe.whitelist() @frappe.whitelist()