Fix company logo missing from PDFs and add AI photo save logging

When a tenant uploads a logo it is stored in Azure Blob Storage and
LogoData (the legacy DB byte[]) is cleared. All PDF controllers were
still reading the now-null LogoData, so logos never appeared on any
PDF after upload. Fixed by injecting ICompanyLogoService into all six
affected controllers (Quotes, Invoices, Deposits, GiftCertificates,
PurchaseOrders, CatalogItems) and loading the blob-stored logo first
before falling back to the legacy DB field.

Also added structured logging to the AI photo promotion path in
QuotesController Create/Edit POST so upload failures are visible in
production logs instead of silently swallowed.

Added onclick safety net to the Create and Edit quote submit buttons
so dynamically-injected hidden fields (AiPhotoTempIds) are written
before iOS Safari collects the form data on submit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 12:27:18 -04:00
parent ca4fb959aa
commit a8fb56e8ec
8 changed files with 231 additions and 22 deletions
@@ -40,6 +40,7 @@ namespace PowderCoating.Web.Controllers
private readonly ICatalogImageService _catalogImageService;
private readonly IAiCatalogPriceCheckService _priceCheckService;
private readonly IPlatformSettingsService _platformSettings;
private readonly ICompanyLogoService _logoService;
public CatalogItemsController(
IUnitOfWork unitOfWork,
@@ -52,7 +53,8 @@ namespace PowderCoating.Web.Controllers
ISubscriptionService subscriptionService,
ICatalogImageService catalogImageService,
IAiCatalogPriceCheckService priceCheckService,
IPlatformSettingsService platformSettings)
IPlatformSettingsService platformSettings,
ICompanyLogoService logoService)
{
_unitOfWork = unitOfWork;
_mapper = mapper;
@@ -65,6 +67,7 @@ namespace PowderCoating.Web.Controllers
_catalogImageService = catalogImageService;
_priceCheckService = priceCheckService;
_platformSettings = platformSettings;
_logoService = logoService;
}
/// <summary>
@@ -906,11 +909,12 @@ namespace PowderCoating.Web.Controllers
.ToList();
// Generate PDF
var (logoData, logoContentType) = await LoadCompanyLogoAsync(company);
var pdfBytes = await _pdfService.GenerateCatalogPdfAsync(
itemsByCategory,
company.CompanyName,
company.LogoData,
company.LogoContentType
logoData,
logoContentType
);
// Return PDF file
@@ -1146,6 +1150,17 @@ namespace PowderCoating.Web.Controllers
}
return parts.Count > 0 ? string.Join(" > ", parts) : "Uncategorized";
}
private async Task<(byte[]? LogoData, string? LogoContentType)> LoadCompanyLogoAsync(Company? company)
{
if (company == null) return (null, null);
if (!string.IsNullOrEmpty(company.LogoFilePath))
{
var (ok, content, contentType, _) = await _logoService.GetCompanyLogoAsync(company.LogoFilePath);
if (ok) return (content, contentType);
}
return (company.LogoData, company.LogoContentType);
}
}
// Helper class for hierarchical display