diff --git a/src/shared/shared/pdf.py b/src/shared/shared/pdf.py index df337d3..01278b1 100644 --- a/src/shared/shared/pdf.py +++ b/src/shared/shared/pdf.py @@ -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()) diff --git a/tests/test_pdf.py b/tests/test_pdf.py index 57a28f4..d0b74d0 100644 --- a/tests/test_pdf.py +++ b/tests/test_pdf.py @@ -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