Files
Vorrania/backend/app/services/group_codes.py
Scarriffle 5293810529 Gruppen-Codes: Dublette behoben und Altbestand nachgezogen
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>
2026-07-22 21:41:43 +02:00

97 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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)