Packages

phoenix_kit

1.7.204
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit migrations postgres v151.ex
Raw

lib/phoenix_kit/migrations/postgres/v151.ex

defmodule PhoenixKit.Migrations.Postgres.V151 do
@moduledoc """
V151: supplier-info source/primary columns + CRM email normalization.
Completes the V149 `phoenix_kit_cat_item_supplier_info` junction with the
two columns the merged `phoenix_kit_catalogue` sourcing layer (catalogue
PR #44) reads and writes — without them every junction INSERT/UPDATE
crashes on an undefined column:
* `supplier_source` — `'crm_company' | 'crm_contact' | 'local'` (CHECK).
Disambiguates the polymorphic soft `supplier_uuid` (a CRM company, a
CRM contact, or a local `cat_suppliers` row) so the resolver and the
`phoenix_kit_catalogue.audit_supplier_refs` consistency report route
lookups without a trial cascade.
* `is_primary` + partial-unique index (at most one primary row per
item). The catalogue context auto-promotes the first linked supplier
and offers "make primary"; `Suppliers.primary_for_item/1` backs the
warehouse resolver.
The V146 scalar `phoenix_kit_cat_items.primary_supplier_uuid` is left
untouched (V149's design keeps it); the merged catalogue schema simply no
longer maps it.
Also normalises the CRM party email columns to `citext`
(`phoenix_kit_crm_contacts.email`, `phoenix_kit_crm_companies.email`):
case-insensitive matching is a prerequisite for the CRM v2 backfill
(match-by-email) and the user↔contact bridge (`connect_user/2` finds or
creates auth users by email). citext is a core dependency since V01
(`phoenix_kit_users.email`), so `ensure_extension!/1` is a no-op on any
existing install.
**citext conversion cost:** `ALTER COLUMN … TYPE citext` takes an ACCESS
EXCLUSIVE lock and rewrites the table — plan a maintenance window for
large CRM tables. Neither email column carries an index or a unique
constraint anywhere in the chain, so no index rebuild happens and no
latent case-duplicate can surface as a violation; if a unique index on
email is ever added later, case-variant duplicates must be resolved
first (citext compares case-insensitively).
All operations are idempotent.
"""
use Ecto.Migration
alias PhoenixKit.Migrations.Postgres.Helpers
def up(opts) do
prefix = Map.get(opts, :prefix, "public")
escaped_prefix = Map.get(opts, :escaped_prefix, prefix)
p = prefix_str(prefix)
# ------------------------------------------------------------------
# Block 1: supplier_source on the V149 junction
# ------------------------------------------------------------------
execute("""
ALTER TABLE #{p}phoenix_kit_cat_item_supplier_info
ADD COLUMN IF NOT EXISTS supplier_source VARCHAR(20) NOT NULL DEFAULT 'local'
""")
execute("""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'phoenix_kit_cat_item_supplier_info_supplier_source_check'
AND conrelid = '#{p}phoenix_kit_cat_item_supplier_info'::regclass
) THEN
ALTER TABLE #{p}phoenix_kit_cat_item_supplier_info
ADD CONSTRAINT phoenix_kit_cat_item_supplier_info_supplier_source_check
CHECK (supplier_source IN ('crm_company', 'crm_contact', 'local'));
END IF;
END $$;
""")
# ------------------------------------------------------------------
# Block 2: is_primary + partial-unique (one primary per item)
# ------------------------------------------------------------------
execute("""
ALTER TABLE #{p}phoenix_kit_cat_item_supplier_info
ADD COLUMN IF NOT EXISTS is_primary BOOLEAN NOT NULL DEFAULT FALSE
""")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS phoenix_kit_cat_item_supplier_info_primary_uniq
ON #{p}phoenix_kit_cat_item_supplier_info (item_uuid)
WHERE is_primary
""")
# ------------------------------------------------------------------
# Block 3: CRM email normalization to citext
# ------------------------------------------------------------------
# no-op when citext is already installed (avoids the privilege check on
# bare CREATE EXTENSION IF NOT EXISTS for low-privilege roles)
Helpers.ensure_extension!("citext")
execute("""
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = '#{escaped_prefix}'
AND table_name = 'phoenix_kit_crm_contacts'
AND column_name = 'email'
AND udt_name <> 'citext'
) THEN
ALTER TABLE #{p}phoenix_kit_crm_contacts
ALTER COLUMN email TYPE citext;
END IF;
END $$;
""")
execute("""
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = '#{escaped_prefix}'
AND table_name = 'phoenix_kit_crm_companies'
AND column_name = 'email'
AND udt_name <> 'citext'
) THEN
ALTER TABLE #{p}phoenix_kit_crm_companies
ALTER COLUMN email TYPE citext;
END IF;
END $$;
""")
execute("COMMENT ON TABLE #{p}phoenix_kit IS '151'")
end
@doc """
Rolls V151 back.
**Lossy for the two columns:** `supplier_source` values and `is_primary`
flags are dropped (the junction rows themselves survive). CRM email
columns revert from `citext` to `VARCHAR(255)` (their V138 shape).
"""
def down(opts) do
prefix = Map.get(opts, :prefix, "public")
escaped_prefix = Map.get(opts, :escaped_prefix, prefix)
p = prefix_str(prefix)
execute("""
DROP INDEX IF EXISTS #{p}phoenix_kit_cat_item_supplier_info_primary_uniq
""")
execute("""
ALTER TABLE #{p}phoenix_kit_cat_item_supplier_info
DROP COLUMN IF EXISTS is_primary
""")
execute("""
ALTER TABLE #{p}phoenix_kit_cat_item_supplier_info
DROP COLUMN IF EXISTS supplier_source
""")
execute("""
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = '#{escaped_prefix}'
AND table_name = 'phoenix_kit_crm_contacts'
AND column_name = 'email'
AND udt_name = 'citext'
) THEN
ALTER TABLE #{p}phoenix_kit_crm_contacts
ALTER COLUMN email TYPE VARCHAR(255);
END IF;
END $$;
""")
execute("""
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = '#{escaped_prefix}'
AND table_name = 'phoenix_kit_crm_companies'
AND column_name = 'email'
AND udt_name = 'citext'
) THEN
ALTER TABLE #{p}phoenix_kit_crm_companies
ALTER COLUMN email TYPE VARCHAR(255);
END IF;
END $$;
""")
execute("COMMENT ON TABLE #{p}phoenix_kit IS '150'")
end
defp prefix_str("public"), do: "public."
defp prefix_str(prefix), do: "#{prefix}."
end