Einzelstuecke verleihen: eigener Vorgang statt Lagerort-Wert
Der Lagerort bleibt beim Verleihen stehen - er sagt weiter, wohin das Stueck nach der Rueckgabe zurueckgehoert. Ein Lagerort-Wert "verliehen" haette diese Information vernichtet. Stattdessen eine eigene Tabelle item_loans mit Entleiher und Grund als Freitext, Verleihdatum und einem optionalen Rueckgabedatum: "kurz mal mitgenommen" soll nicht an einem Datumszwang scheitern. Zurueckgegebene Vorgaenge bleiben stehen, damit die Tabelle gleichzeitig die Verleih-Historie je Stueck ist. Ein Teilindex auf die offenen Vorgaenge haelt fest, dass ein Stueck nicht zweimal gleichzeitig verliehen sein kann - wie bei den "Ueberall"-Mindestbestaenden, weil Postgres NULLs in UNIQUE als verschieden zaehlt. Verliehen heisst nicht im Bestand: Ein Stueck, das nicht im Haus ist, zaehlt nirgends mehr mit - weder am Lagerort noch im Gesamtbestand. Entschieden wird das an einer Stelle (services/loans.py::nicht_verliehen), die alle fuenf Zaehlungen benutzen. Sonst behauptete "Was liegt hier?" weiterhin, die Kamera liege im Regal. Folge: Faellt ein verliehenes Stueck unter den Mindestbestand, erscheint der Artikel auf der Einkaufsliste. Verleihen und Zuruecknehmen darf jeder angemeldete Benutzer - es ist eine Buchung wie Ein- und Auslagern, keine Stammdatenaenderung - und wird mit dem Konto protokolliert. Nur das Loeschen eines Vorgangs bleibt Admins vorbehalten, weil es Historie vernichtet. Bewusst keine Movement-Bewegung je Verleih: Der Verlauf rechnet den Bestand rueckwaerts aus vorzeichenbehafteten Bewegungen, ein Abgang ohne Gegenbuchung wuerde diese Kurve verfaelschen. GET /loans?due_in= liefert die faelligen Verleihe und ist damit schon die Datenquelle fuer die spaetere Rueckgabe-Erinnerung der App. Oberflaeche in Web und App fehlt noch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ba84e4ea01
commit
467ef3c804
@@ -770,6 +770,54 @@ class ItemDocumentUploadOut(ItemDocumentOut, DocumentSuggestions):
|
||||
"""Antwort nach dem Upload – Beleg-Metadaten plus die geschätzten Werte."""
|
||||
|
||||
|
||||
class LoanCreate(BaseModel):
|
||||
"""Ein Einzelstück verleihen. Nur der Name ist Pflicht."""
|
||||
borrower: str = Field(min_length=1, max_length=120)
|
||||
reason: str | None = Field(default=None, max_length=255)
|
||||
# Leer = heute; rückdatieren erlaubt (man trägt es oft später nach).
|
||||
lent_on: date | None = None
|
||||
# Erwartete Rückgabe – optional, siehe ItemLoan.
|
||||
due_on: date | None = None
|
||||
|
||||
|
||||
class LoanUpdate(BaseModel):
|
||||
"""Laufenden oder abgeschlossenen Verleih korrigieren (Tippfehler, Datum)."""
|
||||
borrower: str | None = Field(default=None, min_length=1, max_length=120)
|
||||
reason: str | None = Field(default=None, max_length=255)
|
||||
lent_on: date | None = None
|
||||
due_on: date | None = None
|
||||
returned_on: date | None = None
|
||||
|
||||
|
||||
class LoanReturn(BaseModel):
|
||||
"""Rückgabe buchen. Leeres Datum = heute."""
|
||||
returned_on: date | None = None
|
||||
|
||||
|
||||
class LoanOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
id: int
|
||||
item_id: int
|
||||
borrower: str
|
||||
reason: str | None = None
|
||||
lent_on: date
|
||||
due_on: date | None = None
|
||||
returned_on: date | None = None
|
||||
created_at: datetime
|
||||
# Wer herausgegeben hat (Konto kann später gelöscht sein).
|
||||
username: str | None = None
|
||||
# Tage über dem Rückgabedatum; None ohne Datum oder wenn noch nicht fällig.
|
||||
# Vom Server gerechnet, damit App und Web nicht getrennt rechnen.
|
||||
days_overdue: int | None = None
|
||||
# Für die Übersicht „was ist gerade draußen" mitgeliefert:
|
||||
item_uid: str | None = None
|
||||
product_id: int | None = None
|
||||
product_name: str | None = None
|
||||
product_brand: str | None = None
|
||||
location_id: str | None = None
|
||||
location_name: str | None = None
|
||||
|
||||
|
||||
class ItemOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
id: int
|
||||
@@ -791,6 +839,9 @@ class ItemOut(BaseModel):
|
||||
category_id: int | None = None
|
||||
category_name: str | None = None
|
||||
documents: list[ItemDocumentOut] = []
|
||||
# Der offene Verleih, falls das Stück gerade draußen ist – sonst None.
|
||||
# Der Lagerort bleibt daneben stehen: dorthin gehört es zurück.
|
||||
loan: LoanOut | None = None
|
||||
|
||||
|
||||
# ---- Views ----
|
||||
|
||||
Reference in New Issue
Block a user