mirror of
https://github.com/Garmelon/Arbeitszeitdokumentationsgenerator.git
synced 2026-04-12 00:35:04 +02:00
Add and document typst template
This commit is contained in:
parent
3a687cdb36
commit
2131c2fb0d
4 changed files with 562 additions and 0 deletions
|
|
@ -5,3 +5,12 @@ inspired by the [SDQ TimeSheetGenerator][tsg] and a friend's very useful wrapper
|
|||
website that spits out PDFs directly.
|
||||
|
||||
[tsg]: https://github.com/kit-sdq/TimeSheetGenerator
|
||||
|
||||
## Typst template
|
||||
|
||||
The centerpiece of the repository is a [typst][typst] template that generates
|
||||
and validates time sheets. It aims to mirror the look of the official form as
|
||||
closely as possible. For usage information, see [its docs][tdocs].
|
||||
|
||||
[typst]: https://github.com/typst/typst
|
||||
[tdocs]: kit_timesheet.md
|
||||
|
|
|
|||
47
kit_logo.svg
Normal file
47
kit_logo.svg
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 196.18 90.32" width="196.18px" height="90.32px" x="0px" y="0px" role="img" aria-labelledby="kit-logo-alt-title">
|
||||
<title id="kit-logo-alt-title">KIT - Karlsruher Institut für Technologie</title>
|
||||
<defs>
|
||||
<style>.cls-1,.cls-2,.cls-3{fill-rule:evenodd;}.cls-2{fill:#fff;}.cls-3{fill:#009682;}</style></defs>
|
||||
<path class="cls-1" d="M195.75.47h-46.3V16.23H163V70.59h19.22V16.23h13.54V.47ZM144.84,70.52H125.62V.47h19.22V70.52Zm-22.46,0H101L75.58,43.23V27.61L100.2.39h22L93.87,35.67l28.51,34.85Z" transform="translate(0 -0.39)"></path>
|
||||
<polygon class="cls-2" points="70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13"></polygon>
|
||||
<polygon class="cls-2" points="70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13"></polygon>
|
||||
<path class="cls-3" d="M16.18,26.1A68.63,68.63,0,0,0,6.89,40.71L71,70.52,16.18,26.1Z" transform="translate(0 -0.39)"></path>
|
||||
<path class="cls-3" d="M71,70.52,41.09,7A69.32,69.32,0,0,0,26.4,16L71,70.52Z" transform="translate(0 -0.39)"></path>
|
||||
<path class="cls-1" d="M71,70.52,71,.47h-.58a70.22,70.22,0,0,0-16.55,2L71,70.52Z" transform="translate(0 -0.39)"></path>
|
||||
<polygon class="cls-2" points="70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13"></polygon>
|
||||
<polygon class="cls-2" points="70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13 70.97 70.13"></polygon>
|
||||
<path class="cls-3" d="M2.28,53.38A70.75,70.75,0,0,0,.05,70.52H71L2.28,53.38Z" transform="translate(0 -0.39)"></path>
|
||||
<g class="logo-text">
|
||||
<polygon points="0 87.54 1.25 87.54 1.25 83.28 5.13 87.54 6.95 87.54 2.53 82.95 6.63 78.83 4.97 78.83 1.25 82.67 1.25 78.83 0 78.83 0 87.54 0 87.54"></polygon>
|
||||
<path d="M8.44,83A2.47,2.47,0,0,1,9,82.68a2.87,2.87,0,0,1,.64-.23,3,3,0,0,1,.67-.08,1.88,1.88,0,0,1,.92.19,1,1,0,0,1,.5.55,2.5,2.5,0,0,1,.15.92H10.61a5.19,5.19,0,0,0-.77.06,4.2,4.2,0,0,0-.79.22,2.55,2.55,0,0,0-.71.38,2.05,2.05,0,0,0-.5.6,2,2,0,0,0-.19.83A2.12,2.12,0,0,0,8,87.23a1.71,1.71,0,0,0,.82.65,3.25,3.25,0,0,0,1.22.21,2.66,2.66,0,0,0,.72-.1,2.2,2.2,0,0,0,.56-.24,2.89,2.89,0,0,0,.42-.32,3.29,3.29,0,0,0,.27-.31h0v.82h1.1a1.47,1.47,0,0,1,0-.25,3.9,3.9,0,0,1,0-.4c0-.16,0-.36,0-.6V84.06a3.5,3.5,0,0,0-.26-1.46,1.78,1.78,0,0,0-.83-.88,3.09,3.09,0,0,0-1.47-.3,4.11,4.11,0,0,0-1.13.17,4.19,4.19,0,0,0-1,.43l.06,1Zm3.42,2.45a1.81,1.81,0,0,1-.2.85,1.57,1.57,0,0,1-.6.6,2,2,0,0,1-1,.22,1.23,1.23,0,0,1-.42-.07,1.5,1.5,0,0,1-.38-.19A1,1,0,0,1,9,86.55a.94.94,0,0,1-.11-.45.88.88,0,0,1,.17-.55,1.22,1.22,0,0,1,.42-.36,2.27,2.27,0,0,1,.57-.2,3.05,3.05,0,0,1,.57-.09,4.36,4.36,0,0,1,.48,0h.37l.38,0v.56Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M14.94,87.94h1.18V85a4.09,4.09,0,0,1,.21-1.38,2,2,0,0,1,.6-.89,1.38,1.38,0,0,1,.92-.32,1.77,1.77,0,0,1,.32,0,2,2,0,0,1,.33.09v-1l-.23-.05-.25,0-.29,0a1.76,1.76,0,0,0-.67.15,2,2,0,0,0-.57.4,1.87,1.87,0,0,0-.42.59h0v-1h-1.1v6.37Z" transform="translate(0 -0.39)"></path>
|
||||
<polygon points="19.87 87.54 21.05 87.54 21.05 78.19 19.87 78.19 19.87 87.54 19.87 87.54"></polygon>
|
||||
<path d="M22.58,87.74a2.93,2.93,0,0,0,.85.28,5.46,5.46,0,0,0,.9.07,2.87,2.87,0,0,0,.83-.12,2.27,2.27,0,0,0,.71-.36,1.75,1.75,0,0,0,.49-.62,1.91,1.91,0,0,0,.19-.89,1.61,1.61,0,0,0-.15-.73,1.58,1.58,0,0,0-.4-.52,3.29,3.29,0,0,0-.53-.36l-.59-.28a4.54,4.54,0,0,1-.54-.26,1.31,1.31,0,0,1-.39-.34.8.8,0,0,1-.16-.46.71.71,0,0,1,.16-.44.89.89,0,0,1,.41-.25,1.64,1.64,0,0,1,.56-.09l.27,0,.35.07.35.11a1.06,1.06,0,0,1,.26.12l.1-1c-.23-.07-.46-.12-.69-.17a4.66,4.66,0,0,0-.77-.08,2.8,2.8,0,0,0-.88.13,1.91,1.91,0,0,0-.71.36,1.52,1.52,0,0,0-.48.61,2.06,2.06,0,0,0-.17.86,1.34,1.34,0,0,0,.15.66,1.69,1.69,0,0,0,.39.49,5.07,5.07,0,0,0,.54.36l.59.29.54.29a1.62,1.62,0,0,1,.39.33.64.64,0,0,1,.15.43.79.79,0,0,1-.39.71,1.32,1.32,0,0,1-.39.15,1.48,1.48,0,0,1-.41,0,2.58,2.58,0,0,1-.43,0,3.06,3.06,0,0,1-.53-.15,2.59,2.59,0,0,1-.5-.25l-.07,1Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M27.9,87.94h1.17V85a4.34,4.34,0,0,1,.22-1.38,1.9,1.9,0,0,1,.6-.89,1.38,1.38,0,0,1,.92-.32,1.77,1.77,0,0,1,.32,0,2,2,0,0,1,.33.09v-1l-.23-.05-.25,0-.3,0a1.76,1.76,0,0,0-.66.15,2.23,2.23,0,0,0-.58.4,2.26,2.26,0,0,0-.42.59h0v-1H27.9v6.37Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M38.31,81.57H37.14v3A4.35,4.35,0,0,1,37,85.58a2.31,2.31,0,0,1-.35.83,1.72,1.72,0,0,1-.6.54,1.8,1.8,0,0,1-.87.19,1.31,1.31,0,0,1-.8-.29A1.37,1.37,0,0,1,34,86.1a4.37,4.37,0,0,1-.12-1.23v-3.3H32.68v3.79a3.94,3.94,0,0,0,.25,1.46,1.93,1.93,0,0,0,.79.94,2.5,2.5,0,0,0,1.35.33,2.7,2.7,0,0,0,.85-.14,2.24,2.24,0,0,0,.73-.4,1.76,1.76,0,0,0,.5-.62h0v1h1.13V81.57Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M40.23,87.94h1.18V85a4.93,4.93,0,0,1,.11-1.06,2.84,2.84,0,0,1,.35-.83,1.67,1.67,0,0,1,.61-.54,2.06,2.06,0,0,1,.86-.19,1.34,1.34,0,0,1,.81.29,1.44,1.44,0,0,1,.42.75,5.32,5.32,0,0,1,.12,1.23v3.3h1.17V84.16a3.62,3.62,0,0,0-.25-1.47,2,2,0,0,0-.78-.94,2.69,2.69,0,0,0-1.35-.33,2.75,2.75,0,0,0-1.2.28,2.19,2.19,0,0,0-.85.78h0v-3.9H40.23v9.36Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M52.67,86.5a2,2,0,0,1-.32.2,3.63,3.63,0,0,1-.48.21,3.41,3.41,0,0,1-.57.16,2.44,2.44,0,0,1-.59.07,2.15,2.15,0,0,1-.8-.15,2.29,2.29,0,0,1-.67-.44,2.1,2.1,0,0,1-.46-.67,2.2,2.2,0,0,1-.17-.86h4.54A5.88,5.88,0,0,0,53,83.59a3.41,3.41,0,0,0-.46-1.14,2.26,2.26,0,0,0-.87-.75,3.11,3.11,0,0,0-1.33-.28,3.21,3.21,0,0,0-1.23.24,2.74,2.74,0,0,0-.94.67,2.87,2.87,0,0,0-.6,1,4.05,4.05,0,0,0-.22,1.29,4.87,4.87,0,0,0,.2,1.42,2.85,2.85,0,0,0,.6,1.08,2.59,2.59,0,0,0,1,.7,3.87,3.87,0,0,0,1.47.25,5.64,5.64,0,0,0,.89-.07,5.74,5.74,0,0,0,.63-.16l.49-.18V86.5Zm-4.06-2.36a2.9,2.9,0,0,1,.14-.62,2,2,0,0,1,.32-.57,1.51,1.51,0,0,1,.51-.42,1.84,1.84,0,0,1,.74-.16,1.74,1.74,0,0,1,.67.14,1.36,1.36,0,0,1,.5.38,1.57,1.57,0,0,1,.31.57,2.15,2.15,0,0,1,.11.68Z" transform="translate(0 -0.39)"></path> <path d="M54.65,87.94h1.18V85A4.09,4.09,0,0,1,56,83.66a2,2,0,0,1,.61-.89,1.33,1.33,0,0,1,.91-.32,1.77,1.77,0,0,1,.32,0,2,2,0,0,1,.33.09v-1L58,81.46l-.24,0-.3,0a1.76,1.76,0,0,0-.67.15,2,2,0,0,0-.57.4,1.87,1.87,0,0,0-.42.59h0v-1h-1.1v6.37Z" transform="translate(0 -0.39)"></path>
|
||||
<polygon points="63.02 87.54 64.26 87.54 64.26 78.83 63.02 78.83 63.02 87.54 63.02 87.54"></polygon>
|
||||
<path d="M66.3,87.94h1.17V85a4.35,4.35,0,0,1,.12-1.06,2.39,2.39,0,0,1,.35-.83,1.72,1.72,0,0,1,.6-.54,2.14,2.14,0,0,1,.87-.19,1.28,1.28,0,0,1,.8.29,1.37,1.37,0,0,1,.42.75,4.78,4.78,0,0,1,.13,1.23v3.3h1.17V84.16a3.8,3.8,0,0,0-.25-1.47,2,2,0,0,0-.78-.94,2.76,2.76,0,0,0-1.36-.33,2.7,2.7,0,0,0-.85.14A2.58,2.58,0,0,0,68,82a1.88,1.88,0,0,0-.5.62h0v-1H66.3v6.37Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M73.32,87.74a2.83,2.83,0,0,0,.85.28,5.26,5.26,0,0,0,.89.07,2.82,2.82,0,0,0,.83-.12,2.27,2.27,0,0,0,.71-.36,1.9,1.9,0,0,0,.5-.62,2,2,0,0,0,.18-.89,1.61,1.61,0,0,0-.15-.73,1.69,1.69,0,0,0-.39-.52,3.38,3.38,0,0,0-.54-.36l-.59-.28c-.19-.08-.37-.17-.54-.26a1.45,1.45,0,0,1-.39-.34.73.73,0,0,1-.15-.46.65.65,0,0,1,.15-.44,1,1,0,0,1,.41-.25,1.69,1.69,0,0,1,.56-.09l.27,0,.35.07a2.89,2.89,0,0,1,.35.11,1.14,1.14,0,0,1,.27.12l.1-1c-.23-.07-.47-.12-.7-.17a4.55,4.55,0,0,0-.77-.08,2.8,2.8,0,0,0-.88.13,1.91,1.91,0,0,0-.71.36,1.72,1.72,0,0,0-.48.61,2.06,2.06,0,0,0-.17.86,1.34,1.34,0,0,0,.15.66,1.57,1.57,0,0,0,.4.49,4.1,4.1,0,0,0,.53.36l.59.29c.2.1.38.19.54.29a1.44,1.44,0,0,1,.39.33.71.71,0,0,1,.16.43.83.83,0,0,1-.12.43.75.75,0,0,1-.28.28,1.32,1.32,0,0,1-.39.15,1.48,1.48,0,0,1-.41,0,2.58,2.58,0,0,1-.43,0,2.74,2.74,0,0,1-.52-.15,3,3,0,0,1-.51-.25l-.06,1Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M82,81.57H80.33V79.73l-1.17.37v1.47H77.72v1h1.44v3.72a2.07,2.07,0,0,0,.24,1.05,1.45,1.45,0,0,0,.67.6,2.55,2.55,0,0,0,1.05.2,2.35,2.35,0,0,0,.54-.06l.46-.12v-1a1.7,1.7,0,0,1-.33.15,1.37,1.37,0,0,1-.47.07,1,1,0,0,1-.5-.13.92.92,0,0,1-.35-.38,1.33,1.33,0,0,1-.14-.63V82.52H82v-1Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M83.57,87.94h1.17V81.57H83.57v6.37Zm1.24-9.11H83.49v1.32h1.32V78.83Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M90.19,81.57H88.51V79.73l-1.17.37v1.47H85.9v1h1.44v3.72a2.07,2.07,0,0,0,.24,1.05,1.4,1.4,0,0,0,.67.6,2.55,2.55,0,0,0,1,.2,2.42,2.42,0,0,0,.54-.06l.45-.12v-1a1.43,1.43,0,0,1-.79.22A.93.93,0,0,1,89,87a.86.86,0,0,1-.35-.38,1.33,1.33,0,0,1-.14-.63V82.52h1.68v-1Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M97.22,81.57H96.05v3a4.35,4.35,0,0,1-.12,1.06,2.31,2.31,0,0,1-.35.83A1.72,1.72,0,0,1,95,87a1.8,1.8,0,0,1-.87.19,1.33,1.33,0,0,1-.8-.29,1.37,1.37,0,0,1-.42-.75,4.37,4.37,0,0,1-.12-1.23v-3.3H91.59v3.79a3.94,3.94,0,0,0,.25,1.46,2,2,0,0,0,.79.94,2.5,2.5,0,0,0,1.35.33,2.7,2.7,0,0,0,.85-.14,2.24,2.24,0,0,0,.73-.4,1.76,1.76,0,0,0,.5-.62h0v1h1.13V81.57Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M102.53,81.57h-1.69V79.73l-1.17.37v1.47H98.23v1h1.44v3.72a2.07,2.07,0,0,0,.24,1.05,1.45,1.45,0,0,0,.67.6,2.59,2.59,0,0,0,1,.2,2.35,2.35,0,0,0,.54-.06l.46-.12v-1a1.7,1.7,0,0,1-.33.15,1.33,1.33,0,0,1-.47.07,1,1,0,0,1-.5-.13,1,1,0,0,1-.35-.38,1.33,1.33,0,0,1-.14-.63V82.52h1.69v-1Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M108.16,87.94h1.17V82.52H111v-1h-1.69v-.68a3.7,3.7,0,0,1,0-.6,1.23,1.23,0,0,1,.15-.48.81.81,0,0,1,.34-.32,1.48,1.48,0,0,1,.61-.11h.3l.26.06,0-1-.37,0-.42,0a2.69,2.69,0,0,0-1.06.18,1.49,1.49,0,0,0-.66.52,2.13,2.13,0,0,0-.32.8,5,5,0,0,0-.09,1.06v.58h-1.5v1h1.5v5.42Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M113.39,80.6h1.17V79.35h-1.17V80.6Zm2,0h1.17V79.35h-1.17V80.6Zm2.37,1h-1.18v3a4.37,4.37,0,0,1-.11,1.06,2.5,2.5,0,0,1-.35.83,1.67,1.67,0,0,1-.61.54,1.79,1.79,0,0,1-.86.19,1.34,1.34,0,0,1-.81-.29,1.51,1.51,0,0,1-.42-.75,5.26,5.26,0,0,1-.12-1.23v-3.3h-1.17v3.79a3.75,3.75,0,0,0,.25,1.46,2,2,0,0,0,.78.94,2.5,2.5,0,0,0,1.35.33,2.72,2.72,0,0,0,.86-.14,2.47,2.47,0,0,0,.73-.4,1.88,1.88,0,0,0,.5-.62h0v1h1.14V81.57Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M119.73,87.94h1.17V85a4.34,4.34,0,0,1,.22-1.38,1.9,1.9,0,0,1,.6-.89,1.38,1.38,0,0,1,.92-.32,1.77,1.77,0,0,1,.32,0,2,2,0,0,1,.33.09v-1l-.24-.05-.24,0-.3,0a1.76,1.76,0,0,0-.66.15,2.23,2.23,0,0,0-.58.4,2.26,2.26,0,0,0-.42.59h0v-1h-1.1v6.37Z" transform="translate(0 -0.39)"></path>
|
||||
<polygon points="129.83 87.54 131.08 87.54 131.08 79.93 133.78 79.93 133.78 78.83 127.12 78.83 127.12 79.93 129.83 79.93 129.83 87.54 129.83 87.54"></polygon>
|
||||
<path d="M138.8,86.5a1.52,1.52,0,0,1-.31.2,3.63,3.63,0,0,1-.48.21,3.41,3.41,0,0,1-.57.16,2.46,2.46,0,0,1-.6.07,2.09,2.09,0,0,1-.79-.15,2.16,2.16,0,0,1-.67-.44,2,2,0,0,1-.46-.67,2.2,2.2,0,0,1-.17-.86h4.54a7.08,7.08,0,0,0-.14-1.43,3.23,3.23,0,0,0-.47-1.14,2.23,2.23,0,0,0-.86-.75,3.13,3.13,0,0,0-1.34-.28,3.16,3.16,0,0,0-1.22.24,2.65,2.65,0,0,0-.94.67,2.75,2.75,0,0,0-.61,1,3.79,3.79,0,0,0-.21,1.29,4.56,4.56,0,0,0,.2,1.42,2.85,2.85,0,0,0,.6,1.08,2.52,2.52,0,0,0,1,.7,3.81,3.81,0,0,0,1.46.25,4.67,4.67,0,0,0,1.53-.23l.48-.18V86.5Zm-4.05-2.36a2.9,2.9,0,0,1,.14-.62,1.78,1.78,0,0,1,.32-.57,1.51,1.51,0,0,1,.51-.42,1.8,1.8,0,0,1,.74-.16,1.74,1.74,0,0,1,.67.14,1.45,1.45,0,0,1,.5.38,1.74,1.74,0,0,1,.31.57,2.15,2.15,0,0,1,.1.68Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M144.93,81.67l-.41-.12a4,4,0,0,0-.5-.09,4.75,4.75,0,0,0-.51,0,3.75,3.75,0,0,0-1.33.25,3.06,3.06,0,0,0-1,.69,3,3,0,0,0-.66,1.06,3.59,3.59,0,0,0-.23,1.34,3.73,3.73,0,0,0,.23,1.29,3.2,3.2,0,0,0,.67,1,3.05,3.05,0,0,0,1,.72,3.35,3.35,0,0,0,1.32.27l.46,0a3,3,0,0,0,.51-.08,2.24,2.24,0,0,0,.5-.16l-.08-1a3,3,0,0,1-.4.2,3.6,3.6,0,0,1-.44.12,2.15,2.15,0,0,1-.43,0,2.24,2.24,0,0,1-.8-.14,1.69,1.69,0,0,1-.59-.37,1.93,1.93,0,0,1-.41-.54,2.54,2.54,0,0,1-.24-.65,3.23,3.23,0,0,1-.08-.68,2.91,2.91,0,0,1,.14-.89,2.41,2.41,0,0,1,.4-.76,1.87,1.87,0,0,1,.64-.54,2,2,0,0,1,.87-.2,2.72,2.72,0,0,1,.61.07,2.81,2.81,0,0,1,.66.21l.1-1Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M146.26,87.94h1.17V85a4.93,4.93,0,0,1,.11-1.06,2.61,2.61,0,0,1,.36-.83,1.64,1.64,0,0,1,.6-.54,2.07,2.07,0,0,1,.87-.19,1.33,1.33,0,0,1,.8.29,1.37,1.37,0,0,1,.42.75,4.82,4.82,0,0,1,.12,1.23v3.3h1.18V84.16a3.79,3.79,0,0,0-.26-1.47,1.91,1.91,0,0,0-.78-.94,2.8,2.8,0,0,0-2.54,0,2.13,2.13,0,0,0-.85.78h0v-3.9h-1.17v9.36Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M153.81,87.94H155V85a4.35,4.35,0,0,1,.12-1.06,2.39,2.39,0,0,1,.35-.83,1.72,1.72,0,0,1,.6-.54,2.1,2.1,0,0,1,.87-.19,1.28,1.28,0,0,1,.8.29,1.37,1.37,0,0,1,.42.75,4.78,4.78,0,0,1,.13,1.23v3.3h1.17V84.16a3.8,3.8,0,0,0-.25-1.47,1.93,1.93,0,0,0-.79-.94,2.82,2.82,0,0,0-2.2-.19,2.58,2.58,0,0,0-.73.4,1.88,1.88,0,0,0-.5.62h0v-1h-1.14v6.37Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M160.94,84.76a3.73,3.73,0,0,0,.23,1.29,3.2,3.2,0,0,0,.67,1,3.05,3.05,0,0,0,1,.72,3.35,3.35,0,0,0,1.32.27,3.26,3.26,0,0,0,1.31-.27,2.86,2.86,0,0,0,1-.72,3.2,3.2,0,0,0,.67-1,4,4,0,0,0,.24-1.29,3.8,3.8,0,0,0-.24-1.34,3.09,3.09,0,0,0-.65-1.06,3.21,3.21,0,0,0-1-.69,3.65,3.65,0,0,0-1.32-.25,3.75,3.75,0,0,0-1.33.25,3.28,3.28,0,0,0-1,.69,3.12,3.12,0,0,0-.66,1.06,3.8,3.8,0,0,0-.23,1.34Zm1.25,0a2.9,2.9,0,0,1,.13-.89,2.6,2.6,0,0,1,.39-.76,1.8,1.8,0,0,1,1.47-.74,1.89,1.89,0,0,1,.84.2,2,2,0,0,1,.62.54,2.6,2.6,0,0,1,.39.76,2.91,2.91,0,0,1,.14.89,3.23,3.23,0,0,1-.08.68,2.52,2.52,0,0,1-.23.65,2.07,2.07,0,0,1-.39.54,1.62,1.62,0,0,1-.55.37,1.91,1.91,0,0,1-.74.14,2,2,0,0,1-.74-.14,1.76,1.76,0,0,1-.56-.37,2.07,2.07,0,0,1-.39-.54,3,3,0,0,1-.23-.65,3.21,3.21,0,0,1-.07-.68Z" transform="translate(0 -0.39)"></path>
|
||||
<polygon points="169.06 87.54 170.24 87.54 170.24 78.19 169.06 78.19 169.06 87.54 169.06 87.54"></polygon>
|
||||
<path d="M171.89,84.76a3.73,3.73,0,0,0,.23,1.29,3.2,3.2,0,0,0,.67,1,3.05,3.05,0,0,0,1,.72,3.35,3.35,0,0,0,1.32.27,3.26,3.26,0,0,0,1.31-.27,2.86,2.86,0,0,0,1-.72,3.2,3.2,0,0,0,.67-1,3.73,3.73,0,0,0,.23-1.29,3.59,3.59,0,0,0-.23-1.34,3,3,0,0,0-.66-1.06,3.06,3.06,0,0,0-1-.69,3.65,3.65,0,0,0-1.32-.25,3.75,3.75,0,0,0-1.33.25,3.17,3.17,0,0,0-1,.69,3,3,0,0,0-.66,1.06,3.8,3.8,0,0,0-.23,1.34Zm1.24,0a2.91,2.91,0,0,1,.14-.89,2.6,2.6,0,0,1,.39-.76,1.93,1.93,0,0,1,.63-.54,1.89,1.89,0,0,1,.84-.2,1.93,1.93,0,0,1,.84.2,2,2,0,0,1,.62.54,2.6,2.6,0,0,1,.39.76,2.91,2.91,0,0,1,.14.89,3.23,3.23,0,0,1-.08.68,2.52,2.52,0,0,1-.23.65,2.07,2.07,0,0,1-.39.54,1.62,1.62,0,0,1-.55.37,2,2,0,0,1-1.48,0,1.66,1.66,0,0,1-.56-.37,2.07,2.07,0,0,1-.39-.54,3,3,0,0,1-.23-.65,3.23,3.23,0,0,1-.08-.68Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M185.49,81.57h-1.08v1h0a1.85,1.85,0,0,0-.45-.59,1.78,1.78,0,0,0-.68-.37,3.06,3.06,0,0,0-1-.14,2.82,2.82,0,0,0-1,.2,2.69,2.69,0,0,0-.8.52,3,3,0,0,0-.56.77,3.62,3.62,0,0,0-.34.92,4.6,4.6,0,0,0-.11,1,4.16,4.16,0,0,0,.17,1.18,3,3,0,0,0,.51,1,2.4,2.4,0,0,0,.85.69,2.75,2.75,0,0,0,1.19.26,3.09,3.09,0,0,0,.8-.11,2.58,2.58,0,0,0,.74-.33,2.06,2.06,0,0,0,.59-.55h0v.63a2.58,2.58,0,0,1-.24,1.16,1.64,1.64,0,0,1-.67.75,2,2,0,0,1-1.06.27,5.6,5.6,0,0,1-.6,0,5.12,5.12,0,0,1-.54-.1l-.54-.18-.62-.26-.12,1.08a7.74,7.74,0,0,0,.89.27,4.17,4.17,0,0,0,.73.13,4.87,4.87,0,0,0,.66,0,4.24,4.24,0,0,0,1.87-.36,2.3,2.3,0,0,0,1.07-1,3.6,3.6,0,0,0,.34-1.65V81.57Zm-3,5.42a1.5,1.5,0,0,1-.74-.18,1.76,1.76,0,0,1-.56-.49,2.41,2.41,0,0,1-.35-.72,3,3,0,0,1-.12-.86,3,3,0,0,1,.07-.66,2.4,2.4,0,0,1,.21-.64,2.06,2.06,0,0,1,.35-.55,1.7,1.7,0,0,1,.5-.38,1.63,1.63,0,0,1,.64-.14,1.81,1.81,0,0,1,.82.2,1.83,1.83,0,0,1,.58.53,2.45,2.45,0,0,1,.34.79,3.56,3.56,0,0,1,.12.94,2.55,2.55,0,0,1-.14.84,2.31,2.31,0,0,1-.38.69,1.78,1.78,0,0,1-.59.46,1.66,1.66,0,0,1-.75.17Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M187.57,87.94h1.17V81.57h-1.17v6.37Zm1.24-9.11h-1.32v1.32h1.32V78.83Z" transform="translate(0 -0.39)"></path>
|
||||
<path d="M195.69,86.5a1.64,1.64,0,0,1-.32.2,3.13,3.13,0,0,1-.48.21,3.58,3.58,0,0,1-.56.16,2.46,2.46,0,0,1-.6.07,2.09,2.09,0,0,1-.79-.15,2.16,2.16,0,0,1-.67-.44,2,2,0,0,1-.46-.67,2.2,2.2,0,0,1-.17-.86h4.54a7.08,7.08,0,0,0-.14-1.43,3.43,3.43,0,0,0-.47-1.14,2.19,2.19,0,0,0-.87-.75,3.08,3.08,0,0,0-1.33-.28,3.25,3.25,0,0,0-1.23.24,2.7,2.7,0,0,0-.93.67,2.75,2.75,0,0,0-.61,1,3.79,3.79,0,0,0-.21,1.29,4.88,4.88,0,0,0,.19,1.42,3,3,0,0,0,.61,1.08,2.55,2.55,0,0,0,1,.7,3.9,3.9,0,0,0,1.47.25,5.44,5.44,0,0,0,.89-.07,6,6,0,0,0,.64-.16l.48-.18V86.5Zm-4.05-2.36a2.4,2.4,0,0,1,.14-.62,2,2,0,0,1,.31-.57,1.66,1.66,0,0,1,.52-.42,1.8,1.8,0,0,1,.74-.16,1.78,1.78,0,0,1,.67.14,1.45,1.45,0,0,1,.5.38,1.55,1.55,0,0,1,.3.57,2.15,2.15,0,0,1,.11.68Z" transform="translate(0 -0.39)"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 16 KiB |
132
kit_timesheet.md
Normal file
132
kit_timesheet.md
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# The `kit_timesheet` typst module
|
||||
|
||||
This file exhaustively documents the `kit_timesheet` typst module and its
|
||||
exported values.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```typst
|
||||
#import "kit_timesheet.typ" as ts
|
||||
|
||||
#ts.timesheet(
|
||||
name: [McStudentface, Student],
|
||||
staff_id: 1337420,
|
||||
department: [Institut für Informatik],
|
||||
working_area: ts.areas.Großforschung,
|
||||
monthly_hours: 40,
|
||||
hourly_wage: [14.09],
|
||||
carry_prev_month: "02:30",
|
||||
year: 2024,
|
||||
month: 1,
|
||||
ts.entry("Urlaub", 2, "12:00", "14:00", note: ts.notes.Urlaub),
|
||||
ts.entry("Mate trinken", 3, "14:00", "15:30"),
|
||||
ts.entry("Im Bett liegen", 4, "10:00", "12:00"),
|
||||
ts.entry("Stundenzettel ausfüllen", 31, "14:00", "15:30", rest: "01:00"),
|
||||
)
|
||||
```
|
||||
|
||||
## Times and durations
|
||||
|
||||
Some function arguments represent a time of day or a duration. They expect a
|
||||
string of the form `HH:MM` or `-HH:MM`, e.g. `12:34`. More specifically, the
|
||||
string must match the regex `^-?[0-9]+:[0-5][0-9]$`.
|
||||
|
||||
This makes it possible to specify negative durations, which can be useful if you
|
||||
didn't work enough hours last month and want to specify a negative
|
||||
`carry_last_month` value.
|
||||
|
||||
A time of day is just a duration starting from midnight (00:00).
|
||||
|
||||
## Members
|
||||
|
||||
### `areas` (dictionary)
|
||||
|
||||
The `areas` dict contains the enum-like string values accepted by the
|
||||
`working_area` parameter of the `timesheet` function. See its documentation for
|
||||
more detail.
|
||||
|
||||
Available entries are:
|
||||
|
||||
- `areas.Großforschung` (value: `"GF"`)
|
||||
- `areas.Unibereich` (value: `"UB"`)
|
||||
|
||||
### `notes` (dictionary)
|
||||
|
||||
The `notes` dict contains the enum-like string values accepted by the `note`
|
||||
parameter of the `entry` function. See its documentation as well as footnote 1
|
||||
in the generated document for more detail.
|
||||
|
||||
Available entries are:
|
||||
|
||||
- `notes.Urlaub` (value: `"U"`)
|
||||
- `notes.Krankheit` (value: `"K"`)
|
||||
- `notes.Feiertage` (value: `"F"`)
|
||||
- `notes.Sonstiges` (value: `"S"`)
|
||||
|
||||
### `entry` (function)
|
||||
|
||||
Create a single entry for the `entries` parameter of the `timesheet` function.
|
||||
An entry corresponds exactly to a row in the resulting document.
|
||||
|
||||
Positional arguments:
|
||||
|
||||
- `task`:
|
||||
What you worked on. Corresponds to the _Tätigkeit_ column of the table.
|
||||
- `day`:
|
||||
The day of month. Corresponds to the _Datum_ column of the table.
|
||||
- `start`:
|
||||
When you started working. Corresponds to the _Beginn_ column of the table.
|
||||
- `end`:
|
||||
When you stopped working. Corresponds to the _Ende_ column of the table.
|
||||
|
||||
Named arguments:
|
||||
|
||||
- `rest` (default: `"00:00"`):
|
||||
Break time. Corresponds to the _Pause_ column of the table. If it wasn't a
|
||||
keyword, I'd have called it `break` :D
|
||||
- `note` (default: `none`):
|
||||
Additional note for the _Arbeitszeit_ column (whose value is automatically
|
||||
calculated). Entries with a note of `notes.Urlaub` are used to calculate the
|
||||
_Urlaub anteilig_ field in the summary table. See the `notes` dictionary for
|
||||
all available values.
|
||||
|
||||
### `timesheet` (function)
|
||||
|
||||
Generate and validate a full timesheet.
|
||||
|
||||
Positional arguments:
|
||||
|
||||
- `..entries`:
|
||||
All positional arguments are entries for the big table, created using the
|
||||
`entry` function.
|
||||
|
||||
Named arguments:
|
||||
|
||||
- `name`:
|
||||
Your name (Name, Vorname).
|
||||
- `staff_id`:
|
||||
Your staff id (Personalnummer).
|
||||
- `department`:
|
||||
Your department (Organisationseinheit/OE).
|
||||
- `working_area`:
|
||||
Your working area (Großforschung/GF or Unibereich/UB). Corresponds to the _GF_
|
||||
and _UB_ checkboxes on the form. See the `areas` dictionary for all available
|
||||
values.
|
||||
- `monthly_hours`:
|
||||
How many hours per month your contract says you should work (Vertraglich
|
||||
vereinbarte Arbeitszeit).
|
||||
- `hourly_wage`:
|
||||
Your hourly wage (Stundensatz).
|
||||
- `validate` (default: `true`):
|
||||
Whether the template should try to validate the data you entered (check if
|
||||
values look wrong, if you worked on a holiday, ...). If you turn this off, you
|
||||
can do funky things like work a negative amount of time or on Sundays.
|
||||
- `year`:
|
||||
The year this time sheet is being generated for.
|
||||
- `month`:
|
||||
The month this time sheet is being generated for.
|
||||
|
||||
### `timesheet_empty` (function)
|
||||
|
||||
Generate an empty timesheet. Useful if you want to fill it out by hand. Not sure
|
||||
why anyone would want to do this though :P
|
||||
374
kit_timesheet.typ
Normal file
374
kit_timesheet.typ
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/////////////
|
||||
// "Enums" //
|
||||
/////////////
|
||||
|
||||
#let areas = (Großforschung: "GF", Unibereich: "UB")
|
||||
#let notes = (Urlaub: "U", Krankheit: "K", Feiertag: "F", Sonstiges: "S")
|
||||
|
||||
////////////
|
||||
// Layout //
|
||||
////////////
|
||||
|
||||
#let _kit_green = rgb("#009682")
|
||||
#let _kit_stroke = 0.2mm
|
||||
|
||||
#let _frame(body) = {
|
||||
set text(lang: "de", font: "Liberation Sans")
|
||||
set page(margin: (top: 9.5mm, bottom: 12mm, left: 15mm, right: 10mm))
|
||||
set block(spacing: 1mm)
|
||||
set par(leading: 5pt)
|
||||
|
||||
// Weird vertical text in the bottom left
|
||||
place(bottom + left, dx: -6mm, dy: -1mm,
|
||||
rotate(-90deg, origin: bottom + left,
|
||||
text(size: 6pt, tracking: 3.1pt,
|
||||
"K_PSE_PB_AZDoku_01_04-20"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Main box
|
||||
rect(
|
||||
width: 100%,
|
||||
height: 100%,
|
||||
stroke: _kit_stroke,
|
||||
radius: (top-right: 4.5mm, bottom-left: 4.5mm),
|
||||
inset: 0mm
|
||||
)[
|
||||
// Logo
|
||||
#place(top + left, dx: 9.5mm, dy: 3.5mm,
|
||||
image("kit_logo.svg", width: 29mm)
|
||||
)
|
||||
|
||||
// Heading
|
||||
#place(top + left, dx: 78mm, dy: 9mm,
|
||||
text(weight: "bold", size: 14pt, fill: _kit_green,
|
||||
[Arbeitszeitdokumentation]
|
||||
)
|
||||
)
|
||||
|
||||
// Page number
|
||||
#place(bottom + right, dx: -15mm, dy: -1.5mm,
|
||||
text(size: 9pt, [Seite 1 von 1])
|
||||
)
|
||||
|
||||
// Main content
|
||||
#block(
|
||||
inset: (top: 24.5mm, left: 7.5mm, right: 13mm),
|
||||
width: 100%,
|
||||
height: 100%,
|
||||
body
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
#let _underlined(body) = box(
|
||||
baseline: 0%,
|
||||
stroke: (bottom: _kit_stroke),
|
||||
outset: (bottom: 1.5mm),
|
||||
inset: (x: 1mm),
|
||||
body,
|
||||
)
|
||||
|
||||
#let _checkbox(checked) = box(
|
||||
width: 3.5mm,
|
||||
outset: (y: 0.4mm),
|
||||
stroke: _kit_stroke,
|
||||
align(center + horizon, if checked { "X" } else { " " })
|
||||
)
|
||||
|
||||
#let _header(
|
||||
year: " ",
|
||||
month: " ",
|
||||
name: " ",
|
||||
staff_id: " ",
|
||||
working_area: " ",
|
||||
department: " ",
|
||||
monthly_hours: " ",
|
||||
hourly_wage: " ",
|
||||
) = [
|
||||
#set text(size: 11pt)
|
||||
#pad(left: 2.5mm, grid(
|
||||
columns: (1fr, 92mm),
|
||||
rows: 6mm,
|
||||
|
||||
[],
|
||||
align(right)[
|
||||
*Monat / Jahr:* #h(6mm)
|
||||
#_underlined(align(center)[
|
||||
#box(width: 21mm)[#month] / #box(width: 21mm)[#year]
|
||||
])
|
||||
],
|
||||
|
||||
[*Name, Vorname des/r Beschäftigten:*],
|
||||
_underlined(box(width: 100%, name)),
|
||||
|
||||
[*Personalnummer:*],
|
||||
_underlined[
|
||||
#box(width: 1fr)[#staff_id]
|
||||
#h(8mm)
|
||||
*GF:* #h(1mm)
|
||||
#_checkbox(working_area == areas.Großforschung)
|
||||
#h(8mm)
|
||||
*UB:* #h(1mm)
|
||||
#_checkbox(working_area == areas.Unibereich)
|
||||
#h(8mm)
|
||||
],
|
||||
|
||||
[*OE:*], // Institut / Organisationseinheit
|
||||
_underlined(box(width: 100%)[#department]),
|
||||
|
||||
[*Vertraglich vereinbarte Arbeitszeit:*],
|
||||
[
|
||||
#_underlined(align(center)[
|
||||
#h(4mm)
|
||||
#box(width: 10mm)[#monthly_hours]
|
||||
Std.
|
||||
#h(4mm)
|
||||
])
|
||||
#h(1fr)
|
||||
*Stundensatz:*
|
||||
#h(4mm)
|
||||
#_underlined(align(center)[
|
||||
#box(width: 18mm)[#hourly_wage]
|
||||
*€*
|
||||
])
|
||||
]
|
||||
))
|
||||
]
|
||||
|
||||
#let _log(..entries) = {
|
||||
set text(size: 10pt)
|
||||
table(
|
||||
columns: (1fr, 23.3mm, 23.3mm, 23.3mm, 23.3mm, 23.3mm),
|
||||
rows: array.range(24).map(_ => 5.05mm),
|
||||
align: center + horizon,
|
||||
stroke: _kit_stroke,
|
||||
inset: 1mm,
|
||||
table.header(
|
||||
table.cell(rowspan: 2)[
|
||||
*Tätigkeit* \
|
||||
*(Stichwort, Projekt)*
|
||||
],
|
||||
[*Datum*],
|
||||
[*Beginn*],
|
||||
[*Ende*],
|
||||
[*Pause*],
|
||||
[*Arbeitszeit#super[1]*],
|
||||
[*(tt.mm.jj)*],
|
||||
[*(hh:mm)*],
|
||||
[*(hh:mm)*],
|
||||
[*(hh:mm)*],
|
||||
[*(hh:mm)*],
|
||||
),
|
||||
..entries.pos()
|
||||
)
|
||||
}
|
||||
|
||||
#let _summary(
|
||||
holiday: [],
|
||||
total: [],
|
||||
monthly_hours: [],
|
||||
carry_prev_month: [],
|
||||
carry_next_month: [],
|
||||
) = {
|
||||
set text(size: 10pt)
|
||||
align(right, table(
|
||||
columns: (54mm, 23.3mm),
|
||||
rows: 5.05mm,
|
||||
align: center + horizon,
|
||||
stroke: _kit_stroke,
|
||||
inset: 1mm,
|
||||
[*Urlaub anteilig:*], [#holiday],
|
||||
[*Summe:*], [#total],
|
||||
[*monatliche Soll-Arbeitszeit:*], [#monthly_hours],
|
||||
[*Übertrag vom Vormonat:*], [#carry_prev_month],
|
||||
[*Übertrag in den Folgemonat:*], [#carry_next_month],
|
||||
))
|
||||
}
|
||||
|
||||
#let _footer() = pad(left: 2.5mm)[
|
||||
#v(3.5mm)
|
||||
#grid(
|
||||
columns: (1fr, 77.5mm),
|
||||
column-gutter: 6.5mm,
|
||||
row-gutter: (12mm, 3mm),
|
||||
[Ich bestätige die Richtigkeit der Angaben:],
|
||||
[Geprüft:],
|
||||
pad(left: -2.5mm, line(length: 100%, stroke: stroke(thickness: _kit_stroke, dash: "densely-dotted"))),
|
||||
pad(left: -2.5mm, line(length: 100%, stroke: stroke(thickness: _kit_stroke, dash: "densely-dotted"))),
|
||||
[Datum, Unterschrift Beschäftigte/r],
|
||||
[Datum, Unterschrift Dienstvorgesetzte/r],
|
||||
)
|
||||
|
||||
|
||||
#v(5.5mm)
|
||||
|
||||
#set text(size: 10pt)
|
||||
Nach *§ 17 Mindestlohngesetz (MiLoG)* müssen für geringfügig entlohnte und kurzfristig beschäftigte
|
||||
Arbeitnehmer/innen u.a. Beginn, Ende und Dauer der täglichen Arbeitszeit aufgezeichnet und für Kon-
|
||||
trollzwecke mindestens zwei Jahre am Ort der Beschäftigung aufbewahrt werden.
|
||||
|
||||
#v(12.5mm)
|
||||
#line(length: 51mm, stroke: _kit_stroke)
|
||||
#v(1mm)
|
||||
|
||||
#set text(size: 9pt)
|
||||
#super[1] Summe in vollen Stunden und Minuten ohne Pause (Std:Min); bei Abwesenheit können auch folgende Kürzel
|
||||
eingetragen werden: U=Urlaub, K=Krankheit, F=Feiertag, S=Sonstiges
|
||||
]
|
||||
|
||||
//////////
|
||||
// Util //
|
||||
//////////
|
||||
|
||||
#let _pad_int(n, char: "0", width: 2) = {
|
||||
let s = str(n)
|
||||
for _ in array.range(width - s.clusters().len()) { char }
|
||||
s
|
||||
}
|
||||
|
||||
#let _parse_duration(s) = {
|
||||
let matched = s.match(regex("^(-?)([0-9]+):([0-5][0-9])$"))
|
||||
assert(matched != none, message: "invalid duration or time: " + s)
|
||||
let groups = matched.captures
|
||||
let sign = if groups.at(0) == "-" { -1 } else { 1 }
|
||||
let h = int(groups.at(1))
|
||||
let m = int(groups.at(2))
|
||||
sign * (h * 60 + m)
|
||||
}
|
||||
|
||||
#let _fmt_duration(mins) = {
|
||||
if mins < 0 {
|
||||
"-"
|
||||
mins = -mins
|
||||
}
|
||||
let h = int(mins / 60)
|
||||
let m = mins - h * 60
|
||||
_pad_int(h, char: "0", width: 2)
|
||||
":"
|
||||
_pad_int(m, char: "0", width: 2)
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Validation //
|
||||
////////////////
|
||||
|
||||
#let _assert_entry(row, entry, condition, message) = {
|
||||
message = "row " + str(row) + " (day " + str(entry.day) + "): " + message
|
||||
assert(condition, message: message)
|
||||
}
|
||||
|
||||
#let _check_entry(row, entry) = {
|
||||
let e = entry
|
||||
_assert_entry(row, e, e.start <= e.end, "start must be before end")
|
||||
_assert_entry(row, e, e.rest <= e.end - e.start, "rest too long")
|
||||
|
||||
// I think the previous two checks should make it impossible for this assert
|
||||
// to fail, but just to be careful...
|
||||
_assert_entry(row, e, e.duration >= 0, "duration must be positive")
|
||||
}
|
||||
|
||||
//////////////////
|
||||
// Entry points //
|
||||
//////////////////
|
||||
|
||||
#let timesheet_empty() = _frame[
|
||||
#_header()
|
||||
#_log()
|
||||
#_summary()
|
||||
#_footer()
|
||||
]
|
||||
|
||||
#let entry(
|
||||
task,
|
||||
day,
|
||||
start,
|
||||
end,
|
||||
rest: "0:00",
|
||||
note: none,
|
||||
) = {
|
||||
assert(type(day) == int)
|
||||
assert(note == none or notes.values().contains(note))
|
||||
|
||||
start = _parse_duration(start)
|
||||
end = _parse_duration(end)
|
||||
rest = _parse_duration(rest)
|
||||
|
||||
(
|
||||
task: task,
|
||||
day: day,
|
||||
start: start,
|
||||
end: end,
|
||||
rest: rest,
|
||||
duration: end - start - rest,
|
||||
note: note,
|
||||
)
|
||||
}
|
||||
|
||||
#let timesheet(
|
||||
name: "Name, Vorname",
|
||||
staff_id: 1234567,
|
||||
department: "Institut für Informatik",
|
||||
working_area: none,
|
||||
monthly_hours: 40,
|
||||
hourly_wage: [14.09],
|
||||
validate: true,
|
||||
carry_prev_month: "00:00",
|
||||
year: 2024,
|
||||
month: 1,
|
||||
..entries,
|
||||
) = {
|
||||
assert(working_area == none or areas.values().contains(working_area))
|
||||
assert(type(monthly_hours) == int)
|
||||
assert(type(year) == int)
|
||||
assert(type(month) == int)
|
||||
|
||||
carry_prev_month = _parse_duration(carry_prev_month)
|
||||
entries = entries.pos()
|
||||
|
||||
let monthly = monthly_hours * 60
|
||||
let holiday = entries.filter(e => e.note == notes.Urlaub).map(e => e.duration).sum(default: 0)
|
||||
let total = entries.map(e => e.duration).sum(default: 0)
|
||||
let carry_next_month = carry_prev_month + total - monthly
|
||||
|
||||
if validate {
|
||||
for (row, entry) in entries.enumerate(start: 1) {
|
||||
_check_entry(row, entry)
|
||||
}
|
||||
}
|
||||
|
||||
let rows = entries.map(e => (
|
||||
e.task,
|
||||
datetime(year: year, month: month, day: e.day).display("[day].[month].[year]"),
|
||||
_fmt_duration(e.start),
|
||||
_fmt_duration(e.end),
|
||||
_fmt_duration(e.rest),
|
||||
{
|
||||
_fmt_duration(e.duration)
|
||||
if e.note != none { " "; e.note }
|
||||
},
|
||||
))
|
||||
|
||||
_frame[
|
||||
#_header(
|
||||
year: year,
|
||||
month: month,
|
||||
name: name,
|
||||
staff_id: staff_id,
|
||||
working_area: working_area,
|
||||
department: department,
|
||||
monthly_hours: monthly_hours,
|
||||
hourly_wage: hourly_wage,
|
||||
)
|
||||
#_log(..rows.flatten())
|
||||
#_summary(
|
||||
holiday: _fmt_duration(holiday),
|
||||
total: _fmt_duration(total),
|
||||
monthly_hours: _fmt_duration(monthly),
|
||||
carry_prev_month: _fmt_duration(carry_prev_month),
|
||||
carry_next_month: _fmt_duration(carry_next_month),
|
||||
)
|
||||
#_footer()
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue