mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 10:57:49 +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
|
||||
# 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):
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user