Fix incoming powder inventory: defer creation to approval, deduplicate, fix category
Three bugs fixed: 1. Wrong timing — inventory items with IsIncoming=true were auto-created during quote save (in QuotePricingAssemblyService). Now deferred to quote approval so inventory only reflects powders the shop is actually going to process. 2. Duplicate records — same powder on multiple items in one quote created multiple inventory records. Now grouped by PowderCatalogItemId: one record per unique catalog powder, all matching coats linked to the same record. 3. Wrong category — category resolution used first IsCoating=true by DisplayOrder, which could land items in Cerakote or other unintended categories. Now prefers CategoryCode==POWDER explicitly, with DisplayOrder fallback. Changes: - QuoteItemCoat: add PowderCatalogItemId int? — persists catalog reference at quote save time so the approval path knows what to create - QuotePricingAssemblyService.BuildQuoteItemCoatsAsync: store PowderCatalogItemId on coat instead of calling CreateIncomingInventoryItemAsync immediately - QuotePricingAssemblyService.CreateIncomingInventoryItemAsync: signature changed from (coatDto, companyId) to (catalogItemId, companyId); category lookup prefers POWDER code; no longer clears PowderCostPerLb on the DTO - QuotePricingAssemblyService.EnsureIncomingInventoryForApprovedQuoteAsync: new public method called at approval — loads pending coats, groups by catalog ID, creates one inventory item per group, links all coats in each group - IQuotePricingAssemblyService: exposes EnsureIncomingInventoryForApprovedQuoteAsync - QuotesController.ApproveQuote: calls EnsureIncomingInventory after save - QuotesController.ChangeQuoteStatus: calls EnsureIncomingInventory on Approved - QuoteApprovalController: injects IQuotePricingAssemblyService; calls EnsureIncomingInventory in ApproveInternal (customer-facing portal path) - InventoryController.CreateIncomingFromCatalog: same category fix (prefers POWDER) - Migration: AddPowderCatalogItemIdToCoat (nullable int on QuoteItemCoats) - Tests: updated AddAsIncoming test to verify deferred behavior; new deduplication test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2317,6 +2317,17 @@ public class QuotesController : Controller
|
||||
|
||||
_logger.LogInformation("Quote {QuoteId} approved by user {UserId}", id, currentUser.Id);
|
||||
|
||||
// Create incoming inventory records for any catalog-sourced coats that were deferred
|
||||
// from quote-save time. One record per unique powder catalog item, de-duplicated.
|
||||
try
|
||||
{
|
||||
await _quotePricingAssemblyService.EnsureIncomingInventoryForApprovedQuoteAsync(id, quote.CompanyId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "EnsureIncomingInventory failed for quote {QuoteId} — continuing", id);
|
||||
}
|
||||
|
||||
// Notify customer that quote is approved (only if user opted in)
|
||||
if (sendEmail)
|
||||
{
|
||||
@@ -2802,6 +2813,20 @@ public class QuotesController : Controller
|
||||
await _unitOfWork.Quotes.UpdateAsync(quote);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
// When transitioning to Approved: create incoming inventory records for catalog-sourced
|
||||
// coats that were deferred from quote-save time (one record per unique powder, deduplicated).
|
||||
if (newStatus.StatusCode == AppConstants.StatusCodes.Quote.Approved && oldStatusCode != AppConstants.StatusCodes.Quote.Approved)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _quotePricingAssemblyService.EnsureIncomingInventoryForApprovedQuoteAsync(request.QuoteId, quote.CompanyId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "EnsureIncomingInventory failed for quote {QuoteId} — continuing", request.QuoteId);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-create job when quote is approved — guard against double-conversion
|
||||
// (race condition: two simultaneous approval calls could both pass the oldStatusCode check)
|
||||
if (newStatus.StatusCode == AppConstants.StatusCodes.Quote.Approved && oldStatusCode != AppConstants.StatusCodes.Quote.Approved
|
||||
|
||||
Reference in New Issue
Block a user