fix(work-orders): keep GET /board to scheduled-in-week rows only (SH-165)

This commit is contained in:
Arthur Bassi 2026-08-24 18:29:18 -03:00
parent 424d0b88bc
commit 04958e6121
10 changed files with 389 additions and 81 deletions

View file

@ -54,7 +54,8 @@ namespace Api.SeaHavenIndustries.Controllers
[FromQuery] bool myWorkOrders = false,
[FromQuery] List<WorkOrderType>? types = null,
[FromQuery] bool overdue = false,
[FromQuery] string? search = null)
[FromQuery] string? search = null,
CancellationToken cancellationToken = default)
{
var resolvedWeekEnd = weekEnd ?? weekStart.AddDays(4);
var weekValidationError = WorkOrderOperationalWeek.ValidateWeekWindow(weekStart, resolvedWeekEnd);
@ -75,7 +76,7 @@ namespace Api.SeaHavenIndustries.Controllers
Search = search
};
var result = await _workOrderBoardService.GetBoardAsync(query, User, userId);
var result = await _workOrderBoardService.GetBoardAsync(query, User, userId, cancellationToken);
return Ok(result);
}
catch (WorkOrderBoardValidationException ex) when (ex.Code == "Forbidden")

View file

@ -9,7 +9,8 @@ namespace SeaHaven.DataServices.Helpers
public static async Task<IReadOnlyList<WorkOrderBoardRawRow>> ProjectRowsAsync(
ApplicationDbContext context,
IQueryable<WorkOrder> query,
bool isUnscheduled)
bool isUnscheduled,
CancellationToken cancellationToken = default)
{
var rows = await query
.Select(w => new
@ -74,48 +75,14 @@ namespace SeaHaven.DataServices.Helpers
w.FlagColor,
w.PrimaryDispatchId,
w.RowVersion,
DispatchRowVersion = w.PrimaryDispatch != null ? w.PrimaryDispatch.RowVersion : null,
// SH-188: aggregate Pending uplifts on every live dispatch tied to this WO
// (primary WorkOrderId and multi-WO DispatchWorkOrders links).
PendingUpliftCount = context.DispatchUpliftRequests.Count(u =>
u.Status == "Pending"
&& (u.IsDeleted == null || u.IsDeleted == false)
&& u.Dispatch != null
&& (u.Dispatch.IsDeleted == null || u.Dispatch.IsDeleted == false)
&& (
u.Dispatch.WorkOrderId == w.Id
|| u.Dispatch.DispatchWorkOrders!.Any(link => link.WorkOrderId == w.Id))),
HasUplift = context.DispatchUpliftRequests.Any(u =>
(u.IsDeleted == null || u.IsDeleted == false)
&& u.Dispatch != null
&& (u.Dispatch.IsDeleted == null || u.Dispatch.IsDeleted == false)
&& (
u.Dispatch.WorkOrderId == w.Id
|| u.Dispatch.DispatchWorkOrders!.Any(link => link.WorkOrderId == w.Id))),
PrimaryUpliftStatus = context.DispatchUpliftRequests
.Where(u =>
(u.IsDeleted == null || u.IsDeleted == false)
&& u.Dispatch != null
&& (u.Dispatch.IsDeleted == null || u.Dispatch.IsDeleted == false)
&& (
u.Dispatch.WorkOrderId == w.Id
|| u.Dispatch.DispatchWorkOrders!.Any(link => link.WorkOrderId == w.Id)))
.OrderByDescending(u => u.CreatedDate)
.Select(u => u.Status)
.FirstOrDefault(),
PrimaryUpliftAmount = context.DispatchUpliftRequests
.Where(u =>
(u.IsDeleted == null || u.IsDeleted == false)
&& u.Dispatch != null
&& (u.Dispatch.IsDeleted == null || u.Dispatch.IsDeleted == false)
&& (
u.Dispatch.WorkOrderId == w.Id
|| u.Dispatch.DispatchWorkOrders!.Any(link => link.WorkOrderId == w.Id)))
.OrderByDescending(u => u.CreatedDate)
.Select(u => (decimal?)u.RequestedNTE)
.FirstOrDefault()
DispatchRowVersion = w.PrimaryDispatch != null ? w.PrimaryDispatch.RowVersion : null
})
.ToListAsync();
.ToListAsync(cancellationToken);
if (rows.Count == 0)
return Array.Empty<WorkOrderBoardRawRow>();
var upliftByWorkOrder = await LoadUpliftAggregatesAsync(context, query, cancellationToken);
return rows.Select(w =>
{
@ -129,6 +96,7 @@ namespace SeaHaven.DataServices.Helpers
w.VendorId,
w.VendorName,
w.PrimaryDispatchStatus);
upliftByWorkOrder.TryGetValue(w.Id, out var uplift);
return new WorkOrderBoardRawRow(
w.Id,
@ -171,13 +139,133 @@ namespace SeaHaven.DataServices.Helpers
w.PrimaryDispatchId,
w.RowVersion,
w.DispatchRowVersion,
w.PendingUpliftCount,
w.HasUplift,
w.PrimaryUpliftStatus,
w.PrimaryUpliftAmount,
uplift?.PendingCount ?? 0,
uplift?.HasUplift ?? false,
uplift?.PrimaryStatus,
uplift?.PrimaryAmount,
w.PrimaryDispatchStatus,
isUnscheduled);
}).ToList();
}
/// <summary>
/// SH-188: one round-trip for live uplifts on WOs in <paramref name="workOrders"/>
/// (primary WorkOrderId and multi-WO DispatchWorkOrders links). Uses the same
/// IQueryable as a SQL subquery so large boards do not hit the 2100-parameter cap.
/// </summary>
private static async Task<Dictionary<int, BoardUpliftAgg>> LoadUpliftAggregatesAsync(
ApplicationDbContext context,
IQueryable<WorkOrder> workOrders,
CancellationToken cancellationToken)
{
var workOrderIds = workOrders.Select(w => w.Id);
var live = context.DispatchUpliftRequests
.AsNoTracking()
.Where(u =>
(u.IsDeleted == null || u.IsDeleted == false)
&& u.Dispatch != null
&& (u.Dispatch.IsDeleted == null || u.Dispatch.IsDeleted == false));
var fromPrimary = live
.Where(u =>
u.Dispatch!.WorkOrderId != null
&& workOrderIds.Contains(u.Dispatch.WorkOrderId.Value))
.Select(u => new
{
UpliftId = u.Id,
WorkOrderId = u.Dispatch!.WorkOrderId!.Value,
u.Status,
u.RequestedNTE,
u.CreatedDate
});
var fromLinks = live
.SelectMany(
u => u.Dispatch!.DispatchWorkOrders!
.Where(link => workOrderIds.Contains(link.WorkOrderId)),
(u, link) => new
{
UpliftId = u.Id,
WorkOrderId = link.WorkOrderId,
u.Status,
u.RequestedNTE,
u.CreatedDate
});
var links = await fromPrimary.Concat(fromLinks).ToListAsync(cancellationToken);
return AggregateUpliftLinks(links.Select(l => new UpliftLinkRow(
l.UpliftId,
l.WorkOrderId,
l.Status,
l.RequestedNTE,
l.CreatedDate)));
}
private static Dictionary<int, BoardUpliftAgg> AggregateUpliftLinks(IEnumerable<UpliftLinkRow> links)
{
var seen = new HashSet<(int UpliftId, int WorkOrderId)>();
var byWorkOrder = new Dictionary<int, List<UpliftLinkRow>>();
foreach (var link in links)
{
if (!seen.Add((link.UpliftId, link.WorkOrderId)))
continue;
if (!byWorkOrder.TryGetValue(link.WorkOrderId, out var group))
{
group = new List<UpliftLinkRow>();
byWorkOrder[link.WorkOrderId] = group;
}
group.Add(link);
}
var result = new Dictionary<int, BoardUpliftAgg>(byWorkOrder.Count);
foreach (var (workOrderId, group) in byWorkOrder)
{
var pendingCount = 0;
UpliftLinkRow? primary = null;
foreach (var item in group)
{
if (item.Status == "Pending")
pendingCount++;
if (primary == null || IsNewer(item.CreatedDate, primary.CreatedDate))
primary = item;
}
result[workOrderId] = new BoardUpliftAgg(
pendingCount,
HasUplift: true,
primary?.Status,
primary?.RequestedNTE);
}
return result;
}
private static bool IsNewer(DateTime? candidate, DateTime? current)
{
if (candidate == current)
return false;
if (candidate is null)
return false;
if (current is null)
return true;
return candidate.Value > current.Value;
}
private sealed record UpliftLinkRow(
int UpliftId,
int WorkOrderId,
string Status,
decimal RequestedNTE,
DateTime? CreatedDate);
private sealed record BoardUpliftAgg(
int PendingCount,
bool HasUplift,
string? PrimaryStatus,
decimal? PrimaryAmount);
}
}

View file

@ -1,5 +1,4 @@
using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using Microsoft.EntityFrameworkCore;
using SeaHaven.DataServices.Helpers;
using SeaHaven.DataServices.Interfaces;
@ -15,10 +14,12 @@ namespace SeaHaven.DataServices.Implementation
_context = context;
}
public async Task<WorkOrderBoardQueryResult> GetBoardRowsAsync(WorkOrderBoardQuery query)
public async Task<WorkOrderBoardQueryResult> GetBoardRowsAsync(
WorkOrderBoardQuery query,
CancellationToken cancellationToken = default)
{
var weekStartDate = query.WeekStart.ToDateTime(TimeOnly.MinValue);
var weekEndDate = query.WeekEnd.ToDateTime(TimeOnly.MinValue);
var weekEndExclusive = query.WeekEnd.AddDays(1).ToDateTime(TimeOnly.MinValue);
var baseQuery = WorkOrderBoardQueryFilters.ApplyBaseScope(_context.workOrders.AsNoTracking());
if (query.AccountId.HasValue)
@ -29,33 +30,27 @@ namespace SeaHaven.DataServices.Implementation
var scheduledBase = baseQuery.Where(w =>
(w.ScheduledDate != null
&& w.ScheduledDate.Value.Date >= weekStartDate.Date
&& w.ScheduledDate.Value.Date <= weekEndDate.Date)
&& w.ScheduledDate >= weekStartDate
&& w.ScheduledDate < weekEndExclusive)
|| (w.ScheduleWeekOnly == true && w.TargetWeek == query.WeekStart));
var unscheduledBase = baseQuery.Where(w =>
w.ScheduledDate == null
&& !LifecycleStatusSets.Terminal.Contains(w.LifecycleStatus));
var scheduledTotal = await scheduledBase.CountAsync();
var scheduledTotal = await scheduledBase.CountAsync(cancellationToken);
var normalizedSearch = WorkOrderBoardSearchFilter.NormalizeSearch(query.Search);
if (normalizedSearch != null)
{
scheduledBase = WorkOrderBoardSearchFilter.Apply(scheduledBase, normalizedSearch);
unscheduledBase = WorkOrderBoardSearchFilter.Apply(unscheduledBase, normalizedSearch);
}
var scheduledRows = await WorkOrderBoardProjection.ProjectRowsAsync(
_context,
scheduledBase.OrderBy(w => w.ScheduledDate).ThenBy(w => w.InternalWONumber),
isUnscheduled: false);
var unscheduledRows = await WorkOrderBoardProjection.ProjectRowsAsync(
_context,
unscheduledBase.OrderBy(w => w.InternalWONumber),
isUnscheduled: true);
isUnscheduled: false,
cancellationToken);
return new WorkOrderBoardQueryResult(scheduledRows, unscheduledRows, scheduledTotal);
// Weekly board is scheduled-in-week only. Undated rows belong on GET /board/search.
return new WorkOrderBoardQueryResult(
scheduledRows,
Array.Empty<WorkOrderBoardRawRow>(),
scheduledTotal);
}
public async Task<IReadOnlyList<DispatcherLookupRow>> GetDispatcherLookupsAsync()
@ -74,14 +69,18 @@ namespace SeaHaven.DataServices.Implementation
.ToListAsync();
}
public async Task<WorkOrderBoardRawRow?> GetBoardRowByIdAsync(int workOrderId, int? accountId = null)
public async Task<WorkOrderBoardRawRow?> GetBoardRowByIdAsync(
int workOrderId,
int? accountId = null,
CancellationToken cancellationToken = default)
{
var query = WorkOrderBoardQueryFilters.ApplyBaseScope(
_context.workOrders.AsNoTracking().Where(w => w.Id == workOrderId));
if (accountId.HasValue)
query = WorkOrderBoardQueryFilters.ApplyAccountScope(query, accountId.Value);
var rows = await WorkOrderBoardProjection.ProjectRowsAsync(_context, query, isUnscheduled: false);
var rows = await WorkOrderBoardProjection.ProjectRowsAsync(
_context, query, isUnscheduled: false, cancellationToken);
return rows.FirstOrDefault();
}

View file

@ -4,9 +4,14 @@ namespace SeaHaven.DataServices.Interfaces
{
public interface IWorkOrderBoardDataService
{
Task<WorkOrderBoardQueryResult> GetBoardRowsAsync(WorkOrderBoardQuery query);
Task<WorkOrderBoardQueryResult> GetBoardRowsAsync(
WorkOrderBoardQuery query,
CancellationToken cancellationToken = default);
Task<IReadOnlyList<DispatcherLookupRow>> GetDispatcherLookupsAsync();
Task<WorkOrderBoardRawRow?> GetBoardRowByIdAsync(int workOrderId, int? accountId = null);
Task<WorkOrderBoardRawRow?> GetBoardRowByIdAsync(
int workOrderId,
int? accountId = null,
CancellationToken cancellationToken = default);
Task<bool> InternalWoNumberExistsAsync(string normalizedWoNumber, int excludeWorkOrderId);
}
}

View file

@ -15,6 +15,11 @@ namespace SeaHaven.Services.DTOs
public string? Search { get; set; }
}
/// <summary>
/// Scheduled-in-week counts for GET /board. <see cref="Total"/> is the week
/// match before the bar search filter; <see cref="Returned"/> is after it.
/// Undated work orders are not included.
/// </summary>
public class WorkOrderBoardCountsDto
{
public int Returned { get; set; }
@ -111,6 +116,10 @@ namespace SeaHaven.Services.DTOs
public DateOnly WeekStart { get; set; }
public DateOnly WeekEnd { get; set; }
public WorkOrderBoardCountsDto Counts { get; set; } = new();
/// <summary>
/// Always empty on the weekly board. Undated rows are served by GET /board/search.
/// Kept so existing clients can keep reading the field.
/// </summary>
public List<WorkOrderBoardRowDto> Unscheduled { get; set; } = new();
public List<WorkOrderBoardRowDto> Scheduled { get; set; } = new();
}

View file

@ -10,7 +10,7 @@ namespace SeaHaven.Services.Implementation
public class WorkOrderAdvancedSearchService : IWorkOrderAdvancedSearchService
{
private const int DefaultPageSize = 100;
private const int MaxPageSize = 100;
private const int MaxPageSize = 200;
private readonly IWorkOrderAdvancedSearchDataService _searchDataService;
private readonly IWorkOrderAccountResolver _accountResolver;

View file

@ -24,7 +24,8 @@ namespace SeaHaven.Services.Implementation
public async Task<WorkOrderBoardResponseDto> GetBoardAsync(
WorkOrderBoardQueryDto query,
ClaimsPrincipal user,
string? currentUserId)
string? currentUserId,
CancellationToken cancellationToken = default)
{
var accountId = _accountResolver.ResolveAccountFilter(user);
var weekStart = query.WeekStart;
@ -45,7 +46,7 @@ namespace SeaHaven.Services.Implementation
currentUserId,
accountId);
var result = await _boardDataService.GetBoardRowsAsync(dataQuery);
var result = await _boardDataService.GetBoardRowsAsync(dataQuery, cancellationToken);
var utcNow = DateTime.UtcNow;
return new WorkOrderBoardResponseDto

View file

@ -8,7 +8,8 @@ namespace SeaHaven.Services.Interfaces
Task<WorkOrderBoardResponseDto> GetBoardAsync(
WorkOrderBoardQueryDto query,
ClaimsPrincipal user,
string? currentUserId);
string? currentUserId,
CancellationToken cancellationToken = default);
Task<IReadOnlyList<DispatcherLookupDto>> GetDispatcherLookupsAsync();
Task<WorkOrderBoardRowDto?> GetBoardRowAsync(int workOrderId, ClaimsPrincipal user);
}

View file

@ -141,8 +141,7 @@ public class WorkOrderBoardSearchCountsTests
Assert.Equal(2, result.ScheduledTotalBeforeSearch);
Assert.Single(result.ScheduledRows);
Assert.Equal("BK5", result.ScheduledRows[0].SiteCode);
Assert.Single(result.UnscheduledRows);
Assert.Equal("BK5", result.UnscheduledRows[0].SiteCode);
Assert.Empty(result.UnscheduledRows);
}
}
@ -242,7 +241,23 @@ public class WorkOrderAdvancedSearchServiceTests
PageSize = 500
}, WorkOrderAccountTestHelpers.OrgWideAdmin(), null);
Assert.Equal(100, result.PageSize);
Assert.Equal(200, result.PageSize);
}
[Fact]
public async Task SearchAsync_AcceptsPageSize200()
{
await using var context = CreateContext();
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.ThisWeek,
PageSize = 200
}, WorkOrderAccountTestHelpers.OrgWideAdmin(), null);
Assert.Equal(200, result.PageSize);
}
[Fact]

View file

@ -19,7 +19,7 @@ public class WorkOrderBoardDataServiceTests
}
[Fact]
public async Task GetBoardRows_ReturnsScheduledInWeekAndUnscheduled()
public async Task GetBoardRows_ReturnsScheduledInWeekAndOmitsUnscheduledDump()
{
await using var context = CreateContext();
var weekStart = new DateOnly(2026, 6, 22);
@ -74,11 +74,56 @@ public class WorkOrderBoardDataServiceTests
Assert.Single(result.ScheduledRows);
Assert.Equal(1, result.ScheduledRows[0].Id);
Assert.Equal("Acme HVAC", result.ScheduledRows[0].VendorName);
Assert.Single(result.UnscheduledRows);
Assert.Equal(2, result.UnscheduledRows[0].Id);
Assert.Empty(result.UnscheduledRows);
Assert.Equal(1, result.ScheduledTotalBeforeSearch);
}
[Fact]
public async Task GetBoardRows_DifferentWeeksReturnDifferentScheduledSets()
{
await using var context = CreateContext();
var weekA = new DateOnly(2026, 6, 22);
var weekB = new DateOnly(2026, 6, 29);
context.workOrders.AddRange(
new WorkOrder
{
Id = 1,
InternalWONumber = "10000000001",
ScheduledDate = new DateTime(2026, 6, 23),
LifecycleStatus = LifecycleStatus.Scheduled
},
new WorkOrder
{
Id = 2,
InternalWONumber = "10000000002",
ScheduledDate = new DateTime(2026, 6, 30),
LifecycleStatus = LifecycleStatus.Scheduled
},
new WorkOrder
{
Id = 3,
InternalWONumber = "10000000003",
ScheduledDate = null,
LifecycleStatus = LifecycleStatus.Incomplete
});
await context.SaveChangesAsync();
var service = new WorkOrderBoardDataService(context);
var resultA = await service.GetBoardRowsAsync(new WorkOrderBoardQuery(
weekA, weekA.AddDays(4), null, false, null, false, null, null));
var resultB = await service.GetBoardRowsAsync(new WorkOrderBoardQuery(
weekB, weekB.AddDays(4), null, false, null, false, null, null));
Assert.Equal(1, resultA.ScheduledRows[0].Id);
Assert.Equal(2, resultB.ScheduledRows[0].Id);
Assert.Empty(resultA.UnscheduledRows);
Assert.Empty(resultB.UnscheduledRows);
Assert.Equal(1, resultA.ScheduledTotalBeforeSearch);
Assert.Equal(1, resultB.ScheduledTotalBeforeSearch);
}
[Fact]
public async Task GetBoardRows_FiltersByDispatcher()
{
@ -374,6 +419,122 @@ public class WorkOrderBoardPendingUpliftTests
Assert.Single(result.ScheduledRows);
Assert.Equal(0, result.ScheduledRows[0].PendingUpliftCount);
}
[Fact]
public async Task GetBoardRows_PendingUpliftOnPrimaryAndDispatchWorkOrderLink_DoesNotDoubleCount()
{
await using var context = CreateContext();
var weekStart = new DateOnly(2026, 6, 22);
context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC" });
context.Dispatches.Add(new Dispatch
{
Id = 10,
VendorId = 1,
WorkOrderId = 1
});
context.DispatchWorkOrders.Add(new DispatchWorkOrder
{
DispatchId = 10,
WorkOrderId = 1
});
context.workOrders.Add(new WorkOrder
{
Id = 1,
InternalWONumber = "10000000001",
ScheduledDate = new DateTime(2026, 6, 23),
LifecycleStatus = LifecycleStatus.Scheduled,
PrimaryDispatchId = 10
});
context.DispatchUpliftRequests.Add(new DispatchUpliftRequest
{
Id = 100,
DispatchId = 10,
RequestedNTE = 1500m,
Status = "Pending",
RequiredTier = 1,
NotificationStatus = "Pending"
});
await context.SaveChangesAsync();
var service = new WorkOrderBoardDataService(context);
var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery(
weekStart,
weekStart.AddDays(4),
null,
false,
null,
false,
null,
null));
Assert.Single(result.ScheduledRows);
Assert.Equal(1, result.ScheduledRows[0].PendingUpliftCount);
Assert.True(result.ScheduledRows[0].HasUplift);
}
[Fact]
public async Task GetBoardRows_ScheduledDateWithTimeOnWeekEnd_IsIncluded()
{
await using var context = CreateContext();
var weekStart = new DateOnly(2026, 6, 22);
var weekEnd = weekStart.AddDays(4);
context.workOrders.Add(new WorkOrder
{
Id = 1,
InternalWONumber = "10000000001",
ScheduledDate = weekEnd.ToDateTime(new TimeOnly(23, 59, 59)),
LifecycleStatus = LifecycleStatus.Scheduled
});
await context.SaveChangesAsync();
var service = new WorkOrderBoardDataService(context);
var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery(
weekStart,
weekEnd,
null,
false,
null,
false,
null,
null));
Assert.Single(result.ScheduledRows);
Assert.Equal(1, result.ScheduledRows[0].Id);
}
[Fact]
public async Task GetBoardRows_CanceledToken_ThrowsOperationCanceled()
{
await using var context = CreateContext();
var weekStart = new DateOnly(2026, 6, 22);
context.workOrders.Add(new WorkOrder
{
Id = 1,
InternalWONumber = "10000000001",
ScheduledDate = new DateTime(2026, 6, 23),
LifecycleStatus = LifecycleStatus.Scheduled
});
await context.SaveChangesAsync();
using var cts = new CancellationTokenSource();
cts.Cancel();
var service = new WorkOrderBoardDataService(context);
await Assert.ThrowsAnyAsync<OperationCanceledException>(() =>
service.GetBoardRowsAsync(
new WorkOrderBoardQuery(
weekStart,
weekStart.AddDays(4),
null,
false,
null,
false,
null,
null),
cts.Token));
}
}
public class WorkOrderBoardServiceTests
@ -460,4 +621,32 @@ public class WorkOrderBoardServiceTests
WeekEnd = new DateOnly(2026, 6, 15)
}, WorkOrderAccountTestHelpers.OrgWideAdmin(), null));
}
[Fact]
public async Task GetBoardAsync_CanceledToken_ThrowsOperationCanceled()
{
await using var context = CreateContext();
context.workOrders.Add(new WorkOrder
{
Id = 1,
InternalWONumber = "10000000001",
ScheduledDate = new DateTime(2026, 6, 23),
LifecycleStatus = LifecycleStatus.Scheduled
});
await context.SaveChangesAsync();
var boardService = new WorkOrderBoardService(
new WorkOrderBoardDataService(context),
WorkOrderAccountTestHelpers.Resolver(context));
using var cts = new CancellationTokenSource();
cts.Cancel();
await Assert.ThrowsAnyAsync<OperationCanceledException>(() =>
boardService.GetBoardAsync(
new WorkOrderBoardQueryDto { WeekStart = new DateOnly(2026, 6, 22) },
WorkOrderAccountTestHelpers.OrgWideAdmin(),
null,
cts.Token));
}
}