Files
PowderCoatingLogix/src/PowderCoating.Web/Views/PurchaseOrders/Details.cshtml
T
2026-04-23 21:38:24 -04:00

273 lines
14 KiB
Plaintext

@using PowderCoating.Application.DTOs.PurchaseOrder
@using PowderCoating.Core.Enums
@model PurchaseOrderDto
@{
ViewData["Title"] = $"PO {Model.PoNumber}";
string StatusBadge(PurchaseOrderStatus s) => s switch {
PurchaseOrderStatus.Draft => "secondary",
PurchaseOrderStatus.Submitted => "primary",
PurchaseOrderStatus.PartiallyReceived => "warning",
PurchaseOrderStatus.Received => "success",
PurchaseOrderStatus.Cancelled => "danger",
_ => "secondary"
};
}
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<div class="d-flex align-items-center gap-2">
<h4 class="mb-0">
@Model.PoNumber
<span class="badge bg-@StatusBadge(Model.Status) ms-2">@Model.Status</span>
@if (Model.IsOverdue)
{
<span class="badge bg-danger ms-1">Overdue</span>
}
</h4>
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
data-bs-title="Purchase Order"
data-bs-content="PO lifecycle: Draft (editable) → Submit → Receive Goods (updates inventory) → Create Bill (creates a payable bill in Accounts Payable). Partially Received means some items have arrived; you can receive multiple partial shipments. Once fully received you can create a bill to pay the vendor. Cancelled POs can be deleted.">
<i class="bi bi-question-circle"></i>
</a>
</div>
<small class="text-muted">@Model.VendorName</small>
</div>
<div class="d-flex gap-2">
<a asp-action="DownloadPdf" asp-route-id="@Model.Id" class="btn btn-sm btn-outline-secondary" target="_blank">
<i class="bi bi-file-earmark-pdf me-1"></i> Download PDF
</a>
<a asp-action="Index" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Back to List
</a>
</div>
</div>
<div class="row g-3">
<!-- Left: Line Items -->
<div class="col-lg-8">
<div class="card border-0 shadow-sm">
<div class="card-header bg-transparent fw-semibold">
<i class="bi bi-list-ul me-1"></i> Line Items
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table mb-0">
<thead class="table-light small">
<tr>
<th>Item</th>
<th>SKU</th>
<th class="text-center">Ordered</th>
<th class="text-center">Received</th>
<th class="text-end">Unit Cost</th>
<th class="text-end">Line Total</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Items)
{
<tr class="@(item.IsFullyReceived ? "table-success" : "")">
<td>
@item.ItemName
@if (!string.IsNullOrEmpty(item.Notes))
{
<br /><small class="text-muted">@item.Notes</small>
}
</td>
<td class="text-muted small">@item.ItemSKU</td>
<td class="text-center">@item.QuantityOrdered.ToString("G29") @item.UnitOfMeasure</td>
<td class="text-center">
@item.QuantityReceived.ToString("G29")
@if (item.IsFullyReceived)
{
<i class="bi bi-check-circle-fill text-success ms-1"></i>
}
</td>
<td class="text-end">$@item.UnitCost.ToString("N2")</td>
<td class="text-end fw-semibold">$@item.LineTotal.ToString("N2")</td>
</tr>
}
</tbody>
<tfoot class="table-light">
<tr>
<td colspan="5" class="text-end">Subtotal</td>
<td class="text-end fw-semibold">$@Model.SubTotal.ToString("N2")</td>
</tr>
@if (Model.ShippingCost > 0)
{
<tr>
<td colspan="5" class="text-end text-muted">Shipping</td>
<td class="text-end text-muted">$@Model.ShippingCost.ToString("N2")</td>
</tr>
}
<tr>
<td colspan="5" class="text-end fw-bold">Total</td>
<td class="text-end fw-bold fs-5">$@Model.TotalAmount.ToString("N2")</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
@if (!string.IsNullOrEmpty(Model.Notes))
{
<div class="card border-0 shadow-sm mt-3">
<div class="card-header bg-transparent fw-semibold"><i class="bi bi-chat-text me-1"></i> Notes</div>
<div class="card-body">@Model.Notes</div>
</div>
}
@if (!string.IsNullOrEmpty(Model.InternalNotes))
{
<div class="card border-0 shadow-sm mt-3 border-warning">
<div class="card-header bg-transparent fw-semibold text-warning"><i class="bi bi-lock me-1"></i> Internal Notes</div>
<div class="card-body">@Model.InternalNotes</div>
</div>
}
</div>
<!-- Right: Actions + Info -->
<div class="col-lg-4">
<!-- Actions -->
<div class="card border-0 shadow-sm mb-3">
<div class="card-header bg-transparent d-flex align-items-center gap-2">
<span class="fw-semibold">Actions</span>
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="left" data-bs-trigger="focus"
data-bs-title="Actions"
data-bs-content="Draft: Edit or Submit. Submit to lock line items and signal the order is placed. Submitted/Partially Received: Receive Goods to enter quantities arrived and update inventory. Cancel to void the order. Received: Create Bill converts this PO into a vendor bill for payment through Accounts Payable.">
<i class="bi bi-question-circle"></i>
</a>
</div>
<div class="card-body d-grid gap-2">
@if (Model.Status == PurchaseOrderStatus.Draft)
{
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-outline-primary">
<i class="bi bi-pencil me-1"></i> Edit
</a>
<form asp-action="Submit" asp-route-id="@Model.Id" method="post">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-primary w-100">
<i class="bi bi-send me-1"></i> Submit PO
</button>
</form>
<a asp-action="Delete" asp-route-id="@Model.Id" class="btn btn-outline-danger">
<i class="bi bi-trash me-1"></i> Delete
</a>
}
@if (Model.Status == PurchaseOrderStatus.Submitted || Model.Status == PurchaseOrderStatus.PartiallyReceived)
{
<a asp-action="Receive" asp-route-id="@Model.Id" class="btn btn-success">
<i class="bi bi-box-arrow-in-down me-1"></i> Receive Goods
</a>
<form asp-action="Cancel" asp-route-id="@Model.Id" method="post">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-outline-danger w-100"
onclick="return confirm('Cancel this purchase order?')">
<i class="bi bi-x-circle me-1"></i> Cancel PO
</button>
</form>
}
@if (Model.Status == PurchaseOrderStatus.Received)
{
<div class="text-center text-success">
<i class="bi bi-check-circle-fill fs-2"></i>
<p class="mb-0 fw-semibold">Fully Received</p>
@if (Model.ReceivedDate.HasValue)
{
<small class="text-muted">@Model.ReceivedDate.Value.ToString("MM/dd/yyyy")</small>
}
</div>
@if (Model.BillId.HasValue)
{
<a asp-controller="Bills" asp-action="Details" asp-route-id="@Model.BillId" class="btn btn-outline-primary w-100">
<i class="bi bi-receipt me-1"></i> View Bill @Model.BillNumber
</a>
}
else
{
<a asp-controller="Bills" asp-action="CreateFromPurchaseOrder" asp-route-purchaseOrderId="@Model.Id"
class="btn btn-primary w-100">
<i class="bi bi-receipt me-1"></i> Create Bill
</a>
}
}
@if (Model.Status == PurchaseOrderStatus.PartiallyReceived && !Model.BillId.HasValue)
{
<a asp-controller="Bills" asp-action="CreateFromPurchaseOrder" asp-route-purchaseOrderId="@Model.Id"
class="btn btn-outline-primary w-100">
<i class="bi bi-receipt me-1"></i> Create Bill (Partial)
</a>
}
@if (Model.BillId.HasValue && Model.Status == PurchaseOrderStatus.PartiallyReceived)
{
<a asp-controller="Bills" asp-action="Details" asp-route-id="@Model.BillId" class="btn btn-outline-primary w-100">
<i class="bi bi-receipt me-1"></i> View Bill @Model.BillNumber
</a>
}
@if (Model.Status == PurchaseOrderStatus.Cancelled)
{
<div class="text-center text-muted">
<i class="bi bi-x-circle fs-2"></i>
<p class="mb-0">Cancelled</p>
</div>
<a asp-action="Delete" asp-route-id="@Model.Id" class="btn btn-outline-danger btn-sm">
<i class="bi bi-trash me-1"></i> Delete
</a>
}
</div>
</div>
<!-- Vendor Info -->
<div class="card border-0 shadow-sm mb-3">
<div class="card-header bg-transparent fw-semibold"><i class="bi bi-truck me-1"></i> Vendor</div>
<div class="card-body small">
<p class="fw-semibold mb-1">@Model.VendorName</p>
@if (!string.IsNullOrEmpty(Model.VendorEmail))
{
<p class="mb-1"><i class="bi bi-envelope me-1 text-muted"></i> @Model.VendorEmail</p>
}
@if (!string.IsNullOrEmpty(Model.VendorPhone))
{
<p class="mb-0"><i class="bi bi-telephone me-1 text-muted"></i> @Model.VendorPhone</p>
}
</div>
</div>
<!-- Dates -->
<div class="card border-0 shadow-sm">
<div class="card-header bg-transparent d-flex align-items-center gap-2">
<span class="fw-semibold"><i class="bi bi-calendar3 me-1"></i> Dates</span>
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="left" data-bs-trigger="focus"
data-bs-title="Dates"
data-bs-content="Order Date is when the PO was placed. Expected Delivery is when goods are due — if today's date is past this and the PO hasn't been fully received, the row shows as Overdue. Received date is set automatically when you record receipt of all items.">
<i class="bi bi-question-circle"></i>
</a>
</div>
<div class="card-body small">
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Order Date</span>
<span>@Model.OrderDate.ToString("MM/dd/yyyy")</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Expected Delivery</span>
<span class="@(Model.IsOverdue ? "text-danger fw-semibold" : "")">
@(Model.ExpectedDeliveryDate?.ToString("MM/dd/yyyy") ?? "—")
</span>
</div>
@if (Model.ReceivedDate.HasValue)
{
<div class="d-flex justify-content-between">
<span class="text-muted">Received</span>
<span class="text-success">@Model.ReceivedDate.Value.ToString("MM/dd/yyyy")</span>
</div>
}
</div>
</div>
</div>
</div>