Zwei Fehler in der EAN-Liste einer Gruppe, beide gemeldet und nachgestellt. Derselbe Code stand zweimal in der Liste: einmal schreibgeschuetzt mit der Kennzeichnung "Artikel", einmal darunter mit Notizfeld. Grund war, dass _group_to_out die Codes der Artikel unabhaengig von den Gruppen-Codes zusammengestellt hat - seit die Zuordnung automatisch einen Gruppen-Code anlegt, trifft beides auf denselben Code zu. Die schreibgeschuetzte Zeile stand oben, deshalb war das Notizfeld darunter leicht zu uebersehen. Jetzt gibt es eine Zeile je Code. Der Gruppen-Code fuehrt den Artikel mit, ueber den er dazugehoert (neues Feld product_name in BarcodeOut), zeigt weiterhin die Kennzeichnung "Artikel" - und hat trotzdem ein Notizfeld. Der Muelleimer entfaellt bei diesen Codes, denn sie kaemen beim naechsten Speichern des Artikels sofort zurueck; dafuer muss der Artikel die Gruppe wechseln. Zweitens fehlte fuer bestehende Daten der Code ganz. Die automatische Pflege greift nur beim Anlegen und Aendern eines Artikels; Zuordnungen, die es vorher schon gab, hatten nie einen Gruppen-Code bekommen. In der Verwaltung stand der Code deshalb ausschliesslich als schreibgeschuetzte Artikel-Zeile - genau die Stelle, an der sich keine Notiz hinterlegen liess. Neu holt backfill() das beim Start nach: fuer jeden Artikel mit Gruppe und Barcode wird der Gruppen-Code angelegt, sofern er fehlt. Gefahrlos wiederholbar. Getestet: 58 pytest-Tests gruen, einer neu (Backfill legt den fehlenden Code an und beim zweiten Lauf nichts doppelt). Der gemeldete Fall wurde vorher gegen die laufende API nachgestellt - Altbestand ohne Gruppen-Code und ein doppelt gelisteter Code nach einer Neuanlage - und danach als behoben bestaetigt: eine Zeile je Code, mit Artikelnamen und Notizfeld. Web-Build laeuft durch. Die Oberflaeche habe ich nicht selbst bedient. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
97 lines
3.3 KiB
Python
97 lines
3.3 KiB
Python
"""Pflegt die EAN-Codes an einer Gruppe automatisch mit.
|
||
|
||
Bisher entstand ein Gruppen-Code ausschließlich von Hand. Wurde ein Artikel
|
||
einer Gruppe zugeordnet, blieb dessen EAN-Code dort unsichtbar – die Gruppe
|
||
zeigte „0 EANs“, obwohl Artikel darin lagen, und der Versuch, den Code
|
||
nachzutragen, scheiterte mit „bereits vergeben“ (er hing ja am Artikel).
|
||
|
||
Jetzt wandert der Haupt-Barcode eines Artikels automatisch an die Gruppe:
|
||
|
||
* beim Anlegen und beim Zuordnen einer Gruppe → Code kommt hinzu
|
||
* beim Wechsel der Gruppe → Code wandert mit
|
||
* beim Entfernen der Gruppe oder Löschen des Artikels → Code verschwindet
|
||
|
||
Beim Scannen ändert das nichts an der Reihenfolge: ``routers.products.lookup``
|
||
findet weiterhin zuerst den Artikel, dessen Gruppe zählt. Der Gruppen-Eintrag
|
||
ist Beleg in der Verwaltung und Rückfall, falls der Artikel später verschwindet.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from sqlalchemy.orm import Session
|
||
|
||
from ..models import Barcode, Product
|
||
|
||
|
||
def _group_row(db: Session, code: str) -> Barcode | None:
|
||
"""Vorhandene Gruppen-Zeile zu diesem Code (nicht: Artikel-Alias)."""
|
||
return (
|
||
db.query(Barcode)
|
||
.filter(Barcode.code == code, Barcode.group_id.isnot(None))
|
||
.first()
|
||
)
|
||
|
||
|
||
def sync(db: Session, product: Product) -> None:
|
||
"""Bringt den Gruppen-Code eines Artikels auf den aktuellen Stand.
|
||
|
||
Deckt Anlegen, Ändern und Entfernen der Gruppe gleichermaßen ab, damit die
|
||
Aufrufer sich nicht merken müssen, was vorher galt.
|
||
"""
|
||
code = (product.barcode or "").strip()
|
||
if not code:
|
||
return
|
||
|
||
row = _group_row(db, code)
|
||
|
||
# Keine Gruppe mehr: einen früher angelegten Eintrag wieder entfernen.
|
||
if product.group_id is None:
|
||
if row is not None:
|
||
db.delete(row)
|
||
return
|
||
|
||
if row is not None:
|
||
# Gruppe gewechselt: Zeile umhängen statt löschen und neu anlegen,
|
||
# damit eine hinterlegte Notiz erhalten bleibt.
|
||
row.group_id = product.group_id
|
||
return
|
||
|
||
# Hängt der Code bereits als Alias an einem Artikel, gehört er dort hin -
|
||
# code ist eindeutig, eine zweite Zeile wäre nicht möglich.
|
||
if db.query(Barcode).filter(Barcode.code == code).first() is not None:
|
||
return
|
||
|
||
db.add(Barcode(code=code, group_id=product.group_id))
|
||
|
||
|
||
def backfill(db: Session) -> int:
|
||
"""Holt die Codes bestehender Zuordnungen nach.
|
||
|
||
``sync`` greift nur beim Anlegen und Ändern eines Artikels. Zuordnungen, die
|
||
es vor dieser Funktion schon gab, hätten sonst nie einen Gruppen-Code
|
||
bekommen: In der Verwaltung stand der Code dann ausschließlich als
|
||
schreibgeschützte Artikel-Zeile, ohne Möglichkeit für eine Notiz.
|
||
|
||
Läuft beim Start und ist gefahrlos wiederholbar.
|
||
"""
|
||
betroffen = (
|
||
db.query(Product)
|
||
.filter(Product.group_id.isnot(None), Product.barcode.isnot(None))
|
||
.all()
|
||
)
|
||
vorher = db.query(Barcode).filter(Barcode.group_id.isnot(None)).count()
|
||
for product in betroffen:
|
||
sync(db, product)
|
||
db.commit()
|
||
return db.query(Barcode).filter(Barcode.group_id.isnot(None)).count() - vorher
|
||
|
||
|
||
def detach(db: Session, product: Product) -> None:
|
||
"""Vor dem Löschen eines Artikels dessen Gruppen-Code aufräumen."""
|
||
code = (product.barcode or "").strip()
|
||
if not code:
|
||
return
|
||
row = _group_row(db, code)
|
||
if row is not None:
|
||
db.delete(row)
|