implement payroll generator v0.9
This commit is contained in:
parent
319e7f3614
commit
4abe9e44d8
|
|
@ -0,0 +1,52 @@
|
||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "./tmp/main"
|
||||||
|
cmd = "go build -o ./tmp/main ./cmd/api"
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
|
include_file = []
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
poll = false
|
||||||
|
poll_interval = 0
|
||||||
|
post_cmd = []
|
||||||
|
pre_cmd = []
|
||||||
|
rerun = false
|
||||||
|
rerun_delay = 500
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = false
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
silent = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[proxy]
|
||||||
|
app_port = 0
|
||||||
|
enabled = false
|
||||||
|
proxy_port = 0
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,35 @@
|
||||||
|
# --- Build Stage ---
|
||||||
|
FROM golang:1.22-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Dependencies laden, bevor wir den Code kopieren, um den Layer-Cache zu nutzen
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# sqlc generieren und App bauen
|
||||||
|
# (Wir brauchen sqlc im Build-Container)
|
||||||
|
RUN go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
|
||||||
|
RUN sqlc generate
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -v -o /business-engine ./cmd/api
|
||||||
|
|
||||||
|
# --- Final Stage ---
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# Root-Zertifikate für sichere Verbindungen
|
||||||
|
RUN apk --no-cache add ca-certificates
|
||||||
|
|
||||||
|
# User anlegen, um nicht als root zu laufen
|
||||||
|
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
WORKDIR /home/appuser
|
||||||
|
COPY --from=builder /business-engine .
|
||||||
|
|
||||||
|
COPY config.yaml .
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["./business-engine"]
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
.PHONY: db/update-schema
|
||||||
|
db/update-schema:
|
||||||
|
@echo "--> Dumping database schema..."
|
||||||
|
@docker exec -it data-connector-avicenna-db-1 pg_dump --schema-only --no-owner --no-privileges \
|
||||||
|
-U johndoe avicenna > ./db/migrations/db.sql
|
||||||
|
@echo "--> Schema dump complete"
|
||||||
|
@$(MAKE) generate/sqlc
|
||||||
|
|
||||||
|
.PHONY: generate/sqlc
|
||||||
|
generate/sqlc:
|
||||||
|
@echo "--> Generating SQLC code..."
|
||||||
|
@sqlc generate
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"business-engine/internal/config"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||||
|
Level: slog.LevelDebug,
|
||||||
|
})
|
||||||
|
|
||||||
|
logger := slog.New(handler)
|
||||||
|
slog.SetDefault(logger)
|
||||||
|
|
||||||
|
cfg, err := config.Load()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to load configuration", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := NewServer(cfg, logger)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to create server", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
server.Start()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"business-engine/internal/config"
|
||||||
|
"business-engine/internal/database"
|
||||||
|
"business-engine/internal/payroll"
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Server contains all dependencies of the application
|
||||||
|
type Server struct {
|
||||||
|
config *config.Config
|
||||||
|
logger *slog.Logger
|
||||||
|
db *pgxpool.Pool
|
||||||
|
router *http.ServeMux
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer constructs a new server
|
||||||
|
func NewServer(cfg *config.Config, logger *slog.Logger) (*Server, error) {
|
||||||
|
dbpool, err := pgxpool.New(context.Background(), cfg.DatabaseURL)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to connect to database", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
server := &Server{
|
||||||
|
config: cfg,
|
||||||
|
logger: logger,
|
||||||
|
db: dbpool,
|
||||||
|
router: http.NewServeMux(),
|
||||||
|
}
|
||||||
|
|
||||||
|
server.setupRoutes()
|
||||||
|
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupRoutes initializes all routes and handlers
|
||||||
|
func (s *Server) setupRoutes() {
|
||||||
|
employeeRepo := database.New(s.db)
|
||||||
|
employeeService := payroll.NewService(employeeRepo, s.logger)
|
||||||
|
employeeHandler := payroll.NewEmployeeHandler(*employeeService, s.logger)
|
||||||
|
|
||||||
|
s.router.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte("OK"))
|
||||||
|
})
|
||||||
|
|
||||||
|
s.router.HandleFunc("GET /api/v1/employees", employeeHandler.ListEmployees)
|
||||||
|
|
||||||
|
s.router.HandleFunc("GET /api/v1/payroll", employeeHandler.RunPayroll)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() {
|
||||||
|
httpServer := &http.Server{
|
||||||
|
Addr: ":" + s.config.Port,
|
||||||
|
Handler: s.router,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
s.logger.Info("server starting", "port", s.config.Port)
|
||||||
|
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
s.logger.Error("server failed to start", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
stop := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||||
|
<-stop
|
||||||
|
|
||||||
|
s.logger.Info("shutting down server gracefully")
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := httpServer.Shutdown(ctx); err != nil {
|
||||||
|
s.logger.Error("server shutdown failed", "error", err)
|
||||||
|
} else {
|
||||||
|
s.logger.Info("server stopped")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
port: "8080"
|
||||||
|
logLevel: "info"
|
||||||
|
databaseUrl: "postgres://johndoe:randompassword@localhost:5432/avicenna"
|
||||||
|
|
||||||
|
# Unsere komplexe, statische Business-Logik-Konfiguration
|
||||||
|
surchargeRules:
|
||||||
|
- name: "Nachtzuschlag (Mo-Fr)"
|
||||||
|
combinationLogic: "TAKE_HIGHEST"
|
||||||
|
timeConditions:
|
||||||
|
- weekdays: [1, 2, 3, 4, 5] # Mo, Di, Mi, Do, Fr
|
||||||
|
startTime: "22:00"
|
||||||
|
endTime: "06:00"
|
||||||
|
calculation:
|
||||||
|
type: "PERCENTAGE"
|
||||||
|
value: 25.0
|
||||||
|
|
||||||
|
- name: "Sonntagszuschlag"
|
||||||
|
combinationLogic: "TAKE_HIGHEST"
|
||||||
|
timeConditions:
|
||||||
|
- weekdays: [0] # Sonntag
|
||||||
|
calculation:
|
||||||
|
type: "PERCENTAGE"
|
||||||
|
value: 50.0
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,181 @@
|
||||||
|
-- name: ListActiveEmployees :many
|
||||||
|
SELECT
|
||||||
|
de.id,
|
||||||
|
de.firstname,
|
||||||
|
de.surname,
|
||||||
|
de."contractStart",
|
||||||
|
de."contractEnd",
|
||||||
|
de."contractHoursWeek",
|
||||||
|
de."contractSalaryHour",
|
||||||
|
de."contractTypeName",
|
||||||
|
e.id as "dlId",
|
||||||
|
e."personTransportCertificate"
|
||||||
|
FROM "DyflexisEmployee" as de
|
||||||
|
INNER JOIN "Employee" as e ON de.id::text = e."personnelNumber"
|
||||||
|
WHERE de."contractStart" < CURRENT_DATE AND de."contractEnd" > (CURRENT_DATE - INTERVAL '2 months');
|
||||||
|
|
||||||
|
-- name: GetWorkSummaryForEmployeeInPeriod :one
|
||||||
|
WITH wt AS (
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as days,
|
||||||
|
COALESCE(sum(duration), 0)::bigint as minutes
|
||||||
|
FROM "WorkTime"
|
||||||
|
WHERE "userId" = @employee_id AND "startDate" > @start_date AND "startDate" < @end_date
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
wt.days,
|
||||||
|
(minutes / 60.0)::float as hours,
|
||||||
|
wt.minutes
|
||||||
|
FROM wt;
|
||||||
|
|
||||||
|
-- name: ListPlannedTimesForEmployeeInPeriod :many
|
||||||
|
SELECT
|
||||||
|
DISTINCT ON (day)
|
||||||
|
"startDate"::DATE as day,
|
||||||
|
"startDate",
|
||||||
|
"endDate",
|
||||||
|
department,
|
||||||
|
pause,
|
||||||
|
duration
|
||||||
|
FROM "PlannedTime"
|
||||||
|
WHERE
|
||||||
|
"userId" = @employee_id AND
|
||||||
|
"startDate" > @start_date AND
|
||||||
|
"startDate" < @end_date AND
|
||||||
|
department <> 'Fortbildung' AND
|
||||||
|
note IS NULL
|
||||||
|
ORDER BY day, "startDate" ASC;
|
||||||
|
|
||||||
|
-- name: ListWorkTimesForEmployeeInPeriod :many
|
||||||
|
SELECT * FROM "WorkTime"
|
||||||
|
WHERE "userId" = @employee_id AND "startDate" > @start_date AND "startDate" < @end_date;
|
||||||
|
|
||||||
|
-- name: GetSicknessSummaryForEmployeeInPeriod :one
|
||||||
|
WITH pt AS (
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as days,
|
||||||
|
COALESCE(sum(duration), 0)::bigint as minutes
|
||||||
|
FROM "PlannedTime"
|
||||||
|
WHERE "userId" = @employee_id AND "startDate" > @start_date AND "startDate" < @end_date AND note = 'sick'
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
pt.days,
|
||||||
|
(minutes / 60)::float as hours,
|
||||||
|
pt.minutes
|
||||||
|
FROM pt;
|
||||||
|
|
||||||
|
-- name: GetEmployeeDyflexis :one
|
||||||
|
SELECT * FROM "DyflexisEmployee"
|
||||||
|
WHERE
|
||||||
|
id = $1 AND
|
||||||
|
"contractStart" < CURRENT_DATE AND
|
||||||
|
"contractEnd" > (CURRENT_DATE - INTERVAL '2 months')
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- name: FetchDrivenToursInInterval :many
|
||||||
|
SELECT * FROM "Tour"
|
||||||
|
WHERE "check" <> 3
|
||||||
|
AND "deletedAt" IS NULL
|
||||||
|
AND (
|
||||||
|
"patientId" IS NULL
|
||||||
|
OR "patientId" NOT IN (
|
||||||
|
'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
'3554e1a9-9c07-45e5-b53c-692a77e4fa9a'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
-- ID's for Pause and Feierabend respectively
|
||||||
|
AND (
|
||||||
|
"carName" IS NULL
|
||||||
|
OR "carName" NOT IN (
|
||||||
|
'Storno',
|
||||||
|
'Abgabe',
|
||||||
|
'Löschen'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND "startDate" >= @start_date
|
||||||
|
AND "startDate" <= @end_date
|
||||||
|
AND ("driverId" = @employee_id OR "codriverId" = @employee_id)
|
||||||
|
ORDER BY "startDate" DESC;
|
||||||
|
|
||||||
|
-- name: CountTourengeldAmountInPeriod :one
|
||||||
|
WITH ToursInPeriod AS (
|
||||||
|
SELECT
|
||||||
|
(("startDate" AT TIME ZONE 'UTC') AT TIME ZONE 'Europe/Berlin')::DATE as day,
|
||||||
|
"operationId",
|
||||||
|
"occupiedKm",
|
||||||
|
CASE
|
||||||
|
WHEN "occupiedKm" <= 30 THEN 1.0
|
||||||
|
ELSE ROUND("occupiedKm" / 30.0)
|
||||||
|
END AS tour_value,
|
||||||
|
CASE
|
||||||
|
WHEN EXTRACT(ISODOW FROM "startDate") = 6 AND "occupiedKm" <= 30 THEN 0.5
|
||||||
|
ELSE 0.0
|
||||||
|
END AS weekend_bonus,
|
||||||
|
"infectionName",
|
||||||
|
CASE
|
||||||
|
WHEN "infectionName" = 'Covid-19' THEN 0.5
|
||||||
|
ELSE 0.0
|
||||||
|
END AS infection_bonus
|
||||||
|
FROM
|
||||||
|
"Tour"
|
||||||
|
WHERE
|
||||||
|
"check" <> 3
|
||||||
|
AND "deletedAt" IS NULL
|
||||||
|
AND (
|
||||||
|
"patientId" IS NULL
|
||||||
|
OR "patientId" NOT IN (
|
||||||
|
'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
'3554e1a9-9c07-45e5-b53c-692a77e4fa9a'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
-- ID's for Pause and Feierabend respectively
|
||||||
|
AND (
|
||||||
|
"carName" IS NULL
|
||||||
|
OR "carName" NOT IN (
|
||||||
|
'Storno',
|
||||||
|
'Abgabe',
|
||||||
|
'Löschen'
|
||||||
|
)
|
||||||
|
AND "carName" LIKE '%KTW%'
|
||||||
|
)
|
||||||
|
AND "startDate" >= @start_date
|
||||||
|
AND "startDate" <= @end_date
|
||||||
|
AND ("driverId" ILIKE @employee_id OR "codriverId" ILIKE @employee_id)
|
||||||
|
ORDER BY
|
||||||
|
"day"
|
||||||
|
),
|
||||||
|
DailyCounts AS (
|
||||||
|
SELECT
|
||||||
|
day,
|
||||||
|
SUM(tour_value) + SUM(weekend_bonus) + SUM(infection_bonus) AS tour_amount
|
||||||
|
FROM
|
||||||
|
ToursInPeriod
|
||||||
|
GROUP BY
|
||||||
|
day
|
||||||
|
),
|
||||||
|
HighActivityDays AS (
|
||||||
|
SELECT
|
||||||
|
day,
|
||||||
|
GREATEST(0, tour_amount - 6) AS tourengeld
|
||||||
|
FROM
|
||||||
|
DailyCounts
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
SUM(tourengeld)::float as tourengeld
|
||||||
|
FROM
|
||||||
|
HighActivityDays;
|
||||||
|
|
||||||
|
-- name: GetEmployeeByName :one
|
||||||
|
SELECT
|
||||||
|
dy.id as dyflexis_id,
|
||||||
|
dl.id as dispolive_id,
|
||||||
|
dy."contractSalaryHour"
|
||||||
|
FROM "DyflexisEmployee" as dy
|
||||||
|
INNER JOIN
|
||||||
|
"Employee" as dl ON dy.id::text = dl."personnelNumber"
|
||||||
|
WHERE
|
||||||
|
TRIM(dy.firstname) = @firstname AND
|
||||||
|
TRIM(dy.surname) = @lastname AND
|
||||||
|
"contractStart" < CURRENT_DATE AND
|
||||||
|
"contractEnd" > (CURRENT_DATE - INTERVAL '2 months')
|
||||||
|
ORDER BY dy."contractEnd" DESC;
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
module business-engine
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
toolchain go1.24.9
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jackc/pgx/v5 v5.7.6
|
||||||
|
github.com/spf13/viper v1.21.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
codeberg.org/go-pdf/fpdf v0.11.1 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
|
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 // indirect
|
||||||
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgtype v1.14.4 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/crypto v0.37.0 // indirect
|
||||||
|
golang.org/x/sync v0.17.0 // indirect
|
||||||
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
|
golang.org/x/text v0.30.0 // indirect
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
codeberg.org/go-pdf/fpdf v0.11.1 h1:U8+coOTDVLxHIXZgGvkfQEi/q0hYHYvEHFuGNX2GzGs=
|
||||||
|
codeberg.org/go-pdf/fpdf v0.11.1/go.mod h1:Y0DGRAdZ0OmnZPvjbMp/1bYxmIPxm0ws4tfoPOc4LjU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 h1:FWNFq4fM1wPfcK40yHE5UO3RUdSNPaBC+j3PokzA6OQ=
|
||||||
|
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||||
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||||
|
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||||
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
|
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||||
|
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
|
github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8=
|
||||||
|
github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
|
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||||
|
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||||
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||||
|
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
|
||||||
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||||
|
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
|
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||||
|
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Port string
|
||||||
|
LogLevel string
|
||||||
|
DatabaseURL string `mapstructure:"databaseUrl"`
|
||||||
|
// SurchargeRules []models.SurchargeRule `mapstructure:"surchargeRules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load() (*Config, error) {
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
viper.AddConfigPath("/etc/business-engine/")
|
||||||
|
viper.SetConfigName("config")
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||||
|
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viper.BindEnv("databaseUrl", "DATABASE_URL")
|
||||||
|
|
||||||
|
var cfg Config
|
||||||
|
if err := viper.Unmarshal(&cfg); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||||
|
}
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DBTX interface {
|
||||||
|
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
|
||||||
|
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
|
||||||
|
QueryRow(context.Context, string, ...interface{}) pgx.Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db DBTX) *Queries {
|
||||||
|
return &Queries{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queries struct {
|
||||||
|
db DBTX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
|
||||||
|
return &Queries{
|
||||||
|
db: tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,714 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ApprovalState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ApprovalStateUNKNOWN ApprovalState = "UNKNOWN"
|
||||||
|
ApprovalStateFREE ApprovalState = "FREE"
|
||||||
|
ApprovalStateREQUIRED ApprovalState = "REQUIRED"
|
||||||
|
ApprovalStateREQUESTED ApprovalState = "REQUESTED"
|
||||||
|
ApprovalStateAPPROVED ApprovalState = "APPROVED"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *ApprovalState) Scan(src interface{}) error {
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
*e = ApprovalState(s)
|
||||||
|
case string:
|
||||||
|
*e = ApprovalState(s)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported scan type for ApprovalState: %T", src)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullApprovalState struct {
|
||||||
|
ApprovalState ApprovalState `json:"ApprovalState"`
|
||||||
|
Valid bool `json:"valid"` // Valid is true if ApprovalState is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (ns *NullApprovalState) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
ns.ApprovalState, ns.Valid = "", false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ns.Valid = true
|
||||||
|
return ns.ApprovalState.Scan(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (ns NullApprovalState) Value() (driver.Value, error) {
|
||||||
|
if !ns.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return string(ns.ApprovalState), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CostTypeVariant string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CostTypeVariantNACHTZUSCHLAG CostTypeVariant = "NACHTZUSCHLAG"
|
||||||
|
CostTypeVariantWOCHENENDZUSCHLAG CostTypeVariant = "WOCHENENDZUSCHLAG"
|
||||||
|
CostTypeVariantFEIERTAGSZUSCHLAG CostTypeVariant = "FEIERTAGSZUSCHLAG"
|
||||||
|
CostTypeVariantDESINFEKTIONSZUSCHLAG CostTypeVariant = "DESINFEKTIONSZUSCHLAG"
|
||||||
|
CostTypeVariantKILOMETERZUSCHLAG CostTypeVariant = "KILOMETERZUSCHLAG"
|
||||||
|
CostTypeVariantFAHRZEUGZUSCHLAG CostTypeVariant = "FAHRZEUGZUSCHLAG"
|
||||||
|
CostTypeVariantTSWDIFFERENZIERUNG CostTypeVariant = "TSWDIFFERENZIERUNG"
|
||||||
|
CostTypeVariantGRUNDPREIS CostTypeVariant = "GRUNDPREIS"
|
||||||
|
CostTypeVariantGRUNDPREISZEIT CostTypeVariant = "GRUNDPREIS_ZEIT"
|
||||||
|
CostTypeVariantPRIVATVERSICHERUNG CostTypeVariant = "PRIVATVERSICHERUNG"
|
||||||
|
CostTypeVariantTRAGEHILFE CostTypeVariant = "TRAGEHILFE"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *CostTypeVariant) Scan(src interface{}) error {
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
*e = CostTypeVariant(s)
|
||||||
|
case string:
|
||||||
|
*e = CostTypeVariant(s)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported scan type for CostTypeVariant: %T", src)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullCostTypeVariant struct {
|
||||||
|
CostTypeVariant CostTypeVariant `json:"CostTypeVariant"`
|
||||||
|
Valid bool `json:"valid"` // Valid is true if CostTypeVariant is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (ns *NullCostTypeVariant) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
ns.CostTypeVariant, ns.Valid = "", false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ns.Valid = true
|
||||||
|
return ns.CostTypeVariant.Scan(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (ns NullCostTypeVariant) Value() (driver.Value, error) {
|
||||||
|
if !ns.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return string(ns.CostTypeVariant), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TicketState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TicketStateCREATED TicketState = "CREATED"
|
||||||
|
TicketStateAPPROVALREQUIRED TicketState = "APPROVAL_REQUIRED"
|
||||||
|
TicketStateREADY TicketState = "READY"
|
||||||
|
TicketStateDONE TicketState = "DONE"
|
||||||
|
TicketStateBILLABLE TicketState = "BILLABLE"
|
||||||
|
TicketStateARCHIVED TicketState = "ARCHIVED"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *TicketState) Scan(src interface{}) error {
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
*e = TicketState(s)
|
||||||
|
case string:
|
||||||
|
*e = TicketState(s)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported scan type for TicketState: %T", src)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullTicketState struct {
|
||||||
|
TicketState TicketState `json:"TicketState"`
|
||||||
|
Valid bool `json:"valid"` // Valid is true if TicketState is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (ns *NullTicketState) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
ns.TicketState, ns.Valid = "", false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ns.Valid = true
|
||||||
|
return ns.TicketState.Scan(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (ns NullTicketState) Value() (driver.Value, error) {
|
||||||
|
if !ns.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return string(ns.TicketState), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TicketValidationState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TicketValidationStateCREATED TicketValidationState = "CREATED"
|
||||||
|
TicketValidationStateUSERINTERACTIONREQUIRED TicketValidationState = "USER_INTERACTION_REQUIRED"
|
||||||
|
TicketValidationStateTDUNCERTAIN TicketValidationState = "TD_UNCERTAIN"
|
||||||
|
TicketValidationStateDOCUMENTSMISSING TicketValidationState = "DOCUMENTS_MISSING"
|
||||||
|
TicketValidationStateARCHIVED TicketValidationState = "ARCHIVED"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *TicketValidationState) Scan(src interface{}) error {
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
*e = TicketValidationState(s)
|
||||||
|
case string:
|
||||||
|
*e = TicketValidationState(s)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported scan type for TicketValidationState: %T", src)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullTicketValidationState struct {
|
||||||
|
TicketValidationState TicketValidationState `json:"TicketValidationState"`
|
||||||
|
Valid bool `json:"valid"` // Valid is true if TicketValidationState is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (ns *NullTicketValidationState) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
ns.TicketValidationState, ns.Valid = "", false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ns.Valid = true
|
||||||
|
return ns.TicketValidationState.Scan(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (ns NullTicketValidationState) Value() (driver.Value, error) {
|
||||||
|
if !ns.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return string(ns.TicketValidationState), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Anomaly struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
GroupKey string `json:"groupKey"`
|
||||||
|
GroupDescription string `json:"groupDescription"`
|
||||||
|
WorkedOnBy string `json:"workedOnBy"`
|
||||||
|
SleepTimer int32 `json:"sleepTimer"`
|
||||||
|
SleepSince *time.Time `json:"sleepSince"`
|
||||||
|
ResolvedAt *time.Time `json:"resolvedAt"`
|
||||||
|
ManuallyResolved bool `json:"manuallyResolved"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
AssociatedEntities []byte `json:"associatedEntities"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttendanceRegistration struct {
|
||||||
|
AttendanceRegistrationId int32 `json:"attendanceRegistrationId"`
|
||||||
|
EmployeeId int32 `json:"employeeId"`
|
||||||
|
PersonnelNumber string `json:"personnelNumber"`
|
||||||
|
DateTime time.Time `json:"dateTime"`
|
||||||
|
Event string `json:"event"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttendanceRegistrationSource struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
InsertedAt time.Time `json:"insertedAt"`
|
||||||
|
JobId string `json:"jobId"`
|
||||||
|
AttendanceRegistrationId pgtype.Text `json:"attendanceRegistrationId"`
|
||||||
|
EmployeeId pgtype.Text `json:"employeeId"`
|
||||||
|
PersonnelNumber pgtype.Text `json:"personnelNumber"`
|
||||||
|
DateTime pgtype.Text `json:"dateTime"`
|
||||||
|
Event pgtype.Text `json:"event"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttendanceRegistrationStaging struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
AttendanceRegistrationId int32 `json:"attendanceRegistrationId"`
|
||||||
|
EmployeeId int32 `json:"employeeId"`
|
||||||
|
DateTime time.Time `json:"dateTime"`
|
||||||
|
Event string `json:"event"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AvicennaTenant struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
HealthInsuranceNumber string `json:"healthInsuranceNumber"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AvicennaTenantToTariffGroup struct {
|
||||||
|
A pgtype.UUID `json:"A"`
|
||||||
|
B pgtype.UUID `json:"B"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CostType struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Variant CostTypeVariant `json:"variant"`
|
||||||
|
CostAmount int32 `json:"costAmount"`
|
||||||
|
TimeRange pgtype.Text `json:"timeRange"`
|
||||||
|
KmInclusive pgtype.Int4 `json:"kmInclusive"`
|
||||||
|
ExcludeCarNames []string `json:"excludeCarNames"`
|
||||||
|
InfectionType pgtype.Text `json:"infectionType"`
|
||||||
|
TariffId pgtype.UUID `json:"tariffId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DyflexisEmployee struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Firstname string `json:"firstname"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
ContractStart time.Time `json:"contractStart"`
|
||||||
|
ContractEnd *time.Time `json:"contractEnd"`
|
||||||
|
ContractHoursWeek float64 `json:"contractHoursWeek"`
|
||||||
|
ContractSalaryHour float64 `json:"contractSalaryHour"`
|
||||||
|
ContractTypeName pgtype.Text `json:"contractTypeName"`
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Employee struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
PersonnelNumber pgtype.Text `json:"personnelNumber"`
|
||||||
|
Mobile pgtype.Text `json:"mobile"`
|
||||||
|
Qualification pgtype.Text `json:"qualification"`
|
||||||
|
OccuptationalHealthExamination *time.Time `json:"occuptationalHealthExamination"`
|
||||||
|
PersonTransportCertificate *time.Time `json:"personTransportCertificate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmployeeDispoLiveSource struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
InsertedAt time.Time `json:"insertedAt"`
|
||||||
|
JobId string `json:"jobId"`
|
||||||
|
ID pgtype.Text `json:"id"`
|
||||||
|
PScheinAblaufDatum pgtype.Text `json:"pScheinAblaufDatum"`
|
||||||
|
Name pgtype.Text `json:"name"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
PersId pgtype.Text `json:"persId"`
|
||||||
|
ImportPersId pgtype.Text `json:"importPersId"`
|
||||||
|
Qualifikation pgtype.Text `json:"qualifikation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmployeeDispoLiveStaging struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name pgtype.Text `json:"name"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
PersonnelNumber pgtype.Text `json:"personnelNumber"`
|
||||||
|
Qualification pgtype.Text `json:"qualification"`
|
||||||
|
PersonTransportCertificate *time.Time `json:"personTransportCertificate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmployeeDyflexisSource struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
InsertedAt time.Time `json:"insertedAt"`
|
||||||
|
JobId string `json:"jobId"`
|
||||||
|
ID pgtype.Text `json:"id"`
|
||||||
|
Firstname pgtype.Text `json:"firstname"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
ContractID pgtype.Text `json:"contract_id"`
|
||||||
|
ContractStart pgtype.Text `json:"contract_start"`
|
||||||
|
ContractEnd pgtype.Text `json:"contract_end"`
|
||||||
|
ContractHoursWeek pgtype.Text `json:"contract_hours_week"`
|
||||||
|
ContractSalaryHour pgtype.Text `json:"contract_salary_hour"`
|
||||||
|
ContractTypeName pgtype.Text `json:"contract_type_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmployeeDyflexisStaging struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Firstname string `json:"firstname"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
ContractStart time.Time `json:"contractStart"`
|
||||||
|
ContractEnd time.Time `json:"contractEnd"`
|
||||||
|
ContractHoursWeek float64 `json:"contractHoursWeek"`
|
||||||
|
ContractSalaryHour float64 `json:"contractSalaryHour"`
|
||||||
|
ContractTypeName pgtype.Text `json:"contractTypeName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Patient struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
Name pgtype.Text `json:"name"`
|
||||||
|
Street pgtype.Text `json:"street"`
|
||||||
|
Zip pgtype.Text `json:"zip"`
|
||||||
|
City pgtype.Text `json:"city"`
|
||||||
|
Birthday *time.Time `json:"birthday"`
|
||||||
|
HealthinsuranceId pgtype.Text `json:"healthinsuranceId"`
|
||||||
|
Healthinsurance pgtype.Text `json:"healthinsurance"`
|
||||||
|
CareDegree pgtype.Text `json:"careDegree"`
|
||||||
|
DisabilityMark1 pgtype.Text `json:"disabilityMark1"`
|
||||||
|
DisabilityMark2 pgtype.Text `json:"disabilityMark2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PatientSource struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
InsertedAt time.Time `json:"insertedAt"`
|
||||||
|
JobId string `json:"jobId"`
|
||||||
|
ID pgtype.Text `json:"id"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
Name pgtype.Text `json:"name"`
|
||||||
|
Street pgtype.Text `json:"street"`
|
||||||
|
Zip pgtype.Text `json:"zip"`
|
||||||
|
City pgtype.Text `json:"city"`
|
||||||
|
Birthday pgtype.Text `json:"birthday"`
|
||||||
|
KkId pgtype.Text `json:"kkId"`
|
||||||
|
Krankenkasse pgtype.Text `json:"krankenkasse"`
|
||||||
|
Pflegestufe pgtype.Text `json:"pflegestufe"`
|
||||||
|
Behinderung1 pgtype.Text `json:"behinderung1"`
|
||||||
|
Behinderung2 pgtype.Text `json:"behinderung2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PatientStaging struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
ID pgtype.Text `json:"id"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
Name pgtype.Text `json:"name"`
|
||||||
|
Street pgtype.Text `json:"street"`
|
||||||
|
Zip pgtype.Text `json:"zip"`
|
||||||
|
City pgtype.Text `json:"city"`
|
||||||
|
Birthday *time.Time `json:"birthday"`
|
||||||
|
HealthinsuranceId pgtype.Text `json:"healthinsuranceId"`
|
||||||
|
Healthinsurance pgtype.Text `json:"healthinsurance"`
|
||||||
|
CareDegree pgtype.Text `json:"careDegree"`
|
||||||
|
DisabilityMark1 pgtype.Text `json:"disabilityMark1"`
|
||||||
|
DisabilityMark2 pgtype.Text `json:"disabilityMark2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlannedTime struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
Firstname pgtype.Text `json:"firstname"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
StartDate *time.Time `json:"startDate"`
|
||||||
|
EndDate *time.Time `json:"endDate"`
|
||||||
|
Department pgtype.Text `json:"department"`
|
||||||
|
Pause int32 `json:"pause"`
|
||||||
|
Duration int32 `json:"duration"`
|
||||||
|
Note pgtype.Text `json:"note"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlannedTimeSource struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
InsertedAt time.Time `json:"insertedAt"`
|
||||||
|
JobId string `json:"jobId"`
|
||||||
|
ID pgtype.Text `json:"id"`
|
||||||
|
UserID pgtype.Text `json:"user_id"`
|
||||||
|
Firstname pgtype.Text `json:"firstname"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
DepartmentID pgtype.Text `json:"department_id"`
|
||||||
|
DepartmentName pgtype.Text `json:"department_name"`
|
||||||
|
StartDate pgtype.Text `json:"start_date"`
|
||||||
|
EndDate pgtype.Text `json:"end_date"`
|
||||||
|
Pauze pgtype.Text `json:"pauze"`
|
||||||
|
Duration pgtype.Text `json:"duration"`
|
||||||
|
Deleted pgtype.Text `json:"deleted"`
|
||||||
|
Mark pgtype.Text `json:"mark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlannedTimeStaging struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
EmployeeId int32 `json:"employeeId"`
|
||||||
|
Firstname pgtype.Text `json:"firstname"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
DepartmentName pgtype.Text `json:"departmentName"`
|
||||||
|
StartDate time.Time `json:"startDate"`
|
||||||
|
EndDate time.Time `json:"endDate"`
|
||||||
|
Deleted bool `json:"deleted"`
|
||||||
|
Mark pgtype.Text `json:"mark"`
|
||||||
|
Duration int32 `json:"duration"`
|
||||||
|
Pause int32 `json:"pause"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrismaMigration struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Checksum string `json:"checksum"`
|
||||||
|
FinishedAt *time.Time `json:"finished_at"`
|
||||||
|
MigrationName string `json:"migration_name"`
|
||||||
|
Logs pgtype.Text `json:"logs"`
|
||||||
|
RolledBackAt *time.Time `json:"rolled_back_at"`
|
||||||
|
StartedAt time.Time `json:"started_at"`
|
||||||
|
AppliedStepsCount int32 `json:"applied_steps_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SolutionTarget struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
AnomalyId pgtype.Text `json:"anomalyId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SolutionTargetStep struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
SolutionTargetId pgtype.UUID `json:"solutionTargetId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tariff struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
ValidFrom *time.Time `json:"validFrom"`
|
||||||
|
ValidTo *time.Time `json:"validTo"`
|
||||||
|
TariffGroupId pgtype.UUID `json:"tariffGroupId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TariffGroup struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
OrdinanceType string `json:"ordinanceType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ticket struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
CurrentState TicketValidationState `json:"currentState"`
|
||||||
|
Notes []string `json:"notes"`
|
||||||
|
Errors []string `json:"errors"`
|
||||||
|
IsUrgent bool `json:"isUrgent"`
|
||||||
|
Urgency time.Time `json:"urgency"`
|
||||||
|
ApprovalState NullApprovalState `json:"approvalState"`
|
||||||
|
DocumentInfo []byte `json:"documentInfo"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TicketStateHistory struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
State TicketValidationState `json:"state"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
TicketId pgtype.UUID `json:"ticketId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tour struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
Done bool `json:"done"`
|
||||||
|
Direction string `json:"direction"`
|
||||||
|
CarName pgtype.Text `json:"carName"`
|
||||||
|
OperationId string `json:"operationId"`
|
||||||
|
DriverId pgtype.Text `json:"driverId"`
|
||||||
|
DriverName pgtype.Text `json:"driverName"`
|
||||||
|
CodriverId pgtype.Text `json:"codriverId"`
|
||||||
|
CodriverName pgtype.Text `json:"codriverName"`
|
||||||
|
PatientId pgtype.Text `json:"patientId"`
|
||||||
|
PatientName pgtype.Text `json:"patientName"`
|
||||||
|
PatientSurname pgtype.Text `json:"patientSurname"`
|
||||||
|
PatientStreet pgtype.Text `json:"patientStreet"`
|
||||||
|
PatientZip pgtype.Text `json:"patientZip"`
|
||||||
|
PatientCity pgtype.Text `json:"patientCity"`
|
||||||
|
HealthInsurance pgtype.Text `json:"healthInsurance"`
|
||||||
|
HealthInsuranceNumber pgtype.Text `json:"healthInsuranceNumber"`
|
||||||
|
Type pgtype.Text `json:"type"`
|
||||||
|
Category pgtype.Text `json:"category"`
|
||||||
|
TransportType pgtype.Text `json:"transportType"`
|
||||||
|
OrdinanceType pgtype.Text `json:"ordinanceType"`
|
||||||
|
RangeEndDate *time.Time `json:"rangeEndDate"`
|
||||||
|
StartInstitution pgtype.Text `json:"startInstitution"`
|
||||||
|
StartStreet pgtype.Text `json:"startStreet"`
|
||||||
|
StartZip pgtype.Text `json:"startZip"`
|
||||||
|
StartCity pgtype.Text `json:"startCity"`
|
||||||
|
TargetInstitution pgtype.Text `json:"targetInstitution"`
|
||||||
|
TargetStreet pgtype.Text `json:"targetStreet"`
|
||||||
|
TargetZip pgtype.Text `json:"targetZip"`
|
||||||
|
TargetCity pgtype.Text `json:"targetCity"`
|
||||||
|
StartDate time.Time `json:"startDate"`
|
||||||
|
Check int32 `json:"check"`
|
||||||
|
OccupiedKm pgtype.Float8 `json:"occupiedKm"`
|
||||||
|
TotalKm pgtype.Float8 `json:"totalKm"`
|
||||||
|
StartBegin pgtype.Text `json:"startBegin"`
|
||||||
|
StartEnd pgtype.Text `json:"startEnd"`
|
||||||
|
Target pgtype.Text `json:"target"`
|
||||||
|
TargetBegin pgtype.Text `json:"targetBegin"`
|
||||||
|
Empty pgtype.Text `json:"empty"`
|
||||||
|
HasInfection bool `json:"hasInfection"`
|
||||||
|
InfectionName pgtype.Text `json:"infectionName"`
|
||||||
|
RevenueDispoLive pgtype.Float8 `json:"revenueDispoLive"`
|
||||||
|
BillDate *time.Time `json:"billDate"`
|
||||||
|
BillNumber pgtype.Text `json:"billNumber"`
|
||||||
|
Revenue float64 `json:"revenue"`
|
||||||
|
ConsumptionCosts float64 `json:"consumptionCosts"`
|
||||||
|
RevenueDeviation float64 `json:"revenueDeviation"`
|
||||||
|
CreatedAt *time.Time `json:"createdAt"`
|
||||||
|
DeletedAt *time.Time `json:"deletedAt"`
|
||||||
|
TicketId pgtype.UUID `json:"ticketId"`
|
||||||
|
UpdatedAt *time.Time `json:"updatedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TourSource struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
InsertedAt time.Time `json:"insertedAt"`
|
||||||
|
JobId string `json:"jobId"`
|
||||||
|
ID pgtype.Text `json:"id"`
|
||||||
|
Check pgtype.Text `json:"check"`
|
||||||
|
CreateTime pgtype.Text `json:"createTime"`
|
||||||
|
EmpfangenVonId pgtype.Text `json:"empfangenVonId"`
|
||||||
|
Endgen pgtype.Text `json:"endgen"`
|
||||||
|
SerienId pgtype.Text `json:"serienId"`
|
||||||
|
SerienKategorName pgtype.Text `json:"serienKategorName"`
|
||||||
|
SerienKategorie pgtype.Text `json:"serienKategorie"`
|
||||||
|
ZadStatus pgtype.Text `json:"zadStatus"`
|
||||||
|
AbrDate pgtype.Text `json:"abrDate"`
|
||||||
|
AbrNum pgtype.Text `json:"abrNum"`
|
||||||
|
AbrTime pgtype.Text `json:"abrTime"`
|
||||||
|
AusrueckZeit pgtype.Text `json:"ausrueckZeit"`
|
||||||
|
StartTime pgtype.Text `json:"startTime"`
|
||||||
|
StartDate pgtype.Text `json:"startDate"`
|
||||||
|
Date pgtype.Text `json:"date"`
|
||||||
|
DateIso pgtype.Text `json:"date_iso"`
|
||||||
|
Dauergenehmigung pgtype.Text `json:"dauergenehmigung"`
|
||||||
|
Direction pgtype.Text `json:"direction"`
|
||||||
|
DistanceValue pgtype.Text `json:"distanceValue"`
|
||||||
|
BesetztKm pgtype.Text `json:"besetztKm"`
|
||||||
|
Gefahren pgtype.Text `json:"gefahren"`
|
||||||
|
GesamtPreis pgtype.Text `json:"gesamtPreis"`
|
||||||
|
Infektion pgtype.Text `json:"infektion"`
|
||||||
|
ItDesc pgtype.Text `json:"itDesc"`
|
||||||
|
ItKrz pgtype.Text `json:"itKrz"`
|
||||||
|
ItName pgtype.Text `json:"itName"`
|
||||||
|
KkId pgtype.Text `json:"kkId"`
|
||||||
|
Krankenkasse pgtype.Text `json:"krankenkasse"`
|
||||||
|
PatId pgtype.Text `json:"patId"`
|
||||||
|
PatName pgtype.Text `json:"patName"`
|
||||||
|
PatSurname pgtype.Text `json:"patSurname"`
|
||||||
|
PatStreet pgtype.Text `json:"patStreet"`
|
||||||
|
PatZip pgtype.Text `json:"patZip"`
|
||||||
|
PatCity pgtype.Text `json:"patCity"`
|
||||||
|
StartInstitution pgtype.Text `json:"startInstitution"`
|
||||||
|
StartStreet pgtype.Text `json:"startStreet"`
|
||||||
|
StartZip pgtype.Text `json:"startZip"`
|
||||||
|
StartCity pgtype.Text `json:"startCity"`
|
||||||
|
TargetInstitution pgtype.Text `json:"targetInstitution"`
|
||||||
|
TargetStreet pgtype.Text `json:"targetStreet"`
|
||||||
|
TargetZip pgtype.Text `json:"targetZip"`
|
||||||
|
TargetCity pgtype.Text `json:"targetCity"`
|
||||||
|
TransportKrz pgtype.Text `json:"transportKrz"`
|
||||||
|
TransportName pgtype.Text `json:"transportName"`
|
||||||
|
Transportart pgtype.Text `json:"transportart"`
|
||||||
|
VerordnungsId pgtype.Text `json:"verordnungsId"`
|
||||||
|
VerordnungsName pgtype.Text `json:"verordnungsName"`
|
||||||
|
Type pgtype.Text `json:"type"`
|
||||||
|
StartBegin pgtype.Text `json:"startBegin"`
|
||||||
|
StartEnd pgtype.Text `json:"startEnd"`
|
||||||
|
Frei pgtype.Text `json:"frei"`
|
||||||
|
Ziel pgtype.Text `json:"ziel"`
|
||||||
|
ZielBegin pgtype.Text `json:"zielBegin"`
|
||||||
|
EndTime pgtype.Text `json:"endTime"`
|
||||||
|
EndDate pgtype.Text `json:"endDate"`
|
||||||
|
EinsatzNummer pgtype.Text `json:"einsatzNummer"`
|
||||||
|
CarName pgtype.Text `json:"carName"`
|
||||||
|
CarNo pgtype.Text `json:"carNo"`
|
||||||
|
BeifahrerName pgtype.Text `json:"beifahrerName"`
|
||||||
|
Beifahrer pgtype.Text `json:"beifahrer"`
|
||||||
|
FahrerName pgtype.Text `json:"fahrerName"`
|
||||||
|
Fahrer pgtype.Text `json:"fahrer"`
|
||||||
|
Color pgtype.Text `json:"color"`
|
||||||
|
LastUpdate pgtype.Text `json:"lastUpdate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TourStaging struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
ID pgtype.Text `json:"id"`
|
||||||
|
Done pgtype.Bool `json:"done"`
|
||||||
|
Direction pgtype.Text `json:"direction"`
|
||||||
|
CarName pgtype.Text `json:"carName"`
|
||||||
|
OperationId pgtype.Text `json:"operationId"`
|
||||||
|
DriverId pgtype.Text `json:"driverId"`
|
||||||
|
DriverName pgtype.Text `json:"driverName"`
|
||||||
|
CodriverId pgtype.Text `json:"codriverId"`
|
||||||
|
CodriverName pgtype.Text `json:"codriverName"`
|
||||||
|
PatientId pgtype.Text `json:"patientId"`
|
||||||
|
PatientName pgtype.Text `json:"patientName"`
|
||||||
|
PatientSurname pgtype.Text `json:"patientSurname"`
|
||||||
|
PatientStreet pgtype.Text `json:"patientStreet"`
|
||||||
|
PatientZip pgtype.Text `json:"patientZip"`
|
||||||
|
PatientCity pgtype.Text `json:"patientCity"`
|
||||||
|
HealthInsurance pgtype.Text `json:"healthInsurance"`
|
||||||
|
HealthInsuranceNumber pgtype.Text `json:"healthInsuranceNumber"`
|
||||||
|
Type pgtype.Text `json:"type"`
|
||||||
|
Category pgtype.Text `json:"category"`
|
||||||
|
TransportType pgtype.Text `json:"transportType"`
|
||||||
|
OrdinanceType pgtype.Text `json:"ordinanceType"`
|
||||||
|
RangeEndDate *time.Time `json:"rangeEndDate"`
|
||||||
|
StartInstitution pgtype.Text `json:"startInstitution"`
|
||||||
|
StartStreet pgtype.Text `json:"startStreet"`
|
||||||
|
StartZip pgtype.Text `json:"startZip"`
|
||||||
|
StartCity pgtype.Text `json:"startCity"`
|
||||||
|
TargetInstitution pgtype.Text `json:"targetInstitution"`
|
||||||
|
TargetStreet pgtype.Text `json:"targetStreet"`
|
||||||
|
TargetZip pgtype.Text `json:"targetZip"`
|
||||||
|
TargetCity pgtype.Text `json:"targetCity"`
|
||||||
|
StartDate *time.Time `json:"startDate"`
|
||||||
|
Check int32 `json:"check"`
|
||||||
|
OccupiedKm pgtype.Float8 `json:"occupiedKm"`
|
||||||
|
TotalKm pgtype.Float8 `json:"totalKm"`
|
||||||
|
StartBegin pgtype.Text `json:"startBegin"`
|
||||||
|
StartEnd pgtype.Text `json:"startEnd"`
|
||||||
|
Target pgtype.Text `json:"target"`
|
||||||
|
TargetBegin pgtype.Text `json:"targetBegin"`
|
||||||
|
Empty pgtype.Text `json:"empty"`
|
||||||
|
HasInfection bool `json:"hasInfection"`
|
||||||
|
InfectionName pgtype.Text `json:"infectionName"`
|
||||||
|
RevenueDispoLive pgtype.Float8 `json:"revenueDispoLive"`
|
||||||
|
BillDate *time.Time `json:"billDate"`
|
||||||
|
BillNumber pgtype.Text `json:"billNumber"`
|
||||||
|
Revenue float64 `json:"revenue"`
|
||||||
|
RevenueDeviation float64 `json:"revenueDeviation"`
|
||||||
|
ConsumptionCosts float64 `json:"consumptionCosts"`
|
||||||
|
CreatedAt *time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt *time.Time `json:"updatedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Trace struct {
|
||||||
|
ID pgtype.UUID `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
EntityKey string `json:"entityKey"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkTime struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
Firstname string `json:"firstname"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
StartDate *time.Time `json:"startDate"`
|
||||||
|
EndDate *time.Time `json:"endDate"`
|
||||||
|
Department pgtype.Text `json:"department"`
|
||||||
|
Pause int32 `json:"pause"`
|
||||||
|
Duration int32 `json:"duration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkTimeSource struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
InsertedAt time.Time `json:"insertedAt"`
|
||||||
|
JobId string `json:"jobId"`
|
||||||
|
ID pgtype.Text `json:"id"`
|
||||||
|
UserID pgtype.Text `json:"user_id"`
|
||||||
|
Firstname pgtype.Text `json:"firstname"`
|
||||||
|
Surname pgtype.Text `json:"surname"`
|
||||||
|
DepartmentID pgtype.Text `json:"department_id"`
|
||||||
|
DepartmentName pgtype.Text `json:"department_name"`
|
||||||
|
StartDate pgtype.Text `json:"start_date"`
|
||||||
|
EndDate pgtype.Text `json:"end_date"`
|
||||||
|
Pauze pgtype.Text `json:"pauze"`
|
||||||
|
Duration pgtype.Text `json:"duration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkTimeStaging struct {
|
||||||
|
InternalId pgtype.UUID `json:"internalId"`
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
EmployeeId int32 `json:"employeeId"`
|
||||||
|
Firstname string `json:"firstname"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
DepartmentName pgtype.Text `json:"departmentName"`
|
||||||
|
StartDate time.Time `json:"startDate"`
|
||||||
|
EndDate time.Time `json:"endDate"`
|
||||||
|
Duration int32 `json:"duration"`
|
||||||
|
Pause int32 `json:"pause"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,490 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
// source: payroll.sql
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
const countTourengeldAmountInPeriod = `-- name: CountTourengeldAmountInPeriod :one
|
||||||
|
WITH ToursInPeriod AS (
|
||||||
|
SELECT
|
||||||
|
(("startDate" AT TIME ZONE 'UTC') AT TIME ZONE 'Europe/Berlin')::DATE as day,
|
||||||
|
"operationId",
|
||||||
|
"occupiedKm",
|
||||||
|
CASE
|
||||||
|
WHEN "occupiedKm" <= 30 THEN 1.0
|
||||||
|
ELSE ROUND("occupiedKm" / 30.0)
|
||||||
|
END AS tour_value,
|
||||||
|
CASE
|
||||||
|
WHEN EXTRACT(ISODOW FROM "startDate") = 6 AND "occupiedKm" <= 30 THEN 0.5
|
||||||
|
ELSE 0.0
|
||||||
|
END AS weekend_bonus,
|
||||||
|
"infectionName",
|
||||||
|
CASE
|
||||||
|
WHEN "infectionName" = 'Covid-19' THEN 0.5
|
||||||
|
ELSE 0.0
|
||||||
|
END AS infection_bonus
|
||||||
|
FROM
|
||||||
|
"Tour"
|
||||||
|
WHERE
|
||||||
|
"check" <> 3
|
||||||
|
AND "deletedAt" IS NULL
|
||||||
|
AND (
|
||||||
|
"patientId" IS NULL
|
||||||
|
OR "patientId" NOT IN (
|
||||||
|
'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
'3554e1a9-9c07-45e5-b53c-692a77e4fa9a'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
-- ID's for Pause and Feierabend respectively
|
||||||
|
AND (
|
||||||
|
"carName" IS NULL
|
||||||
|
OR "carName" NOT IN (
|
||||||
|
'Storno',
|
||||||
|
'Abgabe',
|
||||||
|
'Löschen'
|
||||||
|
)
|
||||||
|
AND "carName" LIKE '%KTW%'
|
||||||
|
)
|
||||||
|
AND "startDate" >= $1
|
||||||
|
AND "startDate" <= $2
|
||||||
|
AND ("driverId" ILIKE $3 OR "codriverId" ILIKE $3)
|
||||||
|
ORDER BY
|
||||||
|
"day"
|
||||||
|
),
|
||||||
|
DailyCounts AS (
|
||||||
|
SELECT
|
||||||
|
day,
|
||||||
|
SUM(tour_value) + SUM(weekend_bonus) + SUM(infection_bonus) AS tour_amount
|
||||||
|
FROM
|
||||||
|
ToursInPeriod
|
||||||
|
GROUP BY
|
||||||
|
day
|
||||||
|
),
|
||||||
|
HighActivityDays AS (
|
||||||
|
SELECT
|
||||||
|
day,
|
||||||
|
GREATEST(0, tour_amount - 6) AS tourengeld
|
||||||
|
FROM
|
||||||
|
DailyCounts
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
SUM(tourengeld)::float as tourengeld
|
||||||
|
FROM
|
||||||
|
HighActivityDays
|
||||||
|
`
|
||||||
|
|
||||||
|
type CountTourengeldAmountInPeriodParams struct {
|
||||||
|
StartDate time.Time `json:"start_date"`
|
||||||
|
EndDate time.Time `json:"end_date"`
|
||||||
|
EmployeeID pgtype.Text `json:"employee_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CountTourengeldAmountInPeriod(ctx context.Context, arg CountTourengeldAmountInPeriodParams) (float64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countTourengeldAmountInPeriod, arg.StartDate, arg.EndDate, arg.EmployeeID)
|
||||||
|
var tourengeld float64
|
||||||
|
err := row.Scan(&tourengeld)
|
||||||
|
return tourengeld, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchDrivenToursInInterval = `-- name: FetchDrivenToursInInterval :many
|
||||||
|
SELECT id, done, direction, "carName", "operationId", "driverId", "driverName", "codriverId", "codriverName", "patientId", "patientName", "patientSurname", "patientStreet", "patientZip", "patientCity", "healthInsurance", "healthInsuranceNumber", type, category, "transportType", "ordinanceType", "rangeEndDate", "startInstitution", "startStreet", "startZip", "startCity", "targetInstitution", "targetStreet", "targetZip", "targetCity", "startDate", "check", "occupiedKm", "totalKm", "startBegin", "startEnd", target, "targetBegin", empty, "hasInfection", "infectionName", "revenueDispoLive", "billDate", "billNumber", revenue, "consumptionCosts", "revenueDeviation", "createdAt", "deletedAt", "ticketId", "updatedAt" FROM "Tour"
|
||||||
|
WHERE "check" <> 3
|
||||||
|
AND "deletedAt" IS NULL
|
||||||
|
AND (
|
||||||
|
"patientId" IS NULL
|
||||||
|
OR "patientId" NOT IN (
|
||||||
|
'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
'3554e1a9-9c07-45e5-b53c-692a77e4fa9a'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
"carName" IS NULL
|
||||||
|
OR "carName" NOT IN (
|
||||||
|
'Storno',
|
||||||
|
'Abgabe',
|
||||||
|
'Löschen'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND "startDate" >= $1
|
||||||
|
AND "startDate" <= $2
|
||||||
|
AND ("driverId" = $3 OR "codriverId" = $3)
|
||||||
|
ORDER BY "startDate" DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
type FetchDrivenToursInIntervalParams struct {
|
||||||
|
StartDate time.Time `json:"start_date"`
|
||||||
|
EndDate time.Time `json:"end_date"`
|
||||||
|
EmployeeID pgtype.Text `json:"employee_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID's for Pause and Feierabend respectively
|
||||||
|
func (q *Queries) FetchDrivenToursInInterval(ctx context.Context, arg FetchDrivenToursInIntervalParams) ([]Tour, error) {
|
||||||
|
rows, err := q.db.Query(ctx, fetchDrivenToursInInterval, arg.StartDate, arg.EndDate, arg.EmployeeID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Tour
|
||||||
|
for rows.Next() {
|
||||||
|
var i Tour
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Done,
|
||||||
|
&i.Direction,
|
||||||
|
&i.CarName,
|
||||||
|
&i.OperationId,
|
||||||
|
&i.DriverId,
|
||||||
|
&i.DriverName,
|
||||||
|
&i.CodriverId,
|
||||||
|
&i.CodriverName,
|
||||||
|
&i.PatientId,
|
||||||
|
&i.PatientName,
|
||||||
|
&i.PatientSurname,
|
||||||
|
&i.PatientStreet,
|
||||||
|
&i.PatientZip,
|
||||||
|
&i.PatientCity,
|
||||||
|
&i.HealthInsurance,
|
||||||
|
&i.HealthInsuranceNumber,
|
||||||
|
&i.Type,
|
||||||
|
&i.Category,
|
||||||
|
&i.TransportType,
|
||||||
|
&i.OrdinanceType,
|
||||||
|
&i.RangeEndDate,
|
||||||
|
&i.StartInstitution,
|
||||||
|
&i.StartStreet,
|
||||||
|
&i.StartZip,
|
||||||
|
&i.StartCity,
|
||||||
|
&i.TargetInstitution,
|
||||||
|
&i.TargetStreet,
|
||||||
|
&i.TargetZip,
|
||||||
|
&i.TargetCity,
|
||||||
|
&i.StartDate,
|
||||||
|
&i.Check,
|
||||||
|
&i.OccupiedKm,
|
||||||
|
&i.TotalKm,
|
||||||
|
&i.StartBegin,
|
||||||
|
&i.StartEnd,
|
||||||
|
&i.Target,
|
||||||
|
&i.TargetBegin,
|
||||||
|
&i.Empty,
|
||||||
|
&i.HasInfection,
|
||||||
|
&i.InfectionName,
|
||||||
|
&i.RevenueDispoLive,
|
||||||
|
&i.BillDate,
|
||||||
|
&i.BillNumber,
|
||||||
|
&i.Revenue,
|
||||||
|
&i.ConsumptionCosts,
|
||||||
|
&i.RevenueDeviation,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.DeletedAt,
|
||||||
|
&i.TicketId,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const getEmployeeByName = `-- name: GetEmployeeByName :one
|
||||||
|
SELECT
|
||||||
|
dy.id as dyflexis_id,
|
||||||
|
dl.id as dispolive_id,
|
||||||
|
dy."contractSalaryHour"
|
||||||
|
FROM "DyflexisEmployee" as dy
|
||||||
|
INNER JOIN
|
||||||
|
"Employee" as dl ON dy.id::text = dl."personnelNumber"
|
||||||
|
WHERE
|
||||||
|
TRIM(dy.firstname) = $1 AND
|
||||||
|
TRIM(dy.surname) = $2 AND
|
||||||
|
"contractStart" < CURRENT_DATE AND
|
||||||
|
"contractEnd" > (CURRENT_DATE - INTERVAL '2 months')
|
||||||
|
ORDER BY dy."contractEnd" DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetEmployeeByNameParams struct {
|
||||||
|
Firstname string `json:"firstname"`
|
||||||
|
Lastname string `json:"lastname"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetEmployeeByNameRow struct {
|
||||||
|
DyflexisID int32 `json:"dyflexis_id"`
|
||||||
|
DispoliveID string `json:"dispolive_id"`
|
||||||
|
ContractSalaryHour float64 `json:"contractSalaryHour"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetEmployeeByName(ctx context.Context, arg GetEmployeeByNameParams) (GetEmployeeByNameRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getEmployeeByName, arg.Firstname, arg.Lastname)
|
||||||
|
var i GetEmployeeByNameRow
|
||||||
|
err := row.Scan(&i.DyflexisID, &i.DispoliveID, &i.ContractSalaryHour)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getEmployeeDyflexis = `-- name: GetEmployeeDyflexis :one
|
||||||
|
SELECT id, firstname, surname, "contractStart", "contractEnd", "contractHoursWeek", "contractSalaryHour", "contractTypeName", "internalId" FROM "DyflexisEmployee"
|
||||||
|
WHERE
|
||||||
|
id = $1 AND
|
||||||
|
"contractStart" < CURRENT_DATE AND
|
||||||
|
"contractEnd" > (CURRENT_DATE - INTERVAL '2 months')
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetEmployeeDyflexis(ctx context.Context, id int32) (DyflexisEmployee, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getEmployeeDyflexis, id)
|
||||||
|
var i DyflexisEmployee
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Firstname,
|
||||||
|
&i.Surname,
|
||||||
|
&i.ContractStart,
|
||||||
|
&i.ContractEnd,
|
||||||
|
&i.ContractHoursWeek,
|
||||||
|
&i.ContractSalaryHour,
|
||||||
|
&i.ContractTypeName,
|
||||||
|
&i.InternalId,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSicknessSummaryForEmployeeInPeriod = `-- name: GetSicknessSummaryForEmployeeInPeriod :one
|
||||||
|
WITH pt AS (
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as days,
|
||||||
|
COALESCE(sum(duration), 0)::bigint as minutes
|
||||||
|
FROM "PlannedTime"
|
||||||
|
WHERE "userId" = $1 AND "startDate" > $2 AND "startDate" < $3 AND note = 'sick'
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
pt.days,
|
||||||
|
(minutes / 60)::float as hours,
|
||||||
|
pt.minutes
|
||||||
|
FROM pt
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetSicknessSummaryForEmployeeInPeriodParams struct {
|
||||||
|
EmployeeID string `json:"employee_id"`
|
||||||
|
StartDate *time.Time `json:"start_date"`
|
||||||
|
EndDate *time.Time `json:"end_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSicknessSummaryForEmployeeInPeriodRow struct {
|
||||||
|
Days int64 `json:"days"`
|
||||||
|
Hours float64 `json:"hours"`
|
||||||
|
Minutes int64 `json:"minutes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetSicknessSummaryForEmployeeInPeriod(ctx context.Context, arg GetSicknessSummaryForEmployeeInPeriodParams) (GetSicknessSummaryForEmployeeInPeriodRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getSicknessSummaryForEmployeeInPeriod, arg.EmployeeID, arg.StartDate, arg.EndDate)
|
||||||
|
var i GetSicknessSummaryForEmployeeInPeriodRow
|
||||||
|
err := row.Scan(&i.Days, &i.Hours, &i.Minutes)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getWorkSummaryForEmployeeInPeriod = `-- name: GetWorkSummaryForEmployeeInPeriod :one
|
||||||
|
WITH wt AS (
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as days,
|
||||||
|
COALESCE(sum(duration), 0)::bigint as minutes
|
||||||
|
FROM "WorkTime"
|
||||||
|
WHERE "userId" = $1 AND "startDate" > $2 AND "startDate" < $3
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
wt.days,
|
||||||
|
(minutes / 60.0)::float as hours,
|
||||||
|
wt.minutes
|
||||||
|
FROM wt
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetWorkSummaryForEmployeeInPeriodParams struct {
|
||||||
|
EmployeeID string `json:"employee_id"`
|
||||||
|
StartDate *time.Time `json:"start_date"`
|
||||||
|
EndDate *time.Time `json:"end_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetWorkSummaryForEmployeeInPeriodRow struct {
|
||||||
|
Days int64 `json:"days"`
|
||||||
|
Hours float64 `json:"hours"`
|
||||||
|
Minutes int64 `json:"minutes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetWorkSummaryForEmployeeInPeriod(ctx context.Context, arg GetWorkSummaryForEmployeeInPeriodParams) (GetWorkSummaryForEmployeeInPeriodRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getWorkSummaryForEmployeeInPeriod, arg.EmployeeID, arg.StartDate, arg.EndDate)
|
||||||
|
var i GetWorkSummaryForEmployeeInPeriodRow
|
||||||
|
err := row.Scan(&i.Days, &i.Hours, &i.Minutes)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const listActiveEmployees = `-- name: ListActiveEmployees :many
|
||||||
|
SELECT
|
||||||
|
de.id,
|
||||||
|
de.firstname,
|
||||||
|
de.surname,
|
||||||
|
de."contractStart",
|
||||||
|
de."contractEnd",
|
||||||
|
de."contractHoursWeek",
|
||||||
|
de."contractSalaryHour",
|
||||||
|
de."contractTypeName",
|
||||||
|
e.id as "dlId",
|
||||||
|
e."personTransportCertificate"
|
||||||
|
FROM "DyflexisEmployee" as de
|
||||||
|
INNER JOIN "Employee" as e ON de.id::text = e."personnelNumber"
|
||||||
|
WHERE de."contractStart" < CURRENT_DATE AND de."contractEnd" > (CURRENT_DATE - INTERVAL '2 months')
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListActiveEmployeesRow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Firstname string `json:"firstname"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
ContractStart time.Time `json:"contractStart"`
|
||||||
|
ContractEnd *time.Time `json:"contractEnd"`
|
||||||
|
ContractHoursWeek float64 `json:"contractHoursWeek"`
|
||||||
|
ContractSalaryHour float64 `json:"contractSalaryHour"`
|
||||||
|
ContractTypeName pgtype.Text `json:"contractTypeName"`
|
||||||
|
DlId string `json:"dlId"`
|
||||||
|
PersonTransportCertificate *time.Time `json:"personTransportCertificate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListActiveEmployees(ctx context.Context) ([]ListActiveEmployeesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listActiveEmployees)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ListActiveEmployeesRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i ListActiveEmployeesRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Firstname,
|
||||||
|
&i.Surname,
|
||||||
|
&i.ContractStart,
|
||||||
|
&i.ContractEnd,
|
||||||
|
&i.ContractHoursWeek,
|
||||||
|
&i.ContractSalaryHour,
|
||||||
|
&i.ContractTypeName,
|
||||||
|
&i.DlId,
|
||||||
|
&i.PersonTransportCertificate,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listPlannedTimesForEmployeeInPeriod = `-- name: ListPlannedTimesForEmployeeInPeriod :many
|
||||||
|
SELECT
|
||||||
|
DISTINCT ON (day)
|
||||||
|
"startDate"::DATE as day,
|
||||||
|
"startDate",
|
||||||
|
"endDate",
|
||||||
|
department,
|
||||||
|
pause,
|
||||||
|
duration
|
||||||
|
FROM "PlannedTime"
|
||||||
|
WHERE
|
||||||
|
"userId" = $1 AND
|
||||||
|
"startDate" > $2 AND
|
||||||
|
"startDate" < $3 AND
|
||||||
|
department <> 'Fortbildung' AND
|
||||||
|
note IS NULL
|
||||||
|
ORDER BY "day"
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListPlannedTimesForEmployeeInPeriodParams struct {
|
||||||
|
EmployeeID string `json:"employee_id"`
|
||||||
|
StartDate *time.Time `json:"start_date"`
|
||||||
|
EndDate *time.Time `json:"end_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListPlannedTimesForEmployeeInPeriodRow struct {
|
||||||
|
Day pgtype.Date `json:"day"`
|
||||||
|
StartDate *time.Time `json:"startDate"`
|
||||||
|
EndDate *time.Time `json:"endDate"`
|
||||||
|
Department pgtype.Text `json:"department"`
|
||||||
|
Pause int32 `json:"pause"`
|
||||||
|
Duration int32 `json:"duration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListPlannedTimesForEmployeeInPeriod(ctx context.Context, arg ListPlannedTimesForEmployeeInPeriodParams) ([]ListPlannedTimesForEmployeeInPeriodRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listPlannedTimesForEmployeeInPeriod, arg.EmployeeID, arg.StartDate, arg.EndDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ListPlannedTimesForEmployeeInPeriodRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i ListPlannedTimesForEmployeeInPeriodRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.Day,
|
||||||
|
&i.StartDate,
|
||||||
|
&i.EndDate,
|
||||||
|
&i.Department,
|
||||||
|
&i.Pause,
|
||||||
|
&i.Duration,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listWorkTimesForEmployeeInPeriod = `-- name: ListWorkTimesForEmployeeInPeriod :many
|
||||||
|
SELECT id, "userId", firstname, surname, "startDate", "endDate", department, pause, duration FROM "WorkTime"
|
||||||
|
WHERE "userId" = $1 AND "startDate" > $2 AND "startDate" < $3
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListWorkTimesForEmployeeInPeriodParams struct {
|
||||||
|
EmployeeID string `json:"employee_id"`
|
||||||
|
StartDate *time.Time `json:"start_date"`
|
||||||
|
EndDate *time.Time `json:"end_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListWorkTimesForEmployeeInPeriod(ctx context.Context, arg ListWorkTimesForEmployeeInPeriodParams) ([]WorkTime, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listWorkTimesForEmployeeInPeriod, arg.EmployeeID, arg.StartDate, arg.EndDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []WorkTime
|
||||||
|
for rows.Next() {
|
||||||
|
var i WorkTime
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.UserId,
|
||||||
|
&i.Firstname,
|
||||||
|
&i.Surname,
|
||||||
|
&i.StartDate,
|
||||||
|
&i.EndDate,
|
||||||
|
&i.Department,
|
||||||
|
&i.Pause,
|
||||||
|
&i.Duration,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package excelread
|
||||||
|
|
||||||
|
import (
|
||||||
|
"business-engine/internal/database"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gocarina/gocsv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sortSliceByDate[T any](slice []T, getDate func(T) time.Time) {
|
||||||
|
compare := func(a, b T) int {
|
||||||
|
aDate := getDate(a)
|
||||||
|
bDate := getDate(b)
|
||||||
|
|
||||||
|
if aDate.Before(bDate) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if aDate.After(bDate) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
slices.SortFunc(slice, compare)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zu kalkulieren
|
||||||
|
// - Puenktlichkeitspraemie
|
||||||
|
// - Tourengeld Anzahl
|
||||||
|
// - Tourengeld Bonus
|
||||||
|
func GeneratePayrollStatement(data EmployeeDataRow, input PayrollInputData) PayrollStatement {
|
||||||
|
isKTW := slices.IndexFunc(input.Plannedtimes, func(pt database.ListPlannedTimesForEmployeeInPeriodRow) bool {
|
||||||
|
return strings.Contains(pt.Department.String, "KTW")
|
||||||
|
}) >= 0 && slices.IndexFunc(input.Plannedtimes, func(pt database.ListPlannedTimesForEmployeeInPeriodRow) bool {
|
||||||
|
return strings.Contains(pt.Department.String, "Leitstelle")
|
||||||
|
}) == -1
|
||||||
|
|
||||||
|
statement := PayrollStatement{
|
||||||
|
Nachname: data.Nachname,
|
||||||
|
Vorname: data.Vorname,
|
||||||
|
Verpflegungspauschale: data.AuszahlenVerpflegungspauschaleTage,
|
||||||
|
VertragStundenlohn: input.ContractSalaryHour,
|
||||||
|
Vertragsstunden: data.IstSollVertragsStunden,
|
||||||
|
GearbeiteteStunden: data.GearbeiteteStunden,
|
||||||
|
GearbeiteteTage: data.GearbeiteteTage,
|
||||||
|
Krankheitstage: data.Krankheitstage,
|
||||||
|
Ueberstunden: data.IstSollPlusMinusStunden,
|
||||||
|
Wochenendzuschlag15: data.AuszahlenZuschlag15Stunden,
|
||||||
|
Sonntagszuschlag50: data.AuszahlenZuschlag50Stunden,
|
||||||
|
Nachtzuschlag25: data.AuszahlenZuschlag25Stunden,
|
||||||
|
Nachtzuschlag40: data.AuszahlenZuschlag40Stunden,
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Krankheitstage > 0 || !isKTW {
|
||||||
|
statement.GesundheitsbonusAbzug = "ja"
|
||||||
|
}
|
||||||
|
|
||||||
|
if isKTW && data.GenommeneUrlaubstage > 0 {
|
||||||
|
statement.Urlaubsgeld = data.GenommeneUrlaubstage
|
||||||
|
}
|
||||||
|
|
||||||
|
if isKTW && data.AuszahlenAnzahlPSchein > 0 {
|
||||||
|
statement.PScheinZulage = data.AuszahlenAnzahlPSchein
|
||||||
|
}
|
||||||
|
|
||||||
|
if isKTW && input.Tourengeld > 0 {
|
||||||
|
statement.Tourengeld = float64(input.Tourengeld)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isKTW && len(input.Worktimes) > 0 && len(input.Plannedtimes) > 0 {
|
||||||
|
ptGetStartDate := func(pt database.ListPlannedTimesForEmployeeInPeriodRow) time.Time {
|
||||||
|
return *pt.StartDate
|
||||||
|
}
|
||||||
|
wtGetStartDate := func(pt database.WorkTime) time.Time {
|
||||||
|
return *pt.StartDate
|
||||||
|
}
|
||||||
|
sortSliceByDate(input.Plannedtimes, ptGetStartDate)
|
||||||
|
sortSliceByDate(input.Worktimes, wtGetStartDate)
|
||||||
|
|
||||||
|
var employeeOnTime []EmployeeOnTime
|
||||||
|
for _, pt := range input.Plannedtimes {
|
||||||
|
for _, wt := range input.Worktimes {
|
||||||
|
if isSameDay(ptGetStartDate(pt), wtGetStartDate(wt)) && (wtGetStartDate(wt).Before(ptGetStartDate(pt)) || wtGetStartDate(wt) == ptGetStartDate(pt)) {
|
||||||
|
statement.PuenktlichkeitspraemieAnzahl++
|
||||||
|
|
||||||
|
employeeOnTime = append(employeeOnTime, EmployeeOnTime{
|
||||||
|
EmployeeId: fmt.Sprintf("%s %s", data.Vorname, data.Nachname),
|
||||||
|
WorkTime: *wt.StartDate,
|
||||||
|
PlannedTime: *pt.StartDate,
|
||||||
|
IsOnTime: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.Vorname == "David" && data.Nachname == "Hastedt" {
|
||||||
|
GenerateEmployeesOnTime(employeeOnTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateEmployeesOnTime(employeesontime []EmployeeOnTime) error {
|
||||||
|
file, err := os.Create("employees_on_time.csv")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if err := gocsv.MarshalFile(&employeesontime, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isSameDay prüft, ob zwei Zeitpunkte am selben Kalendertag liegen.
|
||||||
|
func isSameDay(t1, t2 time.Time) bool {
|
||||||
|
return t1.Year() == t2.Year() && t1.Month() == t2.Month() && t1.Day() == t2.Day()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
package excelread
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mapRowToStruct konvertiert eine einzelne CSV-Zeile (als []string)
|
||||||
|
// in die EmployeeDataRow-Struct.
|
||||||
|
func mapRowToStruct(row []string) EmployeeDataRow {
|
||||||
|
data := EmployeeDataRow{}
|
||||||
|
|
||||||
|
data.Personalnummer = parseInt(row[0])
|
||||||
|
data.Nachname = strings.TrimSpace(row[1])
|
||||||
|
data.Vorname = strings.TrimSpace(row[2])
|
||||||
|
data.Vertragsart = strings.TrimSpace(row[3])
|
||||||
|
data.HatPersonenbefoerderungsschein = strings.TrimSpace(row[4])
|
||||||
|
data.HatRechtAufZuschlaege = strings.TrimSpace(row[5])
|
||||||
|
data.Vertragsstart = strings.TrimSpace(row[6])
|
||||||
|
data.VertragstageProWoche = parseFloat(row[7])
|
||||||
|
data.VertragsstundenProWoche = parseFloat(row[8])
|
||||||
|
data.VertragsstundenMax = strings.TrimSpace(row[9])
|
||||||
|
data.UrlaubstageProJahr = parseFloat(row[10])
|
||||||
|
data.GearbeiteteTage = parseFloat(row[11])
|
||||||
|
data.GearbeiteteStunden = parseFloat(row[12])
|
||||||
|
data.GenommeneUrlaubstage = parseFloat(row[13])
|
||||||
|
data.GenommeneUrlaubsstunden = parseFloat(row[14])
|
||||||
|
data.Krankheitstage = parseFloat(row[15])
|
||||||
|
data.Krankheitsstunden = parseFloat(row[16])
|
||||||
|
|
||||||
|
// data.KrankengeldTage = parseFloat(row[17])
|
||||||
|
// data.KrankengeldStunden = parseFloat(row[18])
|
||||||
|
// data.UnbezahlterUrlaubTage = parseFloat(row[19])
|
||||||
|
// data.UnbezahlterUrlaubStunden = parseFloat(row[20])
|
||||||
|
|
||||||
|
data.IstSollGesamtTage = parseFloat(row[17])
|
||||||
|
data.IstSollGesamtStunden = parseFloat(row[18])
|
||||||
|
data.IstSollVertragsTage = parseFloat(row[19])
|
||||||
|
data.IstSollVertragsStunden = parseFloat(row[20])
|
||||||
|
data.IstSollPlusMinusStunden = parseFloat(row[21])
|
||||||
|
data.BerechnungMehrstunden = parseFloat(row[22])
|
||||||
|
data.BerechnungUeberstunden = parseFloat(row[23])
|
||||||
|
data.BerechnungAnzahlPSchein = parseFloat(row[24])
|
||||||
|
data.BerechnungKrankOhneAUTage = parseFloat(row[25])
|
||||||
|
data.BerechnungKrankOhneAUStunden = parseFloat(row[26])
|
||||||
|
data.BerechnungNachtzuschlag25Tage = parseFloat(row[27])
|
||||||
|
data.BerechnungNachtzuschlag25Stunden = parseFloat(row[28])
|
||||||
|
data.BerechnungNachtzuschlag40Tage = parseFloat(row[29])
|
||||||
|
data.BerechnungNachtzuschlag40Stunden = parseFloat(row[30])
|
||||||
|
data.BerechnungSonntagszuschlag50Tage = parseFloat(row[31])
|
||||||
|
data.BerechnungSonntagszuschlag50Stunden = parseFloat(row[32])
|
||||||
|
data.BerechnungVerpflegungspauschale14Tage = parseFloat(row[33])
|
||||||
|
data.BerechnungSamstagszuschlag15Tage = parseFloat(row[34])
|
||||||
|
data.BerechnungSamstagszuschlag15Stunden = parseFloat(row[35])
|
||||||
|
data.BerechnungZeitausgleichStart = parseFloat(row[36])
|
||||||
|
data.BerechnungZeitausgleichPlusMinus = parseFloat(row[37])
|
||||||
|
data.BerechnungZeitausgleichAenderungen = parseFloat(row[38])
|
||||||
|
data.BerechnungZeitausgleichAuszahlen = parseFloat(row[39])
|
||||||
|
data.BerechnungZeitausgleichJetzt = parseFloat(row[40])
|
||||||
|
data.SaldenUrlaubStart = parseFloat(row[41])
|
||||||
|
data.SaldenUrlaubAbbau = parseFloat(row[42])
|
||||||
|
|
||||||
|
data.SaldenUrlaubKorrektur = parseFloat(row[43])
|
||||||
|
|
||||||
|
data.SaldenUrlaubJetzt = parseFloat(row[44])
|
||||||
|
data.SaldenMehrstunden = parseFloat(row[45])
|
||||||
|
data.SaldenUeberstunden = parseFloat(row[46])
|
||||||
|
data.AuszahlenFahrtkostenKm = parseFloat(row[47])
|
||||||
|
data.AuszahlenVerpflegungspauschaleTage = parseFloat(row[48])
|
||||||
|
data.AuszahlenAnzahlPSchein = parseFloat(row[49])
|
||||||
|
data.AuszahlenZuschlag25Stunden = parseFloat(row[50])
|
||||||
|
data.AuszahlenZuschlag40Stunden = parseFloat(row[51])
|
||||||
|
data.AuszahlenZuschlag50Stunden = parseFloat(row[52])
|
||||||
|
data.AuszahlenZuschlag14Stunden = parseFloat(row[53])
|
||||||
|
data.AuszahlenZuschlag15Stunden = parseFloat(row[54])
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// map from: July 2025
|
||||||
|
// func mapRowToStruct(row []string) EmployeeDataRow {
|
||||||
|
// data := EmployeeDataRow{}
|
||||||
|
|
||||||
|
// data.Personalnummer = parseInt(row[0])
|
||||||
|
// data.Nachname = strings.TrimSpace(row[1])
|
||||||
|
// data.Vorname = strings.TrimSpace(row[2])
|
||||||
|
// data.Vertragsart = strings.TrimSpace(row[3])
|
||||||
|
// data.HatPersonenbefoerderungsschein = strings.TrimSpace(row[4])
|
||||||
|
// data.HatRechtAufZuschlaege = strings.TrimSpace(row[5])
|
||||||
|
// data.Vertragsstart = strings.TrimSpace(row[6])
|
||||||
|
// data.VertragstageProWoche = parseFloat(row[7])
|
||||||
|
// data.VertragsstundenProWoche = parseFloat(row[8])
|
||||||
|
// data.VertragsstundenMax = strings.TrimSpace(row[9])
|
||||||
|
// data.UrlaubstageProJahr = parseFloat(row[10])
|
||||||
|
// data.GearbeiteteTage = parseFloat(row[11])
|
||||||
|
// data.GearbeiteteStunden = parseFloat(row[12])
|
||||||
|
// data.GenommeneUrlaubstage = parseFloat(row[13])
|
||||||
|
// data.GenommeneUrlaubsstunden = parseFloat(row[14])
|
||||||
|
// data.Krankheitstage = parseFloat(row[15])
|
||||||
|
// data.Krankheitsstunden = parseFloat(row[16])
|
||||||
|
// data.KrankengeldTage = parseFloat(row[17])
|
||||||
|
// data.KrankengeldStunden = parseFloat(row[18])
|
||||||
|
// data.UnbezahlterUrlaubTage = parseFloat(row[19])
|
||||||
|
// data.UnbezahlterUrlaubStunden = parseFloat(row[20])
|
||||||
|
// data.IstSollGesamtTage = parseFloat(row[21])
|
||||||
|
// data.IstSollGesamtStunden = parseFloat(row[22])
|
||||||
|
// data.IstSollVertragsTage = parseFloat(row[23])
|
||||||
|
// data.IstSollVertragsStunden = parseFloat(row[24])
|
||||||
|
// data.IstSollPlusMinusStunden = parseFloat(row[25])
|
||||||
|
// data.BerechnungMehrstunden = parseFloat(row[26])
|
||||||
|
// data.BerechnungUeberstunden = parseFloat(row[27])
|
||||||
|
// data.BerechnungAnzahlPSchein = parseFloat(row[28])
|
||||||
|
// data.BerechnungKrankOhneAUTage = parseFloat(row[29])
|
||||||
|
// data.BerechnungKrankOhneAUStunden = parseFloat(row[30])
|
||||||
|
// data.BerechnungNachtzuschlag25Tage = parseFloat(row[31])
|
||||||
|
// data.BerechnungNachtzuschlag25Stunden = parseFloat(row[32])
|
||||||
|
// data.BerechnungNachtzuschlag40Tage = parseFloat(row[33])
|
||||||
|
// data.BerechnungNachtzuschlag40Stunden = parseFloat(row[34])
|
||||||
|
// data.BerechnungSonntagszuschlag50Tage = parseFloat(row[35])
|
||||||
|
// data.BerechnungSonntagszuschlag50Stunden = parseFloat(row[36])
|
||||||
|
// data.BerechnungVerpflegungspauschale14Tage = parseFloat(row[37])
|
||||||
|
// data.BerechnungSamstagszuschlag15Tage = parseFloat(row[38])
|
||||||
|
// data.BerechnungSamstagszuschlag15Stunden = parseFloat(row[39])
|
||||||
|
// data.BerechnungZeitausgleichStart = parseFloat(row[40])
|
||||||
|
// data.BerechnungZeitausgleichPlusMinus = parseFloat(row[41])
|
||||||
|
// data.BerechnungZeitausgleichAenderungen = parseFloat(row[42])
|
||||||
|
// data.BerechnungZeitausgleichAuszahlen = parseFloat(row[43])
|
||||||
|
// data.BerechnungZeitausgleichJetzt = parseFloat(row[44])
|
||||||
|
// data.SaldenUrlaubStart = parseFloat(row[45])
|
||||||
|
// data.SaldenUrlaubAbbau = parseFloat(row[46])
|
||||||
|
// data.SaldenUrlaubJetzt = parseFloat(row[47])
|
||||||
|
// data.SaldenMehrstunden = parseFloat(row[48])
|
||||||
|
// data.SaldenUeberstunden = parseFloat(row[49])
|
||||||
|
// data.AuszahlenFahrtkostenKm = parseFloat(row[50])
|
||||||
|
// data.AuszahlenVerpflegungspauschaleTage = parseFloat(row[51])
|
||||||
|
// data.AuszahlenAnzahlPSchein = parseFloat(row[52])
|
||||||
|
// data.AuszahlenZuschlag25Stunden = parseFloat(row[53])
|
||||||
|
// data.AuszahlenZuschlag40Stunden = parseFloat(row[54])
|
||||||
|
// data.AuszahlenZuschlag50Stunden = parseFloat(row[55])
|
||||||
|
// data.AuszahlenZuschlag14Stunden = parseFloat(row[56])
|
||||||
|
// data.AuszahlenZuschlag15Stunden = parseFloat(row[57])
|
||||||
|
// return data
|
||||||
|
// }
|
||||||
|
|
||||||
|
func ReadEmployeeData() []EmployeeDataRow {
|
||||||
|
content, err := os.ReadFile("daten.csv")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Fehler beim Lesen der Datei: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := csv.NewReader(strings.NewReader(string(content)))
|
||||||
|
reader.Comma = ';'
|
||||||
|
reader.FieldsPerRecord = -1
|
||||||
|
|
||||||
|
records, err := reader.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Fehler beim Parsen der CSV: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(records) < 5 {
|
||||||
|
log.Fatal("Die CSV-Datei hat nicht genügend Zeilen.")
|
||||||
|
}
|
||||||
|
dataRows := records[3 : len(records)-1]
|
||||||
|
|
||||||
|
var employees []EmployeeDataRow
|
||||||
|
|
||||||
|
for i, row := range dataRows {
|
||||||
|
fmt.Printf("%v", len(row))
|
||||||
|
if row[1] != "Shahrasebi" {
|
||||||
|
employee := mapRowToStruct(row)
|
||||||
|
employees = append(employees, employee)
|
||||||
|
fmt.Printf("Zeile %d geladen: %s, %s\n", i+4, employee.Nachname, employee.Vorname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n--- Insgesamt %d Mitarbeiterdatensätze erfolgreich geladen. ---\n", len(employees))
|
||||||
|
|
||||||
|
return employees
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helfer-Funktion, um Strings mit Komma als Dezimaltrennzeichen in float64 umzuwandeln.
|
||||||
|
func parseFloat(s string) float64 {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
// Ersetze das Komma durch einen Punkt für die Standard-Konvertierung
|
||||||
|
s = strings.Replace(s, ",", ".", 1)
|
||||||
|
val, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0.0 // Bei Fehler einfach 0 zurückgeben
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helfer-Funktion, um einen String in einen Integer umzuwandeln.
|
||||||
|
func parseInt(s string) int {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
val, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
package excelread
|
||||||
|
|
||||||
|
import (
|
||||||
|
"business-engine/internal/database"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EmployeeDataRow repräsentiert eine einzelne, vollständige Datenzeile aus der CSV.
|
||||||
|
type EmployeeDataRow struct {
|
||||||
|
Personalnummer int `json:"personalnummer"`
|
||||||
|
Nachname string `json:"nachname"`
|
||||||
|
Vorname string `json:"vorname"`
|
||||||
|
Vertragsart string `json:"vertragsart"`
|
||||||
|
HatPersonenbefoerderungsschein string `json:"hatPersonenbefoerderungsschein"`
|
||||||
|
HatRechtAufZuschlaege string `json:"hatRechtAufZuschlaege"`
|
||||||
|
Vertragsstart string `json:"vertragsstart"`
|
||||||
|
VertragstageProWoche float64 `json:"vertragstageProWoche"`
|
||||||
|
VertragsstundenProWoche float64 `json:"vertragsstundenProWoche"`
|
||||||
|
VertragsstundenMax string `json:"vertragsstundenMax"`
|
||||||
|
UrlaubstageProJahr float64 `json:"urlaubstageProJahr"`
|
||||||
|
GearbeiteteTage float64 `json:"gearbeiteteTage"`
|
||||||
|
GearbeiteteStunden float64 `json:"gearbeiteteStunden"`
|
||||||
|
GenommeneUrlaubstage float64 `json:"genommeneUrlaubstage"`
|
||||||
|
GenommeneUrlaubsstunden float64 `json:"genommeneUrlaubsstunden"`
|
||||||
|
Krankheitstage float64 `json:"krankheitstage"`
|
||||||
|
Krankheitsstunden float64 `json:"krankheitsstunden"`
|
||||||
|
KrankengeldTage float64 `json:"krankengeldTage"`
|
||||||
|
KrankengeldStunden float64 `json:"krankengeldStunden"`
|
||||||
|
UnbezahlterUrlaubTage float64 `json:"unbezahlterUrlaubTage"`
|
||||||
|
UnbezahlterUrlaubStunden float64 `json:"unbezahlterUrlaubStunden"`
|
||||||
|
EinbehaltungGesamtTage float64 `json:"einbehaltungGesamtTage"`
|
||||||
|
EinbehaltungGesamtStunden float64 `json:"einbehaltungGesamtStunden"`
|
||||||
|
IstSollGesamtTage float64 `json:"istSollGesamtTage"`
|
||||||
|
IstSollGesamtStunden float64 `json:"istSollGesamtStunden"`
|
||||||
|
IstSollVertragsTage float64 `json:"istSollVertragsTage"`
|
||||||
|
IstSollVertragsStunden float64 `json:"istSollVertragsStunden"`
|
||||||
|
IstSollPlusMinusStunden float64 `json:"istSollPlusMinusStunden"`
|
||||||
|
BerechnungMehrstunden float64 `json:"berechnungMehrstunden"`
|
||||||
|
BerechnungUeberstunden float64 `json:"berechnungUeberstunden"`
|
||||||
|
BerechnungAnzahlPSchein float64 `json:"berechnungAnzahlPSchein"`
|
||||||
|
BerechnungKrankOhneAUTage float64 `json:"berechnungKrankOhneAUTage"`
|
||||||
|
BerechnungKrankOhneAUStunden float64 `json:"berechnungKrankOhneAUStunden"`
|
||||||
|
BerechnungNachtzuschlag25Tage float64 `json:"berechnungNachtzuschlag25Tage"`
|
||||||
|
BerechnungNachtzuschlag25Stunden float64 `json:"berechnungNachtzuschlag25Stunden"`
|
||||||
|
BerechnungNachtzuschlag40Tage float64 `json:"berechnungNachtzuschlag40Tage"`
|
||||||
|
BerechnungNachtzuschlag40Stunden float64 `json:"berechnungNachtzuschlag40Stunden"`
|
||||||
|
BerechnungSonntagszuschlag50Tage float64 `json:"berechnungSonntagszuschlag50Tage"`
|
||||||
|
BerechnungSonntagszuschlag50Stunden float64 `json:"berechnungSonntagszuschlag50Stunden"`
|
||||||
|
BerechnungVerpflegungspauschale14Tage float64 `json:"berechnungVerpflegungspauschale14Tage"`
|
||||||
|
BerechnungSamstagszuschlag15Tage float64 `json:"berechnungSamstagszuschlag15Tage"`
|
||||||
|
BerechnungSamstagszuschlag15Stunden float64 `json:"berechnungSamstagszuschlag15Stunden"`
|
||||||
|
BerechnungZeitausgleichStart float64 `json:"berechnungZeitausgleichStart"`
|
||||||
|
BerechnungZeitausgleichPlusMinus float64 `json:"berechnungZeitausgleichPlusMinus"`
|
||||||
|
BerechnungZeitausgleichAenderungen float64 `json:"berechnungZeitausgleichAenderungen"`
|
||||||
|
BerechnungZeitausgleichAuszahlen float64 `json:"berechnungZeitausgleichAuszahlen"`
|
||||||
|
BerechnungZeitausgleichJetzt float64 `json:"berechnungZeitausgleichJetzt"`
|
||||||
|
SaldenUrlaubStart float64 `json:"saldenUrlaubStart"`
|
||||||
|
SaldenUrlaubAbbau float64 `json:"saldenUrlaubAbbau"`
|
||||||
|
SaldenUrlaubKorrektur float64 `json:"saldenUrlaubKorrektur"`
|
||||||
|
SaldenUrlaubJetzt float64 `json:"saldenUrlaubJetzt"`
|
||||||
|
SaldenMehrstunden float64 `json:"saldenMehrstunden"`
|
||||||
|
SaldenUeberstunden float64 `json:"saldenUeberstunden"`
|
||||||
|
AuszahlenFahrtkostenKm float64 `json:"auszahlenFahrtkostenKm"`
|
||||||
|
AuszahlenVerpflegungspauschaleTage float64 `json:"auszahlenVerpflegungspauschaleTage"`
|
||||||
|
AuszahlenAnzahlPSchein float64 `json:"auszahlenAnzahlPSchein"`
|
||||||
|
AuszahlenZuschlag25Stunden float64 `json:"auszahlenZuschlag25Stunden"`
|
||||||
|
AuszahlenZuschlag40Stunden float64 `json:"auszahlenZuschlag40Stunden"`
|
||||||
|
AuszahlenZuschlag50Stunden float64 `json:"auszahlenZuschlag50Stunden"`
|
||||||
|
AuszahlenZuschlag14Stunden float64 `json:"auszahlenZuschlag14Stunden"`
|
||||||
|
AuszahlenZuschlag15Stunden float64 `json:"auszahlenZuschlag15Stunden"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayrollInputData struct {
|
||||||
|
ContractSalaryHour float64
|
||||||
|
Tourengeld float64
|
||||||
|
Worktimes []database.WorkTime
|
||||||
|
Plannedtimes []database.ListPlannedTimesForEmployeeInPeriodRow
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPayrollInputData(salaryHour float64, tourengeld float64, worktimes []database.WorkTime, plannedtimes []database.ListPlannedTimesForEmployeeInPeriodRow) PayrollInputData {
|
||||||
|
return PayrollInputData{
|
||||||
|
ContractSalaryHour: salaryHour,
|
||||||
|
Tourengeld: tourengeld,
|
||||||
|
Worktimes: worktimes,
|
||||||
|
Plannedtimes: plannedtimes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayrollStatement struct {
|
||||||
|
Nachname string `json:"nachname" csv:"Nachname"`
|
||||||
|
Vorname string `json:"vorname" csv:"Vorname"`
|
||||||
|
Verpflegungspauschale float64 `json:"verpflegungspauschale" csv:"Verpflegungspauschale"`
|
||||||
|
VertragStundenlohn float64 `json:"stundenlohn" csv:"Vertr. Stundenlohn"`
|
||||||
|
Vertragsstunden float64 `json:"vertragsstunden" csv:"Arbeitsstunden lt. Vertrag"`
|
||||||
|
GearbeiteteStunden float64 `json:"gearbeiteteStunden" csv:"Gearbeitet Stunden"`
|
||||||
|
GearbeiteteTage float64 `json:"gearbeiteteTage" csv:"Gearbeitet Tage"`
|
||||||
|
PuenktlichkeitspraemieAnzahl float64 `json:"puenktlichkeitspraemieAnzahl" csv:"Pünktlichkeitsprämie anzahl"`
|
||||||
|
Krankheitstage float64 `json:"krankheitstage" csv:"Krankheitstage x 8 Stunden"`
|
||||||
|
Ueberstunden float64 `json:"ueberstunden" csv:"davon Überstunden"`
|
||||||
|
Tourengeld float64 `json:"tourengeld" csv:"Tourengeld Anzahl"`
|
||||||
|
TourengeldBonus float64 `json:"tourengeldBonus" csv:"Tourengeld-Bonus (Platz 1-3, EUR)"`
|
||||||
|
GesundheitsbonusAbzug string `json:"gesundheitsbonusAbzug" csv:"Gesundheitsbonus abzuziehen? (ja/nein)"`
|
||||||
|
Urlaubsgeld float64 `json:"urlaubsgeld" csv:"Urlaubsgeld (44 EUR pro tag)"`
|
||||||
|
PScheinZulage float64 `json:"pScheinZulage" csv:"P-schein Zulage"`
|
||||||
|
Wochenendzuschlag15 float64 `json:"wochenendzuschlag15" csv:"Wochenendzuschlag 15%"`
|
||||||
|
Sonntagszuschlag50 float64 `json:"sonntagszuschlag50" csv:"Sonntagszuschlag 50%"`
|
||||||
|
Feiertagszuschlag125 float64 `json:"feiertagszuschlag125" csv:"Feiertagszuschlag 125%"`
|
||||||
|
Nachtzuschlag25 float64 `json:"nachtzuschlag25" csv:"Nachtzuschlag 25%"`
|
||||||
|
Nachtzuschlag40 float64 `json:"nachtzuschlag40" csv:"Nachtzuschlag 40%"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmployeeOnTime struct {
|
||||||
|
EmployeeId string `csv:"EmployeeId"`
|
||||||
|
WorkTime time.Time `csv:"Arbeitszeit"`
|
||||||
|
PlannedTime time.Time `csv:"Schichtplanung"`
|
||||||
|
IsOnTime bool `csv:"Pünktlich?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEmployeeOnTime(employeeId string, worktime, plannedtime time.Time) EmployeeOnTime {
|
||||||
|
loc, err := time.LoadLocation("Europe/Berlin")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Fehler beim Laden der Zeitzone:", err)
|
||||||
|
}
|
||||||
|
return EmployeeOnTime{
|
||||||
|
EmployeeId: employeeId,
|
||||||
|
WorkTime: worktime.In(loc),
|
||||||
|
PlannedTime: plannedtime.In(loc),
|
||||||
|
IsOnTime: worktime.Before(plannedtime) || worktime == plannedtime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package gencsv
|
||||||
|
|
||||||
|
import (
|
||||||
|
excelread "business-engine/internal/excel_read"
|
||||||
|
"encoding/csv"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gocarina/gocsv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GeneratePayrollStatementCSV(stmts []excelread.PayrollStatement) error {
|
||||||
|
file, err := os.Create("payroll_statement.csv")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
gocsv.SetCSVWriter(func(out io.Writer) *gocsv.SafeCSVWriter {
|
||||||
|
writer := csv.NewWriter(out)
|
||||||
|
writer.Comma = ';'
|
||||||
|
return gocsv.NewSafeCSVWriter(writer)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := gocsv.MarshalFile(&stmts, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateEmployeesOnTime(employeesontime []excelread.EmployeeOnTime) error {
|
||||||
|
file, err := os.Create("employees_on_time.csv")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if err := gocsv.MarshalFile(&employeesontime, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,306 @@
|
||||||
|
package genpdf
|
||||||
|
|
||||||
|
import (
|
||||||
|
excelread "business-engine/internal/excel_read"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"codeberg.org/go-pdf/fpdf"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
"golang.org/x/text/message"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LineItem repräsentiert eine einzelne Position in der Abrechnung.
|
||||||
|
type LineItem struct {
|
||||||
|
Lohnart string
|
||||||
|
Beschreibung string
|
||||||
|
Anzahl float64
|
||||||
|
Betrag float64
|
||||||
|
Faktor float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatementInfo struct {
|
||||||
|
GearbeiteteStunden float64
|
||||||
|
GearbeiteteTage float64
|
||||||
|
Urlaubsgeld float64
|
||||||
|
Krankheitstage float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total berechnet den Gesamtwert der Position.
|
||||||
|
func (item LineItem) Total() float64 {
|
||||||
|
if item.Faktor > 0 {
|
||||||
|
return item.Anzahl * item.Betrag * (item.Faktor / 100)
|
||||||
|
}
|
||||||
|
return item.Anzahl * item.Betrag
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateSalaryStatement(stmt excelread.PayrollStatement, filename string, month time.Time) error {
|
||||||
|
pdf := fpdf.New("P", "mm", "A4", "")
|
||||||
|
pdf.AddUTF8Font("DejaVu", "", "DejaVuSans.ttf")
|
||||||
|
pdf.AddUTF8Font("DejaVu", "B", "DejaVuSans-Bold.ttf")
|
||||||
|
pdf.AddPage()
|
||||||
|
|
||||||
|
pdf.SetFont("DejaVu", "", 12)
|
||||||
|
pdf.Cell(0, 10, month.Format("01-2006"))
|
||||||
|
pdf.Ln(12)
|
||||||
|
|
||||||
|
pdf.SetDrawColor(200, 200, 200)
|
||||||
|
pdf.SetLineWidth(0.5)
|
||||||
|
|
||||||
|
x, y := pdf.GetXY()
|
||||||
|
pdf.Line(x, y, x+40, y)
|
||||||
|
pdf.Ln(10)
|
||||||
|
|
||||||
|
pdf.SetFont("DejaVu", "B", 16)
|
||||||
|
title := fmt.Sprintf("Gehaltsaufstellung für %s %s", stmt.Vorname, stmt.Nachname)
|
||||||
|
pdf.Cell(0, 10, title)
|
||||||
|
pdf.Ln(15)
|
||||||
|
|
||||||
|
drawInfoBlock(pdf, StatementInfo{
|
||||||
|
GearbeiteteStunden: stmt.GearbeiteteStunden,
|
||||||
|
GearbeiteteTage: stmt.GearbeiteteTage,
|
||||||
|
Urlaubsgeld: stmt.Urlaubsgeld,
|
||||||
|
Krankheitstage: stmt.Krankheitstage,
|
||||||
|
})
|
||||||
|
|
||||||
|
pdf.Ln(15)
|
||||||
|
drawHeader(pdf)
|
||||||
|
|
||||||
|
items := genLineItemsFromStatement(stmt)
|
||||||
|
|
||||||
|
var gesamtbetrag float64
|
||||||
|
for _, item := range items {
|
||||||
|
drawRow(pdf, item)
|
||||||
|
gesamtbetrag += item.Total()
|
||||||
|
}
|
||||||
|
|
||||||
|
drawSummary(pdf, gesamtbetrag)
|
||||||
|
|
||||||
|
if err := pdf.OutputFileAndClose(filename); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pdf.Err() {
|
||||||
|
return pdf.Error()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func genLineItemsFromStatement(stmt excelread.PayrollStatement) []LineItem {
|
||||||
|
var items []LineItem
|
||||||
|
|
||||||
|
// --- Gehalt & Stunden ---
|
||||||
|
if stmt.Vertragsstunden > 0 && stmt.VertragStundenlohn > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "0005",
|
||||||
|
Beschreibung: "Gehalt",
|
||||||
|
Anzahl: stmt.Vertragsstunden,
|
||||||
|
Betrag: stmt.VertragStundenlohn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.Ueberstunden > 0 && stmt.VertragStundenlohn > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "1250",
|
||||||
|
Beschreibung: "Überstundenvergütung",
|
||||||
|
Anzahl: stmt.Ueberstunden,
|
||||||
|
Betrag: stmt.VertragStundenlohn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if stmt.Verpflegungspauschale > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "0227",
|
||||||
|
Beschreibung: "Verpflegungsmehrauf. 14€",
|
||||||
|
Anzahl: stmt.Verpflegungspauschale,
|
||||||
|
Betrag: 14.00,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.Urlaubsgeld > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "0052",
|
||||||
|
Beschreibung: "Urlaubsgeld Angestellte",
|
||||||
|
Anzahl: stmt.Urlaubsgeld,
|
||||||
|
Betrag: 44.00,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.TourengeldBonus > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "5019",
|
||||||
|
Beschreibung: "Bonus Tourengeld",
|
||||||
|
Anzahl: 1,
|
||||||
|
Betrag: stmt.TourengeldBonus,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.Tourengeld > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "1030",
|
||||||
|
Beschreibung: "Tourengeld",
|
||||||
|
Anzahl: stmt.Tourengeld,
|
||||||
|
Betrag: 2.5,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.PuenktlichkeitspraemieAnzahl > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "0109",
|
||||||
|
Beschreibung: "Verpflegung 25% (Pünktlichkeit)",
|
||||||
|
Anzahl: stmt.PuenktlichkeitspraemieAnzahl,
|
||||||
|
Betrag: 14.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.GesundheitsbonusAbzug == "" {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "1032",
|
||||||
|
Beschreibung: "Gesundheitsbonus",
|
||||||
|
Anzahl: 1,
|
||||||
|
Betrag: 200.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if stmt.PScheinZulage > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "1024",
|
||||||
|
Beschreibung: "Zulage Personenbeförd.",
|
||||||
|
Anzahl: stmt.PScheinZulage,
|
||||||
|
Betrag: 5.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.Wochenendzuschlag15 > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "1063",
|
||||||
|
Beschreibung: "Samstagszuschlag 15%",
|
||||||
|
Anzahl: stmt.Wochenendzuschlag15,
|
||||||
|
Betrag: stmt.VertragStundenlohn,
|
||||||
|
Faktor: 15,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.Sonntagszuschlag50 > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "0581",
|
||||||
|
Beschreibung: "Sonntagszuschlag 50%",
|
||||||
|
Anzahl: stmt.Sonntagszuschlag50,
|
||||||
|
Betrag: stmt.VertragStundenlohn,
|
||||||
|
Faktor: 50,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.Feiertagszuschlag125 > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "0551",
|
||||||
|
Beschreibung: "Feiertagszuschlag 125%",
|
||||||
|
Anzahl: stmt.Feiertagszuschlag125,
|
||||||
|
Betrag: stmt.VertragStundenlohn,
|
||||||
|
Faktor: 125,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.Nachtzuschlag25 > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "0505",
|
||||||
|
Beschreibung: "Nachtzuschl. 20:00-24:00h",
|
||||||
|
Anzahl: stmt.Nachtzuschlag25,
|
||||||
|
Betrag: stmt.VertragStundenlohn,
|
||||||
|
Faktor: 25,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if stmt.Nachtzuschlag40 > 0 {
|
||||||
|
items = append(items, LineItem{
|
||||||
|
Lohnart: "0501",
|
||||||
|
Beschreibung: "Nachtzuschl. 0:00-4:00h",
|
||||||
|
Anzahl: stmt.Nachtzuschlag40,
|
||||||
|
Betrag: stmt.VertragStundenlohn,
|
||||||
|
Faktor: 40,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(items, func(a, b LineItem) int {
|
||||||
|
return int(b.Total() - a.Total())
|
||||||
|
})
|
||||||
|
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawInfoBlock(pdf *fpdf.Fpdf, info StatementInfo) {
|
||||||
|
const padding = 3.0
|
||||||
|
const elementHeight = 4.0 + 6.0
|
||||||
|
|
||||||
|
x, y := pdf.GetXY()
|
||||||
|
drawLabelWithValue(pdf, x+padding, y, "Anw.-Std.", formatFloat(info.GearbeiteteStunden))
|
||||||
|
drawLabelWithValue(pdf, x+padding+18, y, "Anw.-Tage.", formatFloat(info.GearbeiteteTage))
|
||||||
|
pdf.Line(x+padding+38, y-padding, x+padding+38, y+elementHeight+padding)
|
||||||
|
drawLabelWithValue(pdf, x+padding+40, y, "Urlaub", formatFloat(float64(info.Urlaubsgeld)))
|
||||||
|
drawLabelWithValue(pdf, x+padding+55, y, "Krank.-Tage", formatFloat(float64(info.Krankheitstage)))
|
||||||
|
|
||||||
|
rectX := x
|
||||||
|
rectY := y - padding
|
||||||
|
rectWidth := 4*20 + 2*padding - 5
|
||||||
|
rectHeight := elementHeight + 2*padding
|
||||||
|
pdf.SetFillColor(180, 200, 220)
|
||||||
|
pdf.Rect(rectX, rectY, rectWidth, rectHeight, "D")
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawLabelWithValue(pdf *fpdf.Fpdf, x, y float64, label, value string) {
|
||||||
|
const labelHeight = 4.0
|
||||||
|
const valueheight = 6.0
|
||||||
|
|
||||||
|
pdf.SetFont("DejaVu", "", 8)
|
||||||
|
pdf.SetTextColor(80, 100, 120)
|
||||||
|
pdf.SetXY(x, y)
|
||||||
|
pdf.Cell(20, labelHeight, label)
|
||||||
|
|
||||||
|
pdf.SetFont("DejaVu", "", 10)
|
||||||
|
pdf.SetTextColor(0, 0, 0)
|
||||||
|
pdf.SetXY(x, y+labelHeight)
|
||||||
|
pdf.Cell(20, valueheight, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawHeader(pdf *fpdf.Fpdf) {
|
||||||
|
pdf.SetFont("DejaVu", "B", 10)
|
||||||
|
pdf.SetFillColor(240, 240, 240)
|
||||||
|
pdf.SetX(10)
|
||||||
|
pdf.CellFormat(20, 7, "Lohnart", "1", 0, "C", true, 0, "")
|
||||||
|
pdf.SetX(30)
|
||||||
|
pdf.CellFormat(70, 7, "Bezeichnung", "1", 0, "L", true, 0, "")
|
||||||
|
pdf.SetX(100)
|
||||||
|
pdf.CellFormat(25, 7, "Menge", "1", 0, "R", true, 0, "")
|
||||||
|
pdf.SetX(125)
|
||||||
|
pdf.CellFormat(25, 7, "%-Zuschlag", "1", 0, "R", true, 0, "")
|
||||||
|
pdf.SetX(150)
|
||||||
|
pdf.CellFormat(25, 7, "Betrag", "1", 0, "R", true, 0, "")
|
||||||
|
pdf.SetX(175)
|
||||||
|
pdf.CellFormat(30, 7, "Gesamt", "1", 0, "R", true, 0, "")
|
||||||
|
pdf.Ln(7)
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawRow(pdf *fpdf.Fpdf, item LineItem) {
|
||||||
|
pdf.SetFont("DejaVu", "", 10)
|
||||||
|
pdf.SetX(10)
|
||||||
|
pdf.CellFormat(20, 7, item.Lohnart, "1", 0, "C", false, 0, "")
|
||||||
|
pdf.SetX(30)
|
||||||
|
pdf.CellFormat(70, 7, item.Beschreibung, "1", 0, "L", false, 0, "")
|
||||||
|
pdf.SetX(100)
|
||||||
|
pdf.CellFormat(25, 7, formatFloat(item.Anzahl), "1", 0, "R", false, 0, "")
|
||||||
|
pdf.SetX(125)
|
||||||
|
pdf.CellFormat(25, 7, formatFloat(item.Faktor), "1", 0, "R", false, 0, "")
|
||||||
|
pdf.SetX(150)
|
||||||
|
pdf.CellFormat(25, 7, formatCurrency(item.Betrag), "1", 0, "R", false, 0, "")
|
||||||
|
pdf.SetX(175)
|
||||||
|
pdf.CellFormat(30, 7, formatCurrency(item.Total()), "1", 0, "R", false, 0, "")
|
||||||
|
pdf.Ln(7)
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawSummary(pdf *fpdf.Fpdf, total float64) {
|
||||||
|
pdf.SetFont("DejaVu", "B", 11)
|
||||||
|
pdf.SetY(pdf.GetY() + 5) // Etwas Abstand
|
||||||
|
pdf.SetX(120)
|
||||||
|
pdf.CellFormat(50, 8, "Gesamtbetrag:", "T", 0, "L", false, 0, "")
|
||||||
|
pdf.SetX(170)
|
||||||
|
pdf.CellFormat(30, 8, formatCurrency(total), "T", 0, "R", false, 0, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatFloat(f float64) string {
|
||||||
|
p := message.NewPrinter(language.German)
|
||||||
|
str := p.Sprintf("%.2f", f)
|
||||||
|
return str //strings.Replace(str, ".", ",", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatCurrency(val float64) string {
|
||||||
|
return formatFloat(val) + "€"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package payroll
|
||||||
|
|
||||||
|
// SalaryComponent is a single position
|
||||||
|
type SalaryComponent struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Amount int64
|
||||||
|
Value int64
|
||||||
|
Details string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rule interface {
|
||||||
|
Execute(data PayrollInput) []SalaryComponent
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
package payroll
|
||||||
|
|
||||||
|
import (
|
||||||
|
excelread "business-engine/internal/excel_read"
|
||||||
|
"business-engine/internal/gencsv"
|
||||||
|
"business-engine/internal/genpdf"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EmployeeHandler struct {
|
||||||
|
service Service
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEmployeeHandler(service Service, logger *slog.Logger) *EmployeeHandler {
|
||||||
|
return &EmployeeHandler{
|
||||||
|
service: service,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *EmployeeHandler) ListEmployees(w http.ResponseWriter, r *http.Request) {
|
||||||
|
month := time.Date(2025, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
employees, err := h.service.GenerateMonthlyRun(r.Context(), month)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("failed to get employees", "error", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(employees); err != nil {
|
||||||
|
h.logger.Error("failed to encode response", "error", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *EmployeeHandler) RunPayroll(w http.ResponseWriter, r *http.Request) {
|
||||||
|
month := time.Date(2025, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
employeeData := excelread.ReadEmployeeData()
|
||||||
|
|
||||||
|
var payrollStatements []excelread.PayrollStatement
|
||||||
|
for _, employeeRow := range employeeData {
|
||||||
|
input, err := h.service.FetchPayrollInput(r.Context(), month, employeeRow.Vorname, employeeRow.Nachname)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("could not fetch payroll input", "error", err)
|
||||||
|
// http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
statement := excelread.GeneratePayrollStatement(employeeRow, input)
|
||||||
|
payrollStatements = append(payrollStatements, statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(payrollStatements, func(a, b excelread.PayrollStatement) int {
|
||||||
|
if a.Tourengeld < b.Tourengeld {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if a.Tourengeld > b.Tourengeld {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
payrollStatements[0].TourengeldBonus = 500
|
||||||
|
payrollStatements[1].TourengeldBonus = 400
|
||||||
|
payrollStatements[2].TourengeldBonus = 300
|
||||||
|
|
||||||
|
h.logger.Info("generating csv salary statements")
|
||||||
|
err := gencsv.GeneratePayrollStatementCSV(payrollStatements)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("could not generate payroll statement csv", "error", err)
|
||||||
|
}
|
||||||
|
h.logger.Info("generating pdf salary statements")
|
||||||
|
for _, stmt := range payrollStatements {
|
||||||
|
err := genpdf.GenerateSalaryStatement(stmt, fmt.Sprintf("%s_%s.pdf", month.Format("2006-01"), fmt.Sprintf("%s %s", stmt.Vorname, stmt.Nachname)), month)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("could not generate salary statement pdf", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(payrollStatements); err != nil {
|
||||||
|
h.logger.Error("failed to encode response", "error", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package payroll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"business-engine/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PayrollInput struct {
|
||||||
|
database.ListActiveEmployeesRow
|
||||||
|
|
||||||
|
WorkSummary struct {
|
||||||
|
Days int64
|
||||||
|
Hours float64
|
||||||
|
Minutes int64
|
||||||
|
}
|
||||||
|
|
||||||
|
SicknessSummary struct {
|
||||||
|
Days int64
|
||||||
|
Hours float64
|
||||||
|
Minutes int64
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkTime []database.WorkTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPayrollInput(employee database.ListActiveEmployeesRow) PayrollInput {
|
||||||
|
return PayrollInput{
|
||||||
|
ListActiveEmployeesRow: employee,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package payroll
|
||||||
|
|
||||||
|
type BaseSalaryRule struct{}
|
||||||
|
|
||||||
|
func NewBaseSalaryRule() *BaseSalaryRule {
|
||||||
|
return &BaseSalaryRule{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BaseSalaryRule) Execute(data PayrollInput) []SalaryComponent {
|
||||||
|
stundenlohnCent := data.ContractSalaryHour * 100
|
||||||
|
|
||||||
|
monatsGehaltCent := stundenlohnCent * data.ContractHoursWeek * 4.33
|
||||||
|
|
||||||
|
return []SalaryComponent{
|
||||||
|
{
|
||||||
|
Name: "Grundgehalt",
|
||||||
|
Type: "Grundgehalt",
|
||||||
|
Amount: int64(monatsGehaltCent),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
package payroll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"business-engine/internal/database"
|
||||||
|
excelread "business-engine/internal/excel_read"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Repository interface {
|
||||||
|
ListActiveEmployees(ctx context.Context) ([]database.ListActiveEmployeesRow, error)
|
||||||
|
GetWorkSummaryForEmployeeInPeriod(ctx context.Context, args database.GetWorkSummaryForEmployeeInPeriodParams) (database.GetWorkSummaryForEmployeeInPeriodRow, error)
|
||||||
|
GetSicknessSummaryForEmployeeInPeriod(ctx context.Context, args database.GetSicknessSummaryForEmployeeInPeriodParams) (database.GetSicknessSummaryForEmployeeInPeriodRow, error)
|
||||||
|
ListWorkTimesForEmployeeInPeriod(ctx context.Context, args database.ListWorkTimesForEmployeeInPeriodParams) ([]database.WorkTime, error)
|
||||||
|
ListPlannedTimesForEmployeeInPeriod(ctx context.Context, args database.ListPlannedTimesForEmployeeInPeriodParams) ([]database.ListPlannedTimesForEmployeeInPeriodRow, error)
|
||||||
|
FetchDrivenToursInInterval(ctx context.Context, args database.FetchDrivenToursInIntervalParams) ([]database.Tour, error)
|
||||||
|
CountTourengeldAmountInPeriod(ctx context.Context, args database.CountTourengeldAmountInPeriodParams) (float64, error)
|
||||||
|
GetEmployeeByName(ctx context.Context, args database.GetEmployeeByNameParams) (database.GetEmployeeByNameRow, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Engine interface {
|
||||||
|
Calculate() // payroll input -> payroll statement
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
repo Repository
|
||||||
|
engine Engine
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(repo Repository, logger *slog.Logger) *Service {
|
||||||
|
return &Service{
|
||||||
|
repo: repo,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) FetchPayrollInput(ctx context.Context, month time.Time, employeeFirstname, employeeLastname string) (excelread.PayrollInputData, error) {
|
||||||
|
s.logger.Info("fetching payroll input", "month", month)
|
||||||
|
|
||||||
|
employeeIDs, err := s.repo.GetEmployeeByName(ctx, database.GetEmployeeByNameParams{
|
||||||
|
Firstname: employeeFirstname,
|
||||||
|
Lastname: employeeLastname,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("could not find employee with that name", "firstname", employeeFirstname, "lastname", employeeLastname, "error", err)
|
||||||
|
return excelread.PayrollInputData{}, err
|
||||||
|
}
|
||||||
|
s.logger.Debug("got employee data", "employee", employeeIDs)
|
||||||
|
|
||||||
|
startOfMonth := BeginningOfMonth(month.AddDate(0, -1, 0))
|
||||||
|
endOfMonth := EndOfMonth(month.AddDate(0, -1, 0))
|
||||||
|
s.logger.Info("fetching data for interval", "startDate", startOfMonth, "endDate", endOfMonth)
|
||||||
|
|
||||||
|
tourengeld, err := s.repo.CountTourengeldAmountInPeriod(ctx, database.CountTourengeldAmountInPeriodParams{
|
||||||
|
StartDate: startOfMonth,
|
||||||
|
EndDate: endOfMonth,
|
||||||
|
EmployeeID: pgtype.Text{
|
||||||
|
String: employeeIDs.DispoliveID,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("could not count tourengeld for employee", "employeeId", employeeIDs.DyflexisID, "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
plannedtimes, err := s.repo.ListPlannedTimesForEmployeeInPeriod(ctx, database.ListPlannedTimesForEmployeeInPeriodParams{
|
||||||
|
EmployeeID: fmt.Sprintf("%d", employeeIDs.DyflexisID),
|
||||||
|
StartDate: &startOfMonth,
|
||||||
|
EndDate: &endOfMonth,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("could not fetch plannedtimes for employee", "employeeId", employeeIDs.DyflexisID, "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
worktimes, err := s.repo.ListWorkTimesForEmployeeInPeriod(ctx, database.ListWorkTimesForEmployeeInPeriodParams{
|
||||||
|
EmployeeID: fmt.Sprintf("%d", employeeIDs.DyflexisID),
|
||||||
|
StartDate: &startOfMonth,
|
||||||
|
EndDate: &endOfMonth,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("could not fetch worktimes for employee", "employeeId", employeeIDs.DyflexisID, "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("Fetched payroll input", "employee", fmt.Sprintf("%s %s", employeeFirstname, employeeLastname), "tourengeld", tourengeld, "workdays", len(worktimes))
|
||||||
|
|
||||||
|
return excelread.NewPayrollInputData(employeeIDs.ContractSalaryHour, tourengeld, worktimes, plannedtimes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GenerateMonthlyRun(ctx context.Context, month time.Time) ([]PayrollInput, error) {
|
||||||
|
s.logger.Info("starting monthly payroll run", "month", month)
|
||||||
|
|
||||||
|
employees, err := s.repo.ListActiveEmployees(ctx)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("could not get active employees", "error", err)
|
||||||
|
return []PayrollInput{}, err
|
||||||
|
}
|
||||||
|
s.logger.Info("found active employees for payroll run", "count", len(employees))
|
||||||
|
|
||||||
|
var inputs []PayrollInput
|
||||||
|
|
||||||
|
for _, employee := range employees {
|
||||||
|
log := s.logger.With("employee_id", employee.ID)
|
||||||
|
log.Info("processing employee")
|
||||||
|
|
||||||
|
payrollInput := NewPayrollInput(employee)
|
||||||
|
startOfMonth := BeginningOfMonth(month.AddDate(0, -1, 0))
|
||||||
|
endOfMonth := EndOfMonth(month.AddDate(0, -1, 0))
|
||||||
|
|
||||||
|
workSummary, err := s.repo.GetWorkSummaryForEmployeeInPeriod(ctx, database.GetWorkSummaryForEmployeeInPeriodParams{
|
||||||
|
EmployeeID: fmt.Sprintf("%d", employee.ID),
|
||||||
|
StartDate: &startOfMonth,
|
||||||
|
EndDate: &endOfMonth,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("could not get work summary for employee", "error", err)
|
||||||
|
return []PayrollInput{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payrollInput.WorkSummary = struct {
|
||||||
|
Days int64
|
||||||
|
Hours float64
|
||||||
|
Minutes int64
|
||||||
|
}(workSummary)
|
||||||
|
|
||||||
|
sicknessSummary, err := s.repo.GetSicknessSummaryForEmployeeInPeriod(ctx, database.GetSicknessSummaryForEmployeeInPeriodParams{
|
||||||
|
EmployeeID: fmt.Sprintf("%d", employee.ID),
|
||||||
|
StartDate: &startOfMonth,
|
||||||
|
EndDate: &endOfMonth,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("could not get work summary for employee", "error", err)
|
||||||
|
return []PayrollInput{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payrollInput.SicknessSummary = struct {
|
||||||
|
Days int64
|
||||||
|
Hours float64
|
||||||
|
Minutes int64
|
||||||
|
}(sicknessSummary)
|
||||||
|
|
||||||
|
worktimes, err := s.repo.ListWorkTimesForEmployeeInPeriod(ctx, database.ListWorkTimesForEmployeeInPeriodParams{
|
||||||
|
EmployeeID: fmt.Sprintf("%d", employee.ID),
|
||||||
|
StartDate: &startOfMonth,
|
||||||
|
EndDate: &endOfMonth,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("could not get worktimes for employee", "error", err)
|
||||||
|
return []PayrollInput{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
payrollInput.WorkTime = worktimes
|
||||||
|
|
||||||
|
inputs = append(inputs, payrollInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BeginningOfMonth(date time.Time) time.Time {
|
||||||
|
return date.AddDate(0, 0, -date.Day()+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EndOfMonth(date time.Time) time.Time {
|
||||||
|
eom := date.AddDate(0, 1, -date.Day())
|
||||||
|
return time.Date(
|
||||||
|
eom.Year(),
|
||||||
|
eom.Month(),
|
||||||
|
eom.Day(),
|
||||||
|
23, 59, 59, 999,
|
||||||
|
eom.Location(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
version: "2"
|
||||||
|
sql:
|
||||||
|
- engine: "postgresql"
|
||||||
|
queries: "./db/queries"
|
||||||
|
schema: "./db/migrations"
|
||||||
|
gen:
|
||||||
|
go:
|
||||||
|
package: "database"
|
||||||
|
out: "./internal/database"
|
||||||
|
sql_package: "pgx/v5"
|
||||||
|
emit_json_tags: true
|
||||||
|
overrides:
|
||||||
|
- db_type: "pg_catalog.timestamp"
|
||||||
|
go_type:
|
||||||
|
type: "time.Time"
|
||||||
|
- db_type: "pg_catalog.timestamp"
|
||||||
|
nullable: true
|
||||||
|
go_type:
|
||||||
|
type: "*time.Time"
|
||||||
|
- db_type: "pg_catalog.timestamptz"
|
||||||
|
go_type:
|
||||||
|
type: "time.Time"
|
||||||
|
- db_type: "pg_catalog.timestamptz"
|
||||||
|
nullable: true
|
||||||
|
go_type:
|
||||||
|
type: "*time.Time"
|
||||||
Loading…
Reference in New Issue