diff --git a/Api.SeaHavenIndustries.Tests/CalendarServiceTests.cs b/Api.SeaHavenIndustries.Tests/CalendarServiceTests.cs
index bceb520..fbab136 100644
--- a/Api.SeaHavenIndustries.Tests/CalendarServiceTests.cs
+++ b/Api.SeaHavenIndustries.Tests/CalendarServiceTests.cs
@@ -44,7 +44,7 @@ public class CalendarServiceTests
StartDate = startDate,
EndDate = startDate,
IsDeleted = isDeleted,
- CreatedDate = DateTime.Now
+ CreatedDate = DateTime.UtcNow
};
ctx.Events.Add(ev);
ctx.SaveChanges();
@@ -64,6 +64,7 @@ public class CalendarServiceTests
saved.Title.Should().Be("Standup");
saved.IsDeleted.Should().Be(false);
saved.CreatedDate.Should().NotBeNull();
+ saved.CreatedDate!.Value.Kind.Should().Be(DateTimeKind.Utc);
}
[Fact]
@@ -97,6 +98,7 @@ public class CalendarServiceTests
var updated = ctx.Events.Single();
updated.Title.Should().Be("New");
updated.LastModificationTime.Should().NotBeNull();
+ updated.LastModificationTime!.Value.Kind.Should().Be(DateTimeKind.Utc);
}
[Fact]
@@ -134,6 +136,7 @@ public class CalendarServiceTests
var row = ctx.Events.Single();
row.IsDeleted.Should().Be(true);
row.DeletionTime.Should().NotBeNull();
+ row.DeletionTime!.Value.Kind.Should().Be(DateTimeKind.Utc);
}
[Fact]
diff --git a/Api.SeaHavenIndustries.Tests/UpliftQueueReadTests.cs b/Api.SeaHavenIndustries.Tests/UpliftQueueReadTests.cs
index 5c398cf..b5427f5 100644
--- a/Api.SeaHavenIndustries.Tests/UpliftQueueReadTests.cs
+++ b/Api.SeaHavenIndustries.Tests/UpliftQueueReadTests.cs
@@ -164,6 +164,59 @@ public sealed class UpliftQueueReadTests
new DateTime(2026, 3, 10));
}
+ [Fact]
+ public async Task List_RejectedStatus_OrdersMostRecentlyRejectedFirst()
+ {
+ using var context = NewContext();
+ var vendor = new Vendor { CompanyName = "Gateway", IsActive = true };
+ var workOrder = new WorkOrder { WorkerOrderTitle = "Repair" };
+ context.AddRange(vendor, workOrder);
+ await context.SaveChangesAsync();
+ var dispatch = await SeedDispatchAsync(context, vendor, workOrder, "DIS-1");
+ // Request order (3/1, 3/2, 3/3) deliberately disagrees with decision order.
+ context.DispatchUpliftRequests.AddRange(
+ Request(dispatch, "Rejected", new DateTime(2026, 3, 1), decided: new DateTime(2026, 3, 10)),
+ Request(dispatch, "Rejected", new DateTime(2026, 3, 2), decided: new DateTime(2026, 3, 20)),
+ Request(dispatch, "Rejected", new DateTime(2026, 3, 3), decided: new DateTime(2026, 3, 15)));
+ await context.SaveChangesAsync();
+ var service = NewService(context);
+
+ var result = await service.ListAsync(UserWithRoles("Approver"), "Rejected", null, 1, 25, CancellationToken.None);
+
+ result.Items.Select(i => i.DecidedAt).Should().Equal(
+ new DateTime(2026, 3, 20),
+ new DateTime(2026, 3, 15),
+ new DateTime(2026, 3, 10));
+ }
+
+ [Fact]
+ public async Task List_RejectedStatus_ReturnsOnlyRejectedRequests_IncludingLegacyDenied()
+ {
+ using var context = NewContext();
+ var vendor = new Vendor { CompanyName = "Gateway", IsActive = true };
+ var workOrder = new WorkOrder { WorkerOrderTitle = "Repair" };
+ context.AddRange(vendor, workOrder);
+ await context.SaveChangesAsync();
+ var dispatch = await SeedDispatchAsync(context, vendor, workOrder, "DIS-1");
+ context.DispatchUpliftRequests.AddRange(
+ Request(dispatch, "Pending", new DateTime(2026, 3, 1)),
+ Request(dispatch, "Approved", new DateTime(2026, 3, 2), decided: new DateTime(2026, 3, 3)),
+ Request(dispatch, "Revoked", new DateTime(2026, 3, 4), decided: new DateTime(2026, 3, 5)),
+ Request(dispatch, "Withdrawn", new DateTime(2026, 3, 6)),
+ Request(dispatch, "Rejected", new DateTime(2026, 3, 7), decided: new DateTime(2026, 3, 8)),
+ Request(dispatch, "Denied", new DateTime(2026, 2, 1), decided: new DateTime(2026, 2, 2)));
+ await context.SaveChangesAsync();
+ var service = NewService(context);
+
+ var result = await service.ListAsync(UserWithRoles("Approver"), "Rejected", null, 1, 25, CancellationToken.None);
+
+ result.Total.Should().Be(2);
+ result.Items.Select(i => i.Status).Should().Equal("Rejected", "Rejected");
+ result.Items.Select(i => i.DecidedAt).Should().Equal(
+ new DateTime(2026, 3, 8),
+ new DateTime(2026, 2, 2));
+ }
+
[Fact]
public async Task List_WithoutStatusFilter_KeepsHistoricalNewestRequestFirstOrder()
{
@@ -558,6 +611,40 @@ public sealed class UpliftQueueReadTests
result.Items.Single().DecidedByName.Should().Be("Grace Hopper");
}
+ [Fact]
+ public async Task List_RejectedRow_CarriesRejecterRequesterDecisionTimeAndReason()
+ {
+ using var context = NewContext();
+ var (vendor, workOrder) = await SeedWorkOrderAsync(context, "WO-R", "SITE-R", "Plumbing");
+ var dispatch = await SeedDispatchAsync(context, vendor, workOrder, "DIS-R");
+ context.Users.Add(new ApplicationUser { Id = "user-7", FirstName = "Ada", LastName = "Lovelace" });
+ await context.SaveChangesAsync();
+ context.DispatchUpliftRequests.Add(new DispatchUpliftRequest
+ {
+ DispatchId = dispatch.Id,
+ Status = "Rejected",
+ CreatedDate = new DateTime(2026, 3, 1),
+ DecidedAt = new DateTime(2026, 3, 2, 16, 40, 0),
+ DecidedByUserId = "user-7",
+ DecisionNote = "Outside this work order's scope",
+ RequestedByVendorName = "Gateway",
+ RequiredTier = 1,
+ RequestedNTE = 250m,
+ NotificationStatus = "Pending"
+ });
+ await context.SaveChangesAsync();
+ var service = NewService(context);
+
+ var result = await service.ListAsync(UserWithRoles("Approver"), "Rejected", null, 1, 25, CancellationToken.None);
+
+ var row = result.Items.Single();
+ row.DecidedByName.Should().Be("Ada Lovelace");
+ row.RequestedByName.Should().Be("Gateway");
+ row.DecidedAt.Should().Be(new DateTime(2026, 3, 2, 16, 40, 0));
+ row.DecisionNote.Should().Be("Outside this work order's scope");
+ row.WorkOrderNumber.Should().Be("WO-R");
+ }
+
[Fact]
public async Task List_PendingExposureTotal_SumsEachPendingRequestsIncreaseAcrossTheQueue()
{
diff --git a/Api.SeaHavenIndustries.Tests/WorkOrderDataServiceTimestampTests.cs b/Api.SeaHavenIndustries.Tests/WorkOrderDataServiceTimestampTests.cs
new file mode 100644
index 0000000..512ad0e
--- /dev/null
+++ b/Api.SeaHavenIndustries.Tests/WorkOrderDataServiceTimestampTests.cs
@@ -0,0 +1,68 @@
+using Data.SeaHavenIndustries;
+using FluentAssertions;
+using Microsoft.EntityFrameworkCore;
+using SeaHaven.DataServices.Implementation;
+using Xunit;
+
+namespace Api.SeaHavenIndustries.Tests;
+
+///
+/// The SLA clock and every "created"/"modified" display read these stamps as UTC, so the data layer
+/// must never write local server time into them.
+///
+public class WorkOrderDataServiceTimestampTests
+{
+ private static ApplicationDbContext NewContext()
+ {
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(Guid.NewGuid().ToString())
+ .Options;
+ return new ApplicationDbContext(options);
+ }
+
+ private static WorkOrder NewWorkOrder() =>
+ new() { InternalWONumber = "WO-1", WorkerOrderTitle = "Leak", LocationId = 100 };
+
+ [Fact]
+ public async Task AddAsync_KeepsTheCreationTimeTheCallerSet()
+ {
+ await using var context = NewContext();
+ var createdAt = new DateTime(2026, 9, 25, 14, 0, 0, DateTimeKind.Utc);
+ var workOrder = NewWorkOrder();
+ workOrder.CreatedDate = createdAt;
+
+ var saved = await new WorkOrderDataService(context).AddAsync(workOrder);
+
+ saved.CreatedDate.Should().Be(createdAt);
+ saved.CreatedDate!.Value.Kind.Should().Be(DateTimeKind.Utc);
+ (await context.workOrders.SingleAsync()).CreatedDate.Should().Be(createdAt);
+ }
+
+ [Fact]
+ public async Task AddAsync_StampsUtcWhenTheCallerSetNoCreationTime()
+ {
+ await using var context = NewContext();
+ var before = DateTime.UtcNow;
+
+ var saved = await new WorkOrderDataService(context).AddAsync(NewWorkOrder());
+
+ saved.CreatedDate.Should().NotBeNull();
+ saved.CreatedDate!.Value.Kind.Should().Be(DateTimeKind.Utc);
+ saved.CreatedDate.Value.Should().BeOnOrAfter(before).And.BeOnOrBefore(DateTime.UtcNow);
+ }
+
+ [Fact]
+ public async Task UpdateAsync_StampsTheModificationTimeInUtc()
+ {
+ await using var context = NewContext();
+ var service = new WorkOrderDataService(context);
+ var saved = await service.AddAsync(NewWorkOrder());
+ var before = DateTime.UtcNow;
+
+ await service.UpdateAsync(saved);
+
+ saved.LastModificationTime.Should().NotBeNull();
+ saved.LastModificationTime!.Value.Kind.Should().Be(DateTimeKind.Utc);
+ saved.LastModificationTime.Value.Should().BeOnOrAfter(before).And.BeOnOrBefore(DateTime.UtcNow);
+ }
+}
diff --git a/README.md b/README.md
index 30598d7..0f1afd4 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,12 @@ Both run in `us-east-1` on the .NET 8 Amazon Linux 2023 platform. Terraform in
`terraform/live/` owns the environments; GitHub Actions owns the application
versions. There is no production environment yet.
+Stored created, modified and deletion times are UTC: the API stamps them with
+`DateTime.UtcNow`, whatever the host's time zone. The API has only ever run on
+Linux Elastic Beanstalk hosts left at their UTC default (nothing in Terraform,
+`.ebextensions` or `.platform` sets a time zone), so rows written before the
+switch from `DateTime.Now` are already UTC and need no backfill.
+
## Architecture
```text
diff --git a/SeaHaven.DataServices/Implementation/AccountDataService.cs b/SeaHaven.DataServices/Implementation/AccountDataService.cs
index 5b00af2..ec2f874 100644
--- a/SeaHaven.DataServices/Implementation/AccountDataService.cs
+++ b/SeaHaven.DataServices/Implementation/AccountDataService.cs
@@ -64,7 +64,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(Accounts account)
{
- account.CreatedDate = DateTime.Now;
+ account.CreatedDate = DateTime.UtcNow;
await _context.Accounts.AddAsync(account);
await _context.SaveChangesAsync();
return account;
@@ -72,7 +72,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(Accounts account)
{
- account.LastModificationTime = DateTime.Now;
+ account.LastModificationTime = DateTime.UtcNow;
_context.Accounts.Update(account);
await _context.SaveChangesAsync();
}
diff --git a/SeaHaven.DataServices/Implementation/AssetDataService.cs b/SeaHaven.DataServices/Implementation/AssetDataService.cs
index 93a3f1e..1d60a7d 100644
--- a/SeaHaven.DataServices/Implementation/AssetDataService.cs
+++ b/SeaHaven.DataServices/Implementation/AssetDataService.cs
@@ -72,7 +72,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(Assets asset)
{
- asset.CreatedDate = DateTime.Now;
+ asset.CreatedDate = DateTime.UtcNow;
await _context.Assets.AddAsync(asset);
await _context.SaveChangesAsync();
return asset;
@@ -80,7 +80,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(Assets asset)
{
- asset.LastModificationTime = DateTime.Now;
+ asset.LastModificationTime = DateTime.UtcNow;
_context.Assets.Update(asset);
await _context.SaveChangesAsync();
}
diff --git a/SeaHaven.DataServices/Implementation/CategoryDataService.cs b/SeaHaven.DataServices/Implementation/CategoryDataService.cs
index c5af096..994acc3 100644
--- a/SeaHaven.DataServices/Implementation/CategoryDataService.cs
+++ b/SeaHaven.DataServices/Implementation/CategoryDataService.cs
@@ -25,7 +25,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(Category category)
{
- category.CreatedDate = DateTime.Now;
+ category.CreatedDate = DateTime.UtcNow;
await _context.Categories.AddAsync(category);
await _context.SaveChangesAsync();
return category;
@@ -33,7 +33,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(Category category)
{
- category.LastModificationTime = DateTime.Now;
+ category.LastModificationTime = DateTime.UtcNow;
_context.Categories.Update(category);
await _context.SaveChangesAsync();
}
diff --git a/SeaHaven.DataServices/Implementation/ContactDataService.cs b/SeaHaven.DataServices/Implementation/ContactDataService.cs
index 8196d2b..acd7e88 100644
--- a/SeaHaven.DataServices/Implementation/ContactDataService.cs
+++ b/SeaHaven.DataServices/Implementation/ContactDataService.cs
@@ -100,7 +100,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(Contacts contact)
{
- contact.CreatedDate = DateTime.Now;
+ contact.CreatedDate = DateTime.UtcNow;
await _context.Contacts.AddAsync(contact);
await _context.SaveChangesAsync();
return contact;
@@ -108,7 +108,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(Contacts contact)
{
- contact.LastModificationTime = DateTime.Now;
+ contact.LastModificationTime = DateTime.UtcNow;
_context.Contacts.Update(contact);
await _context.SaveChangesAsync();
}
@@ -130,15 +130,15 @@ namespace SeaHaven.DataServices.Implementation
public async Task EmailExistsAsync(string email, int? excludeId = null)
{
- return await _context.Contacts.AnyAsync(c =>
- c.Email == email &&
+ return await _context.Contacts.AnyAsync(c =>
+ c.Email == email &&
(excludeId == null || c.Id != excludeId));
}
public async Task PhoneExistsAsync(string phone, int? excludeId = null)
{
- return await _context.Contacts.AnyAsync(c =>
- c.PhoneNumber == phone &&
+ return await _context.Contacts.AnyAsync(c =>
+ c.PhoneNumber == phone &&
(excludeId == null || c.Id != excludeId));
}
diff --git a/SeaHaven.DataServices/Implementation/DispatchDataService.cs b/SeaHaven.DataServices/Implementation/DispatchDataService.cs
index 3274713..398051d 100644
--- a/SeaHaven.DataServices/Implementation/DispatchDataService.cs
+++ b/SeaHaven.DataServices/Implementation/DispatchDataService.cs
@@ -85,7 +85,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(Dispatch dispatch)
{
- dispatch.CreatedDate = DateTime.Now;
+ dispatch.CreatedDate = DateTime.UtcNow;
await _context.Dispatches.AddAsync(dispatch);
await _context.SaveChangesAsync();
return dispatch;
@@ -93,7 +93,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(Dispatch dispatch)
{
- dispatch.LastModificationTime = DateTime.Now;
+ dispatch.LastModificationTime = DateTime.UtcNow;
_context.Dispatches.Update(dispatch);
await _context.SaveChangesAsync();
}
diff --git a/SeaHaven.DataServices/Implementation/EmployeeDataService.cs b/SeaHaven.DataServices/Implementation/EmployeeDataService.cs
index 4606d11..7b0b2c7 100644
--- a/SeaHaven.DataServices/Implementation/EmployeeDataService.cs
+++ b/SeaHaven.DataServices/Implementation/EmployeeDataService.cs
@@ -72,7 +72,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(Employee employee)
{
- employee.CreatedDate = DateTime.Now;
+ employee.CreatedDate = DateTime.UtcNow;
await _context.Employees.AddAsync(employee);
await _context.SaveChangesAsync();
return employee;
@@ -80,7 +80,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(Employee employee)
{
- employee.LastModificationTime = DateTime.Now;
+ employee.LastModificationTime = DateTime.UtcNow;
_context.Employees.Update(employee);
await _context.SaveChangesAsync();
}
diff --git a/SeaHaven.DataServices/Implementation/FollowUpDataService.cs b/SeaHaven.DataServices/Implementation/FollowUpDataService.cs
index 4eebe82..72ef131 100644
--- a/SeaHaven.DataServices/Implementation/FollowUpDataService.cs
+++ b/SeaHaven.DataServices/Implementation/FollowUpDataService.cs
@@ -88,7 +88,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(FollowUps followUp)
{
- followUp.CreatedDate = DateTime.Now;
+ followUp.CreatedDate = DateTime.UtcNow;
await _context.FollowUps.AddAsync(followUp);
await _context.SaveChangesAsync();
return followUp;
@@ -96,7 +96,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(FollowUps followUp)
{
- followUp.LastModificationTime = DateTime.Now;
+ followUp.LastModificationTime = DateTime.UtcNow;
_context.FollowUps.Update(followUp);
await _context.SaveChangesAsync();
}
@@ -218,7 +218,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(FollowUps followUp, CancellationToken cancellationToken)
{
- followUp.CreatedDate = DateTime.Now;
+ followUp.CreatedDate = DateTime.UtcNow;
await _context.FollowUps.AddAsync(followUp, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
return followUp;
@@ -226,7 +226,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(FollowUps followUp, CancellationToken cancellationToken)
{
- followUp.LastModificationTime = DateTime.Now;
+ followUp.LastModificationTime = DateTime.UtcNow;
_context.FollowUps.Update(followUp);
await _context.SaveChangesAsync(cancellationToken);
}
@@ -238,7 +238,7 @@ namespace SeaHaven.DataServices.Implementation
return false;
entity.Status = status;
- entity.LastModificationTime = DateTime.Now;
+ entity.LastModificationTime = DateTime.UtcNow;
await _context.SaveChangesAsync(cancellationToken);
return true;
}
diff --git a/SeaHaven.DataServices/Implementation/LocationDataService.cs b/SeaHaven.DataServices/Implementation/LocationDataService.cs
index 5215e0e..a3d9a6c 100644
--- a/SeaHaven.DataServices/Implementation/LocationDataService.cs
+++ b/SeaHaven.DataServices/Implementation/LocationDataService.cs
@@ -100,7 +100,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(Locations location)
{
- location.CreatedDate = DateTime.Now;
+ location.CreatedDate = DateTime.UtcNow;
await _context.Locations.AddAsync(location);
await _context.SaveChangesAsync();
return location;
@@ -108,7 +108,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(Locations location)
{
- location.LastModificationTime = DateTime.Now;
+ location.LastModificationTime = DateTime.UtcNow;
_context.Locations.Update(location);
await _context.SaveChangesAsync();
}
@@ -232,7 +232,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(Locations location, CancellationToken cancellationToken)
{
- location.CreatedDate = DateTime.Now;
+ location.CreatedDate = DateTime.UtcNow;
await _context.Locations.AddAsync(location, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
return location;
@@ -240,7 +240,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(Locations location, CancellationToken cancellationToken)
{
- location.LastModificationTime = DateTime.Now;
+ location.LastModificationTime = DateTime.UtcNow;
_context.Locations.Update(location);
await _context.SaveChangesAsync(cancellationToken);
}
diff --git a/SeaHaven.DataServices/Implementation/PMScheduleDataService.cs b/SeaHaven.DataServices/Implementation/PMScheduleDataService.cs
index a2418ea..1ce854f 100644
--- a/SeaHaven.DataServices/Implementation/PMScheduleDataService.cs
+++ b/SeaHaven.DataServices/Implementation/PMScheduleDataService.cs
@@ -46,8 +46,8 @@ namespace SeaHaven.DataServices.Implementation
}
public async Task<(IEnumerable Items, int TotalCount)> GetPagedAsync(
- int page,
- int pageSize,
+ int page,
+ int pageSize,
string? search = null)
{
var query = _context.PMSchedules.AsQueryable();
@@ -64,7 +64,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(PMSchedules schedule)
{
- schedule.CreatedDate = DateTime.Now;
+ schedule.CreatedDate = DateTime.UtcNow;
await _context.PMSchedules.AddAsync(schedule);
await _context.SaveChangesAsync();
return schedule;
@@ -72,7 +72,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(PMSchedules schedule)
{
- schedule.LastModificationTime = DateTime.Now;
+ schedule.LastModificationTime = DateTime.UtcNow;
_context.PMSchedules.Update(schedule);
await _context.SaveChangesAsync();
}
diff --git a/SeaHaven.DataServices/Implementation/UpliftDataService.cs b/SeaHaven.DataServices/Implementation/UpliftDataService.cs
index 09566f3..0c3706c 100644
--- a/SeaHaven.DataServices/Implementation/UpliftDataService.cs
+++ b/SeaHaven.DataServices/Implementation/UpliftDataService.cs
@@ -11,6 +11,9 @@ namespace SeaHaven.DataServices.Implementation
public class UpliftDataService : IUpliftDataService
{
private static readonly ConcurrentDictionary WorkOrderGates = new();
+
+ // The Rejected queue also surfaces the legacy "Denied" spelling, which reads as Rejected.
+ private static readonly string[] RejectedStatuses = { "Rejected", "Denied" };
private readonly ApplicationDbContext _context;
public UpliftDataService(ApplicationDbContext context)
@@ -46,7 +49,9 @@ namespace SeaHaven.DataServices.Implementation
&& (d.IsDeleted == null || d.IsDeleted == false)
select new { u, d, v, ev, effectiveWorkOrderId, workOrder, reqUser, decUser };
- if (!string.IsNullOrWhiteSpace(status))
+ if (string.Equals(status, "Rejected", StringComparison.Ordinal))
+ query = query.Where(x => RejectedStatuses.Contains(x.u.Status));
+ else if (!string.IsNullOrWhiteSpace(status))
query = query.Where(x => x.u.Status == status);
if (tier.HasValue)
query = query.Where(x => x.u.RequiredTier == tier.Value);
@@ -54,12 +59,13 @@ namespace SeaHaven.DataServices.Implementation
var total = await query.CountAsync(cancellationToken);
// Approval queue read contract: the actionable queue (Pending) surfaces the
- // oldest request first; the decision log (Approved) surfaces the most
- // recently decided first. Every other read keeps the historical
+ // oldest request first; the decision logs (Approved, Rejected) surface the
+ // most recently decided first. Every other read keeps the historical
// newest-request-first order. Id is the deterministic tiebreaker.
if (string.Equals(status, "Pending", StringComparison.Ordinal))
query = query.OrderBy(x => x.u.CreatedDate).ThenBy(x => x.u.Id);
- else if (string.Equals(status, "Approved", StringComparison.Ordinal))
+ else if (string.Equals(status, "Approved", StringComparison.Ordinal)
+ || string.Equals(status, "Rejected", StringComparison.Ordinal))
query = query.OrderByDescending(x => x.u.DecidedAt).ThenByDescending(x => x.u.Id);
else
query = query.OrderByDescending(x => x.u.CreatedDate).ThenByDescending(x => x.u.Id);
diff --git a/SeaHaven.DataServices/Implementation/WorkOrderDataService.cs b/SeaHaven.DataServices/Implementation/WorkOrderDataService.cs
index d32b77b..b34db11 100644
--- a/SeaHaven.DataServices/Implementation/WorkOrderDataService.cs
+++ b/SeaHaven.DataServices/Implementation/WorkOrderDataService.cs
@@ -193,7 +193,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task AddAsync(WorkOrder workOrder)
{
- workOrder.CreatedDate = DateTime.Now;
+ workOrder.CreatedDate ??= DateTime.UtcNow;
await _context.workOrders.AddAsync(workOrder);
await _context.SaveChangesAsync();
return workOrder;
@@ -201,7 +201,7 @@ namespace SeaHaven.DataServices.Implementation
public async Task UpdateAsync(WorkOrder workOrder)
{
- workOrder.LastModificationTime = DateTime.Now;
+ workOrder.LastModificationTime = DateTime.UtcNow;
_context.workOrders.Update(workOrder);
await _context.SaveChangesAsync();
}
diff --git a/SeaHaven.Services/Implementation/CalendarService.cs b/SeaHaven.Services/Implementation/CalendarService.cs
index cece13d..596c083 100644
--- a/SeaHaven.Services/Implementation/CalendarService.cs
+++ b/SeaHaven.Services/Implementation/CalendarService.cs
@@ -31,7 +31,7 @@ namespace SeaHaven.Services.Implementation
EndDate = input.EndDate,
EndTime = input.EndTime,
AllDay = input.AllDay,
- CreatedDate = DateTime.Now,
+ CreatedDate = DateTime.UtcNow,
IsDeleted = false
};
@@ -57,7 +57,7 @@ namespace SeaHaven.Services.Implementation
model.EndDate = input.EndDate;
model.EndTime = input.EndTime;
model.AllDay = input.AllDay;
- model.LastModificationTime = DateTime.Now;
+ model.LastModificationTime = DateTime.UtcNow;
await _dataService.UpdateEventAsync(model, cancellationToken);
return true;
@@ -76,7 +76,7 @@ namespace SeaHaven.Services.Implementation
return false;
model.IsDeleted = true;
- model.DeletionTime = DateTime.Now;
+ model.DeletionTime = DateTime.UtcNow;
await _dataService.UpdateEventAsync(model, cancellationToken);
return true;
diff --git a/SeaHaven.Services/Implementation/ContactService.cs b/SeaHaven.Services/Implementation/ContactService.cs
index cfeab5b..bf359d2 100644
--- a/SeaHaven.Services/Implementation/ContactService.cs
+++ b/SeaHaven.Services/Implementation/ContactService.cs
@@ -104,7 +104,7 @@ namespace SeaHaven.Services.Implementation
ContactType = dto.Type,
AccountId = dto.AccountId,
createdby = userId,
- CreatedDate = DateTime.Now
+ CreatedDate = DateTime.UtcNow
};
var savedContact = await _contactDataService.AddAsync(contact);
@@ -151,7 +151,7 @@ namespace SeaHaven.Services.Implementation
if (dto.AccountId.HasValue)
existingContact.AccountId = dto.AccountId;
- existingContact.LastModificationTime = DateTime.Now;
+ existingContact.LastModificationTime = DateTime.UtcNow;
await _contactDataService.UpdateAsync(existingContact);
diff --git a/SeaHaven.Services/Implementation/LocationService.cs b/SeaHaven.Services/Implementation/LocationService.cs
index fabeb97..0caa441 100644
--- a/SeaHaven.Services/Implementation/LocationService.cs
+++ b/SeaHaven.Services/Implementation/LocationService.cs
@@ -579,7 +579,7 @@ namespace SeaHaven.Services.Implementation
{
ThrowOnInvalidContacts(contacts);
- var now = DateTime.Now;
+ var now = DateTime.UtcNow;
var siteContacts = contacts
.Select((row, index) => new Contacts
{
@@ -634,7 +634,7 @@ namespace SeaHaven.Services.Implementation
existing.PhoneNumber = phone;
existing.SiteContactOrder = i;
existing.AccountId = location.AccountId;
- existing.LastModificationTime = DateTime.Now;
+ existing.LastModificationTime = DateTime.UtcNow;
}
else
{
@@ -647,7 +647,7 @@ namespace SeaHaven.Services.Implementation
PhoneNumber = phone,
SiteContactOrder = i,
AccountId = location.AccountId,
- CreatedDate = DateTime.Now,
+ CreatedDate = DateTime.UtcNow,
createdby = actorId
});
}
@@ -655,7 +655,7 @@ namespace SeaHaven.Services.Implementation
// Rows omitted from the request are soft deleted so historical
// WorkOrderContacts keep rendering.
- var now = DateTime.Now;
+ var now = DateTime.UtcNow;
foreach (var existing in existingById.Values)
{
if (keptIds.Contains(existing.Id) || existing.IsDeleted == true)
diff --git a/SeaHaven.Services/Implementation/UserService.cs b/SeaHaven.Services/Implementation/UserService.cs
index 99105a3..f448a1f 100644
--- a/SeaHaven.Services/Implementation/UserService.cs
+++ b/SeaHaven.Services/Implementation/UserService.cs
@@ -72,7 +72,7 @@ namespace SeaHaven.Services.Implementation
{
model.EmailConfirmed = true;
model.UserName = model.Email;
- model.CreatedDate = DateTime.Now;
+ model.CreatedDate = DateTime.UtcNow;
model.UniqueName = "Active";
model.PhoneNumber = dto.Role;
@@ -152,7 +152,7 @@ namespace SeaHaven.Services.Implementation
exist.Email = dto.Email;
exist.NormalizedEmail = dto.Email.ToUpperInvariant();
exist.NormalizedUserName = dto.Email.ToUpperInvariant();
- exist.CreatedDate = DateTime.Now;
+ exist.CreatedDate = DateTime.UtcNow;
exist.UniqueName = "Active";
exist.PhoneNumber = dto.Role;
exist.AccountId = dto.AccountId;