sale order
SAP-ERP Portal CI/CD / build (push) Successful in 3m57s

This commit is contained in:
John
2026-10-05 18:45:17 +05:30
parent e725a6571b
commit eead8f5ffd
129 changed files with 14252 additions and 452 deletions
+36 -6
View File
@@ -1,11 +1,13 @@
'use strict';
// routes/notifications.js — aggregates "pending on me" items across the
// workflows that already have clear, per-user pending logic (Work Order,
// Production Order, BOM Requests, and — admin only — Password Reset
// requests), for the sidebar bell + dashboard "Pending Actions" widget.
// Deliberately scoped to these four for now; other approval workflows
// (Purchase Requests, Customer/Vendor registration, Project approvals,
// etc.) aren't included yet.
// Production Order, Batch Issuance, BOM Requests, Production Deviations,
// and — admin only — Password Reset requests), for the sidebar bell +
// dashboard "Pending Actions" widget. Every item is gated by the same
// approval-step/role check its own workflow route enforces, so a user only
// ever sees what they're actually permitted to act on. Other approval
// workflows (Purchase Requests, Customer/Vendor registration, Project
// approvals, etc.) aren't included yet.
const express = require('express');
const router = express.Router();
const { verifyToken, hasStepPerm, hasStepAssigned } = require('../middleware/auth');
@@ -56,13 +58,24 @@ router.get('/pending', verifyToken, async (req, res) => {
// Release and Transfer to Finished Goods have no standalone page of
// their own — those stay on production.html's detail popup.
const STAGE_CARD_HREF = { release: '/production', issuance: '/issue-production', receipt: '/receipt-production', transfer_fg: '/production', close: '/close-production' };
// Consumable Orders (Release → Issue → Close only, no Receipt/Transfer to
// FG) are gated by their OWN dedicated steps, never the general
// production_order:* ones — mirrors services/productionOrderStore.js's
// own notifyStepFor()/CONSUMABLE_STEP_FOR (not exported, so duplicated
// here, same convention as WORK_ORDER_STEP_KEYS above). Without this, a
// user holding the general 'production_order:release' step (but NOT
// 'production_order:consumable_release') incorrectly saw every pending
// Consumable Order in the bell too, even though the Production Order
// page itself already correctly hides Consumable Orders from them.
const CONSUMABLE_STEP_FOR = { release: 'consumable_release', issuance: 'consumable_issue', close: 'consumable_close' };
const pos = await poStore.listProductionOrders({ status: 'IN_PROGRESS' });
// REJECTED orders (receipt posted with rejection lines) still need to be
// CLOSED — surface them to the close-step users too.
const rejected = await poStore.listProductionOrders({ status: 'REJECTED' });
rejected.forEach(p => { pos.push(Object.assign({}, p, { stage: 4 })); }); // stage 4 = Close
pos.forEach(p => {
const key = poStore.STEP_KEYS[p.stage];
const generalKey = poStore.STEP_KEYS[p.stage];
const key = p.isConsumable ? (CONSUMABLE_STEP_FOR[generalKey] || null) : generalKey;
// Same rule as the Issue/Receipt/Close pages themselves: being ASSIGNED
// the step (any perm — view/add/edit/approve) means the action is yours,
// so it must show in the bell too. (Was 'approve'-only, which hid e.g.
@@ -120,6 +133,23 @@ router.get('/pending', verifyToken, async (req, res) => {
});
} catch (e) { console.warn('[notifications] bom scan failed:', e.message); }
// Production Deviations awaiting QA sign-off — pending for whoever holds
// 'approve' on production_order:deviation (see [[production-deviation-workflow]]).
// Only QA_STATUS='PENDING' counts as pending; N_A (QA not required for this
// one) / APPROVED / REJECTED are already resolved, nothing left to do.
try {
if (hasStepPerm(user, 'production_order:deviation', 'approve')) {
const devs = await require('../services/deviationStore').listDeviations({});
devs.filter(d => d.qaStatus === 'PENDING').forEach(d => {
items.push({
module: 'deviation', label: 'Deviation', title: d.sapDocNum ? `PWO #${d.sapDocNum}` : `Item ${d.itemCode}`,
detail: `${d.type.charAt(0) + d.type.slice(1).toLowerCase()} — ${d.itemCode}${d.newItemCode ? ' → ' + d.newItemCode : ''} — awaiting QA`,
link: d.sapAbsEntry ? `/production?open=${d.sapAbsEntry}` : '/deviations', id: d.id, cardHref: '/deviations',
});
});
}
} catch (e) { console.warn('[notifications] deviation scan failed:', e.message); }
if (user.role === 'admin' || user.role === 'sap_adder') {
try {
const pending = await require('../services/passwordResetStore').listRequests('PENDING');