Files
boc/landvex-admin-v2/src/routes/automations.mjs
T
Bernt 48ea61cdcc docs: add quixzoom-auth-core product to AAMOS
- Product documentation in docs/products/
- Updated MEMORY.md with product info
- quiXzoom Auth Core as AAMOS Identity product
2026-07-14 09:58:53 +00:00

194 lines
6.0 KiB
JavaScript

/**
* 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;