// services/notifyStore.js // Stage-change email notifications for the Production module (Work Order, // Production Order, Issue for Production, Verify Work Order, Batch Issuance // Intimation). Given an approval-step fullKey ("workflow:key") and/or a // module key, works out who should be emailed — whoever currently holds that // step/module (reverse-lookup over hanaUsers.listUsers(), there is no such // lookup built into hanaUsers/auth today) PLUS any admin-configured fixed // extra recipients (services/appSettingsStore.notifyExtraEmails()) — and // sends via services/mailer.js. Every entry point here is fire-and-forget // safe: it never throws, so a notification failure can never break the // mutation that triggered it. 'use strict'; const hanaUsers = require('./hanaUsers'); const { normalizeSteps, ALL_PERMS } = require('../middleware/auth'); const appSettingsStore = require('./appSettingsStore'); const mailer = require('./mailer'); function moduleKeyOf(m) { return `module:${m}`; } // Does this user explicitly hold `fullKey`? Deliberately NOT using // middleware/auth's hasStepAssigned() — that helper auto-passes every admin // for every step (a permission-check bypass, correct for gating actions), // which would otherwise email admin@company.com on literally every stage // transition in the whole app regardless of whether they're actually // assigned to it. Email recipients should reflect real assignment only. function isExplicitlyAssigned(user, fullKey) { const steps = normalizeSteps(user?.approvalSteps); const entry = steps.find(s => s.step === fullKey); return !!entry && ALL_PERMS.some(p => entry.perms.includes(p)); } // All active users holding ANY permission on approval step `fullKey`, who // haven't opted out of email notifications (Admin → user → "Send // stage-change email notifications to this user"). async function usersForStep(fullKey) { const all = await hanaUsers.listUsers(); return all.filter(u => u.active && u.emailNotify !== false && isExplicitlyAssigned(u, fullKey)); } // All active users granted a given sidebar module (public/sidebar.js's // module keys, e.g. 'production-batch-issuance'). Admins are not // auto-included — only users explicitly granted the module. Same // email-notify opt-out as usersForStep() above. async function usersForModule(moduleKey) { const all = await hanaUsers.listUsers(); return all.filter(u => u.active && u.emailNotify !== false && Array.isArray(u.modules) && u.modules.includes(moduleKey)); } function extraEmailsFor(key) { const map = appSettingsStore.notifyExtraEmails(); return String(map[key] || '').split(',').map(s => s.trim()).filter(Boolean); } // Item-Group-routed recipients (Admin → System Settings → "Notify by Item // Group") — ALWAYS notified in addition to the step/module recipients above, // keyed by the order's MAIN PRODUCT's SAP Item Group (ItmsGrpCod), not every // component's group. Callers resolve the group code themselves (they already // have SAP/HANA access; notifyStore.js deliberately doesn't) and pass it in // as `itemGroupCode`. function groupExtraEmailsFor(groupCode) { if (groupCode == null || groupCode === '') return []; const map = appSettingsStore.notifyItemGroupEmails(); return String(map[String(groupCode)] || '').split(',').map(s => s.trim()).filter(Boolean); } function esc(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(//g, '>'); } function buildHtml({ title, lines, url }) { const rows = (lines || []).map(([k, v]) => `
Automated notification from the SAP ERP Portal — Production module.