'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;