From 45031df1f4346e4d07357cc2a2743142d680650f Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Tue, 28 Jul 2026 11:12:03 +0200 Subject: [PATCH] Backend: Product.updated_at (Zuletzt geaendert) Neue Spalte updated_at, die bei jeder Stammdaten-Aenderung automatisch nachzieht (onupdate). Migration legt sie nullable an und setzt Altbestaende auf created_at. In ProductOut ausgegeben - Basis fuer eine "Zuletzt geaendert"-Spalte im Web. Co-Authored-By: Claude Opus 4.8 --- backend/app/main.py | 5 +++++ backend/app/models.py | 5 +++++ backend/app/schemas.py | 1 + backend/tests/test_gegenstaende.py | 17 +++++++++++++++++ 4 files changed, 28 insertions(+) diff --git a/backend/app/main.py b/backend/app/main.py index efb4592..298df88 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -179,6 +179,11 @@ def _ensure_schema() -> None: # Verbrauchsgegenstand: Gegenstand wie ein Lebensmittel führen (Chargen). "ALTER TABLE products ADD COLUMN IF NOT EXISTS bulk BOOLEAN " "NOT NULL DEFAULT FALSE", + # Zeitpunkt der letzten Änderung an den Stammdaten (für „Zuletzt geändert"). + # Nullable anlegen und Altbestände auf created_at zurücksetzen; neue Zeilen + # füllt die ORM-Spalte (default/onupdate). + "ALTER TABLE products ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ", + "UPDATE products SET updated_at = created_at WHERE updated_at IS NULL", # Kaufpreis (in Rappen/Cent) und Währung am Einzelstück. "ALTER TABLE items ADD COLUMN IF NOT EXISTS price_cents INTEGER", "ALTER TABLE items ADD COLUMN IF NOT EXISTS currency VARCHAR(3)", diff --git a/backend/app/models.py b/backend/app/models.py index f95b7a9..9167631 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -258,6 +258,11 @@ class Product(Base): bulk: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now) + # Zeitpunkt der letzten Änderung an den Stammdaten des Artikels (nicht am + # Bestand). Wird bei jedem ORM-UPDATE der Zeile automatisch nachgezogen. + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), default=_now, onupdate=_now + ) group: Mapped[Group | None] = relationship(back_populates="products") category: Mapped[Category | None] = relationship() diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 7535b7e..bc8a28e 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -355,6 +355,7 @@ class ProductOut(BaseModel): individual: bool = False bulk: bool = False created_at: datetime + updated_at: datetime | None = None # angereichert: stock: float = 0.0 expired_count: int = 0 diff --git a/backend/tests/test_gegenstaende.py b/backend/tests/test_gegenstaende.py index 0585561..cfaeee7 100644 --- a/backend/tests/test_gegenstaende.py +++ b/backend/tests/test_gegenstaende.py @@ -331,3 +331,20 @@ def test_verbrauchsgegenstand_unter_mindestbestand(db): db.commit() # Unter dem Mindestbestand -> gehört auf die Einkaufsliste (wie ein Lebensmittel). assert current_stock(db, product.id) < product.min_stock + + +# ---- Zuletzt geändert ---- + +def test_updated_at_wird_bei_aenderung_nachgezogen(db): + import time + product = Product(name="Alt") + db.add(product) + db.commit() + db.refresh(product) + erst = product.updated_at + assert erst is not None + time.sleep(0.01) + product.name = "Neu" + db.commit() + db.refresh(product) + assert product.updated_at > erst