diff --git a/src/blenderbim/blenderbim/bim/module/cost/__init__.py b/src/blenderbim/blenderbim/bim/module/cost/__init__.py index b2188023b7..fce0105e0a 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/cost/__init__.py @@ -65,6 +65,7 @@ classes = ( operator.LoadScheduleOfRates, operator.ExpandCostItemRate, operator.ContractCostItemRate, + operator.CalculateCostItemResourceValue, prop.CostItem, prop.CostItemQuantity, prop.CostItemType, diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index c9807fb922..4d08f98010 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -1054,3 +1054,21 @@ class ContractCostItemRate(bpy.types.Operator): props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates) bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(props.schedule_of_rates)) return {"FINISHED"} + + +class CalculateCostItemResourceValue(bpy.types.Operator): + bl_idname = "bim.calculate_cost_item_resource_value" + bl_label = "Calculate Cost Item Resource Value" + bl_options = {"REGISTER", "UNDO"} + cost_item: 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.calculate_cost_item_resource_value", self.file, cost_item=self.file.by_id(self.cost_item) + ) + Data.load(self.file) + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index d2062d98ad..c6512fdbbc 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -432,6 +432,9 @@ class BIM_PT_cost_item_quantities(Panel): row2 = col.row(align=True) row2.label(text="Resources") + op = row2.operator("bim.calculate_cost_item_resource_value", text="", icon="DISC") + op.cost_item = cost_item.ifc_definition_id + rtprops = context.scene.BIMResourceTreeProperties rprops = context.scene.BIMResourceProperties if rtprops.resources and rprops.active_resource_index < len(rtprops.resources): diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py new file mode 100644 index 0000000000..98423308f7 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py @@ -0,0 +1,56 @@ +import ifcopenshell.api +import ifcopenshell.util.date + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"cost_item": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for cost_value in self.settings["cost_item"].CostValues or []: + ifcopenshell.api.run( + "cost.remove_cost_value", self.file, parent=self.settings["cost_item"], cost_value=cost_value + ) + + resources = [] + for rel in self.settings["cost_item"].Controls or []: + for related_object in rel.RelatedObjects: + if related_object.is_a("IfcConstructionResource"): + resources.append(related_object) + elif related_object.is_a("IfcTask"): + for rel2 in related_object.OperatesOn or []: + for related_object2 in rel2.RelatedObjects: + if related_object2.is_a("IfcConstructionResource"): + resources.append(related_object2) + + total_cost = 0 + for resource in resources: + cost = self.get_cost(resource) + quantity = self.get_quantity(resource) + if not cost or not quantity: + continue + total_cost += cost * quantity + + if total_cost: + cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.settings["cost_item"]) + cost_value.AppliedValue = self.file.createIfcMonetaryMeasure(total_cost) + + def get_cost(self, resource): + total = 0 + for cost_value in resource.BaseCosts or []: + total += cost_value.AppliedValue.wrappedValue if cost_value.AppliedValue else 0 + return total + + def get_quantity(self, resource): + total = 0 + if resource.BaseQuantity: + return resource.BaseQuantity[3] + if resource.Usage and resource.Usage.ScheduleWork: + # For now we assume either hourly or daily depending on how duration is stored + duration = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork) + if duration.days: + return duration.days + return duration.seconds / 60 / 60