feat(work-orders): apply schedule lifecycle promote and demote

This commit is contained in:
Arthur Bassi 2026-09-08 16:21:14 -03:00
parent 335390f746
commit e12b3ea54b
2 changed files with 87 additions and 22 deletions

View file

@ -50,25 +50,50 @@ namespace SeaHaven.Services.Helpers
}
/// <summary>
/// SHOC rule: specific scheduled date + assignee + Incomplete → Scheduled.
/// Week-only targets do not auto-schedule.
/// Promote Incomplete/Pending to Scheduled, or demote Scheduled to Pending, from Schedule On.
/// Assignee is not required. Week-only is not a concrete date.
/// </summary>
public static List<WorkOrderBoardFieldMutations.BoardFieldChange> ApplyLifecycleFromSchedule(WorkOrder workOrder)
{
var changes = new List<WorkOrderBoardFieldMutations.BoardFieldChange>();
LifecycleStatus? next = null;
if (WorkOrderBoardMutationRules.ShouldPromoteToScheduled(
workOrder.LifecycleStatus,
workOrder.ScheduledDate,
workOrder.ScheduleWeekOnly))
{
next = LifecycleStatus.Scheduled;
}
else if (WorkOrderBoardMutationRules.ShouldDemoteFromScheduled(
workOrder.LifecycleStatus,
workOrder.ScheduledDate,
workOrder.ScheduleWeekOnly))
{
next = LifecycleStatus.Pending;
}
if (next == null || workOrder.LifecycleStatus == next)
return changes;
var old = workOrder.LifecycleStatus?.ToString();
workOrder.LifecycleStatus = next;
workOrder.Status = GetLifecycleStatusLabel(next);
changes.Add(WorkOrderBoardFieldMutations.BoardFieldChange.StatusChanged(
"LifecycleStatus",
old,
next.Value.ToString()));
return changes;
}
/// <summary>
/// Applies schedule-driven lifecycle (promote or demote). True when the row becomes Scheduled.
/// </summary>
public static bool ApplyAutoScheduleIfEligible(WorkOrder workOrder)
{
if (workOrder.LifecycleStatus != LifecycleStatus.Incomplete)
return false;
if (workOrder.ScheduleWeekOnly == true)
return false;
if (workOrder.ScheduledDate == null)
return false;
if (string.IsNullOrWhiteSpace(workOrder.AssignTo))
return false;
workOrder.LifecycleStatus = LifecycleStatus.Scheduled;
return true;
var before = workOrder.LifecycleStatus;
ApplyLifecycleFromSchedule(workOrder);
return before != LifecycleStatus.Scheduled
&& workOrder.LifecycleStatus == LifecycleStatus.Scheduled;
}
/// <summary>

View file

@ -83,19 +83,48 @@ public class WorkOrderDerivedFieldsTests
}
[Fact]
public void ApplyAutoSchedule_WhenDateAndAssignee_PromotesToScheduled()
public void ApplyAutoSchedule_WhenDateWithoutAssignee_PromotesToScheduled()
{
var wo = new WorkOrder
{
LifecycleStatus = LifecycleStatus.Incomplete,
ScheduledDate = new DateTime(2026, 6, 25),
AssignTo = "dispatcher-1"
ScheduledDate = new DateTime(2026, 6, 25)
};
Assert.True(WorkOrderDerivedFields.ApplyAutoScheduleIfEligible(wo));
Assert.Equal(LifecycleStatus.Scheduled, wo.LifecycleStatus);
}
[Fact]
public void ApplyLifecycleFromSchedule_PendingWithDate_PromotesToScheduled()
{
var wo = new WorkOrder
{
LifecycleStatus = LifecycleStatus.Pending,
ScheduledDate = new DateTime(2026, 6, 25)
};
var changes = WorkOrderDerivedFields.ApplyLifecycleFromSchedule(wo);
Assert.Equal(LifecycleStatus.Scheduled, wo.LifecycleStatus);
Assert.Single(changes);
Assert.Equal(AuditActionType.StatusChanged, changes[0].Action);
}
[Fact]
public void ApplyLifecycleFromSchedule_ScheduledWithoutDate_DemotesToPending()
{
var wo = new WorkOrder
{
LifecycleStatus = LifecycleStatus.Scheduled,
ScheduledDate = null
};
var changes = WorkOrderDerivedFields.ApplyLifecycleFromSchedule(wo);
Assert.Equal(LifecycleStatus.Pending, wo.LifecycleStatus);
Assert.Single(changes);
Assert.Equal(LifecycleStatus.Pending.ToString(), changes[0].NewValue);
}
[Fact]
public void ApplyAutoSchedule_WeekOnly_DoesNotPromote()
{
@ -103,7 +132,6 @@ public class WorkOrderDerivedFieldsTests
{
LifecycleStatus = LifecycleStatus.Incomplete,
ScheduledDate = new DateTime(2026, 6, 25),
AssignTo = "dispatcher-1",
ScheduleWeekOnly = true,
TargetWeek = new DateOnly(2026, 6, 22)
};
@ -112,13 +140,25 @@ public class WorkOrderDerivedFieldsTests
Assert.Equal(LifecycleStatus.Incomplete, wo.LifecycleStatus);
}
[Fact]
public void ApplyLifecycleFromSchedule_InProgress_UnchangedWhenDateCleared()
{
var wo = new WorkOrder
{
LifecycleStatus = LifecycleStatus.InProgress,
ScheduledDate = null
};
Assert.Empty(WorkOrderDerivedFields.ApplyLifecycleFromSchedule(wo));
Assert.Equal(LifecycleStatus.InProgress, wo.LifecycleStatus);
}
[Fact]
public void SetInitialLifecycleStatus_NewWo_StartsIncompleteThenAutoSchedules()
{
var wo = new WorkOrder
{
ScheduledDate = new DateTime(2026, 6, 25),
AssignTo = "dispatcher-1"
ScheduledDate = new DateTime(2026, 6, 25)
};
WorkOrderDerivedFields.SetInitialLifecycleStatus(wo);