ADR-011 — Weekly-Only Hours Computation
ADR-011 — Weekly-Only Hours Computation
Sección titulada «ADR-011 — Weekly-Only Hours Computation»Accepted
Context
Sección titulada «Context»The Hours Ledger service previously supported three period strategies — WEEKLY, MONTHLY_FIXED (scaling factor 4.0), and MONTHLY_CALENDAR (scaling factor = days_in_month / 7) — plus a separate PeriodType enum on assignments (WEEKLY / MONTHLY) with cross-period scaling logic to bridge mismatches between contracts and assignments.
This created three problems:
- Divergent enums:
PeriodStrategy(3 values on contracts) andPeriodType(2 values on assignments) were semantically related but structurally different, requiring a scaling bridge incalculate_balance(). - Arbitrary monthly definitions:
MONTHLY_FIXEDassumed 4 weeks/month;MONTHLY_CALENDARcomputed actual days/7. Neither matched a real-world standard — they were implementation artifacts. - Conflict with Argentine labor law: The LCT (Ley de Contrato de Trabajo 20.744, Articles 196-201; see [[adr-021-argentine-legal-context|ADR-021]]) defines labor hour obligations in weekly units. Jornada completa = 48h/week. Jornada insalubre = 36h/week. Overtime is computed per week. The legal unit of measurement is the week, not the month.
The “month” in hospital operations (e.g., “anesthesiologists rotate monthly”) is a scheduling pattern, not a unit of hour measurement. Monthly views (budgets, payroll) are aggregations of weekly data, not distinct computation strategies.
Decision
Sección titulada «Decision»Weekly-only computation. All hours computation is weekly. The ledger resolves every reference date to its ISO week (Monday–Sunday) and computes balance against base_weekly_hours directly, with no scaling factor. resolve_period() takes only a reference_date and returns a ComputePeriod(start_date, end_date) — always Monday to Sunday. calculate_balance() sums effective_hours directly with no cross-period scaling bridge.
Period enums and fields removed. The PeriodStrategy enum (was on ContractTemplate) and the PeriodType enum (was on Assignment) are removed, along with their corresponding fields ContractTemplate.period_strategy and Assignment.period_type. These were implementation artifacts of the multi-strategy approach and have no place in a weekly-only model.
Monthly views are read-time aggregations. Monthly views (budgets, payroll reports) are computed at read time by summing N weekly balances for the weeks that fall within a given month. This is a presentation concern, not a computation strategy.
Consequences
Sección titulada «Consequences»[!note]
ContractTemplatewas later replaced by tags [[adr-019-tag-based-requirements|ADR-019]] supersededContractTemplate(and itsbase_weekly_hours) with theTagCatalog+EmployeeTagmodel. The weekly-only decision here still holds: the weekly hour pool is nowmax(0, Σ hours_delta)over active tags, resolved to ISO weeks exactly as described above.
- The hours computation pipeline is simpler: no branching by strategy, no scaling factors, no cross-period bridge.
resolve_period()went from ~30 lines to ~3. ContractTemplatehas only two meaningful fields:nameandbase_weekly_hours. Simpler to create, validate, and reason about.Assignment.effective_hoursalways means “hours per week for this assignment.” No ambiguity.- Monthly aggregation endpoints can be added when the Coverage or Reports module requires them, as pure read-time operations. They are not in MVP scope.
- Database migration removed both columns. Existing seed data is unaffected (fields were informational, not used in critical paths beyond the now-simplified service).
- API contract updated:
period_strategyremoved from contract template endpoints;period.strategyandperiod.scaling_factorremoved from balance response.