This commit is contained in:
2025-09-28 19:13:01 +02:00
parent 49edf780b5
commit 541ecb48f2
67 changed files with 5176 additions and 5008 deletions

View File

@@ -0,0 +1,29 @@
from app.core.database import Base
from sqlalchemy import Column, Integer, String, DateTime, Enum, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
import enum
class AuditAction(enum.Enum):
booking_create = "booking_create"
booking_delete = "booking_delete"
topup_create = "topup_create"
# Weitere Aktionen können hier ergänzt werden, z.B.:
# topup_confirm = "topup_confirm"
# user_update = "user_update"
class AuditLog(Base):
__tablename__ = "audit_logs"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
action = Column(String, nullable=False)
amount_cents = Column(Integer, nullable=True)
old_balance_cents = Column(Integer, nullable=True)
new_balance_cents = Column(Integer, nullable=True)
info = Column(String, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
user = relationship("User")

View File

@@ -0,0 +1,18 @@
from sqlalchemy import Column, Integer, ForeignKey, DateTime, String
from sqlalchemy.orm import relationship
from app.core.database import Base
from datetime import datetime
class Booking(Base):
__tablename__ = "bookings"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
amount = Column(Integer, nullable=False) # Anzahl der Produkte in dieser Buchung
total_cents = Column(Integer, nullable=False) # Gesamtsumme in Cent (Preis * Anzahl)
comment = Column(String, nullable=True)
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False) # Zeitpunkt der Buchung
user = relationship("User")
product = relationship("Product")

View File

@@ -0,0 +1,11 @@
from sqlalchemy import Column, String, Text, DateTime, func
from app.core.database import Base
class Config(Base):
__tablename__ = "config"
key = Column(String(64), primary_key=True, index=True)
value = Column(Text, nullable=False, default="")
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)

View File

@@ -0,0 +1,22 @@
from app.core.database import Base
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text
from sqlalchemy.orm import relationship
from datetime import datetime
class Delivery(Base):
__tablename__ = "deliveries"
id = Column(Integer, primary_key=True, index=True)
product_id = Column(Integer, ForeignKey("products.id", ondelete="CASCADE"), nullable=False, index=True)
amount = Column(Integer, nullable=False, default=0) # gelieferte Menge (Stück / Einheiten)
price_cents = Column(Integer, nullable=False, default=0) # Einkaufspreis in Cent (gesamt oder pro Einheit je nach Modell)
delivered_at = Column(DateTime(timezone=True), nullable=True) # Lieferdatum
supplier = Column(String, nullable=True) # Lieferant (frei)
invoice_number = Column(String, nullable=True) # Rechnungsnummer
created_by = Column(Integer, nullable=True) # optional: User-ID, der die Lieferung erfasst hat
deposit_return_cents = Column(Integer, nullable=False, server_default="0")
note = Column(Text, nullable=True) # oder String(1000)
# Rückverknüpfung zum Produkt
product = relationship("Product", back_populates="deliveries")

View File

@@ -0,0 +1,31 @@
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from sqlalchemy.sql import func
from sqlalchemy.orm import relationship
from app.core.database import Base
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False, unique=True, index=True)
category = Column(String, nullable=True, index=True)
volume_ml = Column(Integer, nullable=False)
price_cents = Column(Integer, nullable=False)
supplier_number = Column(String, nullable=True)
image_url = Column(String, nullable=True)
stock = Column(Integer, nullable=False, default=0)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
pack_size = Column(Integer, nullable=False, default=1) # z.B. 6, 12, 24
purchase_price_cents = Column(Integer, nullable=False, default=0)
# >>> NEU: Gegenbeziehung zur Delivery-Relation
deliveries = relationship(
"Delivery",
back_populates="product",
cascade="all, delete-orphan",
lazy="selectin",
)

View File

@@ -0,0 +1,14 @@
from app.core.database import Base
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship
from datetime import datetime
class Session(Base):
__tablename__ = "sessions"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
token = Column(String(64), unique=True, nullable=False, index=True)
created_at = Column(DateTime, default=datetime.utcnow)
user = relationship("User")

View File

@@ -0,0 +1,25 @@
from app.core.database import Base
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Enum
from sqlalchemy.orm import relationship
from datetime import datetime
import enum
class TopupStatus(enum.Enum):
pending = "pending"
confirmed = "confirmed"
rejected = "rejected"
class Topup(Base):
__tablename__ = "topups"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
amount_cents = Column(Integer, nullable=False)
status = Column(Enum(TopupStatus), default=TopupStatus.pending, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
confirmed_at = Column(DateTime, nullable=True)
paypal_email = Column(String, nullable=True)
note = Column(String, nullable=True)
user = relationship("User")

View File

@@ -0,0 +1,42 @@
from app.core.database import Base
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Enum, JSON
import enum
from datetime import datetime
class UserRole(enum.Enum):
user = "user"
manager = "manager"
admin = "admin"
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
email = Column(String, nullable=False, unique=True, index=True)
hashed_password = Column(String, nullable=False)
# Hinweis: hashed_pin als unique kann problematisch sein, falls None in deiner DB ist es gesetzt.
hashed_pin = Column(String, nullable=False, unique=True, index=True)
# Sichtbarkeit/Alias für die Stats-Seite
alias = Column(String, nullable=True, unique=True)
public_stats = Column(Boolean, nullable=False, default=False) # <— NEU: Opt-in
paypal_email = Column(String, nullable=True)
role = Column(Enum(UserRole), nullable=False, default=UserRole.user)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
balance_cents = Column(Integer, nullable=False, default=0)
favorites = Column(JSON, nullable=False, default=list)
avatar_url = Column(String, nullable=True)
# PIN-Sicherheit (Lockout etc.)
from sqlalchemy import String as SQLAString
pin_lookup = Column(SQLAString(64), index=True, nullable=True) # HMAC-SHA256(PEPPER, pin)
pin_fail_count = Column(Integer, nullable=False, default=0)
pin_locked_until = Column(DateTime, nullable=True)