From 2446c10117142c36013c76c8c883b42267068e68 Mon Sep 17 00:00:00 2001 From: Alexandre Brandizzi Date: Mon, 14 Sep 2026 19:46:13 -0300 Subject: [PATCH] fix(work-orders): preserve unscheduled status scope --- .../Helpers/WorkOrderBoardQueryFilters.cs | 9 ++- .../WorkOrderBoardSearchTests.cs | 63 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/SeaHaven.DataServices/Helpers/WorkOrderBoardQueryFilters.cs b/SeaHaven.DataServices/Helpers/WorkOrderBoardQueryFilters.cs index 3d877e0..7ff26ba 100644 --- a/SeaHaven.DataServices/Helpers/WorkOrderBoardQueryFilters.cs +++ b/SeaHaven.DataServices/Helpers/WorkOrderBoardQueryFilters.cs @@ -176,10 +176,15 @@ namespace SeaHaven.DataServices.Helpers /// /// Restricts to unscheduled rows. A null ScheduledDate is the canonical /// unscheduled representation; replaces the date-range filter when the - /// unscheduled-only search mode is active. + /// unscheduled-only search mode is active. Mirrors the undated branch of + /// : completed and canceled rows are + /// excluded so both modes agree on the protected unscheduled semantics. /// public static IQueryable ApplyUnscheduledOnlyFilter(IQueryable query) - => query.Where(w => w.ScheduledDate == null); + => query.Where(w => + w.ScheduledDate == null + && w.LifecycleStatus != LifecycleStatus.Completed + && w.LifecycleStatus != LifecycleStatus.Canceled); public static IQueryable ApplyDateRangeFilter( IQueryable query, diff --git a/SeaHavenIndustries.Tests/WorkOrderBoardSearchTests.cs b/SeaHavenIndustries.Tests/WorkOrderBoardSearchTests.cs index d1d4890..721291a 100644 --- a/SeaHavenIndustries.Tests/WorkOrderBoardSearchTests.cs +++ b/SeaHavenIndustries.Tests/WorkOrderBoardSearchTests.cs @@ -1425,4 +1425,67 @@ public class WorkOrderAdvancedSearchServiceTests Assert.Empty(page0.Items.Select(i => i.Id).Intersect(page1.Items.Select(i => i.Id))); Assert.Equal(new[] { 1, 2, 3 }, page0.Items.Concat(page1.Items).Select(i => i.Id).OrderBy(id => id).ToArray()); } + + [Fact] + public async Task SearchAsync_UnscheduledOnly_ExcludesCompletedAndCanceledUndatedRows() + { + await using var context = CreateContext(); + + context.workOrders.AddRange( + new WorkOrder + { + Id = 1, + ScheduledDate = null, + LifecycleStatus = LifecycleStatus.Incomplete, + InternalWONumber = "10000000001", + istemplate = false + }, + new WorkOrder + { + Id = 2, + ScheduledDate = null, + LifecycleStatus = LifecycleStatus.Completed, + InternalWONumber = "10000000002", + istemplate = false + }, + new WorkOrder + { + Id = 3, + ScheduledDate = null, + LifecycleStatus = LifecycleStatus.Canceled, + InternalWONumber = "10000000003", + istemplate = false + }, + new WorkOrder + { + Id = 4, + ScheduledDate = new DateTime(2026, 6, 23), + LifecycleStatus = LifecycleStatus.Completed, + InternalWONumber = "10000000004", + istemplate = false + }); + + await context.SaveChangesAsync(); + + var dataService = new WorkOrderAdvancedSearchDataService(context); + var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( + dataService, + WorkOrderAccountTestHelpers.Resolver(context)); + + var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto + { + DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, + DateFrom = new DateOnly(2026, 6, 22), + DateTo = new DateOnly(2026, 6, 26), + UnscheduledOnly = true + }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); + + Assert.Equal(1, result.TotalCount); + Assert.Single(result.Items); + Assert.Equal(1, result.Items.Single().Id); + Assert.Null(result.Items.Single().ScheduledDate); + Assert.DoesNotContain(result.Items, i => i.Id == 2); + Assert.DoesNotContain(result.Items, i => i.Id == 3); + Assert.DoesNotContain(result.Items, i => i.Id == 4); + } }