mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-20 12:12:15 +00:00
web ui cost module features:
- improve UI for editing cost quantites - add, edit, delete manual quantities
This commit is contained in:
@@ -20,6 +20,22 @@ body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-form {
|
||||||
|
width: 50vw;
|
||||||
|
max-height: 80vh;
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
border: 1px solid black;
|
||||||
|
z-index: 9999;
|
||||||
|
cursor: move;
|
||||||
|
user-select: none;
|
||||||
|
padding-bottom: var(--padding-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
[id^="enable-editing-quantities"].floating-form {
|
||||||
|
height: 70vh;
|
||||||
|
}
|
||||||
|
|
||||||
.form-header {
|
.form-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@@ -30,36 +46,44 @@ body {
|
|||||||
font-size: large;
|
font-size: large;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
border-radius: 6px 6px 0 0;
|
border-radius: 6px 6px 0 0;
|
||||||
|
height: 5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-form span {
|
.form-container {
|
||||||
padding: var(--padding-small);
|
height: 95%;
|
||||||
margin-left: var(--margin-small);
|
padding-left: var(--padding-medium);
|
||||||
|
padding-right: var(--padding-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-form {
|
.form-section {
|
||||||
width: 50vw;
|
margin-bottom: 1em;
|
||||||
max-height: 80vh;
|
height: 65%;
|
||||||
position: absolute;
|
overflow-y: auto;
|
||||||
background-color: var(--background-color);
|
overflow-x: hidden;
|
||||||
border: 1px solid black;
|
|
||||||
z-index: 9999;
|
|
||||||
cursor: move;
|
|
||||||
user-select: none;
|
|
||||||
resize: both;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-section h3 {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-section {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.floating-form table tr {
|
.floating-form table tr {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-container {
|
|
||||||
padding: var(--padding-medium);
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-button {
|
.action-button {
|
||||||
|
position:relative;
|
||||||
background-color: var(--button-background);
|
background-color: var(--button-background);
|
||||||
border: none;
|
border: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -72,6 +96,32 @@ body {
|
|||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
|
z-index: 11000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button::after {
|
||||||
|
content: attr(data-tooltip);
|
||||||
|
position: absolute;
|
||||||
|
bottom: -30px;
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button:hover::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-btn {
|
||||||
|
background-color: #887821;
|
||||||
|
/* make a nic shadow */
|
||||||
|
box-shadow: 0 0 10px #887821;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button:hover {
|
.action-button:hover {
|
||||||
@@ -141,7 +191,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.floating-form .table-container {
|
.floating-form .table-container {
|
||||||
max-height: 70%;
|
max-height: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cost-items {
|
#cost-items {
|
||||||
@@ -149,7 +199,6 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[id^="cost-values-form"] table {
|
[id^="cost-values-form"] table {
|
||||||
/* fixed */
|
|
||||||
max-height: 50%;
|
max-height: 50%;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
@@ -186,6 +235,7 @@ td,
|
|||||||
th {
|
th {
|
||||||
border-right: 1px solid #ddd;
|
border-right: 1px solid #ddd;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
@@ -221,15 +271,7 @@ select {
|
|||||||
color: var(--blender-button-text, var(--text-color));
|
color: var(--blender-button-text, var(--text-color));
|
||||||
border-color: var(--blender-button-border, var(--border-color));
|
border-color: var(--blender-button-border, var(--border-color));
|
||||||
transition: filter 0.2s ease;
|
transition: filter 0.2s ease;
|
||||||
border-radius: 6px;
|
|
||||||
padding: 6px 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
transition: border-color 0.2s cubic-bezier(0.3, 0, 0.5, 1);
|
transition: border-color 0.2s cubic-bezier(0.3, 0, 0.5, 1);
|
||||||
margin-left: 0.5em;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
font-size: var(--fontSize);
|
|
||||||
min-width: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus {
|
input:focus {
|
||||||
@@ -239,6 +281,31 @@ input:focus {
|
|||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#cost-items input, #cost-items
|
||||||
|
select {
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
font-size: var(--fontSize);
|
||||||
|
min-width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input {
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 10px;
|
||||||
|
margin-left: 0.2em;
|
||||||
|
margin-right: 0.2em;
|
||||||
|
font-size: inherit;
|
||||||
|
height: inherit;
|
||||||
|
width: inherit;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
#cost-items tr:hover,
|
#cost-items tr:hover,
|
||||||
.highlighted {
|
.highlighted {
|
||||||
background-color: #28a74657;
|
background-color: #28a74657;
|
||||||
@@ -332,7 +399,7 @@ form table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.clickable-cell:hover {
|
.clickable-cell:hover {
|
||||||
border: 1px solid #28a746;
|
border: 1px solid #94a728;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -36,19 +36,51 @@ function handleEditQuantities(data) {
|
|||||||
const products = data.data["quantities"]["selected_products"];
|
const products = data.data["quantities"]["selected_products"];
|
||||||
const assigned_products = data.data["quantities"]["assigned_products"];
|
const assigned_products = data.data["quantities"]["assigned_products"];
|
||||||
const quantityNames = data.data["quantities"]["product_quantity_names"];
|
const quantityNames = data.data["quantities"]["product_quantity_names"];
|
||||||
|
const costQuantities = data.data["quantities"]["cost_quantities"];
|
||||||
|
|
||||||
CostUI.editQuantities({
|
CostUI.enableEditingQuantities({
|
||||||
costItemId: costItemId,
|
costItemId: costItemId,
|
||||||
selectedProducts: products,
|
selectedProducts: products,
|
||||||
assignedProducts: assigned_products,
|
assignedProducts: assigned_products,
|
||||||
quantityNames: quantityNames,
|
quantityNames: quantityNames,
|
||||||
|
costQuantities: costQuantities,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
addProductAssignments: addProductAssignments,
|
addProductAssignments: addProductAssignments,
|
||||||
enableEditingQuantities: enableEditingQuantities,
|
enableEditingQuantities: enableEditingQuantities,
|
||||||
|
addQuantity: addQuantity,
|
||||||
|
editQuantity: editQuantity,
|
||||||
|
deleteQuantity: deleteQuantity,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addQuantity(costItemId, ifcClass) {
|
||||||
|
executeOperator({
|
||||||
|
type: "AddCostItemQuantity",
|
||||||
|
costItemId: costItemId,
|
||||||
|
ifcClass: ifcClass,
|
||||||
|
});
|
||||||
|
|
||||||
|
//CostUI.addQuantity(costItemId, quantityName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function editQuantity(costItemId, quantityId, attributes) {
|
||||||
|
executeOperator({
|
||||||
|
type: "editCostItemQuantity",
|
||||||
|
costItemId: costItemId,
|
||||||
|
quantityId: quantityId,
|
||||||
|
attributes: attributes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteQuantity(costItemId, quantityId) {
|
||||||
|
executeOperator({
|
||||||
|
type: "deleteCostItemQuantity",
|
||||||
|
costItemId: costItemId,
|
||||||
|
quantityId: quantityId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handlePredefinedTypes(data) {
|
function handlePredefinedTypes(data) {
|
||||||
CostUI.enableAddingCostSchedule(
|
CostUI.enableAddingCostSchedule(
|
||||||
data.data["predefined_types"],
|
data.data["predefined_types"],
|
||||||
|
|||||||
@@ -793,11 +793,15 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static getRowNameCell(costItemId) {
|
static getRowNameCell(costItemId) {
|
||||||
return document.getElementById(costItemId).querySelector("td input");
|
return document.getElementById(costItemId)
|
||||||
|
? document.getElementById(costItemId).querySelector("td input")
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getCostItemName(costItemId) {
|
static getCostItemName(costItemId) {
|
||||||
return CostUI.getRowNameCell(costItemId).value;
|
return CostUI.getRowNameCell(costItemId)
|
||||||
|
? CostUI.getRowNameCell(costItemId).value
|
||||||
|
: "Unnamed";
|
||||||
}
|
}
|
||||||
|
|
||||||
static createCostValuesForm({ costItemId, costValues, callbacks }) {
|
static createCostValuesForm({ costItemId, costValues, callbacks }) {
|
||||||
@@ -840,7 +844,6 @@ export class CostUI {
|
|||||||
static addNewCostValueRow(costItemId, costValueId, costValueCallbacks) {
|
static addNewCostValueRow(costItemId, costValueId, costValueCallbacks) {
|
||||||
const table = CostUI.getCostValuesTable(costItemId);
|
const table = CostUI.getCostValuesTable(costItemId);
|
||||||
if (!table) {
|
if (!table) {
|
||||||
console.log("Cost values table not found for cost item ID:", costItemId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tr = CostUI.createCostvaluesRow(
|
const tr = CostUI.createCostvaluesRow(
|
||||||
@@ -908,8 +911,8 @@ export class CostUI {
|
|||||||
} else if (costValue.applied_value) {
|
} else if (costValue.applied_value) {
|
||||||
costType = "FIXED";
|
costType = "FIXED";
|
||||||
}
|
}
|
||||||
|
const options = ["FIXED", "CATEGORY", "SUM"];
|
||||||
const typeCell = CostUI.createTableDropdown("type", costType);
|
const typeCell = CostUI.createTableDropdown("type", options, costType);
|
||||||
const dropdown = typeCell.querySelector("select");
|
const dropdown = typeCell.querySelector("select");
|
||||||
dropdown.addEventListener("change", function () {
|
dropdown.addEventListener("change", function () {
|
||||||
const selectedType = this.value;
|
const selectedType = this.value;
|
||||||
@@ -1012,12 +1015,11 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static createTableDropdown(name, value = "FIXED") {
|
static createTableDropdown(name, options, value = "FIXED") {
|
||||||
const cell = document.createElement("td");
|
const cell = document.createElement("td");
|
||||||
const dropdown = document.createElement("select");
|
const dropdown = document.createElement("select");
|
||||||
dropdown.name = name;
|
dropdown.name = name;
|
||||||
|
|
||||||
const options = ["FIXED", "CATEGORY", "SUM"];
|
|
||||||
options.forEach((optionValue) => {
|
options.forEach((optionValue) => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = optionValue;
|
option.value = optionValue;
|
||||||
@@ -1306,7 +1308,7 @@ export class CostUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static createProductTable({
|
static createProductTable({
|
||||||
form,
|
container,
|
||||||
products,
|
products,
|
||||||
quantityNames,
|
quantityNames,
|
||||||
costItemId,
|
costItemId,
|
||||||
@@ -1316,7 +1318,7 @@ export class CostUI {
|
|||||||
const noProductsMessage = document.createElement("p");
|
const noProductsMessage = document.createElement("p");
|
||||||
noProductsMessage.textContent =
|
noProductsMessage.textContent =
|
||||||
"Your Blender Selection is empty! Select objects first.";
|
"Your Blender Selection is empty! Select objects first.";
|
||||||
form.appendChild(noProductsMessage);
|
container.appendChild(noProductsMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { tableContainer, table } = CostUI.addTable({
|
const { tableContainer, table } = CostUI.addTable({
|
||||||
@@ -1324,7 +1326,7 @@ export class CostUI {
|
|||||||
className: "",
|
className: "",
|
||||||
id: "cost-values-table-" + costItemId,
|
id: "cost-values-table-" + costItemId,
|
||||||
});
|
});
|
||||||
form.appendChild(tableContainer);
|
container.appendChild(tableContainer);
|
||||||
const tbody = document.createElement("tbody");
|
const tbody = document.createElement("tbody");
|
||||||
table.appendChild(tbody);
|
table.appendChild(tbody);
|
||||||
|
|
||||||
@@ -1432,7 +1434,7 @@ export class CostUI {
|
|||||||
|
|
||||||
const productIds = products.map((product) => product.info.id);
|
const productIds = products.map((product) => product.info.id);
|
||||||
callbacks.emptyForm = () => {
|
callbacks.emptyForm = () => {
|
||||||
form.innerHTML = "";
|
container.innerHTML = "";
|
||||||
};
|
};
|
||||||
const addProductAssignmentsButton = CostUI.addProductAssignmentsButton(
|
const addProductAssignmentsButton = CostUI.addProductAssignmentsButton(
|
||||||
costItemId,
|
costItemId,
|
||||||
@@ -1441,13 +1443,13 @@ export class CostUI {
|
|||||||
callbacks
|
callbacks
|
||||||
);
|
);
|
||||||
|
|
||||||
form.appendChild(quantitySelect);
|
container.appendChild(quantitySelect);
|
||||||
form.appendChild(addProductAssignmentsButton);
|
container.appendChild(addProductAssignmentsButton);
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createAssignmentsTable({
|
static createAssignmentsTable({
|
||||||
form,
|
container,
|
||||||
products,
|
products,
|
||||||
costItemId,
|
costItemId,
|
||||||
quantityNames,
|
quantityNames,
|
||||||
@@ -1459,7 +1461,7 @@ export class CostUI {
|
|||||||
id: "cost-values-table-" + costItemId,
|
id: "cost-values-table-" + costItemId,
|
||||||
});
|
});
|
||||||
|
|
||||||
form.appendChild(tableContainer);
|
container.appendChild(tableContainer);
|
||||||
const tbody = document.createElement("tbody");
|
const tbody = document.createElement("tbody");
|
||||||
table.appendChild(tbody);
|
table.appendChild(tbody);
|
||||||
|
|
||||||
@@ -1512,8 +1514,10 @@ export class CostUI {
|
|||||||
quantitySelect,
|
quantitySelect,
|
||||||
callbacks
|
callbacks
|
||||||
) {
|
) {
|
||||||
const addProductAssignmentsButton = document.createElement("button");
|
const addProductAssignmentsButton = CostUI.createButton(
|
||||||
addProductAssignmentsButton.textContent = "Add Product Assignments";
|
"Add Product Assignments",
|
||||||
|
"fa-solid fa-plus"
|
||||||
|
);
|
||||||
addProductAssignmentsButton.addEventListener("click", (event) => {
|
addProductAssignmentsButton.addEventListener("click", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let propName = quantitySelect.value;
|
let propName = quantitySelect.value;
|
||||||
@@ -1528,7 +1532,7 @@ export class CostUI {
|
|||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
callbacks.emptyForm ? callbacks.emptyForm() : null;
|
callbacks.emptyForm ? callbacks.emptyForm() : null;
|
||||||
callbacks.getSelectedProducts(costItemId);
|
callbacks.enableEditingQuantities(costItemId);
|
||||||
});
|
});
|
||||||
addProductAssignmentsButton.classList.add("action-button");
|
addProductAssignmentsButton.classList.add("action-button");
|
||||||
return addProductAssignmentsButton;
|
return addProductAssignmentsButton;
|
||||||
@@ -1730,49 +1734,424 @@ export class CostUI {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static editQuantities({
|
static enableEditingQuantities({
|
||||||
costItemId,
|
costItemId,
|
||||||
selectedProducts,
|
selectedProducts,
|
||||||
quantityNames,
|
|
||||||
assignedProducts,
|
assignedProducts,
|
||||||
|
quantityNames,
|
||||||
|
costQuantities,
|
||||||
callbacks,
|
callbacks,
|
||||||
}) {
|
}) {
|
||||||
const formId = "selected-products-" + costItemId;
|
CostUI.highlightElement(costItemId);
|
||||||
const form = CostUI.Form({
|
const form = CostUI.Form({
|
||||||
id: formId,
|
id: "enable-editing-quantities-" + costItemId,
|
||||||
name:
|
name: "Editing Quantities for: " + CostUI.getCostItemName(costItemId),
|
||||||
"Edit Product Assignments for: " + CostUI.getCostItemName(costItemId),
|
|
||||||
icon: "fa-solid fa-box",
|
icon: "fa-solid fa-box",
|
||||||
});
|
});
|
||||||
const numberOfProducts = CostUI.Text(
|
|
||||||
"Selection basket : " + selectedProducts.length + " products",
|
const ribbonBar = CostUI.createRibbonBar();
|
||||||
"fa-solid fa-cart-shopping",
|
form.appendChild(ribbonBar);
|
||||||
"large"
|
|
||||||
);
|
const selectedProductsSection = CostUI.selectedProductsSection({
|
||||||
form.appendChild(numberOfProducts);
|
|
||||||
CostUI.highlightElement(costItemId);
|
|
||||||
const selectedProductsTable = CostUI.createProductTable({
|
|
||||||
form,
|
|
||||||
products: selectedProducts,
|
products: selectedProducts,
|
||||||
quantityNames,
|
quantityNames,
|
||||||
costItemId,
|
costItemId,
|
||||||
callbacks,
|
callbacks,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (assignedProducts.length > 0) {
|
const assignedProductsSection = CostUI.assignedProductsSection({
|
||||||
|
products: assignedProducts,
|
||||||
|
quantityNames,
|
||||||
|
costItemId,
|
||||||
|
callbacks,
|
||||||
|
});
|
||||||
|
|
||||||
|
const manualQuantitiesSection = CostUI.manualQuantitiesSection({
|
||||||
|
costItemId,
|
||||||
|
costQuantities,
|
||||||
|
has_assigned_products: assignedProducts.length > 0,
|
||||||
|
callbacks,
|
||||||
|
});
|
||||||
|
|
||||||
|
form.appendChild(selectedProductsSection);
|
||||||
|
form.appendChild(assignedProductsSection);
|
||||||
|
form.appendChild(manualQuantitiesSection);
|
||||||
|
|
||||||
|
const summarySection = CostUI.createSummarySection({
|
||||||
|
selectedProducts,
|
||||||
|
assignedProducts,
|
||||||
|
costQuantities,
|
||||||
|
});
|
||||||
|
form.appendChild(summarySection);
|
||||||
|
|
||||||
|
CostUI.addEventListeners({
|
||||||
|
switchBar: ribbonBar,
|
||||||
|
costItemId,
|
||||||
|
selectedProductsSection,
|
||||||
|
assignedProductsSection,
|
||||||
|
manualQuantitiesSection,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lastActiveSection =
|
||||||
|
localStorage.getItem("lastActiveSection") || "selected-products";
|
||||||
|
const lastActiveButton = document.getElementById(
|
||||||
|
`${lastActiveSection}-btn`
|
||||||
|
);
|
||||||
|
if (lastActiveButton) {
|
||||||
|
lastActiveButton.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static createRibbonBar() {
|
||||||
|
const ribbonBar = document.createElement("div");
|
||||||
|
ribbonBar.className = "switch-bar";
|
||||||
|
ribbonBar.innerHTML = `
|
||||||
|
<button class="action-button" id="selected-products-btn">Selected Products</button>
|
||||||
|
<button class="action-button" id="assigned-products-btn">Assigned Products</button>
|
||||||
|
<button class="action-button" id="manual-quantities-btn">Manual Quantities</button>
|
||||||
|
`;
|
||||||
|
return ribbonBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createSummarySection({
|
||||||
|
selectedProducts,
|
||||||
|
assignedProducts,
|
||||||
|
costQuantities,
|
||||||
|
}) {
|
||||||
|
const summarySection = document.createElement("div");
|
||||||
|
summarySection.classList.add("summary-section");
|
||||||
|
|
||||||
|
const manualQuantities = costQuantities.quantities.filter(
|
||||||
|
(quantity) => !quantity.fromProduct
|
||||||
|
);
|
||||||
|
const paramaterQuantities = costQuantities.quantities.filter(
|
||||||
|
(quantity) => quantity.fromProduct
|
||||||
|
);
|
||||||
|
|
||||||
|
const table = document.createElement("table");
|
||||||
|
table.classList.add("summary-table");
|
||||||
|
|
||||||
|
const thead = document.createElement("thead");
|
||||||
|
const headerRow = document.createElement("tr");
|
||||||
|
const headers = ["Category", "Count"];
|
||||||
|
headers.forEach((headerText) => {
|
||||||
|
const th = document.createElement("th");
|
||||||
|
th.textContent = headerText;
|
||||||
|
headerRow.appendChild(th);
|
||||||
|
});
|
||||||
|
thead.appendChild(headerRow);
|
||||||
|
table.appendChild(thead);
|
||||||
|
const tbody = document.createElement("tbody");
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{ category: "Assigned Products", count: assignedProducts.length },
|
||||||
|
{ category: "Manual Quantities", count: manualQuantities.length },
|
||||||
|
{
|
||||||
|
category: "Product derived Quantities",
|
||||||
|
count: paramaterQuantities.length,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
const row = document.createElement("tr");
|
||||||
|
const categoryCell = document.createElement("td");
|
||||||
|
categoryCell.textContent = item.category;
|
||||||
|
const countCell = document.createElement("td");
|
||||||
|
countCell.textContent = item.count;
|
||||||
|
row.appendChild(categoryCell);
|
||||||
|
row.appendChild(countCell);
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.appendChild(tbody);
|
||||||
|
summarySection.appendChild(table);
|
||||||
|
|
||||||
|
return summarySection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static addEventListeners({
|
||||||
|
switchBar,
|
||||||
|
costItemId,
|
||||||
|
selectedProductsSection,
|
||||||
|
assignedProductsSection,
|
||||||
|
manualQuantitiesSection,
|
||||||
|
}) {
|
||||||
|
const buttons = switchBar.querySelectorAll(".action-button");
|
||||||
|
|
||||||
|
buttons.forEach((button) => {
|
||||||
|
button.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const sectionName = button.id.replace("-btn", "");
|
||||||
|
const sectionId = sectionName + "-section-" + costItemId;
|
||||||
|
|
||||||
|
if (selectedProductsSection)
|
||||||
|
selectedProductsSection.style.display = "none";
|
||||||
|
if (assignedProductsSection)
|
||||||
|
assignedProductsSection.style.display = "none";
|
||||||
|
if (manualQuantitiesSection)
|
||||||
|
manualQuantitiesSection.style.display = "none";
|
||||||
|
|
||||||
|
const section = document.getElementById(sectionId);
|
||||||
|
if (section) {
|
||||||
|
section.style.display = "block";
|
||||||
|
}
|
||||||
|
buttons.forEach((btn) => btn.classList.remove("active-btn"));
|
||||||
|
button.classList.add("active-btn");
|
||||||
|
|
||||||
|
localStorage.setItem("lastActiveSection", sectionName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSection(costItemId, sectionName) {
|
||||||
|
return document.getElementById(`${sectionName}-${costItemId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
static selectedProductsSection({
|
||||||
|
products,
|
||||||
|
quantityNames,
|
||||||
|
costItemId,
|
||||||
|
callbacks,
|
||||||
|
}) {
|
||||||
|
const selectedProductsSection = document.createElement("div");
|
||||||
|
selectedProductsSection.id = "selected-products-section-" + costItemId;
|
||||||
|
selectedProductsSection.classList.add("form-section");
|
||||||
|
const numberOfProducts = CostUI.Text(
|
||||||
|
"Selection basket : " + products.length + " products",
|
||||||
|
"fa-solid fa-cart-shopping",
|
||||||
|
"medium"
|
||||||
|
);
|
||||||
|
selectedProductsSection.appendChild(numberOfProducts);
|
||||||
|
|
||||||
|
const selectedProductsTable = CostUI.createProductTable({
|
||||||
|
container: selectedProductsSection,
|
||||||
|
products: products,
|
||||||
|
quantityNames,
|
||||||
|
costItemId,
|
||||||
|
callbacks,
|
||||||
|
});
|
||||||
|
return selectedProductsSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static assignedProductsSection({
|
||||||
|
products,
|
||||||
|
quantityNames,
|
||||||
|
costItemId,
|
||||||
|
callbacks,
|
||||||
|
}) {
|
||||||
|
const assignedProductsSection = document.createElement("div");
|
||||||
|
assignedProductsSection.id = "assigned-products-section-" + costItemId;
|
||||||
|
assignedProductsSection.style.display = "none";
|
||||||
|
assignedProductsSection.classList.add("form-section");
|
||||||
|
if (products.length > 0) {
|
||||||
|
let text = " Assigned Products : " + products.length;
|
||||||
const assignedProductsText = CostUI.Text(
|
const assignedProductsText = CostUI.Text(
|
||||||
"Assigned Products",
|
text,
|
||||||
"fa-solid fa-solid fa-paperclip",
|
"fa-solid fa-solid fa-paperclip",
|
||||||
"large"
|
"medium"
|
||||||
);
|
);
|
||||||
form.appendChild(assignedProductsText);
|
assignedProductsSection.appendChild(assignedProductsText);
|
||||||
const assignmentsTable = CostUI.createAssignmentsTable({
|
const assignmentsTable = CostUI.createAssignmentsTable({
|
||||||
form,
|
container: assignedProductsSection,
|
||||||
products: assignedProducts,
|
products: products,
|
||||||
quantityNames,
|
quantityNames,
|
||||||
costItemId,
|
costItemId,
|
||||||
callbacks,
|
callbacks,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return assignedProductsSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static manualQuantitiesSection({
|
||||||
|
costItemId,
|
||||||
|
costQuantities,
|
||||||
|
has_assigned_products,
|
||||||
|
callbacks,
|
||||||
|
}) {
|
||||||
|
const manualQuantitiesSection = document.createElement("div");
|
||||||
|
manualQuantitiesSection.classList.add("form-section");
|
||||||
|
manualQuantitiesSection.id = "manual-quantities-section-" + costItemId;
|
||||||
|
manualQuantitiesSection.style.display = "none";
|
||||||
|
|
||||||
|
const title = CostUI.Text(
|
||||||
|
"Manual Quantities",
|
||||||
|
"fa-solid fa-ruler",
|
||||||
|
"medium"
|
||||||
|
);
|
||||||
|
manualQuantitiesSection.appendChild(title);
|
||||||
|
const unitSymbol = costQuantities.unit_symbol;
|
||||||
|
|
||||||
|
let quantityType = costQuantities.quantity_type;
|
||||||
|
|
||||||
|
if (quantityType) {
|
||||||
|
const text = CostUI.Text(
|
||||||
|
quantityType + " (" + unitSymbol + " )",
|
||||||
|
"fa-solid fa-ruler",
|
||||||
|
"small"
|
||||||
|
);
|
||||||
|
manualQuantitiesSection.appendChild(text);
|
||||||
|
} else {
|
||||||
|
// add dropdown to chose quantity type , from "IfcQuantityArea", "IfcQuantityLength", "IfcQuantityVolume", "IfcQuantityCount", "IfcQuantityWeight "
|
||||||
|
const quantityTypes = [
|
||||||
|
"IfcQuantityArea",
|
||||||
|
"IfcQuantityLength",
|
||||||
|
"IfcQuantityVolume",
|
||||||
|
"IfcQuantityCount",
|
||||||
|
"IfcQuantityWeight",
|
||||||
|
];
|
||||||
|
const dropdown = CostUI.createTableDropdown(
|
||||||
|
"type",
|
||||||
|
quantityTypes,
|
||||||
|
"IfcQuantityArea"
|
||||||
|
);
|
||||||
|
dropdown.id = "quantity-type-dropdown-" + costItemId;
|
||||||
|
manualQuantitiesSection.appendChild(dropdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if quantity is ty IfcQuantityCount, and the costQuantities
|
||||||
|
if (quantityType === "IfcQuantityCount" && has_assigned_products) {
|
||||||
|
// write text that one of the quantities is assigned to the product selection
|
||||||
|
const text = CostUI.Text(
|
||||||
|
" One of the quantities is assigned to the product selection",
|
||||||
|
"fa-solid fa-warning",
|
||||||
|
"small"
|
||||||
|
);
|
||||||
|
manualQuantitiesSection.appendChild(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
let paramaterQuantities = costQuantities.quantities.filter(
|
||||||
|
(quantity) => quantity.fromProduct
|
||||||
|
);
|
||||||
|
let manualQuantities = costQuantities.quantities.filter(
|
||||||
|
(quantity) => !quantity.fromProduct
|
||||||
|
);
|
||||||
|
|
||||||
|
let headerNames = ["Name", "Value" + " (" + unitSymbol + " )", "Actions"];
|
||||||
|
|
||||||
|
if (manualQuantities.length > 0) {
|
||||||
|
headerNames = [];
|
||||||
|
Object.entries(manualQuantities[0]).forEach(([key, value]) => {
|
||||||
|
if (key !== "id" && key !== "fromProduct") {
|
||||||
|
headerNames.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
headerNames.push("Actions");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { tableContainer, table } = CostUI.addTable({
|
||||||
|
headers: headerNames,
|
||||||
|
className: "manual-quantities-table",
|
||||||
|
id: "manual-quantities-table-" + costItemId,
|
||||||
|
});
|
||||||
|
|
||||||
|
manualQuantities.forEach((quantity) => {
|
||||||
|
const tr = CostUI.createManualQuantityRow(
|
||||||
|
costItemId,
|
||||||
|
quantity,
|
||||||
|
callbacks
|
||||||
|
);
|
||||||
|
table.appendChild(tr);
|
||||||
|
});
|
||||||
|
const addButton = CostUI.createAddManualQuantityButton(
|
||||||
|
costItemId,
|
||||||
|
quantityType,
|
||||||
|
callbacks
|
||||||
|
);
|
||||||
|
|
||||||
|
manualQuantitiesSection.appendChild(tableContainer);
|
||||||
|
// manualQuantitiesSection.appendChild(tableContainer2);
|
||||||
|
manualQuantitiesSection.appendChild(addButton);
|
||||||
|
return manualQuantitiesSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createManualQuantityRow(costItemId, quantity, callbacks) {
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
tr.id = quantity.id;
|
||||||
|
|
||||||
|
// get quantity keys and values to create the table row cells
|
||||||
|
// Label cell
|
||||||
|
|
||||||
|
Object.entries(quantity).forEach(([key, value]) => {
|
||||||
|
if (key !== "id" && key !== "fromProduct") {
|
||||||
|
if (key === "Name") {
|
||||||
|
// create input
|
||||||
|
const cell = document.createElement("td");
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "text";
|
||||||
|
input.value = value;
|
||||||
|
cell.appendChild(input);
|
||||||
|
tr.appendChild(cell);
|
||||||
|
|
||||||
|
input.addEventListener("keydown", function (e) {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
callbacks.editQuantity(costItemId, quantity.id, {
|
||||||
|
[key]: input.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if key contains value
|
||||||
|
else if (key.toLowerCase().includes("value")) {
|
||||||
|
const cell = document.createElement("td");
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "number";
|
||||||
|
input.value = value;
|
||||||
|
cell.appendChild(input);
|
||||||
|
|
||||||
|
input.addEventListener("keydown", function (e) {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
callbacks.editQuantity(costItemId, quantity.id, {
|
||||||
|
[key]: parseFloat(input.value), // Dynamically set the key and parse the value as a double
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tr.appendChild(cell);
|
||||||
|
} else {
|
||||||
|
const cell = document.createElement("td");
|
||||||
|
cell.textContent = value;
|
||||||
|
tr.appendChild(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add delete button
|
||||||
|
const deleteButton = CostUI.createButton("Delete", "fa-solid fa-trash");
|
||||||
|
const deleteCell = document.createElement("td");
|
||||||
|
deleteButton.addEventListener("click", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
callbacks.deleteQuantity(costItemId, quantity.id);
|
||||||
|
tr.remove();
|
||||||
|
});
|
||||||
|
deleteCell.appendChild(deleteButton);
|
||||||
|
tr.appendChild(deleteCell);
|
||||||
|
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createAddManualQuantityButton(costItemId, quantityType, callbacks) {
|
||||||
|
const addButton = CostUI.createButton(
|
||||||
|
"Add Manual Quantity",
|
||||||
|
"fa-solid fa-plus"
|
||||||
|
);
|
||||||
|
|
||||||
|
addButton.addEventListener("click", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
let type = quantityType;
|
||||||
|
if (!type) {
|
||||||
|
// get quantityType from dropdown
|
||||||
|
const qtoSection = document.getElementById(
|
||||||
|
"manual-quantities-section-" + costItemId
|
||||||
|
);
|
||||||
|
const dropdown = qtoSection.querySelector("select");
|
||||||
|
type = dropdown ? dropdown.value : null;
|
||||||
|
}
|
||||||
|
callbacks.addQuantity(costItemId, type);
|
||||||
|
});
|
||||||
|
|
||||||
|
return addButton;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,6 +295,15 @@ class Cost(bonsai.core.tool.Cost):
|
|||||||
unit = cls.get_quantity_unit_symbol(quantity)
|
unit = cls.get_quantity_unit_symbol(quantity)
|
||||||
return selected_quantitites, unit
|
return selected_quantitites, unit
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_assigned_product(cls, cost_item, quantity):
|
||||||
|
assigned_products = cls.get_cost_item_assignments(cost_item, filter_by_type="PRODUCT", is_deep=False)
|
||||||
|
for product in assigned_products:
|
||||||
|
assigned_quantities, _ = cls.get_assigned_quantities(cost_item, product)
|
||||||
|
if quantity in assigned_quantities:
|
||||||
|
return product
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_products(cls, related_object_type: RELATED_OBJECT_TYPE) -> list[ifcopenshell.entity_instance]:
|
def get_products(cls, related_object_type: RELATED_OBJECT_TYPE) -> list[ifcopenshell.entity_instance]:
|
||||||
if related_object_type == "PRODUCT":
|
if related_object_type == "PRODUCT":
|
||||||
@@ -859,7 +868,33 @@ class Cost(bonsai.core.tool.Cost):
|
|||||||
bpy.ops.bim.connect_websocket_server(page="costing")
|
bpy.ops.bim.connect_websocket_server(page="costing")
|
||||||
cost_schedule_data = cls.create_cost_schedule_json(cost_chedule)
|
cost_schedule_data = cls.create_cost_schedule_json(cost_chedule)
|
||||||
tool.Web.send_webui_data(
|
tool.Web.send_webui_data(
|
||||||
data={"cost_items": cost_schedule_data, "cost_schedule_id": cost_chedule.id()},
|
data={
|
||||||
|
"cost_items": cost_schedule_data,
|
||||||
|
"cost_schedule_id": cost_chedule.id(),
|
||||||
|
"currency": cls.currency()
|
||||||
|
},
|
||||||
data_key="cost_items",
|
data_key="cost_items",
|
||||||
event="cost_items",
|
event="cost_items",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_cost_quantities(cls, cost_item: ifcopenshell.entity_instance) -> dict:
|
||||||
|
results = {
|
||||||
|
"quantities": [],
|
||||||
|
"unit_symbol": None,
|
||||||
|
}
|
||||||
|
if not cost_item:
|
||||||
|
return results
|
||||||
|
results["quantity_type"] = cost_item.CostQuantities[0].is_a() if cost_item.CostQuantities else None
|
||||||
|
unit = ifcopenshell.util.unit.get_property_unit(cost_item.CostQuantities[0], tool.Ifc.get()) if cost_item.CostQuantities else None
|
||||||
|
if unit:
|
||||||
|
results["unit_symbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||||
|
for quantity in cost_item.CostQuantities or []:
|
||||||
|
assigned_product = cls.get_assigned_product(cost_item, quantity)
|
||||||
|
info = quantity.get_info()
|
||||||
|
info["fromProduct"] = assigned_product.get_info(recursive=True) if assigned_product else None
|
||||||
|
results["quantities"].append(info)
|
||||||
|
if results["quantity_type"] == "IfcQuantityCount":
|
||||||
|
results["unit_symbol"] = "U"
|
||||||
|
return results
|
||||||
|
|
||||||
|
|||||||
@@ -344,52 +344,6 @@ class Web(bonsai.core.tool.Web):
|
|||||||
operator_data (dict): A dictionary containing the operator data.
|
operator_data (dict): A dictionary containing the operator data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def selection_data(cost_item, elements):
|
|
||||||
print(cost_item, elements)
|
|
||||||
if not elements:
|
|
||||||
return []
|
|
||||||
if not cost_item:
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
"info": {
|
|
||||||
"id": element.id(),
|
|
||||||
"name": element.Name,
|
|
||||||
"class": element.is_a(),
|
|
||||||
"type": get_type(element).Name if get_type(element) else None,
|
|
||||||
},
|
|
||||||
"qtos": get_psets(element, qtos_only=True),
|
|
||||||
}
|
|
||||||
for element in elements or []
|
|
||||||
]
|
|
||||||
data = []
|
|
||||||
for element in elements or []:
|
|
||||||
psets = get_psets(element, qtos_only=True)
|
|
||||||
element_type = get_type(element)
|
|
||||||
quantities, unit = tool.Cost.get_assigned_quantities(cost_item, element)
|
|
||||||
data.append(
|
|
||||||
{
|
|
||||||
"info": {
|
|
||||||
"id": element.id(),
|
|
||||||
"name": element.Name,
|
|
||||||
"class": element.is_a(),
|
|
||||||
"type": element_type.Name if element_type else None,
|
|
||||||
},
|
|
||||||
"qtos": psets,
|
|
||||||
"assigned_quantities": [
|
|
||||||
{
|
|
||||||
"cost_item_id": cost_item.id(),
|
|
||||||
"product_id": element.id(),
|
|
||||||
"name": q.Name,
|
|
||||||
"value": q[3],
|
|
||||||
"type": q.Unit,
|
|
||||||
}
|
|
||||||
for q in quantities or []
|
|
||||||
],
|
|
||||||
"unit": unit,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return data
|
|
||||||
|
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
if operator_data["type"] == "getPredefinedTypes":
|
if operator_data["type"] == "getPredefinedTypes":
|
||||||
print("getting predefined types")
|
print("getting predefined types")
|
||||||
@@ -425,31 +379,6 @@ class Web(bonsai.core.tool.Web):
|
|||||||
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
|
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
|
||||||
products = tool.Cost.get_cost_item_products(cost_item, is_deep=True)
|
products = tool.Cost.get_cost_item_products(cost_item, is_deep=True)
|
||||||
tool.Spatial.select_products(products, unhide=True)
|
tool.Spatial.select_products(products, unhide=True)
|
||||||
if operator_data["type"] == "enableEditingQuantities":
|
|
||||||
cost_item_id = operator_data["costItemId"]
|
|
||||||
cost_item = ifc_file.by_id(cost_item_id)
|
|
||||||
if not cost_item:
|
|
||||||
print("---> cost item not found")
|
|
||||||
return
|
|
||||||
|
|
||||||
selected_products = list(tool.Spatial.get_selected_products())
|
|
||||||
selected_products_data = selection_data(cost_item, selected_products)
|
|
||||||
|
|
||||||
assigned_products = tool.Cost.get_cost_item_products(cost_item, is_deep=False)
|
|
||||||
assigned_products_data = selection_data(cost_item, assigned_products)
|
|
||||||
|
|
||||||
names = ifcopenshell.util.cost.get_product_quantity_names(selected_products)
|
|
||||||
cls.send_webui_data(
|
|
||||||
data={
|
|
||||||
"selected_products": selected_products_data,
|
|
||||||
"assigned_products": assigned_products_data,
|
|
||||||
"product_quantity_names": names,
|
|
||||||
"cost_item_id": cost_item_id,
|
|
||||||
},
|
|
||||||
data_key="quantities",
|
|
||||||
event="quantities",
|
|
||||||
)
|
|
||||||
|
|
||||||
if operator_data["type"] == "addSummaryCostItem":
|
if operator_data["type"] == "addSummaryCostItem":
|
||||||
cost_schedule = ifc_file.by_id(operator_data["costScheduleId"])
|
cost_schedule = ifc_file.by_id(operator_data["costScheduleId"])
|
||||||
bonsai.core.cost.add_summary_cost_item(tool.Ifc, tool.Cost, cost_schedule=cost_schedule)
|
bonsai.core.cost.add_summary_cost_item(tool.Ifc, tool.Cost, cost_schedule=cost_schedule)
|
||||||
@@ -520,12 +449,109 @@ class Web(bonsai.core.tool.Web):
|
|||||||
prop_name = operator_data["propName"]
|
prop_name = operator_data["propName"]
|
||||||
if prop_name == "count":
|
if prop_name == "count":
|
||||||
prop_name = ""
|
prop_name = ""
|
||||||
print(prop_name)
|
|
||||||
bpy.ops.bim.assign_cost_item_quantity(
|
bpy.ops.bim.assign_cost_item_quantity(
|
||||||
cost_item=operator_data["costItemId"], related_object_type="PRODUCT", prop_name=prop_name
|
cost_item=operator_data["costItemId"], related_object_type="PRODUCT", prop_name=prop_name
|
||||||
)
|
)
|
||||||
cost_schedule = tool.Cost.get_cost_schedule(cost_item=tool.Ifc.get().by_id(operator_data["costItemId"]))
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item=tool.Ifc.get().by_id(operator_data["costItemId"]))
|
||||||
cls.load_cost_schedule_web_ui(cost_schedule)
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
|
if operator_data["type"] == "enableEditingQuantities":
|
||||||
|
cost_item_id = operator_data["costItemId"]
|
||||||
|
cost_item = ifc_file.by_id(cost_item_id)
|
||||||
|
cls.enableEditingCostItemQuantities(cost_item)
|
||||||
|
if operator_data["type"] == "AddCostItemQuantity":
|
||||||
|
cost_item_id = operator_data["costItemId"]
|
||||||
|
cost_item = ifc_file.by_id(cost_item_id)
|
||||||
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item)
|
||||||
|
bpy.ops.bim.add_cost_item_quantity(cost_item=cost_item_id, ifc_class=operator_data["ifcClass"])
|
||||||
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
|
cls.enableEditingCostItemQuantities(cost_item)
|
||||||
|
if operator_data["type"] == "editCostItemQuantity":
|
||||||
|
cost_item_id = operator_data["costItemId"]
|
||||||
|
cost_item = ifc_file.by_id(cost_item_id)
|
||||||
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item)
|
||||||
|
physical_quantity = tool.Ifc.get().by_id(operator_data["quantityId"])
|
||||||
|
attributes = operator_data["attributes"]
|
||||||
|
tool.Ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=attributes)
|
||||||
|
tool.Cost.load_cost_item_quantities(ifc_file.by_id(operator_data["costItemId"]))
|
||||||
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
|
cls.enableEditingCostItemQuantities(cost_item)
|
||||||
|
if operator_data["type"] == "deleteCostItemQuantity":
|
||||||
|
bpy.ops.bim.remove_cost_item_quantity(cost_item=operator_data["costItemId"], physical_quantity=operator_data["quantityId"])
|
||||||
|
cost_item_id = operator_data["costItemId"]
|
||||||
|
cost_item = ifc_file.by_id(cost_item_id)
|
||||||
|
cost_schedule = tool.Cost.get_cost_schedule(cost_item)
|
||||||
|
cls.load_cost_schedule_web_ui(cost_schedule)
|
||||||
|
cls.enableEditingCostItemQuantities(cost_item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def enableEditingCostItemQuantities(cls, cost_item):
|
||||||
|
if not cost_item:
|
||||||
|
return
|
||||||
|
selected_products = list(tool.Spatial.get_selected_products())
|
||||||
|
selected_products_data = cls.selection_data(cost_item, selected_products)
|
||||||
|
|
||||||
|
assigned_products = tool.Cost.get_cost_item_products(cost_item, is_deep=False)
|
||||||
|
assigned_products_data = cls.selection_data(cost_item, assigned_products)
|
||||||
|
|
||||||
|
names = ifcopenshell.util.cost.get_product_quantity_names(selected_products)
|
||||||
|
cls.send_webui_data(
|
||||||
|
data={
|
||||||
|
"selected_products": selected_products_data,
|
||||||
|
"assigned_products": assigned_products_data,
|
||||||
|
"product_quantity_names": names,
|
||||||
|
"cost_item_id": cost_item.id(),
|
||||||
|
"cost_quantities": tool.Cost.get_cost_quantities(cost_item),
|
||||||
|
},
|
||||||
|
data_key="quantities",
|
||||||
|
event="quantities",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def selection_data(cls, cost_item, elements):
|
||||||
|
if not elements:
|
||||||
|
return []
|
||||||
|
if not cost_item:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"id": element.id(),
|
||||||
|
"name": element.Name,
|
||||||
|
"class": element.is_a(),
|
||||||
|
"type": get_type(element).Name if get_type(element) else None,
|
||||||
|
},
|
||||||
|
"qtos": get_psets(element, qtos_only=True),
|
||||||
|
}
|
||||||
|
for element in elements or []
|
||||||
|
]
|
||||||
|
data = []
|
||||||
|
for element in elements or []:
|
||||||
|
psets = get_psets(element, qtos_only=True)
|
||||||
|
element_type = get_type(element)
|
||||||
|
quantities, unit = tool.Cost.get_assigned_quantities(cost_item, element)
|
||||||
|
data.append(
|
||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"id": element.id(),
|
||||||
|
"name": element.Name,
|
||||||
|
"class": element.is_a(),
|
||||||
|
"type": element_type.Name if element_type else None,
|
||||||
|
},
|
||||||
|
"qtos": psets,
|
||||||
|
"assigned_quantities": [
|
||||||
|
{
|
||||||
|
"cost_item_id": cost_item.id(),
|
||||||
|
"product_id": element.id(),
|
||||||
|
"name": q.Name,
|
||||||
|
"value": q[3],
|
||||||
|
"type": q.Unit,
|
||||||
|
}
|
||||||
|
for q in quantities or []
|
||||||
|
],
|
||||||
|
"unit": unit,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_cost_schedule_web_ui(cls, cost_schedule):
|
def load_cost_schedule_web_ui(cls, cost_schedule):
|
||||||
|
|||||||
Reference in New Issue
Block a user