Packages

phoenix_kit

1.7.193
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 v140.ex
Raw

lib/phoenix_kit/migrations/postgres/v140.ex

defmodule PhoenixKit.Migrations.Postgres.V140 do
@moduledoc """
V140: Warehouse module tables.
Creates the six `phoenix_kit_warehouse_*` tables that back the standalone
`phoenix_kit_warehouse` package: `stock`, `inventory_documents`,
`internal_orders`, `supplier_orders`, `goods_receipts`, `goods_issues`.
These mirror the shape of the host-application tables the module was
extracted from, minus every FK that pointed at a host-specific table:
- `internal_orders` and `goods_issues` drop the `sub_order_uuid` FK — that
relationship now lives exclusively in the generic `source_refs` JSONB
column (`[%{"kind" => "sub_order", "uuid" => ...}, ...]`), resolved by a
host-registered callback rather than a hard FK to an order table this
package doesn't own. A GIN index on `source_refs` stands in for the index
the dropped FK column used to provide, so the reverse lookup ("which
documents reference this order?") stays off a sequential scan.
- Intra-module FKs are kept: `supplier_orders.internal_order_uuid`
`internal_orders`, `goods_receipts.supplier_order_uuid`
`supplier_orders`, `goods_issues.internal_order_uuid``internal_orders`.
- `performed_by_uuid` stays FK'd to `phoenix_kit_users` on every document
table (core-to-core reference, unchanged from the originals).
- `item_uuid`, `location_uuid`, `storage_folder_uuid`, and `supplier_uuid`
are plain UUID columns. Their targets (`phoenix_kit_cat_items`,
`phoenix_kit_locations`, `phoenix_kit_media_folders`,
`phoenix_kit_cat_suppliers`) are all created by this same core migration
set, so an FK would be physically possible. It is omitted because the
delete semantics — restrict a location that still holds stock, versus
cascade the documents away with it — are a product decision the warehouse
package has not made yet. Until it does, referential integrity for these
four columns is **not** enforced by the database, and a dangling
`location_uuid` will not be rejected on insert.
This migration ships the tables only — no application code reads or writes
them yet, and no data is copied from the host's legacy tables (that is a
separate, later host-side data migration, run only after the new package
and its LiveViews are built and verified).
"""
use Ecto.Migration
def up(opts) do
prefix = Map.get(opts, :prefix, "public")
p = prefix_str(prefix)
# 1. Stock — per (item, location) balance.
execute("""
CREATE TABLE IF NOT EXISTS #{p}phoenix_kit_warehouse_stock (
uuid UUID PRIMARY KEY DEFAULT #{p}uuid_generate_v7(),
item_uuid UUID NOT NULL,
location_uuid UUID NOT NULL,
quantity NUMERIC NOT NULL DEFAULT 0,
unit_value NUMERIC,
inserted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS phoenix_kit_warehouse_stock_item_location_index
ON #{p}phoenix_kit_warehouse_stock (item_uuid, location_uuid)
""")
# The composite index above only serves item_uuid-leading lookups; "what is
# stored at this location" needs location_uuid on its own.
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_stock_location_uuid_index
ON #{p}phoenix_kit_warehouse_stock (location_uuid)
""")
execute("""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'phoenix_kit_warehouse_stock_quantity_non_negative'
AND conrelid = '#{p}phoenix_kit_warehouse_stock'::regclass
) THEN
ALTER TABLE #{p}phoenix_kit_warehouse_stock
ADD CONSTRAINT phoenix_kit_warehouse_stock_quantity_non_negative
CHECK (quantity >= 0);
END IF;
END $$;
""")
# 2. Inventory documents (stocktakes) — standalone, no source_refs.
execute(
"CREATE SEQUENCE IF NOT EXISTS #{p}phoenix_kit_warehouse_inventory_documents_number_seq"
)
execute("""
CREATE TABLE IF NOT EXISTS #{p}phoenix_kit_warehouse_inventory_documents (
uuid UUID PRIMARY KEY DEFAULT #{p}uuid_generate_v7(),
number BIGINT NOT NULL DEFAULT nextval('#{p}phoenix_kit_warehouse_inventory_documents_number_seq'),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
track_value BOOLEAN NOT NULL DEFAULT false,
location_uuid UUID NOT NULL,
storage_folder_uuid UUID,
note TEXT,
lines JSONB NOT NULL DEFAULT '[]'::jsonb,
created_by_uuid UUID,
performed_by_uuid UUID REFERENCES #{p}phoenix_kit_users(uuid) ON DELETE SET NULL,
posted_at TIMESTAMPTZ,
deleted_at TIMESTAMPTZ,
deleted_by_uuid UUID,
inserted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS phoenix_kit_warehouse_inventory_documents_number_index
ON #{p}phoenix_kit_warehouse_inventory_documents (number)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_inventory_documents_status_index
ON #{p}phoenix_kit_warehouse_inventory_documents (status)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_inventory_documents_inserted_at_index
ON #{p}phoenix_kit_warehouse_inventory_documents (inserted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_inventory_documents_deleted_at_index
ON #{p}phoenix_kit_warehouse_inventory_documents (deleted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_inventory_documents_location_uuid_index
ON #{p}phoenix_kit_warehouse_inventory_documents (location_uuid)
""")
# 3. Internal orders — no sub_order_uuid column; source_refs carries it.
execute("CREATE SEQUENCE IF NOT EXISTS #{p}phoenix_kit_warehouse_internal_orders_number_seq")
execute("""
CREATE TABLE IF NOT EXISTS #{p}phoenix_kit_warehouse_internal_orders (
uuid UUID PRIMARY KEY DEFAULT #{p}uuid_generate_v7(),
number BIGINT NOT NULL DEFAULT nextval('#{p}phoenix_kit_warehouse_internal_orders_number_seq'),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
location_uuid UUID NOT NULL,
note TEXT,
lines JSONB NOT NULL DEFAULT '[]'::jsonb,
source_refs JSONB NOT NULL DEFAULT '[]'::jsonb,
created_by_uuid UUID,
performed_by_uuid UUID REFERENCES #{p}phoenix_kit_users(uuid) ON DELETE SET NULL,
posted_at TIMESTAMPTZ,
deleted_at TIMESTAMPTZ,
deleted_by_uuid UUID,
inserted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS phoenix_kit_warehouse_internal_orders_number_index
ON #{p}phoenix_kit_warehouse_internal_orders (number)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_internal_orders_status_index
ON #{p}phoenix_kit_warehouse_internal_orders (status)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_internal_orders_inserted_at_index
ON #{p}phoenix_kit_warehouse_internal_orders (inserted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_internal_orders_deleted_at_index
ON #{p}phoenix_kit_warehouse_internal_orders (deleted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_internal_orders_location_uuid_index
ON #{p}phoenix_kit_warehouse_internal_orders (location_uuid)
""")
# Replaces the index the dropped sub_order_uuid FK used to provide: without
# it, "which orders reference this source?" is a sequential scan.
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_internal_orders_source_refs_index
ON #{p}phoenix_kit_warehouse_internal_orders USING GIN (source_refs)
""")
create_supplier_orders(p)
create_goods_receipts(p)
create_goods_issues(p)
execute("COMMENT ON TABLE #{p}phoenix_kit IS '140'")
end
@doc """
Drops all six warehouse tables and their sequences, in FK-safe order
(dependents before the tables they reference). Destroys any data written
to them — safe only because, per this plan, nothing writes to them yet.
"""
def down(opts) do
prefix = Map.get(opts, :prefix, "public")
p = prefix_str(prefix)
execute("DROP TABLE IF EXISTS #{p}phoenix_kit_warehouse_goods_issues")
execute("DROP SEQUENCE IF EXISTS #{p}phoenix_kit_warehouse_goods_issues_number_seq")
execute("DROP TABLE IF EXISTS #{p}phoenix_kit_warehouse_goods_receipts")
execute("DROP SEQUENCE IF EXISTS #{p}phoenix_kit_warehouse_goods_receipts_number_seq")
execute("DROP TABLE IF EXISTS #{p}phoenix_kit_warehouse_supplier_orders")
execute("DROP SEQUENCE IF EXISTS #{p}phoenix_kit_warehouse_supplier_orders_number_seq")
execute("DROP TABLE IF EXISTS #{p}phoenix_kit_warehouse_internal_orders")
execute("DROP SEQUENCE IF EXISTS #{p}phoenix_kit_warehouse_internal_orders_number_seq")
execute("DROP TABLE IF EXISTS #{p}phoenix_kit_warehouse_inventory_documents")
execute("DROP SEQUENCE IF EXISTS #{p}phoenix_kit_warehouse_inventory_documents_number_seq")
execute("DROP TABLE IF EXISTS #{p}phoenix_kit_warehouse_stock")
execute("COMMENT ON TABLE #{p}phoenix_kit IS '139'")
end
defp create_supplier_orders(p) do
execute("CREATE SEQUENCE IF NOT EXISTS #{p}phoenix_kit_warehouse_supplier_orders_number_seq")
execute("""
CREATE TABLE IF NOT EXISTS #{p}phoenix_kit_warehouse_supplier_orders (
uuid UUID PRIMARY KEY DEFAULT #{p}uuid_generate_v7(),
number BIGINT NOT NULL DEFAULT nextval('#{p}phoenix_kit_warehouse_supplier_orders_number_seq'),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
supplier_uuid UUID,
internal_order_uuid UUID REFERENCES #{p}phoenix_kit_warehouse_internal_orders(uuid) ON DELETE SET NULL,
location_uuid UUID NOT NULL,
note TEXT,
storage_folder_uuid UUID,
lines JSONB NOT NULL DEFAULT '[]'::jsonb,
source_refs JSONB NOT NULL DEFAULT '[]'::jsonb,
created_by_uuid UUID,
performed_by_uuid UUID REFERENCES #{p}phoenix_kit_users(uuid) ON DELETE SET NULL,
posted_at TIMESTAMPTZ,
deleted_at TIMESTAMPTZ,
deleted_by_uuid UUID,
inserted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS phoenix_kit_warehouse_supplier_orders_number_index
ON #{p}phoenix_kit_warehouse_supplier_orders (number)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_supplier_orders_status_index
ON #{p}phoenix_kit_warehouse_supplier_orders (status)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_supplier_orders_inserted_at_index
ON #{p}phoenix_kit_warehouse_supplier_orders (inserted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_supplier_orders_deleted_at_index
ON #{p}phoenix_kit_warehouse_supplier_orders (deleted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_supplier_orders_location_uuid_index
ON #{p}phoenix_kit_warehouse_supplier_orders (location_uuid)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_supplier_orders_supplier_uuid_index
ON #{p}phoenix_kit_warehouse_supplier_orders (supplier_uuid)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_supplier_orders_internal_order_uuid_index
ON #{p}phoenix_kit_warehouse_supplier_orders (internal_order_uuid)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_supplier_orders_source_refs_index
ON #{p}phoenix_kit_warehouse_supplier_orders USING GIN (source_refs)
""")
end
defp create_goods_receipts(p) do
execute("CREATE SEQUENCE IF NOT EXISTS #{p}phoenix_kit_warehouse_goods_receipts_number_seq")
execute("""
CREATE TABLE IF NOT EXISTS #{p}phoenix_kit_warehouse_goods_receipts (
uuid UUID PRIMARY KEY DEFAULT #{p}uuid_generate_v7(),
number BIGINT NOT NULL DEFAULT nextval('#{p}phoenix_kit_warehouse_goods_receipts_number_seq'),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
supplier_order_uuid UUID REFERENCES #{p}phoenix_kit_warehouse_supplier_orders(uuid) ON DELETE SET NULL,
supplier_uuid UUID,
location_uuid UUID NOT NULL,
note TEXT,
storage_folder_uuid UUID,
lines JSONB NOT NULL DEFAULT '[]'::jsonb,
source_refs JSONB NOT NULL DEFAULT '[]'::jsonb,
created_by_uuid UUID,
performed_by_uuid UUID REFERENCES #{p}phoenix_kit_users(uuid) ON DELETE SET NULL,
posted_at TIMESTAMPTZ,
deleted_at TIMESTAMPTZ,
deleted_by_uuid UUID,
inserted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_receipts_number_index
ON #{p}phoenix_kit_warehouse_goods_receipts (number)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_receipts_status_index
ON #{p}phoenix_kit_warehouse_goods_receipts (status)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_receipts_inserted_at_index
ON #{p}phoenix_kit_warehouse_goods_receipts (inserted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_receipts_deleted_at_index
ON #{p}phoenix_kit_warehouse_goods_receipts (deleted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_receipts_location_uuid_index
ON #{p}phoenix_kit_warehouse_goods_receipts (location_uuid)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_receipts_supplier_order_uuid_index
ON #{p}phoenix_kit_warehouse_goods_receipts (supplier_order_uuid)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_receipts_source_refs_index
ON #{p}phoenix_kit_warehouse_goods_receipts USING GIN (source_refs)
""")
end
defp create_goods_issues(p) do
execute("CREATE SEQUENCE IF NOT EXISTS #{p}phoenix_kit_warehouse_goods_issues_number_seq")
execute("""
CREATE TABLE IF NOT EXISTS #{p}phoenix_kit_warehouse_goods_issues (
uuid UUID PRIMARY KEY DEFAULT #{p}uuid_generate_v7(),
number BIGINT NOT NULL DEFAULT nextval('#{p}phoenix_kit_warehouse_goods_issues_number_seq'),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
internal_order_uuid UUID REFERENCES #{p}phoenix_kit_warehouse_internal_orders(uuid) ON DELETE SET NULL,
location_uuid UUID NOT NULL,
note TEXT,
storage_folder_uuid UUID,
lines JSONB NOT NULL DEFAULT '[]'::jsonb,
source_refs JSONB NOT NULL DEFAULT '[]'::jsonb,
created_by_uuid UUID,
performed_by_uuid UUID REFERENCES #{p}phoenix_kit_users(uuid) ON DELETE SET NULL,
posted_at TIMESTAMPTZ,
deleted_at TIMESTAMPTZ,
deleted_by_uuid UUID,
inserted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
""")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_issues_number_index
ON #{p}phoenix_kit_warehouse_goods_issues (number)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_issues_status_index
ON #{p}phoenix_kit_warehouse_goods_issues (status)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_issues_inserted_at_index
ON #{p}phoenix_kit_warehouse_goods_issues (inserted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_issues_deleted_at_index
ON #{p}phoenix_kit_warehouse_goods_issues (deleted_at)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_issues_location_uuid_index
ON #{p}phoenix_kit_warehouse_goods_issues (location_uuid)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_issues_internal_order_uuid_index
ON #{p}phoenix_kit_warehouse_goods_issues (internal_order_uuid)
""")
execute("""
CREATE INDEX IF NOT EXISTS phoenix_kit_warehouse_goods_issues_source_refs_index
ON #{p}phoenix_kit_warehouse_goods_issues USING GIN (source_refs)
""")
end
defp prefix_str("public"), do: "public."
defp prefix_str(prefix), do: "#{prefix}."
end