diff --git a/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs b/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs index 9c73034..82795a3 100644 --- a/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs +++ b/Api.SeaHavenIndustries/Controllers/WorkOrderController.cs @@ -100,6 +100,7 @@ namespace Api.SeaHavenIndustries.Controllers LocationId = model.LocationId, AssignTo = model.AssignTo, DueDate = model.DueDate, + ScheduledDate = model.ScheduledDate, CompletedDate = model.CompletedDate, ContactIds = model.ContactIds, CategoryIds = model.CategoryIds, diff --git a/SeaHaven.Services/DTOs/WorkOrderDTOs.cs b/SeaHaven.Services/DTOs/WorkOrderDTOs.cs index f4f6003..fab9492 100644 --- a/SeaHaven.Services/DTOs/WorkOrderDTOs.cs +++ b/SeaHaven.Services/DTOs/WorkOrderDTOs.cs @@ -111,6 +111,7 @@ namespace SeaHaven.Services.DTOs public int? LocationId { get; set; } public string? AssignTo { get; set; } public DateTime? DueDate { get; set; } + public DateTime? ScheduledDate { get; set; } public DateTime? CompletedDate { get; set; } public static void UpdateEntity(UpdateWorkOrderDTO dto, WorkOrder entity) @@ -124,6 +125,7 @@ namespace SeaHaven.Services.DTOs if (dto.LocationId.HasValue) entity.LocationId = dto.LocationId; if (dto.AssignTo != null) entity.AssignTo = dto.AssignTo; if (dto.DueDate.HasValue) entity.DueDate = dto.DueDate; + if (dto.ScheduledDate.HasValue) entity.ScheduledDate = dto.ScheduledDate.Value.Date; if (dto.CompletedDate.HasValue) entity.CompletedDate = dto.CompletedDate; entity.LastModificationTime = DateTime.UtcNow; diff --git a/SeaHaven.Services/DTOs/WorkOrderServiceDTOs.cs b/SeaHaven.Services/DTOs/WorkOrderServiceDTOs.cs index 7665da4..fb59789 100644 --- a/SeaHaven.Services/DTOs/WorkOrderServiceDTOs.cs +++ b/SeaHaven.Services/DTOs/WorkOrderServiceDTOs.cs @@ -32,6 +32,7 @@ namespace SeaHaven.Services.DTOs public int? LocationId { get; set; } public string? AssignTo { get; set; } public DateTime? DueDate { get; set; } + public DateTime? ScheduledDate { get; set; } public DateTime? CompletedDate { get; set; } public List? ContactIds { get; set; } public List? CategoryIds { get; set; } diff --git a/SeaHaven.Services/Implementation/WorkOrderService.cs b/SeaHaven.Services/Implementation/WorkOrderService.cs index 6cfa57f..19695b2 100644 --- a/SeaHaven.Services/Implementation/WorkOrderService.cs +++ b/SeaHaven.Services/Implementation/WorkOrderService.cs @@ -6,6 +6,7 @@ using SeaHaven.DataServices.Interfaces; using SeaHaven.DataServices.Models; using SeaHaven.Services.DTOs; using SeaHaven.Services.Exceptions; +using SeaHaven.Services.Helpers; using SeaHaven.Services.Interfaces; using SeaHaven.Services.Validation; @@ -105,7 +106,19 @@ namespace SeaHaven.Services.Implementation var changes = new List(); changes.AddRange(TrackChanges(existingWorkOrder, dto)); + var scheduledDateChanged = dto.ScheduledDate.HasValue + && dto.ScheduledDate.Value.Date != existingWorkOrder.ScheduledDate?.Date; + var previousIsAddOn = existingWorkOrder.IsAddOn; UpdateWorkOrderDTO.UpdateEntity(dto, existingWorkOrder); + if (scheduledDateChanged) + { + existingWorkOrder.IsAddOn = WorkOrderAddonCutoff.RecalculateIsAddOnFromSchedule( + existingWorkOrder.CreatedDate ?? DateTime.UtcNow, + existingWorkOrder.ScheduledDate, + existingWorkOrder.TargetWeek); + if (existingWorkOrder.IsAddOn != previousIsAddOn) + changes.Add($"IsAddOn: {previousIsAddOn} → {existingWorkOrder.IsAddOn}"); + } await _workOrderDataService.UpdateAsync(existingWorkOrder); if (changes.Any()) @@ -405,6 +418,7 @@ namespace SeaHaven.Services.Implementation LocationId = input.LocationId, AssignTo = input.AssignTo, DueDate = input.DueDate, + ScheduledDate = input.ScheduledDate, CompletedDate = input.CompletedDate }; @@ -630,6 +644,8 @@ namespace SeaHaven.Services.Implementation changes.Add($"Assigned To: {existing.AssignTo} → {dto.AssignTo}"); if (dto.DueDate.HasValue && dto.DueDate != existing.DueDate) changes.Add($"Due Date: {existing.DueDate:yyyy-MM-dd} → {dto.DueDate:yyyy-MM-dd}"); + if (dto.ScheduledDate.HasValue && dto.ScheduledDate.Value.Date != existing.ScheduledDate?.Date) + changes.Add($"Schedule On: {existing.ScheduledDate:yyyy-MM-dd} → {dto.ScheduledDate:yyyy-MM-dd}"); return changes; } diff --git a/SeaHavenIndustries.Tests/WorkOrderEditAddonRescheduleTests.cs b/SeaHavenIndustries.Tests/WorkOrderEditAddonRescheduleTests.cs new file mode 100644 index 0000000..e595ea7 --- /dev/null +++ b/SeaHavenIndustries.Tests/WorkOrderEditAddonRescheduleTests.cs @@ -0,0 +1,78 @@ +using System.Security.Claims; +using Api.SeaHavenIndustries.Controllers; +using Api.SeaHavenIndustries.DTOs; +using Data.SeaHavenIndustries; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging.Abstractions; +using SeaHaven.DataServices.Implementation; +using SeaHaven.DataServices.Models; +using SeaHaven.Services.Implementation; +using SeaHaven.Services.Validation; + +namespace SeaHavenIndustries.Tests; + +public sealed class WorkOrderEditAddonRescheduleTests +{ + [Fact] + public async Task EditWorkorder_RescheduleIntoEarlierWeek_EnablesAndAuditsAddOn() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(Guid.NewGuid().ToString()) + .Options; + await using var context = new ApplicationDbContext(options); + var workOrder = new WorkOrder + { + Id = 1, + CreatedDate = new DateTime(2026, 6, 21, 0, 0, 0, DateTimeKind.Utc), + ScheduledDate = new DateTime(2026, 6, 29), + IsAddOn = false + }; + context.workOrders.Add(workOrder); + await context.SaveChangesAsync(); + + var accountResolver = WorkOrderAccountTestHelpers.Resolver(context); + var workOrderService = new WorkOrderService( + new WorkOrderDataService(context), + new CommentDataService(context), + new UserDataService(context), + new QuotesDataService(context), + fileStorage: null!, + new CreateWorkOrderValidation(), + new UpdateWorkOrderValidation(), + accountResolver); + var controller = new WorkOrderController( + workOrderService, + accountResolver, + NullLogger.Instance) + { + ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext + { + User = new ClaimsPrincipal(new ClaimsIdentity( + new[] { new Claim(ClaimTypes.NameIdentifier, "dispatcher-1") }, + authenticationType: "test")) + } + } + }; + + var result = await controller.Editworkorder(new EditWorkorder_DTO + { + Id = 1, + ScheduledDate = new DateTime(2026, 6, 25) + }); + + Assert.IsType(result); + var persisted = await context.workOrders.SingleAsync(x => x.Id == 1); + Assert.Equal(new DateTime(2026, 6, 25), persisted.ScheduledDate); + Assert.True(persisted.IsAddOn); + var audit = await context.WorkOrderAuditLogs + .Where(x => x.WorkOrderId == 1) + .ToListAsync(); + Assert.Single(audit); + Assert.Contains(audit, x => x.FieldName?.Contains("Schedule On", StringComparison.Ordinal) == true); + Assert.Contains(audit, x => x.FieldName?.Contains("IsAddOn", StringComparison.Ordinal) == true); + } +}