enable displaying schedule of rates - web ui ref #5369

This commit is contained in:
myoualid
2024-09-21 00:16:11 +01:00
parent e0095cd446
commit 8c4ab5bf03
3 changed files with 297 additions and 115 deletions
@@ -11,6 +11,7 @@ export class CostUI {
table ? table.parentElement.remove() : null;
}
static createCostTable({ costSchedule, currency, callbacks }) {
const isScheduleOfRates = costSchedule.PredefinedType === "SCHEDULEOFRATES";
let tableWrapper;
const id = costSchedule.id;
if (CostUI.isCostScheduleLoaded(id)) {
@@ -50,6 +51,11 @@ export class CostUI {
"Total Cost (" + currency + ")",
"Actions",
];
if (isScheduleOfRates) {
columnHeaders.splice(2, 1);
columnHeaders[3] = "Rate (" + currency + ")";
columnHeaders[4] = "Total Rate (" + currency + ")";
}
const thead = document.createElement("thead");
const tr = document.createElement("tr");
thead.appendChild(tr);
@@ -80,7 +86,7 @@ export class CostUI {
}
static deleteCostItem(costItemId) {
const costItemRow = document.getElementById(costItemId);
const costItemRow = CostUI.getCostItemRow(costItemId);
let expandedState = JSON.parse(localStorage.getItem("expandedState")) || {};
expandedState = CostUI.deleteCostItemRow(costItemRow, expandedState);
localStorage.setItem("expandedState", JSON.stringify(expandedState));
@@ -149,18 +155,6 @@ export class CostUI {
}
}
// if (deleteCostItemButton && !deleteCostItemButton.hasListener) {
// deleteCostItemButton.addEventListener("click", function () {
// const targetRow = document.getElementById("context-menu").targetRow;
// if (targetRow) {
// const costItemId = parseInt(targetRow.getAttribute("id"));
// CostUI.deleteCostItem(costItemId, callbacks.deleteCostItem);
// }
// document.getElementById("context-menu").style.display = "none";
// });
// deleteCostItemButton.hasListener = true;
// }
handleButtonClick(deleteCostItemButton, callbacks.deleteCostItem);
handleButtonClick(duplicateButton, callbacks.duplicateCostItem);
handleButtonClick(getSelectedProducts, callbacks.enableEditingQuantities);
@@ -187,10 +181,13 @@ export class CostUI {
const columnIndex = Array.from(targetRow.children).indexOf(
targetCell
);
const tableId = targetRow.parentElement.parentElement.id;
const costItemId = parseInt(targetRow.getAttribute("id"));
const columnName = getColumnNames("cost-items")[columnIndex];
if (columnName.includes("Cost") && !columnName.includes("Total")) {
const columnName = getColumnNames(tableId)[columnIndex];
if (
(columnName.includes("Cost") || columnName.includes("Rate")) &&
!columnName.includes("Total")
) {
callbacks.enableEditingCostValues
? callbacks.enableEditingCostValues(costItemId)
: null;
@@ -374,47 +371,51 @@ export class CostUI {
addSummaryCostItemButton.textContent = "Add Summary Cost Item";
addSummaryCostItemButton.addEventListener("click", function () {
callbacks.addSummaryCostItem
? callbacks.addSummaryCostItem(costScheduleId)
? callbacks.addSummaryCostItem(costSchedule.id)
: null;
});
td.appendChild(addSummaryCostItemButton);
addSummaryCostItemButton.classList.add("action-button");
} else {
CostUI.createCostTree(
costSchedule["cost_items"],
tbody,
0,
null,
callbacks
);
CostUI.createCostTree({
costItems: costSchedule["cost_items"],
container: tbody,
nestingLevel: 0,
parentID: null,
callbacks: callbacks,
isScheduleOfRates: costSchedule.PredefinedType === "SCHEDULEOFRATES",
});
CostUI.applyExpandedState();
}
}
static createCostTree(
static createCostTree({
costItems,
container,
nestingLevel = 0,
parentID = null,
callbacks = {}
) {
nestingLevel,
parentID,
callbacks,
isScheduleOfRates = false,
}) {
costItems.forEach((costItem) => {
const row = CostUI.addCostItemRow(
costItem,
nestingLevel,
parentID,
isScheduleOfRates,
callbacks
);
container.appendChild(row);
if (costItem.IsNestedBy && costItem.IsNestedBy.length > 0) {
CostUI.createCostTree(
costItem.IsNestedBy,
container,
nestingLevel + 1,
costItem.id,
callbacks
);
CostUI.createCostTree({
costItems: costItem.IsNestedBy,
container: container,
nestingLevel: nestingLevel + 1,
parentID: costItem.id,
isScheduleOfRates: isScheduleOfRates,
callbacks: callbacks,
});
}
});
}
@@ -429,18 +430,32 @@ export class CostUI {
.replace(/,/g, " ");
}
static addCostItemRow(costItem, nestingLevel, parentID, callbacks = {}) {
static addCostItemRow(
costItem,
nestingLevel,
parentID,
isScheduleOfRates,
callbacks = {}
) {
const totalQuantity = costItem.TotalCostQuantity
? CostUI.format_number(costItem.TotalCostQuantity)
: "-";
const appliedValue = costItem.TotalAppliedValue
let appliedValue = costItem.TotalAppliedValue
? CostUI.format_number(costItem.TotalAppliedValue)
: "-";
const unitBasisValue = costItem.UnitBasisValueComponent
? CostUI.format_number(costItem.UnitBasisValueComponent)
: null;
if (isScheduleOfRates && unitBasisValue) {
appliedValue = appliedValue + " / " + unitBasisValue;
}
const row = CostUI.createCostItemRow(costItem, nestingLevel, parentID);
const identification = costItem.Identification
? costItem.Identification
: "XXX";
const idCell = CostUI.createTableCell(identification);
row.appendChild(idCell);
const expandButton = CostUI.createExpandButton(costItem);
const nameCell = CostUI.createNameCell(
costItem,
@@ -448,33 +463,43 @@ export class CostUI {
expandButton,
callbacks
);
const totalCostQuantityCell = CostUI.createTableCell(totalQuantity);
totalCostQuantityCell.classList.add("clickable-cell");
const unitSymbolCell = CostUI.createTableCell(costItem.UnitSymbol);
row.appendChild(nameCell);
if (isScheduleOfRates) {
const unitBasisUnitSymbol = costItem.UnitBasisUnitSymbol
? costItem.UnitBasisUnitSymbol
: "-";
const unitBasisUnitSymbolCell =
CostUI.createTableCell(unitBasisUnitSymbol);
row.appendChild(unitBasisUnitSymbolCell);
} else {
const totalCostQuantityCell = CostUI.createTableCell(totalQuantity);
row.appendChild(totalCostQuantityCell);
totalCostQuantityCell.classList.add("clickable-cell");
const unitSymbolCell = CostUI.createTableCell(costItem.UnitSymbol);
row.appendChild(unitSymbolCell);
}
const totalAppliedValueCell = CostUI.createCostCell(appliedValue);
totalAppliedValueCell.classList.add("clickable-cell");
row.appendChild(totalAppliedValueCell);
const totalCostCell = CostUI.createTotalCostCell(
costItem,
isScheduleOfRates,
callbacks.addSumCostValue
);
const actionsCell = CostUI.costItemActions(costItem, callbacks);
actionsCell.classList.add("actions-column");
row.appendChild(idCell);
row.appendChild(nameCell);
row.appendChild(totalCostQuantityCell);
row.appendChild(unitSymbolCell);
row.appendChild(totalAppliedValueCell);
row.appendChild(totalCostCell);
const actionsCell = CostUI.costItemActions(costItem, callbacks);
actionsCell.classList.add("actions-column");
row.appendChild(actionsCell);
row.get_id = function () {
return this.getAttribute("id");
};
row.isRate = isScheduleOfRates;
row.get_parent = function () {
const parentId = this.getAttribute("parent-id");
return parentId ? document.getElementById(parentId) : null;
@@ -657,10 +682,13 @@ export class CostUI {
return CostUI.createTableCell(appliedValue);
}
static createTotalCostCell(costItem, callback) {
static createTotalCostCell(costItem, isScheduleOfRates, callback) {
const totalCostCell = document.createElement("td");
const totalCost = CostUI.format_number(costItem.TotalCost);
totalCostCell.textContent = totalCost;
if (isScheduleOfRates) {
return totalCostCell;
}
if (costItem.IsSum) {
totalCostCell.textContent = totalCost + " *";
} else if (!costItem.TotalCost) {
@@ -835,9 +863,13 @@ export class CostUI {
return card;
}
static getCostItemRow(costItemId) {
return document.getElementById(costItemId);
}
static getRowNameCell(costItemId) {
return document.getElementById(costItemId)
? document.getElementById(costItemId).querySelector("td input")
return CostUI.getCostItemRow(costItemId)
? CostUI.getCostItemRow(costItemId).querySelector("td input")
: null;
}
@@ -849,6 +881,10 @@ export class CostUI {
static createCostValuesForm({ costItemId, costValues, callbacks }) {
const formId = "cost-values-form-" + costItemId;
const costItemRow = CostUI.getCostItemRow(costItemId);
const isRate = costItemRow.isRate;
let existingForm = document.getElementById(formId);
if (existingForm) {
existingForm.remove();
@@ -861,22 +897,41 @@ export class CostUI {
id: formId,
name: formName,
});
let headers;
if (isRate) {
headers = [
"Type",
"Category",
"Value",
"Unit Symbol",
"Unit Component",
"",
];
} else {
headers = ["Type", "Category", "Value", ""];
}
const { tableContainer, table } = CostUI.addTable({
headers: ["Type", "Category", "Value", ""],
headers: headers,
className: "cost-values-table",
id: "cost-values-table-" + costItemId,
});
costValues.forEach((costValue) => {
const tr = CostUI.createCostvaluesRow(costItemId, costValue, callbacks);
costValue.isRate = isRate;
const tr = CostUI.createCostvaluesRow({
costItemId,
costValue,
costValueCallbacks: callbacks,
});
table.appendChild(tr);
});
const addButton = CostUI.createAddCostValueButton(costItemId, callbacks);
form.appendChild(tableContainer);
form.appendChild(addButton);
if (!isRate) {
//TODO: IMPLEMENT ADDING COST VALUES FOR COST RATES
const addButton = CostUI.createAddCostValueButton(costItemId, callbacks);
form.appendChild(addButton);
}
return form;
}
@@ -889,17 +944,35 @@ export class CostUI {
if (!table) {
return;
}
const tr = CostUI.createCostvaluesRow(
costItemId,
{
const costItemRow = CostUI.getCostItemRow(costItemId);
const isRate = costItemRow.isRate;
let newData;
if (isRate) {
newData = {
category: "",
name: "",
applied_value: 0,
id: costValueId,
parent: costItemId,
},
costValueCallbacks
);
unit_data: {
unit_symbol: "",
value_component: 0,
},
};
} else {
newData = {
category: "",
name: "",
applied_value: 0,
id: costValueId,
parent: costItemId,
};
}
const tr = CostUI.createCostvaluesRow({
costItemId,
costValue: newData,
costValueCallbacks,
});
table.appendChild(tr);
}
@@ -933,35 +1006,54 @@ export class CostUI {
return { tableContainer: div, table: table };
}
static createCostvaluesRow(costItemId, costValue, costValueCallbacks) {
function cleanLabel(value) {
return value.replace(/[^0-9.]/g, "");
}
static createCostvaluesRow({ costItemId, costValue, costValueCallbacks }) {
if (document.getElementById(costValue.id)) {
return document.getElementById(costValue.id);
}
const tr = document.createElement("tr");
tr.isCostValue = true;
tr.parent = costItemId;
tr.id = costValue.id;
let costType = "FIXED";
if (costValue.category === "*") {
costType = "SUM";
} else if (costValue.category && costValue.category !== "*") {
costType = "CATEGORY";
} else if (costValue.applied_value) {
costType = "FIXED";
const costType = CostUI.determineCostType(costValue);
const { dropdown, typeCell } = CostUI.createTypeCell(
tr,
costType,
costItemId,
costValue,
costValueCallbacks
);
tr.appendChild(typeCell);
const categoryCell = CostUI.createTableInput(
"text",
"category",
costValue.category
);
tr.appendChild(categoryCell);
const value = CostUI.determineValue(costValue);
const valueCell1 = CostUI.createTableInput("number", "value", value);
tr.appendChild(valueCell1);
let unitSymbolCell;
let unitComponentCell;
if (costValue.isRate) {
unitSymbolCell = CostUI.Text(costValue.unit_data.unit_symbol); //TODO CostUI.createTableInput("text", "unit_symbol", costValue.unit_data.unit_symbol);
tr.appendChild(unitSymbolCell);
unitComponentCell = CostUI.createTableInput(
"number",
"unit_value_component",
costValue.unit_data.value_component
);
tr.appendChild(unitComponentCell);
}
const options = ["FIXED", "CATEGORY", "SUM"];
const typeCell = document.createElement("td");
const dropdown = CostUI.createTableDropdown({
name: "type",
options: options,
defaultValue: costType,
});
typeCell.appendChild(dropdown);
CostUI.addValueChangeListeners(
tr,
costItemId,
costValue,
costValueCallbacks
);
dropdown.addEventListener("change", function () {
const selectedType = this.value;
CostUI.updateRowBasedOnType(selectedType, categoryCell, valueCell1);
@@ -986,62 +1078,139 @@ export class CostUI {
}
}
});
tr.appendChild(typeCell);
const categoryCell = CostUI.createTableInput(
"text",
"category",
costValue.category
const deleteCell = CostUI.createDeleteCell(
costItemId,
costValue,
costValueCallbacks,
tr
);
tr.appendChild(categoryCell);
tr.appendChild(deleteCell);
let value;
CostUI.updateRowBasedOnType(costType, categoryCell, valueCell1);
return tr;
}
static determineCostType(costValue) {
if (costValue.category === "*") {
return "SUM";
} else if (costValue.category && costValue.category !== "*") {
return "CATEGORY";
} else if (costValue.applied_value) {
return "FIXED";
}
return "FIXED";
}
static createTypeCell(costType, costItemId, costValue, costValueCallbacks) {
const options = ["FIXED", "CATEGORY", "SUM"];
const typeCell = document.createElement("td");
const dropdown = CostUI.createTableDropdown({
name: "type",
options: options,
defaultValue: costType,
});
typeCell.appendChild(dropdown);
return { dropdown, typeCell };
}
static determineValue(costValue) {
function cleanLabel(value) {
return value.replace(/[^0-9.]/g, "");
}
if (costValue.category === "*") {
value = cleanLabel(costValue.label);
return cleanLabel(costValue.label);
} else {
value = costValue.applied_value;
return costValue.applied_value;
}
const valueCell1 = CostUI.createTableInput("number", "value", value);
tr.appendChild(valueCell1);
}
const inputValue = valueCell1.querySelector("input");
static getCostValueData({
typeCell,
categoryCell,
valueCell1,
componentCell,
}) {
const costType = typeCell.value;
const costCategory = categoryCell ? categoryCell.value : null;
const appliedValue = parseFloat(valueCell1.value);
const unitBasisValue = componentCell
? parseFloat(componentCell.value)
: null;
return { costType, costCategory, appliedValue, unitBasisValue };
}
static addValueChangeListeners(
tr,
costItemId,
costValue,
costValueCallbacks
) {
function handleCostValueChange(costItemId) {
const costValueData = {
costType: typeCell.querySelector("select").value,
costCategory: categoryCell.querySelector("input").value,
appliedValue: parseFloat(this.value),
let costValueData = CostUI.getCostValueData({
typeCell,
categoryCell,
valueCell1,
componentCell,
});
costValueData = {
...costValueData,
id: costValue.id,
costItemId: costItemId,
};
if (costValueData.unitBasisValue) {
costValueData.unitComponent = costValue.unit_data.unit_component;
}
if (costValueCallbacks.editCostValues) {
costValueCallbacks.editCostValues(costItemId, [costValueData]);
}
}
inputValue.addEventListener(
const typeCell = tr.querySelector("td select[name='type']");
const categoryCell = tr.querySelector("td input[name='category']");
const valueCell1 = tr.querySelector("td input[name='value']");
const componentCell = tr.querySelector(
"td input[name='unit_value_component']"
);
valueCell1.addEventListener(
"change",
handleCostValueChange.bind(inputValue, costItemId)
handleCostValueChange.bind(valueCell1, costItemId)
);
inputValue.addEventListener("keydown", function (event) {
valueCell1.addEventListener("keydown", function (event) {
if (event.key === "Enter") {
event.preventDefault();
handleCostValueChange.call(this, costItemId);
}
});
if (componentCell) {
componentCell.addEventListener(
"change",
handleCostValueChange.bind(componentCell, costItemId)
);
componentCell.addEventListener("keydown", function (event) {
if (event.key === "Enter") {
event.preventDefault();
handleCostValueChange.call(this, costItemId);
}
});
}
}
static createDeleteCell(costItemId, costValue, costValueCallbacks, tr) {
const deleteCell = document.createElement("td");
tr.appendChild(deleteCell);
const dleteButton = CostUI.createButton("Delete", "fa-solid fa-trash");
dleteButton.addEventListener("click", function () {
const deleteButton = CostUI.createButton("Delete", "fa-solid fa-trash");
deleteButton.addEventListener("click", function () {
costValueCallbacks.deleteCostValue(costItemId, costValue.id);
tr.remove();
});
deleteCell.appendChild(dleteButton);
CostUI.updateRowBasedOnType(costType, categoryCell, valueCell1);
return tr;
deleteCell.appendChild(deleteButton);
return deleteCell;
}
static updateRowBasedOnType(type, categoryCell, valueCell1) {
@@ -1126,7 +1295,7 @@ export class CostUI {
const costItemId = form.id.split("-").pop();
CostUI.unhighlightElement(costItemId);
};
closeButton = CostUI.createCloseButton(callback); // Pass the function reference without invoking it
closeButton = CostUI.createCloseButton(callback);
}
form.appendChild(header);
form.appendChild(closeButton);
+7 -1
View File
@@ -391,8 +391,8 @@ class Web(bonsai.core.tool.Web):
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "deleteCostItem":
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
tool.Ifc.run("cost.remove_cost_item", cost_item=cost_item)
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
tool.Ifc.run("cost.remove_cost_item", cost_item=cost_item)
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "duplicateCostItem":
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
@@ -439,6 +439,7 @@ class Web(bonsai.core.tool.Web):
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
for value_data in operator_data["costValues"] or []:
value = ifc_file.by_id(value_data["id"])
attributes = {}
if value_data["costType"] == "FIXED":
attributes = {"AppliedValue": value_data["appliedValue"], "Category": None}
elif value_data["costType"] == "CATEGORY":
@@ -448,6 +449,11 @@ class Web(bonsai.core.tool.Web):
}
elif value_data["costType"] == "SUM":
attributes = {"Category": "*"}
if value_data["unitBasisValue"]:
attributes["UnitBasis"] = {
"ValueComponent": value_data["unitBasisValue"],
"UnitComponent": ifc_file.by_id(value_data["unitComponent"]),
}
ifcopenshell.api.cost.edit_cost_value(file=ifc_file, cost_value=value, attributes=attributes)
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "addProductAssignments":
@@ -22,7 +22,7 @@ from typing import Optional, Union, Literal, Generator, Any
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
from ifcopenshell.util.doc import get_predefined_type_doc
from ifcopenshell.util.element import get_psets
from ifcopenshell.util.unit import get_unit_symbol
arithmetic_operator_symbols = {"ADD": "+", "DIVIDE": "/", "MULTIPLY": "*", "SUBTRACT": "-"}
symbol_arithmetic_operators = {"+": "ADD", "/": "DIVIDE", "*": "MULTIPLY", "-": "SUBTRACT"}
FILTER_BY_TYPE = Literal["PRODUCT", "RESOURCE", "PROCESS"]
@@ -255,6 +255,13 @@ def get_cost_values(cost_item: ifcopenshell.entity_instance) -> list[dict[str, s
for cost_value in cost_item.CostValues or []:
label = "{0:.2f}".format(calculate_applied_value(cost_item, cost_value))
label += " = {}".format(serialise_cost_value(cost_value))
unit_data = {"value_component": None, "unit_component": None, "unit_symbol": ""}
if cost_value.UnitBasis:
data = cost_value.UnitBasis.get_info()
unit_data["value_component"] = data["ValueComponent"].wrappedValue
unit_data["unit_component"] = data["UnitComponent"].id()
unit_data["unit_symbol"] = get_unit_symbol(cost_value.UnitBasis.UnitComponent)
results.append(
{
"id": cost_value.id(),
@@ -264,9 +271,9 @@ def get_cost_values(cost_item: ifcopenshell.entity_instance) -> list[dict[str, s
"applied_value": (
get_primitive_applied_value(cost_value.AppliedValue) if cost_value.AppliedValue else None
),
"unit_data": unit_data,
}
)
print(results)
return results