Files
Holzleitner---Backend--aktu…/tool/mark_standard_scanned.sh
Dennis Nemec 6a9b5872e1 Backend-Arbeitsstand: ERP-Sync, Lieferlebenszyklus, Reports + config.toml
Bringt das Backend vom initialen Skeleton auf den aktuellen Arbeitsstand
(Clean Architecture: domain → application → infrastructure → api).

Wesentliche Bereiche:
- ERP-Anbindung (MSSQL-Pull der Touren, Import-Scheduler, Rückschreiben)
- Lieferlebenszyklus: Scan/Hold/Cancel/Complete, Gutschriften, Notizen,
  Bild-Anhänge, Unterschriften, PDF-Lieferreport → DOCUframe
- Stammdaten: Kunden, Artikel, Lager, Zahlungsarten, Services
- Keycloak-JWT-Gate + Fahrer-Provisionierung via Admin-API
- Admin-API-Key-Gate (X-Admin-Api-Key) für Maschinen-Endpunkte

Jüngste Änderungen dieser Session:
- Belegspezifische Kontaktdaten: alle ERP-Adressen (Beleg-/Liefer-/
  Rechnungsadresse, Ansprechpartner, Kundenstamm) mit Telefon/Mobil/
  E-Mail werden gesynct (Migration 0029, MSSQL-Query, TourDetails)
- Konfiguration von .env (envy/dotenvy) auf config.toml (toml/serde)
  umgestellt; Vorlage config.example.toml, Pfad via HOLZLEITNER_CONFIG

Nicht im Repo (per .gitignore): config.toml (Secrets), data/ (Laufzeit-/
Kundendaten), demo.mp4, .claude/, variocontrol-ai/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 17:52:58 +02:00

102 lines
3.3 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Markiert nur die STANDARDLAGER-Items der Test-Touren als gescannt
# (scan_status = 'done'). Filial-Items bleiben bewusst 'in_progress' —
# damit lässt sich der Prozess „Artikel aus der Filiale abholen" testen:
# die Lieferung gilt im Standardlager als fertig, hat aber noch offene
# Filial-Positionen.
#
# Schwester-Skript zu `mark_all_scanned.sh` (das ALLES inkl. Filiale
# scannt). Gleiche Regeln, nur zusätzlich der `w.is_standard = TRUE`-Filter.
#
# Regeln:
# * nur scanbare Artikel (Dienstleistungen werden nicht gescannt)
# * NUR Standardlager (Filiale bleibt offen)
# * Items mit Status 'removed' bleiben unangetastet
# * 'held' wird auf 'done' gehoben
#
# Accounts:
# * Default: 1001,1002
# * via Env überschreibbar: ACCOUNTS="1001,1002,1003" ./tool/mark_standard_scanned.sh
#
# Aufruf:
# ./tool/mark_standard_scanned.sh
set -euo pipefail
CONTAINER="${CONTAINER:-holzleitner-postgres}"
DB_USER="${DB_USER:-holzleitner}"
DB_NAME="${DB_NAME:-holzleitner}"
ACCOUNTS="${ACCOUNTS:-1001,1002}"
# Nur Ziffern + Kommas — schützt die rohe psql-Variablen-Substitution unten
# vor Injection.
if ! [[ "$ACCOUNTS" =~ ^[0-9]+(,[0-9]+)*$ ]]; then
echo "✗ Ungültige ACCOUNTS-Liste '$ACCOUNTS'. Erwartet: kommagetrennte Zahlen, z.B. 1001,1002." >&2
exit 1
fi
if ! docker inspect "$CONTAINER" >/dev/null 2>&1; then
echo "✗ Container '$CONTAINER' läuft nicht. Starte docker compose up -d." >&2
exit 1
fi
echo "→ Nur Standardlager-Items der Accounts ($ACCOUNTS) auf 'done' setzen (Filiale bleibt offen) …"
docker exec -i "$CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -v ON_ERROR_STOP=1 -q \
-v accounts="$ACCOUNTS" <<'SQL'
BEGIN;
-- Status VOR dem Update (Kontrolle).
\echo
\echo --- VORHER ---
SELECT w.name AS lager, di.scan_status, COUNT(*) AS items
FROM delivery_items di
JOIN deliveries d ON d.id = di.delivery_id
JOIN tours t ON t.id = d.tour_id
JOIN warehouses w ON w.id = di.warehouse_id
JOIN articles a ON a.id = di.article_id
WHERE t.account_id IN (:accounts)
AND a.scannable = TRUE
GROUP BY w.name, di.scan_status
ORDER BY w.name, di.scan_status;
-- Update über Subquery: Postgres erlaubt im UPDATE … FROM keinen
-- Self-Join auf die Zieltabelle in der JOIN-Bedingung, daher die Id-Liste
-- vorab per SELECT bestimmen. Filter `w.is_standard = TRUE` lässt das
-- Filiale bewusst offen.
UPDATE delivery_items
SET scanned_quantity = required_quantity,
scan_status = 'done',
scan_last_updated_at = now()
WHERE id IN (
SELECT di.id
FROM delivery_items di
JOIN deliveries d ON d.id = di.delivery_id
JOIN tours t ON t.id = d.tour_id
JOIN warehouses w ON w.id = di.warehouse_id
JOIN articles a ON a.id = di.article_id
WHERE t.account_id IN (:accounts)
AND a.scannable = TRUE
AND w.is_standard = TRUE
AND di.scan_status <> 'removed'
);
\echo
\echo --- NACHHER ---
SELECT w.name AS lager, di.scan_status, COUNT(*) AS items
FROM delivery_items di
JOIN deliveries d ON d.id = di.delivery_id
JOIN tours t ON t.id = d.tour_id
JOIN warehouses w ON w.id = di.warehouse_id
JOIN articles a ON a.id = di.article_id
WHERE t.account_id IN (:accounts)
AND a.scannable = TRUE
GROUP BY w.name, di.scan_status
ORDER BY w.name, di.scan_status;
COMMIT;
SQL
echo "✓ Standardlager gescannt, Filiale offen — Abhol-Prozess testbar."