@@ -140,6 +140,23 @@ async function bootstrap() {
|
||||
function toTs(iso) { return iso ? iso.replace('T', ' ').replace('Z', '').substring(0, 23) : null; }
|
||||
function safeJson(v, f) { if (!v) return f; try { return JSON.parse(v); } catch { return f; } }
|
||||
|
||||
// This Production Order's item's SAP Item Group (ItmsGrpCod), for
|
||||
// notifyStore's Item-Group email routing (Admin → System Settings → "Notify
|
||||
// by Item Group"). Queried against the SAP company's OWN database via
|
||||
// services/sqlPool.js's getPool(company) — NOT this file's own getConn(),
|
||||
// which only ever points at the portal's local APP_SQL_* tracking DB, not
|
||||
// SAP. Never throws — a lookup failure just means no group-routed
|
||||
// recipients get added, the normal step/module recipients still fire.
|
||||
async function itemGroupOf(itemCode, company) {
|
||||
if (!itemCode) return null;
|
||||
try {
|
||||
const { getPool } = require('./sqlPool');
|
||||
const pool = await getPool(company || null);
|
||||
const r = await pool.request().query(`SELECT "ItmsGrpCod" FROM [dbo].[OITM] WHERE "ItemCode" = '${String(itemCode).replace(/'/g, "''")}'`);
|
||||
return r.recordset && r.recordset[0] ? String(r.recordset[0].ItmsGrpCod) : null;
|
||||
} catch (e) { console.warn('[PO-STORE] itemGroupOf lookup failed:', e.message); return null; }
|
||||
}
|
||||
|
||||
function fromRow(row) {
|
||||
if (!row) return null;
|
||||
const stage = row.STAGE || 0;
|
||||
@@ -211,6 +228,7 @@ async function insertProductionOrder(p) {
|
||||
const created = await findById(idRows[0].ID);
|
||||
notify().notify({
|
||||
stepFullKey: notifyStepFor(created, STEP_KEYS[0]),
|
||||
itemGroupCode: await itemGroupOf(created.itemCode, created.company),
|
||||
title: `Production Order ${created.sapDocNum || '#' + created.id} — awaiting ${STEPS[0]}`,
|
||||
lines: [['Production Order', created.sapDocNum || '#' + created.id], ['Item', created.itemName || created.itemCode], ['Created By', created.createdByName], ['Pending Step', STEPS[0]]],
|
||||
url: `${process.env.APP_BASE_URL || ''}/production-order?id=${created.id}`,
|
||||
@@ -338,6 +356,7 @@ async function advanceStage(id, { action, stepKey, by, byName, remarks }) {
|
||||
const nextStepKey = STEP_KEYS[newStage];
|
||||
notify().notify({
|
||||
stepFullKey: notifyStepFor(advanced, nextStepKey),
|
||||
itemGroupCode: await itemGroupOf(advanced.itemCode, advanced.company),
|
||||
title: `Production Order ${advanced.sapDocNum || '#' + advanced.id} — awaiting ${STEPS[newStage]}`,
|
||||
lines: [['Production Order', advanced.sapDocNum || '#' + advanced.id], ['Item', advanced.itemName || advanced.itemCode], ['Completed By', byName || by], ['Pending Step', STEPS[newStage]]],
|
||||
url,
|
||||
@@ -424,6 +443,7 @@ async function catchUpStage(id, targetStage, { by, byName, remarks }) {
|
||||
const nextStepKey = STEP_KEYS[targetStage];
|
||||
notify().notify({
|
||||
stepFullKey: notifyStepFor(caughtUp, nextStepKey),
|
||||
itemGroupCode: await itemGroupOf(caughtUp.itemCode, caughtUp.company),
|
||||
title: `Production Order ${caughtUp.sapDocNum || '#' + caughtUp.id} — awaiting ${STEPS[targetStage]}`,
|
||||
lines: [['Production Order', caughtUp.sapDocNum || '#' + caughtUp.id], ['Item', caughtUp.itemName || caughtUp.itemCode], ['Pending Step', STEPS[targetStage]]],
|
||||
url,
|
||||
@@ -478,8 +498,27 @@ async function receiveManual(id, { by, byName, remarks }) {
|
||||
return findById(id);
|
||||
}
|
||||
|
||||
// BATCH_NUMBER/MFG_DATE/EXP_DATE are copied from the Work Order at
|
||||
// insertProductionOrder() time and never touched again after that — a
|
||||
// deliberate snapshot everywhere else (see LOCKED_BATCH_FIELDS in
|
||||
// routes/batchIntimations.js), EXCEPT a rejected Work Order can still be
|
||||
// edited+resubmitted with a corrected batch no. even after its Production
|
||||
// Order already exists (rejection only checks WO status, not whether a PO
|
||||
// was generated earlier). Without this, the Production Order — and every
|
||||
// screen that searches/displays batch no. off it (Production Order list,
|
||||
// Issue for Production) — permanently keeps showing the OLD batch even
|
||||
// though the Work Order and SAP's own OWOR.Comments have the corrected one.
|
||||
// Called from workOrderStore.resubmitAfterReject(); a no-op if no Production
|
||||
// Order was ever generated for this Work Order.
|
||||
async function syncBatchFromWorkOrder(workOrderId, { batchNumber, mfgDate, expDate }) {
|
||||
await exec(
|
||||
`UPDATE ${TABLE} SET BATCH_NUMBER=?, MFG_DATE=?, EXP_DATE=?, UPDATED_AT=? WHERE WORK_ORDER_ID=? AND (IS_DELETED=0 OR IS_DELETED IS NULL)`,
|
||||
[batchNumber || '', mfgDate || '', expDate || '', toTs(new Date().toISOString()), parseInt(workOrderId)]
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
bootstrap, STEPS, STEP_KEYS, ISSUANCE_STAGE, insertProductionOrder,
|
||||
listProductionOrders, findById, findBySapAbsEntry, advanceStage, verify, receiveManual, softDelete,
|
||||
adminForceClose, adminForceCancel, catchUpStage,
|
||||
adminForceClose, adminForceCancel, catchUpStage, syncBatchFromWorkOrder,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user