- Holiday service with German federal holidays, Easter calculation, DB loading - Deadline calculator adapted from youpc.org (duration calc + non-working day adjustment) - Deadline CRUD service (tenant-scoped: list, create, update, complete, delete) - Deadline rule service (list, filter by proceeding type, hierarchical rule trees) - HTTP handlers for all endpoints with tenant resolution via X-Tenant-ID header - Router wired with all new endpoints under /api/ - Tests for holiday and calculator services (8 passing)
100 lines
2.7 KiB
Go
100 lines
2.7 KiB
Go
package services
|
|
|
|
import (
|
|
"time"
|
|
|
|
"mgit.msbls.de/m/KanzlAI-mGMT/internal/models"
|
|
)
|
|
|
|
// CalculatedDeadline holds a calculated deadline with adjustment info
|
|
type CalculatedDeadline struct {
|
|
RuleCode string `json:"rule_code"`
|
|
RuleID string `json:"rule_id"`
|
|
Title string `json:"title"`
|
|
DueDate string `json:"due_date"`
|
|
OriginalDueDate string `json:"original_due_date"`
|
|
WasAdjusted bool `json:"was_adjusted"`
|
|
}
|
|
|
|
// DeadlineCalculator calculates deadlines from rules and event dates
|
|
type DeadlineCalculator struct {
|
|
holidays *HolidayService
|
|
}
|
|
|
|
// NewDeadlineCalculator creates a new calculator
|
|
func NewDeadlineCalculator(holidays *HolidayService) *DeadlineCalculator {
|
|
return &DeadlineCalculator{holidays: holidays}
|
|
}
|
|
|
|
// CalculateEndDate calculates the end date for a single deadline rule based on an event date.
|
|
// Adapted from youpc.org CalculateDeadlineEndDate.
|
|
func (c *DeadlineCalculator) CalculateEndDate(eventDate time.Time, rule models.DeadlineRule) (adjusted time.Time, original time.Time, wasAdjusted bool) {
|
|
endDate := eventDate
|
|
|
|
timing := "after"
|
|
if rule.Timing != nil {
|
|
timing = *rule.Timing
|
|
}
|
|
|
|
durationValue := rule.DurationValue
|
|
durationUnit := rule.DurationUnit
|
|
|
|
if timing == "before" {
|
|
switch durationUnit {
|
|
case "days":
|
|
endDate = endDate.AddDate(0, 0, -durationValue)
|
|
case "weeks":
|
|
endDate = endDate.AddDate(0, 0, -durationValue*7)
|
|
case "months":
|
|
endDate = endDate.AddDate(0, -durationValue, 0)
|
|
}
|
|
} else {
|
|
switch durationUnit {
|
|
case "days":
|
|
endDate = endDate.AddDate(0, 0, durationValue)
|
|
case "weeks":
|
|
endDate = endDate.AddDate(0, 0, durationValue*7)
|
|
case "months":
|
|
endDate = endDate.AddDate(0, durationValue, 0)
|
|
}
|
|
}
|
|
|
|
original = endDate
|
|
adjusted, _, wasAdjusted = c.holidays.AdjustForNonWorkingDays(endDate)
|
|
return adjusted, original, wasAdjusted
|
|
}
|
|
|
|
// CalculateFromRules calculates deadlines for a set of rules given an event date.
|
|
// Returns a list of calculated deadlines with due dates.
|
|
func (c *DeadlineCalculator) CalculateFromRules(eventDate time.Time, rules []models.DeadlineRule) []CalculatedDeadline {
|
|
results := make([]CalculatedDeadline, 0, len(rules))
|
|
|
|
for _, rule := range rules {
|
|
var adjusted, original time.Time
|
|
var wasAdjusted bool
|
|
|
|
if rule.DurationValue > 0 {
|
|
adjusted, original, wasAdjusted = c.CalculateEndDate(eventDate, rule)
|
|
} else {
|
|
adjusted = eventDate
|
|
original = eventDate
|
|
}
|
|
|
|
code := ""
|
|
if rule.Code != nil {
|
|
code = *rule.Code
|
|
}
|
|
|
|
results = append(results, CalculatedDeadline{
|
|
RuleCode: code,
|
|
RuleID: rule.ID.String(),
|
|
Title: rule.Name,
|
|
DueDate: adjusted.Format("2006-01-02"),
|
|
OriginalDueDate: original.Format("2006-01-02"),
|
|
WasAdjusted: wasAdjusted,
|
|
})
|
|
}
|
|
|
|
return results
|
|
}
|