148 lines
4.8 KiB
JavaScript
148 lines
4.8 KiB
JavaScript
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();
|