shoc-backend/SeaHaven.Services/Implementation/SyncFieldMergePolicy.cs

225 lines
9.3 KiB
C#
Raw Normal View History

using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using SeaHaven.Services.Constants;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
public class SyncFieldMergePolicy : ISyncFieldMergePolicy
{
private static readonly HashSet<string> ShocOwnedFieldSet = new(StringComparer.OrdinalIgnoreCase)
{
WorkOrderFieldNames.LifecycleStatus,
WorkOrderFieldNames.AssignTo,
WorkOrderFieldNames.ScheduledDate,
WorkOrderFieldNames.ScheduledStart,
WorkOrderFieldNames.ScheduledEnd,
WorkOrderFieldNames.TargetWeek,
WorkOrderFieldNames.ScheduleWeekOnly,
WorkOrderFieldNames.DueDate,
WorkOrderFieldNames.Description,
WorkOrderFieldNames.WorkerOrderTitle,
WorkOrderFieldNames.SiteCode,
WorkOrderFieldNames.Building,
WorkOrderFieldNames.LocationId,
WorkOrderFieldNames.WorkOrderType,
WorkOrderFieldNames.Service,
WorkOrderFieldNames.DocStatus,
WorkOrderFieldNames.Trade,
WorkOrderFieldNames.Problem,
WorkOrderFieldNames.Priority,
WorkOrderFieldNames.InternalWONumber,
WorkOrderFieldNames.Status,
};
private readonly IWorkOrderFieldLockService _fieldLocks;
public SyncFieldMergePolicy(IWorkOrderFieldLockService fieldLocks)
{
_fieldLocks = fieldLocks;
}
public IReadOnlySet<string> ShocOwnedFields => ShocOwnedFieldSet;
public async Task<bool> CanSyncOverwriteAsync(int workOrderId, string fieldName)
=> !await _fieldLocks.IsLockedAsync(workOrderId, fieldName);
public async Task<bool> TryApplyAsync(WorkOrderSyncContext context, string fieldName, object? incomingValue)
{
if (incomingValue == null) return false;
if (await context.FieldLocks.IsLockedAsync(context.WorkOrder.Id, fieldName))
{
var current = GetFieldValue(context.WorkOrder, fieldName);
await context.Audit.LogSyncRejectedAsync(
context.WorkOrder.Id,
fieldName,
current,
incomingValue.ToString());
return false;
}
return TryApplyField(context.WorkOrder, fieldName, incomingValue);
}
private static string? GetFieldValue(WorkOrder wo, string fieldName) => fieldName switch
{
WorkOrderFieldNames.Description => wo.Description,
WorkOrderFieldNames.WorkerOrderTitle => wo.WorkerOrderTitle,
WorkOrderFieldNames.Status => wo.Status,
WorkOrderFieldNames.DueDate => wo.DueDate?.ToString("O"),
WorkOrderFieldNames.ScheduledDate => wo.ScheduledDate?.ToString("O"),
WorkOrderFieldNames.ScheduledStart => wo.ScheduledStart?.ToString("O"),
WorkOrderFieldNames.ScheduledEnd => wo.ScheduledEnd?.ToString("O"),
WorkOrderFieldNames.TargetWeek => wo.TargetWeek?.ToString("O"),
WorkOrderFieldNames.ScheduleWeekOnly => wo.ScheduleWeekOnly?.ToString(),
WorkOrderFieldNames.SiteCode => wo.SiteCode,
WorkOrderFieldNames.Building => wo.Building,
WorkOrderFieldNames.Priority => wo.Priority,
WorkOrderFieldNames.AssignTo => wo.AssignTo,
WorkOrderFieldNames.LocationId => wo.LocationId?.ToString(),
WorkOrderFieldNames.LifecycleStatus => wo.LifecycleStatus?.ToString(),
WorkOrderFieldNames.WorkOrderType => wo.WorkOrderType?.ToString(),
WorkOrderFieldNames.Service => wo.Service,
WorkOrderFieldNames.DocStatus => wo.DocStatus?.ToString(),
WorkOrderFieldNames.Trade => wo.Trade,
WorkOrderFieldNames.Problem => wo.Problem,
WorkOrderFieldNames.InternalWONumber => wo.InternalWONumber,
_ => null
};
private static bool TryApplyField(WorkOrder wo, string fieldName, object incomingValue)
{
switch (fieldName)
{
case WorkOrderFieldNames.Description:
wo.Description = incomingValue.ToString();
return true;
case WorkOrderFieldNames.WorkerOrderTitle:
wo.WorkerOrderTitle = incomingValue.ToString();
return true;
case WorkOrderFieldNames.Status:
wo.Status = incomingValue.ToString();
return true;
case WorkOrderFieldNames.DueDate when TryParseDateTime(incomingValue, out var dueDate):
wo.DueDate = dueDate;
return true;
case WorkOrderFieldNames.ScheduledDate when TryParseDateTime(incomingValue, out var scheduledDate):
wo.ScheduledDate = scheduledDate;
return true;
case WorkOrderFieldNames.ScheduledStart when TryParseDateTime(incomingValue, out var scheduledStart):
wo.ScheduledStart = scheduledStart;
return true;
case WorkOrderFieldNames.ScheduledEnd when TryParseDateTime(incomingValue, out var scheduledEnd):
wo.ScheduledEnd = scheduledEnd;
return true;
case WorkOrderFieldNames.TargetWeek when TryParseDateOnly(incomingValue, out var targetWeek):
wo.TargetWeek = targetWeek;
return true;
case WorkOrderFieldNames.ScheduleWeekOnly when TryParseBool(incomingValue, out var scheduleWeekOnly):
wo.ScheduleWeekOnly = scheduleWeekOnly;
return true;
case WorkOrderFieldNames.SiteCode:
wo.SiteCode = incomingValue.ToString();
return true;
case WorkOrderFieldNames.Building:
wo.Building = incomingValue.ToString();
return true;
case WorkOrderFieldNames.Priority:
wo.Priority = incomingValue.ToString();
return true;
case WorkOrderFieldNames.AssignTo:
wo.AssignTo = incomingValue.ToString();
return true;
case WorkOrderFieldNames.LocationId when TryParseInt(incomingValue, out var locationId):
wo.LocationId = locationId;
return true;
case WorkOrderFieldNames.LifecycleStatus when TryParseEnum(incomingValue, out LifecycleStatus lifecycleStatus):
wo.LifecycleStatus = lifecycleStatus;
return true;
case WorkOrderFieldNames.WorkOrderType when TryParseEnum(incomingValue, out WorkOrderType workOrderType):
wo.WorkOrderType = workOrderType;
return true;
case WorkOrderFieldNames.Service:
wo.Service = incomingValue.ToString();
return true;
case WorkOrderFieldNames.DocStatus when TryParseEnum(incomingValue, out DocStatus docStatus):
wo.DocStatus = docStatus;
return true;
case WorkOrderFieldNames.Trade:
wo.Trade = incomingValue.ToString();
return true;
case WorkOrderFieldNames.Problem:
wo.Problem = incomingValue.ToString();
return true;
case WorkOrderFieldNames.InternalWONumber:
wo.InternalWONumber = incomingValue.ToString();
return true;
default:
return false;
}
}
private static bool TryParseDateTime(object value, out DateTime result)
{
if (value is DateTime dt)
{
result = dt;
return true;
}
return DateTime.TryParse(value.ToString(), out result);
}
private static bool TryParseDateOnly(object value, out DateOnly result)
{
if (value is DateOnly dateOnly)
{
result = dateOnly;
return true;
}
if (value is DateTime dt)
{
result = DateOnly.FromDateTime(dt);
return true;
}
return DateOnly.TryParse(value.ToString(), out result);
}
private static bool TryParseBool(object value, out bool result)
{
if (value is bool b)
{
result = b;
return true;
}
return bool.TryParse(value.ToString(), out result);
}
private static bool TryParseInt(object value, out int result)
{
if (value is int i)
{
result = i;
return true;
}
return int.TryParse(value.ToString(), out result);
}
private static bool TryParseEnum<TEnum>(object value, out TEnum result) where TEnum : struct, Enum
{
if (value is TEnum enumValue)
{
result = enumValue;
return true;
}
return Enum.TryParse(value.ToString(), true, out result);
}
}
}