2026-07-30 12:20:07 -03:00
|
|
|
|
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.
|
2026-10-01 16:10:19 -03:00
|
|
|
|
/// At creation the WO arrives when it is created; on a reschedule it arrives when it is moved.
|
2026-07-30 12:20:07 -03:00
|
|
|
|
/// 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>
|
2026-10-01 16:10:19 -03:00
|
|
|
|
/// SH-412: a reschedule into another week makes the WO arrive in that week at the moment
|
|
|
|
|
|
/// of the reschedule, so Add-On compares <paramref name="rescheduledAtUtc"/> (not the
|
|
|
|
|
|
/// creation time) with the new week's cutoff. A move within the same week keeps the
|
|
|
|
|
|
/// current value; a cleared schedule yields false. Matches the prototype's reschedule rule.
|
2026-07-30 12:20:07 -03:00
|
|
|
|
/// </summary>
|
2026-10-01 16:10:19 -03:00
|
|
|
|
public static bool RecalculateIsAddOnOnReschedule(
|
|
|
|
|
|
bool currentIsAddOn,
|
|
|
|
|
|
DateTime? previousScheduledDate,
|
|
|
|
|
|
DateOnly? previousTargetWeek,
|
2026-07-30 12:20:07 -03:00
|
|
|
|
DateTime? scheduledDate,
|
2026-10-01 16:10:19 -03:00
|
|
|
|
DateOnly? targetWeek,
|
|
|
|
|
|
DateTime rescheduledAtUtc)
|
|
|
|
|
|
{
|
|
|
|
|
|
var anchor = ScheduleAnchor(scheduledDate, targetWeek);
|
|
|
|
|
|
if (!anchor.HasValue)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
var previousAnchor = ScheduleAnchor(previousScheduledDate, previousTargetWeek);
|
|
|
|
|
|
if (previousAnchor.HasValue
|
|
|
|
|
|
&& WorkOrderBoardMutationRules.GetWeekStartMonday(previousAnchor.Value)
|
|
|
|
|
|
== WorkOrderBoardMutationRules.GetWeekStartMonday(anchor.Value))
|
|
|
|
|
|
return currentIsAddOn;
|
|
|
|
|
|
|
|
|
|
|
|
return NormalizeUtc(rescheduledAtUtc) > GetAddonCutoffUtc(anchor.Value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static DateTime? ScheduleAnchor(DateTime? scheduledDate, DateOnly? targetWeek)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scheduledDate.HasValue)
|
|
|
|
|
|
return scheduledDate.Value;
|
|
|
|
|
|
if (targetWeek.HasValue)
|
|
|
|
|
|
return targetWeek.Value.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2026-07-30 12:20:07 -03:00
|
|
|
|
|
|
|
|
|
|
private static bool ComputeFromScheduleAnchor(
|
|
|
|
|
|
DateTime createdAtUtc,
|
|
|
|
|
|
DateTime? scheduledDate,
|
|
|
|
|
|
DateOnly? targetWeek,
|
|
|
|
|
|
bool? manualOverride)
|
|
|
|
|
|
{
|
2026-10-01 16:10:19 -03:00
|
|
|
|
var anchor = ScheduleAnchor(scheduledDate, targetWeek);
|
2026-07-30 12:20:07 -03:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|