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:
commit
c83b4043b8
@ -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"):
|
||||||
|
@ -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",
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user