Eigener Rueckfrage-Dialog statt Browsermeldung, Zeitstempel im Export, Notizen an Gruppen-Codes
Rueckfragen liefen bisher ueber window.confirm. Das reisst aus der Oberflaeche,
zeigt den Hostnamen, laesst sich nicht gestalten und die Knoepfe heissen immer
"OK/Abbrechen". Neu ist ein eigener Dialog (src/confirm.jsx) als Provider mit
dem Hook useConfirm. Der Aufruf bleibt so einfach wie vorher, weil er eine
Zusage zurueckgibt:
if (!(await confirm({ title: "…?", message: "…" }))) return;
Damit sind alle neun Stellen umgestellt: Gruppen, Kategorien, Lagerorte,
Einheiten, Benutzer, API-Tokens, Produkt und Charge loeschen sowie das Ersetzen
beim Import. Jede Rueckfrage hat jetzt einen sprechenden Titel, einen Satz zu
den Folgen und einen benannten Knopf ("Loeschen", "Widerrufen", "Ersetzen")
statt eines nichtssagenden OK. Unwiderrufliche Schritte sind rot.
Kuenftig gilt: keine Browserdialoge mehr.
Export: Dateinamen tragen jetzt Datum und Uhrzeit
(bestand_2026-07-22_2130.csv, project-good-backup_2026-07-22_2130.json). Ohne
Zeitstempel hiessen mehrere Ausleitungen alle gleich und der Browser haengte
(1), (2) an - dann war nicht mehr erkennbar, welche die aktuelle ist. Der
Zeitstempel entsteht an beiden Enden gleich: im Content-Disposition-Kopf des
Backends und im Dateinamen, den der Browser setzt. Im JSON steht zusaetzlich
die Ortszeit neben dem bereits vorhandenen UTC-Zeitpunkt. Die CSV bleibt
inhaltlich unveraendert - eine zusaetzliche Spalte oder Kopfzeile wuerde die
Datei in Excel nur stoeren.
Notizen an Gruppen-Codes: Codes, die beim Zuordnen eines Artikels automatisch
entstehen, hatten bisher keine Moeglichkeit, eine Notiz zu bekommen - die liess
sich nur beim Anlegen von Hand mitgeben. Neu PATCH /groups/{id}/barcodes/{code}
und ein Notizfeld in der Liste, das beim Verlassen speichert. Leeren entfernt
die Notiz.
Getestet: 57 pytest-Tests unveraendert gruen, Web-Build laeuft durch. Gegen die
laufende API geprueft: Zeitstempel in beiden Content-Disposition-Koepfen und im
JSON-Inhalt; Notiz an einem automatisch angelegten Code setzen, aendern und
leeren, unbekannter Code antwortet mit 404. Die Dialoge selbst habe ich nicht
im Browser angeklickt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ from ..deps import get_current_user, require_admin
|
||||
from ..models import Barcode, Group, Product, User
|
||||
from ..schemas import (
|
||||
BarcodeCreate,
|
||||
BarcodeNoteUpdate,
|
||||
BarcodeOut,
|
||||
GroupCreate,
|
||||
GroupOut,
|
||||
@@ -171,6 +172,36 @@ def add_group_barcode(
|
||||
return _group_to_out(db, group)
|
||||
|
||||
|
||||
@router.patch("/{group_id}/barcodes/{code}", response_model=GroupOut)
|
||||
def update_group_barcode_note(
|
||||
group_id: int,
|
||||
code: str,
|
||||
payload: BarcodeNoteUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
_: User = Depends(require_admin),
|
||||
) -> GroupOut:
|
||||
"""Notiz zu einem Gruppen-Code setzen.
|
||||
|
||||
Betrifft ausdrücklich auch Codes, die beim Zuordnen eines Artikels
|
||||
automatisch entstanden sind – die hatten bisher gar keine Möglichkeit,
|
||||
eine Notiz zu bekommen.
|
||||
"""
|
||||
group = db.get(Group, group_id)
|
||||
if group is None:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "Gruppe nicht gefunden")
|
||||
entry = (
|
||||
db.query(Barcode).filter(Barcode.group_id == group_id, Barcode.code == code).first()
|
||||
)
|
||||
if entry is None:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "Code nicht gefunden")
|
||||
|
||||
note = (payload.note or "").strip()
|
||||
entry.note = note or None
|
||||
db.commit()
|
||||
db.refresh(group)
|
||||
return _group_to_out(db, group)
|
||||
|
||||
|
||||
@router.delete("/{group_id}/barcodes/{code}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_group_barcode(
|
||||
group_id: int,
|
||||
|
||||
@@ -47,6 +47,15 @@ PACKAGE_TOKENS = {"packung", "package", "pkg", "pack"}
|
||||
# --------------------------------------------------------------------------
|
||||
# Export
|
||||
# --------------------------------------------------------------------------
|
||||
def _zeitstempel() -> str:
|
||||
"""Ortszeit als YYYY-MM-DD_HHMM für Dateinamen.
|
||||
|
||||
Ohne Zeitstempel heißen mehrere Ausleitungen alle gleich und der Browser
|
||||
haengt (1), (2) an – dann weiß niemand mehr, welche die aktuelle ist.
|
||||
"""
|
||||
return datetime.now().strftime("%Y-%m-%d_%H%M")
|
||||
|
||||
|
||||
def _article_unit(product: Product) -> tuple[float, str]:
|
||||
"""Faktor und Bezeichnung der Artikeleinheit (Gebinde, sonst Produkteinheit)."""
|
||||
unit_name, unit_factor = display_unit_info(product)
|
||||
@@ -104,7 +113,9 @@ def export_stock_csv(
|
||||
return Response(
|
||||
content="" + buf.getvalue(),
|
||||
media_type="text/csv; charset=utf-8",
|
||||
headers={"Content-Disposition": 'attachment; filename="bestand.csv"'},
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="bestand_{_zeitstempel()}.csv"'
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -119,6 +130,7 @@ def export_backup_json(
|
||||
data = {
|
||||
"version": 1,
|
||||
"exported_at": datetime.now(timezone.utc).isoformat(),
|
||||
"exported_at_local": datetime.now().isoformat(timespec="seconds"),
|
||||
"units": [
|
||||
{"name": u.name, "kind": u.kind.value, "factor": u.factor}
|
||||
for u in db.query(Unit).order_by(Unit.id).all()
|
||||
@@ -171,7 +183,10 @@ def export_backup_json(
|
||||
return Response(
|
||||
content=json.dumps(data, ensure_ascii=False, indent=2),
|
||||
media_type="application/json; charset=utf-8",
|
||||
headers={"Content-Disposition": 'attachment; filename="project-good-backup.json"'},
|
||||
headers={
|
||||
"Content-Disposition":
|
||||
f'attachment; filename="project-good-backup_{_zeitstempel()}.json"'
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user