From eb13283e76f01e7c982a4e330a9900d65da2c13e Mon Sep 17 00:00:00 2001 From: Scott Pouliot Date: Wed, 20 May 2026 22:58:26 -0400 Subject: [PATCH] Fix inline edit not updating pricing breakdown on Job Details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jobs/PatchItem now returns the full breakdown (itemsSubtotal, subtotalBeforeDiscount, subtotalAfterDiscount, taxAmount) so all rows in the pricing card update live without a page refresh. Added data-pb attributes to the matching spans in the pricing panel. Updated window.inlineItemEdit.totals config for jobs to map each response key to its DOM selector. updateTotals in inline-item-edit.js is now fully generic — cfg.totals keys must match server response property names directly, eliminating the old hardcoded tax/taxAmount and balance/balanceDue mismatches. Updated Quote and Invoice configs accordingly (tax→taxAmount, balance→balanceDue). Co-Authored-By: Claude Sonnet 4.6 --- .../Controllers/JobsController.cs | 13 +++++++++++-- .../Views/Invoices/Details.cshtml | 4 ++-- src/PowderCoating.Web/Views/Jobs/Details.cshtml | 14 +++++++++----- src/PowderCoating.Web/Views/Quotes/Details.cshtml | 6 +++--- .../wwwroot/js/inline-item-edit.js | 15 ++++++--------- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/PowderCoating.Web/Controllers/JobsController.cs b/src/PowderCoating.Web/Controllers/JobsController.cs index 2582725..13f404c 100644 --- a/src/PowderCoating.Web/Controllers/JobsController.cs +++ b/src/PowderCoating.Web/Controllers/JobsController.cs @@ -4264,9 +4264,18 @@ public class JobsController : Controller await _unitOfWork.Jobs.UpdateAsync(job); await _unitOfWork.CompleteAsync(); + // Deserialize again after possible re-serialization to get final values + QuotePricingBreakdownDto? pbFinal = null; + if (!string.IsNullOrEmpty(job.PricingBreakdownJson)) + pbFinal = JsonSerializer.Deserialize(job.PricingBreakdownJson); + return Json(new { - lineTotal = item.TotalPrice, - finalPrice = job.FinalPrice + lineTotal = item.TotalPrice, + finalPrice = job.FinalPrice, + itemsSubtotal = pbFinal?.ItemsSubtotal, + subtotalBeforeDiscount = pbFinal?.SubtotalBeforeDiscount, + subtotalAfterDiscount = pbFinal?.SubtotalAfterDiscount, + taxAmount = pbFinal?.TaxAmount }); } } diff --git a/src/PowderCoating.Web/Views/Invoices/Details.cshtml b/src/PowderCoating.Web/Views/Invoices/Details.cshtml index 1b13309..6a513f0 100644 --- a/src/PowderCoating.Web/Views/Invoices/Details.cshtml +++ b/src/PowderCoating.Web/Views/Invoices/Details.cshtml @@ -1458,9 +1458,9 @@ canEdit: @Json.Serialize(canEdit), totals: { subtotal: '#inv-subtotal', - tax: '#inv-tax', + taxAmount: '#inv-tax', total: '#inv-total', - balance: '#inv-balance' + balanceDue: '#inv-balance' } }; diff --git a/src/PowderCoating.Web/Views/Jobs/Details.cshtml b/src/PowderCoating.Web/Views/Jobs/Details.cshtml index 6b3fd55..eef2c54 100644 --- a/src/PowderCoating.Web/Views/Jobs/Details.cshtml +++ b/src/PowderCoating.Web/Views/Jobs/Details.cshtml @@ -1611,7 +1611,7 @@ }
Items subtotal - @jobPb.ItemsSubtotal.ToString("C") + @jobPb.ItemsSubtotal.ToString("C")
@@ -1660,7 +1660,7 @@
Subtotal - @jobPb.SubtotalBeforeDiscount.ToString("C") + @jobPb.SubtotalBeforeDiscount.ToString("C")
@if (jobPb.DiscountAmount > 0) { @@ -1670,7 +1670,7 @@
After discount - @jobPb.SubtotalAfterDiscount.ToString("C") + @jobPb.SubtotalAfterDiscount.ToString("C")
} @if (jobPb.RushFee > 0) @@ -1684,7 +1684,7 @@ {
Tax (@jobPb.TaxPercent.ToString("G29")%) - @jobPb.TaxAmount.ToString("C") + @jobPb.TaxAmount.ToString("C")
}
@@ -2422,7 +2422,11 @@ patchUrl: '@Url.Action("PatchItem", "Jobs")', canEdit: true, totals: { - finalPrice: '.job-final-price-display' + itemsSubtotal: '[data-pb="itemsSubtotal"]', + subtotalBeforeDiscount: '[data-pb="subtotalBeforeDiscount"]', + subtotalAfterDiscount: '[data-pb="subtotalAfterDiscount"]', + taxAmount: '[data-pb="taxAmount"]', + finalPrice: '.job-final-price-display' } }; diff --git a/src/PowderCoating.Web/Views/Quotes/Details.cshtml b/src/PowderCoating.Web/Views/Quotes/Details.cshtml index 59f5941..4c893f5 100644 --- a/src/PowderCoating.Web/Views/Quotes/Details.cshtml +++ b/src/PowderCoating.Web/Views/Quotes/Details.cshtml @@ -2268,9 +2268,9 @@ patchUrl: '@Url.Action("PatchItem", "Quotes")', canEdit: true, totals: { - subtotal: '#quote-subtotal', - tax: '#quote-tax', - total: '#quote-total' + subtotal: '#quote-subtotal', + taxAmount: '#quote-tax', + total: '#quote-total' } }; diff --git a/src/PowderCoating.Web/wwwroot/js/inline-item-edit.js b/src/PowderCoating.Web/wwwroot/js/inline-item-edit.js index b7d37b0..dffac4f 100644 --- a/src/PowderCoating.Web/wwwroot/js/inline-item-edit.js +++ b/src/PowderCoating.Web/wwwroot/js/inline-item-edit.js @@ -24,17 +24,14 @@ setTimeout(() => el.remove(), 4000); } + // cfg.totals maps response-property-name → CSS selector. + // Each key must exactly match a property returned by the server's PatchItem action. function updateTotals(cfg, data) { const t = cfg.totals || {}; - [ - [t.subtotal, data.subtotal], - [t.tax, data.taxAmount], - [t.total, data.total], - [t.finalPrice, data.finalPrice], - [t.balance, data.balanceDue], - ].forEach(([sel, val]) => { - if (sel && val !== undefined && val !== null) { - document.querySelectorAll(sel).forEach(el => { el.textContent = fmt(val); }); + Object.entries(t).forEach(([key, selector]) => { + const val = data[key]; + if (selector && val !== undefined && val !== null) { + document.querySelectorAll(selector).forEach(el => { el.textContent = fmt(val); }); } }); }