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), 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 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 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 };