Updated A LOT
This commit is contained in:
@@ -1,34 +1,120 @@
|
||||
const express = require('express');
|
||||
const { all, audit, now, one, parseJson, run } = require('../db');
|
||||
const { requireRole } = require('../middleware/auth');
|
||||
const { all, audit, now, one, parseJson, run, tx } = require('../db');
|
||||
const { requireCapability } = require('../middleware/auth');
|
||||
const { expandRuleSet } = require('../rule-catalog');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function coerceRules(value) {
|
||||
if (value === undefined || value === null) return {};
|
||||
if (typeof value === 'string') {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
const error = new Error('rules_json is not valid JSON');
|
||||
error.status = 400;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (typeof value !== 'object') {
|
||||
const error = new Error('rules_json must be a JSON object');
|
||||
error.status = 400;
|
||||
throw error;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function serialize(row) {
|
||||
return { ...row, active: Boolean(row.active), rules_json: parseJson(row.rules_json, {}) };
|
||||
}
|
||||
|
||||
router.get('/tax-rules', (req, res) => {
|
||||
const ruleSets = all('SELECT * FROM tax_rule_sets ORDER BY active DESC, effective_date DESC, id DESC').map((row) => ({
|
||||
...row,
|
||||
active: Boolean(row.active),
|
||||
rules_json: parseJson(row.rules_json, {})
|
||||
}));
|
||||
const ruleSets = all('SELECT * FROM tax_rule_sets ORDER BY active DESC, effective_date DESC, id DESC').map(serialize);
|
||||
res.json({ ruleSets });
|
||||
});
|
||||
|
||||
router.post('/tax-rules', requireRole('admin', 'finance'), (req, res) => {
|
||||
const result = run(
|
||||
'INSERT INTO tax_rule_sets (jurisdiction, name, effective_date, version, rules_json, active, source_note, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
req.body.jurisdiction,
|
||||
req.body.name,
|
||||
req.body.effective_date,
|
||||
req.body.version,
|
||||
JSON.stringify(req.body.rules_json || req.body.rules || {}),
|
||||
req.body.active === false ? 0 : 1,
|
||||
req.body.source_note || null,
|
||||
now()
|
||||
]
|
||||
);
|
||||
audit(req.user.id, 'create', 'tax_rule_set', result.lastInsertRowid, null, req.body);
|
||||
res.status(201).json({ ruleSet: one('SELECT * FROM tax_rule_sets WHERE id = ?', [result.lastInsertRowid]) });
|
||||
router.post('/tax-rules', requireCapability('finance.manage'), (req, res, next) => {
|
||||
try {
|
||||
const rules = coerceRules(req.body.rules_json ?? req.body.rules);
|
||||
const result = run(
|
||||
'INSERT INTO tax_rule_sets (jurisdiction, name, effective_date, version, rules_json, active, source_note, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
req.body.jurisdiction || rules.jurisdiction || 'US-FED',
|
||||
req.body.name || rules.name || 'Untitled rule set',
|
||||
req.body.effective_date || rules.effectiveDate || rules.effective_date || new Date().toISOString().slice(0, 10),
|
||||
req.body.version || rules.version || '1.0',
|
||||
JSON.stringify(rules),
|
||||
req.body.active === false ? 0 : 1,
|
||||
req.body.source_note || rules.sourceNote || null,
|
||||
now()
|
||||
]
|
||||
);
|
||||
audit(req.user.id, 'create', 'tax_rule_set', result.lastInsertRowid, null, { ...req.body, rules_json: '[stored]' });
|
||||
res.status(201).json({ ruleSet: serialize(one('SELECT * FROM tax_rule_sets WHERE id = ?', [result.lastInsertRowid])) });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/tax-rules/:id', requireCapability('finance.manage'), (req, res, next) => {
|
||||
try {
|
||||
const before = one('SELECT * FROM tax_rule_sets WHERE id = ?', [req.params.id]);
|
||||
if (!before) return res.status(404).json({ error: 'Rule set not found' });
|
||||
const rules = req.body.rules_json !== undefined || req.body.rules !== undefined
|
||||
? coerceRules(req.body.rules_json ?? req.body.rules)
|
||||
: parseJson(before.rules_json, {});
|
||||
run(
|
||||
`UPDATE tax_rule_sets SET
|
||||
jurisdiction = ?, name = ?, effective_date = ?, version = ?, rules_json = ?, active = ?, source_note = ?
|
||||
WHERE id = ?`,
|
||||
[
|
||||
req.body.jurisdiction ?? before.jurisdiction,
|
||||
req.body.name ?? before.name,
|
||||
req.body.effective_date ?? before.effective_date,
|
||||
req.body.version ?? before.version,
|
||||
JSON.stringify(rules),
|
||||
req.body.active === undefined ? before.active : (req.body.active ? 1 : 0),
|
||||
req.body.source_note ?? before.source_note,
|
||||
req.params.id
|
||||
]
|
||||
);
|
||||
audit(req.user.id, 'update', 'tax_rule_set', req.params.id, { ...before, rules_json: '[stored]' }, { ...req.body, rules_json: '[stored]' });
|
||||
return res.json({ ruleSet: serialize(one('SELECT * FROM tax_rule_sets WHERE id = ?', [req.params.id])) });
|
||||
} catch (error) {
|
||||
if (/UNIQUE/.test(error.message || '')) {
|
||||
return res.status(400).json({ error: 'A rule set with this jurisdiction and version already exists' });
|
||||
}
|
||||
return next(error);
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/tax-rules/:id', requireCapability('finance.manage'), (req, res) => {
|
||||
const before = one('SELECT * FROM tax_rule_sets WHERE id = ?', [req.params.id]);
|
||||
if (!before) return res.status(404).json({ error: 'Rule set not found' });
|
||||
run('DELETE FROM tax_rule_sets WHERE id = ?', [req.params.id]);
|
||||
audit(req.user.id, 'delete', 'tax_rule_set', req.params.id, { ...before, rules_json: '[stored]' }, null);
|
||||
return res.status(204).end();
|
||||
});
|
||||
|
||||
router.post('/tax-rules/:id/activate', requireCapability('finance.manage'), (req, res) => {
|
||||
const target = one('SELECT * FROM tax_rule_sets WHERE id = ?', [req.params.id]);
|
||||
if (!target) return res.status(404).json({ error: 'Rule set not found' });
|
||||
tx(() => {
|
||||
run('UPDATE tax_rule_sets SET active = 0 WHERE jurisdiction = ? AND id != ?', [target.jurisdiction, target.id]);
|
||||
run('UPDATE tax_rule_sets SET active = 1 WHERE id = ?', [target.id]);
|
||||
});
|
||||
audit(req.user.id, 'activate', 'tax_rule_set', target.id, null, { jurisdiction: target.jurisdiction });
|
||||
return res.json({ ruleSets: all('SELECT * FROM tax_rule_sets ORDER BY active DESC, effective_date DESC, id DESC').map(serialize) });
|
||||
});
|
||||
|
||||
// Merge the generated 68-method depreciation catalog into a rule set (works on unsaved drafts too).
|
||||
router.post('/tax-rules/expand', requireCapability('finance.manage'), (req, res, next) => {
|
||||
try {
|
||||
const rules = coerceRules(req.body.rules_json ?? req.body.rules);
|
||||
return res.json({ rules_json: expandRuleSet(rules) });
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user