Stammdaten-Export/Import (JSON) + Lagerort-Eltern bleibt beim Anlegen

- Backend: services/master_data.py exportiert/importiert Kategorien (inkl.
  eigener Felder), Lagerorte, Einheiten und Gebinde als JSON – MIT IDs, damit
  gedruckte QR-Codes und Verweise nach dem Wiederherstellen passen. Import-Modus
  skip (Vorhandenes lassen) oder overwrite (per ID aktualisieren); Namens-
  Konflikte werden uebersprungen, nicht abgebrochen. Postgres-Sequenzen werden
  danach angehoben. Endpunkte GET /export/master-data, POST /import/master-data.
  5 neue Tests, Suite 162 gruen.
- Web: neue Karte "Stammdaten sichern & wiederherstellen (JSON)" in Import/Export.
- Web: beim Lagerort-Anlegen bleibt der uebergeordnete Ort in der Auswahl.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-27 11:17:14 +02:00
parent 636982f6c7
commit 6e48cd0a7a
6 changed files with 371 additions and 2 deletions

View File

@@ -11,12 +11,13 @@ import io
import json
from datetime import date, datetime, timezone
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from fastapi import APIRouter, Body, Depends, File, HTTPException, UploadFile, status
from fastapi.responses import Response
from sqlalchemy.orm import Session
from ..database import get_db
from ..deps import get_current_user, require_admin
from ..services.master_data import export_master_data, import_master_data
from ..services.dates import MONTH, clean_precision, normalize_best_before
from ..services.group_codes import sync as sync_group_code
from ..models import (
@@ -252,6 +253,30 @@ def export_backup_json(
)
@router.get("/export/master-data")
def export_masterdata(
db: Session = Depends(get_db), _: User = Depends(require_admin)
) -> dict:
"""Stammdaten (Kategorien, Felder, Lagerorte, Einheiten, Gebinde) als JSON
mit IDs, für Sicherung/Umzug."""
return export_master_data(db)
@router.post("/import/master-data")
def import_masterdata(
payload: dict = Body(...),
mode: str = "skip",
db: Session = Depends(get_db),
_: User = Depends(require_admin),
) -> dict:
"""Stammdaten aus JSON einspielen. mode=skip (Vorhandenes lassen) oder
overwrite (per ID überschreiben). IDs werden aus der Datei übernommen."""
try:
return import_master_data(db, payload, mode=mode)
except ValueError as e:
raise HTTPException(status.HTTP_400_BAD_REQUEST, str(e))
@router.get("/export/labels")
def export_labels(
category_ids: str | None = None,