diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index ba5e353e03..7d798cb8cd 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -519,6 +519,14 @@ class LoadCostItemQuantities(bpy.types.Operator): core.load_cost_item_quantities(tool.Cost) return {"FINISHED"} +class LoadCostItemElementQuantities(bpy.types.Operator): + bl_idname = "bim.load_cost_item_element_quantities" + bl_label = "Load Cost Item Element Quantities" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + core.load_cost_item_element_quantities(tool.Cost) + return {"FINISHED"} class LoadCostItemTypes(bpy.types.Operator): bl_idname = "bim.load_cost_item_types" diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index a5bdf4b4da..3d1aea3748 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -134,6 +134,9 @@ class CostItemType(PropertyGroup): name: StringProperty(name="Name") ifc_definition_id: IntProperty(name="IFC Definition ID") +def update_active_cost_item_elements(self, context): + bpy.ops.bim.load_cost_item_element_quantities() + class BIMCostProperties(PropertyGroup): cost_schedule_predefined_types: EnumProperty( @@ -186,3 +189,6 @@ class BIMCostProperties(PropertyGroup): cost_item_rates: CollectionProperty(name="Cost Item Rates", type=CostItem) active_cost_item_rate_index: IntProperty(name="Active Cost Rate Index") contracted_cost_item_rates: StringProperty(name="Contracted Cost Item Rates", default="[]") + show_nested_elements: BoolProperty( + name="Show Nested Tasks", default=False, update=update_active_cost_item_elements + ) diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index b5c9cf4cf1..f475da927f 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -361,6 +361,8 @@ class BIM_PT_cost_item_quantities(Panel): op.cost_item = cost_item.ifc_definition_id op.related_object = 0 + row2 = col.row() + row2.prop(self.props, "show_nested_elements", text="Show nested") row2 = col.row() row2.template_list( "BIM_UL_cost_item_quantities", diff --git a/src/blenderbim/blenderbim/core/cost.py b/src/blenderbim/blenderbim/core/cost.py index 76326f4ed7..14674f782f 100644 --- a/src/blenderbim/blenderbim/core/cost.py +++ b/src/blenderbim/blenderbim/core/cost.py @@ -114,6 +114,11 @@ def load_cost_item_quantities(cost): cost_item = cost.get_highlighted_cost_item() cost.load_cost_item_quantities(cost_item) +def load_cost_item_element_quantities(cost): + cost_item = cost.get_highlighted_cost_item() + cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PRODUCT") + + def assign_cost_value(ifc, cost_item, cost_rate): ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate) diff --git a/src/blenderbim/blenderbim/tool/cost.py b/src/blenderbim/blenderbim/tool/cost.py index 56e0e4204e..2a8c6138d0 100644 --- a/src/blenderbim/blenderbim/tool/cost.py +++ b/src/blenderbim/blenderbim/tool/cost.py @@ -181,36 +181,45 @@ class Cost(blenderbim.core.tool.Cost): new.name = related_object.Name or "Unnamed" @classmethod - def load_cost_item_quantities(cls, cost_item=None): - if not cost_item: - cost_item = cls.get_active_cost_item() - if not cost_item: - return - props = bpy.context.scene.BIMCostProperties - props.cost_item_products.clear() - props.cost_item_processes.clear() - props.cost_item_resources.clear() + def load_cost_item_quantity_assignments(cls, cost_item, related_object_type): + def create_list_items(collection, cost_item, is_deep): + products = cls.get_cost_item_assignments(cost_item, filter_by_type=related_object_type, is_deep=False) + for product in products: + new = collection.add() + new.ifc_definition_id = product.id() + new.name = product.Name or "Unnamed" + total_quantity = cls.calculate_parametric_quantity(cost_item, product) + new.total_quantity = total_quantity if total_quantity else 1 + if is_deep: + for cost_item in ifcopenshell.util.cost.get_nested_cost_items(cost_item, is_deep): + create_list_items(collection, cost_item, is_deep=False) - # for control_id, quantity_ids in cost_item.Controls.items() or {}: - for rel in cost_item.Controls or []: - # control_id, quantity_ids - for related_object in rel.RelatedObjects: - if related_object.is_a("IfcProduct"): - new = props.cost_item_products.add() - elif related_object.is_a("IfcProcess"): - new = props.cost_item_processes.add() - elif related_object.is_a("IfcResource"): - new = props.cost_item_resources.add() - new.ifc_definition_id = related_object.id() - new.name = related_object.Name or "Unnamed" - total_quantity = 0 - qtos = ifcopenshell.util.element.get_psets(related_object, qtos_only=True) - for qset_name, quantities in qtos.items(): - qto = tool.Ifc.get().by_id(quantities["id"]) - for quantity in qto.Quantities: - if quantity in cost_item.CostQuantities: - total_quantity += quantity[3] - new.total_quantity = total_quantity + props = bpy.context.scene.BIMCostProperties + if related_object_type == "PRODUCT": + props.cost_item_products.clear() + is_deep = bpy.context.scene.BIMCostProperties.show_nested_elements + create_list_items(props.cost_item_products, cost_item, is_deep) + elif related_object_type == "PROCESS": + props.cost_item_processes.clear() + is_deep = bpy.context.scene.BIMCostProperties.show_nested_tasks + create_list_items(props.cost_item_processes, cost_item, is_deep) + elif related_object_type == "RESOURCE": + props.cost_item_resources.clear() + is_deep = bpy.context.scene.BIMCostProperties.show_nested_resources + create_list_items(props.cost_item_resources, cost_item, is_deep) + + @classmethod + def calculate_parametric_quantity(cls, cost_item=None, product=None): + if not cost_item.CostQuantities: + return + total_quantity = sum( + quantity[3] + for quantities in ifcopenshell.util.element.get_psets(product, qtos_only=True).values() + for qto in (tool.Ifc.get().by_id(quantities["id"]).Quantities or []) + for quantity in cost_item.CostQuantities + if quantity == qto + ) + return total_quantity @classmethod def get_products(cls, related_object_type=None): @@ -379,8 +388,10 @@ class Cost(blenderbim.core.tool.Cost): ) @classmethod - def get_direct_cost_item_products(cls, cost_item): - return [related_object for r in cost_item.Controls or [] for related_object in r.RelatedObjects] + def get_cost_item_assignments(cls, cost_item, filter_by_type=None, is_deep=False): + return ifcopenshell.util.cost.get_cost_item_assignments( + cost_item, filter_by_type=filter_by_type, is_deep=is_deep + ) @classmethod def get_cost_item_products(cls, cost_item, include_nested=False):