# `PhoenixKitCatalogue.Schemas.Item`
[🔗](https://github.com/BeamLabEU/phoenix_kit_catalogue/blob/0.1.14/lib/phoenix_kit_catalogue/schemas/item.ex#L1)

Schema for catalogue items — individual products/materials with SKU and pricing.

V102 added discount + smart-catalogue fields: `discount_percentage` (per-item
override of the catalogue discount, NULL = inherit), and `default_value` /
`default_unit` (smart-only fallbacks consumed by `CatalogueRule.effective/2`
when a rule row leaves either leg NULL). The optional `:catalogue_rules`
association mirrors the V102 rules table — only populated for items in a
smart catalogue.

# `t`

```elixir
@type t() :: %PhoenixKitCatalogue.Schemas.Item{
  __meta__: term(),
  base_price: term(),
  catalogue: term(),
  catalogue_rules: term(),
  catalogue_uuid: term(),
  category: term(),
  category_uuid: term(),
  data: term(),
  default_unit: term(),
  default_value: term(),
  description: term(),
  discount_percentage: term(),
  inserted_at: term(),
  manufacturer: term(),
  manufacturer_uuid: term(),
  markup_percentage: term(),
  name: term(),
  sku: term(),
  status: term(),
  unit: term(),
  updated_at: term(),
  uuid: term()
}
```

# `allowed_default_units`

```elixir
@spec allowed_default_units() :: [String.t()]
```

# `allowed_units`

```elixir
@spec allowed_units() :: [String.t()]
```

# `changeset`

```elixir
@spec changeset(t() | Ecto.Changeset.t(t()), map()) :: Ecto.Changeset.t(t())
```

# `discount_amount`

```elixir
@spec discount_amount(t(), Decimal.t() | nil, Decimal.t() | nil) :: Decimal.t() | nil
```

Returns the Decimal amount subtracted by the discount for an item —
i.e. `sale_price - final_price`. Useful for "You save $X" UI.

Returns `nil` when `base_price` is `nil` or when no discount applies
(both catalogue and item discount are `nil`).

# `effective_discount`

```elixir
@spec effective_discount(t(), Decimal.t() | nil) :: Decimal.t() | nil
```

Returns the discount percentage that actually applies to an item — the
item's own `discount_percentage` if set, otherwise `catalogue_discount`.

Mirrors `effective_markup/2`: `nil` on the item means "inherit the
catalogue's discount", any Decimal (including `0`) overrides. `nil` on
both sides means "no discount at all".

Use this when you need to display which discount is active without
computing the final price.

# `effective_markup`

```elixir
@spec effective_markup(t(), Decimal.t() | nil) :: Decimal.t() | nil
```

Returns the markup percentage that actually applies to an item — the
item's own `markup_percentage` if set, otherwise `catalogue_markup`.

`nil` on both sides means "no markup at all" and the item should be
sold at its base price. Callers that only need to *display* which
markup is active (without computing a price) can use this directly.

# `final_price`

```elixir
@spec final_price(t(), Decimal.t() | nil, Decimal.t() | nil) :: Decimal.t() | nil
```

Returns the final price for an item — `base_price` with the effective
markup applied, then the effective discount subtracted.

The chain is `base → markup → discount`:

    sale_price  = base_price * (1 + effective_markup   / 100)
    final_price = sale_price  * (1 -  effective_discount / 100)

`catalogue_markup` and `catalogue_discount` are the fallbacks used when
the item has no matching override of its own. `nil` on either side
means "no markup / no discount on that leg"; the other leg still applies.

Returns `nil` when `base_price` is `nil`. Result is rounded to 2
decimal places. Percentage values should be `Decimal`s (e.g.
`Decimal.new("15.0")`).

## Examples

    # 100 * 1.20 * 0.90 = 108.00
    Item.final_price(
      %Item{base_price: Decimal.new("100"), markup_percentage: nil, discount_percentage: nil},
      Decimal.new("20"),
      Decimal.new("10")
    )
    #=> Decimal.new("108.00")

    # Per-item discount 0 overrides a catalogue discount of 10 →
    # final equals sale_price
    Item.final_price(
      %Item{base_price: Decimal.new("100"), discount_percentage: Decimal.new("0")},
      Decimal.new("20"),
      Decimal.new("10")
    )
    #=> Decimal.new("120.00")

# `sale_price`

```elixir
@spec sale_price(t(), Decimal.t() | nil) :: Decimal.t() | nil
```

Calculates the sale price for an item.

`catalogue_markup` is the fallback markup used when the item has no
override of its own. The item's `markup_percentage` takes precedence
if set (including an explicit `0`, which means "sell at base price
even if the catalogue has a markup"). A `nil` catalogue_markup with a
`nil` item override returns the base price unchanged.

Returns `nil` if the item has no base price. Both percentage values
should be `Decimal`s (e.g., `Decimal.new("15.0")` for 15%).

## Examples

    # Item has no override — inherits catalogue's 20%
    Item.sale_price(%Item{base_price: Decimal.new("100"), markup_percentage: nil}, Decimal.new("20"))
    #=> Decimal.new("120.00")

    # Item explicitly overrides to 50% — catalogue markup is ignored
    Item.sale_price(%Item{base_price: Decimal.new("100"), markup_percentage: Decimal.new("50")}, Decimal.new("20"))
    #=> Decimal.new("150.00")

    # Item override of 0 means "sell at base price" even if catalogue marks up
    Item.sale_price(%Item{base_price: Decimal.new("100"), markup_percentage: Decimal.new("0")}, Decimal.new("20"))
    #=> Decimal.new("100.00")

---

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