'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;