mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 08:23:12 +00:00
510 lines
23 KiB
C#
510 lines
23 KiB
C#
using Data.SeaHavenIndustries;
|
|
using Data.SeaHavenIndustries.Enums;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using SeaHaven.DataServices.Interfaces;
|
|
using SeaHaven.Services.DTOs;
|
|
using SeaHaven.Services.Exceptions;
|
|
using SeaHaven.Services.Helpers;
|
|
using SeaHaven.Services.Interfaces;
|
|
|
|
namespace SeaHaven.Services.Implementation
|
|
{
|
|
public class WorkOrderBoardUpdateService : IWorkOrderBoardUpdateService
|
|
{
|
|
private readonly ApplicationDbContext _context;
|
|
private readonly IWorkOrderBoardDataService _boardDataService;
|
|
private readonly IWorkOrderAuditService _auditService;
|
|
|
|
public WorkOrderBoardUpdateService(
|
|
ApplicationDbContext context,
|
|
IWorkOrderBoardDataService boardDataService,
|
|
IWorkOrderAuditService auditService)
|
|
{
|
|
_context = context;
|
|
_boardDataService = boardDataService;
|
|
_auditService = auditService;
|
|
}
|
|
|
|
public async Task<WorkOrderBoardRowDto> PatchFieldAsync(
|
|
int workOrderId,
|
|
WorkOrderBoardPatchRequestDto request,
|
|
string? actorId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(request.Field))
|
|
throw new WorkOrderBoardValidationException("InvalidField", "Field is required.");
|
|
|
|
var field = request.Field.Trim();
|
|
if (!WorkOrderBoardFieldNames.IsEditable(field))
|
|
throw new WorkOrderBoardValidationException("InvalidField", $"Field '{field}' is not editable.");
|
|
|
|
var workOrderVersion = ParseRowVersion(request.WorkOrderVersion, "workOrderVersion");
|
|
if (workOrderVersion == null)
|
|
throw new WorkOrderBoardValidationException("WorkOrderVersionRequired", "workOrderVersion is required.");
|
|
|
|
var dispatchVersion = ParseRowVersion(request.DispatchVersion, "dispatchVersion");
|
|
|
|
var workOrder = await _context.workOrders
|
|
.FirstOrDefaultAsync(w => w.Id == workOrderId && w.istemplate != true && (w.IsDeleted != true || w.IsDeleted == null));
|
|
|
|
if (workOrder == null)
|
|
throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
|
|
|
|
if (WorkOrderBoardMutationRules.IsReadOnly(workOrder.LifecycleStatus))
|
|
throw new WorkOrderBoardValidationException("CanceledReadOnly", "Work order is read-only in its current status.");
|
|
|
|
var isPastDue = WorkOrderDerivedFields.IsPastDue(workOrder.ScheduledDate, workOrder.LifecycleStatus);
|
|
if (WorkOrderBoardMutationRules.ShouldBlockStatusChangeWhenPastDue(field, isPastDue))
|
|
throw new WorkOrderBoardValidationException("PastDueStatusBlocked", "Status cannot be changed while work order is past due. Reschedule first.");
|
|
|
|
SetOriginalRowVersion(_context.Entry(workOrder).Property(w => w.RowVersion), workOrderVersion);
|
|
|
|
Dispatch? dispatch = null;
|
|
if (WorkOrderBoardFieldNames.IsDispatchField(field))
|
|
{
|
|
var vendorIdHint = field.Equals(WorkOrderBoardFieldNames.VendorId, StringComparison.OrdinalIgnoreCase)
|
|
? ParseVendorIdHint(request.Value)
|
|
: null;
|
|
|
|
dispatch = await ResolveDispatchAsync(workOrder, request.PrimaryDispatchId, vendorIdHint);
|
|
|
|
if (dispatchVersion != null)
|
|
SetOriginalRowVersion(_context.Entry(dispatch).Property(d => d.RowVersion), dispatchVersion);
|
|
else if (dispatch.RowVersion != null)
|
|
throw new WorkOrderBoardValidationException("DispatchVersionRequired", "dispatchVersion is required for dispatch fields.");
|
|
}
|
|
|
|
var auditField = WorkOrderBoardFieldNames.ToAuditFieldName(field);
|
|
var changes = ApplyFieldMutation(field, workOrder, dispatch, request.Value, auditField);
|
|
changes = changes.Where(c => c.Changed).ToList();
|
|
|
|
if (changes.Count == 0)
|
|
{
|
|
var unchanged = await LoadBoardRowAsync(workOrderId);
|
|
return unchanged ?? throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
|
|
}
|
|
|
|
foreach (var change in changes)
|
|
{
|
|
switch (change.Action)
|
|
{
|
|
case AuditActionType.StatusChanged:
|
|
_auditService.StageStatusChanged(workOrderId, change.OldValue, change.NewValue, actorId);
|
|
break;
|
|
case AuditActionType.AssignmentChanged:
|
|
_auditService.StageAssignmentChanged(workOrderId, change.OldValue, change.NewValue, actorId);
|
|
break;
|
|
default:
|
|
_auditService.StageFieldChanged(workOrderId, change.FieldName, change.OldValue, change.NewValue, actorId, change.DispatchId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
await _context.SaveChangesAsync();
|
|
}
|
|
catch (DbUpdateConcurrencyException)
|
|
{
|
|
_context.ChangeTracker.Clear();
|
|
var currentState = await LoadBoardRowAsync(workOrderId);
|
|
throw new WorkOrderBoardConcurrencyException(currentState);
|
|
}
|
|
|
|
var row = await LoadBoardRowAsync(workOrderId);
|
|
return row ?? throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
|
|
}
|
|
|
|
private List<FieldChange> ApplyFieldMutation(
|
|
string field,
|
|
WorkOrder workOrder,
|
|
Dispatch? dispatch,
|
|
string? value,
|
|
string auditField)
|
|
{
|
|
return field.ToLowerInvariant() switch
|
|
{
|
|
WorkOrderBoardFieldNames.WoNumber => new List<FieldChange> { ApplyWoNumber(workOrder, value, auditField) },
|
|
WorkOrderBoardFieldNames.WorkOrderType => new List<FieldChange> { ApplyWorkOrderType(workOrder, value, auditField) },
|
|
WorkOrderBoardFieldNames.SiteCode => new List<FieldChange> { ApplyStringField(value, auditField, v => workOrder.SiteCode = v, () => workOrder.SiteCode) },
|
|
WorkOrderBoardFieldNames.LifecycleStatus => new List<FieldChange> { ApplyLifecycleStatus(workOrder, value, auditField) },
|
|
WorkOrderBoardFieldNames.AssignTo => ApplyAssignTo(workOrder, value, auditField),
|
|
WorkOrderBoardFieldNames.DueDate => new List<FieldChange> { ApplyDateField(value, auditField, v => workOrder.DueDate = v, () => workOrder.DueDate) },
|
|
WorkOrderBoardFieldNames.ScheduledDate => ApplyScheduledDate(workOrder, value, auditField),
|
|
WorkOrderBoardFieldNames.TargetWeek => new List<FieldChange> { ApplyTargetWeek(workOrder, value, auditField) },
|
|
WorkOrderBoardFieldNames.ScheduleWeekOnly => new List<FieldChange> { ApplyBoolField(value, auditField, v => workOrder.ScheduleWeekOnly = v, () => workOrder.ScheduleWeekOnly) },
|
|
WorkOrderBoardFieldNames.VendorId => new List<FieldChange> { ApplyVendorId(dispatch!, value, auditField) },
|
|
WorkOrderBoardFieldNames.ApptDate => new List<FieldChange> { ApplyApptDate(dispatch!, value, auditField) },
|
|
WorkOrderBoardFieldNames.ApptTime => new List<FieldChange> { ApplyApptTime(workOrder, dispatch!, value, auditField) },
|
|
WorkOrderBoardFieldNames.DocStatus => new List<FieldChange> { ApplyDocStatus(workOrder, value, auditField) },
|
|
_ => throw new WorkOrderBoardValidationException("InvalidField", $"Field '{field}' is not editable.")
|
|
};
|
|
}
|
|
|
|
private async Task<Dispatch> ResolveDispatchAsync(WorkOrder workOrder, int? primaryDispatchId, int? vendorIdForCreate)
|
|
{
|
|
if (primaryDispatchId.HasValue)
|
|
{
|
|
var dispatch = await _context.Dispatches
|
|
.FirstOrDefaultAsync(d => d.Id == primaryDispatchId.Value && d.WorkOrderId == workOrder.Id);
|
|
|
|
if (dispatch == null)
|
|
throw new WorkOrderBoardValidationException("InvalidDispatch", "Primary dispatch does not belong to this work order.");
|
|
|
|
workOrder.PrimaryDispatchId = dispatch.Id;
|
|
return dispatch;
|
|
}
|
|
|
|
if (workOrder.PrimaryDispatchId.HasValue)
|
|
{
|
|
var existing = await _context.Dispatches
|
|
.FirstOrDefaultAsync(d => d.Id == workOrder.PrimaryDispatchId.Value);
|
|
|
|
if (existing != null)
|
|
return existing;
|
|
}
|
|
|
|
if (!vendorIdForCreate.HasValue || vendorIdForCreate.Value <= 0)
|
|
throw new WorkOrderBoardValidationException("DispatchRequired", "A primary dispatch is required. Set vendorId first or provide primaryDispatchId.");
|
|
|
|
var created = new Dispatch
|
|
{
|
|
WorkOrderId = workOrder.Id,
|
|
VendorId = vendorIdForCreate.Value,
|
|
Status = "Pending",
|
|
CreatedDate = DateTime.UtcNow
|
|
};
|
|
_context.Dispatches.Add(created);
|
|
await _context.SaveChangesAsync();
|
|
workOrder.PrimaryDispatchId = created.Id;
|
|
return created;
|
|
}
|
|
|
|
private async Task<FieldChange> ApplyWoNumber(WorkOrder workOrder, string? value, string auditField)
|
|
{
|
|
if (!WorkOrderNumberNormalizer.TryNormalize(value, out var normalized, out var error))
|
|
throw new WorkOrderBoardValidationException("InvalidWoNumber", error ?? "Invalid WO number.");
|
|
|
|
if (await _boardDataService.InternalWoNumberExistsAsync(normalized, workOrder.Id))
|
|
throw new WorkOrderBoardValidationException("DuplicateWoNumber", "WO number already exists.");
|
|
|
|
var old = workOrder.InternalWONumber;
|
|
if (old == normalized)
|
|
return FieldChange.Unchanged(auditField);
|
|
|
|
workOrder.InternalWONumber = normalized;
|
|
workOrder.WorkerOrderNumber = normalized;
|
|
return FieldChange.Changed(auditField, old, normalized);
|
|
}
|
|
|
|
private static FieldChange ApplyWorkOrderType(WorkOrder workOrder, string? value, string auditField)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", "workOrderType value is required.");
|
|
|
|
if (!Enum.TryParse<WorkOrderType>(value, true, out var parsed))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid workOrderType: {value}");
|
|
|
|
var old = workOrder.WorkOrderType?.ToString();
|
|
if (workOrder.WorkOrderType == parsed)
|
|
return FieldChange.Unchanged(auditField);
|
|
|
|
workOrder.WorkOrderType = parsed;
|
|
return FieldChange.Changed(auditField, old, parsed.ToString());
|
|
}
|
|
|
|
private static FieldChange ApplyStringField(
|
|
string? value,
|
|
string auditField,
|
|
Action<string?> setter,
|
|
Func<string?> getter)
|
|
{
|
|
var normalized = string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
|
var old = getter();
|
|
if (old == normalized)
|
|
return FieldChange.Unchanged(auditField);
|
|
|
|
setter(normalized);
|
|
return FieldChange.Changed(auditField, old, normalized);
|
|
}
|
|
|
|
private static FieldChange ApplyLifecycleStatus(WorkOrder workOrder, string? value, string auditField)
|
|
{
|
|
var parsed = LifecycleStatusMapper.ParseLifecycleStatus(value);
|
|
if (parsed == null)
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid lifecycleStatus: {value}");
|
|
|
|
var old = workOrder.LifecycleStatus?.ToString() ?? workOrder.Status;
|
|
if (workOrder.LifecycleStatus == parsed)
|
|
return FieldChange.Unchanged(auditField);
|
|
|
|
workOrder.LifecycleStatus = parsed;
|
|
workOrder.Status = WorkOrderDerivedFields.GetLifecycleStatusLabel(parsed);
|
|
if (workOrder.LegacyStatus == null && workOrder.Status != null)
|
|
workOrder.LegacyStatus = workOrder.Status;
|
|
|
|
return FieldChange.StatusChanged(auditField, old, parsed.ToString());
|
|
}
|
|
|
|
private static List<FieldChange> ApplyAssignTo(WorkOrder workOrder, string? value, string auditField)
|
|
{
|
|
var normalized = string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
|
var old = workOrder.AssignTo;
|
|
var changes = new List<FieldChange>();
|
|
|
|
if (old != normalized)
|
|
{
|
|
workOrder.AssignTo = normalized;
|
|
changes.Add(FieldChange.AssignmentChanged(auditField, old, normalized));
|
|
}
|
|
|
|
changes.AddRange(ApplyAutoScheduleSideEffects(workOrder));
|
|
return changes;
|
|
}
|
|
|
|
private static FieldChange ApplyDateField(
|
|
string? value,
|
|
string auditField,
|
|
Action<DateTime?> setter,
|
|
Func<DateTime?> getter)
|
|
{
|
|
DateTime? parsed = null;
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
{
|
|
if (!DateTime.TryParse(value, out var dt))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid date for {auditField}: {value}");
|
|
parsed = dt.Date;
|
|
}
|
|
|
|
var old = FormatDate(getter());
|
|
var newVal = FormatDate(parsed);
|
|
if (old == newVal)
|
|
return FieldChange.Unchanged(auditField);
|
|
|
|
setter(parsed);
|
|
return FieldChange.Changed(auditField, old, newVal);
|
|
}
|
|
|
|
private static List<FieldChange> ApplyScheduledDate(WorkOrder workOrder, string? value, string auditField)
|
|
{
|
|
var previousDate = workOrder.ScheduledDate;
|
|
DateTime? parsed = null;
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
{
|
|
if (!DateTime.TryParse(value, out var dt))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid scheduledDate: {value}");
|
|
parsed = dt.Date;
|
|
}
|
|
|
|
var changes = new List<FieldChange>();
|
|
var oldFormatted = FormatDate(previousDate);
|
|
var newFormatted = FormatDate(parsed);
|
|
|
|
if (oldFormatted != newFormatted)
|
|
{
|
|
workOrder.ScheduledDate = parsed;
|
|
changes.Add(FieldChange.Changed(auditField, oldFormatted, newFormatted));
|
|
|
|
if (!workOrder.OriginalDate.HasValue && parsed.HasValue)
|
|
{
|
|
workOrder.OriginalDate = DateOnly.FromDateTime(parsed.Value);
|
|
var originalWeek = WorkOrderBoardMutationRules.ComputeOriginalWeek(parsed);
|
|
if (originalWeek.HasValue)
|
|
workOrder.OriginalWeek = originalWeek;
|
|
}
|
|
|
|
if (WorkOrderBoardMutationRules.IsReschedule(previousDate, parsed))
|
|
{
|
|
var oldCount = workOrder.RescheduleCount.ToString();
|
|
workOrder.RescheduleCount++;
|
|
changes.Add(FieldChange.Changed(WorkOrderBoardFieldNames.RescheduleCount, oldCount, workOrder.RescheduleCount.ToString()));
|
|
}
|
|
}
|
|
|
|
changes.AddRange(ApplyAutoScheduleSideEffects(workOrder));
|
|
return changes;
|
|
}
|
|
|
|
private static FieldChange ApplyTargetWeek(WorkOrder workOrder, string? value, string auditField)
|
|
{
|
|
DateOnly? parsed = null;
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
{
|
|
if (!DateOnly.TryParse(value, out var d))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid targetWeek: {value}");
|
|
parsed = d;
|
|
}
|
|
|
|
var old = workOrder.TargetWeek?.ToString("yyyy-MM-dd");
|
|
var newVal = parsed?.ToString("yyyy-MM-dd");
|
|
if (old == newVal)
|
|
return FieldChange.Unchanged(auditField);
|
|
|
|
workOrder.TargetWeek = parsed;
|
|
return FieldChange.Changed(auditField, old, newVal);
|
|
}
|
|
|
|
private static FieldChange ApplyBoolField(
|
|
string? value,
|
|
string auditField,
|
|
Action<bool?> setter,
|
|
Func<bool?> getter)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"{auditField} value is required.");
|
|
|
|
if (!bool.TryParse(value, out var parsed))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid boolean for {auditField}: {value}");
|
|
|
|
var old = getter()?.ToString();
|
|
if (getter() == parsed)
|
|
return FieldChange.Unchanged(auditField);
|
|
|
|
setter(parsed);
|
|
return FieldChange.Changed(auditField, old, parsed.ToString());
|
|
}
|
|
|
|
private static FieldChange ApplyVendorId(Dispatch dispatch, string? value, string auditField)
|
|
{
|
|
var vendorId = ParseVendorIdHint(value);
|
|
if (!vendorId.HasValue)
|
|
throw new WorkOrderBoardValidationException("InvalidValue", "vendorId must be a positive integer.");
|
|
|
|
var old = dispatch.VendorId.ToString();
|
|
if (dispatch.VendorId == vendorId.Value)
|
|
return FieldChange.Unchanged(auditField, dispatch.Id);
|
|
|
|
dispatch.VendorId = vendorId.Value;
|
|
return FieldChange.Changed(auditField, old, vendorId.Value.ToString(), dispatch.Id);
|
|
}
|
|
|
|
private static FieldChange ApplyApptDate(Dispatch dispatch, string? value, string auditField)
|
|
{
|
|
DateTime? parsed = null;
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
{
|
|
if (!DateTime.TryParse(value, out var dt))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid apptDate: {value}");
|
|
parsed = dt.Date;
|
|
}
|
|
|
|
var old = FormatDate(dispatch.ScheduledDate);
|
|
var newVal = FormatDate(parsed);
|
|
if (old == newVal)
|
|
return FieldChange.Unchanged(auditField, dispatch.Id);
|
|
|
|
dispatch.ScheduledDate = parsed;
|
|
return FieldChange.Changed(auditField, old, newVal, dispatch.Id);
|
|
}
|
|
|
|
private static FieldChange ApplyApptTime(WorkOrder workOrder, Dispatch dispatch, string? value, string auditField)
|
|
{
|
|
if (!WorkOrderBoardApptTimeParser.TryParse(value, out var start, out var end, out var error))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", error ?? "Invalid apptTime.");
|
|
|
|
var apptDate = dispatch.ScheduledDate ?? workOrder.ScheduledDate;
|
|
var oldStart = FormatDateTime(workOrder.ScheduledStart);
|
|
var oldEnd = FormatDateTime(workOrder.ScheduledEnd);
|
|
|
|
workOrder.ScheduledStart = WorkOrderBoardApptTimeParser.CombineDateAndTime(apptDate, start);
|
|
workOrder.ScheduledEnd = WorkOrderBoardApptTimeParser.CombineDateAndTime(apptDate, end);
|
|
|
|
var newStart = FormatDateTime(workOrder.ScheduledStart);
|
|
var newEnd = FormatDateTime(workOrder.ScheduledEnd);
|
|
var oldCombined = FormatApptRange(oldStart, oldEnd);
|
|
var newCombined = FormatApptRange(newStart, newEnd);
|
|
|
|
if (oldCombined == newCombined)
|
|
return FieldChange.Unchanged(auditField, dispatch.Id);
|
|
|
|
return FieldChange.Changed(auditField, oldCombined, newCombined, dispatch.Id);
|
|
}
|
|
|
|
private static FieldChange ApplyDocStatus(WorkOrder workOrder, string? value, string auditField)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", "docStatus value is required.");
|
|
|
|
if (!Enum.TryParse<DocStatus>(value, true, out var parsed))
|
|
throw new WorkOrderBoardValidationException("InvalidValue", $"Invalid docStatus: {value}");
|
|
|
|
var old = workOrder.DocStatus?.ToString();
|
|
if (workOrder.DocStatus == parsed)
|
|
return FieldChange.Unchanged(auditField);
|
|
|
|
workOrder.DocStatus = parsed;
|
|
return FieldChange.Changed(auditField, old, parsed.ToString());
|
|
}
|
|
|
|
private static List<FieldChange> ApplyAutoScheduleSideEffects(WorkOrder workOrder)
|
|
{
|
|
var changes = new List<FieldChange>();
|
|
if (!WorkOrderBoardMutationRules.ShouldAutoSchedule(workOrder.LifecycleStatus, workOrder.ScheduledDate, workOrder.AssignTo))
|
|
return changes;
|
|
|
|
var old = workOrder.LifecycleStatus?.ToString();
|
|
workOrder.LifecycleStatus = LifecycleStatus.Scheduled;
|
|
workOrder.Status = WorkOrderDerivedFields.GetLifecycleStatusLabel(LifecycleStatus.Scheduled);
|
|
changes.Add(FieldChange.StatusChanged("LifecycleStatus", old, LifecycleStatus.Scheduled.ToString()));
|
|
return changes;
|
|
}
|
|
|
|
private static void SetOriginalRowVersion<T>(PropertyEntry<T, byte[]?> property, byte[] version)
|
|
{
|
|
property.OriginalValue = version;
|
|
}
|
|
|
|
private async Task<WorkOrderBoardRowDto?> LoadBoardRowAsync(int workOrderId)
|
|
{
|
|
var raw = await _boardDataService.GetBoardRowByIdAsync(workOrderId);
|
|
return raw == null ? null : WorkOrderBoardService.MapRawRow(raw, DateTime.UtcNow);
|
|
}
|
|
|
|
private static byte[]? ParseRowVersion(string? base64, string fieldName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(base64))
|
|
return null;
|
|
|
|
try
|
|
{
|
|
return Convert.FromBase64String(base64);
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
throw new WorkOrderBoardValidationException("InvalidRowVersion", $"Invalid {fieldName} format.");
|
|
}
|
|
}
|
|
|
|
private static int? ParseVendorIdHint(string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value) || !int.TryParse(value, out var vendorId) || vendorId <= 0)
|
|
return null;
|
|
return vendorId;
|
|
}
|
|
|
|
private static string? FormatDate(DateTime? value) => value?.ToString("yyyy-MM-dd");
|
|
private static string? FormatDateTime(DateTime? value) => value?.ToString("O");
|
|
private static string? FormatApptRange(string? start, string? end)
|
|
=> start == null && end == null ? null : $"{start}|{end}";
|
|
|
|
private sealed record FieldChange(
|
|
string FieldName,
|
|
string? OldValue,
|
|
string? NewValue,
|
|
AuditActionType Action,
|
|
int? DispatchId,
|
|
bool Changed)
|
|
{
|
|
public static FieldChange Unchanged(string fieldName, int? dispatchId = null)
|
|
=> new(fieldName, null, null, AuditActionType.FieldChanged, dispatchId, false);
|
|
|
|
public static FieldChange Changed(string fieldName, string? oldValue, string? newValue, int? dispatchId = null)
|
|
=> new(fieldName, oldValue, newValue, AuditActionType.FieldChanged, dispatchId, true);
|
|
|
|
public static FieldChange StatusChanged(string fieldName, string? oldValue, string? newValue)
|
|
=> new(fieldName, oldValue, newValue, AuditActionType.StatusChanged, null, true);
|
|
|
|
public static FieldChange AssignmentChanged(string fieldName, string? oldValue, string? newValue)
|
|
=> new(fieldName, oldValue, newValue, AuditActionType.AssignmentChanged, null, true);
|
|
}
|
|
}
|
|
}
|