first commit
SAP-ERP Portal CI/CD / build (push) Failing after 5m20s

This commit is contained in:
John
2026-09-23 17:31:02 +05:30
commit 69b4e68baf
51657 changed files with 3864077 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
'use strict';
const express = require('express');
const router = express.Router();
const { verifyToken } = require('../middleware/auth');
const { getPool } = require('../services/sqlPool');
// ── Open Production Orders ────────────────────────────────────────────────
function buildOpenProductionOrdersSQL(from, to, status, segment) {
const statusClause = status && status !== 'all'
? `AND OWOR.Status = '${status === 'Released' ? 'R' : 'P'}'`
: `AND OWOR.Status IN ('R','P')`;
const segmentClause = segment && segment !== 'all'
? `AND (
CASE
WHEN OITM.U_ItemSubGroup LIKE '%SFG Equipment%' OR OITM.U_ItemSubGroup LIKE '%EQUIPMENT%' THEN 'Equipment'
WHEN OITM.U_ItemSubGroup LIKE '%Consumable%' OR OITM.U_ItemSubGroup LIKE '%Consumables%' THEN 'Consumable'
WHEN OITM.U_ItemSubGroup LIKE '%FG Stent%' THEN 'FG Stent'
WHEN OITM.U_ItemSubGroup LIKE '%FG TT Device%' THEN 'FG TT Device'
WHEN OITM.U_ItemSubGroup LIKE '%PLASTIC%' THEN 'PLASTIC'
WHEN OITM.U_ItemSubGroup LIKE '%NEEDLE%' THEN 'NEEDLE'
WHEN OITM.U_ItemSubGroup LIKE '%MOULDING%' THEN 'MOULDING'
WHEN OITM.U_ItemSubGroup LIKE '%FG CAPD ACCESSORIES%' THEN 'FG CAPD ACCESSORIES'
WHEN OITM.U_ItemSubGroup LIKE '%FG CAPD%' THEN 'FG CAPD'
WHEN OITM.U_ItemSubGroup LIKE '%CAPD%' THEN 'CAPD'
WHEN OITM.U_ItemSubGroup LIKE '%FG Equipment%' THEN 'FG Equipment'
WHEN OITM.U_ItemSubGroup LIKE '%FG Blood Bag%' THEN 'FG Blood Bag'
WHEN OITM.U_ItemSubGroup LIKE '%SFG Blood bag%' THEN 'SFG Blood bag'
WHEN OITM.U_ItemSubGroup LIKE '%BLOOD BAG%' THEN 'BLOOD BAG'
ELSE 'Other'
END
) = '${segment.replace(/'/g, "''")}'`
: '';
return `
SELECT DISTINCT
OWOR.DocEntry,
k.SeriesName,
OWOR.DocNum,
CASE WHEN OWOR.Type = 'S' THEN 'Standard' ELSE 'Other' END AS Type,
CASE
WHEN OWOR.Status = 'R' THEN 'Released'
WHEN OWOR.Status = 'P' THEN 'Planned'
ELSE 'Unknown'
END AS Status,
OWOR.ItemCode AS ProductCode,
CASE
WHEN OITM.U_ItemSubGroup LIKE '%SFG Equipment%' OR OITM.U_ItemSubGroup LIKE '%EQUIPMENT%' THEN 'Equipment'
WHEN OITM.U_ItemSubGroup LIKE '%Consumable%' OR OITM.U_ItemSubGroup LIKE '%Consumables%' THEN 'Consumable'
WHEN OITM.U_ItemSubGroup LIKE '%FG Stent%' THEN 'FG Stent'
WHEN OITM.U_ItemSubGroup LIKE '%FG TT Device%' THEN 'FG TT Device'
WHEN OITM.U_ItemSubGroup LIKE '%PLASTIC%' THEN 'PLASTIC'
WHEN OITM.U_ItemSubGroup LIKE '%NEEDLE%' THEN 'NEEDLE'
WHEN OITM.U_ItemSubGroup LIKE '%MOULDING%' THEN 'MOULDING'
WHEN OITM.U_ItemSubGroup LIKE '%FG CAPD ACCESSORIES%' THEN 'FG CAPD ACCESSORIES'
WHEN OITM.U_ItemSubGroup LIKE '%FG CAPD%' THEN 'FG CAPD'
WHEN OITM.U_ItemSubGroup LIKE '%CAPD%' THEN 'CAPD'
WHEN OITM.U_ItemSubGroup LIKE '%FG Equipment%' THEN 'FG Equipment'
WHEN OITM.U_ItemSubGroup LIKE '%FG Blood Bag%' THEN 'FG Blood Bag'
WHEN OITM.U_ItemSubGroup LIKE '%SFG Blood bag%' THEN 'SFG Blood bag'
WHEN OITM.U_ItemSubGroup LIKE '%BLOOD BAG%' THEN 'BLOOD BAG'
ELSE 'Other'
END AS ProductSegment,
OWOR.ProdName AS ProductName,
OWOR.PlannedQty AS PlannedQuantity,
OWOR.CmpltQty AS CompletedQty,
CONVERT(VARCHAR(10), OWOR.PostDate, 105) AS PostDate,
CONVERT(VARCHAR(10), OWOR.StartDate, 105) AS StartDate,
CONVERT(VARCHAR(10), OWOR.DueDate, 105) AS DueDate
FROM OWOR
INNER JOIN WOR1 ON OWOR.DocEntry = WOR1.DocEntry
INNER JOIN OITM ON OWOR.ItemCode = OITM.ItemCode
LEFT JOIN NNM1 k ON k.Series = OWOR.Series
WHERE OWOR.PostDate BETWEEN @from AND @to
${statusClause}
AND OITM.U_ItemSubGroup <> 'FG Scrap'
${segmentClause}
ORDER BY OWOR.DocNum DESC`;
}
// ── GET /api/reports/open-production-orders ───────────────────────────────
router.get('/open-production-orders', verifyToken, async (req, res) => {
try {
let { from, to, status = 'all', segment = 'all' } = req.query;
// Default: last 15 days
if (!from || !to) {
const toDate = new Date();
toDate.setDate(toDate.getDate() - 1);
const frDate = new Date(toDate);
frDate.setDate(frDate.getDate() - 14);
to = toDate.toISOString().split('T')[0];
from = frDate.toISOString().split('T')[0];
}
const pool = await getPool();
const sql = buildOpenProductionOrdersSQL(from, to, status, segment);
const r = await pool.request()
.input('from', from)
.input('to', to)
.query(sql);
res.json({ success: true, data: r.recordset || [], count: (r.recordset || []).length, from, to });
} catch (err) {
res.status(500).json({ success: false, message: err.message });
}
});
// ── Revenue constants (mirrors chatbot.js) ────────────────────────────────
const EXCL_DOCS = `'552520145','552520146','552520147','552520465','552522652','552522653','552620186'`;
const EXCL_TM = `'4110201001','4110202001','4110201003','4110202003','4110201007','4110201002','4110201005'`;
const EXCL_PD = EXCL_TM + `,'4110202005','4110202007','4110201004'`;
const EXCL_ED = `'4110201001','4110201003','4110201007','4110201002','4110201005','4110201004'`;
const EXCL_EI = `'4110202001','4110202003','4110202005','4110202007'`;
function buildRevSubq(dateWhere, isCN) {
const tbl = isCN ? 'RIN1' : 'INV1';
const hdr = isCN ? 'ORIN' : 'OINV';
const sign = isCN ? '-' : '';
const extras = isCN ? '' : `
AND i.DocNum NOT IN (${EXCL_DOCS})
AND (t2.ItmsGrpNam IN ('FG CAPD','FG CAPD Accessories')
OR (i.DocStatus<>'C' OR i.InvntSttus='O'))`;
return `
SELECT
CASE WHEN (t2.ItmsGrpNam='FG BLOOD BAG'
OR (t2.ItmsGrpNam IN ('FG EQUIPMENT','SEMI FINISHED') AND t1.U_BagType='Equipment'))
AND t.AcctCode NOT IN (${EXCL_TM})
THEN ${sign}t.LineTotal ELSE 0 END AS TM,
CASE WHEN (t2.ItmsGrpNam IN ('FG CAPD','FG CAPD Accessories')
OR t1.U_PDGroup='Equipment')
AND t.AcctCode NOT IN (${EXCL_PD})
THEN ${sign}t.LineTotal ELSE 0 END AS PD,
CASE WHEN t.AcctCode IN (${EXCL_ED}) THEN ${sign}t.LineTotal ELSE 0 END AS ED,
CASE WHEN t.AcctCode IN (${EXCL_EI}) THEN ${sign}t.LineTotal ELSE 0 END AS EI
FROM ${tbl} t
LEFT JOIN OITM t1 ON t.ItemCode = t1.ItemCode
LEFT JOIN OITB t2 ON t1.ItmsGrpCod = t2.ItmsGrpCod
INNER JOIN ${hdr} i ON t.DocEntry = i.DocEntry
WHERE ${dateWhere} ${extras}`;
}
// ── GET /api/reports/sales?asOf=YYYY-MM-DD&period=all|daily|monthly|yearly ─
router.get('/sales', verifyToken, async (req, res) => {
try {
const { period = 'all' } = req.query;
const target = req.query.asOf ? new Date(req.query.asOf) : (() => {
const d = new Date(); d.setDate(d.getDate() - 1); return d;
})();
const toDate = target.toISOString().split('T')[0];
const dd = target.getDate(), mm = target.getMonth() + 1, yyyy = target.getFullYear();
const yearStart = (dd < 16 && mm <= 1) ? `${yyyy - 1}-01-16` : `${yyyy}-01-16`;
const pMm = mm === 1 ? 12 : mm - 1;
const pYyyy = mm === 1 ? yyyy - 1 : yyyy;
const monthStart = dd < 16
? `${pYyyy}-${String(pMm).padStart(2, '0')}-16`
: `${yyyy}-${String(mm).padStart(2, '0')}-16`;
const pt = period.toLowerCase();
const periods = [];
if (pt === 'daily' || pt === 'all') periods.push([`Daily (${toDate})`, `i.DocDate = '${toDate}'`]);
if (pt === 'monthly' || pt === 'all') periods.push([`Monthly (${monthStart} – ${toDate})`, `i.DocDate BETWEEN '${monthStart}' AND '${toDate}'`]);
if (pt === 'yearly' || pt === 'all') periods.push([`Yearly (${yearStart} – ${toDate})`, `i.DocDate BETWEEN '${yearStart}' AND '${toDate}'`]);
const pool = await getPool();
const rows = [];
for (const [label, dw] of periods) {
const sql = `
SELECT
'${label}' AS Period,
ROUND(SUM(TM), 2) AS TM_BloodBag,
ROUND(SUM(PD), 2) AS PD_CAPD,
ROUND(SUM(ED), 2) AS ExportDirect,
ROUND(SUM(EI), 2) AS ExportIndirect,
ROUND(SUM(TM)+SUM(PD)+SUM(ED)+SUM(EI), 2) AS Total
FROM (
${buildRevSubq(dw, false)}
UNION ALL
${buildRevSubq(dw, true)}
) CK`;
try {
const r = await pool.request().query(sql);
if (r.recordset?.[0]) rows.push(r.recordset[0]);
} catch (e) {
rows.push({ Period: label, error: e.message });
}
}
res.json({ success: true, data: rows, asOf: toDate });
} catch (err) {
res.status(500).json({ success: false, message: err.message });
}
});
// ── GET /api/reports/fg-inventory?from=&to=&warehouse=&itemGroup= ─────────
router.get('/fg-inventory', verifyToken, async (req, res) => {
try {
let { from, to, warehouse = '01', itemGroup = 'FG BLOOD BAG' } = req.query;
if (!from || !to) {
const d = new Date();
to = d.toISOString().split('T')[0];
const f = new Date(d.getFullYear(), d.getMonth(), 1);
from = f.toISOString().split('T')[0];
}
const pool = await getPool();
const r = await pool.request()
.input('FromDate', from)
.input('ToDate', to)
.input('WhsCode', warehouse)
.input('ItmsGrp', itemGroup)
.execute('INVENTORY_FG_AVG_PRICE');
const data = r.recordset || [];
res.json({ success: true, data, count: data.length, from, to, warehouse, itemGroup });
} catch (err) {
res.status(500).json({ success: false, message: err.message });
}
});
// ── GET /api/reports/bom-cost?itemGroup= ─────────────────────────────────
router.get('/bom-cost', verifyToken, async (req, res) => {
try {
const { itemGroup = 'FG BLOOD BAG' } = req.query;
const pool = await getPool();
const r = await pool.request()
.input('grp', itemGroup)
.query(`
SELECT T1.Code AS ItemCode, T0.ItemName, MIN(P.Price) AS Price
FROM OITT T1
INNER JOIN OITM T0 ON T0.ItemCode = T1.Code
LEFT JOIN ITM1 P ON P.ItemCode = T1.Code
LEFT JOIN OITB T2 ON T2.ItmsGrpCod = T0.ItmsGrpCod
WHERE T2.ItmsGrpNam = @grp AND P.Price > 0
GROUP BY T1.Code, T0.ItemName
ORDER BY T1.Code`);
const data = r.recordset || [];
res.json({ success: true, data, count: data.length, itemGroup });
} catch (err) {
res.status(500).json({ success: false, message: err.message });
}
});
module.exports = router;
// Reused by routes/board.js so the Board Dashboard's Division Summary is
// computed by EXACTLY the same logic as this sales report (verified to match
// the management figures to the paisa).
module.exports.buildRevSubq = buildRevSubq;
module.exports.REV_CONST = { EXCL_DOCS, EXCL_TM, EXCL_PD, EXCL_ED, EXCL_EI };