/** * Automation Routes — Schemalagda och event-baserade automationer */ import { Router } from 'express'; import pg from 'pg'; const { Pool } = pg; const router = Router(); const pool = new Pool({ host: process.env.DB_HOST || 'localhost', port: process.env.DB_PORT || 5432, database: process.env.DB_NAME || 'wavult_identity', user: process.env.DB_USER || 'wavult_admin', password: process.env.DB_PASSWORD || 'wavult_admin' }); // GET /api/automations router.get('/', async (req, res) => { try { const result = await pool.query( 'SELECT * FROM automations ORDER BY created_at DESC' ); res.json({ automations: result.rows }); } catch (e) { res.status(500).json({ error: e.message }); } }); // POST /api/automations router.post('/', async (req, res) => { try { const { name, description, trigger_type, frequency, action } = req.body; const result = await pool.query(` INSERT INTO automations (name, description, trigger_type, frequency, action, is_active) VALUES ($1, $2, $3, $4, $5, true) RETURNING * `, [name, description, trigger_type, frequency, action]); res.status(201).json({ automation: result.rows[0] }); } catch (e) { res.status(500).json({ error: e.message }); } }); // POST /api/automations/:id/run router.post('/:id/run', async (req, res) => { try { const result = await pool.query( 'SELECT * FROM automations WHERE id = $1', [req.params.id] ); if (result.rows.length === 0) { return res.status(404).json({ error: 'Automation hittades inte' }); } const automation = result.rows[0]; // Execute automation logic based on action let executionResult; switch (automation.action) { case 'create_invoice': executionResult = await runInvoiceAutomation(automation); break; case 'send_reminder': executionResult = await runReminderAutomation(automation); break; case 'export_report': executionResult = await runExportAutomation(automation); break; case 'backup': executionResult = await runBackupAutomation(automation); break; default: executionResult = { success: false, message: 'Okänd åtgärd' }; } // Log execution await pool.query(` INSERT INTO automation_logs (automation_id, status, result, executed_at) VALUES ($1, $2, $3, NOW()) `, [req.params.id, executionResult.success ? 'success' : 'failed', JSON.stringify(executionResult)]); res.json(executionResult); } catch (e) { res.status(500).json({ error: e.message }); } }); // POST /api/automations/:id/toggle router.post('/:id/toggle', async (req, res) => { try { const result = await pool.query(` UPDATE automations SET is_active = NOT is_active WHERE id = $1 RETURNING * `, [req.params.id]); if (result.rows.length === 0) { return res.status(404).json({ error: 'Automation hittades inte' }); } res.json({ automation: result.rows[0] }); } catch (e) { res.status(500).json({ error: e.message }); } }); // Automation execution functions async function runInvoiceAutomation(automation) { // Find agreements that need invoicing const agreements = await pool.query(` SELECT a.*, c.name as customer_name, c.email as customer_email FROM billing_agreements a JOIN billing_customers c ON a.customer_id = c.id WHERE a.status = 'active' AND a.next_invoice_date <= NOW() `); const invoices = []; for (const agreement of agreements.rows) { // Create invoice for agreement const invoiceResult = await pool.query(` INSERT INTO billing_invoices (invoice_number, customer_id, customer_name, customer_email, invoice_date, due_date, status, subtotal, vat_total, total, amount_due) VALUES ( (SELECT 'F-' || EXTRACT(YEAR FROM NOW()) || '-' || LPAD((COUNT(*) + 1)::text, 4, '0') FROM billing_invoices WHERE created_at >= DATE_TRUNC('year', NOW())), $1, $2, $3, NOW(), NOW() + INTERVAL '30 days', 'draft', $4, $5, $6, $6 ) RETURNING * `, [agreement.customer_id, agreement.customer_name, agreement.customer_email, agreement.amount, agreement.amount * 0.25, agreement.amount * 1.25]); invoices.push(invoiceResult.rows[0]); // Update next invoice date await pool.query(` UPDATE billing_agreements SET next_invoice_date = CASE billing_frequency WHEN 'monthly' THEN next_invoice_date + INTERVAL '1 month' WHEN 'quarterly' THEN next_invoice_date + INTERVAL '3 months' WHEN 'semi_annual' THEN next_invoice_date + INTERVAL '6 months' WHEN 'annual' THEN next_invoice_date + INTERVAL '1 year' END WHERE id = $1 `, [agreement.id]); } return { success: true, message: `Skapade ${invoices.length} fakturor`, invoices }; } async function runReminderAutomation(automation) { // Find overdue invoices const invoices = await pool.query(` SELECT i.*, c.email as customer_email FROM billing_invoices i JOIN billing_customers c ON i.customer_id = c.id WHERE i.status IN ('sent', 'partially_paid') AND i.due_date < NOW() - INTERVAL '7 days' AND (i.last_reminder_at IS NULL OR i.last_reminder_at < NOW() - INTERVAL '7 days') `); const reminders = []; for (const invoice of invoices.rows) { // Send reminder (would integrate with email service) await pool.query(` UPDATE billing_invoices SET reminder_count = reminder_count + 1, last_reminder_at = NOW() WHERE id = $1 `, [invoice.id]); reminders.push(invoice); } return { success: true, message: `Skickade ${reminders.length} påminnelser`, reminders }; } async function runExportAutomation(automation) { // Would integrate with export service return { success: true, message: 'Rapport exporterad' }; } async function runBackupAutomation(automation) { // Would integrate with backup service return { success: true, message: 'Backup slutförd' }; } export default router;