# `PhoenixKitCatalogue.Catalogue.Suppliers`
[🔗](https://github.com/BeamLabEU/phoenix_kit_catalogue/blob/v0.12.4/lib/phoenix_kit_catalogue/catalogue/suppliers.ex#L1)

Suppliers — delivery companies linked to manufacturers via the
many-to-many `phoenix_kit_cat_manufacturer_suppliers` table.

Same lifecycle as manufacturers: hard-delete only, `"active"` /
`"inactive"` status.

### Cross-module supplier resolution

`resolve/1` and `list_all/1` provide a unified view of suppliers across
sources (local `cat_suppliers` + CRM when available). CRM access is
guarded via `Code.ensure_loaded?` / `function_exported?` — the CRM module
is an optional runtime dependency and may not be present.

Public surface is re-exported from `PhoenixKitCatalogue.Catalogue`.

# `active_info_for`

```elixir
@spec active_info_for(Ecto.UUID.t(), Ecto.UUID.t()) ::
  PhoenixKitCatalogue.Schemas.ItemSupplierInfo.t() | nil
```

Returns the *current* junction row for an item/supplier pair, or `nil`.

"Current" means `valid_to` is `nil`. This is the function warehouse calls
to check whether a receipt line's unit price diverges from the catalogued
cost for the same supplier.

# `change_supplier`

```elixir
@spec change_supplier(PhoenixKitCatalogue.Schemas.Supplier.t(), map()) ::
  Ecto.Changeset.t(PhoenixKitCatalogue.Schemas.Supplier.t())
```

Returns a changeset for tracking supplier changes.

# `create_supplier`

```elixir
@spec create_supplier(
  map(),
  keyword()
) ::
  {:ok, PhoenixKitCatalogue.Schemas.Supplier.t()}
  | {:error, Ecto.Changeset.t(PhoenixKitCatalogue.Schemas.Supplier.t())}
```

Creates a supplier.

## Required attributes

  * `:name` — supplier name (1-255 chars)

## Optional attributes

  * `:description`, `:website`, `:contact_info`, `:notes`
  * `:status` — `"active"` (default) or `"inactive"`
  * `:data` — flexible JSON map

# `delete_supplier`

```elixir
@spec delete_supplier(
  PhoenixKitCatalogue.Schemas.Supplier.t(),
  keyword()
) ::
  {:ok, PhoenixKitCatalogue.Schemas.Supplier.t()}
  | {:error, Ecto.Changeset.t(PhoenixKitCatalogue.Schemas.Supplier.t())}
```

Hard-deletes a supplier from the database.

# `get_supplier`

```elixir
@spec get_supplier(Ecto.UUID.t()) :: PhoenixKitCatalogue.Schemas.Supplier.t() | nil
```

Fetches a supplier by UUID. Returns `nil` if not found.

# `get_supplier!`

```elixir
@spec get_supplier!(Ecto.UUID.t()) :: PhoenixKitCatalogue.Schemas.Supplier.t()
```

Fetches a supplier by UUID. Raises `Ecto.NoResultsError` if not found.

# `list_all`

```elixir
@spec list_all(keyword()) :: [map()]
```

Lists all suppliers from all available sources as normalized maps.

Each entry has keys `:uuid`, `:name`, `:email`, `:phone`, `:website`,
`:source` (`:crm_company | :crm_contact | :local`). CRM companies then
CRM contacts are listed first (when available), then local suppliers
ordered by name.

CRM access is guarded — when `PhoenixKitCRM.PartyRoles` is not loaded,
only local suppliers are returned.

# `list_suppliers`

```elixir
@spec list_suppliers(keyword()) :: [PhoenixKitCatalogue.Schemas.Supplier.t()]
```

Lists all suppliers, ordered by name.

## Options

  * `:status` — filter by status (e.g. `"active"`, `"inactive"`).

# `primary_for_item`

```elixir
@spec primary_for_item(Ecto.UUID.t()) ::
  PhoenixKitCatalogue.Schemas.ItemSupplierInfo.t() | nil
```

Returns the primary supplier-info row for an item, or `nil` if none is marked primary.

# `resolve`

```elixir
@spec resolve(Ecto.UUID.t()) :: {:ok, map()} | :error
```

Resolves a supplier UUID to a unified map regardless of source.

Returns `{:ok, map}` with keys `:uuid`, `:name`, `:email`, `:phone`,
`:website`, `:source` (`:crm | :local`), or `:error` when the supplier
cannot be found in any source.

The CRM branch reports the generic `:crm` tag rather than
`:crm_company` / `:crm_contact` — `PhoenixKitCRM.PartyRoles.get_supplier/1`
resolves either roleable type in one call but its return shape doesn't
say which; `list_all/1` (backed by per-type role listings) is the
source for the more specific tags used elsewhere in this module.

CRM lookup is guarded — when `PhoenixKitCRM.PartyRoles` is not loaded
(the CRM module is an optional runtime dependency), the CRM path is
skipped and only local suppliers are checked.

# `revise_unit_cost`

```elixir
@spec revise_unit_cost(
  PhoenixKitCatalogue.Schemas.ItemSupplierInfo.t(),
  Decimal.t(),
  keyword()
) ::
  {:ok, PhoenixKitCatalogue.Schemas.ItemSupplierInfo.t()}
  | {:error, :not_current | Ecto.Changeset.t()}
```

Delegates to `ItemSupplierInfos.revise_unit_cost/3`.

This is the stable public surface that warehouse and other consumers should
call. See `ItemSupplierInfos.revise_unit_cost/3` for full documentation.

# `update_supplier`

```elixir
@spec update_supplier(PhoenixKitCatalogue.Schemas.Supplier.t(), map(), keyword()) ::
  {:ok, PhoenixKitCatalogue.Schemas.Supplier.t()}
  | {:error, Ecto.Changeset.t(PhoenixKitCatalogue.Schemas.Supplier.t())}
```

Updates a supplier with the given attributes.

---

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