import dotenv from 'dotenv'; import fs from 'fs'; import path from 'path'; // Load .env variables const envPath = path.resolve(process.cwd(), '.env'); if (fs.existsSync(envPath)) { dotenv.config({ path: envPath }); } const HASURA_ADMIN_SECRET = process.env.HASURA_ADMIN_SECRET || 'nearle-admin-secret'; const HASURA_QUERY_URL = 'https://api.workolik.com/v2/query'; const HASURA_METADATA_URL = 'https://api.workolik.com/v1/metadata'; let sourceName = 'default'; async function getSourceName() { const response = await fetch(HASURA_METADATA_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-hasura-admin-secret': HASURA_ADMIN_SECRET, }, body: JSON.stringify({ type: 'export_metadata', args: {} }), }); const data = await response.json(); if (data.error) { throw new Error(`Hasura Metadata Error: ${data.error}`); } if (data.sources && data.sources.length > 0) { return data.sources[0].name; } return 'default'; } async function runSql(sqlQuery) { const response = await fetch(HASURA_QUERY_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-hasura-admin-secret': HASURA_ADMIN_SECRET, }, body: JSON.stringify({ type: 'run_sql', args: { source: sourceName, sql: sqlQuery, cascade: false, check_metadata_consistency: false, }, }), }); const data = await response.json(); if (data.error) { throw new Error(`Hasura SQL Error: ${data.error}`); } return data; } async function main() { try { console.log('🔄 Connecting to Hasura Database...'); sourceName = await getSourceName(); console.log(`✅ Using database source: "${sourceName}"`); // 1. Let's introspect the tables to ensure we have the correct table names before running the trigger. const targetTables = ['orders', 'orderdetails', 'productstocks']; const columnsCheckSql = ` SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name IN ('orders', 'orderdetails', 'productstocks'); `; console.log('🔍 Introspecting columns for orders, orderdetails, productstocks...'); const colData = await runSql(columnsCheckSql); const cols = colData.result.slice(1); const getCols = (table) => cols.filter(r => r[0] === table).map(r => r[1]); const ordersCols = getCols('orders'); const orderDetailsCols = getCols('orderdetails'); const productStockCols = getCols('productstocks'); console.log(`✅ orders columns: ${ordersCols.join(', ')}`); console.log(`✅ orderdetails columns: ${orderDetailsCols.join(', ')}`); console.log(`✅ productstocks columns: ${productStockCols.join(', ')}`); // Determine exact column names const orderPk = ordersCols.includes('orderheaderid') ? 'orderheaderid' : 'orderid'; const detailOrderId = orderDetailsCols.includes('orderheaderid') ? 'orderheaderid' : 'orderid'; const qtyCol = orderDetailsCols.includes('qty') ? 'qty' : 'orderqty'; const stockCol = productStockCols.includes('physicalstock') ? 'physicalstock' : (productStockCols.includes('closing') ? 'closing' : 'stock'); // 2. Define the SQL for the Trigger Functions console.log('🛠️ Creating trigger functions...'); const createTriggerSql = ` -- Function to reduce stock when a new order detail is inserted CREATE OR REPLACE FUNCTION update_stock_on_order_insert() RETURNS trigger AS $$ DECLARE v_locationid INT; BEGIN -- Try to fetch locationid from orders BEGIN SELECT locationid INTO v_locationid FROM orders WHERE ${orderPk} = NEW.${detailOrderId}; EXCEPTION WHEN OTHERS THEN v_locationid := NULL; END; IF v_locationid IS NOT NULL THEN UPDATE productstocks SET ${stockCol} = GREATEST(0, COALESCE(${stockCol}, 0) - COALESCE(NEW.${qtyCol}, 1)) WHERE productid = NEW.productid AND locationid = v_locationid; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -- Create the trigger on orderdetails table DROP TRIGGER IF EXISTS trigger_reduce_stock_on_order ON orderdetails; CREATE TRIGGER trigger_reduce_stock_on_order AFTER INSERT ON orderdetails FOR EACH ROW EXECUTE FUNCTION update_stock_on_order_insert(); `; console.log('Deploying trigger...'); await runSql(createTriggerSql); console.log('✅ Stock Reduction Trigger successfully deployed!'); console.log('🎉 Setup complete. The database will now automatically reduce physicalstock when an order is created.'); } catch (error) { console.error('❌ Error executing deployment script:'); console.error(error.message); } } main();