Move invoice PAID stamp inline to header; add email to company block

- Remove watermark overlay layer; PAID badge now sits centered between
  the header row and the accent rule so it never obscures line items
- Add PrimaryContactEmail to company info block in header
- Remove ComposePaidStamp helper (no longer needed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 16:39:11 -04:00
parent b8057295ec
commit 66c3febd7a
@@ -98,12 +98,7 @@ public class PdfService : IPdfService
page.DefaultTextStyle(x => x.FontSize(10).FontFamily("Arial"));
page.Header().Element(c => ComposeInvoiceHeader(c, companyLogo, companyInfo, accentColor, invoiceDto));
page.Content().Layers(layers =>
{
layers.PrimaryLayer().Element(c => ComposeInvoiceContent(c, invoiceDto, accentColor, template));
if (invoiceDto.Status == InvoiceStatus.Paid)
layers.Layer().Element(c => ComposePaidStamp(c));
});
page.Content().Element(c => ComposeInvoiceContent(c, invoiceDto, accentColor, template));
page.Footer().AlignCenter().Text(text =>
{
text.CurrentPageNumber();
@@ -148,6 +143,8 @@ public class PdfService : IPdfService
column.Item().Text(cityLine).FontSize(9).FontColor(Colors.Grey.Darken1);
if (!string.IsNullOrWhiteSpace(companyInfo.Phone))
column.Item().Text(FormatPhoneNumber(companyInfo.Phone)).FontSize(9).FontColor(Colors.Grey.Darken1);
if (!string.IsNullOrWhiteSpace(companyInfo.PrimaryContactEmail))
column.Item().Text(companyInfo.PrimaryContactEmail).FontSize(9).FontColor(Colors.Grey.Darken1);
});
row.RelativeItem().AlignRight().Column(column =>
@@ -161,31 +158,20 @@ public class PdfService : IPdfService
});
});
if (invoice.Status == InvoiceStatus.Paid)
{
col.Item().PaddingVertical(6).AlignCenter().Column(badge =>
{
badge.Item().AlignCenter().Border(2).BorderColor(Colors.Green.Darken1)
.PaddingVertical(4).PaddingHorizontal(24)
.Text("PAID").FontSize(18).Bold().FontColor(Colors.Green.Darken1).LetterSpacing(0.15f);
});
}
col.Item().PaddingVertical(4).LineHorizontal(1).LineColor(accentColor);
});
}
/// <summary>
/// Renders a semi-transparent angled PAID stamp centred over the invoice content layer.
/// Uses QuestPDF layout primitives (AlignCenter, AlignMiddle, Rotate, Opacity) so no
/// external Skia/SkiaSharp dependency is needed.
/// </summary>
private static void ComposePaidStamp(IContainer container)
{
container
.AlignCenter()
.AlignMiddle()
.Rotate(-45f)
.Border(2)
.BorderColor(Colors.Green.Lighten2)
.PaddingVertical(10)
.PaddingHorizontal(20)
.Text("PAID")
.FontSize(52)
.Bold()
.FontColor(Colors.Green.Lighten2);
}
/// <summary>
/// Composes the body of the invoice PDF: bill-to address block, job reference, alternating-row
/// line-item table, and a right-aligned totals block that conditionally shows discount, tax,