134 lines
5.2 KiB
JavaScript
134 lines
5.2 KiB
JavaScript
'use strict';
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const { verifyToken } = require('../middleware/auth');
|
|
const { getPool } = require('../services/sqlPool');
|
|
|
|
const store = () => require('../services/balanceSheetStore');
|
|
|
|
// ── Bootstrap on first require ────────────────────────────────────────────────
|
|
store().bootstrap().catch(e => console.error('[BalanceSheet] bootstrap error:', e));
|
|
|
|
// GET /api/balance-sheet/config
|
|
router.get('/config', verifyToken, async (req, res) => {
|
|
try {
|
|
const cfg = await store().getConfig();
|
|
res.json({ success: true, data: cfg, items: store().BS_ITEMS });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// POST /api/balance-sheet/config body: { group_key, acct_codes[], fixed_value, notes }
|
|
router.post('/config', verifyToken, async (req, res) => {
|
|
try {
|
|
const { group_key, acct_codes, opening_acct_codes, fixed_value, notes } = req.body;
|
|
if (!group_key) return res.status(400).json({ success: false, message: 'group_key required' });
|
|
await store().saveConfig(group_key, acct_codes, opening_acct_codes, fixed_value, notes);
|
|
res.json({ success: true });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/balance-sheet/data?asOf=YYYY-MM-DD&from=YYYY-MM-DD&to=YYYY-MM-DD
|
|
router.get('/data', verifyToken, async (req, res) => {
|
|
try {
|
|
const { asOf, from, to, snapshotId, netProfitOverride } = req.query;
|
|
if (!asOf) return res.status(400).json({ success: false, message: 'asOf date required' });
|
|
if (!from || !to) return res.status(400).json({ success: false, message: 'from and to required for P&L data' });
|
|
|
|
const s = store();
|
|
const pool = await getPool();
|
|
const cfg = await s.getConfig();
|
|
|
|
const [netProfit, closingStock, totalProvisions] = await Promise.all([
|
|
netProfitOverride != null && netProfitOverride !== ''
|
|
? Promise.resolve(parseFloat(netProfitOverride))
|
|
: snapshotId
|
|
? s.fetchNetProfitFromSnapshot(pool, snapshotId)
|
|
: s.fetchNetProfit(pool, from, to),
|
|
s.fetchClosingStock(pool, from, to),
|
|
s.fetchTotalProvisions(pool, from, to),
|
|
]);
|
|
|
|
const values = {};
|
|
let reservesDebug = null;
|
|
await Promise.all(
|
|
s.BS_ITEMS.map(async item => {
|
|
const c = cfg[item.key] || {};
|
|
let val = 0;
|
|
|
|
if (item.src === 'fixed') {
|
|
val = c.fixed_value != null ? c.fixed_value * 100000 : 0;
|
|
} else if (item.src === 'pl_net') {
|
|
val = netProfit;
|
|
} else if (item.src === 'closing_stock') {
|
|
val = closingStock;
|
|
} else if (item.src === 'provisions') {
|
|
const ledger = await s.fetchGroupBalance(pool, c.acct_codes || [], null, asOf);
|
|
val = -ledger + totalProvisions;
|
|
} else if (item.src === 'reserves_formula') {
|
|
const rd = await s.fetchReservesSurplusDebug(
|
|
pool, c.acct_codes || [], c.opening_acct_codes || [], from, to, asOf
|
|
);
|
|
val = rd.result;
|
|
reservesDebug = rd;
|
|
} else {
|
|
val = await s.fetchGroupBalance(pool, c.acct_codes || [], null, asOf);
|
|
}
|
|
|
|
values[item.key] = val;
|
|
})
|
|
);
|
|
|
|
|
|
|
|
res.json({
|
|
success: true,
|
|
asOf, from, to,
|
|
values,
|
|
meta: { net_profit: netProfit, closing_stock: closingStock, total_provisions: totalProvisions },
|
|
reserves_debug: reservesDebug,
|
|
});
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
// GET /api/balance-sheet/snapshot-profit?id= — compute net profit from snapshot period
|
|
router.get('/snapshot-profit', verifyToken, async (req, res) => {
|
|
try {
|
|
const { id } = req.query;
|
|
if (!id) return res.status(400).json({ success: false, message: 'id required' });
|
|
const pool = await getPool();
|
|
const s = store();
|
|
const net = await s.fetchNetProfitFromSnapshot(pool, id);
|
|
res.json({ success: true, net_profit: net });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/balance-sheet/snapshots — list available P&L snapshots
|
|
router.get('/snapshots', verifyToken, async (req, res) => {
|
|
try {
|
|
const snaps = await require('../services/costingStore').getSnapshots();
|
|
res.json({ success: true, data: snaps });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
// GET /api/balance-sheet/accounts?q= — search chart of accounts for configure panel
|
|
router.get('/accounts', verifyToken, async (req, res) => {
|
|
try {
|
|
const { q } = req.query;
|
|
const pool = await getPool();
|
|
const req2 = pool.request();
|
|
let where = `WHERE T0.GroupMask IN (1,2,3)`;
|
|
if (q) {
|
|
req2.input('q', `%${q}%`);
|
|
where += ` AND (T0.AcctCode LIKE @q OR T0.AcctName LIKE @q)`;
|
|
}
|
|
const r = await req2.query(`
|
|
SELECT TOP 100 T0.AcctCode, T0.AcctName, T0.GroupMask
|
|
FROM OACT T0
|
|
${where}
|
|
ORDER BY T0.AcctCode
|
|
`);
|
|
res.json({ success: true, data: r.recordset });
|
|
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
|
|
});
|
|
|
|
module.exports = router;
|