mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-09-30 22:53:12 +00:00
Some checks are pending
Deploy / deploy (push) Waiting to run
Generate a per-person weekly summary PDF at Thursday close and store it
alongside the CSV reports, plus a client-side admin download that rolls
orders up into item -> total quantity for bulk ordering.
- shared/pdf.py: build_weekly_summary_pdf() via fpdf2 (pure-Python,
ARM64-safe; first non-boto3 layer dep). Per-person employee -> item ->
quantity, no pricing.
- aggregate_orders: write reports/{week}/weekly-summary-{week}.pdf
(application/pdf) and stamp weekly_summary_pdf_s3_key on the SUMMARY.
No new IAM (existing S3CrudPolicy). No email/Slack delivery.
- generate_form.py: "Download order list" admin button aggregates the
loaded week's orders into an item->qty CSV (no per-employee breakdown,
no prices) via a Blob download. Works for open weeks too.
- Tests: tests/test_pdf.py; aggregate happy-path now asserts 3 S3
uploads + the pdf key.
- README updated.
105 lines
3.4 KiB
Python
105 lines
3.4 KiB
Python
"""Unit tests for src/shared/shared/pdf.py (weekly summary PDF generator)."""
|
|
|
|
import pytest
|
|
from fpdf import FPDF as _RealFPDF
|
|
|
|
from shared.pdf import build_weekly_summary_pdf
|
|
|
|
|
|
class _NoCompressFPDF(_RealFPDF):
|
|
"""FPDF subclass with stream compression off, so the content stream holds
|
|
readable text we can assert against without a PDF parser."""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.set_compression(False)
|
|
|
|
|
|
@pytest.fixture()
|
|
def readable_pdf(monkeypatch):
|
|
"""Patch the generator to emit uncompressed (searchable) PDFs."""
|
|
monkeypatch.setattr("shared.pdf.FPDF", _NoCompressFPDF)
|
|
|
|
|
|
def _orders():
|
|
return [
|
|
{
|
|
"employee_name": "Bob Jones",
|
|
"employee_email": "bob@seahavenind.com",
|
|
"items": [
|
|
{"name": "Chicken Parm", "quantity": 2},
|
|
{"name": "Caesar Salad", "quantity": 1},
|
|
],
|
|
},
|
|
{
|
|
"employee_name": "Alice Smith",
|
|
"employee_email": "alice@seahavenind.com",
|
|
"items": [{"name": "Veggie Bowl", "quantity": 3}],
|
|
},
|
|
]
|
|
|
|
|
|
def _build(orders, **overrides):
|
|
kwargs = {
|
|
"week": "2026-W19",
|
|
"generated_at": "2026-05-22T16:35:40-04:00",
|
|
"total_employees": len(orders),
|
|
"total_meals": sum(i["quantity"] for o in orders for i in o["items"]),
|
|
}
|
|
kwargs.update(overrides)
|
|
return build_weekly_summary_pdf(orders, **kwargs)
|
|
|
|
|
|
class TestPdfStructure:
|
|
def test_returns_pdf_bytes(self):
|
|
pdf = _build(_orders())
|
|
assert isinstance(pdf, bytes)
|
|
assert pdf[:5] == b"%PDF-", "Output should be a valid PDF"
|
|
assert len(pdf) > 0
|
|
|
|
def test_empty_orders_still_valid_pdf(self):
|
|
pdf = _build([], total_employees=0, total_meals=0)
|
|
assert pdf[:5] == b"%PDF-"
|
|
|
|
|
|
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 "2 employees" in text
|
|
assert "6 meals" in text
|
|
|
|
def test_each_employee_and_item_present(self, readable_pdf):
|
|
orders = _orders()
|
|
pdf = _build(orders)
|
|
text = pdf.decode("latin-1")
|
|
for order in orders:
|
|
assert order["employee_name"] in text, f"missing {order['employee_name']}"
|
|
assert order["employee_email"] in text
|
|
for item in order["items"]:
|
|
assert item["name"] in text, f"missing item {item['name']}"
|
|
|
|
def test_no_prices_anywhere(self, readable_pdf):
|
|
"""Per issue #16: strictly employee, item, qty — never a price."""
|
|
orders = _orders()
|
|
# Even if price fields are present on the items, they must not be rendered.
|
|
for o in orders:
|
|
for i in o["items"]:
|
|
i["price"] = 12.50
|
|
i["subtotal"] = 99.99
|
|
pdf = _build(orders)
|
|
text = pdf.decode("latin-1")
|
|
assert "$" not in text, "PDF must contain no currency symbol"
|
|
assert "12.50" not in text and "99.99" not in text, "PDF must not render prices"
|
|
|
|
def test_non_latin1_chars_do_not_crash(self):
|
|
orders = [
|
|
{
|
|
"employee_name": "José 🍜 Ramírez",
|
|
"employee_email": "jose@seahavenind.com",
|
|
"items": [{"name": "Phở Bowl", "quantity": 1}],
|
|
}
|
|
]
|
|
pdf = _build(orders)
|
|
assert pdf[:5] == b"%PDF-"
|