shoc-backend/SeaHaven.Services/Helpers/WorkOrderAddonCutoff.cs
Arthur Bassi 97e042f552 feat(work-orders): persist IsAddOn frozen at create (SH-126)
Add set-once IsAddOn with server cutoff at create, board DTO exposure, legacy type-7 backfill, and Types=AddOn search compat. Aligns with FE PR #61 frozen contract.
2026-08-13 13:30:09 -03:00

77 lines
3.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace SeaHaven.Services.Helpers
{
/// <summary>
/// Server-side Add-On cutoff: Saturday 23:59:59 UTC of the week prior to the schedule week.
/// SH-184: recalculated whenever scheduling is set, changed, or cleared; audited on change.
/// Create may accept a client hint when unscheduled; schedule present ignores override.
/// </summary>
public static class WorkOrderAddonCutoff
{
/// <summary>
/// Cutoff = Monday-of(anchor) − 2 days, end of day UTC (prior-week Saturday 23:59:59).
/// </summary>
public static DateTime GetAddonCutoffUtc(DateTime anchor)
{
var monday = WorkOrderBoardMutationRules.GetWeekStartMonday(anchor);
var saturday = monday.Date.AddDays(-2);
return DateTime.SpecifyKind(
saturday.AddHours(23).AddMinutes(59).AddSeconds(59),
DateTimeKind.Utc);
}
public static DateTime GetAddonCutoffUtc(DateOnly anchor)
=> GetAddonCutoffUtc(anchor.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc));
/// <summary>
/// With schedule (ScheduledDate or TargetWeek): IsAddOn when createdAtUtc is after cutoff.
/// Without schedule: client <paramref name="manualOverride"/> (default false).
/// </summary>
public static bool ComputeIsAddOnAtCreation(
DateTime createdAtUtc,
DateTime? scheduledDate,
DateOnly? targetWeek,
bool? manualOverride)
{
return ComputeFromScheduleAnchor(createdAtUtc, scheduledDate, targetWeek, manualOverride);
}
/// <summary>
/// SH-184 authoritative recalc after schedule set/change/clear. No manual override on patch.
/// Cleared schedule yields false.
/// </summary>
public static bool RecalculateIsAddOnFromSchedule(
DateTime createdAtUtc,
DateTime? scheduledDate,
DateOnly? targetWeek)
=> ComputeFromScheduleAnchor(createdAtUtc, scheduledDate, targetWeek, manualOverride: false);
private static bool ComputeFromScheduleAnchor(
DateTime createdAtUtc,
DateTime? scheduledDate,
DateOnly? targetWeek,
bool? manualOverride)
{
DateTime? anchor = null;
if (scheduledDate.HasValue)
anchor = scheduledDate.Value;
else if (targetWeek.HasValue)
anchor = targetWeek.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
if (!anchor.HasValue)
return manualOverride ?? false;
var cutoff = GetAddonCutoffUtc(anchor.Value);
var created = NormalizeUtc(createdAtUtc);
return created > cutoff;
}
private static DateTime NormalizeUtc(DateTime value)
{
if (value.Kind == DateTimeKind.Utc)
return value;
if (value.Kind == DateTimeKind.Local)
return value.ToUniversalTime();
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
}
}
}