205 lines
8.6 KiB
JavaScript
205 lines
8.6 KiB
JavaScript
'use strict';
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const { verifyToken } = require('../middleware/auth');
|
|
|
|
const store = () => require('../services/costingStore');
|
|
|
|
function defaultSheet(groupMask) {
|
|
return groupMask === 4 ? 'Revenue' : 'Other';
|
|
}
|
|
|
|
const GROUP_LABELS = { 1:'Assets', 2:'Liabilities', 3:'Equity', 4:'Revenue', 5:'Expenses' };
|
|
|
|
// GET /api/costing/trial-balance?from=&to=
|
|
router.get('/trial-balance', verifyToken, async (req, res) => {
|
|
try {
|
|
const { from, to } = req.query;
|
|
if (!from || !to)
|
|
return res.status(400).json({ success: false, message: 'from and to dates required' });
|
|
|
|
const [rows, overrides] = await Promise.all([
|
|
store().fetchAllTrialBalance(from, to),
|
|
store().getOverrides(from, to),
|
|
]);
|
|
|
|
const ovByCode = {};
|
|
overrides.forEach(o => { ovByCode[o.acct_code] = o; });
|
|
|
|
const data = rows.map(r => ({
|
|
acct_code: r.acct_code,
|
|
acct_name: r.acct_name,
|
|
group_mask: r.group_mask,
|
|
group_label: GROUP_LABELS[r.group_mask] || `Group ${r.group_mask}`,
|
|
opening_balance: Number(r.opening_balance) || 0,
|
|
period_debit: Number(r.period_debit) || 0,
|
|
period_credit: Number(r.period_credit) || 0,
|
|
closing_balance: Number(r.closing_balance) || 0,
|
|
provision: ovByCode[r.acct_code]?.override_val ?? null,
|
|
note: ovByCode[r.acct_code]?.note ?? '',
|
|
}));
|
|
|
|
// Stock values
|
|
const stockOpen = ovByCode['__STOCK_OPEN__']?.override_val ?? null;
|
|
const stockClose = ovByCode['__STOCK_CLOSE__']?.override_val ?? null;
|
|
|
|
res.json({ success: true, data, opening_stock: stockOpen, closing_stock: stockClose });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/costing/data?from=YYYY-MM-DD&to=YYYY-MM-DD
|
|
router.get('/data', verifyToken, async (req, res) => {
|
|
try {
|
|
const { from, to } = req.query;
|
|
if (!from || !to)
|
|
return res.status(400).json({ success: false, message: 'from and to dates required' });
|
|
|
|
const [tb, mapping, overrides] = await Promise.all([
|
|
store().fetchTrialBalance(from, to),
|
|
store().getMapping(),
|
|
store().getOverrides(from, to),
|
|
]);
|
|
|
|
const mapByCode = {};
|
|
mapping.forEach(m => { mapByCode[m.acct_code] = m; });
|
|
const ovByCode = {};
|
|
overrides.forEach(o => { ovByCode[o.acct_code] = o; });
|
|
|
|
const data = tb.map(row => ({
|
|
acct_code: row.acct_code,
|
|
acct_name: row.acct_name,
|
|
group_mask: row.group_mask,
|
|
opening_balance: Number(row.opening_balance) || 0,
|
|
period_debit: Number(row.period_debit) || 0,
|
|
period_credit: Number(row.period_credit) || 0,
|
|
closing_balance: Number(row.closing_balance) || 0,
|
|
sheet: mapByCode[row.acct_code]?.sheet ?? defaultSheet(row.group_mask),
|
|
sort_order: mapByCode[row.acct_code]?.sort_order ?? 999,
|
|
head: mapByCode[row.acct_code]?.head ?? '',
|
|
override_val: ovByCode[row.acct_code]?.override_val ?? null,
|
|
note: ovByCode[row.acct_code]?.note ?? '',
|
|
}));
|
|
|
|
res.json({ success: true, data });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// POST /api/costing/mapping body: [{acct_code, sheet, sort_order}]
|
|
router.post('/mapping', verifyToken, async (req, res) => {
|
|
try {
|
|
const entries = Array.isArray(req.body) ? req.body : [req.body];
|
|
await store().saveMapping(entries);
|
|
res.json({ success: true });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/costing/stock?from=&to=
|
|
router.get('/stock', verifyToken, async (req, res) => {
|
|
try {
|
|
const { from, to } = req.query;
|
|
if (!from || !to) return res.status(400).json({ success: false, message: 'from and to required' });
|
|
const overrides = await store().getOverrides(from, to);
|
|
const open = overrides.find(o => o.acct_code === '__STOCK_OPEN__');
|
|
const close = overrides.find(o => o.acct_code === '__STOCK_CLOSE__');
|
|
res.json({ success: true, opening_stock: open?.override_val ?? 0, closing_stock: close?.override_val ?? 0 });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// POST /api/costing/stock body: {from, to, opening_stock, closing_stock}
|
|
router.post('/stock', verifyToken, async (req, res) => {
|
|
try {
|
|
const { from, to, opening_stock, closing_stock } = req.body;
|
|
if (!from || !to) return res.status(400).json({ success: false, message: 'from and to required' });
|
|
await Promise.all([
|
|
store().saveOverride('__STOCK_OPEN__', from, to, opening_stock ?? 0, null, req.user.username),
|
|
store().saveOverride('__STOCK_CLOSE__', from, to, closing_stock ?? 0, null, req.user.username),
|
|
]);
|
|
res.json({ success: true });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/costing/stock-detail?from=&to=
|
|
router.get('/stock-detail', verifyToken, async (req, res) => {
|
|
try {
|
|
const { from, to } = req.query;
|
|
if (!from || !to) return res.status(400).json({ success: false, message: 'from and to required' });
|
|
const keys = ['__OPEN_RM__','__OPEN_WIP__','__OPEN_FG__','__OPEN_TRADE__','__OPEN_CONV__',
|
|
'__CLOSE_RM__','__CLOSE_WIP__','__CLOSE_FG__','__CLOSE_TRADE__','__CLOSE_CONV__'];
|
|
const overrides = await store().getOverrides(from, to);
|
|
const data = {};
|
|
keys.forEach(k => {
|
|
const ov = overrides.find(o => o.acct_code === k);
|
|
data[k] = ov ? Number(ov.override_val) || 0 : 0;
|
|
});
|
|
res.json({ success: true, data });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// POST /api/costing/override body: {acct_code, from, to, override_val, note}
|
|
router.post('/override', verifyToken, async (req, res) => {
|
|
try {
|
|
const { acct_code, from, to, override_val, note } = req.body;
|
|
if (!acct_code || !from || !to)
|
|
return res.status(400).json({ success: false, message: 'acct_code, from, to required' });
|
|
await store().saveOverride(acct_code, from, to, override_val, note, req.user.username);
|
|
res.json({ success: true });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// DELETE /api/costing/override?acct_code=&from=&to=
|
|
router.delete('/override', verifyToken, async (req, res) => {
|
|
try {
|
|
const { acct_code, from, to } = req.query;
|
|
if (!acct_code || !from || !to)
|
|
return res.status(400).json({ success: false, message: 'acct_code, from, to required' });
|
|
await store().deleteOverride(acct_code, from, to);
|
|
res.json({ success: true });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/costing/provisions - all provisions ever entered, month-wise
|
|
router.get('/provisions', verifyToken, async (req, res) => {
|
|
try {
|
|
const data = await store().getAllOverrides();
|
|
res.json({ success: true, data });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/costing/snapshots
|
|
router.get('/snapshots', verifyToken, async (req, res) => {
|
|
try {
|
|
const snaps = await store().getSnapshots();
|
|
res.json({ success: true, data: snaps });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// POST /api/costing/snapshot body: {from, to, name, data: {accounts, opening_stock, closing_stock, stock_detail}}
|
|
router.post('/snapshot', verifyToken, async (req, res) => {
|
|
try {
|
|
const { from, to, name, data } = req.body;
|
|
if (!from || !to || !data)
|
|
return res.status(400).json({ success: false, message: 'from, to, data required' });
|
|
await store().saveSnapshot(from, to, name || `${from} to ${to}`, JSON.stringify(data), req.user.username);
|
|
res.json({ success: true });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/costing/snapshot/:id
|
|
router.get('/snapshot/:id', verifyToken, async (req, res) => {
|
|
try {
|
|
const snap = await store().getSnapshot(parseInt(req.params.id));
|
|
if (!snap) return res.status(404).json({ success: false, message: 'Snapshot not found' });
|
|
res.json({ success: true, data: JSON.parse(snap.data_json) });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// DELETE /api/costing/snapshot/:id
|
|
router.delete('/snapshot/:id', verifyToken, async (req, res) => {
|
|
try {
|
|
await store().deleteSnapshot(parseInt(req.params.id));
|
|
res.json({ success: true });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
module.exports = router;
|