# `PhoenixKitCatalogue.Import.Matcher`
[🔗](https://github.com/BeamLabEU/phoenix_kit_catalogue/blob/v0.19.0/lib/phoenix_kit_catalogue/import/matcher.ex#L1)

Matches PRO100 rows to existing catalogue items.

The file identifies a row by a numeric code, which is our SKU reduced to digits
(`Pro100.Id.digits_only/1`). That reduction is lossy — it drops the decor letter
and the finish, so `73.U767.18` and `73.U767.PM.18` both become `7376718`.
Matching on it alone leaves every such row permanently ambiguous: on a real
export, 28 codes were shared by 62 rows, none of which could ever be updated.

Resolution therefore tries `{digits, name}` first and falls back to digits alone
only when that is unique. The name decides when the code cannot; the code still
decides when the name has drifted.

Both indexes skip items whose SKU carries no digits. That is what stops a
code-less file row from matching a SKU-less item by name alone — the file's
`Tööpind` spacer row (price `0.00`) is named like a real item and would zero
its price.

# `t`

```elixir
@type t() :: %{
  digits: %{required(String.t()) =&gt; [PhoenixKitCatalogue.Schemas.Item.t()]},
  digits_name: %{
    required({String.t(), String.t()}) =&gt; [PhoenixKitCatalogue.Schemas.Item.t()]
  }
}
```

# `index`

```elixir
@spec index([PhoenixKitCatalogue.Schemas.Item.t()]) :: t()
```

# `normalize_name`

```elixir
@spec normalize_name(String.t() | nil) :: String.t()
```

Collapses a name to its match key: case-folded, whitespace-collapsed, trimmed.

Applied to `item.name` when the index is built and to the row name when a row
is resolved. It must stay a single function: two copies that drift by one
character make the name stage silently stop matching, and the symptom looks
like a data problem rather than a code one.

    iex> PhoenixKitCatalogue.Import.Matcher.normalize_name("  MP  U741   ST9 ")
    "mp u741 st9"

    iex> PhoenixKitCatalogue.Import.Matcher.normalize_name(nil)
    ""

# `resolve`

```elixir
@spec resolve(t(), String.t(), String.t() | nil) ::
  {:matched, PhoenixKitCatalogue.Schemas.Item.t()}
  | {:ambiguous, [PhoenixKitCatalogue.Schemas.Item.t()]}
  | :unmatched
```

Resolves one row against the index.

Stages, in order: a blank id never matches; `{id, name}` wins when unique; the
id alone wins when unique; an id held by several items is refused rather than
guessed.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
