- 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
97 lines
2.0 KiB
Python
97 lines
2.0 KiB
Python
from pydantic import BaseModel, Field
|
|
from datetime import datetime, date
|
|
from typing import Optional
|
|
|
|
|
|
# === Services ===
|
|
class ServiceOut(BaseModel):
|
|
id: int
|
|
name: str
|
|
slug: str
|
|
duration_min: int
|
|
description: Optional[str] = None
|
|
category: str
|
|
price_cents: Optional[int] = None
|
|
availability_text: Optional[str] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ServiceCreate(BaseModel):
|
|
name: str
|
|
slug: str
|
|
duration_min: int = 15
|
|
description: Optional[str] = None
|
|
category: str = "generale"
|
|
price_cents: Optional[int] = None
|
|
availability_text: Optional[str] = None
|
|
|
|
|
|
# === Providers ===
|
|
class ProviderOut(BaseModel):
|
|
id: int
|
|
name: str
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ProviderCreate(BaseModel):
|
|
name: str
|
|
email: Optional[str] = None
|
|
phone: Optional[str] = None
|
|
google_calendar_id: Optional[str] = None
|
|
|
|
|
|
# === Availability ===
|
|
class TimeSlot(BaseModel):
|
|
start: str # "09:00"
|
|
end: str # "09:30"
|
|
provider_id: int
|
|
provider_name: str
|
|
|
|
|
|
class AvailabilityRule(BaseModel):
|
|
weekday: int # 0=lun, 6=dom
|
|
start: str # "09:00"
|
|
end: str # "13:00"
|
|
|
|
|
|
# === Bookings ===
|
|
class BookingCreate(BaseModel):
|
|
service_id: int
|
|
provider_id: int
|
|
start_at: datetime
|
|
customer_name: str
|
|
customer_phone: str
|
|
customer_email: Optional[str] = None
|
|
notes: Optional[str] = None
|
|
|
|
|
|
class BookingOut(BaseModel):
|
|
id: int
|
|
service_id: int
|
|
provider_id: int
|
|
customer_name: str
|
|
customer_phone: str
|
|
customer_email: Optional[str] = None
|
|
start_at: datetime
|
|
end_at: datetime
|
|
status: str
|
|
notes: Optional[str] = None
|
|
created_at: datetime
|
|
receipt_number: Optional[str] = None
|
|
receipt_token: Optional[str] = None
|
|
|
|
service: Optional[ServiceOut] = None
|
|
provider: Optional[ProviderOut] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class BookingUpdate(BaseModel):
|
|
status: Optional[str] = None
|
|
notes: Optional[str] = None
|