Restore all zeroed views + add bulk gift certificate creation
The HTML entity sweep script had a bug where it wrote empty files for any
view that contained no target Unicode characters, zeroing out 215 view files.
All views restored from the pre-sweep commit (cefdf3e).
Bulk gift certificate feature:
- BulkCreateGiftCertificateDto with Quantity (1-500), Amount, Reason, Expiry, Notes
- GenerateBulkGiftCertificatePdfAsync on IPdfService / PdfService: one Letter page
per cert, reusing the same purple/gold branded ComposeGiftCertificateContent helper
- GiftCertificatesController: BulkCreate GET/POST, BulkResult GET, BulkDownloadPdf POST
- Views: BulkCreate.cshtml (form with live total preview), BulkResult.cshtml (table +
Download All PDF button that POSTs cert IDs to avoid URL length limits)
- gift-certificate-bulk.js: live preview + spinner/disable on submit
- Index.cshtml: Bulk Create button added alongside New Certificate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
@model PowderCoating.Application.DTOs.Accounting.ExpenseDto
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Expense {Model.ExpenseNumber}";
|
||||
ViewData["PageIcon"] = "bi-receipt";
|
||||
ViewData["PageHelpTitle"] = "Expense";
|
||||
ViewData["PageHelpContent"] = "A direct purchase paid at the time of transaction. Expense Account shows what was bought; Paid From shows which bank/cash account was debited. Edit to correct any details. Delete permanently removes the record — there is no Void for expenses.";
|
||||
}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a asp-action="Index" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i></a>
|
||||
<span class="text-muted small">@Model.Date.ToString("MMMM d, yyyy")</span>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-pencil me-1"></i>Edit
|
||||
</a>
|
||||
<form asp-action="Delete" asp-route-id="@Model.Id" method="post"
|
||||
onsubmit="return confirm('Delete expense @Model.ExpenseNumber?')">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-outline-danger">
|
||||
<i class="bi bi-trash me-1"></i>Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (TempData["Success"] != null)
|
||||
{
|
||||
<div class="alert alert-success alert-dismissible fade show">
|
||||
<i class="bi bi-check-circle me-2"></i>@TempData["Success"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-7">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header fw-semibold d-flex align-items-center gap-2">
|
||||
Expense Details
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Expense Details"
|
||||
data-bs-content="Expense Account: the category debited (what was purchased). Paid From: the bank or cash account that was credited. Payment Method: how it was paid. If a Job is linked, this expense contributes to that job's cost tracking.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-4 text-muted">Date</dt>
|
||||
<dd class="col-sm-8">@Model.Date.ToString("MMMM d, yyyy")</dd>
|
||||
|
||||
<dt class="col-sm-4 text-muted">Expense Account</dt>
|
||||
<dd class="col-sm-8">
|
||||
<code>@Model.ExpenseAccountNumber</code> @Model.ExpenseAccountName
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-4 text-muted">Paid From</dt>
|
||||
<dd class="col-sm-8">@Model.PaymentAccountName</dd>
|
||||
|
||||
<dt class="col-sm-4 text-muted">Payment Method</dt>
|
||||
<dd class="col-sm-8">@Model.PaymentMethod</dd>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.VendorName))
|
||||
{
|
||||
<dt class="col-sm-4 text-muted">Vendor</dt>
|
||||
<dd class="col-sm-8">@Model.VendorName</dd>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.JobNumber))
|
||||
{
|
||||
<dt class="col-sm-4 text-muted">Job</dt>
|
||||
<dd class="col-sm-8">@Model.JobNumber</dd>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.Memo))
|
||||
{
|
||||
<dt class="col-sm-4 text-muted">Memo</dt>
|
||||
<dd class="col-sm-8">@Model.Memo</dd>
|
||||
}
|
||||
|
||||
<dt class="col-sm-4 text-muted">Recorded</dt>
|
||||
<dd class="col-sm-8 text-muted small">@Model.CreatedAt.ToString("MMM d, yyyy h:mm tt")</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-5">
|
||||
<div class="card shadow-sm border-primary mb-3">
|
||||
<div class="card-body text-center py-4">
|
||||
<p class="text-muted mb-1 small">Amount</p>
|
||||
<p class="display-5 fw-bold text-primary mb-0">@Model.Amount.ToString("C")</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Receipt -->
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header fw-semibold d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span><i class="bi bi-receipt me-1"></i>Receipt</span>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="left" data-bs-trigger="focus"
|
||||
data-bs-title="Receipt"
|
||||
data-bs-content="Attached receipt image or PDF for this expense. Click to view full size. Use the trash icon to remove it. To replace the receipt, use Edit and upload a new file.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(Model.ReceiptFilePath))
|
||||
{
|
||||
<form asp-action="DeleteReceipt" asp-route-id="@Model.Id" method="post"
|
||||
onsubmit="return confirm('Remove receipt?')">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (!string.IsNullOrEmpty(Model.ReceiptFilePath))
|
||||
{
|
||||
var ext = System.IO.Path.GetExtension(Model.ReceiptFilePath).ToLowerInvariant();
|
||||
if (ext == ".pdf")
|
||||
{
|
||||
<div class="text-center py-3">
|
||||
<i class="bi bi-file-earmark-pdf text-danger" style="font-size:3rem;"></i>
|
||||
<p class="small text-muted mt-2 mb-3">PDF receipt attached</p>
|
||||
<a asp-action="ViewReceipt" asp-route-id="@Model.Id"
|
||||
class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-download me-1"></i>Download PDF
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="#" data-bs-toggle="modal" data-bs-target="#receiptModal" style="cursor:zoom-in;">
|
||||
<img src="@Url.Action("ViewReceipt", new { id = Model.Id })"
|
||||
alt="Receipt" class="img-fluid rounded" style="max-height:300px;object-fit:contain;" />
|
||||
</a>
|
||||
<div class="text-center mt-2">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm"
|
||||
data-bs-toggle="modal" data-bs-target="#receiptModal">
|
||||
<i class="bi bi-arrows-fullscreen me-1"></i>View Full Size
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-center py-3 text-muted">
|
||||
<i class="bi bi-image fs-2 d-block mb-2"></i>
|
||||
<p class="small mb-2">No receipt attached</p>
|
||||
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-upload me-1"></i>Upload Receipt
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.ReceiptFilePath) &&
|
||||
System.IO.Path.GetExtension(Model.ReceiptFilePath).ToLowerInvariant() != ".pdf")
|
||||
{
|
||||
<div class="modal fade" id="receiptModal" tabindex="-1" aria-labelledby="receiptModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="receiptModalLabel">
|
||||
<i class="bi bi-receipt me-2"></i>Receipt — @Model.ExpenseNumber
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-2">
|
||||
<img src="@Url.Action("ViewReceipt", new { id = Model.Id })"
|
||||
alt="Receipt" class="img-fluid" style="max-height:80vh;object-fit:contain;" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a asp-action="ViewReceipt" asp-route-id="@Model.Id" target="_blank"
|
||||
class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-box-arrow-up-right me-1"></i>Open in New Tab
|
||||
</a>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user