91 lines
3.4 KiB
JavaScript
91 lines
3.4 KiB
JavaScript
// 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 };
|