mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-03 13:28:19 +00:00
You can now assign rates from a schedule of rates to a cost plan
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
# 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_idname = "bim.remove_cost_item_value"
|
||||||
bl_label = "Add Cost Item Value"
|
bl_label = "Add Cost Item Value"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
parent: bpy.props.IntProperty()
|
||||||
cost_value: bpy.props.IntProperty()
|
cost_value: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@@ -617,7 +617,12 @@ class RemoveCostItemValue(bpy.types.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
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)
|
Data.load(self.file)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -660,6 +665,7 @@ class EnableEditingCostItemValue(bpy.types.Operator):
|
|||||||
prop.float_value = data["UnitBasis"]["ValueComponent"] or 0
|
prop.float_value = data["UnitBasis"]["ValueComponent"] or 0
|
||||||
else:
|
else:
|
||||||
prop.float_value = 0
|
prop.float_value = 0
|
||||||
|
|
||||||
prop = self.props.cost_value_attributes.add()
|
prop = self.props.cost_value_attributes.add()
|
||||||
prop.name = "UnitBasisUnit"
|
prop.name = "UnitBasisUnit"
|
||||||
prop.data_type = "enum"
|
prop.data_type = "enum"
|
||||||
@@ -897,9 +903,9 @@ class LoadCostItemTypes(bpy.types.Operator):
|
|||||||
while len(self.props.cost_item_type_products) > 0:
|
while len(self.props.cost_item_type_products) > 0:
|
||||||
self.props.cost_item_type_products.remove(0)
|
self.props.cost_item_type_products.remove(0)
|
||||||
# TODO implement process and resource types
|
# 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)
|
# 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)
|
# self.props.cost_item_resources.remove(0)
|
||||||
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
|
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():
|
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"):
|
if related_object.is_a("IfcTypeProduct"):
|
||||||
new = self.props.cost_item_type_products.add()
|
new = self.props.cost_item_type_products.add()
|
||||||
# TODO implement process and resource types
|
# 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()
|
# 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 = self.props.cost_item_resources.add()
|
||||||
new.ifc_definition_id = control_id
|
new.ifc_definition_id = control_id
|
||||||
new.name = related_object.Name or "Unnamed"
|
new.name = related_object.Name or "Unnamed"
|
||||||
@@ -920,11 +926,21 @@ class AssignCostValue(bpy.types.Operator):
|
|||||||
bl_idname = "bim.assign_cost_value"
|
bl_idname = "bim.assign_cost_value"
|
||||||
bl_label = "Assign Cost Value"
|
bl_label = "Assign Cost Value"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
cost_item: bpy.props.IntProperty()
|
||||||
|
cost_rate: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,10 @@ def getResourceQuantityNames(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def update_cost_item_index(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):
|
def updateCostItemIdentification(self, context):
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from bpy.types import Panel, UIList
|
|||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.helper import draw_attributes
|
from blenderbim.bim.helper import draw_attributes
|
||||||
from ifcopenshell.api.cost.data import Data
|
from ifcopenshell.api.cost.data import Data
|
||||||
|
from ifcopenshell.api.unit.data import Data as UnitData
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_cost_schedules(Panel):
|
class BIM_PT_cost_schedules(Panel):
|
||||||
@@ -42,6 +43,9 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
if not Data.is_loaded:
|
if not Data.is_loaded:
|
||||||
Data.load(IfcStore.get_file())
|
Data.load(IfcStore.get_file())
|
||||||
|
|
||||||
|
if not UnitData.is_loaded:
|
||||||
|
UnitData.load(IfcStore.get_file())
|
||||||
|
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.operator("bim.add_cost_schedule", icon="ADD")
|
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])
|
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):
|
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 = Data.cost_values[cost_value_id]
|
||||||
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
|
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
|
||||||
if cost_value["Category"]:
|
if cost_value["Category"]:
|
||||||
cost_value_label += " ({})".format(cost_value["Category"])
|
cost_value_label += " ({})".format(cost_value["Category"])
|
||||||
layout.label(text="", icon="DISC")
|
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)
|
layout.label(text=cost_value_label)
|
||||||
|
|
||||||
for component_id in cost_value["Components"] or []:
|
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):
|
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)
|
self.draw_cost_value_operator_ui(layout, cost_value_id, parent_id)
|
||||||
cost_value = Data.cost_values[cost_value_id]
|
cost_value = Data.cost_values[cost_value_id]
|
||||||
cost_value_label = ">" * level
|
cost_value_label = ">" * level
|
||||||
cost_value_label += "{0:.2f}".format(cost_value["AppliedValue"])
|
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)
|
layout.label(text=cost_value_label)
|
||||||
|
|
||||||
for component_id in cost_value["Components"] or []:
|
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:
|
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 = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK")
|
||||||
op.cost_value = cost_value_id
|
op.cost_value = cost_value_id
|
||||||
@@ -233,6 +238,7 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
if self.props.cost_types == "CATEGORY":
|
if self.props.cost_types == "CATEGORY":
|
||||||
op.cost_category = self.props.cost_category
|
op.cost_category = self.props.cost_category
|
||||||
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
|
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
|
||||||
|
op.parent = parent_id
|
||||||
op.cost_value = cost_value_id
|
op.cost_value = cost_value_id
|
||||||
else:
|
else:
|
||||||
op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
|
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":
|
if self.props.cost_types == "CATEGORY":
|
||||||
op.cost_category = self.props.cost_category
|
op.cost_category = self.props.cost_category
|
||||||
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
|
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
|
||||||
|
op.parent = parent_id
|
||||||
op.cost_value = cost_value_id
|
op.cost_value = cost_value_id
|
||||||
|
|
||||||
def draw_editable_cost_value_ui(self, layout, cost_value):
|
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]
|
cost_item = self.props.cost_items[self.props.active_cost_item_index]
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(self.props, "schedule_of_rates", text="")
|
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(
|
self.layout.template_list(
|
||||||
"BIM_UL_cost_item_rates",
|
"BIM_UL_cost_item_rates",
|
||||||
"",
|
"",
|
||||||
@@ -493,10 +502,7 @@ class BIM_UL_cost_items_trait():
|
|||||||
split2.alignment = "RIGHT"
|
split2.alignment = "RIGHT"
|
||||||
split2.prop(item, "name", emboss=False, text="")
|
split2.prop(item, "name", emboss=False, text="")
|
||||||
|
|
||||||
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
|
self.draw_quantity_column(split2, cost_item)
|
||||||
self.draw_uom_column(split2, cost_item)
|
|
||||||
else:
|
|
||||||
self.draw_quantity_column(split2, cost_item)
|
|
||||||
|
|
||||||
split2.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"]))
|
split2.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"]))
|
||||||
|
|
||||||
@@ -518,6 +524,12 @@ class BIM_UL_cost_items_trait():
|
|||||||
else:
|
else:
|
||||||
row.label(text="", icon="DOT")
|
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):
|
def draw_uom_column(self, layout, cost_item):
|
||||||
text = "-"
|
text = "-"
|
||||||
if cost_item["CostValues"]:
|
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 '?'})"
|
text = "{0:.2f}".format(unit_basis["ValueComponent"]) + f" ({unit_basis['UnitSymbol'] or '?'})"
|
||||||
layout.label(text=text)
|
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 '?'})")
|
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.contract_operator = "bim.contract_cost_item_rate"
|
||||||
self.expand_operator = "bim.expand_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):
|
class BIM_UL_cost_columns(UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
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:
|
class Usecase:
|
||||||
def __init__(self, file, **settings):
|
def __init__(self, file, **settings):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {"cost_value": None}
|
self.settings = {"parent": None, "cost_value": None}
|
||||||
for key, value in settings.items():
|
for key, value in settings.items():
|
||||||
self.settings[key] = value
|
self.settings[key] = value
|
||||||
|
|
||||||
def execute(self):
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user