diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index 7348e8178c..766c9941bc 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -103,6 +103,7 @@ class EnableEditingCostItems(bpy.types.Operator): if context.preferences.addons["blenderbim"].preferences.should_play_chaching_sound: self.play_chaching_sound() # lol self.props = context.scene.BIMCostProperties + self.props.is_cost_update_enabled = False self.props.active_cost_schedule_id = self.cost_schedule while len(self.props.cost_items) > 0: self.props.cost_items.remove(0) @@ -111,6 +112,7 @@ class EnableEditingCostItems(bpy.types.Operator): for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]: self.create_new_cost_item_li(related_object_id, 0) self.props.is_editing = "COST_ITEMS" + self.props.is_cost_update_enabled = True return {"FINISHED"} def play_chaching_sound(self): @@ -134,6 +136,7 @@ class EnableEditingCostItems(bpy.types.Operator): new = self.props.cost_items.add() new.ifc_definition_id = related_object_id new.name = cost_item["Name"] or "Unnamed" + new.identification = cost_item["Identification"] or "XXX" new.is_expanded = related_object_id not in self.contracted_cost_items new.level_index = level_index if cost_item["IsNestedBy"]: diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index d6fd443ec0..d57d373960 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -128,11 +128,27 @@ def update_cost_item_index(self, context): bpy.ops.bim.load_cost_item_quantities() -def updateCostItemName(self, context): - if self.name == "Unnamed": +def updateCostItemIdentification(self, context): + props = context.scene.BIMCostProperties + if not props.is_cost_update_enabled or self.identification == "XXX": return self.file = IfcStore.get_file() + ifcopenshell.api.run( + "cost.edit_cost_item", + self.file, + **{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}}, + ) + Data.load(self.file) + if props.active_cost_item_id == self.ifc_definition_id: + attribute = props.cost_item_attributes.get("Identification") + attribute.string_value = self.identification + + +def updateCostItemName(self, context): props = context.scene.BIMCostProperties + if not props.is_cost_update_enabled or self.name == "Unnamed": + return + self.file = IfcStore.get_file() ifcopenshell.api.run( "cost.edit_cost_item", self.file, @@ -146,6 +162,7 @@ def updateCostItemName(self, context): class CostItem(PropertyGroup): name: StringProperty(name="Name", update=updateCostItemName) + identification: StringProperty(name="Identification", update=updateCostItemIdentification) ifc_definition_id: IntProperty(name="IFC Definition ID") has_children: BoolProperty(name="Has Children") is_expanded: BoolProperty(name="Is Expanded") @@ -159,6 +176,7 @@ class CostItemQuantity(PropertyGroup): class BIMCostProperties(PropertyGroup): + is_cost_update_enabled: BoolProperty(name="Is Cost Update Enabled", default=True) cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute) is_editing: StringProperty(name="Is Editing") active_cost_schedule_id: IntProperty(name="Active Cost Schedule Id") diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index fe569e466e..952dabd44d 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -76,6 +76,32 @@ class BIM_PT_cost_schedules(Panel): row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") def draw_editable_cost_item_ui(self, cost_schedule_id): + row = self.layout.row(align=True) + row.alignment = "RIGHT" + ifc_definition_id = None + if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items): + ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id + if ifc_definition_id: + + if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES": + op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES") + op.cost_item = ifc_definition_id + + op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC") + op.cost_item = ifc_definition_id + + row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = ifc_definition_id + + if self.props.active_cost_item_id == ifc_definition_id: + if self.props.cost_item_editing_type == "ATTRIBUTES": + row.operator("bim.edit_cost_item", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL") + else: + op = row.operator("bim.enable_editing_cost_item", text="", icon="GREASEPENCIL") + op.cost_item = ifc_definition_id + + row.operator("bim.remove_cost_item", text="", icon="X").cost_item = ifc_definition_id + self.layout.template_list( "BIM_UL_cost_items", "", @@ -348,7 +374,7 @@ class BIM_PT_cost_item_quantities(Panel): class BIM_UL_cost_items(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: - props = context.scene.BIMCostProperties + self.props = context.scene.BIMCostProperties cost_item = Data.cost_items[item.ifc_definition_id] row = layout.row(align=True) @@ -366,40 +392,30 @@ class BIM_UL_cost_items(UIList): else: row.label(text="", icon="DOT") - split1 = row.split(factor=0.7) - split1.prop(item, "name", emboss=False, text="") + split1 = row.split(factor=0.1) + split1.prop(item, "identification", emboss=False, text="") + split2 = split1.split(factor=0.5) + split2.alignment = "RIGHT" + split2.prop(item, "name", emboss=False, text="") - if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES": - op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES") - op.cost_item = item.ifc_definition_id - row.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol']})") + if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES": + split2.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol'] or '?'})") - op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC") - op.cost_item = item.ifc_definition_id - row.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"])) + split2.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"])) - for column in props.columns: - row.label(text=str(cost_item["CategoryValues"].get(column.name, "-"))) + for column in self.props.columns: + split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-"))) - row.label(text="{0:.2f}".format(cost_item["TotalCostValue"]), icon="CON_TRANSLIKE") + split2.label(text="{0:.2f}".format(cost_item["TotalCostValue"])) + self.draw_buttons(split2, item, cost_item) - if props.active_cost_item_id == item.ifc_definition_id: - if props.cost_item_editing_type == "ATTRIBUTES": - row.operator("bim.edit_cost_item", text="", icon="CHECKMARK") - row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL") - elif props.active_cost_item_id: - if props.cost_item_editing_type == "VALUES": - op = row.operator("bim.copy_cost_item_values", text="", icon="COPYDOWN") - op.source = props.active_cost_item_id - op.destination = item.ifc_definition_id - row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id - row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id - else: - row.operator( - "bim.enable_editing_cost_item", text="", icon="GREASEPENCIL" - ).cost_item = item.ifc_definition_id - row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id - row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id + def draw_buttons(self, row, item, cost_item): + pass # TODO: reimplement somewhere with better UX + # elif self.props.active_cost_item_id: + # if self.props.cost_item_editing_type == "VALUES": + # op = row.operator("bim.copy_cost_item_values", text="", icon="COPYDOWN") + # op.source = self.props.active_cost_item_id + # op.destination = item.ifc_definition_id class BIM_UL_cost_columns(UIList): diff --git a/src/ifc5d/ifc5d/csv2ifc.py b/src/ifc5d/ifc5d/csv2ifc.py index 0ecb5f5d54..656f7716dc 100644 --- a/src/ifc5d/ifc5d/csv2ifc.py +++ b/src/ifc5d/ifc5d/csv2ifc.py @@ -60,18 +60,20 @@ class Csv2Ifc: def get_row_cost_data(self, row): name = row[self.headers["Name"]] + identification = row[self.headers["Identification"]] if "Identification" in self.headers else None cost_quantities = row[self.headers["Quantity"]] cost_quantities_unit = row[self.headers["Unit"]] if self.has_categories: cost_values = { k: float(row[v]) for k, v in self.headers.items() - if k not in ["Hierarchy", "Name", "Quantity", "Unit", "Subtotal"] and row[v] + if k not in ["Hierarchy", "Identification", "Name", "Quantity", "Unit", "Subtotal"] and row[v] } else: cost_values = row[self.headers["Value"]] cost_values = float(cost_values) if cost_values else None return { + "Identification": str(identification) if identification else None, "Name": str(name) if name else None, "CostQuantities": float(cost_quantities) if cost_quantities else None, "CostQuantitiesUnit": str(cost_quantities_unit) if cost_quantities_unit else None, @@ -99,6 +101,7 @@ class Csv2Ifc: cost_item["ifc"] = ifcopenshell.api.run("cost.add_cost_item", self.file, cost_item=parent) cost_item["ifc"].Name = cost_item["Name"] + cost_item["ifc"].Identification = cost_item["Identification"] if not cost_item["CostValues"]: cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"]) diff --git a/src/ifc5d/test.csv b/src/ifc5d/test.csv index 203122abae..c57458bee9 100644 --- a/src/ifc5d/test.csv +++ b/src/ifc5d/test.csv @@ -1,14 +1,14 @@ -"Hierarchy","Name","Quantity","Unit","Foo","Bar","Baz","Subtotal" -1,"Demolition",,,,,,301 -,,,,,,, -2,"Building A",,,,,,96 -,,,,,,, -3,"Soft strip",1,"m",5,4,3,12 -3,"Hard Strip",2,"m2",6,5,4,30 -3,"Hazmat",3,"m3",7,6,5,54 -,,,,,,, -2,"Building B",,,,,,205 -,,,,,,, -3,"Soft strip",4,"hr",8,7,,60 -3,"Hard Strip",5,,9,,8,85 -3,"Hazmat",6,"kg",10,,,60 +"Hierarchy","Identification","Name","Quantity","Unit","Foo","Bar","Baz","Subtotal" +1,1,"Demolition",,,,,,301 +,,,,,,,, +2,1.1,"Building A",,,,,,96 +,,,,,,,, +3,"1.1.1","Soft strip",1,"m",5,4,3,12 +3,"1.1.2","Hard Strip",2,"m2",6,5,4,30 +3,"1.1.3","Hazmat",3,"m3",7,6,5,54 +,,,,,,,, +2,1.2,"Building B",,,,,,205 +,,,,,,,, +3,"1.2.1","Soft strip",4,"hr",8,7,,60 +3,"1.2.2","Hard Strip",5,,9,,8,85 +3,"1.2.3","Hazmat",6,"kg",10,,,60