dev/pdf #69
@@ -39,34 +39,34 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
context: Backend
|
context: Backend
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
document-creator:
|
# document-creator:
|
||||||
name: Build Document Creator
|
# name: Build Document Creator
|
||||||
runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
steps:
|
# steps:
|
||||||
- name: Checkout
|
# - name: Checkout
|
||||||
uses: actions/checkout@v4
|
# uses: actions/checkout@v4
|
||||||
- name: Login to GitHub Container Registry
|
# - name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
# uses: docker/login-action@v3
|
||||||
with:
|
# with:
|
||||||
registry: git.letsstein.de
|
# registry: git.letsstein.de
|
||||||
username: ${{ gitea.actor }}
|
# username: ${{ gitea.actor }}
|
||||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
# password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
- name: Set up QEMU
|
# - name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
# uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
# - name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
# uses: docker/setup-buildx-action@v3
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
# - name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
# id: meta
|
||||||
uses: docker/metadata-action@v5
|
# uses: docker/metadata-action@v5
|
||||||
with:
|
# with:
|
||||||
images: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
# images: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
||||||
tags: |
|
# tags: |
|
||||||
type=raw,value=latest
|
# type=raw,value=latest
|
||||||
type=pep440,pattern={{version}}
|
# type=pep440,pattern={{version}}
|
||||||
- name: Build and push
|
# - name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
# uses: docker/build-push-action@v6
|
||||||
with:
|
# with:
|
||||||
platforms: linux/amd64,linux/arm64
|
# platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
# push: true
|
||||||
context: DocumentCreator
|
# context: DocumentCreator
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
# tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ COPY . .
|
|||||||
RUN go build -o server .
|
RUN go build -o server .
|
||||||
|
|
||||||
FROM alpine:3.22
|
FROM alpine:3.22
|
||||||
RUN apk add --no-cache tzdata
|
RUN apk add --no-cache tzdata typst
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /app/server /app/server
|
COPY --from=build /app/server /app/server
|
||||||
|
COPY ./doc/static /doc/static
|
||||||
|
COPY ./doc/templates /doc/templates
|
||||||
|
|
||||||
COPY /static /app/static
|
COPY /static /app/static
|
||||||
ENTRYPOINT ["./server"]
|
ENTRYPOINT ["./server"]
|
||||||
|
|||||||
BIN
Backend/doc/static/logo.png
vendored
Normal file
BIN
Backend/doc/static/logo.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
97
Backend/doc/templates/abrechnung.typ
vendored
Normal file
97
Backend/doc/templates/abrechnung.typ
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#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, .875fr, 1.25fr),
|
||||||
|
fill: (x, y) =>
|
||||||
|
if y == 0 { oklch(87%, 0, 0deg) },
|
||||||
|
table-header(
|
||||||
|
[Datum], [Kommen], [Gehen], [Arbeitsart], [Stunden], [Kurzarbeit], [Pause], [Überstunden]
|
||||||
|
),
|
||||||
|
.. for day in days {
|
||||||
|
(
|
||||||
|
[#day.Date],
|
||||||
|
if day.DayParts.len() == 0{
|
||||||
|
table.cell(colspan: 3)[Keine Buchungen]
|
||||||
|
}else if day.DayParts.len() == 1 and 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 {
|
||||||
|
(
|
||||||
|
if Zeit.IsWorkDay{
|
||||||
|
(
|
||||||
|
table.cell()[#Zeit.BookingFrom],
|
||||||
|
table.cell()[#Zeit.BookingTo],
|
||||||
|
table.cell()[#Zeit.WorkType],
|
||||||
|
)
|
||||||
|
}else{
|
||||||
|
(table.cell(colspan: 3)[#Zeit.WorkType],)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[#day.Worktime],
|
||||||
|
[#day.Kurzarbeit],
|
||||||
|
[#day.Pausetime],
|
||||||
|
[#day.Overtime],
|
||||||
|
)
|
||||||
|
if day.IsFriday {
|
||||||
|
( table.cell(colspan: 8, 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],
|
||||||
|
[Kurzarbeit :], table.cell(align: left)[#meta.Kurzarbeit],
|
||||||
|
[Überstunden :], table.cell(align: left)[#meta.Overtime],
|
||||||
|
[Überstunden lfd. :],table.cell(align: left)[#meta.OvertimeTotal],
|
||||||
|
table.hline(start: 0, end: 2),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -198,8 +198,9 @@ func renderPDFSingle(data []typstData) (bytes.Buffer, error) {
|
|||||||
var markup bytes.Buffer
|
var markup bytes.Buffer
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
|
|
||||||
typstCLI := typst.DockerExec{
|
typstCLI := typst.CLI{
|
||||||
ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
WorkingDirectory: "/doc/",
|
||||||
|
// ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := typst.InjectValues(&markup, map[string]any{"data": data}); err != nil {
|
if err := typst.InjectValues(&markup, map[string]any{"data": data}); err != nil {
|
||||||
@@ -225,8 +226,9 @@ func renderPDFSingle(data []typstData) (bytes.Buffer, error) {
|
|||||||
func renderPDFMulti(data []typstData) ([]bytes.Buffer, error) {
|
func renderPDFMulti(data []typstData) ([]bytes.Buffer, error) {
|
||||||
var outputMulti []bytes.Buffer
|
var outputMulti []bytes.Buffer
|
||||||
|
|
||||||
typstRender := typst.DockerExec{
|
typstRender := typst.CLI{
|
||||||
ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
WorkingDirectory: "/doc/",
|
||||||
|
// ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range data {
|
for _, d := range data {
|
||||||
|
|||||||
@@ -17,7 +17,16 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
var logLevel slog.LevelVar
|
var logLevel slog.LevelVar
|
||||||
logLevel.Set(slog.LevelWarn)
|
switch helper.GetEnv("LOG_LEVEL", "warn") {
|
||||||
|
case "debug":
|
||||||
|
logLevel.Set(slog.LevelDebug)
|
||||||
|
case "info":
|
||||||
|
logLevel.Set(slog.LevelInfo)
|
||||||
|
case "warn":
|
||||||
|
logLevel.Set(slog.LevelWarn)
|
||||||
|
case "error":
|
||||||
|
logLevel.Set(slog.LevelError)
|
||||||
|
}
|
||||||
|
|
||||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: &logLevel}))
|
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: &logLevel}))
|
||||||
slog.SetDefault(logger)
|
slog.SetDefault(logger)
|
||||||
|
|||||||
@@ -25,12 +25,11 @@ services:
|
|||||||
- ${WEB_PORT}:8080
|
- ${WEB_PORT}:8080
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
- document-creator
|
|
||||||
volumes:
|
volumes:
|
||||||
- ../logs:/app/Backend/logs
|
- ${LOG_PATH}:/app/logs
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
document-creator:
|
# document-creator:
|
||||||
image: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
# image: git.letsstein.de/tom/arbeitszeitmessung-doc-creator
|
||||||
container_name: ${TYPST_CONTAINER}
|
# container_name: ${TYPST_CONTAINER}
|
||||||
restart: unless-stopped
|
# restart: unless-stopped
|
||||||
|
|||||||
Reference in New Issue
Block a user