# `PhoenixKitEcommerce.Migrations`
[🔗](https://github.com/BeamLabEU/phoenix_kit_ecommerce/blob/0.5.11/lib/phoenix_kit_ecommerce/migrations.ex#L1)

Module-owned migration chain for the shop tables (`phoenix_kit_shop_config`,
`phoenix_kit_shop_shipping_methods`, `phoenix_kit_shop_categories`,
`phoenix_kit_shop_products`, `phoenix_kit_shop_product_slugs`,
`phoenix_kit_shop_category_slugs`, `phoenix_kit_shop_carts`,
`phoenix_kit_shop_cart_items`, `phoenix_kit_shop_import_configs`,
`phoenix_kit_shop_import_logs`) plus the two slug-projection functions and
their triggers.

## Ownership situation — read before touching

All ten tables are core V135(+later) baseline tables; core still creates
them today. V1 is purely an ADOPTION step (owner decision 2026-09-05: this
chain adopts every shop table, including `phoenix_kit_shop_products` and
`phoenix_kit_shop_categories`, so upstream `phoenix_kit_ecommerce` — which
still owns products for hosts that have not switched to the catalogue —
stays whole through core's next baseline squash):

  * on existing installs every table is already there (core's baseline),
    the `CREATE TABLE IF NOT EXISTS` finds it, and the only new objects
    are the `CREATE OR REPLACE FUNCTION` bodies (already core-owned,
    unchanged) and the `pke_schema:1` marker;
  * on a hypothetical future install whose core baseline no longer
    creates these tables, the same statements create them —
    shape-identical to core's objects, with core's exact index,
    constraint, function and trigger names.

This deployment's `shop_products`/`shop_categories`/both slug
projections are deprecated in favor of `phoenix_kit_catalogue` later
(Block 3, after the storefront switch) via a host-side
`COMMENT ON TABLE … 'deprecated …'` — never a DROP, and not part of
this chain.

## V2 — `DROP DEFAULT` on the four `currency` columns

V1 adopts core's shape verbatim; V2 is the chain's first deliberate
divergence from it. Core's baseline declares `"currency" character
varying(3) DEFAULT 'USD'` on `phoenix_kit_shop_carts`,
`…_cart_items`, `…_products` and `…_shipping_methods`. Dropping the
Elixir-side `default: "USD"` (PR #31) did not remove that literal — it
only stopped Ecto from sending a value, which let Postgres substitute
`'USD'` itself. V2 drops the column defaults so an insert that names no
currency stores NULL. Existing rows are untouched; `down/1` restores the
defaults.

## What `down/1` is NOT

`down/1` unstamps the version marker and restores the V2 column
defaults; it NEVER drops any of the ten tables, the two functions, or
the two triggers. The tables are
core-created, and rolling back this module's chain must not destroy
data — only core's own baseline rollback does that.

The migrated version is tracked as a `pke_schema:<N>` COMMENT on
`phoenix_kit_shop_config` (the marker convention `phoenix_kit_billing`,
`phoenix_kit_entities` and `phoenix_kit_catalogue` also use). A
marker-less or foreign-comment table reads as version 0 — the
core-baseline shape before this chain existed.

Protocol: `phoenix_kit_hello_world` README, "Adopting a table core
already creates (extraction)".

Every column name below is double-quoted, unlike the `pg_dump` source
(which only quotes `"position"`, a reserved word). This is a
normalization, not drift — the two forms are semantically identical.

# `current_version`

```elixir
@spec current_version() :: pos_integer()
```

# `down`

Rolls back to `target` (`:version` in `opts`). Never drops any table — see the moduledoc.

# `down_statements`

```elixir
@spec down_statements(String.t(), non_neg_integer()) :: [String.t()]
```

The SQL `down/1` executes, as data (marker bookkeeping plus the V2 default restore).

# `migrated_version`

```elixir
@spec migrated_version(keyword() | map()) :: non_neg_integer()
```

The chain version applied in the database, read INSIDE a running
migration (via `repo()`, same as `up/1`/`down/1`).

# `migrated_version_runtime`

The chain version currently applied in the database, read OUTSIDE a
migration (the protocol shape core's update task calls — `opts` with
`:prefix`): the `pke_schema:<N>` marker when present; a marker-less or
foreign-comment table reads as `0` (core-baseline shape — V1 is purely
adoptive, there is no pre-chain content to defend).

# `up`

Applies every chain version up to `current_version/0` (idempotent).

# `up_statements`

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

The SQL `up/1` executes, as data — the testable single source. The test
suite scans this for: table/function/trigger names matching core's
exact names (including the five `*_uuid_idx` core embeds the schema name
into under a non-public prefix), a representative sample of index and
constraint names, no statement that can drop or truncate a table (except the scoped,
pre-existing `DELETE` inside the two adopted slug-projection function
bodies — core-authored runtime logic, unchanged, not migration-time
destruction), and that every index/constraint is guarded.

# `version_table`

```elixir
@spec version_table() :: String.t()
```

The table carrying the `pke_schema:<N>` marker (auditor contract).

---

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