From 335390f7468a5cc26385087da6bbcb5faa610761 Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Tue, 8 Sep 2026 16:19:50 -0300 Subject: [PATCH] feat(work-orders): derive Scheduled from date without assignee --- .../Helpers/WorkOrderBoardMutationRules.cs | 33 +++++++++-- .../WorkOrderBoardMutationRulesTests.cs | 59 +++++++++++++++++-- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs b/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs index dde4bfb..e1cec9d 100644 --- a/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs +++ b/SeaHaven.Services/Helpers/WorkOrderBoardMutationRules.cs @@ -46,7 +46,13 @@ namespace SeaHaven.Services.Helpers => field.Equals(WorkOrderBoardFieldNames.LifecycleStatus, StringComparison.OrdinalIgnoreCase) && isPastDue; /// - /// SHOC rule: Incomplete + specific scheduled date + assignee → Scheduled. + /// Concrete Schedule On: a calendar day is set and the row is not week-only. + /// + public static bool HasConcreteSchedule(DateTime? scheduledDate, bool? scheduleWeekOnly) + => scheduledDate.HasValue && scheduleWeekOnly != true; + + /// + /// Incomplete or Pending + concrete scheduled date → Scheduled. Assignee is not required. /// Week-only targets do not auto-schedule (same as ). /// public static bool ShouldAutoSchedule( @@ -54,10 +60,27 @@ namespace SeaHaven.Services.Helpers DateTime? scheduledDate, string? assignTo, bool? scheduleWeekOnly = null) - => status == LifecycleStatus.Incomplete - && scheduleWeekOnly != true - && scheduledDate.HasValue - && !string.IsNullOrWhiteSpace(assignTo); + { + _ = assignTo; + return ShouldPromoteToScheduled(status, scheduledDate, scheduleWeekOnly); + } + + public static bool ShouldPromoteToScheduled( + LifecycleStatus? status, + DateTime? scheduledDate, + bool? scheduleWeekOnly = null) + => (status == LifecycleStatus.Incomplete || status == LifecycleStatus.Pending) + && HasConcreteSchedule(scheduledDate, scheduleWeekOnly); + + /// + /// Scheduled without a concrete date (cleared or week-only) → Pending. Later statuses are ignored. + /// + public static bool ShouldDemoteFromScheduled( + LifecycleStatus? status, + DateTime? scheduledDate, + bool? scheduleWeekOnly = null) + => status == LifecycleStatus.Scheduled + && !HasConcreteSchedule(scheduledDate, scheduleWeekOnly); public static bool IsReschedule(DateTime? previousDate, DateTime? newDate) => previousDate.HasValue diff --git a/SeaHavenIndustries.Tests/WorkOrderBoardMutationRulesTests.cs b/SeaHavenIndustries.Tests/WorkOrderBoardMutationRulesTests.cs index 6494004..548a50f 100644 --- a/SeaHavenIndustries.Tests/WorkOrderBoardMutationRulesTests.cs +++ b/SeaHavenIndustries.Tests/WorkOrderBoardMutationRulesTests.cs @@ -27,12 +27,21 @@ public class WorkOrderBoardMutationRulesTests } [Fact] - public void ShouldAutoSchedule_WhenIncompleteWithDateAndAssignee() + public void ShouldAutoSchedule_WhenIncompleteWithDate_DoesNotRequireAssignee() { Assert.True(WorkOrderBoardMutationRules.ShouldAutoSchedule( LifecycleStatus.Incomplete, new DateTime(2026, 6, 25), - "dispatcher-1")); + assignTo: null)); + } + + [Fact] + public void ShouldAutoSchedule_WhenPendingWithConcreteDate() + { + Assert.True(WorkOrderBoardMutationRules.ShouldAutoSchedule( + LifecycleStatus.Pending, + new DateTime(2026, 6, 25), + assignTo: null)); } [Fact] @@ -41,7 +50,7 @@ public class WorkOrderBoardMutationRulesTests Assert.False(WorkOrderBoardMutationRules.ShouldAutoSchedule( LifecycleStatus.Scheduled, new DateTime(2026, 6, 25), - "dispatcher-1")); + assignTo: null)); } [Fact] @@ -50,10 +59,52 @@ public class WorkOrderBoardMutationRulesTests Assert.False(WorkOrderBoardMutationRules.ShouldAutoSchedule( LifecycleStatus.Incomplete, new DateTime(2026, 6, 25), - "dispatcher-1", + assignTo: null, scheduleWeekOnly: true)); } + [Fact] + public void ShouldAutoSchedule_FalseWhenInProgress() + { + Assert.False(WorkOrderBoardMutationRules.ShouldAutoSchedule( + LifecycleStatus.InProgress, + new DateTime(2026, 6, 25), + assignTo: null)); + } + + [Fact] + public void ShouldDemoteFromScheduled_WhenDateCleared() + { + Assert.True(WorkOrderBoardMutationRules.ShouldDemoteFromScheduled( + LifecycleStatus.Scheduled, + scheduledDate: null)); + } + + [Fact] + public void ShouldDemoteFromScheduled_WhenWeekOnly() + { + Assert.True(WorkOrderBoardMutationRules.ShouldDemoteFromScheduled( + LifecycleStatus.Scheduled, + new DateTime(2026, 6, 25), + scheduleWeekOnly: true)); + } + + [Fact] + public void ShouldDemoteFromScheduled_FalseWhenConcreteDateRemains() + { + Assert.False(WorkOrderBoardMutationRules.ShouldDemoteFromScheduled( + LifecycleStatus.Scheduled, + new DateTime(2026, 6, 25))); + } + + [Fact] + public void ShouldDemoteFromScheduled_FalseWhenInProgress() + { + Assert.False(WorkOrderBoardMutationRules.ShouldDemoteFromScheduled( + LifecycleStatus.InProgress, + scheduledDate: null)); + } + [Fact] public void CanPatchBoardField_AllowsOnlyFlagColorWhenCompleted() {