# QAF → Wertstrom: Feld-Mapping (kanonisch, versioniert)

Stand: 2026-07-17 · `MAPPING_VERSION = qvs-1` · Maschinenlesbar: `mappings/qaf-to-value-stream-mapping.yaml`.
Quelle: `QAFRowValues` (`lib/qaf-parser.ts:96-119`, 22 Felder = MANUFACTURING-Canonical-Registry `mfg_*`) → Ziel: `VsmNode` (`lib/vsm-types.ts`) inkl. der in QVS-P1 ergänzten additiven Felder (kursiv markiert als *neu*).

## Globale Regeln (gelten für jedes Mapping)

- **Missing-Value:** Feld wird im Node WEGGELASSEN (kein 0, kein null-Stopfen); Grund landet in `missing_field_reasons` des Import-Records (`no_column_mapped` | `cell_empty` | `not_numeric[rawText]` | `below_confidence`). Spec 6/27.
- **Confidence:** Numerische Felder übernehmen die Spalten-Confidence des Parsers (1.0 exakt / 0.9 alias-normalisiert) in `qafSource.fields[k].confidence`. Kein Mindest-Cutoff in qvs-1 (Parser mappt nur ≥0.9); Schwelle ist im Mapper parametrisiert.
- **Conflict (mehrere Quellzellen für ein Ziel):** in qvs-1 unmöglich (1 Spalte je Canonical-Field pro Sheet); bei künftigen Multi-Kandidaten gilt das `FieldResolution`-Muster (AGREEMENT/INCONSISTENT, nie still wählen).
- **Provenance:** JEDES gemappte Feld erhält `qafSource.fields[k] = { cell, original, unit?, confidence }` aus `sourceCells`/`raw_values`; Formeln (falls vorhanden) referenziert über den Import-Record (`engine_context`), nicht im Node dupliziert.
- **Einheiten:** Zykluszeit-Quelle ist lt. Registry Sekunden (`[s]`); Ziel `cycleTimeSec` ebenfalls s → `direct`. Es findet KEINE stille Einheiten-Heuristik statt; weicht ein künftiges Template ab (Korpus-Scan P1), wird die Konversion HIER versioniert nachgetragen (qvs-2), nie im Code versteckt. Kosten bleiben in Quell-Währung (`currency` am Node), keine Umrechnung.
- **Semantik-Schutz:** Kosten werden NIE in Zeit-Felder gemappt (`ruestkosten` ≠ `setupTimeSec`) — der zentrale Verwechslungs-Kandidat ist explizit gebannt (Spec 11: „Do not force semantically different values into the same field").

## Mapping-Tabelle (alle 22 Quellfelder, vollständig)

| # | QAF-Feld | Canonical-ID | VSM-Ziel | Regel | Anmerkung |
|---|---|---|---|---|---|
| 1 | `prozessbezeichnung` | `mfg_process_designation` | `name` | direct | Pflichtfeld — Zeile ohne Prozessname wird nicht importiert (zählt als excluded, Grund `missing_process_name`) |
| 2 | `positionsnummer` | `mfg_position_number` | Node-Reihenfolge | order-evidence | Primär-Sequenz = `row_index` (Quell-Reihenfolge, Spec 7); `positionsnummer` = Zusatz-Evidenz + Anzeige; bei Widerspruch row_index gewinnt + `sequence_confidence=ambiguous`-Warnung |
| 3 | `zykluszeit` | `mfg_cycle_time` | `cycleTimeSec` | direct (s→s) | EIN undifferenzierter Wert; kein Split auf machine/manual (Quelle gibt es nicht her — nie raten) |
| 4 | `teileProZyklus` | `mfg_parts_per_cycle` | *`partsPerCycle`* (neu) | direct | KEINE stille Division von Zykluszeit; Anzeige + Kennzahlen nutzen beides explizit |
| 5 | `anzahlMA` | `mfg_direct_employee_count` | `numWorkers` | direct | Zielfeld existiert bereits |
| 6 | `bezeichnungAnlage` | `mfg_facility_designation` | `machineType` | direct | Freitext→Freitext (Zielfeld existiert); keine Equipment-Stammdaten-Anbindung (existiert nicht) |
| 7 | `standort` | `mfg_site` | *`location`* (neu) | direct | Spec-Beispiel „Production location → Location"; Freitext |
| 8 | `beschaffungswaehrung` | `mfg_procurement_currency` | *`currency`* (neu) | direct | Währung der BW-Kosten-Felder 11-14 (`mss`, `lohnkosten`, `ruestkosten`, `fk`) — NICHT Feld 10 (`ausschusskosten`): das ist AW (Angebotswährung), s. Zeile 10 (Review-Fix) |
| 9 | `ausschuss` | `mfg_scrap_rate` | *`scrapRate`* (neu) | direct (%) | |
| 10 | `ausschusskosten` | `mfg_scrap_cost` | *`scrapCostPerUnit`* (neu) | direct | AW (Angebotswährung), NICHT `currency`/BW — Kosten-Währung ≠ Node-`currency`; keine `wechselkurs`-Konversion in P1 (Review-Fix, analog Zeile 13 Kosten≠Zeit) |
| 11 | `mss` | `mfg_machine_hour_rate` | *`machineHourRate`* (neu) | direct | |
| 12 | `lohnkosten` | `mfg_direct_labor_cost` | *`laborHourRate`* (neu, vormals fälschlich `laborCostPerUnit`) | direct | Stundensatz [BW/h], KEIN Pro-Stück-Wert — analog `machineHourRate` (Zeile 11); Pro-Stück bräuchte eine FEK-Konversion (Zykluszeit/Teile-pro-Zyklus/SGK), nie still gemacht (Review-Fix) |
| 13 | `ruestkosten` | `mfg_setup_cost_per_unit` | *`setupCostPerUnit`* (neu) | direct | NICHT `setupTimeSec` (Kosten ≠ Zeit) |
| 14 | `fk` | `mfg_manufacturing_cost_bw` | *`costPerUnit`* (neu) | direct | Primäre Prozesskosten je Teil (BW-Sicht) |
| 15 | `teilebenennung` | `mfg_part_designation` | `qafSource`-only | metadata | Teil-Kontext; fließt zusätzlich in Titel-Vorschlag (naming) |
| 16 | `lohnzuschlagssaetze` | `mfg_social_overhead_rate` | `qafSource`-only | metadata | Satz-Detail, kein primäres VSM-Feld — Detail-Panel |
| 17 | `fek` | `mfg_direct_manufacturing_cost` | `qafSource`-only | metadata | Kosten-Komponente von `fk`; Detail-Panel statt Doppel-Feld |
| 18 | `rfgk` | `mfg_remaining_overhead_rate` | `qafSource`-only | metadata | dito |
| 19 | `angebotswaehrung` | `mfg_quotation_currency` | `qafSource`-only | metadata | AW-Sicht; Primär-Anzeige ist BW |
| 20 | `wechselkurs` | `mfg_exchange_rate` | `qafSource`-only | metadata | |
| 21 | `anzahlProAngebotsteil` | `mfg_quantity_per_quoted_part` | `qafSource`-only | metadata | Mengen-Kontext; bewusst NICHT auf `quantity` (Inventory-Semantik) gezwungen |
| 22 | `fkAW` | `mfg_manufacturing_cost_aw` | `qafSource`-only | metadata | AW-Duplikat von `fk` |

**Abdeckung:** 14/22 Felder → primäre Node-Felder oder Sequenz, 8/22 → strukturierte Metadata (`qafSource.fields`, im Detail-Panel sichtbar). 0 Felder verworfen — Spec-Kriterium „All important QAF fields that cannot be mapped are retained as structured metadata" erfüllt per Konstruktion, weil ALLE 22 Felder ohnehin vollständig in `qafSource.fields` mit Zell-Referenz landen.

## Abgeleitete Werte (kein Quellfeld, dokumentierte Ableitung)

| Ziel | Ableitung | Regel |
|---|---|---|
| `vaClass` | aus `prozessbezeichnung` | konservative Muster-Klassifikation (`va-classification.ts`): Transport/Logistik→`nva`, Prüf-/Mess-/Inspektion→`nnva`, Rework/Nacharbeit→`nva`, Lager/Puffer→`nva`, SONST `unknown`. NIE automatisch `va` (Spec 14) |
| `isValueAdded` | `vaClass === 'va'` | Kompatibilität mit Bestand-Editor/Metriken; führend ist `vaClass` |
| `type` | konstant `'process'` | Fertigungszeilen sind Prozessschritte; supplier/customer-Endpunkte setzt der Mapper NICHT automatisch dazu (kein Erfinden von Quell-/Senke-Knoten in qvs-1 — Nutzer kann sie im Editor ergänzen) |
| `x`, `y` | Layout-Formel | lineare Kette, fester Abstand (Editor-Konvention `NODE_W=160` + Lücke); kein Auto-Layout-Anspruch |
| `fieldStatus` | alle gemappten Felder → `'imported'` | Editor setzt bei Änderung `'modified'` (Spec 18) |
| VSM-`title` | `naming.ts` | `[Lieferant] · [Projekt] · [Variante] · QAF-Wertstrom` — nur real vorhandene Teile, Dateiname nie als Primärname (Spec 10); Quelldateiname separat im Import-Record |
| `VsmConnection[]` | aus Sequenz | lineare Verkettung Schritt i → i+1; `sequence_confidence` im Import-Record |

## Explizit NICHT gemappt (ehrlich, mit Grund)

- `machineTimeSec` / `manualTimeSec` — Quelle hat keinen Zeit-Split (nur `zykluszeit` gesamt). Bleibt leer; Korpus-Scan P1 prüft, ob Templates mit Split existieren → ggf. qvs-2.
- `setupTimeSec`, `waitTimeSec`, `transportTimeSec` — keine Quellfelder im QAF-Fertigungsblatt (nur Rüst-KOSTEN). Bleibt leer statt fabriziert.
- `capacityPerHour`, `oee`, `demand`, `distance`, `quantity` — keine Schritt-Ebene-Quelle. `sum_planned_capacity`/`sum_lot_size` sind Datei-Ebene und werden seit QVS-P4/KAR-973 geparst (summary-parser.ts) — bewusst NICHT auf eines dieser Schritt-Felder gemappt, sondern als Import-Record-Kontext (`engine_context`) und in den P4-Kennzahlen (`computeCapacityContext`, vsm-metrics.ts) genutzt.
- Multi-QAF-Varianten-Zykluszeiten je Schritt — existieren strukturell nicht (Gap G5); Varianten erscheinen als `variantTags` + Aggregat-Metadaten im Import-Record.
