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
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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;
|
||||
Reference in New Issue
Block a user