fix(pdf): fit weekly summaries on one page without a broken title (#138)
Some checks failed
Build Lambda Layer / build (push) Has been cancelled

Helvetica cannot encode an em dash, so the header rendered a question mark. Tighter single-column spacing keeps a 12-person week on page 1 while still paginating large weeks.
This commit is contained in:
Adam Moussa 2026-08-14 20:17:27 -04:00 • committed by GitHub
parent 10c86f2de6
commit 58b9d4f83b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 20 deletions

View file

@ -50,37 +50,37 @@ def build_weekly_summary_pdf(
Raw PDF bytes. Contains no prices — strictly employee, item, quantity.
"""
pdf = FPDF(orientation="P", unit="mm", format="A4")
pdf.set_auto_page_break(auto=True, margin=15)
pdf.set_auto_page_break(auto=True, margin=10)
pdf.add_page()
# ─── Header ───────────────────────────────────────────────────
pdf.set_font("Helvetica", "B", 18)
pdf.set_font("Helvetica", "B", 14)
pdf.cell(
0,
10,
_latin1(f"Weekly Meal Order Summary — {week}"),
6,
_latin1(f"Weekly Meal Order Summary - {week}"),
new_x="LMARGIN",
new_y="NEXT",
)
pdf.set_font("Helvetica", "", 10)
pdf.set_font("Helvetica", "", 9)
pdf.set_text_color(110, 110, 110)
pdf.cell(
0,
6,
4,
_latin1(f"Generated {_format_generated_at(generated_at)}"),
new_x="LMARGIN",
new_y="NEXT",
)
pdf.cell(
0,
6,
4,
_latin1(f"{int(total_employees)} employees - {int(total_meals)} meals"),
new_x="LMARGIN",
new_y="NEXT",
)
pdf.set_text_color(0, 0, 0)
pdf.ln(4)
pdf.ln(1.5)
# ─── Per-employee sections ────────────────────────────────────
ordered = sorted(orders, key=lambda o: str(o.get("employee_name", "")).lower())
@ -88,21 +88,21 @@ def build_weekly_summary_pdf(
name = order.get("employee_name", "")
email = order.get("employee_email", "")
pdf.set_font("Helvetica", "B", 12)
pdf.cell(0, 7, _latin1(name), new_x="LMARGIN", new_y="NEXT")
pdf.set_font("Helvetica", "B", 10)
pdf.cell(0, 4.5, _latin1(name), new_x="LMARGIN", new_y="NEXT")
if email:
pdf.set_font("Helvetica", "", 9)
pdf.set_font("Helvetica", "", 8)
pdf.set_text_color(110, 110, 110)
pdf.cell(0, 5, _latin1(email), new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 3, _latin1(email), new_x="LMARGIN", new_y="NEXT")
pdf.set_text_color(0, 0, 0)
# Item / Qty table
pdf.set_font("Helvetica", "B", 9)
pdf.set_font("Helvetica", "B", 8)
pdf.set_fill_color(243, 244, 246)
pdf.cell(140, 7, "Item", border="B", fill=True)
pdf.cell(140, 4, "Item", border="B", fill=True)
pdf.cell(
30,
7,
4,
"Qty",
border="B",
align="R",
@ -111,18 +111,18 @@ def build_weekly_summary_pdf(
new_y="NEXT",
)
pdf.set_font("Helvetica", "", 10)
pdf.set_font("Helvetica", "", 9)
for item in order.get("items", []):
pdf.cell(140, 7, _latin1(item.get("name", "")), border="B")
pdf.cell(140, 4, _latin1(item.get("name", "")), border="B")
pdf.cell(
30,
7,
4,
str(int(item.get("quantity", 0))),
border="B",
align="R",
new_x="LMARGIN",
new_y="NEXT",
)
pdf.ln(5)
pdf.ln(1.5)
return bytes(pdf.output())

View file

@ -10,10 +10,16 @@ class _NoCompressFPDF(_RealFPDF):
"""FPDF subclass with stream compression off, so the content stream holds
readable text we can assert against without a PDF parser."""
last_pages_count = 0
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_compression(False)
def output(self, *args, **kwargs):
_NoCompressFPDF.last_pages_count = self.pages_count
return super().output(*args, **kwargs)
@pytest.fixture()
def readable_pdf(monkeypatch):
@ -50,6 +56,20 @@ def _build(orders, **overrides):
return build_weekly_summary_pdf(orders, **kwargs)
def _volume_orders(count, items_per_employee=2):
"""Synthetic orders for layout/page-count checks."""
return [
{
"employee_name": f"Employee {i:02d}",
"employee_email": f"emp{i:02d}@seahavenind.com",
"items": [
{"name": f"Meal {j}", "quantity": 1} for j in range(items_per_employee)
],
}
for i in range(count)
]
class TestPdfStructure:
def test_returns_pdf_bytes(self):
pdf = _build(_orders())
@ -66,7 +86,7 @@ class TestPdfContent:
def test_header_shows_week_and_counts(self, readable_pdf):
pdf = _build(_orders())
text = pdf.decode("latin-1")
assert "2026-W19" in text
assert "Weekly Meal Order Summary - 2026-W19" in text
assert "2 employees" in text
assert "6 meals" in text
@ -103,3 +123,16 @@ class TestPdfContent:
]
pdf = _build(orders)
assert pdf[:5] == b"%PDF-"
class TestPdfLayout:
def test_typical_week_fits_on_one_page(self, readable_pdf):
"""12 employees with 2 items each overflowed the pre-DEV-210 layout."""
pdf = _build(_volume_orders(12))
assert pdf[:5] == b"%PDF-"
assert _NoCompressFPDF.last_pages_count == 1
def test_high_volume_still_paginates(self, readable_pdf):
pdf = _build(_volume_orders(40))
assert pdf[:5] == b"%PDF-"
assert _NoCompressFPDF.last_pages_count >= 2