All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m16s
568 lines
27 KiB
JavaScript
568 lines
27 KiB
JavaScript
const { Pool } = require('pg');
|
|
const { DATABASE_URL } = require('./config');
|
|
|
|
const pool = new Pool({
|
|
connectionString: DATABASE_URL
|
|
});
|
|
|
|
const defaultCatalogCategories = [
|
|
['Camiseta regular', 'Bases lisas e camisetas adultas.'],
|
|
['Camiseta infantil', 'Produtos infantis por cor e tamanho.'],
|
|
['Moletom', 'Moletons, cangurus e produtos de frio.'],
|
|
['Oversized', 'Modelagens oversized e variações relacionadas.'],
|
|
['Acessórios', 'Bonés, itens complementares e produtos não têxteis.'],
|
|
['DTF', 'Insumos e serviços relacionados a impressão DTF.'],
|
|
['Malha', 'Tecidos e malhas usados como matéria-prima.'],
|
|
['Aviamentos', 'Ribanas, linhas, ilhós e componentes de costura.'],
|
|
['Embalagens', 'Sacos, etiquetas, tags e materiais de expedição.'],
|
|
['Insumos gerais', 'Materiais de apoio sem família operacional específica.']
|
|
];
|
|
|
|
const seedDefaultCatalogCategories = async () => {
|
|
for (const [name, description] of defaultCatalogCategories) {
|
|
await pool.query(`
|
|
INSERT INTO catalog_categories (name, description, updated_at)
|
|
VALUES ($1, $2, CURRENT_TIMESTAMP)
|
|
ON CONFLICT (name) DO NOTHING;
|
|
`, [name, description]);
|
|
}
|
|
};
|
|
|
|
const initDB = async () => {
|
|
try {
|
|
await pool.query(`SET TIME ZONE 'America/Sao_Paulo';`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS orders (
|
|
id SERIAL PRIMARY KEY,
|
|
cliente_nome VARCHAR(255),
|
|
data_pedido VARCHAR(50),
|
|
data_pedido_date DATE,
|
|
valor_pedido NUMERIC(10, 2),
|
|
produto_id VARCHAR(100),
|
|
produto_descricao TEXT,
|
|
quantidade INTEGER,
|
|
valor_unitario NUMERIC(10, 5),
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS pedido_id VARCHAR(100);`).catch(() => {});
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS cliente_fone VARCHAR(50);`).catch(() => {});
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS data_pedido_date DATE;`).catch(() => {});
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS cliente_nome_fantasia VARCHAR(255);`).catch(() => {});
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS id_vendedor VARCHAR(100);`).catch(() => {});
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS nome_vendedor VARCHAR(255);`).catch(() => {});
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS marketplace VARCHAR(255);`).catch(() => {});
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS canal_venda VARCHAR(255);`).catch(() => {});
|
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS numero_ecommerce VARCHAR(100);`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE orders
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
UPDATE orders
|
|
SET data_pedido_date = CASE
|
|
WHEN data_pedido ~ '^\\d{4}[-/]\\d{1,2}[-/]\\d{1,2}' THEN to_date(replace(left(data_pedido, 10), '/', '-'), 'YYYY-MM-DD')
|
|
WHEN data_pedido ~ '^\\d{1,2}[-/]\\d{1,2}[-/]\\d{4}' THEN to_date(replace(left(data_pedido, 10), '/', '-'), 'DD-MM-YYYY')
|
|
ELSE NULL
|
|
END
|
|
WHERE data_pedido_date IS NULL
|
|
AND data_pedido IS NOT NULL
|
|
AND data_pedido != '';
|
|
`).catch(err => {
|
|
console.error('Notice: Could not backfill normalized order dates:', err.message);
|
|
});
|
|
|
|
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS unique_order_product ON orders (pedido_id, produto_id);`).catch(err => {
|
|
console.error('Notice: Could not create unique index (might already exist or there are duplicates):', err.message);
|
|
});
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS stock (
|
|
produto_id VARCHAR(100) PRIMARY KEY,
|
|
nome TEXT,
|
|
saldo INTEGER DEFAULT 0,
|
|
delta_estoque INTEGER DEFAULT 0,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
ALTER TABLE stock
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS stock_campaign_queue (
|
|
id SERIAL PRIMARY KEY,
|
|
base_product_name TEXT NOT NULL,
|
|
produto_id VARCHAR(100) NOT NULL,
|
|
nome TEXT NOT NULL,
|
|
saldo INTEGER DEFAULT 0,
|
|
delta_estoque INTEGER DEFAULT 0,
|
|
status VARCHAR(20) DEFAULT 'pending',
|
|
attempts INTEGER DEFAULT 0,
|
|
last_error TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
sent_at TIMESTAMPTZ
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS production_orders (
|
|
id SERIAL PRIMARY KEY,
|
|
tiny_id VARCHAR(100) UNIQUE,
|
|
number VARCHAR(100),
|
|
status VARCHAR(40) DEFAULT 'open',
|
|
order_reference TEXT,
|
|
issue_date DATE,
|
|
expected_date DATE,
|
|
product_sku VARCHAR(255),
|
|
product_description TEXT NOT NULL,
|
|
quantity NUMERIC(14, 4) DEFAULT 0,
|
|
unit VARCHAR(20) DEFAULT 'UN',
|
|
integration_status VARCHAR(100),
|
|
notes TEXT,
|
|
supplier TEXT,
|
|
lot_code VARCHAR(120),
|
|
roll_quantity NUMERIC(14, 4),
|
|
fabric_kg NUMERIC(14, 4),
|
|
rib_kg NUMERIC(14, 4),
|
|
yield_pieces_per_kg NUMERIC(14, 4),
|
|
tiny_payload JSONB,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS production_order_markers (
|
|
id SERIAL PRIMARY KEY,
|
|
production_order_id INTEGER NOT NULL REFERENCES production_orders(id) ON DELETE CASCADE,
|
|
label VARCHAR(100) NOT NULL,
|
|
color VARCHAR(40),
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE (production_order_id, label)
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS production_order_components (
|
|
id SERIAL PRIMARY KEY,
|
|
production_order_id INTEGER NOT NULL REFERENCES production_orders(id) ON DELETE CASCADE,
|
|
component_tiny_id VARCHAR(100),
|
|
component_sku VARCHAR(255),
|
|
component_name TEXT NOT NULL,
|
|
quantity_per_unit NUMERIC(14, 4) DEFAULT 0,
|
|
total_quantity NUMERIC(14, 4) DEFAULT 0,
|
|
unit VARCHAR(30),
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS production_order_steps (
|
|
id SERIAL PRIMARY KEY,
|
|
production_order_id INTEGER NOT NULL REFERENCES production_orders(id) ON DELETE CASCADE,
|
|
step_number INTEGER,
|
|
name VARCHAR(160) NOT NULL,
|
|
start_date DATE,
|
|
end_date DATE,
|
|
status VARCHAR(80),
|
|
color VARCHAR(40),
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS cutting_family_rules (
|
|
family_key VARCHAR(20) PRIMARY KEY,
|
|
units_per_roll NUMERIC(14, 4),
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS cutting_product_overrides (
|
|
product_id VARCHAR(100) PRIMARY KEY,
|
|
family_key VARCHAR(20),
|
|
color VARCHAR(100),
|
|
size VARCHAR(40),
|
|
product_type VARCHAR(40),
|
|
planning_notes TEXT,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS catalog_categories (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL UNIQUE,
|
|
description TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS catalog_products (
|
|
id SERIAL PRIMARY KEY,
|
|
type VARCHAR(40) NOT NULL DEFAULT 'finished_product',
|
|
sku VARCHAR(100) NOT NULL UNIQUE,
|
|
name TEXT NOT NULL,
|
|
category_id INTEGER REFERENCES catalog_categories(id) ON DELETE SET NULL,
|
|
composition TEXT,
|
|
notes TEXT,
|
|
gramature NUMERIC(14, 4),
|
|
material_yield NUMERIC(14, 4),
|
|
width_cm NUMERIC(14, 4),
|
|
color VARCHAR(100),
|
|
subcategory VARCHAR(80),
|
|
sizes TEXT[] DEFAULT '{}',
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS consumption_references (
|
|
id SERIAL PRIMARY KEY,
|
|
product_id INTEGER NOT NULL REFERENCES catalog_products(id) ON DELETE CASCADE,
|
|
material_product_id INTEGER REFERENCES catalog_products(id) ON DELETE SET NULL,
|
|
color VARCHAR(100),
|
|
general_yield NUMERIC(14, 4),
|
|
size_yields JSONB DEFAULT '{}'::jsonb,
|
|
size_areas JSONB DEFAULT '{}'::jsonb,
|
|
gramature NUMERIC(14, 4),
|
|
efficiency_percent NUMERIC(7, 3),
|
|
rib_g_per_piece NUMERIC(14, 4),
|
|
material_cost_per_kg NUMERIC(14, 4),
|
|
consumption_quantity NUMERIC(14, 4),
|
|
consumption_unit VARCHAR(30),
|
|
source VARCHAR(60) DEFAULT 'manual',
|
|
last_production_order_id INTEGER REFERENCES production_orders(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS supply_receipts (
|
|
id SERIAL PRIMARY KEY,
|
|
category VARCHAR(120) NOT NULL,
|
|
product TEXT NOT NULL,
|
|
quantity NUMERIC(14, 4) NOT NULL,
|
|
unit VARCHAR(30) NOT NULL DEFAULT 'kg',
|
|
supplier TEXT,
|
|
invoice VARCHAR(120),
|
|
notes TEXT,
|
|
status VARCHAR(30) NOT NULL DEFAULT 'pending',
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
approved_at TIMESTAMPTZ
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS supply_stock_lots (
|
|
id SERIAL PRIMARY KEY,
|
|
receipt_id INTEGER REFERENCES supply_receipts(id) ON DELETE SET NULL,
|
|
category VARCHAR(120) NOT NULL,
|
|
product TEXT NOT NULL,
|
|
quantity NUMERIC(14, 4) NOT NULL,
|
|
unit VARCHAR(30) NOT NULL DEFAULT 'kg',
|
|
supplier TEXT,
|
|
invoice VARCHAR(120),
|
|
status VARCHAR(30) NOT NULL DEFAULT 'active',
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS supply_movements (
|
|
id SERIAL PRIMARY KEY,
|
|
receipt_id INTEGER REFERENCES supply_receipts(id) ON DELETE SET NULL,
|
|
lot_id INTEGER REFERENCES supply_stock_lots(id) ON DELETE SET NULL,
|
|
type VARCHAR(40) NOT NULL,
|
|
category VARCHAR(120) NOT NULL,
|
|
product TEXT NOT NULL,
|
|
quantity NUMERIC(14, 4) NOT NULL,
|
|
unit VARCHAR(30) NOT NULL DEFAULT 'kg',
|
|
reason TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS supply_fabric_plans (
|
|
id SERIAL PRIMARY KEY,
|
|
material TEXT NOT NULL,
|
|
color VARCHAR(120),
|
|
quantity_kg NUMERIC(14, 4) NOT NULL,
|
|
supplier TEXT,
|
|
priority VARCHAR(40) NOT NULL DEFAULT 'Normal',
|
|
status VARCHAR(30) NOT NULL DEFAULT 'active',
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
ALTER TABLE production_orders
|
|
ADD COLUMN IF NOT EXISTS notes TEXT,
|
|
ADD COLUMN IF NOT EXISTS supplier TEXT,
|
|
ADD COLUMN IF NOT EXISTS lot_code VARCHAR(120),
|
|
ADD COLUMN IF NOT EXISTS roll_quantity NUMERIC(14, 4),
|
|
ADD COLUMN IF NOT EXISTS fabric_kg NUMERIC(14, 4),
|
|
ADD COLUMN IF NOT EXISTS rib_kg NUMERIC(14, 4),
|
|
ADD COLUMN IF NOT EXISTS yield_pieces_per_kg NUMERIC(14, 4);
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE production_orders
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE production_order_components
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE production_order_steps
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE cutting_family_rules
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE cutting_product_overrides
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE cutting_product_overrides
|
|
ADD COLUMN IF NOT EXISTS product_type VARCHAR(40),
|
|
ADD COLUMN IF NOT EXISTS planning_notes TEXT;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE catalog_categories
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE catalog_products
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE consumption_references
|
|
ADD COLUMN IF NOT EXISTS consumption_quantity NUMERIC(14, 4),
|
|
ADD COLUMN IF NOT EXISTS consumption_unit VARCHAR(30),
|
|
ADD COLUMN IF NOT EXISTS source VARCHAR(60) DEFAULT 'manual',
|
|
ADD COLUMN IF NOT EXISTS last_production_order_id INTEGER REFERENCES production_orders(id) ON DELETE SET NULL;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE consumption_references
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await seedDefaultCatalogCategories();
|
|
|
|
await pool.query(`
|
|
ALTER TABLE supply_receipts
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN approved_at TYPE TIMESTAMPTZ USING approved_at AT TIME ZONE 'America/Sao_Paulo';
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE supply_stock_lots
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE supply_movements
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE supply_fabric_plans
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS app_users (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS client_identity_tokens (
|
|
customer_key TEXT PRIMARY KEY,
|
|
token TEXT NOT NULL UNIQUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
`);
|
|
|
|
await pool.query(`
|
|
ALTER TABLE app_users
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
ALTER TABLE stock_campaign_queue
|
|
ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo',
|
|
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP,
|
|
ALTER COLUMN sent_at TYPE TIMESTAMPTZ USING sent_at AT TIME ZONE 'America/Sao_Paulo';
|
|
`).catch(() => {});
|
|
|
|
await pool.query(`
|
|
UPDATE stock_campaign_queue
|
|
SET base_product_name = TRIM(regexp_replace(
|
|
base_product_name,
|
|
'\\s+-\\s+(?:(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2})(?:/(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2}))*)$',
|
|
'',
|
|
'i'
|
|
))
|
|
WHERE status IN ('pending', 'failed', 'processing')
|
|
AND base_product_name ~* '\\s+-\\s+(?:(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2})(?:/(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2}))*)$';
|
|
`).catch(err => {
|
|
console.error('Notice: Could not normalize queued campaign product names:', err.message);
|
|
});
|
|
|
|
await pool.query(`
|
|
UPDATE stock_campaign_queue
|
|
SET base_product_name = nome
|
|
WHERE status IN ('pending', 'failed', 'processing')
|
|
AND nome ILIKE 'ETIQUETA%'
|
|
AND base_product_name != nome;
|
|
`).catch(err => {
|
|
console.error('Notice: Could not restore queued etiqueta product names:', err.message);
|
|
});
|
|
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_stock_campaign_queue_status ON stock_campaign_queue (status);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_production_orders_status ON production_orders (status);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_production_orders_issue_date ON production_orders (issue_date DESC);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_production_orders_expected_date ON production_orders (expected_date DESC);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_production_order_markers_order_id ON production_order_markers (production_order_id);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_production_order_components_order_id ON production_order_components (production_order_id);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_production_order_components_sku ON production_order_components (component_sku);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_production_order_steps_order_id ON production_order_steps (production_order_id);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_cutting_product_overrides_family_key ON cutting_product_overrides (family_key);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_catalog_products_type ON catalog_products (type);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_catalog_products_category_id ON catalog_products (category_id);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_consumption_references_product_id ON consumption_references (product_id);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_consumption_references_material_product_id ON consumption_references (material_product_id);`);
|
|
await pool.query(`
|
|
CREATE UNIQUE INDEX IF NOT EXISTS unique_consumption_reference_source
|
|
ON consumption_references (
|
|
product_id,
|
|
COALESCE(material_product_id, 0),
|
|
COALESCE(color, ''),
|
|
COALESCE(source, 'manual')
|
|
);
|
|
`).catch(err => {
|
|
console.error('Notice: Could not create unique consumption reference source index:', err.message);
|
|
});
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_supply_receipts_status ON supply_receipts (status);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_supply_receipts_created_at ON supply_receipts (created_at DESC);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_supply_stock_lots_status ON supply_stock_lots (status);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_supply_movements_created_at ON supply_movements (created_at DESC);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_supply_fabric_plans_status ON supply_fabric_plans (status);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_orders_cliente_fone ON orders (cliente_fone);`);
|
|
await pool.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_orders_normalized_cliente_nome
|
|
ON orders ((NULLIF(LOWER(TRIM(regexp_replace(COALESCE(cliente_nome, ''), '\\s+', ' ', 'g'))), '')));
|
|
`).catch(err => {
|
|
console.error('Notice: Could not create normalized client name index:', err.message);
|
|
});
|
|
await pool.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_orders_normalized_cliente_nome_phone_date
|
|
ON orders (
|
|
(NULLIF(LOWER(TRIM(regexp_replace(COALESCE(cliente_nome, ''), '\\s+', ' ', 'g'))), '')),
|
|
data_pedido_date DESC,
|
|
id DESC
|
|
)
|
|
WHERE NULLIF(cliente_fone, '') IS NOT NULL;
|
|
`).catch(err => {
|
|
console.error('Notice: Could not create normalized client phone lookup index:', err.message);
|
|
});
|
|
await pool.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_orders_customer_key_date
|
|
ON orders (
|
|
(COALESCE(NULLIF(cliente_fone, ''), 'name:' || COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido'))),
|
|
data_pedido_date
|
|
);
|
|
`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_orders_produto_id ON orders (produto_id);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_orders_data_pedido_date ON orders (data_pedido_date);`);
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_app_users_email ON app_users (LOWER(email));`);
|
|
|
|
console.log('Database initialized successfully.');
|
|
} catch (err) {
|
|
console.error('Failed to initialize database:', err);
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
pool,
|
|
initDB
|
|
};
|