Files
sap-erp/services/rmOvgSettingsStore.js
T
John 69b4e68baf
SAP-ERP Portal CI/CD / build (push) Failing after 5m20s
first commit
2026-09-23 17:31:02 +05:30

136 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// services/rmOvgSettingsStore.js
// Admin-configured Raw Material "Ovg%" override — maps an item code and/or
// its SAP item group to a default overage percentage. Used by work-order.html
// to pre-fill a raw material row's Ovg. cell and to recompute "Qty Req./100 ml"
// as (BOM value) − (BOM value × Ovg%/100) instead of the plain BOM figure,
// for any item this resolves a match for. See [[work-order-qty-req-two-way-sync]]
// for the existing Raw Material calc model this feature layers on top of.
const sql = require('mssql');
const TABLE = `[dbo].[ZRM_OVG_SETTINGS]`;
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('[RM-OVG-SETTINGS] Checking table', TABLE, '...');
await exec(`
CREATE TABLE ${TABLE} (
ID INT IDENTITY(1,1) PRIMARY KEY,
ITEM_CODE NVARCHAR(60) NULL,
ITEM_GROUP INT NULL,
OVG_PERCENT FLOAT NOT NULL,
CREATED_BY NVARCHAR(50),
CREATED_AT DATETIME2,
UPDATED_BY NVARCHAR(50),
UPDATED_AT DATETIME2
)
`).catch(e => {
if (isAlreadyExists(e)) { console.log('[RM-OVG-SETTINGS] Table exists — OK'); }
else throw e;
});
console.log('[RM-OVG-SETTINGS] ✅ Ready');
}
function fromRow(row) {
return {
id: row.ID,
itemCode: row.ITEM_CODE || '',
itemGroup: row.ITEM_GROUP != null ? row.ITEM_GROUP : null,
ovgPercent: row.OVG_PERCENT,
createdBy: row.CREATED_BY || '',
createdAt: row.CREATED_AT ? new Date(row.CREATED_AT).toISOString() : null,
updatedBy: row.UPDATED_BY || '',
updatedAt: row.UPDATED_AT ? new Date(row.UPDATED_AT).toISOString() : null,
};
}
async function listAll() {
const rows = await exec(`SELECT * FROM ${TABLE} ORDER BY ITEM_CODE, ITEM_GROUP`);
return rows.map(fromRow);
}
async function create({ itemCode, itemGroup, ovgPercent, by }) {
const code = (itemCode || '').trim().toUpperCase() || null;
const group = itemGroup != null && itemGroup !== '' ? parseInt(itemGroup) : null;
if (!code && group == null) throw new Error('Provide an Item Code and/or Item Group');
const pct = parseFloat(ovgPercent);
if (isNaN(pct)) throw new Error('Ovg% must be a number');
const now = new Date().toISOString();
const idRows = await exec(`
INSERT INTO ${TABLE} (ITEM_CODE, ITEM_GROUP, OVG_PERCENT, CREATED_BY, CREATED_AT, UPDATED_BY, UPDATED_AT)
VALUES (?,?,?,?,?,?,?);
SELECT SCOPE_IDENTITY() AS ID;
`, [code, group, pct, by || '', now.replace('T', ' ').substring(0, 23), by || '', now.replace('T', ' ').substring(0, 23)]);
const rows = await exec(`SELECT * FROM ${TABLE} WHERE ID = ?`, [idRows[0].ID]);
return fromRow(rows[0]);
}
async function update(id, { itemCode, itemGroup, ovgPercent, by }) {
const code = (itemCode || '').trim().toUpperCase() || null;
const group = itemGroup != null && itemGroup !== '' ? parseInt(itemGroup) : null;
if (!code && group == null) throw new Error('Provide an Item Code and/or Item Group');
const pct = parseFloat(ovgPercent);
if (isNaN(pct)) throw new Error('Ovg% must be a number');
const now = new Date().toISOString().replace('T', ' ').substring(0, 23);
await exec(`UPDATE ${TABLE} SET ITEM_CODE=?, ITEM_GROUP=?, OVG_PERCENT=?, UPDATED_BY=?, UPDATED_AT=? WHERE ID=?`,
[code, group, pct, by || '', now, parseInt(id)]);
const rows = await exec(`SELECT * FROM ${TABLE} WHERE ID = ?`, [parseInt(id)]);
if (!rows.length) throw new Error('Not found');
return fromRow(rows[0]);
}
async function remove(id) {
await exec(`DELETE FROM ${TABLE} WHERE ID = ?`, [parseInt(id)]);
}
// Resolves an Ovg% for each item code, given that item's own SAP item group
// (caller looks the group up from OITM — this store has no SAP connection of
// its own). Item-code-specific rows win over item-group rows for the same
// item. Returns { itemCode: percent } — items with no match are simply
// absent from the result (never a 0 entry, since 0 IS a valid configured %).
async function resolveForItems(itemCodesUpper, groupByItemCode) {
const rows = await listAll();
const byCode = {};
const byGroup = {};
rows.forEach(r => {
if (r.itemCode) byCode[r.itemCode] = r.ovgPercent;
else if (r.itemGroup != null) byGroup[r.itemGroup] = r.ovgPercent;
});
const out = {};
itemCodesUpper.forEach(code => {
if (byCode[code] != null) { out[code] = byCode[code]; return; }
const grp = groupByItemCode[code];
if (grp != null && byGroup[grp] != null) out[code] = byGroup[grp];
});
return out;
}
module.exports = { bootstrap, listAll, create, update, remove, resolveForItems };