Inline item editing on details pages; fix Stripe receipt_email
Allow description, quantity, and price to be edited inline on Quote, Job, and Invoice details pages without re-opening the wizard. Coating and prep service rows remain read-only by design. Invoice editing is gated to Draft/Sent/Overdue statuses; totals update live in the DOM. Remove receipt_email from Stripe PaymentIntent creation so customers can use any email they choose at checkout — Stripe validates format and sends the receipt to whatever the customer enters in the Payment Element, eliminating the risk of a stored email mismatch blocking a payment from processing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -341,9 +341,9 @@
|
||||
@foreach (var item in catalogItems)
|
||||
{
|
||||
var catIdx = allItems.IndexOf(item);
|
||||
<tr>
|
||||
<tr data-item-id="@item.Id">
|
||||
<td>
|
||||
<strong>@item.Description</strong>
|
||||
<span data-inline-field="description" data-raw-value="@item.Description"><strong>@item.Description</strong></span>
|
||||
@if (item.Coats != null && item.Coats.Any())
|
||||
{
|
||||
<br />
|
||||
@@ -399,9 +399,9 @@
|
||||
<br /><small class="text-muted"><i class="bi bi-sticky me-1"></i>@item.Notes</small>
|
||||
}
|
||||
</td>
|
||||
<td class="text-center">@item.Quantity</td>
|
||||
<td class="text-end">@item.UnitPrice.ToString("C")</td>
|
||||
<td class="text-end fw-semibold">@item.TotalPrice.ToString("C")</td>
|
||||
<td class="text-center"><span data-inline-field="quantity" data-raw-value="@item.Quantity">@item.Quantity</span></td>
|
||||
<td class="text-end"><span data-inline-field="unitPrice" data-raw-value="@item.UnitPrice">@item.UnitPrice.ToString("C")</span></td>
|
||||
<td class="text-end fw-semibold" data-line-total>@item.TotalPrice.ToString("C")</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="openWizard(@catIdx)" title="Edit"><i class="bi bi-pencil"></i></button>
|
||||
@@ -440,6 +440,7 @@
|
||||
{
|
||||
var custIdx = allItems.IndexOf(item);
|
||||
// Use stored PowderToOrder per coat; fall back to calculating from efficiency data
|
||||
// Note: row has data-item-id for inline editing
|
||||
decimal totalPowderNeeded = 0;
|
||||
if (item.Coats != null && item.Coats.Any(c => c.PowderToOrder > 0))
|
||||
{
|
||||
@@ -458,9 +459,9 @@
|
||||
{
|
||||
totalPowderNeeded = (item.SurfaceAreaSqFt * item.Quantity) / (30m * 0.65m);
|
||||
}
|
||||
<tr>
|
||||
<tr data-item-id="@item.Id">
|
||||
<td>
|
||||
<strong>@item.Description</strong>
|
||||
<span data-inline-field="description" data-raw-value="@item.Description"><strong>@item.Description</strong></span>
|
||||
@if (item.RequiresSandblasting || item.RequiresMasking)
|
||||
{
|
||||
<br />
|
||||
@@ -528,7 +529,7 @@
|
||||
<br /><small class="text-muted"><i class="bi bi-sticky me-1"></i>@item.Notes</small>
|
||||
}
|
||||
</td>
|
||||
<td class="text-center">@item.Quantity</td>
|
||||
<td class="text-center"><span data-inline-field="quantity" data-raw-value="@item.Quantity">@item.Quantity</span></td>
|
||||
<td class="text-center">
|
||||
@if (item.SurfaceAreaSqFt > 0)
|
||||
{
|
||||
@@ -553,8 +554,8 @@
|
||||
}
|
||||
else { <span class="text-muted">—</span> }
|
||||
</td>
|
||||
<td class="text-end">@item.UnitPrice.ToString("C")</td>
|
||||
<td class="text-end fw-semibold">@item.TotalPrice.ToString("C")</td>
|
||||
<td class="text-end"><span data-inline-field="unitPrice" data-raw-value="@item.UnitPrice">@item.UnitPrice.ToString("C")</span></td>
|
||||
<td class="text-end fw-semibold" data-line-total>@item.TotalPrice.ToString("C")</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="openWizard(@custIdx)" title="Edit"><i class="bi bi-pencil"></i></button>
|
||||
@@ -590,15 +591,15 @@
|
||||
@foreach (var item in laborItems)
|
||||
{
|
||||
var labIdx = allItems.IndexOf(item);
|
||||
<tr>
|
||||
<tr data-item-id="@item.Id">
|
||||
<td>
|
||||
<strong>@item.Description</strong>
|
||||
<span data-inline-field="description" data-raw-value="@item.Description"><strong>@item.Description</strong></span>
|
||||
@if (!string.IsNullOrEmpty(item.Notes))
|
||||
{
|
||||
<br /><small class="text-muted"><i class="bi bi-sticky me-1"></i>@item.Notes</small>
|
||||
}
|
||||
</td>
|
||||
<td class="text-center">@item.Quantity</td>
|
||||
<td class="text-center"><span data-inline-field="quantity" data-raw-value="@item.Quantity">@item.Quantity</span></td>
|
||||
<td class="text-center">
|
||||
@if (item.EstimatedMinutes > 0)
|
||||
{
|
||||
@@ -606,8 +607,8 @@
|
||||
}
|
||||
else { <span class="text-muted">—</span> }
|
||||
</td>
|
||||
<td class="text-end">@item.UnitPrice.ToString("C")</td>
|
||||
<td class="text-end fw-semibold">@item.TotalPrice.ToString("C")</td>
|
||||
<td class="text-end"><span data-inline-field="unitPrice" data-raw-value="@item.UnitPrice">@item.UnitPrice.ToString("C")</span></td>
|
||||
<td class="text-end fw-semibold" data-line-total>@item.TotalPrice.ToString("C")</td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="openWizard(@labIdx)" title="Edit"><i class="bi bi-pencil"></i></button>
|
||||
@@ -1688,7 +1689,7 @@
|
||||
}
|
||||
<div class="d-flex justify-content-between fw-bold border-top pt-2 mt-1">
|
||||
<span>Total</span>
|
||||
<span>@jobPb.Total.ToString("C")</span>
|
||||
<span class="job-final-price-display">@jobPb.Total.ToString("C")</span>
|
||||
</div>
|
||||
@{
|
||||
var jobTotalDirectCost = jobPb.MaterialCosts + jobPb.LaborCosts + jobPb.EquipmentCosts + jobPb.OvenBatchCost + jobPb.FacilityOverheadCost + jobPb.ShopSuppliesAmount;
|
||||
@@ -1717,7 +1718,7 @@
|
||||
}
|
||||
<div>
|
||||
<label class="text-muted small mb-1">Final Price</label>
|
||||
<h3 class="mb-0 text-primary">@Model.FinalPrice.ToString("C")</h3>
|
||||
<h3 class="mb-0 text-primary job-final-price-display">@Model.FinalPrice.ToString("C")</h3>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -2415,6 +2416,16 @@
|
||||
<link rel="stylesheet" href="~/css/job-photos.css" />
|
||||
<script src="~/js/job-photos.js" asp-append-version="true"></script>
|
||||
<script src="~/js/customer-change.js" asp-append-version="true"></script>
|
||||
<script src="~/js/inline-item-edit.js" asp-append-version="true"></script>
|
||||
<script>
|
||||
window.inlineItemEdit = {
|
||||
patchUrl: '@Url.Action("PatchItem", "Jobs")',
|
||||
canEdit: true,
|
||||
totals: {
|
||||
finalPrice: '.job-final-price-display'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script>
|
||||
// -- Inline date editing ----------------------------------------------
|
||||
const jobId = @Model.Id;
|
||||
|
||||
Reference in New Issue
Block a user