120 lines
5.9 KiB
JavaScript
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;
|