Files
paliad/internal/services/deadline_calculator.go
mAi bc5b3557d0 feat(t-paliad-209): rename DeadlineRule.Code → SubmissionCode across Go layer
Workstream B Go sweep — matches mig 098. Every place the deadline-rules
service reads/writes the per-rule identifier now uses the new column
name and the new struct field. Distinct from rule_code (legal citation)
and from proceeding_types.code (the proceeding's 3-segment code).

Touch points:
- models.DeadlineRule.Code → SubmissionCode (db + json tags renamed
  in lockstep — JSON contract `submission_code` is the new shape).
- deadline_rule_service: ruleColumns SELECT list updated.
- rule_editor_service: CreateRuleInput.Code → SubmissionCode (json tag
  too), INSERT + CloneAsDraft SELECT updated.
- projection_service: lookupRuleByCode → lookupRuleBySubmissionCode
  (SQL WHERE clause + error message); every r.Code / parent.Code /
  rule.Code / first.Code / src.rule.Code read renamed.
- fristenrechner: r.Code / prev.Code / rule.Code reads renamed in
  Calculate (parent-anchor + override-key + computed-by-code map) and
  in CalculateRule's LocalCode emission; the proceeding-code+submission-
  code resolver query uses `submission_code = $2`.
- event_trigger_service / deadline_calculator: r.Code reads renamed.

UIDeadline.Code (the calculator's wire response) is unchanged — that
field is a separate API contract pointing at the same value; renaming
it would force every frontend deadline-renderer through a contract
break that isn't part of this workstream.

Test fixtures updated to the new SubmissionCode field name; live-DB
tests updated to the post-mig-098 prefixed values (`inf.sod` →
`upc.inf.cfi.sod` etc.). New submission_codes_shape_test asserts
every active+published row matches the 4+-segment proceeding-prefixed
shape (sibling of TestProceedingCodeShape; mirrors mig 098 §6.1).

go build ./... clean. go test ./internal/... green.
2026-05-18 15:06:04 +02:00

90 lines
2.8 KiB
Go

package services
import (
"time"
"mgit.msbls.de/m/paliad/internal/models"
)
// CalculatedDeadline is one computed deadline (rule + due date + adjustment info).
type CalculatedDeadline struct {
RuleCode string `json:"rule_code"`
RuleID string `json:"rule_id"`
Title string `json:"title"`
DueDate string `json:"due_date"` // YYYY-MM-DD, after holiday/weekend adjust
OriginalDueDate string `json:"original_due_date"` // YYYY-MM-DD, before adjust
WasAdjusted bool `json:"was_adjusted"`
}
// DeadlineCalculator turns rules into dates given a trigger event.
type DeadlineCalculator struct {
holidays *HolidayService
}
// NewDeadlineCalculator wires the calculator to the holiday service.
func NewDeadlineCalculator(holidays *HolidayService) *DeadlineCalculator {
return &DeadlineCalculator{holidays: holidays}
}
// CalculateEndDate applies a single rule's duration + timing to the event date,
// then bumps forward off non-working days for the given (country, regime).
// Returns (adjusted, original, didAdjust).
func (c *DeadlineCalculator) CalculateEndDate(eventDate time.Time, rule models.DeadlineRule, country, regime string) (time.Time, time.Time, bool) {
endDate := eventDate
timing := "after"
if rule.Timing != nil {
timing = *rule.Timing
}
sign := 1
if timing == "before" {
sign = -1
}
switch rule.DurationUnit {
case "days":
endDate = endDate.AddDate(0, 0, sign*rule.DurationValue)
case "weeks":
endDate = endDate.AddDate(0, 0, sign*rule.DurationValue*7)
case "months":
endDate = endDate.AddDate(0, sign*rule.DurationValue, 0)
}
original := endDate
adjusted, _, wasAdjusted := c.holidays.AdjustForNonWorkingDays(endDate, country, regime)
return adjusted, original, wasAdjusted
}
// CalculateFromRules calculates deadlines for a slice of rules using the
// given (country, regime) for non-working-day adjustment. Rules with
// duration == 0 (court-set hearings, decisions) return the event date itself.
func (c *DeadlineCalculator) CalculateFromRules(eventDate time.Time, rules []models.DeadlineRule, country, regime string) []CalculatedDeadline {
results := make([]CalculatedDeadline, 0, len(rules))
for _, r := range rules {
var adjusted, original time.Time
var wasAdjusted bool
if r.DurationValue > 0 {
adjusted, original, wasAdjusted = c.CalculateEndDate(eventDate, r, country, regime)
} else {
adjusted, original = eventDate, eventDate
}
code := ""
if r.SubmissionCode != nil {
code = *r.SubmissionCode
}
results = append(results, CalculatedDeadline{
RuleCode: code,
RuleID: r.ID.String(),
Title: r.Name,
DueDate: adjusted.Format("2006-01-02"),
OriginalDueDate: original.Format("2006-01-02"),
WasAdjusted: wasAdjusted,
})
}
return results
}