mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 10:43:13 +00:00
Merge pull request #97 from Sea-Haven-Industries/fix/sh-131-clear-appt-time
fix(work-orders): persist empty apptTime as null scheduled instants
This commit is contained in:
commit
9665be0ae5
7 changed files with 204 additions and 7 deletions
|
|
@ -53,5 +53,8 @@ namespace SeaHaven.Services.Helpers
|
|||
|
||||
return date.Value.Date.Add(time.Value);
|
||||
}
|
||||
|
||||
public static DateTime? ToScheduledInstant(DateTime? date, TimeSpan? time)
|
||||
=> time.HasValue ? CombineDateAndTime(date, time) : null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ namespace SeaHaven.Services.Helpers
|
|||
var oldStart = FormatDateTime(workOrder.ScheduledStart);
|
||||
var oldEnd = FormatDateTime(workOrder.ScheduledEnd);
|
||||
|
||||
workOrder.ScheduledStart = WorkOrderBoardApptTimeParser.CombineDateAndTime(apptDate, start);
|
||||
workOrder.ScheduledEnd = WorkOrderBoardApptTimeParser.CombineDateAndTime(apptDate, end);
|
||||
workOrder.ScheduledStart = WorkOrderBoardApptTimeParser.ToScheduledInstant(apptDate, start);
|
||||
workOrder.ScheduledEnd = WorkOrderBoardApptTimeParser.ToScheduledInstant(apptDate, end);
|
||||
|
||||
var newStart = FormatDateTime(workOrder.ScheduledStart);
|
||||
var newEnd = FormatDateTime(workOrder.ScheduledEnd);
|
||||
|
|
|
|||
|
|
@ -85,8 +85,10 @@ namespace SeaHaven.Services.Implementation
|
|||
public static WorkOrderBoardRowDto MapRawRow(WorkOrderBoardRawRow row, DateTime utcNow)
|
||||
{
|
||||
// Appt column: date prefers dispatch appointment (vendor slot), then WO ScheduledDate.
|
||||
// Time prefers WO ScheduledStart/End (dispatcher window), then dispatch datetime.
|
||||
var apptStart = row.ScheduledStart ?? row.DispatchApptDate;
|
||||
// Time uses WO ScheduledStart/End only. Do not fall back to a date-only dispatch
|
||||
// instant (midnight), or clearing apptTime still renders 00:00.
|
||||
var apptStart = row.ScheduledStart
|
||||
?? (HasClockTime(row.DispatchApptDate) ? row.DispatchApptDate : null);
|
||||
var apptEnd = row.ScheduledEnd;
|
||||
var (vendorId, vendorName) = WorkOrderBoardDispatchAssignment.LiveVendor(
|
||||
row.VendorId,
|
||||
|
|
@ -145,6 +147,9 @@ namespace SeaHaven.Services.Implementation
|
|||
};
|
||||
}
|
||||
|
||||
private static bool HasClockTime(DateTime? value)
|
||||
=> value.HasValue && value.Value.TimeOfDay != TimeSpan.Zero;
|
||||
|
||||
private static string FormatName(string? firstName, string? lastName)
|
||||
=> $"{firstName ?? ""} {lastName ?? ""}".Trim();
|
||||
|
||||
|
|
|
|||
|
|
@ -651,8 +651,8 @@ namespace SeaHaven.Services.Implementation
|
|||
var oldEnd = FormatDateTime(workOrder.ScheduledEnd);
|
||||
var dispatchId = ResolveDispatchIdForAudit(dispatch);
|
||||
|
||||
workOrder.ScheduledStart = WorkOrderBoardApptTimeParser.CombineDateAndTime(apptDate, start);
|
||||
workOrder.ScheduledEnd = WorkOrderBoardApptTimeParser.CombineDateAndTime(apptDate, end);
|
||||
workOrder.ScheduledStart = WorkOrderBoardApptTimeParser.ToScheduledInstant(apptDate, start);
|
||||
workOrder.ScheduledEnd = WorkOrderBoardApptTimeParser.ToScheduledInstant(apptDate, end);
|
||||
|
||||
var newStart = FormatDateTime(workOrder.ScheduledStart);
|
||||
var newEnd = FormatDateTime(workOrder.ScheduledEnd);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.Services.Helpers;
|
||||
|
||||
namespace SeaHavenIndustries.Tests;
|
||||
|
||||
public class WorkOrderBoardFieldMutationsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ApplyApptTime_EmptyValue_ClearsStartAndEnd_KeepsDates()
|
||||
{
|
||||
var appointmentDate = new DateTime(2026, 6, 25);
|
||||
var workOrder = new WorkOrder
|
||||
{
|
||||
ScheduledDate = appointmentDate,
|
||||
ScheduledStart = new DateTime(2026, 6, 25, 9, 0, 0),
|
||||
ScheduledEnd = new DateTime(2026, 6, 25, 11, 0, 0)
|
||||
};
|
||||
var dispatch = new Dispatch { Id = 10, ScheduledDate = appointmentDate };
|
||||
|
||||
WorkOrderBoardFieldMutations.ApplyApptTime(workOrder, dispatch, "");
|
||||
|
||||
Assert.Null(workOrder.ScheduledStart);
|
||||
Assert.Null(workOrder.ScheduledEnd);
|
||||
Assert.Equal(appointmentDate, workOrder.ScheduledDate);
|
||||
Assert.Equal(appointmentDate, dispatch.ScheduledDate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyApptTime_StartOnly_LeavesEndNull()
|
||||
{
|
||||
var appointmentDate = new DateTime(2026, 6, 25);
|
||||
var workOrder = new WorkOrder { ScheduledDate = appointmentDate };
|
||||
var dispatch = new Dispatch { Id = 10, ScheduledDate = appointmentDate };
|
||||
|
||||
WorkOrderBoardFieldMutations.ApplyApptTime(workOrder, dispatch, "09:00");
|
||||
|
||||
Assert.Equal(new DateTime(2026, 6, 25, 9, 0, 0), workOrder.ScheduledStart);
|
||||
Assert.Null(workOrder.ScheduledEnd);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyApptTime_Range_SetsStartAndEnd()
|
||||
{
|
||||
var appointmentDate = new DateTime(2026, 6, 25);
|
||||
var workOrder = new WorkOrder { ScheduledDate = appointmentDate };
|
||||
var dispatch = new Dispatch { Id = 10, ScheduledDate = appointmentDate };
|
||||
|
||||
WorkOrderBoardFieldMutations.ApplyApptTime(workOrder, dispatch, "09:00 – 11:00");
|
||||
|
||||
Assert.Equal(new DateTime(2026, 6, 25, 9, 0, 0), workOrder.ScheduledStart);
|
||||
Assert.Equal(new DateTime(2026, 6, 25, 11, 0, 0), workOrder.ScheduledEnd);
|
||||
}
|
||||
}
|
||||
|
|
@ -869,4 +869,93 @@ public class WorkOrderBoardServiceTests
|
|||
null,
|
||||
cts.Token));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapRawRow_DateOnlyDispatch_KeepsApptDate_LeavesApptTimeNull()
|
||||
{
|
||||
var dispatchApptDate = new DateTime(2026, 6, 25);
|
||||
var row = RawRow(
|
||||
scheduledDate: new DateTime(2026, 6, 23),
|
||||
scheduledStart: null,
|
||||
scheduledEnd: null,
|
||||
dispatchApptDate: dispatchApptDate);
|
||||
|
||||
var dto = WorkOrderBoardService.MapRawRow(row, DateTime.UtcNow);
|
||||
|
||||
Assert.Equal(dispatchApptDate, dto.ApptDate);
|
||||
Assert.Null(dto.ApptTime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapRawRow_DispatchWithClock_FallsBackToDispatchTime()
|
||||
{
|
||||
var dispatchApptDate = new DateTime(2026, 6, 25, 9, 0, 0);
|
||||
var row = RawRow(
|
||||
scheduledDate: new DateTime(2026, 6, 23),
|
||||
scheduledStart: null,
|
||||
scheduledEnd: null,
|
||||
dispatchApptDate: dispatchApptDate);
|
||||
|
||||
var dto = WorkOrderBoardService.MapRawRow(row, DateTime.UtcNow);
|
||||
|
||||
Assert.Equal(dispatchApptDate, dto.ApptDate);
|
||||
Assert.Equal("09:00", dto.ApptTime);
|
||||
}
|
||||
|
||||
private static WorkOrderBoardRawRow RawRow(
|
||||
DateTime? scheduledDate,
|
||||
DateTime? scheduledStart,
|
||||
DateTime? scheduledEnd,
|
||||
DateTime? dispatchApptDate)
|
||||
=> new(
|
||||
Id: 1,
|
||||
InternalWONumber: "10000000001",
|
||||
RescheduleCount: 0,
|
||||
CarriedOver: 0,
|
||||
IsAddOn: false,
|
||||
WorkOrderType: null,
|
||||
SiteCode: null,
|
||||
LocationName: null,
|
||||
PocName: null,
|
||||
PocPhone: null,
|
||||
PocNotes: null,
|
||||
LifecycleStatus: LifecycleStatus.Scheduled,
|
||||
LegacyStatus: null,
|
||||
AssignTo: null,
|
||||
DispatcherFirstName: null,
|
||||
DispatcherLastName: null,
|
||||
Initials: null,
|
||||
Color: null,
|
||||
DueDate: null,
|
||||
ScheduledDate: scheduledDate,
|
||||
ScheduledStart: scheduledStart,
|
||||
ScheduledEnd: scheduledEnd,
|
||||
TargetWeek: null,
|
||||
ScheduleWeekOnly: null,
|
||||
VendorId: null,
|
||||
VendorName: null,
|
||||
TechName: null,
|
||||
TechPhone: null,
|
||||
DispatchApptDate: dispatchApptDate,
|
||||
Trade: null,
|
||||
Problem: null,
|
||||
ServiceNotes: null,
|
||||
ExtraServices: null,
|
||||
AdditionalContacts: null,
|
||||
DocStatus: null,
|
||||
CompletedDate: null,
|
||||
FlagColor: null,
|
||||
PrimaryDispatchId: 10,
|
||||
RowVersion: null,
|
||||
DispatchRowVersion: null,
|
||||
PendingUpliftCount: 0,
|
||||
HasUplift: false,
|
||||
PrimaryUpliftStatus: null,
|
||||
PrimaryUpliftAmount: null,
|
||||
PrimaryDispatchStatus: null,
|
||||
AvetaRequired: false,
|
||||
HasAvetaDocument: false,
|
||||
OriginalDate: null,
|
||||
OriginalWeek: null,
|
||||
IsUnscheduled: false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1070,6 +1070,53 @@ public class WorkOrderBoardUpdateServiceTests
|
|||
Assert.Equal(new DateTime(2026, 6, 25, 10, 0, 0), reloaded.ScheduledEnd);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PatchField_ApptTime_EmptyClearsStartAndEnd_KeepsAppointmentDate()
|
||||
{
|
||||
var (context, service) = CreateSut();
|
||||
var appointmentDate = new DateTime(2026, 6, 25);
|
||||
var dispatch = new Dispatch
|
||||
{
|
||||
Id = 10,
|
||||
WorkOrderId = 1,
|
||||
VendorId = 1,
|
||||
ScheduledDate = appointmentDate,
|
||||
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 2 }
|
||||
};
|
||||
var wo = new WorkOrder
|
||||
{
|
||||
Id = 1,
|
||||
LifecycleStatus = LifecycleStatus.Scheduled,
|
||||
PrimaryDispatchId = 10,
|
||||
ScheduledDate = appointmentDate,
|
||||
ScheduledStart = new DateTime(2026, 6, 25, 9, 0, 0),
|
||||
ScheduledEnd = new DateTime(2026, 6, 25, 11, 0, 0),
|
||||
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
|
||||
};
|
||||
context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Vendor A" });
|
||||
context.Dispatches.Add(dispatch);
|
||||
context.workOrders.Add(wo);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var result = await service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
|
||||
{
|
||||
Field = WorkOrderBoardFieldNames.ApptTime,
|
||||
Value = "",
|
||||
WorkOrderVersion = ToVersion(wo),
|
||||
DispatchVersion = ToVersion(dispatch),
|
||||
PrimaryDispatchId = 10
|
||||
}, "actor-1");
|
||||
|
||||
Assert.True(string.IsNullOrEmpty(result.ApptTime));
|
||||
Assert.Equal(appointmentDate, result.ScheduledDate);
|
||||
var reloaded = await context.workOrders.FindAsync(1);
|
||||
Assert.Null(reloaded!.ScheduledStart);
|
||||
Assert.Null(reloaded.ScheduledEnd);
|
||||
Assert.Equal(appointmentDate, reloaded.ScheduledDate);
|
||||
var reloadedDispatch = await context.Dispatches.FindAsync(10);
|
||||
Assert.Equal(appointmentDate, reloadedDispatch!.ScheduledDate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PatchField_ApptTime_DashPrefixedToken_TreatedAsSingleStart()
|
||||
{
|
||||
|
|
@ -1106,7 +1153,7 @@ public class WorkOrderBoardUpdateServiceTests
|
|||
|
||||
var reloaded = await context.workOrders.FindAsync(1);
|
||||
Assert.NotNull(reloaded!.ScheduledStart);
|
||||
Assert.Equal(new DateTime(2026, 6, 25), reloaded.ScheduledEnd);
|
||||
Assert.Null(reloaded.ScheduledEnd);
|
||||
Assert.Equal(new DateTime(2026, 6, 25).Add(TimeSpan.FromDays(30)), reloaded.ScheduledStart);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue