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

126 lines
4.9 KiB
C#
Raw Normal View History

namespace SeaHaven.Services.Helpers
{
public enum WorkOrderAdvancedSearchDatePreset
{
ThisWeek,
LastWeek,
ThisMonth,
Last3Months,
NextWeek,
NextMonth,
Custom
}
public record WorkOrderSearchDateRange(DateOnly From, DateOnly To);
public static class WorkOrderSearchDateRangeResolver
{
// Widest windows the frontend sends for "every week": the no-range search
// (ADVANCED_SEARCH_ALL_WEEKS_FROM/TO, 2000-01-01..2099-12-31) and the pinned
// Unassigned queue (UNASSIGNED_QUEUE_DATE_FROM/TO, 1970-01-01..2099-12-31).
private static readonly DateOnly AllWeeksFrom = new(2000, 1, 1);
private static readonly DateOnly AllWeeksTo = new(2099, 12, 31);
/// <summary>
/// Whether a board search adds open undated rows to its date range (SH-391).
/// An explicit <paramref name="includeDateless"/> wins. When it is omitted,
/// as by clients that predate the flag, a search with no date input or with
/// an all-weeks Custom window still returns undated rows, as before SH-391:
/// those requests mean "no range selected". Any other range is strict.
/// </summary>
public static bool ResolveIncludeDateless(
bool? includeDateless,
WorkOrderAdvancedSearchDatePreset? preset,
DateOnly? dateFrom,
DateOnly? dateTo)
{
if (includeDateless.HasValue)
return includeDateless.Value;
if (!preset.HasValue && !dateFrom.HasValue && !dateTo.HasValue)
return true;
return preset == WorkOrderAdvancedSearchDatePreset.Custom
&& dateFrom <= AllWeeksFrom
&& dateTo >= AllWeeksTo;
}
public static WorkOrderSearchDateRange Resolve(
WorkOrderAdvancedSearchDatePreset? preset,
DateOnly? dateFrom,
DateOnly? dateTo,
DateOnly? referenceDate = null)
{
var today = referenceDate ?? DateOnly.FromDateTime(DateTime.UtcNow);
var resolvedPreset = preset ?? WorkOrderAdvancedSearchDatePreset.ThisWeek;
if (resolvedPreset == WorkOrderAdvancedSearchDatePreset.Custom)
{
if (!dateFrom.HasValue || !dateTo.HasValue)
throw new ArgumentException("dateFrom and dateTo are required when datePreset is custom.");
if (dateTo.Value < dateFrom.Value)
throw new ArgumentException("dateTo must be on or after dateFrom.");
return new WorkOrderSearchDateRange(dateFrom.Value, dateTo.Value);
}
return resolvedPreset switch
{
WorkOrderAdvancedSearchDatePreset.ThisWeek => ThisIsoWeek(today),
WorkOrderAdvancedSearchDatePreset.LastWeek => LastIsoWeek(today),
WorkOrderAdvancedSearchDatePreset.ThisMonth => ThisMonth(today),
WorkOrderAdvancedSearchDatePreset.Last3Months => Last3Months(today),
WorkOrderAdvancedSearchDatePreset.NextWeek => NextIsoWeek(today),
WorkOrderAdvancedSearchDatePreset.NextMonth => NextMonth(today),
_ => ThisIsoWeek(today)
};
}
public static DateOnly GetIsoWeekStart(DateOnly date)
{
var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7;
return date.AddDays(-diff);
}
private static WorkOrderSearchDateRange ThisIsoWeek(DateOnly today)
{
var start = GetIsoWeekStart(today);
return new WorkOrderSearchDateRange(start, start.AddDays(4));
}
private static WorkOrderSearchDateRange LastIsoWeek(DateOnly today)
{
var start = GetIsoWeekStart(today).AddDays(-7);
return new WorkOrderSearchDateRange(start, start.AddDays(4));
}
private static WorkOrderSearchDateRange NextIsoWeek(DateOnly today)
{
var start = GetIsoWeekStart(today).AddDays(7);
return new WorkOrderSearchDateRange(start, start.AddDays(4));
}
private static WorkOrderSearchDateRange ThisMonth(DateOnly today)
{
var start = new DateOnly(today.Year, today.Month, 1);
var end = start.AddMonths(1).AddDays(-1);
return new WorkOrderSearchDateRange(start, end);
}
private static WorkOrderSearchDateRange NextMonth(DateOnly today)
{
var start = new DateOnly(today.Year, today.Month, 1).AddMonths(1);
var end = start.AddMonths(1).AddDays(-1);
return new WorkOrderSearchDateRange(start, end);
}
private static WorkOrderSearchDateRange Last3Months(DateOnly today)
{
var end = today;
var start = today.AddMonths(-3);
return new WorkOrderSearchDateRange(start, end);
}
}
}