implement latest changes to employee payroll process
- Add ContractDaysWeek - Add Sickness Days to employee export sheet - Add Gesundheitsbonus value per employee - Additional fixes
This commit is contained in:
@@ -25,7 +25,7 @@ func NewEmployeeHandler(service Service, logger *slog.Logger) *EmployeeHandler {
|
||||
}
|
||||
|
||||
func (h *EmployeeHandler) RunPayroll(w http.ResponseWriter, r *http.Request) {
|
||||
month := time.Date(2025, time.November, 1, 0, 0, 0, 0, time.UTC)
|
||||
month := time.Date(2025, time.December, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
employees, err := h.service.FetchActiveEmployees(r.Context())
|
||||
if err != nil {
|
||||
@@ -85,7 +85,7 @@ func (h *EmployeeHandler) RunPayroll(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(data); err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ type Repository interface {
|
||||
CountTourengeldAmountInPeriod(ctx context.Context, args database.CountTourengeldAmountInPeriodParams) (float64, error)
|
||||
GetWeeklyTimeSummaryForEmployee(ctx context.Context, args database.GetWeeklyTimeSummaryForEmployeeParams) ([]database.GetWeeklyTimeSummaryForEmployeeRow, error)
|
||||
EvaluatePunctualityForEmployee(ctx context.Context, args database.EvaluatePunctualityForEmployeeParams) (int64, error)
|
||||
IsEmployeesDepartmentNotLeitstelle(ctx context.Context, args database.IsEmployeesDepartmentNotLeitstelleParams) (bool, error)
|
||||
IsEmployeesDepartmentKTW(ctx context.Context, args database.IsEmployeesDepartmentKTWParams) (bool, error)
|
||||
CountVerpflegungspauschale(ctx context.Context, args database.CountVerpflegungspauschaleParams) (int64, error)
|
||||
ListSickdaysForEmployee(ctx context.Context, employeeId int32) ([]database.ListSickdaysForEmployeeRow, error)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
@@ -55,8 +57,7 @@ func (s *Service) GeneratePayrollStatement(input domain.PayrollInput) domain.Pay
|
||||
GearbeiteteTage: float64(input.WorkedDays),
|
||||
Krankheitstage: float64(input.SickDays),
|
||||
Ueberstunden: input.OvertimeHours,
|
||||
|
||||
Verpflegungspauschale: float64(input.Verpflegungspauschale),
|
||||
SickdaysYear: input.SickdaysYear,
|
||||
|
||||
Wochenendzuschlag15: input.ZuschlagSamstag,
|
||||
Sonntagszuschlag50: input.ZuschlagSonntag,
|
||||
@@ -66,10 +67,27 @@ func (s *Service) GeneratePayrollStatement(input domain.PayrollInput) domain.Pay
|
||||
Feiertagszuschlag150: input.ZuschlagFeiertag150,
|
||||
}
|
||||
|
||||
if input.DepartmentNotLeitstelle {
|
||||
statement.Verpflegungspauschale = float64(input.Verpflegungspauschale)
|
||||
}
|
||||
|
||||
if input.SickDays > 0 || !input.DepartmentIsKtw {
|
||||
statement.GesundheitsbonusAbzug = "ja"
|
||||
}
|
||||
|
||||
if input.SickDays <= 0 && input.DepartmentIsKtw {
|
||||
switch {
|
||||
case statement.Vertragsstunden > 173.0:
|
||||
statement.Gesundheitsbonus = 200.0
|
||||
case statement.Vertragsstunden > 120:
|
||||
statement.Gesundheitsbonus = 150.0
|
||||
case statement.Vertragsstunden > 100:
|
||||
statement.Gesundheitsbonus = 100.0
|
||||
default:
|
||||
statement.Gesundheitsbonus = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
if input.DepartmentIsKtw {
|
||||
statement.Urlaubsgeld = float64(input.HolidayDays)
|
||||
statement.PScheinZulage = float64(input.ZulagePassengerTransport)
|
||||
@@ -107,19 +125,30 @@ func (s *Service) LoadPayrollInput(ctx context.Context, month time.Time, employe
|
||||
return domain.PayrollInput{}, err
|
||||
}
|
||||
|
||||
isNotLeitstelle, err := s.isEmployeesDepartmentNotLeitstelle(ctx, month, int64(employee.ID))
|
||||
if err != nil {
|
||||
s.logger.Error("could not load employee department", "error", err)
|
||||
return domain.PayrollInput{}, err
|
||||
}
|
||||
|
||||
isKtw, err := s.isEmployeesDepartmentKTW(ctx, month, int64(employee.ID))
|
||||
if err != nil {
|
||||
s.logger.Error("could not load employee department", "error", err)
|
||||
return domain.PayrollInput{}, err
|
||||
}
|
||||
s.logger.Debug("fetched department", "isKtw", isKtw)
|
||||
|
||||
sickdays, err := s.loadSickdays(ctx, int64(employee.ID))
|
||||
if err != nil {
|
||||
s.logger.Error("could not load sickdays of employee", "employeeId", employee.ID, "error", err)
|
||||
}
|
||||
|
||||
input := domain.PayrollInput{
|
||||
Firstname: employee.Firstname,
|
||||
Surname: employee.Surname,
|
||||
HourlyRate: employee.ContractSalaryHour,
|
||||
ContractHours: int64(employee.ContractHoursWeek),
|
||||
DepartmentIsKtw: isKtw,
|
||||
Firstname: employee.Firstname,
|
||||
Surname: employee.Surname,
|
||||
HourlyRate: employee.ContractSalaryHour,
|
||||
ContractHours: int64(employee.ContractHoursWeek),
|
||||
DepartmentIsKtw: isKtw,
|
||||
DepartmentNotLeitstelle: isNotLeitstelle,
|
||||
|
||||
WorkedHours: metrics.WorkedHours,
|
||||
WorkedDays: int64(metrics.WorkedDays),
|
||||
@@ -128,6 +157,8 @@ func (s *Service) LoadPayrollInput(ctx context.Context, month time.Time, employe
|
||||
OvertimeHours: metrics.OvertimeHours,
|
||||
TotalHours: metrics.TotalHours,
|
||||
|
||||
SickdaysYear: sickdays,
|
||||
|
||||
ZulageTourengeld: tourengeld,
|
||||
ZulagePuenktlichkeit: punctuality,
|
||||
Verpflegungspauschale: verpflegungsPauschale,
|
||||
@@ -150,12 +181,15 @@ func (s *Service) LoadPayrollInput(ctx context.Context, month time.Time, employe
|
||||
input.ZuschlagVerpflegung = zuschlagData.Verpflegung14
|
||||
} else {
|
||||
s.logger.Warn("Keine Zuschlag-Daten für Mitarbeiter gefunden", "key", compoundKey)
|
||||
// (Kein Fehler, der Mitarbeiter hatte vielleicht keine Zuschläge)
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (s *Service) loadSickdays(ctx context.Context, employeeId int64) ([]database.ListSickdaysForEmployeeRow, error) {
|
||||
return s.repo.ListSickdaysForEmployee(ctx, int32(employeeId))
|
||||
}
|
||||
|
||||
func (s *Service) countVerpflegungspauschale(ctx context.Context, month time.Time, employeeId int64) (int64, error) {
|
||||
startOfMonth := beginningOfMonth(month)
|
||||
endOfMonth := endOfMonth(month)
|
||||
@@ -172,6 +206,22 @@ func (s *Service) countVerpflegungspauschale(ctx context.Context, month time.Tim
|
||||
return verpflegungsPauschale, nil
|
||||
}
|
||||
|
||||
func (s *Service) isEmployeesDepartmentNotLeitstelle(ctx context.Context, month time.Time, employeeId int64) (bool, error) {
|
||||
startOfMonth := beginningOfMonth(month)
|
||||
endOfMonth := endOfMonth(month)
|
||||
s.logger.Info("fetching department for employee", "employeeId", employeeId, "startDate", startOfMonth, "endDate", endOfMonth)
|
||||
|
||||
isNotLeitstelle, err := s.repo.IsEmployeesDepartmentNotLeitstelle(ctx, database.IsEmployeesDepartmentNotLeitstelleParams{
|
||||
StartDate: &startOfMonth,
|
||||
EndDate: &endOfMonth,
|
||||
EmployeeID: employeeId,
|
||||
})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to fetch department for employee %d: %w", employeeId, err)
|
||||
}
|
||||
return isNotLeitstelle, nil
|
||||
}
|
||||
|
||||
func (s *Service) isEmployeesDepartmentKTW(ctx context.Context, month time.Time, employeeId int64) (bool, error) {
|
||||
startOfMonth := beginningOfMonth(month)
|
||||
endOfMonth := endOfMonth(month)
|
||||
@@ -217,7 +267,13 @@ type payrollMetrics struct {
|
||||
func (s *Service) calculatePayrollMetrics(weeklyTimeData []domain.WeeklyTimeData, employee database.ListActiveEmployeesRow) payrollMetrics {
|
||||
var m payrollMetrics
|
||||
|
||||
employeeDailyContractHours := employee.ContractHoursWeek / 5
|
||||
employeeDailyContractHours := 0.0
|
||||
switch {
|
||||
case employee.ContractDaysWeek <= 0:
|
||||
employeeDailyContractHours = employee.ContractHoursWeek / 5
|
||||
default:
|
||||
employeeDailyContractHours = employee.ContractHoursWeek / float64(employee.ContractDaysWeek)
|
||||
}
|
||||
|
||||
for _, week := range weeklyTimeData {
|
||||
m.WorkedHours += week.WorkedHours
|
||||
@@ -261,6 +317,7 @@ func (s *Service) getWeeklyTimeData(ctx context.Context, month time.Time, employ
|
||||
func (s *Service) fetchTourengeld(ctx context.Context, month time.Time, dlEmployeeId string) (float64, error) {
|
||||
startOfMonth := beginningOfMonth(month)
|
||||
endOfMonth := endOfMonth(month)
|
||||
s.logger.Info("fetching tourengeld for employee", "employeeId", dlEmployeeId, "startDate", startOfMonth, "endDate", endOfMonth)
|
||||
|
||||
tourengeld, err := s.repo.CountTourengeldAmountInPeriod(ctx, database.CountTourengeldAmountInPeriodParams{
|
||||
StartDate: startOfMonth,
|
||||
|
||||
Reference in New Issue
Block a user