See #6570. More consistent cost rate assignment

Now when a cost rate is assigned to a cost item, it creates also a relation between them, making the relation more consistent. It is also shown in the UI
This commit is contained in:
Massimo Fabbro
2025-05-11 08:51:46 +02:00
committed by Massimo Fabbro
parent 8cb871a6a2
commit 2bef2a0219
7 changed files with 74 additions and 2 deletions
@@ -64,6 +64,7 @@ classes = (
operator.RefreshCostScheduleCsv,
operator.LoadCostItemElementQuantities,
operator.LoadCostItemQuantities,
operator.ShowAssignedCostRate,
operator.LoadCostItemResourceQuantities,
operator.LoadCostItemTaskQuantities,
operator.LoadCostItemTypes,
@@ -110,6 +110,7 @@ class CostSchedulesData:
cls._load_cost_item_quantities(cost_item, data)
cls._load_cost_values(cost_item, data)
cls._load_nesting_index(cost_item, data)
cls._load_assigned_cost_rate(cost_item, data)
results[cost_item.id()] = data
return results
@@ -154,7 +155,12 @@ class CostSchedulesData:
else:
data["TotalCost"] = data["TotalAppliedValue"] * cost_quantity
if is_sum:
#pass If it is None it doesn't allow me to assign a cost rate composed by sum
data["TotalAppliedValue"] = None
@classmethod
def _load_assigned_cost_rate(cls, cost_item: ifcopenshell.entity_instance, data: CostItem) -> None:
data["AssignedCostRate"] = tool.Cost.get_cost_item_rate_assignment(cost_item)
@classmethod
def _load_cost_item_quantities(cls, cost_item: ifcopenshell.entity_instance, data: CostItem) -> None:
@@ -248,6 +254,8 @@ class CostSchedulesData:
) -> None:
value_data = cost_value.get_info()
del value_data["AppliedValue"]
if tool.Cost.get_assigned_rate_cost_item(root_element):
root_element = tool.Cost.get_assigned_rate_cost_item(root_element)
if value_data["UnitBasis"]:
data = cost_value.UnitBasis.get_info()
data["ValueComponent"] = data["ValueComponent"].wrappedValue
+32 -1
View File
@@ -19,6 +19,7 @@
# pyright: reportUnnecessaryTypeIgnoreComment=error
import bpy
import textwrap
import ifcopenshell.api
import bonsai.tool as tool
from bpy_extras.io_utils import ImportHelper, ExportHelper
@@ -607,6 +608,33 @@ class LoadCostItemQuantities(bpy.types.Operator):
core.load_cost_item_quantities(tool.Cost)
return {"FINISHED"}
class ShowAssignedCostRate(bpy.types.Operator):
bl_idname = "bim.show_assigned_cost_rate"
bl_label = "Info about the assigned cost item rate"
bl_options = {"REGISTER"}
assigned_rate_id: bpy.props.IntProperty()
assigned_rate_name: bpy.props.StringProperty()
assigned_rate_description: bpy.props.StringProperty()
assigned_rate_total_value: bpy.props.FloatProperty()
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=450)
def execute(self, context):
#core.load_cost_item_quantities(tool.Cost) IS IT NECESSARY?
return {"FINISHED"}
def draw(self, context):
layout = self.layout
wrapper = textwrap.TextWrapper(width=80)
layout.label(text=f"ID: {self.assigned_rate_id}")
layout.label(text=f"Name: {self.assigned_rate_name}")
layout.label(text="Description:")
for line in wrapper.wrap(str(self.assigned_rate_description)):
layout.label(text=line)
layout.label(text=f"Value: {self.assigned_rate_total_value}")
class LoadCostItemElementQuantities(bpy.types.Operator):
bl_idname = "bim.load_cost_item_element_quantities"
@@ -658,7 +686,10 @@ class AssignCostValue(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
core.assign_cost_value(
tool.Ifc, cost_item=tool.Ifc.get().by_id(self.cost_item), cost_rate=tool.Ifc.get().by_id(self.cost_rate)
tool.Ifc,
tool.Cost,
cost_item=tool.Ifc.get().by_id(self.cost_item),
cost_rate=tool.Ifc.get().by_id(self.cost_rate)
)
+16
View File
@@ -655,6 +655,8 @@ class BIM_UL_cost_items_trait:
for column in props.columns:
split2.label(text=column.name)
split2.label(text="Total Cost")
split2.alignment = "LEFT"
split2.label(text="Rate")
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
@@ -684,6 +686,8 @@ class BIM_UL_cost_items_trait:
if self.props.change_cost_item_parent:
self.draw_parent_operator(row, item.ifc_definition_id)
self.draw_assigned_rate_column(split2, cost_item)
# TODO: reimplement "bim.copy_cost_item_values" somewhere with better UX
@@ -723,6 +727,18 @@ class BIM_UL_cost_items_trait:
layout.label(text=label)
else:
layout.label(text="-")
def draw_assigned_rate_column(self, layout, cost_item):
row=layout.row()
row.alignment= "LEFT"
if cost_item["AssignedCostRate"] is not None:
op = row.operator("bim.show_assigned_cost_rate", text="", emboss=False, icon="ZOOM_IN")
op.assigned_rate_id = cost_item["AssignedCostRate"].id()
op.assigned_rate_name = cost_item["AssignedCostRate"].Name or ""
op.assigned_rate_description = cost_item["AssignedCostRate"].Description or ""
op.assigned_rate_total_value = CostSchedulesData.data["cost_items"][cost_item["AssignedCostRate"].id()]["TotalAppliedValue"]
else:
row.label(text="-")
def draw_value_column(self, layout, cost_item):
if cost_item["TotalAppliedValue"]:
+10 -1
View File
@@ -193,9 +193,18 @@ def load_cost_item_resource_quantities(cost: tool.Cost) -> None:
def assign_cost_value(
ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, cost_rate: ifcopenshell.entity_instance
ifc: tool.Ifc,
cost: tool.Cost,
cost_item: ifcopenshell.entity_instance,
cost_rate: ifcopenshell.entity_instance
) -> None:
ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate)
existing_cost_rate = cost.get_cost_item_rate_assignment(cost_item)
if existing_cost_rate is None:
ifc.run("control.assign_control", relating_control=cost_rate, related_object=cost_item)
else:
ifc.run("control.unassign_control", relating_control=existing_cost_rate, related_object=cost_item)
ifc.run("control.assign_control", relating_control=cost_rate, related_object=cost_item)
def load_schedule_of_rates(cost: tool.Cost, schedule_of_rates: ifcopenshell.entity_instance) -> None:
+1
View File
@@ -228,6 +228,7 @@ class Cost:
def get_active_cost_schedule(cls): pass
def get_active_cost_schedule(cls): pass
def get_attributes_for_cost_value(cls, cost_type, cost_category): pass
def get_assigned_rate_cost_item(cls, cost_item): pass
def get_cost_item_attributes(cls, cost_item): pass
def get_cost_item_products(cls): pass
def get_cost_item_quantity_attributes(cls): pass
+6
View File
@@ -835,6 +835,12 @@ class Cost(bonsai.core.tool.Cost):
]
return bool(cost_items)
@classmethod
def get_assigned_rate_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
for assignment in cost_item.HasAssignments:
if assignment.RelatingControl.is_a() == "IfcCostItem":
return assignment.RelatingControl
@classmethod
def load_product_cost_items(cls, product: ifcopenshell.entity_instance) -> None:
props = cls.get_cost_props()