Files
sap-erp/routes/costing.js
T
John 69b4e68baf
SAP-ERP Portal CI/CD / build (push) Failing after 5m20s
first commit
2026-09-23 17:31:02 +05:30

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;