You can now assign rates from a schedule of rates to a cost plan

This commit is contained in:
Dion Moult
2021-08-17 12:22:57 +10:00
parent 899a0c6fb5
commit 912699e63c
5 changed files with 82 additions and 21 deletions
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -610,6 +609,7 @@ class RemoveCostItemValue(bpy.types.Operator):
bl_idname = "bim.remove_cost_item_value"
bl_label = "Add Cost Item Value"
bl_options = {"REGISTER", "UNDO"}
parent: bpy.props.IntProperty()
cost_value: bpy.props.IntProperty()
def execute(self, context):
@@ -617,7 +617,12 @@ class RemoveCostItemValue(bpy.types.Operator):
def _execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run("cost.remove_cost_item_value", self.file, cost_value=self.file.by_id(self.cost_value))
ifcopenshell.api.run(
"cost.remove_cost_item_value",
self.file,
parent=self.file.by_id(self.parent),
cost_value=self.file.by_id(self.cost_value),
)
Data.load(self.file)
return {"FINISHED"}
@@ -660,6 +665,7 @@ class EnableEditingCostItemValue(bpy.types.Operator):
prop.float_value = data["UnitBasis"]["ValueComponent"] or 0
else:
prop.float_value = 0
prop = self.props.cost_value_attributes.add()
prop.name = "UnitBasisUnit"
prop.data_type = "enum"
@@ -897,9 +903,9 @@ class LoadCostItemTypes(bpy.types.Operator):
while len(self.props.cost_item_type_products) > 0:
self.props.cost_item_type_products.remove(0)
# TODO implement process and resource types
#while len(self.props.cost_item_processes) > 0:
# while len(self.props.cost_item_processes) > 0:
# self.props.cost_item_processes.remove(0)
#while len(self.props.cost_item_resources) > 0:
# while len(self.props.cost_item_resources) > 0:
# self.props.cost_item_resources.remove(0)
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
for control_id, quantity_ids in Data.cost_items[ifc_definition_id]["Controls"].items():
@@ -907,9 +913,9 @@ class LoadCostItemTypes(bpy.types.Operator):
if related_object.is_a("IfcTypeProduct"):
new = self.props.cost_item_type_products.add()
# TODO implement process and resource types
#elif related_object.is_a("IfcProcess"):
# elif related_object.is_a("IfcProcess"):
# new = self.props.cost_item_processes.add()
#elif related_object.is_a("IfcResource"):
# elif related_object.is_a("IfcResource"):
# new = self.props.cost_item_resources.add()
new.ifc_definition_id = control_id
new.name = related_object.Name or "Unnamed"
@@ -920,11 +926,21 @@ class AssignCostValue(bpy.types.Operator):
bl_idname = "bim.assign_cost_value"
bl_label = "Assign Cost Value"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
cost_rate: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.assign_cost_value",
self.file,
cost_item=self.file.by_id(self.cost_item),
cost_rate=self.file.by_id(self.cost_rate),
)
Data.load(self.file)
return {"FINISHED"}
@@ -160,7 +160,10 @@ def getResourceQuantityNames(self, context):
def update_cost_item_index(self, context):
bpy.ops.bim.load_cost_item_quantities()
if Data.cost_schedules[self.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
bpy.ops.bim.load_cost_item_types()
else:
bpy.ops.bim.load_cost_item_quantities()
def updateCostItemIdentification(self, context):
+27 -12
View File
@@ -21,6 +21,7 @@ from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes
from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.unit.data import Data as UnitData
class BIM_PT_cost_schedules(Panel):
@@ -42,6 +43,9 @@ class BIM_PT_cost_schedules(Panel):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not UnitData.is_loaded:
UnitData.load(IfcStore.get_file())
row = self.layout.row()
row.operator("bim.add_cost_schedule", icon="ADD")
@@ -193,19 +197,20 @@ class BIM_PT_cost_schedules(Panel):
self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_item_value_id])
def draw_readonly_cost_value_ui(self, layout, cost_value_id):
# This UI is really poor. Delete and start again.
cost_value = Data.cost_values[cost_value_id]
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
if cost_value["Category"]:
cost_value_label += " ({})".format(cost_value["Category"])
layout.label(text="", icon="DISC")
self.draw_cost_value_operator_ui(layout, cost_value_id)
self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id)
layout.label(text=cost_value_label)
for component_id in cost_value["Components"] or []:
self.draw_readonly_component_cost_value_ui(layout, component_id)
self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"])
def draw_readonly_component_cost_value_ui(self, layout, cost_value_id, level=1):
self.draw_cost_value_operator_ui(layout, cost_value_id)
def draw_readonly_component_cost_value_ui(self, layout, cost_value_id, parent_id, level=1):
self.draw_cost_value_operator_ui(layout, cost_value_id, parent_id)
cost_value = Data.cost_values[cost_value_id]
cost_value_label = ">" * level
cost_value_label += "{0:.2f}".format(cost_value["AppliedValue"])
@@ -214,9 +219,9 @@ class BIM_PT_cost_schedules(Panel):
layout.label(text=cost_value_label)
for component_id in cost_value["Components"] or []:
self.draw_readonly_component_cost_value_ui(layout, component_id, level + 1)
self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"], level + 1)
def draw_cost_value_operator_ui(self, layout, cost_value_id):
def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id):
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK")
op.cost_value = cost_value_id
@@ -233,6 +238,7 @@ class BIM_PT_cost_schedules(Panel):
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
op.parent = parent_id
op.cost_value = cost_value_id
else:
op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
@@ -243,6 +249,7 @@ class BIM_PT_cost_schedules(Panel):
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
op.parent = parent_id
op.cost_value = cost_value_id
def draw_editable_cost_value_ui(self, layout, cost_value):
@@ -467,7 +474,9 @@ class BIM_PT_cost_item_rates(Panel):
cost_item = self.props.cost_items[self.props.active_cost_item_index]
row = self.layout.row(align=True)
row.prop(self.props, "schedule_of_rates", text="")
row.operator("bim.assign_cost_value", text="", icon="COPYDOWN")
op = row.operator("bim.assign_cost_value", text="", icon="COPYDOWN")
op.cost_item = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
op.cost_rate = self.props.cost_item_rates[self.props.active_cost_item_rate_index].ifc_definition_id
self.layout.template_list(
"BIM_UL_cost_item_rates",
"",
@@ -493,10 +502,7 @@ class BIM_UL_cost_items_trait():
split2.alignment = "RIGHT"
split2.prop(item, "name", emboss=False, text="")
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
self.draw_uom_column(split2, cost_item)
else:
self.draw_quantity_column(split2, cost_item)
self.draw_quantity_column(split2, cost_item)
split2.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"]))
@@ -518,6 +524,12 @@ class BIM_UL_cost_items_trait():
else:
row.label(text="", icon="DOT")
def draw_quantity_column(self, layout, cost_item):
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
self.draw_uom_column(layout, cost_item)
else:
self.draw_total_quantity_column(layout, cost_item)
def draw_uom_column(self, layout, cost_item):
text = "-"
if cost_item["CostValues"]:
@@ -526,7 +538,7 @@ class BIM_UL_cost_items_trait():
text = "{0:.2f}".format(unit_basis["ValueComponent"]) + f" ({unit_basis['UnitSymbol'] or '?'})"
layout.label(text=text)
def draw_quantity_column(self, layout, cost_item):
def draw_total_quantity_column(self, layout, cost_item):
layout.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol'] or '?'})")
@@ -544,6 +556,9 @@ class BIM_UL_cost_item_rates(BIM_UL_cost_items_trait, UIList):
self.contract_operator = "bim.contract_cost_item_rate"
self.expand_operator = "bim.expand_cost_item_rate"
def draw_quantity_column(self, layout, cost_item):
self.draw_uom_column(layout, cost_item)
class BIM_UL_cost_columns(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
@@ -0,0 +1,17 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"cost_item": None, "cost_rate": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
for cost_value in self.settings["cost_item"].CostValues:
ifcopenshell.api.run(
"cost.remove_cost_item_value", self.file, parent=self.settings["cost_item"], cost_value=cost_value
)
# This is an assumption, and not part of the official IFC documentation
self.settings["cost_item"].CostValues = self.settings["cost_rate"].CostValues
@@ -1,9 +1,19 @@
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"cost_value": None}
self.settings = {"parent": None, "cost_value": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.file.remove(self.settings["cost_value"])
if len(self.file.get_inverse(self.settings["cost_value"])) == 1:
self.file.remove(self.settings["cost_value"])
# TODO deep purge
elif self.settings["parent"].is_a("IfcCostItem"):
values = list(self.settings["parent"].CostValues)
values.remove(self.settings["cost_value"])
self.settings["parent"].CostValues = values if values else None
elif self.settings["parent"].is_a("IfcCostValue"):
components = list(self.settings["parent"].Components)
components.remove(self.settings["cost_value"])
self.settings["parent"].Components = components if components else None