a0bdd2b5b4
Replace all corruption variants with HTML entities across 226 view files: - 3-char UTF-8-as-Win1252 sequences (ae-corruption) - Standalone smart/curly quotes that break C# Razor expressions - Partially re-corrupted variants where the 3rd byte was normalised to ASCII tools/Fix-Encoding.ps1: re-runnable sweep; uses [char] code points so the script itself never contains a literal non-ASCII character; supports -DryRun .githooks/pre-commit: blocks commits containing the ae-corruption byte signature (xc3xa2xe2x82xac); git core.hooksPath = .githooks so the hook is repo-committed and active for all future work on this machine. Build clean; 225 unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
180 lines
8.5 KiB
Plaintext
180 lines
8.5 KiB
Plaintext
@using PowderCoating.Application.DTOs.PurchaseOrder
|
|
@model UpdatePurchaseOrderDto
|
|
|
|
@{
|
|
ViewData["Title"] = $"Edit {ViewBag.PoNumber}";
|
|
int poId = (int)ViewBag.PoId;
|
|
}
|
|
|
|
@section Scripts {
|
|
<script id="inventoryItemsData" type="application/json">@Html.Raw(ViewBag.InventoryItemsJson ?? "[]")</script>
|
|
<script id="existingItemsData" type="application/json">@Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.Items.Select(i => new {
|
|
type = i.InventoryItemId.HasValue ? "inventory" : "custom",
|
|
selectedId = i.InventoryItemId,
|
|
description = i.Description,
|
|
uom = i.UnitOfMeasure,
|
|
qty = i.QuantityOrdered,
|
|
cost = i.UnitCost,
|
|
notes = i.Notes
|
|
})))</script>
|
|
<script src="~/js/purchase-orders.js"></script>
|
|
}
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h4 class="mb-0">Edit @ViewBag.PoNumber</h4>
|
|
<a asp-action="Details" asp-route-id="@poId" class="btn btn-sm btn-outline-secondary">
|
|
<i class="bi bi-arrow-left"></i> Cancel
|
|
</a>
|
|
</div>
|
|
|
|
<div class="alert alert-info alert-permanent d-flex align-items-center gap-2 mb-3">
|
|
<i class="bi bi-info-circle-fill"></i>
|
|
<span>Only <strong>Draft</strong> purchase orders can be edited.</span>
|
|
</div>
|
|
|
|
@if (!ViewData.ModelState.IsValid)
|
|
{
|
|
<div class="alert alert-danger alert-permanent">
|
|
<ul class="mb-0">
|
|
@foreach (var error in ViewData.ModelState.Values.SelectMany(v => v.Errors))
|
|
{
|
|
<li>@error.ErrorMessage</li>
|
|
}
|
|
</ul>
|
|
</div>
|
|
}
|
|
|
|
<form asp-action="Edit" asp-route-id="@poId" method="post">
|
|
@Html.AntiForgeryToken()
|
|
|
|
<div class="row g-3">
|
|
<div class="col-lg-8">
|
|
<div class="card border-0 shadow-sm mb-3">
|
|
<div class="card-header bg-transparent fw-semibold">Order Details</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label asp-for="VendorId" class="form-label fw-semibold">Vendor <span class="text-danger">*</span></label>
|
|
<select asp-for="VendorId" asp-items="@ViewBag.Vendors" class="form-select"
|
|
data-quick-add-url="/Vendors/Create" data-quick-add-title="Add New Vendor">
|
|
<option value="__new__">+ Add New Vendor…</option>
|
|
</select>
|
|
<span asp-validation-for="VendorId" class="text-danger small"></span>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label asp-for="OrderDate" class="form-label fw-semibold">Order Date</label>
|
|
<input asp-for="OrderDate" type="date" class="form-control"
|
|
value="@Model.OrderDate.ToString("yyyy-MM-dd")" />
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label asp-for="ExpectedDeliveryDate" class="form-label fw-semibold">Expected Delivery</label>
|
|
<input asp-for="ExpectedDeliveryDate" type="date" class="form-control"
|
|
value="@Model.ExpectedDeliveryDate?.ToString("yyyy-MM-dd")" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-sm mb-3">
|
|
<div class="card-header bg-transparent d-flex justify-content-between align-items-center">
|
|
<span class="fw-semibold">Line Items</span>
|
|
<button type="button" class="btn btn-sm btn-primary" onclick="addItem()">
|
|
<i class="bi bi-plus-lg"></i> Add Item
|
|
</button>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div id="emptyItemsMessage" class="text-center py-4 text-muted"
|
|
style="display:@(Model.Items.Any() ? "none" : "block")">
|
|
<i class="bi bi-cart-plus d-block" style="font-size:2rem;"></i>
|
|
<p class="mb-0 mt-1 small">Click "Add Item" to add line items.</p>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table mb-0">
|
|
<thead class="table-light small" id="lineItemsHeader"
|
|
style="display:@(Model.Items.Any() ? "" : "none")">
|
|
<tr>
|
|
<th style="min-width:240px">Item / Description</th>
|
|
<th style="width:95px">Qty</th>
|
|
<th style="width:110px">Unit Cost</th>
|
|
<th style="width:100px" class="text-end">Line Total</th>
|
|
<th>Notes</th>
|
|
<th style="width:50px"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="lineItemsBody">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label asp-for="Notes" class="form-label fw-semibold">Notes</label>
|
|
<textarea asp-for="Notes" class="form-control" rows="3"
|
|
placeholder="Visible on PO…"></textarea>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label asp-for="InternalNotes" class="form-label fw-semibold">Internal Notes</label>
|
|
<textarea asp-for="InternalNotes" class="form-control" rows="3"
|
|
placeholder="Internal use only…"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<div class="card border-0 shadow-sm mb-3">
|
|
<div class="card-header bg-transparent fw-semibold">Totals</div>
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span class="text-muted">Subtotal</span>
|
|
<span id="subTotalDisplay" class="fw-semibold">$0.00</span>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label asp-for="ShippingCost" class="form-label small text-muted">Shipping Cost</label>
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text">$</span>
|
|
<input asp-for="ShippingCost" id="shippingCostInput" type="number"
|
|
class="form-control" min="0" step="0.01" oninput="updateTotals()" />
|
|
</div>
|
|
</div>
|
|
<hr />
|
|
<div class="d-flex justify-content-between">
|
|
<span class="fw-bold">Grand Total</span>
|
|
<span id="grandTotalDisplay" class="fw-bold fs-5">$0.00</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-grid gap-2">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="bi bi-save me-1"></i> Save Changes
|
|
</button>
|
|
<a asp-action="Details" asp-route-id="@poId" class="btn btn-outline-secondary">Cancel</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const existing = JSON.parse(document.getElementById('existingItemsData')?.textContent || '[]');
|
|
const tbody = document.getElementById('lineItemsBody');
|
|
const header = document.getElementById('lineItemsHeader');
|
|
existing.forEach(item => {
|
|
const i = itemIndex++;
|
|
const tr = document.createElement('tr');
|
|
tr.setAttribute('data-index', i);
|
|
tr.innerHTML = buildRowHtml(i, item);
|
|
tbody.appendChild(tr);
|
|
});
|
|
if (header) header.style.display = existing.length > 0 ? '' : 'none';
|
|
toggleEmptyMessage();
|
|
updateTotals();
|
|
});
|
|
</script>
|