Merge pull request #104 from Sea-Haven-Industries/feat/sh-169-board-create-lifecycle

feat(work-orders): persist board create lifecycleStatus (SH-169)
This commit is contained in:
Arthur Bassi 2026-09-04 13:42:42 -03:00 • committed by GitHub
commit 6d66737211
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 105 additions and 2 deletions

View file

@ -41,6 +41,8 @@ namespace SeaHaven.Services.DTOs
public DateOnly? TargetWeek { get; set; }
public bool? ScheduleWeekOnly { get; set; }
public DocStatus? DocStatus { get; set; }
/// <summary>Optional create-time lifecycle. When omitted, server defaults to Incomplete.</summary>
public LifecycleStatus? LifecycleStatus { get; set; }
public int? VendorId { get; set; }
public DateTime? ApptDate { get; set; }
public string? ApptTime { get; set; }

View file

@ -0,0 +1,47 @@
using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using SeaHaven.Services.Exceptions;
namespace SeaHaven.Services.Helpers
{
public static class WorkOrderBoardCreateLifecycleRules
{
private static readonly HashSet<LifecycleStatus> AllowedCreateStatuses =
[
LifecycleStatus.Incomplete,
LifecycleStatus.Pending,
LifecycleStatus.Scheduled,
];
public static void ApplyRequestedOrInitialLifecycleStatus(
WorkOrder workOrder,
LifecycleStatus? requested,
DateTime? scheduledDate,
bool? scheduleWeekOnly)
{
if (!requested.HasValue)
{
WorkOrderBoardFieldMutations.SetInitialLifecycleStatus(workOrder);
return;
}
if (!AllowedCreateStatuses.Contains(requested.Value))
{
throw new WorkOrderBoardValidationException(
"InvalidLifecycleStatus",
"lifecycleStatus must be Incomplete, Pending, or Scheduled at create.");
}
if (requested == LifecycleStatus.Scheduled
&& (!scheduledDate.HasValue || scheduleWeekOnly == true))
{
throw new WorkOrderBoardValidationException(
"ScheduledRequiresDate",
"Scheduled requires a concrete scheduledDate when scheduleWeekOnly is not true.");
}
workOrder.LifecycleStatus = requested.Value;
workOrder.Status = WorkOrderDerivedFields.GetLifecycleStatusLabel(requested.Value);
}
}
}

View file

@ -108,7 +108,11 @@ namespace SeaHaven.Services.Implementation
workOrder.VendorNotes = vendorNotes;
}
WorkOrderBoardFieldMutations.SetInitialLifecycleStatus(workOrder);
WorkOrderBoardCreateLifecycleRules.ApplyRequestedOrInitialLifecycleStatus(
workOrder,
request.LifecycleStatus,
request.ScheduledDate,
request.ScheduleWeekOnly);
workOrder.IsAddOn = WorkOrderAddonCutoff.ComputeIsAddOnAtCreation(
workOrder.CreatedDate!.Value,
@ -121,7 +125,7 @@ namespace SeaHaven.Services.Implementation
WorkOrderBoardFieldMutations.BoardFieldChange.Changed("InternalWONumber", "", woNumber),
WorkOrderBoardFieldMutations.BoardFieldChange.Changed("WorkOrderType", "", request.WorkOrderType.ToString()),
WorkOrderBoardFieldMutations.BoardFieldChange.Changed("SiteCode", "", siteCode),
WorkOrderBoardFieldMutations.BoardFieldChange.Changed("LifecycleStatus", "", LifecycleStatus.Incomplete.ToString())
WorkOrderBoardFieldMutations.BoardFieldChange.Changed("LifecycleStatus", "", workOrder.LifecycleStatus?.ToString() ?? LifecycleStatus.Incomplete.ToString())
};
if (workOrder.Description != null)

View file

@ -654,4 +654,54 @@ public class WorkOrderBoardCreateServiceTests
Assert.Equal("+1 555-0101", result.Info.AdditionalContacts[0].Phone);
Assert.Equal("Security Desk", result.Info.AdditionalContacts[1].Name);
}
[Fact]
public async Task Create_WithPendingLifecycle_PersistsPending()
{
var (_, service) = CreateSut();
var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto
{
WorkOrderType = WorkOrderType.PM,
SiteCode = "BK5",
LocationId = 1,
LifecycleStatus = LifecycleStatus.Pending,
}, WorkOrderAccountTestHelpers.AccountUser(), "actor-1");
Assert.Equal(LifecycleStatus.Pending, result.LifecycleStatus);
}
[Fact]
public async Task Create_WithScheduledLifecycleAndDate_PersistsWithoutAssignee()
{
var (_, service) = CreateSut();
var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto
{
WorkOrderType = WorkOrderType.PM,
SiteCode = "BK5",
LocationId = 1,
LifecycleStatus = LifecycleStatus.Scheduled,
ScheduledDate = new DateTime(2026, 7, 15),
ScheduleWeekOnly = false,
}, WorkOrderAccountTestHelpers.AccountUser(), "actor-1");
Assert.Equal(LifecycleStatus.Scheduled, result.LifecycleStatus);
Assert.Equal(new DateTime(2026, 7, 15), result.ScheduledDate?.Date);
}
[Fact]
public async Task Create_WithScheduledLifecycleWithoutDate_Throws()
{
var (_, service) = CreateSut();
await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
service.CreateAsync(new WorkOrderBoardCreateRequestDto
{
WorkOrderType = WorkOrderType.PM,
SiteCode = "BK5",
LocationId = 1,
LifecycleStatus = LifecycleStatus.Scheduled,
}, WorkOrderAccountTestHelpers.AccountUser(), "actor-1"));
}
}