mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 21:13:12 +00:00
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.
77 lines
3.1 KiB
C#
77 lines
3.1 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|