feat: Only update exploded items rate and amount

- Generate RM-Rate map from Items table (will include subassembly items with rate)
- Function to reset exploded item rate from above map
- `db_update` exploded item rate only if rate is changed
- Via Update Cost, only update exploded items rate, do not regenerate table again
- Exploded Items are regenerated on Save and Replace BOM job
- `calculate_exploded_cost` is run only via non doc events (Update Cost button, Update BOMs Cost Job)
This commit is contained in:
marination 2022-05-25 15:32:42 +05:30
parent 9f5f18e94d
commit eabd8290d4
3 changed files with 39 additions and 5 deletions

View File

@ -5,7 +5,7 @@ import functools
import re import re
from collections import deque from collections import deque
from operator import itemgetter from operator import itemgetter
from typing import List from typing import Dict, List
import frappe import frappe
from frappe import _ from frappe import _
@ -185,6 +185,7 @@ class BOM(WebsiteGenerator):
self.validate_transfer_against() self.validate_transfer_against()
self.set_routing_operations() self.set_routing_operations()
self.validate_operations() self.validate_operations()
self.update_exploded_items(save=False)
self.calculate_cost() self.calculate_cost()
self.update_stock_qty() self.update_stock_qty()
self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False) self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False)
@ -391,8 +392,6 @@ class BOM(WebsiteGenerator):
if save: if save:
self.db_update() self.db_update()
self.update_exploded_items(save=save)
# update parent BOMs # update parent BOMs
if self.total_cost != existing_bom_cost and update_parent: if self.total_cost != existing_bom_cost and update_parent:
parent_boms = frappe.db.sql_list( parent_boms = frappe.db.sql_list(
@ -594,6 +593,10 @@ class BOM(WebsiteGenerator):
self.calculate_op_cost(update_hour_rate) self.calculate_op_cost(update_hour_rate)
self.calculate_rm_cost(save=save_updates) self.calculate_rm_cost(save=save_updates)
self.calculate_sm_cost(save=save_updates) self.calculate_sm_cost(save=save_updates)
if save_updates:
# not via doc event, table is not regenerated and needs updation
self.calculate_exploded_cost()
self.total_cost = self.operating_cost + self.raw_material_cost - self.scrap_material_cost self.total_cost = self.operating_cost + self.raw_material_cost - self.scrap_material_cost
self.base_total_cost = ( self.base_total_cost = (
self.base_operating_cost + self.base_raw_material_cost - self.base_scrap_material_cost self.base_operating_cost + self.base_raw_material_cost - self.base_scrap_material_cost
@ -689,6 +692,36 @@ class BOM(WebsiteGenerator):
self.scrap_material_cost = total_sm_cost self.scrap_material_cost = total_sm_cost
self.base_scrap_material_cost = base_total_sm_cost self.base_scrap_material_cost = base_total_sm_cost
def calculate_exploded_cost(self):
"Set exploded row cost from it's parent BOM."
rm_rate_map = self.get_rm_rate_map()
for row in self.get("exploded_items"):
old_rate = flt(row.rate)
row.rate = rm_rate_map.get(row.item_code)
row.amount = flt(row.stock_qty) * row.rate
if old_rate != row.rate:
# Only db_update if unchanged
row.db_update()
def get_rm_rate_map(self) -> Dict[str, float]:
"Create Raw Material-Rate map for Exploded Items. Fetch rate from Items table or Subassembly BOM."
rm_rate_map = {}
for item in self.get("items"):
if item.bom_no:
# Get Item-Rate from Subassembly BOM
explosion_items = frappe.db.get_all(
"BOM Explosion Item", filters={"parent": item.bom_no}, fields=["item_code", "rate"]
)
explosion_item_rate = {item.item_code: flt(item.rate) for item in explosion_items}
rm_rate_map.update(explosion_item_rate)
else:
rm_rate_map[item.item_code] = flt(item.base_rate) / flt(item.conversion_factor or 1.0)
return rm_rate_map
def update_exploded_items(self, save=True): def update_exploded_items(self, save=True):
"""Update Flat BOM, following will be correct data""" """Update Flat BOM, following will be correct data"""
self.get_exploded_items() self.get_exploded_items()

View File

@ -169,13 +169,15 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-10-08 16:21:29.386212", "modified": "2022-05-27 13:42:23.305455",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM Explosion Item", "name": "BOM Explosion Item",
"naming_rule": "Random",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@ -120,7 +120,6 @@ def update_cost_in_boms(bom_list: List[str], docname: str) -> Dict[str, Dict]:
for bom in bom_list: for bom in bom_list:
bom_doc = frappe.get_cached_doc("BOM", bom) bom_doc = frappe.get_cached_doc("BOM", bom)
bom_doc.calculate_cost(save_updates=True, update_hour_rate=True) bom_doc.calculate_cost(save_updates=True, update_hour_rate=True)
# bom_doc.update_exploded_items(save=True) #TODO: edit exploded items rate
bom_doc.db_update() bom_doc.db_update()
updated_boms[bom] = True updated_boms[bom] = True