// services/autoclaveRejectionStore.js // Stores the "Autoclave Rejection" counts captured on a Receipt from // Production for FG items (see routes/sap.js POST /receipt-production and // public/receipt-production.html). This data is PURELY informational — it is // NOT posted to SAP and has no effect on any inventory/production quantity. // It lives in the app's own database (APP_SQL_*), keyed by the SAP receipt // document, and is only ever written AFTER the SAP receipt posts successfully. 'use strict'; const sql = require('mssql'); const TABLE = `[dbo].[ZFG_AUTOCLAVE_REJECTIONS]`; let _conn = null; async function getConn() { if (_conn) return _conn; _conn = await sql.connect({ server: process.env.APP_SQL_HOST, port: parseInt(process.env.APP_SQL_PORT), user: process.env.APP_SQL_USER, password: process.env.APP_SQL_PASSWORD, database: process.env.APP_SQL_DATABASE, options: { encrypt: true, trustServerCertificate: true }, }); return _conn; } async function exec(sqlQuery, params = []) { const conn = await getConn(); const request = conn.request(); params.forEach((param, index) => { request.input(`param${index}`, param); }); const replacedSql = sqlQuery.replace(/\?/g, (m, offset, string) => { const i = (string.slice(0, offset).match(/\?/g) || []).length; return `@param${i}`; }); const result = await request.query(replacedSql); return result.recordset || []; } function isAlreadyExists(e) { const m = (e.message || '').toLowerCase(); return m.includes('already exists') || m.includes('duplicate') || m.includes('existing object') || m.includes('there is already an object'); } async function bootstrap() { console.log('[AUTOCLAVE-STORE] Checking table', TABLE, '...'); await exec(` CREATE TABLE ${TABLE} ( ID INT IDENTITY(1,1) PRIMARY KEY, ABS_ENTRY INT, DOC_ENTRY INT, DOC_NUM NVARCHAR(30), ITEM_CODE NVARCHAR(60), BATCH_NUMBER NVARCHAR(60), ROWS_JSON NVARCHAR(MAX), TOTAL_REJECTION NVARCHAR(60), CREATED_BY NVARCHAR(50), CREATED_NAME NVARCHAR(100), CREATED_AT DATETIME2, COMPANY NVARCHAR(60) ) `).catch(e => { if (isAlreadyExists(e)) { console.log('[AUTOCLAVE-STORE] Table exists — OK'); } else throw e; }); console.log('[AUTOCLAVE-STORE] ✅ Ready'); } function toTs(iso) { return iso ? iso.replace('T', ' ').replace('Z', '').substring(0, 23) : null; } // rows: array of numbers (rejection qty per manually-added row). async function saveRejection({ absEntry, docEntry, docNum, itemCode, batchNumber, rows, company, createdBy, createdByName }) { const clean = (Array.isArray(rows) ? rows : []) .map(r => Number(r)) .filter(n => !isNaN(n) && n > 0); const total = clean.reduce((s, n) => s + n, 0); await exec(` INSERT INTO ${TABLE} ( ABS_ENTRY, DOC_ENTRY, DOC_NUM, ITEM_CODE, BATCH_NUMBER, ROWS_JSON, TOTAL_REJECTION, CREATED_BY, CREATED_NAME, CREATED_AT, COMPANY ) VALUES (?,?,?,?,?,?,?,?,?,?,?) `, [ absEntry != null ? parseInt(absEntry) : null, docEntry != null ? parseInt(docEntry) : null, docNum != null ? String(docNum) : null, itemCode || '', batchNumber || '', JSON.stringify(clean), String(total), createdBy || '', createdByName || createdBy || '', toTs(new Date().toISOString()), company || '', ]); return { total, count: clean.length }; } module.exports = { bootstrap, saveRejection };