first commit
SAP-ERP Portal CI/CD / build (push) Failing after 5m20s

This commit is contained in:
John
2026-09-23 17:31:02 +05:30
commit 69b4e68baf
51657 changed files with 3864077 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
// 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 };