diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index 0d0ae85c55..f960b76e1d 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -379,14 +379,30 @@ class AddCostItemQuantity(bpy.types.Operator): def execute(self, context): self.file = IfcStore.get_file() + self.props = context.scene.BIMCostProperties + if self.props.quantity_types == "QTO": + self.add_quantities_from_qto_filter() + else: + self.add_manual_quantity() + Data.load(self.file) + return {"FINISHED"} + + def add_quantities_from_qto_filter(self): + ifcopenshell.api.run( + "cost.assign_cost_item_product_quantities", + self.file, + cost_item=self.file.by_id(self.cost_item), + qto_name=self.props.qto_name, + prop_name=self.props.prop_name + ) + + def add_manual_quantity(self): ifcopenshell.api.run( "cost.add_cost_item_quantity", self.file, cost_item=self.file.by_id(self.cost_item), ifc_class=self.ifc_class, ) - Data.load(self.file) - return {"FINISHED"} class RemoveCostItemQuantity(bpy.types.Operator): diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index a604fd9f1a..3ee77dd403 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -28,9 +28,13 @@ def getQuantityTypes(self, context): global quantitytypes_enum if len(quantitytypes_enum) == 0 and IfcStore.get_schema(): quantitytypes_enum.clear() - quantitytypes_enum = [ - (t.name(), t.name(), "") for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes() - ] + quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")] + quantitytypes_enum.extend( + [ + (t.name(), t.name(), "") + for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes() + ] + ) return quantitytypes_enum @@ -69,13 +73,18 @@ class BIMCostProperties(PropertyGroup): cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute) contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]") quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types") + qto_name: StringProperty(name="Qto Name") + prop_name: StringProperty(name="Prop Name") active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id") quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute) - cost_types: EnumProperty(items=[ - ("FIXED", "Fixed", "The cost value is a fixed number"), - ("SUM", "Sum", "The cost value is automatically derived from the sum of all nested cost items"), - ("CATEGORY", "Category", "The cost value represents a single category"), - ], name="Cost Types") + cost_types: EnumProperty( + items=[ + ("FIXED", "Fixed", "The cost value is a fixed number"), + ("SUM", "Sum", "The cost value is automatically derived from the sum of all nested cost items"), + ("CATEGORY", "Category", "The cost value represents a single category"), + ], + name="Cost Types", + ) cost_category: StringProperty(name="Cost Category") active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id") cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute) diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index 88b16a0bf4..588ddac7c4 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -96,6 +96,9 @@ class BIM_PT_cost_schedules(Panel): def draw_editable_cost_item_quantities_ui(self): row = self.layout.row(align=True) row.prop(self.props, "quantity_types", text="") + if self.props.quantity_types == "QTO": + row.prop(self.props, "qto_name", text="") + row.prop(self.props, "prop_name", text="") op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD") op.cost_item = self.props.active_cost_item_id op.ifc_class = self.props.quantity_types diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_product_quantities.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_product_quantities.py new file mode 100644 index 0000000000..c20a2fa861 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_product_quantities.py @@ -0,0 +1,32 @@ +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"cost_item": None, "qto_name": "", "prop_name": ""} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.quantities = set(self.settings["cost_item"].CostQuantities or []) + for control in self.settings["cost_item"].Controls or []: + for related_object in control.RelatedObjects: + self.add_quantity_from_related_object(related_object) + self.settings["cost_item"].CostQuantities = list(self.quantities) + + def add_quantity_from_related_object(self, element): + if element.is_a("IfcTypeObject"): + for definition in element.HasPropertySets or []: + self.add_quantity_from_qto(definition) + else: + for relationship in element.IsDefinedBy: + if relationship.is_a("IfcRelDefinesByProperties"): + self.add_quantity_from_qto(relationship.RelatingPropertyDefinition) + + def add_quantity_from_qto(self, qto): + if not qto.is_a("IfcElementQuantity") or qto.Name.lower() != self.settings["qto_name"].lower(): + return + for prop in qto.Quantities: + if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower(): + self.quantities.add(prop)