mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-23 14:26:30 +00:00
BBIM 5D: Various Cost Schedule UI Improvemements
This commit is contained in:
@@ -71,7 +71,7 @@ class CostSchedulesData:
|
|||||||
{
|
{
|
||||||
"id": schedule.id(),
|
"id": schedule.id(),
|
||||||
"name": schedule.Name or "Unnamed",
|
"name": schedule.Name or "Unnamed",
|
||||||
"predefined_type":ifcopenshell.util.element.get_predefined_type(schedule),
|
"predefined_type": ifcopenshell.util.element.get_predefined_type(schedule),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return results
|
return results
|
||||||
@@ -109,6 +109,7 @@ class CostSchedulesData:
|
|||||||
data["UnitBasisUnitSymbol"] = None
|
data["UnitBasisUnitSymbol"] = None
|
||||||
data["TotalAppliedValue"] = 0.0
|
data["TotalAppliedValue"] = 0.0
|
||||||
data["TotalCost"] = 0.0
|
data["TotalCost"] = 0.0
|
||||||
|
has_unit_basis = False
|
||||||
if root_element.is_a("IfcCostItem"):
|
if root_element.is_a("IfcCostItem"):
|
||||||
values = root_element.CostValues
|
values = root_element.CostValues
|
||||||
elif root_element.is_a("IfcConstructionResource"):
|
elif root_element.is_a("IfcConstructionResource"):
|
||||||
@@ -121,10 +122,15 @@ class CostSchedulesData:
|
|||||||
cost_value_data = cls._cost_values[cost_value.id()]
|
cost_value_data = cls._cost_values[cost_value.id()]
|
||||||
data["UnitBasisValueComponent"] = cost_value_data["UnitBasis"]["ValueComponent"]
|
data["UnitBasisValueComponent"] = cost_value_data["UnitBasis"]["ValueComponent"]
|
||||||
data["UnitBasisUnitSymbol"] = cost_value_data["UnitBasis"]["UnitSymbol"]
|
data["UnitBasisUnitSymbol"] = cost_value_data["UnitBasis"]["UnitSymbol"]
|
||||||
if data["UnitBasisValueComponent"]:
|
has_unit_basis = True
|
||||||
data["TotalCost"] = data["TotalCostQuantity"] / data["UnitBasisValueComponent"] * data["TotalAppliedValue"]
|
if has_unit_basis:
|
||||||
|
data["TotalCost"] = data["TotalAppliedValue"] / data["UnitBasisValueComponent"]
|
||||||
else:
|
else:
|
||||||
data["TotalCost"] = data["TotalCostQuantity"] * data["TotalAppliedValue"]
|
if data["TotalCostQuantity"] is not None:
|
||||||
|
data["TotalCost"] = data["TotalAppliedValue"] * data["TotalCostQuantity"]
|
||||||
|
else:
|
||||||
|
data["TotalCost"] = data["TotalAppliedValue"]
|
||||||
|
data["TotalAppliedValue"] = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load_cost_item_quantities(cls, cost_item, data):
|
def _load_cost_item_quantities(cls, cost_item, data):
|
||||||
|
|||||||
@@ -128,6 +128,8 @@ class CostItemQuantity(PropertyGroup):
|
|||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
total_quantity: FloatProperty(name="Total Quantity")
|
total_quantity: FloatProperty(name="Total Quantity")
|
||||||
|
unit_symbol: StringProperty(name="Unit Symbol")
|
||||||
|
total_cost_quantity: FloatProperty(name="Total Quantity")
|
||||||
|
|
||||||
|
|
||||||
class CostItemType(PropertyGroup):
|
class CostItemType(PropertyGroup):
|
||||||
@@ -204,7 +206,7 @@ class BIMCostProperties(PropertyGroup):
|
|||||||
cost_item_rates: CollectionProperty(name="Cost Item Rates", type=CostItem)
|
cost_item_rates: CollectionProperty(name="Cost Item Rates", type=CostItem)
|
||||||
active_cost_item_rate_index: IntProperty(name="Active Cost Rate Index")
|
active_cost_item_rate_index: IntProperty(name="Active Cost Rate Index")
|
||||||
contracted_cost_item_rates: StringProperty(name="Contracted Cost Item Rates", default="[]")
|
contracted_cost_item_rates: StringProperty(name="Contracted Cost Item Rates", default="[]")
|
||||||
product_cost_items: CollectionProperty(name="Product Cost Items", type=CostItem)
|
product_cost_items: CollectionProperty(name="Product Cost Items", type=CostItemQuantity)
|
||||||
active_product_cost_item_index: IntProperty(name="Active Product Cost Item Index")
|
active_product_cost_item_index: IntProperty(name="Active Product Cost Item Index")
|
||||||
enable_reorder: BoolProperty(name="Enable Reorder", default=False)
|
enable_reorder: BoolProperty(name="Enable Reorder", default=False)
|
||||||
show_nested_elements: BoolProperty(name="Show Nested Tasks", default=False, update=update_active_cost_item_elements)
|
show_nested_elements: BoolProperty(name="Show Nested Tasks", default=False, update=update_active_cost_item_elements)
|
||||||
|
|||||||
@@ -59,7 +59,10 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
def draw_cost_schedule_ui(self, cost_schedule):
|
def draw_cost_schedule_ui(self, cost_schedule):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule["id"]:
|
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule["id"]:
|
||||||
row.label(text="Currently editing: {}[{}]".format(cost_schedule["name"], cost_schedule["predefined_type"]), icon="LINENUMBERS_ON")
|
row.label(
|
||||||
|
text="Currently editing: {}[{}]".format(cost_schedule["name"], cost_schedule["predefined_type"]),
|
||||||
|
icon="LINENUMBERS_ON",
|
||||||
|
)
|
||||||
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
grid = self.layout.grid_flow(columns=2, even_columns=True)
|
||||||
col = grid.column()
|
col = grid.column()
|
||||||
row1 = col.row(align=True)
|
row1 = col.row(align=True)
|
||||||
@@ -85,7 +88,9 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
|
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
|
||||||
row.operator("bim.disable_editing_cost_schedule", text="Disable Editing", icon="CANCEL")
|
row.operator("bim.disable_editing_cost_schedule", text="Disable Editing", icon="CANCEL")
|
||||||
else:
|
else:
|
||||||
row.label(text="{}[{}]".format(cost_schedule["name"], cost_schedule["predefined_type"]), icon="LINENUMBERS_ON")
|
row.label(
|
||||||
|
text="{}[{}]".format(cost_schedule["name"], cost_schedule["predefined_type"]), icon="LINENUMBERS_ON"
|
||||||
|
)
|
||||||
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule["id"]
|
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule["id"]
|
||||||
row.operator(
|
row.operator(
|
||||||
"bim.enable_editing_cost_schedule_attributes", text="", icon="GREASEPENCIL"
|
"bim.enable_editing_cost_schedule_attributes", text="", icon="GREASEPENCIL"
|
||||||
@@ -214,6 +219,7 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, self.layout.box())
|
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, self.layout.box())
|
||||||
|
|
||||||
def draw_readonly_cost_value_ui(self, layout, cost_value):
|
def draw_readonly_cost_value_ui(self, layout, cost_value):
|
||||||
|
print(cost_value)
|
||||||
if self.props.active_cost_value_id == cost_value["id"] and self.props.cost_value_editing_type == "FORMULA":
|
if self.props.active_cost_value_id == cost_value["id"] and self.props.cost_value_editing_type == "FORMULA":
|
||||||
layout.prop(self.props, "cost_value_formula", text="")
|
layout.prop(self.props, "cost_value_formula", text="")
|
||||||
else:
|
else:
|
||||||
@@ -599,7 +605,7 @@ class BIM_UL_cost_items_trait:
|
|||||||
row.label(text="", icon="DOT")
|
row.label(text="", icon="DOT")
|
||||||
|
|
||||||
def draw_total_cost_column(self, layout, cost_item):
|
def draw_total_cost_column(self, layout, cost_item):
|
||||||
layout.label(text="{0:.2f}".format(cost_item["TotalCost"]))
|
layout.label(text="{0:,.2f}".format(cost_item["TotalCost"]).replace(",", " "))
|
||||||
|
|
||||||
def draw_quantity_column(self, layout, cost_item):
|
def draw_quantity_column(self, layout, cost_item):
|
||||||
if CostSchedulesData.data["is_editing_rates"]:
|
if CostSchedulesData.data["is_editing_rates"]:
|
||||||
@@ -608,7 +614,11 @@ class BIM_UL_cost_items_trait:
|
|||||||
self.draw_total_quantity_column(layout, cost_item)
|
self.draw_total_quantity_column(layout, cost_item)
|
||||||
|
|
||||||
def draw_value_column(self, layout, cost_item):
|
def draw_value_column(self, layout, cost_item):
|
||||||
text = "{0:.2f}".format(cost_item["TotalAppliedValue"])
|
text = (
|
||||||
|
"{0:,.2f}".format(cost_item["TotalAppliedValue"]).replace(",", " ")
|
||||||
|
if cost_item["TotalAppliedValue"]
|
||||||
|
else "-"
|
||||||
|
)
|
||||||
if cost_item["UnitBasisValueComponent"] not in [None, 1]:
|
if cost_item["UnitBasisValueComponent"] not in [None, 1]:
|
||||||
text += " / {}".format(round(cost_item["UnitBasisValueComponent"], 2))
|
text += " / {}".format(round(cost_item["UnitBasisValueComponent"], 2))
|
||||||
layout.label(text=text)
|
layout.label(text=text)
|
||||||
@@ -628,7 +638,12 @@ class BIM_UL_cost_items_trait:
|
|||||||
op.new_index = cost_item["NestingIndex"] - 1
|
op.new_index = cost_item["NestingIndex"] - 1
|
||||||
|
|
||||||
def draw_total_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 '-'}")
|
label = (
|
||||||
|
"{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '-'}"
|
||||||
|
if cost_item["TotalCostQuantity"]
|
||||||
|
else "-"
|
||||||
|
)
|
||||||
|
layout.label(text=label)
|
||||||
# if cost_item["DerivedTotalCostQuantity"] not in [None, 0]:
|
# if cost_item["DerivedTotalCostQuantity"] not in [None, 0]:
|
||||||
# layout.label(text="{0:.2f}".format(cost_item["DerivedTotalCostQuantity"]) + f" {cost_item['DerivedUnitSymbol'] or '-'}")
|
# layout.label(text="{0:.2f}".format(cost_item["DerivedTotalCostQuantity"]) + f" {cost_item['DerivedUnitSymbol'] or '-'}")
|
||||||
# else:
|
# else:
|
||||||
@@ -685,14 +700,14 @@ class BIM_UL_cost_item_quantities(UIList):
|
|||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
props = context.scene.BIMCostProperties
|
props = context.scene.BIMCostProperties
|
||||||
cost_item = props.cost_items[props.active_cost_item_index]
|
cost_item = props.cost_items[props.active_cost_item_index]
|
||||||
|
|
||||||
if item:
|
if item:
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
op = row.operator("bim.select_product", text="", icon="RESTRICT_SELECT_OFF")
|
op = row.operator("bim.select_product", text="", icon="RESTRICT_SELECT_OFF")
|
||||||
op.product = item.ifc_definition_id
|
op.product = item.ifc_definition_id
|
||||||
row.split(factor=0.8)
|
row.split(factor=0.8)
|
||||||
row.label(text=item.name)
|
row.label(text=item.name)
|
||||||
row.label(text="{0:.2f}".format(item.total_quantity))
|
formatted_quantity = "{0:.2f}".format(item.total_quantity)
|
||||||
|
row.label(text="{}{}".format(formatted_quantity, item.unit_symbol))
|
||||||
op = row.operator("bim.unassign_cost_item_quantity", text="", icon="X")
|
op = row.operator("bim.unassign_cost_item_quantity", text="", icon="X")
|
||||||
op.cost_item = cost_item.ifc_definition_id
|
op.cost_item = cost_item.ifc_definition_id
|
||||||
op.related_object = item.ifc_definition_id
|
op.related_object = item.ifc_definition_id
|
||||||
@@ -704,8 +719,11 @@ class BIM_UL_product_cost_items(UIList):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
op = row.operator("bim.highlight_product_cost_item", text="", icon="STYLUS_PRESSURE")
|
op = row.operator("bim.highlight_product_cost_item", text="", icon="STYLUS_PRESSURE")
|
||||||
op.cost_item = item.ifc_definition_id
|
op.cost_item = item.ifc_definition_id
|
||||||
row.split(factor=0.8)
|
row.split(factor=0.5)
|
||||||
row.label(text=item.name)
|
row.label(text=item.name)
|
||||||
|
qty = "{0:.2f}".format(item.total_quantity)
|
||||||
|
formatted_total_quantity = "{0:.2f}".format(item.total_cost_quantity)
|
||||||
|
row.label(text="{}/{} {}".format(qty, formatted_total_quantity, item.unit_symbol))
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_Costing_Tools(Panel):
|
class BIM_PT_Costing_Tools(Panel):
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import bpy
|
|||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import ifcopenshell.util.date
|
import ifcopenshell.util.date
|
||||||
import ifcopenshell.util.cost
|
import ifcopenshell.util.cost
|
||||||
|
import ifcopenshell.util.unit
|
||||||
import blenderbim.bim.helper
|
import blenderbim.bim.helper
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@@ -188,8 +189,9 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
new = collection.add()
|
new = collection.add()
|
||||||
new.ifc_definition_id = product.id()
|
new.ifc_definition_id = product.id()
|
||||||
new.name = product.Name or "Unnamed"
|
new.name = product.Name or "Unnamed"
|
||||||
total_quantity = cls.calculate_parametric_quantity(cost_item, product)
|
total_quantity, unit = cls.calculate_parametric_quantity(cost_item, product)
|
||||||
new.total_quantity = total_quantity if total_quantity else 1
|
new.total_quantity = total_quantity or 0
|
||||||
|
new.unit_symbol = unit or ""
|
||||||
if is_deep:
|
if is_deep:
|
||||||
for cost_item in ifcopenshell.util.cost.get_nested_cost_items(cost_item, 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)
|
create_list_items(collection, cost_item, is_deep=False)
|
||||||
@@ -210,16 +212,21 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def calculate_parametric_quantity(cls, cost_item=None, product=None):
|
def calculate_parametric_quantity(cls, cost_item=None, product=None):
|
||||||
if not cost_item.CostQuantities:
|
quantities, unit = cls.get_assigned_quantities(cost_item, product)
|
||||||
return
|
return sum(quantity[3] for quantity in quantities), unit
|
||||||
total_quantity = sum(
|
|
||||||
quantity[3]
|
@classmethod
|
||||||
for quantities in ifcopenshell.util.element.get_psets(product, qtos_only=True).values()
|
def get_assigned_quantities(cls, cost_item, product):
|
||||||
for qto in (tool.Ifc.get().by_id(quantities["id"]).Quantities or [])
|
selected_quantitites = []
|
||||||
for quantity in cost_item.CostQuantities
|
unit = ""
|
||||||
if quantity == qto
|
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 []):
|
||||||
return total_quantity
|
for quantity in cost_item.CostQuantities:
|
||||||
|
if quantity == qto:
|
||||||
|
selected_quantitites.append(quantity)
|
||||||
|
if not unit:
|
||||||
|
unit = cls.get_quantity_unit_symbol(quantity)
|
||||||
|
return selected_quantitites, unit
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_products(cls, related_object_type=None):
|
def get_products(cls, related_object_type=None):
|
||||||
@@ -616,6 +623,18 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
new = props.product_cost_items.add()
|
new = props.product_cost_items.add()
|
||||||
new.name = cost_item.Name or "Unnamed"
|
new.name = cost_item.Name or "Unnamed"
|
||||||
new.ifc_definition_id = cost_item.id()
|
new.ifc_definition_id = cost_item.id()
|
||||||
|
quantity, unit = cls.calculate_parametric_quantity(cost_item, product)
|
||||||
|
new.total_quantity = quantity or 1
|
||||||
|
new.unit_symbol = unit or ""
|
||||||
|
new.total_cost_quantity = ifcopenshell.util.cost.get_total_quantity(cost_item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_quantity_unit_symbol(cls, quantity):
|
||||||
|
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
|
||||||
|
if unit:
|
||||||
|
return ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_root_cost_item(cls, cost_item):
|
def is_root_cost_item(cls, cost_item):
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def get_primitive_applied_value(applied_value):
|
|||||||
|
|
||||||
def get_total_quantity(root_element):
|
def get_total_quantity(root_element):
|
||||||
if root_element.is_a("IfcCostItem"):
|
if root_element.is_a("IfcCostItem"):
|
||||||
return sum([q[3] for q in root_element.CostQuantities or []]) or 1.0
|
return sum([q[3] for q in root_element.CostQuantities or []]) or None
|
||||||
elif root_element.is_a("IfcConstructionResource"):
|
elif root_element.is_a("IfcConstructionResource"):
|
||||||
return root_element.BaseQuantity[3] if root_element.BaseQuantity else 1.0
|
return root_element.BaseQuantity[3] if root_element.BaseQuantity else 1.0
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ def sum_child_root_elements(root_element, category_filter=None):
|
|||||||
if category_filter and child_cost_value.Category != category_filter:
|
if category_filter and child_cost_value.Category != category_filter:
|
||||||
continue
|
continue
|
||||||
child_applied_value = calculate_applied_value(child_root_element, child_cost_value)
|
child_applied_value = calculate_applied_value(child_root_element, child_cost_value)
|
||||||
child_quantity = get_total_quantity(child_root_element)
|
child_quantity = get_total_quantity(child_root_element) or 1.0
|
||||||
if child_cost_value.UnitBasis:
|
if child_cost_value.UnitBasis:
|
||||||
value_component = child_cost_value.UnitBasis.ValueComponent.wrappedValue
|
value_component = child_cost_value.UnitBasis.ValueComponent.wrappedValue
|
||||||
result += child_quantity / value_component * child_applied_value
|
result += child_quantity / value_component * child_applied_value
|
||||||
|
|||||||
Reference in New Issue
Block a user