# `PhoenixKitEcommerce.Shopify.ProductDiff.Change`
[🔗](https://github.com/BeamLabEU/phoenix_kit_ecommerce/blob/0.5.10/lib/phoenix_kit_ecommerce/shopify/product_diff.ex#L62)

One local product's field-by-field diff against its matched Shopify
product. `changes` only contains fields that actually differ, keyed by
field atom, each holding `%{current:, incoming:}`. `price_extreme?` is
true only when `:price` is in `changes` and the current/incoming ratio
exceeds 3x.

`base_locale` is the locale `diff/4` matched/compared this product
against — carried on the struct (not re-derived later) so
`Shopify.Sync.apply_change/2` writes localized fields back into the
SAME locale they were diffed against. Reading a fresh default at
apply time would silently apply a change diffed in one locale into
another.

`create?`/`shopify_product` are only set on a create-`Change` (see
`ProductDiff.new_product_changes/3`, catalogue source only):
`product_uuid` is `nil` (there is no local product yet), `changes` is
always `%{}`, and `shopify_product` carries the raw Shopify payload
`Shopify.Sync.apply_change/2` hands to
`PhoenixKitEcommerce.Catalogue.Writer.create_from_shopify/2`. A
regular diff-`Change` never sets `shopify_product`.

`product_id` is the Shopify product's own `"id"` — carried on every
`Change` (regular or create), independent of `changes`/`opts[:only]`:
it isn't diffed against a current value, just carried through, so
`Shopify.Sync.apply_change/2` can backfill `data["ecommerce"]
["shopify"]["product_id"]` on a matched item even when the caller
only asked to apply a subset of the comparable fields. This does NOT
reach every local product on its own — `diff/4` still only returns a
`Change` when some comparable field actually differs (see its own
moduledoc); `product_id` just rides along on whichever `Change`s
already exist.

# `t`

```elixir
@type t() :: %PhoenixKitEcommerce.Shopify.ProductDiff.Change{
  base_locale: String.t(),
  changes: %{required(atom()) =&gt; %{current: term(), incoming: term()}},
  create?: boolean(),
  handle: String.t(),
  price_extreme?: boolean(),
  product_id: term() | nil,
  product_uuid: String.t() | nil,
  shopify_product: map() | nil,
  title: String.t() | nil
}
```

---

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