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>
This commit is contained in:
68
packages/database/migrations/1788438907074_products.cjs
Normal file
68
packages/database/migrations/1788438907074_products.cjs
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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;
|
||||
`);
|
||||
};
|
||||
Reference in New Issue
Block a user