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:
@@ -107,7 +107,8 @@ public class GiftCertificatesController : Controller
|
||||
IssuedReason = gc.IssuedReason,
|
||||
Status = gc.Status,
|
||||
IssueDate = gc.IssueDate,
|
||||
ExpiryDate = gc.ExpiryDate
|
||||
ExpiryDate = gc.ExpiryDate,
|
||||
BatchId = gc.BatchId
|
||||
})
|
||||
.ToList();
|
||||
|
||||
@@ -487,7 +488,7 @@ public class GiftCertificatesController : Controller
|
||||
discountAcctId = acct?.Id;
|
||||
}
|
||||
|
||||
var createdIds = new List<int>();
|
||||
var batchId = Guid.NewGuid();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
for (int i = 0; i < dto.Quantity; i++)
|
||||
@@ -507,12 +508,12 @@ public class GiftCertificatesController : Controller
|
||||
IssuedById = currentUser?.Id,
|
||||
CompanyId = companyId,
|
||||
CreatedAt = now,
|
||||
CreatedBy = currentUser?.Email
|
||||
CreatedBy = currentUser?.Email,
|
||||
BatchId = batchId
|
||||
};
|
||||
|
||||
await _unitOfWork.GiftCertificates.AddAsync(cert);
|
||||
await _unitOfWork.CompleteAsync();
|
||||
createdIds.Add(cert.Id);
|
||||
|
||||
await _accountBalanceService.CreditAsync(gcLiabilityAcctId, cert.OriginalAmount);
|
||||
if (dto.IssuedReason == GiftCertificateIssuedReason.Sold)
|
||||
@@ -521,10 +522,7 @@ public class GiftCertificatesController : Controller
|
||||
await _accountBalanceService.DebitAsync(discountAcctId, cert.OriginalAmount);
|
||||
}
|
||||
|
||||
TempData["BulkCertIds"] = System.Text.Json.JsonSerializer.Serialize(createdIds);
|
||||
TempData["BulkCertCount"] = dto.Quantity;
|
||||
TempData["BulkCertAmount"] = dto.Amount.ToString("F2");
|
||||
return RedirectToAction(nameof(BulkResult));
|
||||
return RedirectToAction(nameof(BulkResult), new { batchId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -535,31 +533,30 @@ public class GiftCertificatesController : Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays the bulk creation confirmation page with the list of generated certificate codes
|
||||
/// and a button to download all as a single print-ready PDF.
|
||||
/// Displays the batch confirmation page. Driven by BatchId so it is bookmarkable and survives
|
||||
/// browser back/refresh — the user can return here any time to re-download the batch PDF.
|
||||
/// </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));
|
||||
|
||||
var ids = System.Text.Json.JsonSerializer.Deserialize<List<int>>(json) ?? new();
|
||||
var certs = await _unitOfWork.GiftCertificates.FindAsync(gc => ids.Contains(gc.Id), false);
|
||||
var certs = await _unitOfWork.GiftCertificates.FindAsync(
|
||||
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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates and streams a single PDF containing one page per certificate in the batch.
|
||||
/// The certificate IDs are posted as a form array so there is no URL-length limit on batch size.
|
||||
/// Streams a multi-page PDF for an entire batch identified by BatchId. GET endpoint so the
|
||||
/// user can bookmark or re-open it at any time after the batch was originally created.
|
||||
/// </summary>
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> BulkDownloadPdf(int[] certIds)
|
||||
public async Task<IActionResult> BatchDownloadPdf(Guid batchId)
|
||||
{
|
||||
if (certIds == null || certIds.Length == 0)
|
||||
if (batchId == Guid.Empty)
|
||||
return BadRequest();
|
||||
|
||||
var currentUser = await _userManager.GetUserAsync(User);
|
||||
@@ -578,9 +575,12 @@ public class GiftCertificatesController : Controller
|
||||
};
|
||||
|
||||
var certs = await _unitOfWork.GiftCertificates.FindAsync(
|
||||
gc => certIds.Contains(gc.Id), false,
|
||||
gc => gc.BatchId == batchId, false,
|
||||
gc => gc.RecipientCustomer);
|
||||
|
||||
if (!certs.Any())
|
||||
return NotFound();
|
||||
|
||||
var dtos = certs.OrderBy(c => c.CertificateCode).Select(cert => new GiftCertificateDto
|
||||
{
|
||||
Id = cert.Id,
|
||||
@@ -603,18 +603,18 @@ public class GiftCertificatesController : Controller
|
||||
{
|
||||
var (logoData, logoContentType) = await LoadCompanyLogoAsync(company);
|
||||
var pdfBytes = await _pdfService.GenerateBulkGiftCertificatePdfAsync(dtos, logoData, logoContentType, companyInfo);
|
||||
var firstName = dtos.FirstOrDefault()?.CertificateCode ?? "batch";
|
||||
var lastName = dtos.LastOrDefault()?.CertificateCode ?? string.Empty;
|
||||
var first = dtos.First().CertificateCode;
|
||||
var last = dtos.Last().CertificateCode;
|
||||
var fileName = dtos.Count == 1
|
||||
? $"GiftCertificate-{firstName}.pdf"
|
||||
: $"GiftCertificates-{firstName}-to-{lastName}.pdf";
|
||||
? $"GiftCertificate-{first}.pdf"
|
||||
: $"GiftCertificates-{first}-to-{last}.pdf";
|
||||
return File(pdfBytes, "application/pdf", fileName);
|
||||
}
|
||||
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.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
return RedirectToAction(nameof(BulkResult), new { batchId });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user