When a template BOM is updated, the script ensures that all associated variant BOMs (BOMs for variant items) are updated to reflect the template’s items and operations. I want it to work on submittable variants also.
I created this server script but it was not working:
server script:
Server Script for BOM: Sync Template BOM changes to Variant BOMs
Trigger: On Update (after save) and on Create Variant BOM button
Check if the BOM is a template (item has variants)
is_template = frappe.db.get_value(“Item”, doc.item, “has_variants”)
if not is_template:
frappe.response[“message”] = “This BOM is not a template with variants.”
frappe.response[“status”] = “success”
else:
# Fetch all variant BOMs for the item (excluding the template BOM)
variant_boms = frappe.db.get_all(“BOM”, filters={
“item”: doc.item,
“is_active”: 1,
“name”: [“!=”, doc.name]
}, fields=[“name”, “docstatus”])
if not variant_boms:
frappe.response["message"] = "No variant BOMs found for this template."
frappe.response["status"] = "success"
else:
for variant in variant_boms:
variant_bom_name = variant.name
variant_docstatus = variant.docstatus
# Load the variant BOM document
variant_doc = frappe.get_doc("BOM", variant_bom_name)
# Clear existing items and operations in variant BOM
variant_doc.items = []
variant_doc.operations = []
# Sync items from template BOM
for item in doc.items:
new_item = frappe.get_doc({
"doctype": "BOM Item",
"item_code": item.item_code,
"item_name": item.item_name,
"do_not_explode": item.do_not_explode,
"bom_no": item.bom_no,
"allow_alternative_item": item.allow_alternative_item,
"is_stock_item": item.is_stock_item,
"qty": item.qty,
"uom": item.uom,
"stock_qty": item.stock_qty,
"stock_uom": item.stock_uom,
"conversion_factor": item.conversion_factor,
"rate": item.rate,
"has_variants": item.has_variants,
"include_item_in_manufacturing": item.include_item_in_manufacturing,
"amount": item.amount,
"sourced_by_supplier": item.sourced_by_supplier,
"idx": item.idx
})
variant_doc.append("items", new_item)
# Sync operations from template BOM
for operation in doc.operations:
new_operation = frappe.get_doc({
"doctype": "BOM Operation",
"operation": operation.operation,
"description": operation.description,
"workstation": operation.workstation,
"time_in_mins": operation.time_in_mins,
"fixed_time": operation.fixed_time,
"idx": operation.idx
})
variant_doc.append("operations", new_operation)
# Save the variant BOM, ignoring permissions and validation
try:
variant_doc.save(ignore_permissions=True, ignore_validate=True)
if variant_docstatus == 1:
# Update modified timestamp for submitted BOMs
frappe.db.set_value("BOM", variant_bom_name, "modified", frappe.utils.now())
frappe.db.commit()
except Exception as e:
frappe.log_error(f"Error updating variant BOM {variant_bom_name}: {str(e)}")
frappe.response["message"] = f"Error updating variant BOM {variant_bom_name}: {str(e)}"
frappe.response["status"] = "error"
break
else:
frappe.response["message"] = "Successfully updated all variant BOMs."
frappe.response["status"] = "success"
Handle Create Variant BOM button
if doc.get(“create_variant_bom_company”):
# Check if a variant BOM exists for the selected company
new_variant_bom = frappe.db.get_value(“BOM”, {
“item”: doc.item,
“company”: doc.create_variant_bom_company,
“is_active”: 1,
“name”: [“!=”, doc.name]
}, “name”)
if new_variant_bom:
# Load the new variant BOM document
variant_doc = frappe.get_doc("BOM", new_variant_bom)
# Clear existing items and operations
variant_doc.items = []
variant_doc.operations = []
# Sync items from template BOM
for item in doc.items:
new_item = frappe.get_doc({
"doctype": "BOM Item",
"item_code": item.item_code,
"item_name": item.item_name,
"do_not_explode": item.do_not_explode,
"bom_no": item.bom_no,
"allow_alternative_item": item.allow_alternative_item,
"is_stock_item": item.is_stock_item,
"qty": item.qty,
"uom": item.uom,
"stock_qty": item.stock_qty,
"stock_uom": item.stock_uom,
"conversion_factor": item.conversion_factor,
"rate": item.rate,
"has_variants": item.has_variants,
"include_item_in_manufacturing": item.include_item_in_manufacturing,
"amount": item.amount,
"sourced_by_supplier": item.sourced_by_supplier,
"idx": item.idx
})
variant_doc.append("items", new_item)
# Sync operations from template BOM
for operation in doc.operations:
new_operation = frappe.get_doc({
"doctype": "BOM Operation",
"operation": operation.operation,
"description": operation.description,
"workstation": operation.workstation,
"time_in_mins": operation.time_in_mins,
"fixed_time": operation.fixed_time,
"idx": operation.idx
})
variant_doc.append("operations", new_operation)
# Save the variant BOM, ignoring permissions and validation
try:
variant_doc.save(ignore_permissions=True, ignore_validate=True)
frappe.db.commit()
frappe.response["message"] = "New variant BOM updated with template data."
frappe.response["status"] = "success"
except Exception as e:
frappe.log_error(f"Error updating new variant BOM {new_variant_bom}: {str(e)}")
frappe.response["message"] = f"Error updating new variant BOM {new_variant_bom}: {str(e)}"
frappe.response["status"] = "error"