mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-03 10:23:28 +00:00
fix(work-orders): restore NTE on auto-approved revoke and serialize cancel
This commit is contained in:
parent
aeface594a
commit
5ea5a1d702
5 changed files with 266 additions and 8 deletions
|
|
@ -112,4 +112,39 @@ public sealed class WorkOrderUpliftControllerTests
|
||||||
|
|
||||||
result.Should().BeOfType<ObjectResult>().Which.StatusCode.Should().Be(StatusCodes.Status403Forbidden);
|
result.Should().BeOfType<ObjectResult>().Which.StatusCode.Should().Be(StatusCodes.Status403Forbidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CancelUplift_TerminalWorkOrder_Returns400()
|
||||||
|
{
|
||||||
|
var service = new Mock<IWorkOrderUpliftService>();
|
||||||
|
service.Setup(x => x.CancelAsync(7, 99, It.IsAny<ClaimsPrincipal>(), It.IsAny<CancellationToken>()))
|
||||||
|
.ThrowsAsync(new InvalidOperationException("Cannot change uplifts on a 'Completed' work order"));
|
||||||
|
|
||||||
|
var controller = NewController(service);
|
||||||
|
var result = await controller.CancelUplift(7, 99, CancellationToken.None);
|
||||||
|
|
||||||
|
result.Should().BeOfType<BadRequestObjectResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RevokeUplift_TerminalWorkOrder_Returns400()
|
||||||
|
{
|
||||||
|
var service = new Mock<IWorkOrderUpliftService>();
|
||||||
|
service.Setup(x => x.RevokeAsync(
|
||||||
|
7,
|
||||||
|
99,
|
||||||
|
It.IsAny<RevokeWorkOrderUpliftRequestDto>(),
|
||||||
|
It.IsAny<ClaimsPrincipal>(),
|
||||||
|
It.IsAny<CancellationToken>()))
|
||||||
|
.ThrowsAsync(new InvalidOperationException("Cannot change uplifts on a 'Canceled' work order"));
|
||||||
|
|
||||||
|
var controller = NewController(service, "Admin");
|
||||||
|
var result = await controller.RevokeUplift(
|
||||||
|
7,
|
||||||
|
99,
|
||||||
|
new RevokeWorkOrderUpliftRequestDto { Reason = "Completed" },
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
result.Should().BeOfType<BadRequestObjectResult>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,20 @@ namespace SeaHaven.Services.Implementation
|
||||||
private readonly IWorkOrderBoardService _boardService;
|
private readonly IWorkOrderBoardService _boardService;
|
||||||
private readonly IWorkOrderAuditService _auditService;
|
private readonly IWorkOrderAuditService _auditService;
|
||||||
private readonly IWorkOrderUpliftService _upliftService;
|
private readonly IWorkOrderUpliftService _upliftService;
|
||||||
|
private readonly IUpliftDataService _upliftData;
|
||||||
|
|
||||||
public WorkOrderBoardCancelService(
|
public WorkOrderBoardCancelService(
|
||||||
IWorkOrderBoardMutationDataService mutationData,
|
IWorkOrderBoardMutationDataService mutationData,
|
||||||
IWorkOrderBoardService boardService,
|
IWorkOrderBoardService boardService,
|
||||||
IWorkOrderAuditService auditService,
|
IWorkOrderAuditService auditService,
|
||||||
IWorkOrderUpliftService upliftService)
|
IWorkOrderUpliftService upliftService,
|
||||||
|
IUpliftDataService upliftData)
|
||||||
{
|
{
|
||||||
_mutationData = mutationData;
|
_mutationData = mutationData;
|
||||||
_boardService = boardService;
|
_boardService = boardService;
|
||||||
_auditService = auditService;
|
_auditService = auditService;
|
||||||
_upliftService = upliftService;
|
_upliftService = upliftService;
|
||||||
|
_upliftData = upliftData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<WorkOrderBoardRowDto> CancelAsync(
|
public async Task<WorkOrderBoardRowDto> CancelAsync(
|
||||||
|
|
@ -32,7 +35,19 @@ namespace SeaHaven.Services.Implementation
|
||||||
ClaimsPrincipal user,
|
ClaimsPrincipal user,
|
||||||
string? actorId)
|
string? actorId)
|
||||||
{
|
{
|
||||||
var workOrder = await _mutationData.GetTrackedWorkOrderAsync(workOrderId, CancellationToken.None);
|
return await _upliftData.ExecuteWorkOrderMutationAsync(
|
||||||
|
workOrderId,
|
||||||
|
ct => CancelLockedAsync(workOrderId, user, actorId, ct),
|
||||||
|
CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<WorkOrderBoardRowDto> CancelLockedAsync(
|
||||||
|
int workOrderId,
|
||||||
|
ClaimsPrincipal user,
|
||||||
|
string? actorId,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var workOrder = await _mutationData.GetTrackedWorkOrderAsync(workOrderId, cancellationToken);
|
||||||
|
|
||||||
if (workOrder == null)
|
if (workOrder == null)
|
||||||
throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
|
throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
|
||||||
|
|
@ -52,9 +67,9 @@ namespace SeaHaven.Services.Implementation
|
||||||
if (workOrder.LegacyStatus == null && workOrder.Status != null)
|
if (workOrder.LegacyStatus == null && workOrder.Status != null)
|
||||||
workOrder.LegacyStatus = workOrder.Status;
|
workOrder.LegacyStatus = workOrder.Status;
|
||||||
|
|
||||||
await _upliftService.WithdrawPendingForWorkOrderAsync(workOrderId, actorId, CancellationToken.None);
|
await _upliftService.WithdrawPendingForWorkOrderAsync(workOrderId, actorId, cancellationToken);
|
||||||
await _auditService.StageStatusChangedAsync(workOrderId, oldStatus, LifecycleStatus.Canceled.ToString(), actorId);
|
await _auditService.StageStatusChangedAsync(workOrderId, oldStatus, LifecycleStatus.Canceled.ToString(), actorId);
|
||||||
await _mutationData.SaveAsync(CancellationToken.None);
|
await _mutationData.SaveAsync(cancellationToken);
|
||||||
|
|
||||||
var row = await _boardService.GetBoardRowAsync(workOrderId, user);
|
var row = await _boardService.GetBoardRowAsync(workOrderId, user);
|
||||||
return row ?? throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
|
return row ?? throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Data.SeaHavenIndustries;
|
using Data.SeaHavenIndustries;
|
||||||
|
using Data.SeaHavenIndustries.Enums;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using SeaHaven.DataServices.Interfaces;
|
using SeaHaven.DataServices.Interfaces;
|
||||||
using SeaHaven.Services.Configuration;
|
using SeaHaven.Services.Configuration;
|
||||||
|
|
@ -97,6 +98,12 @@ namespace SeaHaven.Services.Implementation
|
||||||
if (workOrder?.PrimaryDispatchId is not int dispatchId)
|
if (workOrder?.PrimaryDispatchId is not int dispatchId)
|
||||||
throw new InvalidOperationException("Work order has no primary dispatch for uplift requests");
|
throw new InvalidOperationException("Work order has no primary dispatch for uplift requests");
|
||||||
|
|
||||||
|
if (workOrder.LifecycleStatus is LifecycleStatus.Completed or LifecycleStatus.Canceled)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Cannot change uplifts on a '{workOrder.LifecycleStatus}' work order");
|
||||||
|
}
|
||||||
|
|
||||||
var dispatch = await _dispatchData.GetByIdAsync(dispatchId);
|
var dispatch = await _dispatchData.GetByIdAsync(dispatchId);
|
||||||
if (dispatch == null)
|
if (dispatch == null)
|
||||||
throw new KeyNotFoundException("Dispatch not found");
|
throw new KeyNotFoundException("Dispatch not found");
|
||||||
|
|
@ -159,6 +166,8 @@ namespace SeaHaven.Services.Implementation
|
||||||
if (!await HasWorkOrderAccessAsync(workOrderId, user, cancellationToken))
|
if (!await HasWorkOrderAccessAsync(workOrderId, user, cancellationToken))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
await EnsureWorkOrderAllowsUpliftMutationAsync(workOrderId, user, cancellationToken);
|
||||||
|
|
||||||
var req = await _upliftData.GetByIdAndWorkOrderAsync(upliftId, workOrderId, cancellationToken);
|
var req = await _upliftData.GetByIdAndWorkOrderAsync(upliftId, workOrderId, cancellationToken);
|
||||||
if (req == null)
|
if (req == null)
|
||||||
throw new KeyNotFoundException("Uplift request not found");
|
throw new KeyNotFoundException("Uplift request not found");
|
||||||
|
|
@ -204,6 +213,8 @@ namespace SeaHaven.Services.Implementation
|
||||||
if (!await HasWorkOrderAccessAsync(workOrderId, user, cancellationToken))
|
if (!await HasWorkOrderAccessAsync(workOrderId, user, cancellationToken))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
await EnsureWorkOrderAllowsUpliftMutationAsync(workOrderId, user, cancellationToken);
|
||||||
|
|
||||||
var req = await _upliftData.GetByIdAndWorkOrderAsync(upliftId, workOrderId, cancellationToken);
|
var req = await _upliftData.GetByIdAndWorkOrderAsync(upliftId, workOrderId, cancellationToken);
|
||||||
if (req == null)
|
if (req == null)
|
||||||
throw new KeyNotFoundException("Uplift request not found");
|
throw new KeyNotFoundException("Uplift request not found");
|
||||||
|
|
@ -239,7 +250,7 @@ namespace SeaHaven.Services.Implementation
|
||||||
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
var now = _timeProvider.GetUtcNow().UtcDateTime;
|
||||||
var previous = canonical;
|
var previous = canonical;
|
||||||
|
|
||||||
if (canonical == UpliftStatus.Approved)
|
if (canonical == UpliftStatus.Approved || canonical == UpliftStatus.NoApprovalRequired)
|
||||||
{
|
{
|
||||||
var oldNte = dispatch.NTEAmount ?? 0m;
|
var oldNte = dispatch.NTEAmount ?? 0m;
|
||||||
dispatch.NTEAmount = req.CurrentNTE ?? oldNte;
|
dispatch.NTEAmount = req.CurrentNTE ?? oldNte;
|
||||||
|
|
@ -348,6 +359,26 @@ namespace SeaHaven.Services.Implementation
|
||||||
return WorkOrderUpliftContractMapper.MapItem(created, requesterName, null);
|
return WorkOrderUpliftContractMapper.MapItem(created, requesterName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task EnsureWorkOrderAllowsUpliftMutationAsync(
|
||||||
|
int workOrderId,
|
||||||
|
ClaimsPrincipal user,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var accountFilter = _accountResolver.ResolveAccountFilter(user);
|
||||||
|
var workOrder = await _detailData.GetWorkOrderForMediaAsync(
|
||||||
|
workOrderId,
|
||||||
|
cancellationToken,
|
||||||
|
accountFilter);
|
||||||
|
if (workOrder == null)
|
||||||
|
throw new KeyNotFoundException("Work order not found");
|
||||||
|
|
||||||
|
if (workOrder.LifecycleStatus is LifecycleStatus.Completed or LifecycleStatus.Canceled)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Cannot change uplifts on a '{workOrder.LifecycleStatus}' work order");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<bool> HasWorkOrderAccessAsync(
|
private async Task<bool> HasWorkOrderAccessAsync(
|
||||||
int workOrderId,
|
int workOrderId,
|
||||||
ClaimsPrincipal user,
|
ClaimsPrincipal user,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,12 @@ public class WorkOrderBoardCancelServiceTests
|
||||||
var boardService = new WorkOrderBoardService(boardData, WorkOrderAccountTestHelpers.Resolver(context));
|
var boardService = new WorkOrderBoardService(boardData, WorkOrderAccountTestHelpers.Resolver(context));
|
||||||
var fieldLocks = new WorkOrderFieldLockService(new WorkOrderFieldLockDataService(context));
|
var fieldLocks = new WorkOrderFieldLockService(new WorkOrderFieldLockDataService(context));
|
||||||
var audit = new WorkOrderAuditService(new WorkOrderAuditDataService(context), fieldLocks);
|
var audit = new WorkOrderAuditService(new WorkOrderAuditDataService(context), fieldLocks);
|
||||||
var cancel = new WorkOrderBoardCancelService(mutationData, boardService, audit, new NoOpUpliftService());
|
var cancel = new WorkOrderBoardCancelService(
|
||||||
|
mutationData,
|
||||||
|
boardService,
|
||||||
|
audit,
|
||||||
|
new NoOpUpliftService(),
|
||||||
|
new UpliftDataService(context));
|
||||||
var update = new WorkOrderBoardUpdateService(boardData, mutationData, audit);
|
var update = new WorkOrderBoardUpdateService(boardData, mutationData, audit);
|
||||||
return (context, cancel, update);
|
return (context, cancel, update);
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +210,7 @@ public class WorkOrderBoardCancelServiceTests
|
||||||
new UserDataService(context),
|
new UserDataService(context),
|
||||||
TimeProvider.System,
|
TimeProvider.System,
|
||||||
Options.Create(new ApprovalsOptions()));
|
Options.Create(new ApprovalsOptions()));
|
||||||
var cancel = new WorkOrderBoardCancelService(mutationData, boardService, audit, uplifts);
|
var cancel = new WorkOrderBoardCancelService(mutationData, boardService, audit, uplifts, new UpliftDataService(context));
|
||||||
|
|
||||||
var result = await cancel.CancelAsync(1, WorkOrderAccountTestHelpers.OrgWideAdmin(), "actor-1");
|
var result = await cancel.CancelAsync(1, WorkOrderAccountTestHelpers.OrgWideAdmin(), "actor-1");
|
||||||
|
|
||||||
|
|
@ -270,7 +275,8 @@ public class WorkOrderBoardCancelServiceTests
|
||||||
new ThrowingSaveMutationData(mutationData),
|
new ThrowingSaveMutationData(mutationData),
|
||||||
boardService,
|
boardService,
|
||||||
audit,
|
audit,
|
||||||
uplifts);
|
uplifts,
|
||||||
|
new UpliftDataService(context));
|
||||||
|
|
||||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||||
cancel.CancelAsync(1, WorkOrderAccountTestHelpers.OrgWideAdmin(), "actor-1"));
|
cancel.CancelAsync(1, WorkOrderAccountTestHelpers.OrgWideAdmin(), "actor-1"));
|
||||||
|
|
@ -281,6 +287,92 @@ public class WorkOrderBoardCancelServiceTests
|
||||||
Assert.Empty(verify.WorkOrderAuditLogs);
|
Assert.Empty(verify.WorkOrderAuditLogs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Cancel_SerializedWithCreate_DoesNotLeavePendingOnCanceledWorkOrder()
|
||||||
|
{
|
||||||
|
var databaseName = Guid.NewGuid().ToString();
|
||||||
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||||
|
.UseInMemoryDatabase(databaseName)
|
||||||
|
.Options;
|
||||||
|
|
||||||
|
await using (var seed = new ApplicationDbContext(options))
|
||||||
|
{
|
||||||
|
await WorkOrderAccountTestHelpers.EnsureAccountAsync(seed);
|
||||||
|
seed.Users.Add(new ApplicationUser
|
||||||
|
{
|
||||||
|
Id = "dispatcher-1",
|
||||||
|
UserName = "dispatcher-1",
|
||||||
|
FirstName = "Alex",
|
||||||
|
LastName = "Dispatcher",
|
||||||
|
});
|
||||||
|
seed.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC" });
|
||||||
|
seed.Dispatches.Add(new Dispatch
|
||||||
|
{
|
||||||
|
Id = 10,
|
||||||
|
VendorId = 1,
|
||||||
|
WorkOrderId = 1,
|
||||||
|
NTEAmount = 1000m,
|
||||||
|
DispatchNumber = "DIS-10",
|
||||||
|
Status = "Scheduled",
|
||||||
|
});
|
||||||
|
seed.workOrders.Add(new WorkOrder
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
InternalWONumber = "10000000001",
|
||||||
|
PrimaryDispatchId = 10,
|
||||||
|
AccountId = 1,
|
||||||
|
WorkOrderType = WorkOrderType.PM,
|
||||||
|
LifecycleStatus = LifecycleStatus.Incomplete,
|
||||||
|
Status = "Incomplete",
|
||||||
|
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 },
|
||||||
|
});
|
||||||
|
await seed.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var createContext = new ApplicationDbContext(options);
|
||||||
|
await using var cancelContext = new ApplicationDbContext(options);
|
||||||
|
var createService = new WorkOrderUpliftService(
|
||||||
|
new UpliftDataService(createContext),
|
||||||
|
new DispatchDataService(createContext),
|
||||||
|
new WorkOrderDetailDataService(createContext),
|
||||||
|
WorkOrderAccountTestHelpers.Resolver(createContext),
|
||||||
|
new UserDataService(createContext),
|
||||||
|
TimeProvider.System,
|
||||||
|
Options.Create(new ApprovalsOptions()));
|
||||||
|
var boardData = new WorkOrderBoardDataService(cancelContext);
|
||||||
|
var mutationData = new WorkOrderBoardMutationDataService(cancelContext);
|
||||||
|
var boardService = new WorkOrderBoardService(boardData, WorkOrderAccountTestHelpers.Resolver(cancelContext));
|
||||||
|
var fieldLocks = new WorkOrderFieldLockService(new WorkOrderFieldLockDataService(cancelContext));
|
||||||
|
var audit = new WorkOrderAuditService(new WorkOrderAuditDataService(cancelContext), fieldLocks);
|
||||||
|
var cancelUplifts = new WorkOrderUpliftService(
|
||||||
|
new UpliftDataService(cancelContext),
|
||||||
|
new DispatchDataService(cancelContext),
|
||||||
|
new WorkOrderDetailDataService(cancelContext),
|
||||||
|
WorkOrderAccountTestHelpers.Resolver(cancelContext),
|
||||||
|
new UserDataService(cancelContext),
|
||||||
|
TimeProvider.System,
|
||||||
|
Options.Create(new ApprovalsOptions()));
|
||||||
|
var cancel = new WorkOrderBoardCancelService(
|
||||||
|
mutationData,
|
||||||
|
boardService,
|
||||||
|
audit,
|
||||||
|
cancelUplifts,
|
||||||
|
new UpliftDataService(cancelContext));
|
||||||
|
|
||||||
|
await Task.WhenAll(
|
||||||
|
createService.CreateAsync(
|
||||||
|
1,
|
||||||
|
new CreateWorkOrderUpliftRequestDto { Amount = 501m, Notes = "Pending" },
|
||||||
|
WorkOrderAccountTestHelpers.OrgWideAdmin("dispatcher-1"),
|
||||||
|
CancellationToken.None),
|
||||||
|
cancel.CancelAsync(1, WorkOrderAccountTestHelpers.OrgWideAdmin(), "actor-1"));
|
||||||
|
|
||||||
|
await using var verify = new ApplicationDbContext(options);
|
||||||
|
var workOrder = Assert.Single(verify.workOrders);
|
||||||
|
Assert.Equal(LifecycleStatus.Canceled, workOrder.LifecycleStatus);
|
||||||
|
Assert.DoesNotContain(verify.DispatchUpliftRequests, row => row.Status == "Pending");
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class ThrowingSaveMutationData : IWorkOrderBoardMutationDataService
|
private sealed class ThrowingSaveMutationData : IWorkOrderBoardMutationDataService
|
||||||
{
|
{
|
||||||
private readonly IWorkOrderBoardMutationDataService _inner;
|
private readonly IWorkOrderBoardMutationDataService _inner;
|
||||||
|
|
|
||||||
|
|
@ -374,4 +374,89 @@ public sealed class WorkOrderUpliftServiceTests
|
||||||
Assert.Single(pending);
|
Assert.Single(pending);
|
||||||
Assert.True(autoApproved.Sum(row => row.RequestedNTE) <= 500m);
|
Assert.True(autoApproved.Sum(row => row.RequestedNTE) <= 500m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RevokeAsync_OwnerRevokesAutoApproved_RestoresNteAndAllowance()
|
||||||
|
{
|
||||||
|
await using var context = CreateContext();
|
||||||
|
var (workOrder, _) = await SeedWorkOrderAsync(context);
|
||||||
|
var service = NewService(context);
|
||||||
|
|
||||||
|
var created = await service.CreateAsync(
|
||||||
|
workOrder.Id,
|
||||||
|
new CreateWorkOrderUpliftRequestDto { Amount = 400m, Notes = "Within limit" },
|
||||||
|
Dispatcher(),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal(1400m, context.Dispatches.Single(d => d.Id == 10).NTEAmount);
|
||||||
|
|
||||||
|
var revoked = await service.RevokeAsync(
|
||||||
|
workOrder.Id,
|
||||||
|
created!.Id,
|
||||||
|
new RevokeWorkOrderUpliftRequestDto(),
|
||||||
|
Dispatcher(),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal("revoked", revoked!.Status);
|
||||||
|
Assert.Equal(1000m, context.Dispatches.Single(d => d.Id == 10).NTEAmount);
|
||||||
|
Assert.Equal(0m, await new UpliftDataService(context)
|
||||||
|
.SumAutoApprovedAmountForWorkOrderAsync(workOrder.Id, CancellationToken.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(LifecycleStatus.Completed)]
|
||||||
|
[InlineData(LifecycleStatus.Canceled)]
|
||||||
|
public async Task RevokeAsync_TerminalWorkOrder_Throws(LifecycleStatus status)
|
||||||
|
{
|
||||||
|
await using var context = CreateContext();
|
||||||
|
var (workOrder, dispatch) = await SeedWorkOrderAsync(context);
|
||||||
|
workOrder.LifecycleStatus = status;
|
||||||
|
context.DispatchUpliftRequests.Add(new DispatchUpliftRequest
|
||||||
|
{
|
||||||
|
Id = 100,
|
||||||
|
DispatchId = dispatch.Id,
|
||||||
|
RequestedNTE = 400m,
|
||||||
|
CurrentNTE = 1000m,
|
||||||
|
Status = "NoApprovalRequired",
|
||||||
|
RequiredTier = 0,
|
||||||
|
NotificationStatus = "Sent",
|
||||||
|
createdby = "dispatcher-1",
|
||||||
|
});
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var service = NewService(context);
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||||
|
service.RevokeAsync(
|
||||||
|
workOrder.Id,
|
||||||
|
100,
|
||||||
|
new RevokeWorkOrderUpliftRequestDto(),
|
||||||
|
Dispatcher(),
|
||||||
|
CancellationToken.None));
|
||||||
|
Assert.Contains("work order", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(LifecycleStatus.Completed)]
|
||||||
|
[InlineData(LifecycleStatus.Canceled)]
|
||||||
|
public async Task CancelAsync_TerminalWorkOrder_Throws(LifecycleStatus status)
|
||||||
|
{
|
||||||
|
await using var context = CreateContext();
|
||||||
|
var (workOrder, dispatch) = await SeedWorkOrderAsync(context);
|
||||||
|
workOrder.LifecycleStatus = status;
|
||||||
|
context.DispatchUpliftRequests.Add(new DispatchUpliftRequest
|
||||||
|
{
|
||||||
|
Id = 100,
|
||||||
|
DispatchId = dispatch.Id,
|
||||||
|
RequestedNTE = 600m,
|
||||||
|
Status = "Pending",
|
||||||
|
RequiredTier = 1,
|
||||||
|
NotificationStatus = "Pending",
|
||||||
|
});
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var service = NewService(context);
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||||
|
service.CancelAsync(workOrder.Id, 100, Dispatcher(), CancellationToken.None));
|
||||||
|
Assert.Contains("work order", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue