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
+105
View File
@@ -0,0 +1,105 @@
'use strict';
// routes/prodDashboard.js — Production Dashboard analytics, open to ANY
// logged-in user (page visibility is controlled by the 'production-dashboard'
// MODULE grant, like every other production page). Read-only aggregates:
// - SAP: OWOR (orders), OIGN/IGN1 BaseType=202 (receipts from production)
// - Local: ZPRODUCTION_ORDERS (portal workflow stages)
const express = require('express');
const router = express.Router();
const { verifyToken } = require('../middleware/auth');
const { getPool } = require('../services/sqlPool');
const { getPool: getAppPool } = require('../services/appSqlPool');
async function hana(sqlText) {
const pool = await getPool();
return (await pool.request().query(sqlText)).recordset || [];
}
async function appq(sqlText) {
const pool = await getAppPool();
return (await pool.request().query(sqlText)).recordset || [];
}
// Superseded by the redesigned Production Dashboard (Work Order + Rejection
// Register + OEE) — this SAP OWOR/IGN1-based summary now backs the
// admin-only legacy page (public/production-dashboard-legacy.html) only,
// so it's admin-gated here too rather than relying on the client alone.
router.get('/summary', verifyToken, async (req, res) => {
if (req.user?.role !== 'admin')
return res.status(403).json({ success: false, message: 'This legacy dashboard is admin-only.' });
try {
const DS = /^\d{4}-\d{2}-\d{2}$/;
const iso = d => d.toISOString().slice(0, 10);
let from = DS.test(req.query.from || '') ? req.query.from : null;
let to = DS.test(req.query.to || '') ? req.query.to : null;
if (!from || !to) {
const now = new Date(); to = iso(now);
const f = new Date(now); f.setMonth(f.getMonth() - 6); from = iso(f);
}
const like = s => String(s || '').replace(/'/g, "''").slice(0, 60).trim();
const itemQ = like(req.query.itemQ);
const itemCond = itemQ ? ` AND (T1.ItemCode LIKE '%${itemQ}%' OR T1.Dscription LIKE '%${itemQ}%')` : '';
const rc = `T0.DocDate>='${from}' AND T0.DocDate<='${to}'`; // receipts range
const oc = `PostDate>='${from}' AND PostDate<='${to}'`; // orders range
// ── KPIs ─────────────────────────────────────────────────────────────
const k = (await hana(`SELECT
(SELECT ISNULL(SUM(T1.Quantity),0) FROM IGN1 T1 JOIN OIGN T0 ON T0.DocEntry=T1.DocEntry WHERE T1.BaseType=202 AND ${rc}) AS outputQty,
(SELECT COUNT(DISTINCT T0.DocEntry) FROM IGN1 T1 JOIN OIGN T0 ON T0.DocEntry=T1.DocEntry WHERE T1.BaseType=202 AND ${rc}) AS receiptDocs,
(SELECT COUNT(DISTINCT T1.ItemCode) FROM IGN1 T1 JOIN OIGN T0 ON T0.DocEntry=T1.DocEntry WHERE T1.BaseType=202 AND ${rc}) AS itemsProduced,
(SELECT COUNT(*) FROM OWOR WHERE ${oc}) AS ordersCreated,
(SELECT ISNULL(SUM(PlannedQty),0) FROM OWOR WHERE ${oc}) AS plannedQtyCreated,
(SELECT COUNT(*) FROM OWOR WHERE ${oc} AND Status='L') AS ordersClosed,
(SELECT COUNT(*) FROM OWOR WHERE Status='P') AS openPlanned,
(SELECT COUNT(*) FROM OWOR WHERE Status='R') AS openReleased`))[0] || {};
// ── Monthly production output (receipts from production) ─────────────
const monthly = await hana(`
SELECT YEAR(T0.DocDate) y, MONTH(T0.DocDate) m,
SUM(T1.Quantity) AS qty, COUNT(DISTINCT T0.DocEntry) AS docs
FROM IGN1 T1 JOIN OIGN T0 ON T0.DocEntry=T1.DocEntry
WHERE T1.BaseType=202 AND ${rc}
GROUP BY YEAR(T0.DocDate), MONTH(T0.DocDate)
ORDER BY y, m`);
// ── Top produced items in range (searchable) ─────────────────────────
const topItems = await hana(`
SELECT TOP ${itemQ ? 12 : 8} T1.ItemCode AS code, MAX(T1.Dscription) AS name, SUM(T1.Quantity) AS qty
FROM IGN1 T1 JOIN OIGN T0 ON T0.DocEntry=T1.DocEntry
WHERE T1.BaseType=202 AND ${rc}${itemCond}
GROUP BY T1.ItemCode ORDER BY qty DESC`);
// ── Portal workflow funnel (local tracking, point-in-time) ───────────
const STEPS = ['Release', 'Issuance', 'Receipt from Production', 'Transfer to Finished Goods', 'Close'];
let stages = STEPS.map((label, i) => ({ stage: i, label, count: 0 }));
let rejected = 0;
try {
const rows = await appq(`SELECT STAGE, STATUS, COUNT(*) n FROM [dbo].[ZPRODUCTION_ORDERS] WHERE IS_DELETED=0 AND STATUS IN ('IN_PROGRESS','REJECTED') GROUP BY STAGE, STATUS`);
rows.forEach(r => {
if (r.STATUS === 'REJECTED') rejected += Number(r.n) || 0;
else if (stages[r.STAGE]) stages[r.STAGE].count = Number(r.n) || 0;
});
} catch (e) { console.warn('[PROD-DASH] local stages failed (non-fatal):', e.message); }
res.json({ success: true, data: {
from, to,
kpi: {
outputQty: Number(k.outputQty) || 0,
receiptDocs: Number(k.receiptDocs) || 0,
itemsProduced: Number(k.itemsProduced) || 0,
ordersCreated: Number(k.ordersCreated) || 0,
plannedQtyCreated:Number(k.plannedQtyCreated) || 0,
ordersClosed: Number(k.ordersClosed) || 0,
openPlanned: Number(k.openPlanned) || 0,
openReleased: Number(k.openReleased) || 0,
},
monthly: monthly.map(r => ({ y: r.y, m: r.m, qty: Number(r.qty) || 0, docs: Number(r.docs) || 0 })),
topItems: topItems.map(r => ({ code: r.code, name: r.name, qty: Number(r.qty) || 0 })),
stages, rejected,
}});
} catch (err) {
console.error('[PROD-DASH] summary failed:', err.message);
res.status(500).json({ success: false, message: err.message });
}
});
module.exports = router;