#let table-header(..headers) = { table.header( ..headers.pos().map(h => strong(h)) ) } #let abrechnung(meta, days) = { set page(paper: "a4", margin: (x:1.5cm, y:2.25cm), footer:[#grid( columns: (3fr, .65fr), align: left + horizon, inset: .5em, [#meta.EmployeeName -- #meta.TimeRange], grid.cell(rowspan: 2)[#image("static/logo.png")], [Arbeitszeitrechnung maschinell erstellt am #meta.CurrentTimestamp], ) ]) set text(font: "Noto Sans", size:10pt, fill: luma(10%)) set table( stroke: 0.5pt + luma(10%), inset: .5em, align: center + horizon, ) show text: it => { if it.text == "0min"{ text(oklch(70.8%, 0, 0deg))[#it] }else if it.text.starts-with("-"){ text(red)[#it] }else{ it } } [= Abrechnung Arbeitszeit -- #meta.EmployeeName] [Zeitraum: #meta.TimeRange] table( columns: (1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1.25fr), fill: (x, y) => if y == 0 { oklch(87%, 0, 0deg) }, table-header( [Datum], [Kommen], [Gehen], [Arbeitsart], [Stunden], [Pause], [Überstunden] ), .. for day in days { ( [#day.Date], if day.DayParts.len() == 0{ table.cell(colspan: 3)[Keine Buchungen] }else if not day.DayParts.first().IsWorkDay{ table.cell(colspan: 3)[#day.DayParts.first().WorkType] } else { table.cell(colspan: 3, inset: 0em)[ #table( columns: (1fr, 1fr, 1fr), .. for Zeit in day.DayParts { ( [#Zeit.BookingFrom], [#Zeit.BookingTo], [#Zeit.WorkType], ) }, ) ] }, [#day.Worktime], [#day.Pausetime], [#day.Overtime], ) if day.IsFriday { ( table.cell(colspan: 7, fill: oklch(87%, 0, 0deg))[Wochenende], ) // note the trailing comma } } ) table( columns: (3fr, 1fr), align: right, inset: (x: .25em, y:.75em), stroke: none, table.hline(start: 0, end: 2, stroke: stroke(dash:"dashed", thickness:.5pt)), [Arbeitszeit :], table.cell(align: left)[#meta.WorkTime], [Überstunden :], table.cell(align: left)[#meta.Overtime], [Überstunden :],table.cell(align: left)[#meta.OvertimeTotal], table.hline(start: 0, end: 2), ) }