Import (Import-Jobs)
Zweck
Die /import-Route ist die Detail-Sicht laufender und vergangener Import-Jobs (DataImportJob). Pro Job werden Datei-Verweis, Ziel-Modell (targetModel, z. B. Customer, Product), Modus (upsert, insert, ...), Mapping (Spalten-Zuordnung), Match-Keys (eindeutige Identifikation pro Datensatz) und Optionen gepflegt. Der Status (pending, running, completed, failed) wird mit progress-Daten zu totalRows, processedRows, createdRows, updatedRows angezeigt.
/import/history zeigt vergangene Jobs.
Voraussetzungen
Berechtigungen (CASL)
| Action | Subject | Wirkung | Keycloak-Rolle |
|---|---|---|---|
view | FE_Import, DataImportJob | Liste/Detail aufrufbar | — |
create | DataImportJob | Neuen Import starten | APP_SPEAMCORE_CREATE_DATA_IMPORT_JOB |
cancel | DataImportJob | Laufenden Import abbrechen | APP_SPEAMCORE_CANCEL_DATA_IMPORT_JOB |
Schritt-für-Schritt-Anleitung
Neuen Import anlegen
- Import (
/import) → + Neu. - Quell-Datei hochladen — wird als Document gespeichert.
targetModelwählen (z. B.Customer,Product,SalesDocument).modewählen —insert(nur neue),update(nur bestehende),upsert(beide).- Mapping pflegen — Spalten der Quell-Datei auf Felder des Ziel-Modells abbilden. Auto-Match-Vorschläge werden angeboten.
- Match-Keys wählen — z. B.
email,customerNumber— Felder, die einen Datensatz eindeutig identifizieren. - Optionen einstellen (z. B.
skipErrors,dryRun). - Vor dem Start: optional
POST /api/import/previewfür einen vollständigen Trockenlauf — analysiert alle Zeilen, klassifiziert sie alsCREATE/UPDATE/ERROR, läuft Fuzzy-Name-Search gegen bestehende Stammdaten (Employee, Branch, PaymentTarget) mit Similarity-Score. Ergebnis fliesst in das Pre-Import-Mapping-Modal des FE — User kann Override-Zuordnungen setzen, die beim eigentlichen Import berücksichtigt werden. - Start klicken —
POST /api/data-import-jobs/:id/start. BeidryRun=trueim Upload-Payload läuft der Job durch alle Schritte, schreibt aber nichts in die DB; End-Status istdry_run_completed.
Fortschritt verfolgen
Detail-Sicht zeigt einen Progress-Block mit processedRows / totalRows, sowie Counts für createdRows und updatedRows. Bei Fehlern: Drill-Down in die Fehler-Liste pro Zeile.
Abbrechen
Bei laufendem Job (status = running) ist der Button Abbrechen sichtbar — POST /api/data-import-jobs/:id/cancel.

Felder und Eingaben
| Feldname | Pflicht | Datentyp | Wirkung beim Ausfuellen | Voraussetzung |
|---|---|---|---|---|
targetModel | ja | String | Modellname (z. B. Customer, Product). | — |
mode | ja | ENUM (insert, update, upsert) | Steuert, ob neu, geändert oder beides. | — |
fileName | ja | String | Original-Datei-Name. | — |
documentId | nein | UUID (Document) | Verweis auf hochgeladene Quell-Datei. | view:Document. |
mapping | ja | JSON | Mapping Quell-Spalte → Ziel-Feld. | — |
matchKeys | nein | Array | Eindeutigkeits-Kriterien. | — |
options | nein | JSON | Konfiguration (skipErrors, dryRun, ...). | — |
Job-Progress (read-only)
| Feldname | Datentyp | Bedeutung |
|---|---|---|
job.status | ENUM (pending, running, completed, failed, dry_run_completed) | Lebenszyklus. dry_run_completed zeigt einen Trockenlauf an — alle Validierungen + Fuzzy-Matches durchgelaufen, aber keine DB-Writes. |
job.progress.totalRows | Integer | Anzahl Zeilen in Quelldatei. |
job.progress.processedRows | Integer | Bearbeitete Zeilen. |
job.progress.createdRows | Integer | Anzahl neu angelegter Datensaetze. |
job.progress.updatedRows | Integer | Anzahl aktualisierter Datensaetze. |
Mapping-Wizard — erweiterte Optionen (Juni 2026)
Der Mapping-Schritt des Import-Wizards (/import, /api/import/*) hat vier zusätzliche Werkzeuge bekommen, die die reine Spalte→Feld-Zuordnung ergänzen. Alle werden im Mapping-Schritt konfiguriert, in Vorschau und Validierung berücksichtigt und lassen sich als Vorlage (Template) speichern und mandantenübergreifend wiederverwenden.
Einheiten zuordnen (Unit-Mapping)
Über das Modal „Einheiten zuordnen" werden die Rohwerte einer Einheiten-Spalte (z. B. Stk, Pkg) auf SpeamCore-Units gemappt:
- SpeamCore scannt die eindeutigen Werte der Spalte (
POST /api/import/column-values). - Eine Heuristik schlägt Treffer vor — zuerst über deutsche Alias-Listen (z. B.
stk→ Stück/piece), dann über Abkürzung/Name/Code, zuletzt partiell. - Pro Wert lässt sich die Ziel-Einheit frei wählen; ein „KI-Vorschlag"-Button nutzt ein Sprachmodell als Fallback (
POST /api/import/suggest-units). - Gespeichert wird als
unitMappings(value,unitLabel,unitId).unitLabelist ein mandantenübergreifend stabiles Label, damit Vorlagen auch in anderen Mandanten greifen.
Felder kombinieren (Combine Fields)
Eine Template-Engine führt mehrere Quellspalten (plus Literaltext/HTML) zu einem Zielfeld zusammen. Im „Kombinieren"-Modal wird ein Template mit {Spaltenname}-Tokens gepflegt (farblich hervorgehoben, mit Live-Zeilen-Vorschau):
- Beispiel Zielfeld
description, Template`{Bezeichnung2}<br/>{Bezeichnung3}`— verbindet zwei Spalten mit Zeilenumbruch. Echte Zeilenumbrüche im Template werden zu<br/>. - FK-Auflösung: Auch Referenzfelder funktionieren — Ziel
manufacturerIdmit Template`{manufName}`löst den Namen auf die ID auf. - Ein kombiniertes Zielfeld kann selbst als Match-Key dienen.
- Spalten, die nur in Combine-Templates vorkommen, werden nicht zusätzlich als Produkt-Attribut angelegt.
Gespeichert als combineRules (target, template). Die Vorschau (/api/import/preview) wendet die Regeln an, damit Match-Keys korrekt aufgelöst werden.
Lieferant zuordnen (Supplier-Config)
Beim Produkt-Import verknüpft die Box „Lieferant zuordnen" den Import mit einem Lieferanten und legt/aktualisiert pro Produkt einen ProductSupplier (siehe Produkt — Lieferanten und das Konzept Produkt-Lieferanten-Preise). Lieferanten-spezifische Spalten werden über __supplier__:-Mapping-Targets zugeordnet:
| Target | Bedeutung |
|---|---|
__supplier__:articleNo | Artikelnummer beim Lieferanten |
__supplier__:price | Einkaufspreis (EK) |
__supplier__:salePrice | Verkaufspreis (VK) |
__supplier__:discount | Rabatt je Produkt |
__supplier__:discountable | rabattfähig (Ja/Nein) je Zeile |
__supplier__:deliveryTimeDays / :stock / :minimumOrderQuantity / :packagingUnit | Lieferzeit, Bestand, Mindestmenge, Verpackungseinheit |
Konfiguriert wird über supplierConfig: discountType (percentage/fixed/none), discountableTrueValue (Spezialwert der Rabattfähig-Spalte, z. B. „Ja"), salePriceFallbackMarkupPercent (VK-Aufschlag, falls VK ≤ EK oder fehlt), supplierDiscount und mainSupplier (Hauptlieferant). DATANORM-Dateien füllen Lieferant + Artikelnummer/Preis automatisch vor. Die Rabatt-Fallback-Kette ist Produkt → Warengruppe → Lieferant.
Sind Lieferanten-Spalten gemappt, aber kein Lieferant zugeordnet, bricht die Validierung mit einem Hinweis ab.
Warengruppen-Typ & Hierarchie (ProductGroup)
Ist productGroupId gemappt, stehen zwei Optionen bereit:
productGroupType(internal/external, Defaultexternal) — Typ neu angelegter Warengruppen.productGroupHierarchy+productGroupHierarchySeparator(Default>) — interpretiert einen Spaltenwert als Pfad:`Brandschutz > Melder > Rauchmelder`legt verschachtelte Warengruppen an und hängt das Produkt an das Blatt.
Neue Mapping-Optionen (Übersicht)
| Option | Datentyp | Wirkung | Voraussetzung |
|---|---|---|---|
unitMappings | Objekt[] (value, unitLabel, unitId) | Einheiten-Rohwerte → Unit | Einheiten-Spalte gemappt |
combineRules | Objekt[] (target, template) | mehrere Spalten → ein Zielfeld | — |
supplierConfig | Objekt | Lieferanten-Zuordnung + Rabatt/VK-Logik | Produkt-Import, __supplier__:-Targets |
productGroupType | internal / external | Typ neuer Warengruppen | productGroupId gemappt |
productGroupHierarchy | Boolean | Pfad-Interpretation der Warengruppen-Spalte | productGroupId gemappt |
productGroupHierarchySeparator | String (max 5) | Pfad-Trennzeichen (Default >) | productGroupHierarchy = true |
autoCreateReferences | Boolean | fehlende FK-Referenzen automatisch anlegen (außer Unit) | — |
autoMapUnmappedAsAttributes | Boolean | nicht gemappte Spalten als Produkt-Attribute | Produkt-Import |
Endpoints des Mapping-Wizards (Auswahl)
| Methode | Endpoint | Zweck | CASL |
|---|---|---|---|
POST | /api/import/column-values | eindeutige Werte einer Spalte (Unit-Modal) | create Import |
POST | /api/import/suggest-units | KI-Vorschlag für Einheiten-Zuordnung | create Import |
POST | /api/import/preview-references | Vorschau der FK-Auflösung/-Anlage | view Import |
POST | /api/import/preview | Zeilen-Vorschau (wendet combineRules an) | view Import |
POST | /api/import/execute | Import ausführen (alle Optionen im Body) | create Import |
ARGE 10.0 — Hersteller-Produktdaten importieren (Juni 2026)
Neben CSV/Excel/GAEB/DATANORM versteht der Import jetzt das ARGE-10.0-/DQR-10.0-Format — die Hersteller-Stammdaten-Pakete von building-masterdata.com (ARGE Neue Medien e. V.). Ein Hersteller-Paket ist ein ZIP mit mehreren relationalen CSVs (Kopfdaten, Artikel, Gruppen, Langtexte …), die über die Werksartikelnummer verknüpft werden — entpackt schnell mehrere hundert MB.
Format-Eigenschaften (DQR 10.0): UTF-8, Trennzeichen ;, Anführungszeichen ", Dezimaltrenner ,, Datumsformat DDMMYYYY. In der ersten Ausbaustufe werden die wichtigsten Felder aus der Artikel-Stammdatei auf Produkt + Attribute gemappt.
Wiederverwendbare Konzepte
Verknuepfungen zu anderen Modulen
- Datentransfer — Universal-Page, über die Imports gestartet werden.
- Document-Center — speichert die Quell-Datei.
- Produkt-Import (
/product-import) — spezialisierte Import-Variante (siehe Kontext-Notiz unten). Keine eigene Doku-Seite (Stand: noch nicht migriert).
API/Schnittstellen
| Methode | Endpoint | Zweck | CASL |
|---|---|---|---|
GET | /api/data-import-jobs | Liste | view DataImportJob |
GET | /api/data-import-jobs/:id | Detail | view DataImportJob |
POST | /api/data-import-jobs | Anlegen | create DataImportJob |
POST | /api/data-import-jobs/:id/start | Start | create DataImportJob |
POST | /api/data-import-jobs/:id/cancel | Abbrechen | cancel DataImportJob |
GET | /api/data-import-jobs/history | Historie | view DataImportJob |
CSV-Bulk-Import (Mai 2026)
Parallel zur oben beschriebenen DataImportJob-Pipeline gibt es seit Mai 2026 eine eigene CSV-Bulk-Import-Route-Familie (/imports/*, Modell CsvImportJob), die per Worker + Queue arbeitet und 13 Ziel-Modelle über pro-Entity-Konfigurationen unterstützt.
| Methode | Endpoint | Zweck | CASL |
|---|---|---|---|
POST | /imports/upload | CSV hochladen + Job queuen (CSV als String, Default-Trennzeichen ;) | create:ImportJob |
POST | /imports/preview | Vorschau-/Trockenlauf ohne DB-Write | view:ImportJob |
GET | /imports | Job-Liste (paginiert) | view:ImportJob |
GET | /imports/:id | Job-Detail | view:ImportJob |
PATCH | /imports/:id | Job abbrechen (status=cancelled) | update:ImportJob |
Unterstützte Ziel-Modelle: Customer, Location, Employee, Branch, Supplier, Workorder, SalesDocument, SalesDocumentItem, EmployeeTimeTracking, EmployeeTimeTrackingType, PurchaseDocument, PurchaseDocumentItem, PaymentTarget.
Identität: Spalte _externalId ist der stabile Schlüssel. Dokument-Modelle (SalesDocument, PurchaseDocument, EmployeeTimeTracking) matchen ausschließlich über _externalId; Stammdaten zusätzlich über Business-Key (Name etc.).
Fuzzy-Matching (nur Employee, Branch, PaymentTarget): wort-basiert mit Zahlen-Präfix-Strip; Score = passende Wörter ÷ Gesamtwörter; ≥ 0,5 → Vorschlag, ≥ 0,8 → Auto-Promote.
DryRun: dryRun-Flag im Upload-Body, ephemer (nicht im Modell persistiert). Preview-Response enthält summary (willCreate/willUpdate/willSkip/willError), matches[], unmatchedRows[] mit possibleMatches, allEntities[].
Post-Import-Hook refreshAddressSnapshot: frischt nach dem Import die denormalisierten Parent-Adressfelder bei SalesDocument, PurchaseDocument, Workorder auf (Import läuft mit hooks:false aus Performance-Gründen, daher manueller Refresh).
Versionshinweise
- 2026-06-30: DATANORM-Import großer Bestände verbessert — disk-basierte Zwischenspeicherung der Artikel-/Preis-Maps (konstanter Speicherbedarf statt Abbruch bei sehr großen Katalogen wie GC) und wiederaufnehmbare Verarbeitung nach Unterbrechung; Fix: Mehr-Monats-Archive (Format
DATANORM-25.001) werden jetzt korrekt nach Datum sortiert (vorher gleiche Sortierung → falsche Reihenfolge). Verifiziert andatanormMetadataDiskStore.ts,datanormParser.ts. - 2026-06-24: ARGE-10.0-/DQR-10.0-Import (Hersteller-Stammdaten-Pakete von building-masterdata.com, Multi-CSV-ZIP via
Werksartikelnummer) dokumentiert; großvolumige Importe laufen über denimport-wizard-workermit Live-Vorschau und datensatzweisem Error-Handling. Verifiziert anargeParser.ts(DQR-10.0-Format),import.service.ts(parseArgeSample,ARGE_*),import-wizard-worker.ts. - 2026-06-22: Mapping-Wizard erweitert — Einheiten-Zuordnung (
unitMappings, Heuristik + KI-Vorschlag,Unitnicht mehr auto-anlegbar), Felder kombinieren (combineRules, FK-Auflösung), Lieferant-Zuordnung (supplierConfig,__supplier__:-Targets, DATANORM-Vorbefüllung) und Warengruppen-Typ/-Hierarchie (productGroupType,productGroupHierarchy). Neue Endpointscolumn-values,suggest-units,preview-references. Verifiziert animport.validate.ts,import.router.ts,import.service.ts(AUTO_CREATABLE_MODELS,SUPPLIER_MAPPING_PREFIX),MappingStep.tsx. Bad-Merge-Regressionen (u. a.Unit-Auto-Create) im selben Drop bereinigt. - 2026-05-29: CSV-Bulk-Import-Pipeline ergänzt (13 Entity-Configs,
/imports/*-API, Fuzzy-Matching, Address-Snapshot-Hook). - 2026-04-30: Initiale Veroeffentlichung.