mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-06 12:22:08 +00:00
fix: recalculate add-on on work order reschedule
This commit is contained in:
parent
d03c88d48f
commit
ac5550365f
5 changed files with 98 additions and 0 deletions
|
|
@ -100,6 +100,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
LocationId = model.LocationId,
|
LocationId = model.LocationId,
|
||||||
AssignTo = model.AssignTo,
|
AssignTo = model.AssignTo,
|
||||||
DueDate = model.DueDate,
|
DueDate = model.DueDate,
|
||||||
|
ScheduledDate = model.ScheduledDate,
|
||||||
CompletedDate = model.CompletedDate,
|
CompletedDate = model.CompletedDate,
|
||||||
ContactIds = model.ContactIds,
|
ContactIds = model.ContactIds,
|
||||||
CategoryIds = model.CategoryIds,
|
CategoryIds = model.CategoryIds,
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ namespace SeaHaven.Services.DTOs
|
||||||
public int? LocationId { get; set; }
|
public int? LocationId { get; set; }
|
||||||
public string? AssignTo { get; set; }
|
public string? AssignTo { get; set; }
|
||||||
public DateTime? DueDate { get; set; }
|
public DateTime? DueDate { get; set; }
|
||||||
|
public DateTime? ScheduledDate { get; set; }
|
||||||
public DateTime? CompletedDate { get; set; }
|
public DateTime? CompletedDate { get; set; }
|
||||||
|
|
||||||
public static void UpdateEntity(UpdateWorkOrderDTO dto, WorkOrder entity)
|
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.LocationId.HasValue) entity.LocationId = dto.LocationId;
|
||||||
if (dto.AssignTo != null) entity.AssignTo = dto.AssignTo;
|
if (dto.AssignTo != null) entity.AssignTo = dto.AssignTo;
|
||||||
if (dto.DueDate.HasValue) entity.DueDate = dto.DueDate;
|
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;
|
if (dto.CompletedDate.HasValue) entity.CompletedDate = dto.CompletedDate;
|
||||||
|
|
||||||
entity.LastModificationTime = DateTime.UtcNow;
|
entity.LastModificationTime = DateTime.UtcNow;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ namespace SeaHaven.Services.DTOs
|
||||||
public int? LocationId { get; set; }
|
public int? LocationId { get; set; }
|
||||||
public string? AssignTo { get; set; }
|
public string? AssignTo { get; set; }
|
||||||
public DateTime? DueDate { get; set; }
|
public DateTime? DueDate { get; set; }
|
||||||
|
public DateTime? ScheduledDate { get; set; }
|
||||||
public DateTime? CompletedDate { get; set; }
|
public DateTime? CompletedDate { get; set; }
|
||||||
public List<int>? ContactIds { get; set; }
|
public List<int>? ContactIds { get; set; }
|
||||||
public List<int>? CategoryIds { get; set; }
|
public List<int>? CategoryIds { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using SeaHaven.DataServices.Interfaces;
|
||||||
using SeaHaven.DataServices.Models;
|
using SeaHaven.DataServices.Models;
|
||||||
using SeaHaven.Services.DTOs;
|
using SeaHaven.Services.DTOs;
|
||||||
using SeaHaven.Services.Exceptions;
|
using SeaHaven.Services.Exceptions;
|
||||||
|
using SeaHaven.Services.Helpers;
|
||||||
using SeaHaven.Services.Interfaces;
|
using SeaHaven.Services.Interfaces;
|
||||||
using SeaHaven.Services.Validation;
|
using SeaHaven.Services.Validation;
|
||||||
|
|
||||||
|
|
@ -105,7 +106,19 @@ namespace SeaHaven.Services.Implementation
|
||||||
var changes = new List<string>();
|
var changes = new List<string>();
|
||||||
changes.AddRange(TrackChanges(existingWorkOrder, dto));
|
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);
|
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);
|
await _workOrderDataService.UpdateAsync(existingWorkOrder);
|
||||||
|
|
||||||
if (changes.Any())
|
if (changes.Any())
|
||||||
|
|
@ -405,6 +418,7 @@ namespace SeaHaven.Services.Implementation
|
||||||
LocationId = input.LocationId,
|
LocationId = input.LocationId,
|
||||||
AssignTo = input.AssignTo,
|
AssignTo = input.AssignTo,
|
||||||
DueDate = input.DueDate,
|
DueDate = input.DueDate,
|
||||||
|
ScheduledDate = input.ScheduledDate,
|
||||||
CompletedDate = input.CompletedDate
|
CompletedDate = input.CompletedDate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -630,6 +644,8 @@ namespace SeaHaven.Services.Implementation
|
||||||
changes.Add($"Assigned To: {existing.AssignTo} → {dto.AssignTo}");
|
changes.Add($"Assigned To: {existing.AssignTo} → {dto.AssignTo}");
|
||||||
if (dto.DueDate.HasValue && dto.DueDate != existing.DueDate)
|
if (dto.DueDate.HasValue && dto.DueDate != existing.DueDate)
|
||||||
changes.Add($"Due Date: {existing.DueDate:yyyy-MM-dd} → {dto.DueDate:yyyy-MM-dd}");
|
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;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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<ApplicationDbContext>()
|
||||||
|
.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<WorkOrderController>.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<OkObjectResult>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue