Refactor: extract shared helpers, fix field drift, add assembly services
- IJobItemAssemblyService / IQuotePricingAssemblyService: centralize job item and quote pricing construction that was duplicated across create, rework copy, and quote-to-job conversion paths - BlobFileHelper: single ValidateUpload/GetContentType/SanitizeFileName used by 6 blob services (JobPhoto, QuotePhoto, ProfilePhoto, CompanyLogo, Equipment, Catalog) and BillsController + ExpensesController, removing 8 private copies - PagedResult<T>.From(): static factory eliminates 6-line boilerplate in 11 controllers (Appointments, Customers, Equipment, Inventory, Invoices, Jobs, Maintenance, CompanyUsers, PlatformUsers, Quotes, Vendors) - AccountingDropdownHelper: single LoadAsync() call replaces duplicate vendor/account/job queries in BillsController and ExpensesController - JobTemplateItem: add IsSalesItem + Sku fields with migration; propagate through JobTemplatesController snapshot copy and GetTemplatesJson projection, and JobsController template-application path - Test assertions updated for standardized BlobFileHelper error messages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ public class JobsController : Controller
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly ISubscriptionService _subscriptionService;
|
||||
private readonly IPricingCalculationService _pricingService;
|
||||
private readonly IJobItemAssemblyService _jobItemAssemblyService;
|
||||
private readonly IHubContext<NotificationHub> _hub;
|
||||
private readonly IHubContext<ShopHub> _shopHub;
|
||||
|
||||
@@ -49,6 +50,7 @@ public class JobsController : Controller
|
||||
INotificationService notificationService,
|
||||
ISubscriptionService subscriptionService,
|
||||
IPricingCalculationService pricingService,
|
||||
IJobItemAssemblyService jobItemAssemblyService,
|
||||
IHubContext<NotificationHub> hub,
|
||||
IHubContext<ShopHub> shopHub)
|
||||
{
|
||||
@@ -63,6 +65,7 @@ public class JobsController : Controller
|
||||
_notificationService = notificationService;
|
||||
_subscriptionService = subscriptionService;
|
||||
_pricingService = pricingService;
|
||||
_jobItemAssemblyService = jobItemAssemblyService;
|
||||
_hub = hub;
|
||||
_shopHub = shopHub;
|
||||
}
|
||||
@@ -185,14 +188,9 @@ public class JobsController : Controller
|
||||
.Contains(tagLower)).ToList();
|
||||
}
|
||||
|
||||
// Create paged result
|
||||
var pagedResult = new PagedResult<JobListDto>
|
||||
{
|
||||
Items = jobDtos,
|
||||
PageNumber = gridRequest.PageNumber,
|
||||
PageSize = gridRequest.PageSize,
|
||||
TotalCount = string.IsNullOrWhiteSpace(tagFilter) ? totalCount : jobDtos.Count
|
||||
};
|
||||
var pagedResult = PagedResult<JobListDto>.From(
|
||||
gridRequest, jobDtos,
|
||||
string.IsNullOrWhiteSpace(tagFilter) ? totalCount : jobDtos.Count);
|
||||
|
||||
// Set ViewBag for sorting
|
||||
ViewBag.SearchTerm = searchTerm;
|
||||
@@ -1026,8 +1024,8 @@ public class JobsController : Controller
|
||||
catalogItemId = i.CatalogItemId,
|
||||
isGenericItem = i.IsGenericItem,
|
||||
isLaborItem = i.IsLaborItem,
|
||||
isSalesItem = false, // JobTemplateItem doesn't have IsSalesItem — default false
|
||||
sku = (string?)null,
|
||||
isSalesItem = i.IsSalesItem,
|
||||
sku = i.Sku,
|
||||
manualUnitPrice = i.ManualUnitPrice,
|
||||
requiresSandblasting = i.RequiresSandblasting,
|
||||
requiresMasking = i.RequiresMasking,
|
||||
@@ -1147,82 +1145,20 @@ public class JobsController : Controller
|
||||
{
|
||||
var itemPricing = await _pricingService.CalculateQuoteItemPriceAsync(
|
||||
itemDto, companyId, null);
|
||||
|
||||
var jobItem = new JobItem
|
||||
{
|
||||
JobId = job.Id,
|
||||
Description = itemDto.Description,
|
||||
Quantity = itemDto.Quantity,
|
||||
SurfaceAreaSqFt = itemDto.SurfaceAreaSqFt,
|
||||
EstimatedMinutes = itemDto.EstimatedMinutes,
|
||||
CatalogItemId = itemDto.CatalogItemId,
|
||||
IsGenericItem = itemDto.IsGenericItem,
|
||||
IsLaborItem = itemDto.IsLaborItem,
|
||||
IsSalesItem = itemDto.IsSalesItem,
|
||||
Sku = itemDto.Sku,
|
||||
ManualUnitPrice = itemDto.ManualUnitPrice,
|
||||
PowderCostOverride = itemDto.PowderCostOverride,
|
||||
RequiresSandblasting = itemDto.RequiresSandblasting,
|
||||
RequiresMasking = itemDto.RequiresMasking,
|
||||
Notes = itemDto.Notes,
|
||||
IncludePrepCost = itemDto.IncludePrepCost,
|
||||
Complexity = itemDto.Complexity,
|
||||
UnitPrice = itemPricing.UnitPrice,
|
||||
TotalPrice = itemPricing.TotalPrice,
|
||||
LaborCost = itemPricing.TotalPrice * 0.4m,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
var createdAtUtc = DateTime.UtcNow;
|
||||
var jobItem = _jobItemAssemblyService.CreateJobItem(itemDto, job.Id, companyId, itemPricing, createdAtUtc);
|
||||
|
||||
await _unitOfWork.JobItems.AddAsync(jobItem);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
if (itemDto.Coats?.Any() == true)
|
||||
foreach (var coat in _jobItemAssemblyService.CreateJobItemCoats(itemDto, jobItem.Id, companyId, createdAtUtc))
|
||||
{
|
||||
foreach (var coatDto in itemDto.Coats.OrderBy(c => c.Sequence))
|
||||
{
|
||||
decimal? powderToOrder = coatDto.PowderToOrder;
|
||||
if ((powderToOrder == null || powderToOrder == 0) && itemDto.SurfaceAreaSqFt > 0)
|
||||
{
|
||||
var cov = coatDto.CoverageSqFtPerLb > 0 ? coatDto.CoverageSqFtPerLb : 30m;
|
||||
var eff = coatDto.TransferEfficiency > 0 ? coatDto.TransferEfficiency / 100m : 0.65m;
|
||||
powderToOrder = Math.Round((itemDto.SurfaceAreaSqFt * itemDto.Quantity) / (cov * eff), 2);
|
||||
}
|
||||
await _unitOfWork.JobItemCoats.AddAsync(new JobItemCoat
|
||||
{
|
||||
JobItemId = jobItem.Id,
|
||||
CoatName = coatDto.CoatName,
|
||||
Sequence = coatDto.Sequence,
|
||||
InventoryItemId = coatDto.InventoryItemId,
|
||||
ColorName = coatDto.ColorName,
|
||||
VendorId = coatDto.VendorId,
|
||||
ColorCode = coatDto.ColorCode,
|
||||
Finish = coatDto.Finish,
|
||||
CoverageSqFtPerLb = coatDto.CoverageSqFtPerLb,
|
||||
TransferEfficiency = coatDto.TransferEfficiency,
|
||||
PowderCostPerLb = coatDto.PowderCostPerLb,
|
||||
PowderToOrder = powderToOrder,
|
||||
Notes = coatDto.Notes,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
await _unitOfWork.JobItemCoats.AddAsync(coat);
|
||||
}
|
||||
|
||||
if (itemDto.PrepServices?.Any() == true)
|
||||
foreach (var prepService in _jobItemAssemblyService.CreateJobItemPrepServices(itemDto, jobItem.Id, companyId, createdAtUtc))
|
||||
{
|
||||
foreach (var psDto in itemDto.PrepServices)
|
||||
{
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(new JobItemPrepService
|
||||
{
|
||||
JobItemId = jobItem.Id,
|
||||
PrepServiceId = psDto.PrepServiceId,
|
||||
EstimatedMinutes = psDto.EstimatedMinutes,
|
||||
BlastSetupId = psDto.BlastSetupId,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(prepService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1416,79 +1352,20 @@ public class JobsController : Controller
|
||||
{
|
||||
var itemPricing = await _pricingService.CalculateQuoteItemPriceAsync(
|
||||
itemDto, companyId, null);
|
||||
|
||||
var jobItem = new JobItem
|
||||
{
|
||||
JobId = id,
|
||||
Description = itemDto.Description,
|
||||
Quantity = itemDto.Quantity,
|
||||
SurfaceAreaSqFt = itemDto.SurfaceAreaSqFt,
|
||||
EstimatedMinutes = itemDto.EstimatedMinutes,
|
||||
CatalogItemId = itemDto.CatalogItemId,
|
||||
IsGenericItem = itemDto.IsGenericItem,
|
||||
IsLaborItem = itemDto.IsLaborItem,
|
||||
ManualUnitPrice = itemDto.ManualUnitPrice,
|
||||
PowderCostOverride = itemDto.PowderCostOverride,
|
||||
RequiresSandblasting = itemDto.RequiresSandblasting,
|
||||
RequiresMasking = itemDto.RequiresMasking,
|
||||
Notes = itemDto.Notes,
|
||||
IncludePrepCost = itemDto.IncludePrepCost,
|
||||
Complexity = itemDto.Complexity,
|
||||
UnitPrice = itemPricing.UnitPrice,
|
||||
TotalPrice = itemPricing.TotalPrice,
|
||||
LaborCost = itemPricing.TotalPrice * 0.4m,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
var createdAtUtc = DateTime.UtcNow;
|
||||
var jobItem = _jobItemAssemblyService.CreateJobItem(itemDto, id, companyId, itemPricing, createdAtUtc);
|
||||
|
||||
await _unitOfWork.JobItems.AddAsync(jobItem);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
if (itemDto.Coats?.Any() == true)
|
||||
foreach (var coat in _jobItemAssemblyService.CreateJobItemCoats(itemDto, jobItem.Id, companyId, createdAtUtc))
|
||||
{
|
||||
foreach (var coatDto in itemDto.Coats.OrderBy(c => c.Sequence))
|
||||
{
|
||||
decimal? powderToOrder = coatDto.PowderToOrder;
|
||||
if ((powderToOrder == null || powderToOrder == 0) && itemDto.SurfaceAreaSqFt > 0)
|
||||
{
|
||||
var cov = coatDto.CoverageSqFtPerLb > 0 ? coatDto.CoverageSqFtPerLb : 30m;
|
||||
var eff = coatDto.TransferEfficiency > 0 ? coatDto.TransferEfficiency / 100m : 0.65m;
|
||||
powderToOrder = Math.Round((itemDto.SurfaceAreaSqFt * itemDto.Quantity) / (cov * eff), 2);
|
||||
}
|
||||
await _unitOfWork.JobItemCoats.AddAsync(new JobItemCoat
|
||||
{
|
||||
JobItemId = jobItem.Id,
|
||||
CoatName = coatDto.CoatName,
|
||||
Sequence = coatDto.Sequence,
|
||||
InventoryItemId = coatDto.InventoryItemId,
|
||||
ColorName = coatDto.ColorName,
|
||||
VendorId = coatDto.VendorId,
|
||||
ColorCode = coatDto.ColorCode,
|
||||
Finish = coatDto.Finish,
|
||||
CoverageSqFtPerLb = coatDto.CoverageSqFtPerLb,
|
||||
TransferEfficiency = coatDto.TransferEfficiency,
|
||||
PowderCostPerLb = coatDto.PowderCostPerLb,
|
||||
PowderToOrder = powderToOrder,
|
||||
Notes = coatDto.Notes,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
await _unitOfWork.JobItemCoats.AddAsync(coat);
|
||||
}
|
||||
|
||||
if (itemDto.PrepServices?.Any() == true)
|
||||
foreach (var prepService in _jobItemAssemblyService.CreateJobItemPrepServices(itemDto, jobItem.Id, companyId, createdAtUtc))
|
||||
{
|
||||
foreach (var psDto in itemDto.PrepServices)
|
||||
{
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(new JobItemPrepService
|
||||
{
|
||||
JobItemId = jobItem.Id,
|
||||
PrepServiceId = psDto.PrepServiceId,
|
||||
EstimatedMinutes = psDto.EstimatedMinutes,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(prepService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3130,86 +3007,20 @@ public class JobsController : Controller
|
||||
{
|
||||
var itemPricing = await _pricingService.CalculateQuoteItemPriceAsync(
|
||||
itemDto, currentUser.CompanyId, null);
|
||||
|
||||
var jobItem = new JobItem
|
||||
{
|
||||
JobId = job.Id,
|
||||
Description = itemDto.Description,
|
||||
Quantity = itemDto.Quantity,
|
||||
SurfaceAreaSqFt = itemDto.SurfaceAreaSqFt,
|
||||
EstimatedMinutes = itemDto.EstimatedMinutes,
|
||||
CatalogItemId = itemDto.CatalogItemId,
|
||||
IsGenericItem = itemDto.IsGenericItem,
|
||||
IsLaborItem = itemDto.IsLaborItem,
|
||||
ManualUnitPrice = itemDto.ManualUnitPrice,
|
||||
PowderCostOverride = itemDto.PowderCostOverride,
|
||||
RequiresSandblasting = itemDto.RequiresSandblasting,
|
||||
RequiresMasking = itemDto.RequiresMasking,
|
||||
Notes = itemDto.Notes,
|
||||
IncludePrepCost = itemDto.IncludePrepCost,
|
||||
Complexity = itemDto.Complexity,
|
||||
UnitPrice = itemPricing.UnitPrice,
|
||||
TotalPrice = itemPricing.TotalPrice,
|
||||
LaborCost = itemPricing.TotalPrice * 0.4m,
|
||||
CompanyId = currentUser.CompanyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
var createdAtUtc = DateTime.UtcNow;
|
||||
var jobItem = _jobItemAssemblyService.CreateJobItem(itemDto, job.Id, currentUser.CompanyId, itemPricing, createdAtUtc);
|
||||
|
||||
await _unitOfWork.JobItems.AddAsync(jobItem);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
// Coats
|
||||
if (itemDto.Coats?.Any() == true)
|
||||
foreach (var coat in _jobItemAssemblyService.CreateJobItemCoats(itemDto, jobItem.Id, currentUser.CompanyId, createdAtUtc))
|
||||
{
|
||||
foreach (var coatDto in itemDto.Coats.OrderBy(c => c.Sequence))
|
||||
{
|
||||
// Calculate PowderToOrder if not supplied by the client
|
||||
decimal? powderToOrder = coatDto.PowderToOrder;
|
||||
if ((powderToOrder == null || powderToOrder == 0) && itemDto.SurfaceAreaSqFt > 0)
|
||||
{
|
||||
var cov = coatDto.CoverageSqFtPerLb > 0 ? coatDto.CoverageSqFtPerLb : 30m;
|
||||
var eff = coatDto.TransferEfficiency > 0 ? coatDto.TransferEfficiency / 100m : 0.65m;
|
||||
powderToOrder = Math.Round((itemDto.SurfaceAreaSqFt * itemDto.Quantity) / (cov * eff), 2);
|
||||
}
|
||||
|
||||
var coat = new JobItemCoat
|
||||
{
|
||||
JobItemId = jobItem.Id,
|
||||
CoatName = coatDto.CoatName,
|
||||
Sequence = coatDto.Sequence,
|
||||
InventoryItemId = coatDto.InventoryItemId,
|
||||
ColorName = coatDto.ColorName,
|
||||
VendorId = coatDto.VendorId,
|
||||
ColorCode = coatDto.ColorCode,
|
||||
Finish = coatDto.Finish,
|
||||
CoverageSqFtPerLb = coatDto.CoverageSqFtPerLb,
|
||||
TransferEfficiency = coatDto.TransferEfficiency,
|
||||
PowderCostPerLb = coatDto.PowderCostPerLb,
|
||||
PowderToOrder = powderToOrder,
|
||||
Notes = coatDto.Notes,
|
||||
CompanyId = currentUser.CompanyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
await _unitOfWork.JobItemCoats.AddAsync(coat);
|
||||
}
|
||||
await _unitOfWork.JobItemCoats.AddAsync(coat);
|
||||
}
|
||||
|
||||
// Prep services
|
||||
if (itemDto.PrepServices?.Any() == true)
|
||||
foreach (var prepService in _jobItemAssemblyService.CreateJobItemPrepServices(itemDto, jobItem.Id, currentUser.CompanyId, createdAtUtc))
|
||||
{
|
||||
foreach (var psDto in itemDto.PrepServices)
|
||||
{
|
||||
var ps = new JobItemPrepService
|
||||
{
|
||||
JobItemId = jobItem.Id,
|
||||
PrepServiceId = psDto.PrepServiceId,
|
||||
EstimatedMinutes = psDto.EstimatedMinutes,
|
||||
BlastSetupId = psDto.BlastSetupId,
|
||||
CompanyId = currentUser.CompanyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(ps);
|
||||
}
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(prepService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3638,60 +3449,20 @@ public class JobsController : Controller
|
||||
|
||||
foreach (var item in itemsToCopy)
|
||||
{
|
||||
var newItem = new JobItem
|
||||
{
|
||||
JobId = reworkJob.Id,
|
||||
Description = item.Description,
|
||||
Quantity = item.Quantity,
|
||||
SurfaceAreaSqFt = item.SurfaceAreaSqFt,
|
||||
CatalogItemId = item.CatalogItemId,
|
||||
IsGenericItem = item.IsGenericItem,
|
||||
IsLaborItem = item.IsLaborItem,
|
||||
ManualUnitPrice = item.ManualUnitPrice,
|
||||
RequiresSandblasting = item.RequiresSandblasting,
|
||||
RequiresMasking = item.RequiresMasking,
|
||||
IncludePrepCost = item.IncludePrepCost,
|
||||
EstimatedMinutes = item.EstimatedMinutes,
|
||||
Complexity = item.Complexity,
|
||||
Notes = item.Notes,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
var createdAtUtc = DateTime.UtcNow;
|
||||
var newItem = _jobItemAssemblyService.CreateJobItem(item, reworkJob.Id, companyId, createdAtUtc);
|
||||
|
||||
await _unitOfWork.JobItems.AddAsync(newItem);
|
||||
await _unitOfWork.CompleteAsync();
|
||||
|
||||
foreach (var coat in item.Coats.OrderBy(c => c.Sequence))
|
||||
foreach (var coat in _jobItemAssemblyService.CreateJobItemCoats(item, newItem.Id, companyId, createdAtUtc))
|
||||
{
|
||||
await _unitOfWork.JobItemCoats.AddAsync(new JobItemCoat
|
||||
{
|
||||
JobItemId = newItem.Id,
|
||||
CoatName = coat.CoatName,
|
||||
Sequence = coat.Sequence,
|
||||
InventoryItemId = coat.InventoryItemId,
|
||||
ColorName = coat.ColorName,
|
||||
VendorId = coat.VendorId,
|
||||
ColorCode = coat.ColorCode,
|
||||
Finish = coat.Finish,
|
||||
CoverageSqFtPerLb = coat.CoverageSqFtPerLb,
|
||||
TransferEfficiency = coat.TransferEfficiency,
|
||||
PowderCostPerLb = coat.PowderCostPerLb,
|
||||
Notes = coat.Notes,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
await _unitOfWork.JobItemCoats.AddAsync(coat);
|
||||
}
|
||||
|
||||
foreach (var prep in item.PrepServices)
|
||||
foreach (var prepService in _jobItemAssemblyService.CreateJobItemPrepServices(item, newItem.Id, companyId, createdAtUtc))
|
||||
{
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(new JobItemPrepService
|
||||
{
|
||||
JobItemId = newItem.Id,
|
||||
PrepServiceId = prep.PrepServiceId,
|
||||
EstimatedMinutes = prep.EstimatedMinutes,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(prepService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3910,95 +3681,31 @@ public class JobsController : Controller
|
||||
|
||||
foreach (var quoteItem in fullItems.Where(qi => !qi.IsDeleted))
|
||||
{
|
||||
var firstCoat = quoteItem.Coats?.OrderBy(c => c.Sequence).FirstOrDefault();
|
||||
|
||||
var jobItem = new JobItem
|
||||
{
|
||||
JobId = id,
|
||||
Description = quoteItem.Description,
|
||||
Quantity = quoteItem.Quantity,
|
||||
ColorName = firstCoat?.ColorName,
|
||||
ColorCode = firstCoat?.ColorCode,
|
||||
Finish = firstCoat?.Finish,
|
||||
SurfaceArea = quoteItem.SurfaceAreaSqFt,
|
||||
SurfaceAreaSqFt = quoteItem.SurfaceAreaSqFt,
|
||||
CatalogItemId = quoteItem.CatalogItemId,
|
||||
IsGenericItem = quoteItem.IsGenericItem,
|
||||
IsLaborItem = quoteItem.IsLaborItem,
|
||||
IsSalesItem = quoteItem.IsSalesItem,
|
||||
Sku = quoteItem.Sku,
|
||||
ManualUnitPrice = quoteItem.ManualUnitPrice,
|
||||
PowderCostOverride = quoteItem.PowderCostOverride,
|
||||
UnitPrice = quoteItem.UnitPrice,
|
||||
TotalPrice = quoteItem.TotalPrice,
|
||||
LaborCost = quoteItem.TotalPrice * 0.4m,
|
||||
RequiresSandblasting = quoteItem.RequiresSandblasting,
|
||||
RequiresMasking = quoteItem.RequiresMasking,
|
||||
EstimatedMinutes = quoteItem.EstimatedMinutes,
|
||||
Notes = quoteItem.Notes,
|
||||
Complexity = quoteItem.Complexity,
|
||||
AiTags = quoteItem.AiTags,
|
||||
AiPredictionId = quoteItem.AiPredictionId,
|
||||
IncludePrepCost = !quoteItem.CatalogItemId.HasValue,
|
||||
CompanyId = job.CompanyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
var createdAtUtc = DateTime.UtcNow;
|
||||
var jobItem = _jobItemAssemblyService.CreateJobItem(quoteItem, id, job.CompanyId, createdAtUtc);
|
||||
|
||||
await _unitOfWork.JobItems.AddAsync(jobItem);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
if (quoteItem.Coats != null)
|
||||
foreach (var coat in _jobItemAssemblyService.CreateJobItemCoats(quoteItem, jobItem.Id, job.CompanyId, createdAtUtc))
|
||||
{
|
||||
foreach (var quoteCoat in quoteItem.Coats.OrderBy(c => c.Sequence))
|
||||
{
|
||||
string colorName = quoteCoat.ColorName;
|
||||
string colorCode = quoteCoat.ColorCode;
|
||||
string finish = quoteCoat.Finish;
|
||||
await _unitOfWork.JobItemCoats.AddAsync(coat);
|
||||
}
|
||||
|
||||
if (quoteCoat.InventoryItemId.HasValue && quoteCoat.InventoryItem != null)
|
||||
{
|
||||
colorName = quoteCoat.InventoryItem.Name;
|
||||
colorCode = quoteCoat.InventoryItem.ColorCode;
|
||||
finish = quoteCoat.InventoryItem.Finish;
|
||||
}
|
||||
|
||||
var cov = quoteCoat.CoverageSqFtPerLb > 0 ? quoteCoat.CoverageSqFtPerLb : 30m;
|
||||
var eff = quoteCoat.TransferEfficiency > 0 ? quoteCoat.TransferEfficiency / 100m : 0.65m;
|
||||
var powderToOrder = (quoteCoat.PowderToOrder > 0)
|
||||
? quoteCoat.PowderToOrder
|
||||
: (quoteItem.SurfaceAreaSqFt > 0
|
||||
? Math.Round((quoteItem.SurfaceAreaSqFt * quoteItem.Quantity) / (cov * eff), 2)
|
||||
: (decimal?)null);
|
||||
|
||||
await _unitOfWork.JobItemCoats.AddAsync(new JobItemCoat
|
||||
{
|
||||
JobItemId = jobItem.Id,
|
||||
CoatName = quoteCoat.CoatName,
|
||||
Sequence = quoteCoat.Sequence,
|
||||
InventoryItemId = quoteCoat.InventoryItemId,
|
||||
ColorName = colorName,
|
||||
VendorId = quoteCoat.VendorId,
|
||||
ColorCode = colorCode,
|
||||
Finish = finish,
|
||||
CoverageSqFtPerLb = quoteCoat.CoverageSqFtPerLb,
|
||||
TransferEfficiency = quoteCoat.TransferEfficiency,
|
||||
PowderCostPerLb = quoteCoat.PowderCostPerLb,
|
||||
PowderToOrder = powderToOrder,
|
||||
Notes = quoteCoat.Notes,
|
||||
CompanyId = job.CompanyId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
foreach (var prepService in _jobItemAssemblyService.CreateJobItemPrepServices(quoteItem, jobItem.Id, job.CompanyId, createdAtUtc))
|
||||
{
|
||||
await _unitOfWork.JobItemPrepServices.AddAsync(prepService);
|
||||
}
|
||||
}
|
||||
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
// Aggregate prep services from all quote items and copy to job
|
||||
var quoteItemIds = fullItems.Select(qi => qi.Id).ToList();
|
||||
var itemPrepServices = await _unitOfWork.QuoteItemPrepServices.FindAsync(
|
||||
ps => quoteItemIds.Contains(ps.QuoteItemId));
|
||||
foreach (var prepServiceId in itemPrepServices.Select(ps => ps.PrepServiceId).Distinct())
|
||||
// Aggregate prep services from the fully-loaded quote items and copy to job
|
||||
foreach (var prepServiceId in fullItems
|
||||
.SelectMany(qi => qi.PrepServices)
|
||||
.Where(ps => !ps.IsDeleted)
|
||||
.Select(ps => ps.PrepServiceId)
|
||||
.Distinct())
|
||||
{
|
||||
await _unitOfWork.JobPrepServices.AddAsync(new JobPrepService
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user