Harden paid registration flow and add unit tests

This commit is contained in:
2026-04-24 21:10:28 -04:00
parent 4153acf3aa
commit 27ac793f62
8 changed files with 817 additions and 79 deletions
@@ -0,0 +1 @@
global using Xunit;
@@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.11" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.2" />
@@ -26,6 +27,7 @@
<ProjectReference Include="..\..\src\PowderCoating.Core\PowderCoating.Core.csproj" />
<ProjectReference Include="..\..\src\PowderCoating.Application\PowderCoating.Application.csproj" />
<ProjectReference Include="..\..\src\PowderCoating.Infrastructure\PowderCoating.Infrastructure.csproj" />
<ProjectReference Include="..\..\src\PowderCoating.Web\PowderCoating.Web.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,244 @@
using Microsoft.Extensions.Logging;
using Moq;
using PowderCoating.Application.DTOs.Quote;
using PowderCoating.Application.Services;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Interfaces;
using Xunit;
namespace PowderCoating.UnitTests;
public class PricingCalculationServiceTests
{
[Fact]
public async Task CalculateCoatPriceAsync_CustomPowder_ChargesFullOrderQuantity()
{
var costs = CreateOperatingCosts();
var unitOfWork = CreateUnitOfWorkMock(costs);
var tenantContext = new Mock<ITenantContext>();
tenantContext.Setup(x => x.UseMetricSystemAsync()).ReturnsAsync(false);
var service = new PricingCalculationService(
unitOfWork.Object,
Mock.Of<ILogger<PricingCalculationService>>(),
new MeasurementConversionService(),
tenantContext.Object);
var coat = new CreateQuoteItemCoatDto
{
CoatName = "Custom Red",
PowderCostPerLb = 10m,
PowderToOrder = 3m,
CoverageSqFtPerLb = 30m,
TransferEfficiency = 65m
};
var result = await service.CalculateCoatPriceAsync(
coat,
itemSurfaceAreaSqFt: 5m,
quantity: 2m,
coatIndex: 0,
estimatedMinutesBase: 15,
companyId: 1);
Assert.Equal(30m, result.CoatMaterialCost);
Assert.Equal(30m, result.CoatLaborCost);
Assert.Equal(60m, result.CoatTotalCost);
}
[Fact]
public async Task CalculateQuoteItemPriceAsync_LaborItem_UsesStandardLaborRate()
{
var costs = CreateOperatingCosts();
costs.StandardLaborRate = 80m;
var unitOfWork = CreateUnitOfWorkMock(costs);
var tenantContext = new Mock<ITenantContext>();
tenantContext.Setup(x => x.UseMetricSystemAsync()).ReturnsAsync(false);
var service = new PricingCalculationService(
unitOfWork.Object,
Mock.Of<ILogger<PricingCalculationService>>(),
new MeasurementConversionService(),
tenantContext.Object);
var item = new CreateQuoteItemDto
{
Description = "Shop labor",
IsLaborItem = true,
Quantity = 2.5m
};
var result = await service.CalculateQuoteItemPriceAsync(item, companyId: 1);
Assert.Equal(0m, result.MaterialCost);
Assert.Equal(200m, result.LaborCost);
Assert.Equal(80m, result.UnitPrice);
Assert.Equal(200m, result.TotalPrice);
}
[Fact]
public async Task CalculateQuoteItemPriceAsync_AiItem_UsesManualUnitPriceWithoutAdditionalCosts()
{
var unitOfWork = CreateUnitOfWorkMock(CreateOperatingCosts());
var tenantContext = new Mock<ITenantContext>();
tenantContext.Setup(x => x.UseMetricSystemAsync()).ReturnsAsync(false);
var service = new PricingCalculationService(
unitOfWork.Object,
Mock.Of<ILogger<PricingCalculationService>>(),
new MeasurementConversionService(),
tenantContext.Object);
var item = new CreateQuoteItemDto
{
Description = "AI wheel estimate",
IsAiItem = true,
ManualUnitPrice = 123m,
Quantity = 2m
};
var result = await service.CalculateQuoteItemPriceAsync(item, companyId: 1);
Assert.Equal(0m, result.MaterialCost);
Assert.Equal(0m, result.LaborCost);
Assert.Equal(123m, result.UnitPrice);
Assert.Equal(246m, result.TotalPrice);
}
[Fact]
public async Task CalculateQuoteTotalsAsync_AppliesTierDiscount_QuoteDiscount_RushFee_AndTax()
{
var costs = CreateOperatingCosts();
costs.StandardLaborRate = 100m;
costs.ShopSuppliesRate = 10m;
costs.RushChargeType = "Percentage";
costs.RushChargePercentage = 20m;
costs.TaxPercent = 5m;
costs.OvenOperatingCostPerHour = 0m;
costs.MonthlyRent = 0m;
costs.MonthlyUtilities = 0m;
var customerRepo = new Mock<IRepository<Customer>>();
customerRepo
.Setup(x => x.FindAsync(It.IsAny<System.Linq.Expressions.Expression<Func<Customer, bool>>>(), false, It.IsAny<System.Linq.Expressions.Expression<Func<Customer, object>>[]>()))
.ReturnsAsync(new[]
{
new Customer { Id = 1, CompanyId = 1, PricingTierId = 10 }
});
var pricingTierRepo = new Mock<IRepository<PricingTier>>();
pricingTierRepo
.Setup(x => x.GetByIdAsync(10, false, It.IsAny<System.Linq.Expressions.Expression<Func<PricingTier, object>>[]>()))
.ReturnsAsync(new PricingTier { Id = 10, CompanyId = 1, DiscountPercent = 10m });
var unitOfWork = CreateUnitOfWorkMock(costs);
unitOfWork.SetupGet(x => x.Customers).Returns(customerRepo.Object);
unitOfWork.SetupGet(x => x.PricingTiers).Returns(pricingTierRepo.Object);
var tenantContext = new Mock<ITenantContext>();
tenantContext.Setup(x => x.UseMetricSystemAsync()).ReturnsAsync(false);
var service = new PricingCalculationService(
unitOfWork.Object,
Mock.Of<ILogger<PricingCalculationService>>(),
new MeasurementConversionService(),
tenantContext.Object);
var items = new List<CreateQuoteItemDto>
{
new()
{
Description = "Labor item",
IsLaborItem = true,
Quantity = 2m
}
};
var result = await service.CalculateQuoteTotalsAsync(
items,
companyId: 1,
customerId: 1,
discountType: "FixedAmount",
discountValue: 5m,
isRushJob: true);
Assert.Equal(200m, result.ItemsSubtotal);
Assert.Equal(20m, result.ShopSuppliesAmount);
Assert.Equal(220m, result.SubtotalBeforeDiscount);
Assert.Equal(22m, result.PricingTierDiscountAmount);
Assert.Equal(5m, result.QuoteDiscountAmount);
Assert.Equal(193m, result.SubtotalAfterDiscount);
Assert.Equal(38.6m, result.RushFee);
Assert.Equal(11.58m, result.TaxAmount);
Assert.Equal(243.18m, result.Total);
}
private static Mock<IUnitOfWork> CreateUnitOfWorkMock(CompanyOperatingCosts costs)
{
var unitOfWork = new Mock<IUnitOfWork>();
var companyOperatingCostsRepo = new Mock<IRepository<CompanyOperatingCosts>>();
companyOperatingCostsRepo
.Setup(x => x.FindAsync(It.IsAny<System.Linq.Expressions.Expression<Func<CompanyOperatingCosts, bool>>>(), false, It.IsAny<System.Linq.Expressions.Expression<Func<CompanyOperatingCosts, object>>[]>()))
.ReturnsAsync(new[] { costs });
var inventoryRepo = new Mock<IRepository<InventoryItem>>();
inventoryRepo
.Setup(x => x.GetByIdAsync(It.IsAny<int>(), false, It.IsAny<System.Linq.Expressions.Expression<Func<InventoryItem, object>>[]>()))
.ReturnsAsync((InventoryItem?)null);
var catalogRepo = new Mock<IRepository<CatalogItem>>();
catalogRepo
.Setup(x => x.GetByIdAsync(It.IsAny<int>(), false, It.IsAny<System.Linq.Expressions.Expression<Func<CatalogItem, object>>[]>()))
.ReturnsAsync((CatalogItem?)null);
var customerRepo = new Mock<IRepository<Customer>>();
customerRepo
.Setup(x => x.FindAsync(It.IsAny<System.Linq.Expressions.Expression<Func<Customer, bool>>>(), false, It.IsAny<System.Linq.Expressions.Expression<Func<Customer, object>>[]>()))
.ReturnsAsync(Array.Empty<Customer>());
var pricingTierRepo = new Mock<IRepository<PricingTier>>();
pricingTierRepo
.Setup(x => x.GetByIdAsync(It.IsAny<int>(), false, It.IsAny<System.Linq.Expressions.Expression<Func<PricingTier, object>>[]>()))
.ReturnsAsync((PricingTier?)null);
unitOfWork.SetupGet(x => x.CompanyOperatingCosts).Returns(companyOperatingCostsRepo.Object);
unitOfWork.SetupGet(x => x.InventoryItems).Returns(inventoryRepo.Object);
unitOfWork.SetupGet(x => x.CatalogItems).Returns(catalogRepo.Object);
unitOfWork.SetupGet(x => x.Customers).Returns(customerRepo.Object);
unitOfWork.SetupGet(x => x.PricingTiers).Returns(pricingTierRepo.Object);
return unitOfWork;
}
private static CompanyOperatingCosts CreateOperatingCosts()
{
return new CompanyOperatingCosts
{
Id = 1,
CompanyId = 1,
StandardLaborRate = 60m,
AdditionalCoatLaborPercent = 50m,
OvenOperatingCostPerHour = 25m,
SandblasterCostPerHour = 20m,
CoatingBoothCostPerHour = 10m,
PowderCoatingCostPerSqFt = 1m,
PricingMode = PowderCoating.Core.Enums.PricingMode.MarkupOnMaterial,
GeneralMarkupPercentage = 20m,
TargetMarginPercent = 40m,
TaxPercent = 5m,
ShopSuppliesRate = 10m,
DefaultOvenCycleMinutes = 60,
RushChargeType = "Percentage",
RushChargePercentage = 15m,
RushChargeFixedAmount = 50m,
ShopMinimumCharge = 0m,
ComplexitySimplePercent = 0m,
ComplexityModeratePercent = 5m,
ComplexityComplexPercent = 15m,
ComplexityExtremePercent = 25m,
MonthlyBillableHours = 160
};
}
}
@@ -0,0 +1,191 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using PowderCoating.Application.Interfaces;
using PowderCoating.Core.Entities;
using PowderCoating.Infrastructure.Data;
using PowderCoating.Infrastructure.Repositories;
using PowderCoating.Web.Controllers;
using Xunit;
namespace PowderCoating.UnitTests;
public class RegistrationControllerTests
{
[Fact]
public async Task PaymentSuccess_WhenStripeSessionIsNotPaid_DoesNotBurnPendingSession()
{
await using var context = CreateContext();
context.PendingRegistrationSessions.Add(CreatePendingSession("token-1", "owner@example.com"));
await context.SaveChangesAsync();
var stripeService = new Mock<IStripeService>();
stripeService.Setup(x => x.IsRegistrationCheckoutPaidAsync("sess_unpaid")).ReturnsAsync(false);
var controller = CreateController(context, stripeService: stripeService);
var result = await controller.PaymentSuccess("sess_unpaid", "token-1");
var redirect = Assert.IsType<Microsoft.AspNetCore.Mvc.RedirectToActionResult>(result);
Assert.Equal("Index", redirect.ActionName);
Assert.False((await context.PendingRegistrationSessions.SingleAsync()).IsCompleted);
Assert.Contains("couldn't verify a completed payment", controller.TempData["Error"]?.ToString());
}
[Fact]
public async Task PaymentSuccess_WhenUserCreationFails_ReleasesPendingSessionAndDeletesCompany()
{
await using var context = CreateContext();
context.PendingRegistrationSessions.Add(CreatePendingSession("token-2", "owner2@example.com"));
await context.SaveChangesAsync();
var userManager = CreateUserManagerMock();
userManager.Setup(x => x.FindByEmailAsync("owner2@example.com")).ReturnsAsync((ApplicationUser?)null);
userManager.Setup(x => x.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>()))
.ReturnsAsync(IdentityResult.Failed(new IdentityError { Description = "boom" }));
var stripeService = new Mock<IStripeService>();
stripeService.Setup(x => x.IsRegistrationCheckoutPaidAsync("sess_paid")).ReturnsAsync(true);
var controller = CreateController(context, userManager, stripeService: stripeService);
var result = await controller.PaymentSuccess("sess_paid", "token-2");
var redirect = Assert.IsType<Microsoft.AspNetCore.Mvc.RedirectToActionResult>(result);
Assert.Equal("Index", redirect.ActionName);
Assert.False((await context.PendingRegistrationSessions.SingleAsync()).IsCompleted);
Assert.Empty(context.Companies);
Assert.Contains("Please try the success link again", controller.TempData["Error"]?.ToString());
}
[Fact]
public async Task PaymentSuccess_WhenSessionAlreadyCompletedAndUserExists_SignsUserInAndRedirectsToWelcome()
{
await using var context = CreateContext();
context.PendingRegistrationSessions.Add(CreatePendingSession("token-3", "owner3@example.com", isCompleted: true));
await context.SaveChangesAsync();
var existingUser = new ApplicationUser
{
Id = "user-3",
Email = "owner3@example.com",
UserName = "owner3@example.com",
FirstName = "Terry",
LastName = "Tenant",
CompanyId = 3
};
var userManager = CreateUserManagerMock();
userManager.Setup(x => x.FindByEmailAsync("owner3@example.com")).ReturnsAsync(existingUser);
userManager.Setup(x => x.UpdateAsync(existingUser)).ReturnsAsync(IdentityResult.Success);
var signInManager = CreateSignInManagerMock(userManager.Object);
signInManager.Setup(x => x.SignInAsync(existingUser, false, null)).Returns(Task.CompletedTask).Verifiable();
var controller = CreateController(context, userManager, signInManager.Object);
var result = await controller.PaymentSuccess("sess_complete", "token-3");
var redirect = Assert.IsType<Microsoft.AspNetCore.Mvc.RedirectToActionResult>(result);
Assert.Equal("Welcome", redirect.ActionName);
signInManager.Verify(x => x.SignInAsync(existingUser, false, null), Times.Once);
Assert.True((await context.PendingRegistrationSessions.SingleAsync()).IsCompleted);
}
private static RegistrationController CreateController(
ApplicationDbContext context,
Mock<UserManager<ApplicationUser>>? userManager = null,
SignInManager<ApplicationUser>? signInManager = null,
Mock<IStripeService>? stripeService = null)
{
var unitOfWork = new UnitOfWork(context);
var userManagerMock = userManager ?? CreateUserManagerMock();
var signInManagerInstance = signInManager ?? CreateSignInManagerMock(userManagerMock.Object).Object;
var platformSettings = new Mock<IPlatformSettingsService>();
platformSettings.Setup(x => x.GetAsync(It.IsAny<string>())).ReturnsAsync((string?)null);
var controller = new RegistrationController(
unitOfWork,
context,
userManagerMock.Object,
signInManagerInstance,
Mock.Of<ISeedDataService>(),
Mock.Of<IAdminNotificationService>(),
Mock.Of<IInAppNotificationService>(),
platformSettings.Object,
(stripeService ?? new Mock<IStripeService>()).Object,
Mock.Of<IEmailService>(),
Mock.Of<ILogger<RegistrationController>>());
var httpContext = new DefaultHttpContext();
controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext
{
HttpContext = httpContext
};
controller.TempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
return controller;
}
private static Mock<UserManager<ApplicationUser>> CreateUserManagerMock()
{
var store = new Mock<IUserStore<ApplicationUser>>();
return new Mock<UserManager<ApplicationUser>>(
store.Object,
null!,
null!,
null!,
null!,
null!,
null!,
null!,
null!);
}
private static Mock<SignInManager<ApplicationUser>> CreateSignInManagerMock(UserManager<ApplicationUser> userManager)
{
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(x => x.HttpContext).Returns(new DefaultHttpContext());
var claimsFactory = new Mock<IUserClaimsPrincipalFactory<ApplicationUser>>();
return new Mock<SignInManager<ApplicationUser>>(
userManager,
contextAccessor.Object,
claimsFactory.Object,
null!,
null!,
null!,
null!);
}
private static ApplicationDbContext CreateContext()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
return new ApplicationDbContext(options);
}
private static PendingRegistrationSession CreatePendingSession(string token, string email, bool isCompleted = false)
{
return new PendingRegistrationSession
{
Token = token,
CompanyName = "Retry Co",
CompanyPhone = "555-0100",
FirstName = "Pat",
LastName = "Owner",
Email = email,
Plan = 1,
IsAnnual = false,
IsCompleted = isCompleted,
CreatedAt = DateTime.UtcNow
};
}
}
@@ -0,0 +1,221 @@
using Microsoft.EntityFrameworkCore;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Enums;
using PowderCoating.Infrastructure.Data;
using PowderCoating.Infrastructure.Repositories;
using PowderCoating.Infrastructure.Services;
using Xunit;
namespace PowderCoating.UnitTests;
public class SubscriptionServiceTests
{
[Fact]
public async Task GetUserCountAsync_PrefersCompanyOverrideOverPlanDefault()
{
await using var context = CreateContext();
SeedCompanyAndPlan(context, companyId: 7, plan: 1, maxUsers: 3);
var company = context.Companies.Local.Single(c => c.Id == 7);
company.MaxUsersOverride = 7;
context.Users.AddRange(
new ApplicationUser { Id = "u1", CompanyId = 7, UserName = "u1", Email = "u1@example.com", FirstName = "A", LastName = "One", IsActive = true },
new ApplicationUser { Id = "u2", CompanyId = 7, UserName = "u2", Email = "u2@example.com", FirstName = "B", LastName = "Two", IsActive = true });
await context.SaveChangesAsync();
var service = new SubscriptionService(new UnitOfWork(context), context);
var (used, max) = await service.GetUserCountAsync(7);
Assert.Equal(2, used);
Assert.Equal(7, max);
}
[Fact]
public async Task GetJobCountAsync_ExcludesTerminalStatuses()
{
await using var context = CreateContext();
SeedCompanyAndPlan(context, companyId: 8, plan: 2, maxActiveJobs: 50);
SeedJobStatuses(context, 8);
context.Jobs.AddRange(
new Job { Id = 1, CompanyId = 8, JobNumber = "JOB-1", CustomerId = 1, Description = "Active", JobStatusId = 1, JobPriorityId = 1 },
new Job { Id = 2, CompanyId = 8, JobNumber = "JOB-2", CustomerId = 1, Description = "Done", JobStatusId = 2, JobPriorityId = 1 },
new Job { Id = 3, CompanyId = 8, JobNumber = "JOB-3", CustomerId = 1, Description = "Delivered", JobStatusId = 3, JobPriorityId = 1 });
await context.SaveChangesAsync();
var service = new SubscriptionService(new UnitOfWork(context), context);
var (used, max) = await service.GetJobCountAsync(8);
Assert.Equal(1, used);
Assert.Equal(50, max);
}
[Fact]
public async Task GetQuoteCountAsync_CountsOnlyCurrentMonth()
{
await using var context = CreateContext();
SeedCompanyAndPlan(context, companyId: 9, plan: 3, maxQuotes: 5);
var currentQuote = new Quote
{
Id = 1,
CompanyId = 9,
QuoteNumber = "Q-001",
QuoteStatusId = 1
};
var oldQuote = new Quote
{
Id = 2,
CompanyId = 9,
QuoteNumber = "Q-OLD",
QuoteStatusId = 1
};
context.Quotes.AddRange(currentQuote, oldQuote);
await context.SaveChangesAsync();
oldQuote.CreatedAt = DateTime.UtcNow.AddMonths(-1);
await context.SaveChangesAsync();
var service = new SubscriptionService(new UnitOfWork(context), context);
var (used, max) = await service.GetQuoteCountAsync(9);
Assert.Equal(1, used);
Assert.Equal(5, max);
}
[Fact]
public async Task CanAddCustomerAsync_CompedCompany_BypassesPlanLimits()
{
await using var context = CreateContext();
SeedCompanyAndPlan(context, companyId: 10, plan: 4, maxCustomers: 0);
var company = await context.Companies.FindAsync(10);
company!.IsComped = true;
context.Customers.Add(new Customer { Id = 1, CompanyId = 10, CompanyName = "Customer A" });
await context.SaveChangesAsync();
var service = new SubscriptionService(new UnitOfWork(context), context);
var allowed = await service.CanAddCustomerAsync(10);
Assert.True(allowed);
}
[Fact]
public async Task CanUseAiPhotoQuoteAsync_RequiresFeatureEnabledAndQuotaAvailable()
{
await using var context = CreateContext();
SeedCompanyAndPlan(context, companyId: 11, plan: 5, maxAiPhotoQuotesPerMonth: 2, allowAiPhotoQuotes: true);
context.AiItemPredictions.Add(new AiItemPrediction { Id = 1, CompanyId = 11, CreatedAt = DateTime.UtcNow.AddDays(-1) });
await context.SaveChangesAsync();
var service = new SubscriptionService(new UnitOfWork(context), context);
var allowed = await service.CanUseAiPhotoQuoteAsync(11);
Assert.True(allowed);
}
[Fact]
public async Task CanUseAiPhotoQuoteAsync_ReturnsFalse_WhenPlanDisablesFeature()
{
await using var context = CreateContext();
SeedCompanyAndPlan(context, companyId: 12, plan: 6, maxAiPhotoQuotesPerMonth: 10, allowAiPhotoQuotes: false);
await context.SaveChangesAsync();
var service = new SubscriptionService(new UnitOfWork(context), context);
var allowed = await service.CanUseAiPhotoQuoteAsync(12);
Assert.False(allowed);
}
private static ApplicationDbContext CreateContext()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
return new ApplicationDbContext(options);
}
private static void SeedCompanyAndPlan(
ApplicationDbContext context,
int companyId,
int plan,
int maxUsers = -1,
int maxActiveJobs = -1,
int maxCustomers = -1,
int maxQuotes = -1,
int maxAiPhotoQuotesPerMonth = -1,
bool allowAiPhotoQuotes = true)
{
context.Companies.Add(new Company
{
Id = companyId,
CompanyId = companyId,
CompanyName = $"Company {companyId}",
PrimaryContactName = "Owner",
PrimaryContactEmail = $"owner{companyId}@example.com",
SubscriptionPlan = plan,
SubscriptionStatus = SubscriptionStatus.Active,
IsActive = true
});
context.SubscriptionPlanConfigs.Add(new SubscriptionPlanConfig
{
Id = companyId,
CompanyId = 0,
Plan = plan,
DisplayName = $"Plan {plan}",
IsActive = true,
MaxUsers = maxUsers,
MaxActiveJobs = maxActiveJobs,
MaxCustomers = maxCustomers,
MaxQuotes = maxQuotes,
MaxAiPhotoQuotesPerMonth = maxAiPhotoQuotesPerMonth,
AllowAiPhotoQuotes = allowAiPhotoQuotes
});
context.JobPriorityLookups.Add(new JobPriorityLookup
{
Id = companyId,
CompanyId = companyId,
PriorityCode = "NORMAL",
DisplayName = "Normal",
DisplayOrder = 1
});
}
private static void SeedJobStatuses(ApplicationDbContext context, int companyId)
{
context.JobStatusLookups.AddRange(
new JobStatusLookup
{
Id = 1,
CompanyId = companyId,
StatusCode = "Pending",
DisplayName = "Pending",
DisplayOrder = 1,
IsTerminalStatus = false
},
new JobStatusLookup
{
Id = 2,
CompanyId = companyId,
StatusCode = "Completed",
DisplayName = "Completed",
DisplayOrder = 2,
IsTerminalStatus = true
},
new JobStatusLookup
{
Id = 3,
CompanyId = companyId,
StatusCode = "Delivered",
DisplayName = "Delivered",
DisplayOrder = 3,
IsTerminalStatus = true
});
}
}