mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-05 07:12:10 +00:00
fix(work-orders): process all WeekRolled candidates across batches
Offset paging skipped remaining WOs after ledger writes; always refetch the first page of unprocessed candidates and cover with a >BatchSize test.
This commit is contained in:
parent
8c33eb7f9a
commit
f977483c17
4 changed files with 41 additions and 7 deletions
|
|
@ -24,12 +24,13 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
DateOnly sourceWeekStart,
|
DateOnly sourceWeekStart,
|
||||||
DateOnly sourceWeekEnd,
|
DateOnly sourceWeekEnd,
|
||||||
int batchSize = 500,
|
int batchSize = 500,
|
||||||
int skip = 0,
|
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var weekStartDate = sourceWeekStart.ToDateTime(TimeOnly.MinValue);
|
var weekStartDate = sourceWeekStart.ToDateTime(TimeOnly.MinValue);
|
||||||
var weekEndDate = sourceWeekEnd.ToDateTime(TimeOnly.MinValue);
|
var weekEndDate = sourceWeekEnd.ToDateTime(TimeOnly.MinValue);
|
||||||
|
|
||||||
|
// Always return the first page of remaining unprocessed candidates.
|
||||||
|
// Callers must not offset-page: ledger writes shrink this set after each batch.
|
||||||
return await _context.workOrders
|
return await _context.workOrders
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(w => w.IsDeleted != true)
|
.Where(w => w.IsDeleted != true)
|
||||||
|
|
@ -42,7 +43,6 @@ namespace SeaHaven.DataServices.Implementation
|
||||||
.Where(w => !_context.WorkOrderWeekRolledLedgers.Any(l =>
|
.Where(w => !_context.WorkOrderWeekRolledLedgers.Any(l =>
|
||||||
l.WorkOrderId == w.Id && l.SourceWeekStart == sourceWeekStart))
|
l.WorkOrderId == w.Id && l.SourceWeekStart == sourceWeekStart))
|
||||||
.OrderBy(w => w.Id)
|
.OrderBy(w => w.Id)
|
||||||
.Skip(skip)
|
|
||||||
.Take(batchSize)
|
.Take(batchSize)
|
||||||
.Select(w => w.Id)
|
.Select(w => w.Id)
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ namespace SeaHaven.DataServices.Interfaces
|
||||||
DateOnly sourceWeekStart,
|
DateOnly sourceWeekStart,
|
||||||
DateOnly sourceWeekEnd,
|
DateOnly sourceWeekEnd,
|
||||||
int batchSize = 500,
|
int batchSize = 500,
|
||||||
int skip = 0,
|
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task<WeekRolledProcessOutcome> TryProcessWeekRolledAsync(
|
Task<WeekRolledProcessOutcome> TryProcessWeekRolledAsync(
|
||||||
|
|
|
||||||
|
|
@ -36,16 +36,17 @@ namespace SeaHaven.Services.Implementation
|
||||||
var processed = 0;
|
var processed = 0;
|
||||||
var skipped = 0;
|
var skipped = 0;
|
||||||
var failed = 0;
|
var failed = 0;
|
||||||
var skip = 0;
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"WeekRolled job started. CorrelationId={CorrelationId}, SourceWeekStart={SourceWeekStart}, SourceWeekEnd={SourceWeekEnd}",
|
"WeekRolled job started. CorrelationId={CorrelationId}, SourceWeekStart={SourceWeekStart}, SourceWeekEnd={SourceWeekEnd}",
|
||||||
correlationId, sourceWeekStart, sourceWeekEnd);
|
correlationId, sourceWeekStart, sourceWeekEnd);
|
||||||
|
|
||||||
|
// Always fetch the first page of remaining candidates. Processed rows write ledger
|
||||||
|
// entries that exclude them from the next query; offset paging would skip unprocessed rows.
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var candidates = await _dataService.GetWeekRolledCandidatesAsync(
|
var candidates = await _dataService.GetWeekRolledCandidatesAsync(
|
||||||
sourceWeekStart, sourceWeekEnd, BatchSize, skip, cancellationToken);
|
sourceWeekStart, sourceWeekEnd, BatchSize, cancellationToken);
|
||||||
|
|
||||||
if (candidates.Count == 0)
|
if (candidates.Count == 0)
|
||||||
break;
|
break;
|
||||||
|
|
@ -84,8 +85,6 @@ namespace SeaHaven.Services.Implementation
|
||||||
|
|
||||||
if (candidates.Count < BatchSize)
|
if (candidates.Count < BatchSize)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
skip += BatchSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,42 @@ public class WorkOrderWeekRolledTests
|
||||||
Assert.Equal(1, wo!.CarriedOver);
|
Assert.Equal(1, wo!.CarriedOver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WeekRolled_ProcessesAllEligible_WhenMoreThanBatchSize()
|
||||||
|
{
|
||||||
|
// WorkOrderWeekRolledService.BatchSize is 500; more than one page must all be carried over.
|
||||||
|
const int eligibleCount = 501;
|
||||||
|
|
||||||
|
await using var context = CreateContext();
|
||||||
|
for (var i = 1; i <= eligibleCount; i++)
|
||||||
|
{
|
||||||
|
context.workOrders.Add(new WorkOrder
|
||||||
|
{
|
||||||
|
Id = i,
|
||||||
|
ScheduledDate = new DateTime(2026, 6, 18),
|
||||||
|
LifecycleStatus = LifecycleStatus.Scheduled,
|
||||||
|
CarriedOver = 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var service = CreateService(context);
|
||||||
|
var result = await service.ProcessWeekRolledAsync(SourceWeekStart);
|
||||||
|
|
||||||
|
Assert.Equal(eligibleCount, result.Processed);
|
||||||
|
Assert.Equal(0, result.Skipped);
|
||||||
|
Assert.Equal(0, result.Failed);
|
||||||
|
|
||||||
|
var carriedOverCount = await context.workOrders.CountAsync(w => w.CarriedOver == 1);
|
||||||
|
Assert.Equal(eligibleCount, carriedOverCount);
|
||||||
|
|
||||||
|
var ledgerCount = await context.WorkOrderWeekRolledLedgers.CountAsync();
|
||||||
|
Assert.Equal(eligibleCount, ledgerCount);
|
||||||
|
|
||||||
|
var auditCount = await context.WorkOrderAuditLogs.CountAsync(l => l.Action == "WeekRolled");
|
||||||
|
Assert.Equal(eligibleCount, auditCount);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task PastDueCache_SyncsOperationalFlags()
|
public async Task PastDueCache_SyncsOperationalFlags()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue