Add BatchId to GiftCertificate for persistent bulk batch tracking
BatchId (Guid?) is stamped on every certificate in a bulk run so the batch is permanently addressable. BulkResult is now a bookmarkable GET by batchId rather than TempData, so users can return to re-download at any time. BatchDownloadPdf is a GET link (no form POST needed). Index shows a Batch badge on bulk certs that links directly back to the batch result page. Migration: AddGiftCertificateBatchId Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ public class GiftCertificateListDto
|
|||||||
public GiftCertificateStatus Status { get; set; }
|
public GiftCertificateStatus Status { get; set; }
|
||||||
public DateTime IssueDate { get; set; }
|
public DateTime IssueDate { get; set; }
|
||||||
public DateTime? ExpiryDate { get; set; }
|
public DateTime? ExpiryDate { get; set; }
|
||||||
|
public Guid? BatchId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GiftCertificateDto : GiftCertificateListDto
|
public class GiftCertificateDto : GiftCertificateListDto
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ public class GiftCertificate : BaseEntity
|
|||||||
/// <summary>Set when this GC was sold via an invoice line item.</summary>
|
/// <summary>Set when this GC was sold via an invoice line item.</summary>
|
||||||
public int? SourceInvoiceItemId { get; set; }
|
public int? SourceInvoiceItemId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Groups all certificates created in a single bulk run. Null for individually issued certs.</summary>
|
||||||
|
public Guid? BatchId { get; set; }
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
public virtual Customer? RecipientCustomer { get; set; }
|
public virtual Customer? RecipientCustomer { get; set; }
|
||||||
public virtual Customer? PurchasingCustomer { get; set; }
|
public virtual Customer? PurchasingCustomer { get; set; }
|
||||||
|
|||||||
Generated
+10754
File diff suppressed because it is too large
Load Diff
+71
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PowderCoating.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddGiftCertificateBatchId : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "BatchId",
|
||||||
|
table: "GiftCertificates",
|
||||||
|
type: "uniqueidentifier",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "PricingTiers",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "CreatedAt",
|
||||||
|
value: new DateTime(2026, 5, 15, 0, 30, 26, 297, DateTimeKind.Utc).AddTicks(7656));
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "PricingTiers",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
column: "CreatedAt",
|
||||||
|
value: new DateTime(2026, 5, 15, 0, 30, 26, 297, DateTimeKind.Utc).AddTicks(7662));
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "PricingTiers",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
column: "CreatedAt",
|
||||||
|
value: new DateTime(2026, 5, 15, 0, 30, 26, 297, DateTimeKind.Utc).AddTicks(7664));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BatchId",
|
||||||
|
table: "GiftCertificates");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "PricingTiers",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "CreatedAt",
|
||||||
|
value: new DateTime(2026, 5, 14, 20, 43, 43, 116, DateTimeKind.Utc).AddTicks(7475));
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "PricingTiers",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
column: "CreatedAt",
|
||||||
|
value: new DateTime(2026, 5, 14, 20, 43, 43, 116, DateTimeKind.Utc).AddTicks(7481));
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "PricingTiers",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
column: "CreatedAt",
|
||||||
|
value: new DateTime(2026, 5, 14, 20, 43, 43, 116, DateTimeKind.Utc).AddTicks(7482));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3290,6 +3290,9 @@ namespace PowderCoating.Infrastructure.Migrations
|
|||||||
|
|
||||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<Guid?>("BatchId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
b.Property<string>("CertificateCode")
|
b.Property<string>("CertificateCode")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("nvarchar(450)");
|
.HasColumnType("nvarchar(450)");
|
||||||
@@ -6708,7 +6711,7 @@ namespace PowderCoating.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
CompanyId = 0,
|
CompanyId = 0,
|
||||||
CreatedAt = new DateTime(2026, 5, 14, 20, 43, 43, 116, DateTimeKind.Utc).AddTicks(7475),
|
CreatedAt = new DateTime(2026, 5, 15, 0, 30, 26, 273, DateTimeKind.Utc).AddTicks(2464),
|
||||||
Description = "Standard pricing for regular customers",
|
Description = "Standard pricing for regular customers",
|
||||||
DiscountPercent = 0m,
|
DiscountPercent = 0m,
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
@@ -6719,7 +6722,7 @@ namespace PowderCoating.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2,
|
||||||
CompanyId = 0,
|
CompanyId = 0,
|
||||||
CreatedAt = new DateTime(2026, 5, 14, 20, 43, 43, 116, DateTimeKind.Utc).AddTicks(7481),
|
CreatedAt = new DateTime(2026, 5, 15, 0, 30, 26, 273, DateTimeKind.Utc).AddTicks(2473),
|
||||||
Description = "5% discount for preferred customers",
|
Description = "5% discount for preferred customers",
|
||||||
DiscountPercent = 5m,
|
DiscountPercent = 5m,
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
@@ -6730,7 +6733,7 @@ namespace PowderCoating.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
Id = 3,
|
Id = 3,
|
||||||
CompanyId = 0,
|
CompanyId = 0,
|
||||||
CreatedAt = new DateTime(2026, 5, 14, 20, 43, 43, 116, DateTimeKind.Utc).AddTicks(7482),
|
CreatedAt = new DateTime(2026, 5, 15, 0, 30, 26, 273, DateTimeKind.Utc).AddTicks(2474),
|
||||||
Description = "10% discount for premium customers",
|
Description = "10% discount for premium customers",
|
||||||
DiscountPercent = 10m,
|
DiscountPercent = 10m,
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ public class GiftCertificatesController : Controller
|
|||||||
IssuedReason = gc.IssuedReason,
|
IssuedReason = gc.IssuedReason,
|
||||||
Status = gc.Status,
|
Status = gc.Status,
|
||||||
IssueDate = gc.IssueDate,
|
IssueDate = gc.IssueDate,
|
||||||
ExpiryDate = gc.ExpiryDate
|
ExpiryDate = gc.ExpiryDate,
|
||||||
|
BatchId = gc.BatchId
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -487,7 +488,7 @@ public class GiftCertificatesController : Controller
|
|||||||
discountAcctId = acct?.Id;
|
discountAcctId = acct?.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
var createdIds = new List<int>();
|
var batchId = Guid.NewGuid();
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
for (int i = 0; i < dto.Quantity; i++)
|
for (int i = 0; i < dto.Quantity; i++)
|
||||||
@@ -507,12 +508,12 @@ public class GiftCertificatesController : Controller
|
|||||||
IssuedById = currentUser?.Id,
|
IssuedById = currentUser?.Id,
|
||||||
CompanyId = companyId,
|
CompanyId = companyId,
|
||||||
CreatedAt = now,
|
CreatedAt = now,
|
||||||
CreatedBy = currentUser?.Email
|
CreatedBy = currentUser?.Email,
|
||||||
|
BatchId = batchId
|
||||||
};
|
};
|
||||||
|
|
||||||
await _unitOfWork.GiftCertificates.AddAsync(cert);
|
await _unitOfWork.GiftCertificates.AddAsync(cert);
|
||||||
await _unitOfWork.CompleteAsync();
|
await _unitOfWork.CompleteAsync();
|
||||||
createdIds.Add(cert.Id);
|
|
||||||
|
|
||||||
await _accountBalanceService.CreditAsync(gcLiabilityAcctId, cert.OriginalAmount);
|
await _accountBalanceService.CreditAsync(gcLiabilityAcctId, cert.OriginalAmount);
|
||||||
if (dto.IssuedReason == GiftCertificateIssuedReason.Sold)
|
if (dto.IssuedReason == GiftCertificateIssuedReason.Sold)
|
||||||
@@ -521,10 +522,7 @@ public class GiftCertificatesController : Controller
|
|||||||
await _accountBalanceService.DebitAsync(discountAcctId, cert.OriginalAmount);
|
await _accountBalanceService.DebitAsync(discountAcctId, cert.OriginalAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
TempData["BulkCertIds"] = System.Text.Json.JsonSerializer.Serialize(createdIds);
|
return RedirectToAction(nameof(BulkResult), new { batchId });
|
||||||
TempData["BulkCertCount"] = dto.Quantity;
|
|
||||||
TempData["BulkCertAmount"] = dto.Amount.ToString("F2");
|
|
||||||
return RedirectToAction(nameof(BulkResult));
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -535,31 +533,30 @@ public class GiftCertificatesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Displays the bulk creation confirmation page with the list of generated certificate codes
|
/// Displays the batch confirmation page. Driven by BatchId so it is bookmarkable and survives
|
||||||
/// and a button to download all as a single print-ready PDF.
|
/// browser back/refresh — the user can return here any time to re-download the batch PDF.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<IActionResult> BulkResult()
|
public async Task<IActionResult> BulkResult(Guid batchId)
|
||||||
{
|
{
|
||||||
if (TempData["BulkCertIds"] is not string json)
|
if (batchId == Guid.Empty)
|
||||||
return RedirectToAction(nameof(Index));
|
return RedirectToAction(nameof(Index));
|
||||||
|
|
||||||
var ids = System.Text.Json.JsonSerializer.Deserialize<List<int>>(json) ?? new();
|
var certs = await _unitOfWork.GiftCertificates.FindAsync(
|
||||||
var certs = await _unitOfWork.GiftCertificates.FindAsync(gc => ids.Contains(gc.Id), false);
|
gc => gc.BatchId == batchId, false);
|
||||||
|
|
||||||
|
if (!certs.Any())
|
||||||
|
return RedirectToAction(nameof(Index));
|
||||||
|
|
||||||
ViewBag.CertCount = TempData["BulkCertCount"];
|
|
||||||
ViewBag.CertAmount = TempData["BulkCertAmount"];
|
|
||||||
ViewBag.CertIds = ids;
|
|
||||||
return View(certs.OrderBy(c => c.CertificateCode).ToList());
|
return View(certs.OrderBy(c => c.CertificateCode).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates and streams a single PDF containing one page per certificate in the batch.
|
/// Streams a multi-page PDF for an entire batch identified by BatchId. GET endpoint so the
|
||||||
/// The certificate IDs are posted as a form array so there is no URL-length limit on batch size.
|
/// user can bookmark or re-open it at any time after the batch was originally created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost, ValidateAntiForgeryToken]
|
public async Task<IActionResult> BatchDownloadPdf(Guid batchId)
|
||||||
public async Task<IActionResult> BulkDownloadPdf(int[] certIds)
|
|
||||||
{
|
{
|
||||||
if (certIds == null || certIds.Length == 0)
|
if (batchId == Guid.Empty)
|
||||||
return BadRequest();
|
return BadRequest();
|
||||||
|
|
||||||
var currentUser = await _userManager.GetUserAsync(User);
|
var currentUser = await _userManager.GetUserAsync(User);
|
||||||
@@ -578,9 +575,12 @@ public class GiftCertificatesController : Controller
|
|||||||
};
|
};
|
||||||
|
|
||||||
var certs = await _unitOfWork.GiftCertificates.FindAsync(
|
var certs = await _unitOfWork.GiftCertificates.FindAsync(
|
||||||
gc => certIds.Contains(gc.Id), false,
|
gc => gc.BatchId == batchId, false,
|
||||||
gc => gc.RecipientCustomer);
|
gc => gc.RecipientCustomer);
|
||||||
|
|
||||||
|
if (!certs.Any())
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
var dtos = certs.OrderBy(c => c.CertificateCode).Select(cert => new GiftCertificateDto
|
var dtos = certs.OrderBy(c => c.CertificateCode).Select(cert => new GiftCertificateDto
|
||||||
{
|
{
|
||||||
Id = cert.Id,
|
Id = cert.Id,
|
||||||
@@ -603,18 +603,18 @@ public class GiftCertificatesController : Controller
|
|||||||
{
|
{
|
||||||
var (logoData, logoContentType) = await LoadCompanyLogoAsync(company);
|
var (logoData, logoContentType) = await LoadCompanyLogoAsync(company);
|
||||||
var pdfBytes = await _pdfService.GenerateBulkGiftCertificatePdfAsync(dtos, logoData, logoContentType, companyInfo);
|
var pdfBytes = await _pdfService.GenerateBulkGiftCertificatePdfAsync(dtos, logoData, logoContentType, companyInfo);
|
||||||
var firstName = dtos.FirstOrDefault()?.CertificateCode ?? "batch";
|
var first = dtos.First().CertificateCode;
|
||||||
var lastName = dtos.LastOrDefault()?.CertificateCode ?? string.Empty;
|
var last = dtos.Last().CertificateCode;
|
||||||
var fileName = dtos.Count == 1
|
var fileName = dtos.Count == 1
|
||||||
? $"GiftCertificate-{firstName}.pdf"
|
? $"GiftCertificate-{first}.pdf"
|
||||||
: $"GiftCertificates-{firstName}-to-{lastName}.pdf";
|
: $"GiftCertificates-{first}-to-{last}.pdf";
|
||||||
return File(pdfBytes, "application/pdf", fileName);
|
return File(pdfBytes, "application/pdf", fileName);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error generating bulk gift certificate PDF");
|
_logger.LogError(ex, "Error generating batch gift certificate PDF for batch {BatchId}", batchId);
|
||||||
TempData["Error"] = "Could not generate PDF.";
|
TempData["Error"] = "Could not generate PDF.";
|
||||||
return RedirectToAction(nameof(Index));
|
return RedirectToAction(nameof(BulkResult), new { batchId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,41 +2,35 @@
|
|||||||
@using PowderCoating.Core.Enums
|
@using PowderCoating.Core.Enums
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Batch Created";
|
ViewData["Title"] = "Batch Gift Certificates";
|
||||||
ViewData["PageIcon"] = "bi-gift";
|
ViewData["PageIcon"] = "bi-gift";
|
||||||
var count = ViewBag.CertCount as int? ?? Model.Count;
|
var batchId = Model.FirstOrDefault()?.BatchId ?? Guid.Empty;
|
||||||
var amount = ViewBag.CertAmount as string ?? "0.00";
|
var count = Model.Count;
|
||||||
var ids = ViewBag.CertIds as List<int> ?? Model.Select(c => c.Id).ToList();
|
var amount = Model.FirstOrDefault()?.OriginalAmount ?? 0m;
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="alert alert-success alert-permanent mb-4">
|
<div class="alert alert-success alert-permanent mb-4">
|
||||||
<i class="bi bi-check-circle-fill me-2"></i>
|
<i class="bi bi-check-circle-fill me-2"></i>
|
||||||
<strong>@count gift certificates created</strong> — each worth $@amount.
|
<strong>@count gift certificates created</strong> — each worth @amount.ToString("C").
|
||||||
Download the PDF below to print the full batch.
|
Download the PDF below to print the full batch. This page is bookmarkable — you can return here any time to re-download.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card border-0 shadow-sm mb-4">
|
<div class="card border-0 shadow-sm mb-4">
|
||||||
<div class="card-header bg-white border-bottom d-flex justify-content-between align-items-center py-3">
|
<div class="card-header bg-white border-bottom d-flex justify-content-between align-items-center py-3">
|
||||||
<h5 class="mb-0">
|
<h5 class="mb-0">
|
||||||
<i class="bi bi-collection me-2 text-primary"></i>Batch Certificates (@count)
|
<i class="bi bi-collection me-2 text-primary"></i>Batch Certificates (@count)
|
||||||
|
<span class="text-muted small fw-normal ms-2 font-monospace">@batchId.ToString("N")[..8]…</span>
|
||||||
</h5>
|
</h5>
|
||||||
<form asp-action="BulkDownloadPdf" method="post">
|
<a asp-action="BatchDownloadPdf" asp-route-batchId="@batchId" class="btn btn-primary">
|
||||||
@Html.AntiForgeryToken()
|
<i class="bi bi-file-pdf me-2"></i>Download All as PDF
|
||||||
@foreach (var id in ids)
|
</a>
|
||||||
{
|
|
||||||
<input type="hidden" name="certIds" value="@id" />
|
|
||||||
}
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<i class="bi bi-file-pdf me-2"></i>Download All as PDF
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
<div class="card-body p-0">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover mb-0">
|
<table class="table table-hover mb-0">
|
||||||
<thead class="table-light">
|
<thead class="table-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Certificate Code</th>
|
<th class="ps-3">Certificate Code</th>
|
||||||
<th>Face Value</th>
|
<th>Face Value</th>
|
||||||
<th>Issued</th>
|
<th>Issued</th>
|
||||||
<th>Expiry</th>
|
<th>Expiry</th>
|
||||||
@@ -48,7 +42,7 @@
|
|||||||
@foreach (var cert in Model)
|
@foreach (var cert in Model)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td class="fw-mono fw-semibold">@cert.CertificateCode</td>
|
<td class="ps-3 fw-semibold font-monospace">@cert.CertificateCode</td>
|
||||||
<td>@cert.OriginalAmount.ToString("C")</td>
|
<td>@cert.OriginalAmount.ToString("C")</td>
|
||||||
<td>@cert.IssueDate.ToLocalTime().ToString("MMM d, yyyy")</td>
|
<td>@cert.IssueDate.ToLocalTime().ToString("MMM d, yyyy")</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -58,7 +52,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td><span class="badge bg-success">Active</span></td>
|
<td><span class="badge bg-success">Active</span></td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<a asp-action="Details" asp-route-id="@cert.Id" class="btn btn-sm btn-outline-secondary">
|
<a asp-action="Details" asp-route-id="@cert.Id" class="btn btn-sm btn-outline-secondary" title="View details">
|
||||||
<i class="bi bi-eye"></i>
|
<i class="bi bi-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
<a asp-action="DownloadPdf" asp-route-id="@cert.Id" class="btn btn-sm btn-outline-secondary" title="Download single PDF">
|
<a asp-action="DownloadPdf" asp-route-id="@cert.Id" class="btn btn-sm btn-outline-secondary" title="Download single PDF">
|
||||||
@@ -75,15 +69,8 @@
|
|||||||
<a asp-action="Index" class="btn btn-outline-secondary">
|
<a asp-action="Index" class="btn btn-outline-secondary">
|
||||||
<i class="bi bi-arrow-left me-1"></i>Back to Gift Certificates
|
<i class="bi bi-arrow-left me-1"></i>Back to Gift Certificates
|
||||||
</a>
|
</a>
|
||||||
<form asp-action="BulkDownloadPdf" method="post">
|
<a asp-action="BatchDownloadPdf" asp-route-batchId="@batchId" class="btn btn-primary">
|
||||||
@Html.AntiForgeryToken()
|
<i class="bi bi-printer me-2"></i>Print Batch PDF (@count pages)
|
||||||
@foreach (var id in ids)
|
</a>
|
||||||
{
|
|
||||||
<input type="hidden" name="certIds" value="@id" />
|
|
||||||
}
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<i class="bi bi-printer me-2"></i>Print Batch PDF (@count pages)
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -80,6 +80,14 @@ else
|
|||||||
<a asp-action="Details" asp-route-id="@cert.Id" class="fw-semibold text-decoration-none font-monospace">
|
<a asp-action="Details" asp-route-id="@cert.Id" class="fw-semibold text-decoration-none font-monospace">
|
||||||
@cert.CertificateCode
|
@cert.CertificateCode
|
||||||
</a>
|
</a>
|
||||||
|
@if (cert.BatchId.HasValue)
|
||||||
|
{
|
||||||
|
<a asp-action="BulkResult" asp-route-batchId="@cert.BatchId"
|
||||||
|
class="badge bg-primary-subtle text-primary text-decoration-none ms-1"
|
||||||
|
title="View & download batch">
|
||||||
|
<i class="bi bi-collection me-1"></i>Batch
|
||||||
|
</a>
|
||||||
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if (!string.IsNullOrEmpty(cert.RecipientName))
|
@if (!string.IsNullOrEmpty(cert.RecipientName))
|
||||||
@@ -88,7 +96,7 @@ else
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span class="text-muted">—</span>
|
<span class="text-muted">—</span>
|
||||||
}
|
}
|
||||||
@if (!string.IsNullOrEmpty(cert.RecipientEmail))
|
@if (!string.IsNullOrEmpty(cert.RecipientEmail))
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user