IfcCostSchedule PDF export with typst: fix bugs

Fixed a bug when a summary cost has no sum applied.
Added Currency in table header.
Cleanup.
Added guards for end summary.
This commit is contained in:
carlopav
2026-05-22 22:23:58 +02:00
committed by Thomas Krijnen
parent 3e9b947b3f
commit 11154baa39
@@ -26,7 +26,7 @@
#let bill_of_quantities_table = table( #let bill_of_quantities_table(currency: "") = table(
columns: (18mm,54mm, 12mm,12mm,12mm,12mm, 20mm, 20mm, 25mm), columns: (18mm,54mm, 12mm,12mm,12mm,12mm, 20mm, 20mm, 25mm),
rows: (6mm, 248mm), rows: (6mm, 248mm),
align: (center, left, center, center, center, center, center, center, center), align: (center, left, center, center, center, center, center, center, center),
@@ -36,12 +36,12 @@
top: 1pt, top: 1pt,
bottom: 1pt bottom: 1pt
), ),
[Hierarchy], [Description], [],[l],[w],[h/w], [Quantity], [Rate], [Total] [Hierarchy], [Description], [],[l],[w],[h/w], [Quantity], [Rate (#currency)], [Total (#currency)]
) )
#let schedule_of_rates_table = table( #let schedule_of_rates_table(currency: "") = table(
columns: (30mm,130mm, 25mm), columns: (30mm,130mm, 25mm),
rows: (6mm, 248mm), rows: (6mm, 248mm),
align: (center, left, center), align: (center, left, center),
@@ -51,12 +51,12 @@
top: 1pt, top: 1pt,
bottom: 1pt bottom: 1pt
), ),
[Identification], [Description], [Rate] [Identification], [Description], [Rate (#currency)]
) )
#let summary_table = table( #let summary_table(currency: "") = table(
columns: (18mm,107mm, 30mm, 30mm), columns: (18mm,107mm, 30mm, 30mm),
rows: (6mm, 248mm), rows: (6mm, 248mm),
align: (center, left, center, center, center, center, center, center, center), align: (center, left, center, center, center, center, center, center, center),
@@ -67,9 +67,9 @@
bottom: 1pt bottom: 1pt
), ),
text(size: 8pt)[Hierarchy], text(size: 8pt)[Hierarchy],
text(size: 8pt)[Description], text(size: 8pt)[Description],
text(size: 8pt)[Sub Total], text(size: 8pt)[Sub Total (#currency)],
text(size: 8pt)[Total] text(size: 8pt)[Total (#currency)]
) )
@@ -127,7 +127,6 @@
#let arrange_summary_row(row, options) = { #let arrange_summary_row(row, options) = {
let name = strong(upper(row.at("Name"))) let name = strong(upper(row.at("Name")))
let description = [#par(justify: true, text(8pt, row.at("Description", default: "")))] let description = [#par(justify: true, text(8pt, row.at("Description", default: "")))]
let total = if row.at("RateSubtotal") == "" {0.0} else {float(row.at("RateSubtotal"))}
if row.at("ItemIsASum") == "True" { if row.at("ItemIsASum") == "True" {
if row.at("Index") == "1" { if row.at("Index") == "1" {
// ROOT COST // ROOT COST
@@ -216,7 +215,8 @@
format-decimal(float(row.at("Quantity")))} format-decimal(float(row.at("Quantity")))}
let rate = if row.at("RateSubtotal") == "" {0.0} else { let rate = if row.at("RateSubtotal") == "" {0.0} else {
format-decimal(float(row.at("RateSubtotal")))} format-decimal(float(row.at("RateSubtotal")))}
let total = if row.at("Quantity") == "" {0.0} else { let total = if row.at("Quantity") == "" or row.at("RateSubtotal") == "" {
format-decimal(0.0, places: 2)} else {
format-decimal(float(row.at("Quantity")) * float(row.at("RateSubtotal")), places: 2)} format-decimal(float(row.at("Quantity")) * float(row.at("RateSubtotal")), places: 2)}
( (
@@ -281,18 +281,18 @@
#let arrange_schedule_of_rates_row(row, options) = { #let arrange_schedule_of_rates_row(row, options) = {
let name = strong(upper(row.at("Name"))) let name = strong(upper(row.at("Name")))
let description = [#par(justify: true, text(8pt, row.at("Description", default: "")))] let description = [#par(justify: true, text(8pt, row.at("Description", default: "")))]
let unit = table.cell(align: right)[#unit_map.at(row.at("Unit"), default: "")] let unit = table.cell(align: right + bottom)[#unit_map.at(row.at("Unit"), default: "")]
let rate = if row.at("RateSubtotal") == "" {0.0} else { let rate = if row.at("RateSubtotal") == "" {0.0} else {
format-decimal(float(row.at("RateSubtotal")))} format-decimal(float(row.at("RateSubtotal")))}
if row.at("ItemIsASum") == "True" {return ()} //skip sections in schedule of rates if row.at("ItemIsASum") == "True" {return ()} //skip sections in schedule of rates
( (
row.at("Identification"), row.at("Identification"),
if row.at("Identification") == "" {name + linebreak() + description} else {name + linebreak() + description}, name + linebreak() + description,
[] []
) )
( (
[], [],
table.cell(align: right+bottom)[#unit], unit,
table.cell(align: right+bottom)[#rate], table.cell(align: right+bottom)[#rate],
) )
( (
@@ -342,8 +342,12 @@
) = { ) = {
let data = csv(path, delimiter: delimiter, row-type: dictionary) let data = csv(path, delimiter: delimiter, row-type: dictionary)
let new_rows = data.map(item => arrange_summary_row(item, options)) let new_rows = data.map(item => arrange_summary_row(item, options))
let general_total = data.filter(row => row.at("ItemIsASum") == "False") let general_total = data.filter(row => row.at("ItemIsASum") == "False")
.map(row => float(row.at("RateSubtotal", default: 0.0))*float(row.at("Quantity", default: 0.0))) .map(row => {
let qty = if row.at("Quantity", default: "") == "" { 0.0 } else { float(row.at("Quantity")) }
let rate = if row.at("RateSubtotal", default: "") == "" { 0.0 } else { float(row.at("RateSubtotal")) }
qty * rate
})
.sum(default: 0.00) .sum(default: 0.00)
set text(size: 10pt) set text(size: 10pt)
@@ -477,9 +481,9 @@
[#counter(page).display("1/1", both: true)] [#counter(page).display("1/1", both: true)]
) )
], ],
background: background:
place( top + left, dx: 15mm, dy: 25mm, place( top + left, dx: 15mm, dy: 25mm,
format_table.at(schedule_type, default: bill_of_quantities_table) (format_table.at(schedule_type, default: bill_of_quantities_table))(currency: project_currency)
) )
) )
@@ -522,9 +526,9 @@
set page( set page(
background: background:
place( top + left, dx: 15mm, dy: 25mm, place( top + left, dx: 15mm, dy: 25mm,
format_table.at("SUMMARY") (format_table.at("SUMMARY"))(currency: project_currency)
) )
) )
create-summary(schedule_path, options) create-summary(schedule_path, options)
} }
} }