# qaf-differences

> Module created via `npm run new:module qaf-differences`.

## Owner

SupplierPulse / QAF

## Purpose

The auditable QAF Diff Engine (KAR-799): given parsed QAFs of the **same part
number**, it normalizes values, matches process steps via a 5-stage cascade,
computes field-level diffs (absolute / relative / percentage-points), and feeds
deterministic root-cause and plausibility analysis. UI-independent — usable from
server actions, batch jobs, CLI and tests. The UI is only a consumption layer.

## Public API

The public surface is the named exports of `index.ts`. Anything in
`internal/` is private and may change without notice.

Currently exported:

- **Types** — `SourceRef`, `MoneyValue`, `FieldDiff`, `StepMatch`, `MatchConfig`, status/method unions, `ENGINE_VERSION`.
- **Normalizer** — `parseLocaleNumber`, `normalizeProcessName`, `normalizePosition`, `normalizeCurrency`, `isBlank`.
- **Diff** — `computeFieldDiff`, `diffSteps`.
- **Multi-QAF container domain model** (KAR-929/Multi-QAF-Programm P1.1) — `MultiQafContainer`/`VariantDefinition`/`VariantMatrixRow`/`SharedCostProfile`/`VariantProfileBinding`/`ColumnClassification`/`VirtualQafVariant` types, `buildCompositeCanonicalKey`/`deriveActiveState` (identity), `serializeMultiQafContainer`/`deserializeMultiQafContainer` (versioned JSON round-trip), `toCanonicalInputs` (bridge to the standard comparison engine). Pure types + pure functions only — no DB migration, no ingest wiring. See `internal/multi-qaf/types.ts` module header for the full scope statement.
- **Multi-QAF variant header-block parser + column classifier** (KAR-930/Multi-QAF-Programm P1.2) — `parseVariantHeaderBlock` (pure, reads a Summary or BOM/detail-sheet grid: multi-row dimension block, optional numbered slot-index row with gap/outlier handling, volume rows) producing `VariantDefinition[]` + a full `ColumnClassification[]` scan report (active/inactive/reserved/benchmark/comparison/delta/percentage-delta/comment/helper/total/unknown), plus `headerParserInputFromWorksheet` (ExcelJS bridge, reuses workbook-adapter.ts's merged-cell/formula resolution). `classifyColumn` (column-classifier.ts) is the standalone classification decision, evidence-carrying, `unknown` a valid result. Conservative by design — no opinion on whether the workbook was confirmed Multi-QAF first (caller's job, not wired into ingest in this PR). See `internal/multi-qaf/header-parser.ts` module header for the full algorithm + real-file evidence.

Planned (in progress): matching cascade, baseline selection, root-cause, plausibility, Zusammenfassung parser, 8-sheet export.

## Dependencies

- `@/lib/qaf-parser` — `QAFRow` shape + Fertigungskosten parser (reused, extended).
- `@/lib/qaf/comparison` — `NUMERIC_FIELDS` (reused for diff coverage).

## Tests

Co-located under `internal/__tests__/` and `__tests__/`. Run via `npx vitest run lib/qaf-differences`.

## Conventions (ADR 019)

- No deep imports into other modules' `internal/` directories.
- This module never imports from `lib/customers/**` (ADR 010 / ADR 015).
- Customer-specific behaviour goes via the active composition profile (ADR 013).
