Files
sap-erp/routes/businessMaster.js
John eead8f5ffd
SAP-ERP Portal CI/CD / build (push) Successful in 3m57s
sale order
2026-10-05 18:45:17 +05:30

120 lines
5.9 KiB
JavaScript

'use strict';
const express = require('express');
const router = express.Router();
const { verifyToken, verifyAdmin } = require('../middleware/auth');
const { resolve: resolveCompany } = require('../services/companyConfig');
function getSap() { return require('../services/sapServiceLayer'); }
// Fields returned in the list view (kept small for performance)
const LIST_SELECT = [
'CardCode','CardName','CardType','GroupCode','Currency','Phone1','EmailAddress',
'Valid','Frozen','CreditLimit','Cellular','Address','Country',
].join(',');
// Fields the UI is allowed to edit via PATCH
const EDITABLE_FIELDS = [
'CardName','CardForeignName','GroupCode','Currency','Phone1','Phone2','Cellular','Fax',
'EmailAddress','Website','ContactPerson','Notes','Valid','Frozen','CreditLimit',
'PayTermsGrpCode','Address','MailAddress','City','MailCity','Country','MailCountry',
'ZipCode','MailZipCode','County','MailCounty','Block','MailBlock',
'FederalTaxID','VatLiable','GSTType','LicTradNum',
];
// Array/sub-object fields the UI may replace wholesale
const EDITABLE_ARRAYS = ['BPAddresses', 'BPBankAccounts', 'ContactEmployees'];
function extractPatch(body) {
const p = {};
EDITABLE_FIELDS.forEach(f => { if (body[f] !== undefined) p[f] = body[f]; });
EDITABLE_ARRAYS.forEach(f => { if (Array.isArray(body[f])) p[f] = body[f]; });
// Any user-defined field (U_*) is passed through as-is
Object.keys(body).forEach(k => {
if (k.startsWith('U_')) p[k] = body[k];
});
return p;
}
// ══ LIST ═════════════════════════════════════════════════════════════════════
// GET /api/business-master?type=cCustomer|cSupplier|cLid&search=abc&top=50&skip=0
router.get('/', verifyToken, async (req, res) => {
const companyDB = resolveCompany(req.query.company);
const top = Math.min(parseInt(req.query.top, 10) || 50, 200);
const skip = parseInt(req.query.skip, 10) || 0;
const type = (req.query.type || '').trim();
const search = (req.query.search || '').trim().replace(/'/g, "''");
const filters = [];
if (type) filters.push(`CardType eq '${type}'`);
if (search) {
filters.push(`(contains(CardName,'${search}') or contains(CardCode,'${search}'))`);
}
let endpoint = `BusinessPartners?$select=${LIST_SELECT}&$top=${top}&$skip=${skip}&$orderby=CardName`;
if (filters.length) endpoint += `&$filter=${encodeURIComponent(filters.join(' and '))}`;
try {
const result = await getSap().sapRequest('GET', endpoint, null, companyDB, true, { Prefer: `odata.maxpagesize=${top}` });
// Genuine total (same filter, no paging) — drives "Page X of Y" on the
// client instead of an open-ended "Page X".
let total = null;
try {
const countEndpoint = `BusinessPartners/$count${filters.length ? `?$filter=${encodeURIComponent(filters.join(' and '))}` : ''}`;
const c = await getSap().sapRequest('GET', countEndpoint, null, companyDB);
const n = Number(c);
if (!isNaN(n)) total = n;
} catch (e) { console.warn('[BUSINESS-MASTER] $count failed — pager will show no total:', e.message); }
res.json({ success: true, data: result.value || [], count: result['odata.count'], total });
} catch (err) {
res.status(500).json({ success: false, message: err.message });
}
});
// ══ GET ONE ══════════════════════════════════════════════════════════════════
router.get('/:cardCode', verifyToken, async (req, res) => {
const companyDB = resolveCompany(req.query.company);
const cardCode = encodeURIComponent(req.params.cardCode);
try {
const result = await getSap().sapRequest('GET', `BusinessPartners('${cardCode}')`, null, companyDB);
res.json({ success: true, data: result });
} catch (err) {
res.status(500).json({ success: false, message: err.message });
}
});
// ══ UPDATE ═══════════════════════════════════════════════════════════════════
// PATCH /api/business-master/:cardCode
router.patch('/:cardCode', verifyToken, verifyAdmin, async (req, res) => {
const companyDB = resolveCompany(req.body.company);
const cardCode = encodeURIComponent(req.params.cardCode);
const patch = extractPatch(req.body);
if (!Object.keys(patch).length) {
return res.status(400).json({ success: false, message: 'No editable fields provided' });
}
try {
await getSap().sapRequest('PATCH', `BusinessPartners('${cardCode}')`, patch, companyDB);
console.log(`[BUSINESS-MASTER] ${req.user.username} updated ${req.params.cardCode}:`, Object.keys(patch).join(', '));
res.json({ success: true, message: 'Business partner updated' });
} catch (err) {
console.error('[BUSINESS-MASTER] update error:', err.message);
res.status(500).json({ success: false, message: err.message });
}
});
// ══ DELETE ═══════════════════════════════════════════════════════════════════
// DELETE /api/business-master/:cardCode
router.delete('/:cardCode', verifyToken, verifyAdmin, async (req, res) => {
const companyDB = resolveCompany(req.query.company);
const cardCode = encodeURIComponent(req.params.cardCode);
try {
await getSap().sapRequest('DELETE', `BusinessPartners('${cardCode}')`, null, companyDB);
console.log(`[BUSINESS-MASTER] ${req.user.username} deleted ${req.params.cardCode}`);
res.json({ success: true, message: 'Business partner deleted' });
} catch (err) {
console.error('[BUSINESS-MASTER] delete error:', err.message);
res.status(500).json({ success: false, message: err.message });
}
});
module.exports = router;