Files
booking-service/app/models.py
ECO a3f1d3291a v1.4.0 — 16 servizi reali Ianni, sala unica, ricevuta PDF
- 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
2026-05-14 12:31:45 +00:00

76 lines
2.9 KiB
Python

from sqlalchemy import Column, Integer, String, Boolean, Text, DateTime, ForeignKey, UniqueConstraint
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.database import Base
class Service(Base):
__tablename__ = "services"
id = Column(Integer, primary_key=True)
name = Column(Text, nullable=False)
slug = Column(Text, nullable=False, unique=True)
duration_min = Column(Integer, nullable=False, default=15)
description = Column(Text)
category = Column(Text, default="generale")
price_cents = Column(Integer)
availability_text = Column(Text)
active = Column(Boolean, default=True)
sort_order = Column(Integer, default=0)
created_at = Column(DateTime(timezone=True), server_default=func.now())
provider_services = relationship("ProviderService", back_populates="service")
bookings = relationship("Booking", back_populates="service")
class Provider(Base):
__tablename__ = "providers"
id = Column(Integer, primary_key=True)
name = Column(Text, nullable=False)
email = Column(Text)
phone = Column(Text)
google_calendar_id = Column(Text)
active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
provider_services = relationship("ProviderService", back_populates="provider")
bookings = relationship("Booking", back_populates="provider")
class ProviderService(Base):
__tablename__ = "provider_services"
__table_args__ = (UniqueConstraint("provider_id", "service_id"),)
id = Column(Integer, primary_key=True)
provider_id = Column(Integer, ForeignKey("providers.id", ondelete="CASCADE"))
service_id = Column(Integer, ForeignKey("services.id", ondelete="CASCADE"))
availability_rules = Column(JSONB, nullable=False, default=[])
provider = relationship("Provider", back_populates="provider_services")
service = relationship("Service", back_populates="provider_services")
class Booking(Base):
__tablename__ = "bookings"
id = Column(Integer, primary_key=True)
service_id = Column(Integer, ForeignKey("services.id"))
provider_id = Column(Integer, ForeignKey("providers.id"))
customer_name = Column(Text, nullable=False)
customer_phone = Column(Text, nullable=False)
customer_email = Column(Text)
start_at = Column(DateTime(timezone=True), nullable=False)
end_at = Column(DateTime(timezone=True), nullable=False)
status = Column(Text, default="confirmed")
google_event_id = Column(Text)
receipt_number = Column(Text)
receipt_token = Column(Text)
notes = Column(Text)
reminder_sent = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
service = relationship("Service", back_populates="bookings")
provider = relationship("Provider", back_populates="bookings")