using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.EntityFrameworkCore; namespace SeaHaven.DataServices.Helpers { /// /// The single rule and write for "a work order that becomes Canceled cancels its pending /// uplifts in the same action". Every path that can cancel a work order (board update, /// the cancel endpoint, CRM webhook and reconciliation) asks and /// stages the cancellation into its own unit of work, so the uplift change and its own /// audit row commit, or roll back, with the work order's status change. /// public static class PendingUpliftCancellation { public const string AuditAction = "uplift_cancel"; public const string CancelledStatus = "Withdrawn"; private static readonly string[] PendingStatuses = { "Pending", "ChangesRequested" }; /// True when a lifecycle change moves a work order into Canceled. public static bool Applies(LifecycleStatus? before, LifecycleStatus? after) => before != LifecycleStatus.Canceled && after == LifecycleStatus.Canceled; /// /// Uplift requests that belong to a work order: on a live dispatch that the work /// order owns or is linked to. /// internal static IQueryable ForWorkOrder(ApplicationDbContext context, int workOrderId) => context.DispatchUpliftRequests.Where(u => (u.IsDeleted == null || u.IsDeleted == false) && u.Dispatch != null && (u.Dispatch.IsDeleted == null || u.Dispatch.IsDeleted == false) && ( u.Dispatch.WorkOrderId == workOrderId || u.Dispatch.DispatchWorkOrders!.Any(link => link.WorkOrderId == workOrderId))); /// /// Stages (does not save) the cancellation of every pending uplift on the work order, /// with one audit row per uplift. Returns how many uplifts were cancelled. /// internal static async Task StageAsync( ApplicationDbContext context, int workOrderId, string? actorId, string actorType, DateTime now, CancellationToken cancellationToken) { var pending = await ForWorkOrder(context, workOrderId) .Include(u => u.Dispatch) .Where(u => PendingStatuses.Contains(u.Status)) .ToListAsync(cancellationToken); foreach (var request in pending) { var previous = request.Status; request.Status = CancelledStatus; request.DecidedAt = now; request.DecidedByUserId = actorId; request.LastModificationTime = now; context.WorkOrderAuditLogs.Add(new WorkOrderAuditLog { WorkOrderId = workOrderId, UserId = actorId, FieldName = $"Dispatch {request.Dispatch!.DispatchNumber} Uplift", OldValue = previous, NewValue = CancelledStatus, Action = AuditAction, ActorType = actorType, CreatedAt = now, }); } return pending.Count; } } }