shoc-backend/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs
Arthur Bassi 7c7c6bc525 feat(work-orders): persist Aveta Extra Docs media category
Round-trip category 5 on media POST/PATCH/GET and project hasAvetaDocument so pending vs attached survives reopen.
2026-08-25 15:10:45 -03:00

78 lines
3.1 KiB
C#

using Data.SeaHavenIndustries.Enums;
namespace SeaHaven.Services.Helpers
{
public static class WorkOrderBoardMutationRules
{
private static readonly HashSet<LifecycleStatus?> ReadOnlyStatuses =
LifecycleStatusSets.Terminal.Cast<LifecycleStatus?>().ToHashSet();
public static bool IsReadOnly(LifecycleStatus? status) => ReadOnlyStatuses.Contains(status);
public static bool IsFullyLocked(LifecycleStatus? status)
=> status == LifecycleStatus.Canceled;
public static bool IsCoreLocked(LifecycleStatus? status)
=> status == LifecycleStatus.Completed;
public static bool CanPatchBoardField(LifecycleStatus? status, string field)
{
if (IsFullyLocked(status))
return false;
if (!IsCoreLocked(status))
return true;
return field.Equals(WorkOrderBoardFieldNames.FlagColor, StringComparison.OrdinalIgnoreCase);
}
public static bool CanMutateComments(LifecycleStatus? status)
=> !IsFullyLocked(status);
public static bool CanMutateExtraMedia(LifecycleStatus? status, WorkOrderMediaCategory? category)
{
if (IsFullyLocked(status))
return false;
if (!IsCoreLocked(status))
return true;
return IsExtraDocsFamily(category);
}
public static bool IsExtraDocsFamily(WorkOrderMediaCategory? category)
{
var resolved = category ?? WorkOrderMediaCategory.Extra;
return resolved == WorkOrderMediaCategory.Extra || resolved == WorkOrderMediaCategory.Aveta;
}
public static bool ShouldBlockStatusChangeWhenPastDue(string field, bool isPastDue)
=> field.Equals(WorkOrderBoardFieldNames.LifecycleStatus, StringComparison.OrdinalIgnoreCase) && isPastDue;
/// <summary>
/// SHOC rule: Incomplete + specific scheduled date + assignee → Scheduled.
/// Week-only targets do not auto-schedule (same as <see cref="WorkOrderDerivedFields.ApplyAutoScheduleIfEligible"/>).
/// </summary>
public static bool ShouldAutoSchedule(
LifecycleStatus? status,
DateTime? scheduledDate,
string? assignTo,
bool? scheduleWeekOnly = null)
=> status == LifecycleStatus.Incomplete
&& scheduleWeekOnly != true
&& scheduledDate.HasValue
&& !string.IsNullOrWhiteSpace(assignTo);
public static bool IsReschedule(DateTime? previousDate, DateTime? newDate)
=> previousDate.HasValue
&& newDate.HasValue
&& previousDate.Value.Date != newDate.Value.Date;
public static DateOnly? ComputeOriginalWeek(DateTime? scheduledDate)
=> scheduledDate.HasValue
? DateOnly.FromDateTime(GetWeekStartMonday(scheduledDate.Value))
: null;
public static DateTime GetWeekStartMonday(DateTime date)
{
var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7;
return date.Date.AddDays(-diff);
}
}
}