Files
eden/packages/database/migrations/1788438907074_products.cjs
Matheus (Handix) e5fd75c44a Add Products/Catalog module (MVP): migration, repository, API
Ports the legacy products table's core shape (eden.md §2 §1.1, Master
Prompt §6.3) faithfully but MVP-scoped: product_code sequence, the 5 fixed
price tiers (price_0/12/24/36/48), impl_unit_price, and stock/serial/
asset-tag/MAC tracking flags. Leaves out legacy telecom-billing specifics
(service_type, metered, tariff_rates, ixc_product_code) for a later phase.

Mirrors the Companies module's structure and conventions exactly: COALESCE
partial updates, JwtAuthGuard/PermissionGuard with open read / gated write
(products:edit) per ADR-0004, product.created/product.updated audit events.
Money fields are validated as non-negative NUMERIC in the DTO only — no
pricing/calculation logic here, that belongs to the future Quotes phase.

Does not touch apps/api/src/app.module.ts or packages/database/src/index.ts
(wiring is left to integration) to avoid conflicting with sibling agents'
concurrent edits to those files; the ProductsService deep-imports the
repository module directly instead of via the shared barrel, noted in a
code comment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 09:38:35 -03:00

69 lines
3.2 KiB
JavaScript

/**
* Commercial bounded context, first slice: products (sellable catalog
* items) — Master Prompt §6.3, eden.md §2 §1.1. MVP scope only: the shape
* below ports the legacy `products` table's core identity/pricing/flag
* columns faithfully (product_code sequence, the 5 fixed price tiers,
* impl_unit_price), but deliberately leaves out service_type/metered/
* tariff_rates/ixc_product_code — those are legacy telecom-billing
* specifics that belong to a later phase, not invented here. `product_type`
* is free text for now (no CHECK) since the enum of types isn't finalized;
* tightening it to a CHECK/lookup table is future work once Quotes needs
* to branch behavior on it.
*
* Quotes/pricing-tier selection logic is explicitly out of scope here —
* this migration only stores the data, it computes nothing.
*/
exports.shorthands = undefined;
exports.up = (pgm) => {
pgm.sql(`
CREATE SEQUENCE products_product_code_seq START 1;
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
created_by UUID REFERENCES users(id),
product_code INTEGER NOT NULL UNIQUE DEFAULT nextval('products_product_code_seq'),
name TEXT NOT NULL,
description TEXT,
product_type TEXT NOT NULL,
product_group TEXT,
product_subgroup TEXT,
brand TEXT,
unit TEXT NOT NULL DEFAULT 'unidade',
active BOOLEAN NOT NULL DEFAULT true,
discontinued BOOLEAN NOT NULL DEFAULT false,
tracks_stock BOOLEAN NOT NULL DEFAULT false,
tracks_serial BOOLEAN NOT NULL DEFAULT false,
tracks_asset_tag BOOLEAN NOT NULL DEFAULT false,
tracks_mac BOOLEAN NOT NULL DEFAULT false,
price_0 NUMERIC(12,2),
price_12 NUMERIC(12,2),
price_24 NUMERIC(12,2),
price_36 NUMERIC(12,2),
price_48 NUMERIC(12,2),
impl_unit_price NUMERIC(12,2) NOT NULL DEFAULT 0
);
COMMENT ON TABLE products IS
'Catálogo de produtos vendáveis — eden.md §2 §1.1, Master Prompt §6.3. product_code é a sequência legada (distinta do UUID); price_0/12/24/36/48 são as 5 faixas fixas de preço por período de fidelidade, não uma tabela relacionada. Leitura aberta a qualquer autenticado (catálogo precisa ser navegável por qualquer vendedor); escrita restrita à permissão products:edit.';
COMMENT ON COLUMN products.impl_unit_price IS
'Valor de implantação por unidade, multiplicado pela quantidade do item na oferta (fase futura — Quotes). NUMERIC sempre, nunca float.';
CREATE TRIGGER trg_products_updated_at BEFORE UPDATE ON products
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
CREATE INDEX idx_products_active ON products(active);
ALTER SEQUENCE products_product_code_seq OWNED BY products.product_code;
`);
};
exports.down = (pgm) => {
pgm.sql(`
DROP TABLE IF EXISTS products;
DROP SEQUENCE IF EXISTS products_product_code_seq;
`);
};