- Migration: services.price_cents, services.availability_text
- Migration: bookings.receipt_number (trigger annuale IANNI-YYYY-NNNN) + receipt_token
- Constraint EXCLUDE bookings_no_overlap (sala unica, status confirmed/completed)
- availability.py: calcolo slot globale (non più per-provider)
- 16 servizi reali da Servizi.xls inseriti, 9 demo archiviati con FK preservata
- provider_services: 3 profili orari (lun-sab 9-19, lun-mar 9-13, lun-gio 9-13)
- Endpoint GET /api/receipts/{token} → PDF WeasyPrint
- Template HTML ricevuta con palette Ianni
- Dockerfile: deps sistema weasyprint (pango/cairo/fonts)
- requirements: +weasyprint>=63.0
- Frontend index.html: prezzo + fascia oraria nelle card servizio, link Scarica ricevuta nella conferma
55 lines
1.9 KiB
Python
55 lines
1.9 KiB
Python
"""Generazione ricevuta PDF per booking."""
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from zoneinfo import ZoneInfo
|
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
from weasyprint import HTML
|
|
|
|
TZ = ZoneInfo("Europe/Rome")
|
|
TEMPLATE_DIR = Path(__file__).resolve().parent.parent / "templates"
|
|
|
|
_env = Environment(
|
|
loader=FileSystemLoader(str(TEMPLATE_DIR)),
|
|
autoescape=select_autoescape(["html"]),
|
|
)
|
|
|
|
_WEEKDAY_IT = ["lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato", "domenica"]
|
|
_MONTH_IT = ["", "gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno",
|
|
"luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre"]
|
|
|
|
|
|
def _fmt_datetime(dt: datetime) -> str:
|
|
d = dt.astimezone(TZ)
|
|
return f"{_WEEKDAY_IT[d.weekday()]} {d.day} {_MONTH_IT[d.month]} {d.year} alle ore {d:%H:%M}"
|
|
|
|
|
|
def _fmt_now() -> str:
|
|
d = datetime.now(TZ)
|
|
return f"{d.day} {_MONTH_IT[d.month]} {d.year} alle ore {d:%H:%M}"
|
|
|
|
|
|
def _price_str(price_cents):
|
|
if price_cents is None:
|
|
return "—"
|
|
return f"€ {price_cents/100:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
|
|
|
|
|
|
def render_receipt_pdf(booking, service, provider, settings_map: dict) -> bytes:
|
|
"""Costruisce il PDF della ricevuta. settings_map è {key: value} dalla tabella settings."""
|
|
template = _env.get_template("receipt.html")
|
|
html = template.render(
|
|
booking=booking,
|
|
service=service,
|
|
provider=provider,
|
|
pharmacy={
|
|
"name": settings_map.get("pharmacy_name", "Farmacia Ianni SNC"),
|
|
"address": settings_map.get("pharmacy_address", ""),
|
|
"phone": settings_map.get("pharmacy_phone", ""),
|
|
"email": settings_map.get("smtp_from", ""),
|
|
},
|
|
start_str=_fmt_datetime(booking.start_at),
|
|
now_str=_fmt_now(),
|
|
price_str=_price_str(service.price_cents),
|
|
)
|
|
return HTML(string=html).write_pdf()
|