shoc-backend/SeaHaven.Services/Helpers/WorkOrderAddonCutoff.cs

78 lines
3.1 KiB
C#
Raw Normal View History

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);
}
}
}