Compare commits

..

1 commit

Author SHA1 Message Date
Alexandre Brandizzi
deb02c99f8
Merge 9d7efa34a6 into d204536215 2026-09-16 18:42:36 -03:00
2 changed files with 11 additions and 51 deletions

View file

@ -369,14 +369,9 @@ public class DashboardServiceTests
}
[Fact]
public async Task GetTrendAsync_DailyBucketsClassifyByCalendarScheduledDate()
public async Task GetTrendAsync_DailyBucketsClassifyByNewYorkScheduledDate()
{
using var ctx = NewContext();
// ScheduledDate is a stored calendar value (board writes persist `.Date`), so a work
// order buckets on its own calendar day regardless of the time-of-day component. The
// UTC hours below were previously shifted a full day by an America/New_York conversion
// (03:00 UTC -> prior evening), so they double as a regression guard: each row must now
// stay on the calendar date it was scheduled for.
ctx.workOrders.AddRange(
new WorkOrder
{
@ -431,21 +426,17 @@ public class DashboardServiceTests
trend.Buckets.Select(bucket => bucket.Label).Should().Equal(
"2026-01-14", "2026-01-15", "2026-01-16");
// 01-14: only the canceled row scheduled that calendar day.
trend.Buckets[0].Total.Should().Be(1);
trend.Buckets[0].Open.Should().Be(0);
trend.Buckets[0].Total.Should().Be(2);
trend.Buckets[0].Open.Should().Be(1);
trend.Buckets[0].Canceled.Should().Be(1);
trend.Buckets[0].Completed.Should().Be(0);
trend.Buckets[0].Overdue.Should().Be(0);
trend.Buckets[0].Overdue.Should().Be(1);
// 01-15: the 03:00 UTC scheduled row (no longer shifted to 01-14) plus the completed row.
trend.Buckets[1].Total.Should().Be(2);
trend.Buckets[1].Open.Should().Be(1);
trend.Buckets[1].Completed.Should().Be(1);
trend.Buckets[1].Canceled.Should().Be(0);
trend.Buckets[1].Overdue.Should().Be(1);
// 01-16: the 03:00 UTC scheduled row that stays on its own day.
trend.Buckets[2].Total.Should().Be(1);
trend.Buckets[2].Open.Should().Be(1);
trend.Buckets[2].Overdue.Should().Be(1);
@ -453,38 +444,6 @@ public class DashboardServiceTests
trend.Buckets.Should().OnlyContain(bucket => !bucket.IsCurrent);
}
[Fact]
public async Task GetTrendAsync_MidnightScheduledDateStaysInTodayBucketAndIsNotOverdue()
{
using var ctx = NewContext();
var today = DashboardBusinessTime.Today();
// A board-scheduled work order for today is stored as midnight (`.Date`). It must land
// in the Today bucket and count as open, not roll back to yesterday and read as overdue.
ctx.workOrders.Add(new WorkOrder
{
AccountId = 1,
LifecycleStatus = Data.SeaHavenIndustries.Enums.LifecycleStatus.Scheduled,
ScheduledDate = today.ToDateTime(TimeOnly.MinValue)
});
ctx.SaveChanges();
var query = new DashboardTrendQueryDTO
{
DateFrom = today.AddDays(-1),
DateTo = today.AddDays(1)
};
var trend = await NewService(ctx).GetTrendAsync(
AccountUser(1), query, CancellationToken.None);
var todayBucket = trend.Buckets.Single(bucket => bucket.Date == today);
todayBucket.Total.Should().Be(1);
todayBucket.Open.Should().Be(1);
todayBucket.Overdue.Should().Be(0);
var yesterdayBucket = trend.Buckets.Single(bucket => bucket.Date == today.AddDays(-1));
yesterdayBucket.Total.Should().Be(0);
}
[Fact]
public async Task GetTrendAsync_ThreeMonthRangeUsesWeeklyMondayBuckets()
{
@ -556,8 +515,6 @@ public class DashboardServiceTests
LifecycleStatus = Data.SeaHavenIndustries.Enums.LifecycleStatus.Scheduled,
ScheduledDate = new DateTime(2025, 12, 31, 20, 0, 0, DateTimeKind.Utc)
},
// 2027-01-01 by calendar: outside the 2026 window. Previously an
// America/New_York conversion pulled it back to 2026-12-31 and into December.
new WorkOrder
{
AccountId = 1,
@ -578,10 +535,10 @@ public class DashboardServiceTests
trend.Buckets[2].Label.Should().Be("2026-03-01");
trend.Buckets[2].Total.Should().Be(1);
trend.Buckets[2].Completed.Should().Be(1);
// December stays empty: the 2027-01-01 row is no longer pulled inside the window.
trend.Buckets[11].Label.Should().Be("2026-12-01");
trend.Buckets[11].Total.Should().Be(0);
trend.Buckets[11].Open.Should().Be(0);
trend.Buckets[11].Total.Should().Be(1);
trend.Buckets[11].Open.Should().Be(1);
trend.Buckets[11].Overdue.Should().Be(0);
trend.Today.Should().Be(DashboardBusinessTime.Today());
}

View file

@ -196,6 +196,7 @@ namespace SeaHaven.Services.Implementation
var today = DashboardBusinessTime.Today();
var (windowFrom, windowTo, granularity) = ResolveTrendWindow(query, today);
var timezone = TimeZoneInfo.FindSystemTimeZoneById(DashboardBusinessTime.TimeZoneId);
var firstBucketStart = BucketStart(windowFrom, granularity);
var lastBucketStart = BucketStart(windowTo, granularity);
var lastBucketEnd = BucketEnd(lastBucketStart, granularity);
@ -216,7 +217,9 @@ namespace SeaHaven.Services.Implementation
{
if (workOrder.ScheduledDate is not DateTime scheduled)
continue;
var businessDate = DateOnly.FromDateTime(scheduled.Date);
var businessDate = DateOnly.FromDateTime(
TimeZoneInfo.ConvertTimeFromUtc(
DateTime.SpecifyKind(scheduled, DateTimeKind.Utc), timezone));
if (businessDate < firstBucketStart || businessDate > lastBucketEnd)
continue;
var bucket = buckets[LocateBucket(bucketStarts, businessDate)];