Merge pull request #203 from Sea-Haven-Industries/fix/ab/sh-412-addon-weekonly-tests
Some checks are pending
Backend CI / Build and test (push) Waiting to run
Backend CI / architecture (push) Waiting to run
Backend CI / review (push) Waiting to run
Backend CI / ci-complete (push) Blocked by required conditions

SH-412: keep Add-On stable on week-only toggles and date-less edits
This commit is contained in:
Alexandre Brandizzi 2026-10-01 22:12:55 +00:00 • committed by GitHub
commit 93968dc2fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 109 additions and 2 deletions

View file

@ -823,12 +823,15 @@ namespace SeaHaven.Services.Implementation
return changes;
}
// Toggling week-only mode keeps the scheduled week, so Add-On is unchanged (SH-412).
private static List<FieldChange> ApplyScheduleWeekOnly(WorkOrder workOrder, string? value, string auditField)
private List<FieldChange> ApplyScheduleWeekOnly(WorkOrder workOrder, string? value, string auditField)
{
var previousDate = workOrder.ScheduledDate;
var previousTargetWeek = workOrder.TargetWeek;
var change = ApplyBoolField(value, auditField, v => workOrder.ScheduleWeekOnly = v, () => workOrder.ScheduleWeekOnly);
var changes = new List<FieldChange> { change };
changes.AddRange(ApplyAutoScheduleSideEffects(workOrder));
if (change.HasChanged)
AppendIsAddOnRecalculation(workOrder, previousDate, previousTargetWeek, changes);
return changes;
}
@ -838,6 +841,10 @@ namespace SeaHaven.Services.Implementation
DateOnly? previousTargetWeek,
List<FieldChange> changes)
{
// An unmoved schedule keeps Add-On, including the create-time hint on unscheduled WOs (SH-412).
if (workOrder.ScheduledDate == previousScheduledDate && workOrder.TargetWeek == previousTargetWeek)
return;
var oldValue = workOrder.IsAddOn.ToString();
var newIsAddOn = WorkOrderAddonCutoff.RecalculateIsAddOnOnReschedule(
workOrder.IsAddOn,

View file

@ -2267,6 +2267,76 @@ public class WorkOrderBoardUpdateServiceTests
Assert.True(persisted.IsAddOn);
}
[Theory]
[InlineData(true, "true")]
[InlineData(false, "true")]
[InlineData(true, "false")]
[InlineData(false, "false")]
public async Task PatchField_ScheduleWeekOnlyToggle_KeepsIsAddOn(bool isAddOn, string weekOnly)
{
var (context, service) = CreateSut(new FixedTimeProvider());
var wo = new WorkOrder
{
Id = 1,
LifecycleStatus = weekOnly == "true" ? LifecycleStatus.Scheduled : LifecycleStatus.Pending,
WorkOrderType = WorkOrderType.PM,
ScheduledDate = new DateTime(2026, 6, 25),
ScheduleWeekOnly = weekOnly != "true",
CreatedDate = new DateTime(2026, 6, 1, 0, 0, 0, DateTimeKind.Utc),
IsAddOn = isAddOn,
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
};
context.workOrders.Add(wo);
await context.SaveChangesAsync();
var result = await service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
{
Field = WorkOrderBoardFieldNames.ScheduleWeekOnly,
Value = weekOnly,
WorkOrderVersion = ToVersion(wo)
}, "actor-1");
Assert.Equal(isAddOn, result.IsAddOn);
var persisted = await context.workOrders.SingleAsync(w => w.Id == 1);
Assert.Equal(new DateTime(2026, 6, 25), persisted.ScheduledDate);
Assert.Equal(isAddOn, persisted.IsAddOn);
Assert.Empty(await context.WorkOrderAuditLogs.Where(a => a.FieldName == "IsAddOn").ToListAsync());
}
[Theory]
[InlineData("true")]
[InlineData("false")]
public async Task PatchField_ScheduleWeekOnlyToggle_Unscheduled_KeepsAddOnHint(string weekOnly)
{
var (context, service) = CreateSut(new FixedTimeProvider());
var wo = new WorkOrder
{
Id = 1,
LifecycleStatus = LifecycleStatus.Pending,
WorkOrderType = WorkOrderType.PM,
ScheduleWeekOnly = weekOnly != "true",
CreatedDate = new DateTime(2026, 6, 1, 0, 0, 0, DateTimeKind.Utc),
IsAddOn = true,
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
};
context.workOrders.Add(wo);
await context.SaveChangesAsync();
var result = await service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
{
Field = WorkOrderBoardFieldNames.ScheduleWeekOnly,
Value = weekOnly,
WorkOrderVersion = ToVersion(wo)
}, "actor-1");
Assert.True(result.IsAddOn);
var persisted = await context.workOrders.SingleAsync(w => w.Id == 1);
Assert.Null(persisted.ScheduledDate);
Assert.Null(persisted.TargetWeek);
Assert.True(persisted.IsAddOn);
Assert.Empty(await context.WorkOrderAuditLogs.Where(a => a.FieldName == "IsAddOn").ToListAsync());
}
[Theory]
[InlineData("2026-07-08", false, "2026-06-25", true)] // into the current week, past its cutoff
[InlineData("2026-06-24", true, "2026-07-01", false)] // into next week, before its cutoff

View file

@ -129,6 +129,36 @@ public sealed class WorkOrderEditAddonRescheduleTests
Assert.DoesNotContain("IsAddOn", audit.FieldName);
}
[Fact]
public async Task EditWorkorder_WithoutScheduledDate_LeavesScheduleAndAddOnUntouched()
{
// The edit form sends only changed fields: a null Schedule On means "not sent", never
// "clear". Clearing happens through the board patch, which turns Add-On off.
await using var context = CreateContext();
context.workOrders.Add(new WorkOrder
{
Id = 1,
CreatedDate = new DateTime(2026, 6, 23, 0, 0, 0, DateTimeKind.Utc),
ScheduledDate = new DateTime(2026, 6, 24),
IsAddOn = true
});
await context.SaveChangesAsync();
var result = await CreateController(context, new FixedTimeProvider()).Editworkorder(new EditWorkorder_DTO
{
Id = 1,
DueDate = new DateTime(2026, 6, 30)
});
Assert.IsType<OkObjectResult>(result);
var persisted = await context.workOrders.SingleAsync(x => x.Id == 1);
Assert.Equal(new DateTime(2026, 6, 24), persisted.ScheduledDate);
Assert.True(persisted.IsAddOn);
Assert.DoesNotContain(
await context.WorkOrderAuditLogs.Where(x => x.WorkOrderId == 1).ToListAsync(),
x => x.FieldName?.Contains("IsAddOn", StringComparison.Ordinal) == true);
}
[Fact]
public async Task EditWorkorder_FirstSchedule_SetsOriginalWeekAndPromotesIncomplete()
{