478 lines
23 KiB
JavaScript
478 lines
23 KiB
JavaScript
// backend/services/hanaVendorStore.js
|
|
// SQL persistence for vendor registrations.
|
|
// Schema: uses SAP_B1_COMPANY env var (same as hanaStore.js)
|
|
// Table: ZVENDOR_PORTAL (auto-created on first run)
|
|
|
|
const { getPool } = require('./appSqlPool');
|
|
const { DEFAULT_COMPANY } = require('./companyConfig');
|
|
|
|
const DEFAULT_SCHEMA = DEFAULT_COMPANY;
|
|
const tbl = (s) => `[dbo].[ZVENDOR_PORTAL]`;
|
|
|
|
async function exec(sqlQuery, params = []) {
|
|
const pool = await getPool();
|
|
const request = pool.request();
|
|
params.forEach((param, index) => {
|
|
request.input(`param${index}`, param);
|
|
});
|
|
const replacedSql = sqlQuery.replace(/\?/g, (match, offset, string) => {
|
|
const paramIndex = (string.slice(0, offset).match(/\?/g) || []).length;
|
|
return `@param${paramIndex}`;
|
|
});
|
|
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');
|
|
}
|
|
|
|
// ── Bootstrap ─────────────────────────────────────────────────────────────────
|
|
async function bootstrap(schema) {
|
|
console.log('[VENDOR STORE] Checking table', tbl(schema), '...');
|
|
|
|
let tableExisted = false;
|
|
await exec(`
|
|
CREATE TABLE ${tbl(schema)} (
|
|
[ID] INT IDENTITY(1,1) PRIMARY KEY,
|
|
[STATUS] NVARCHAR(30) DEFAULT 'PENDING',
|
|
[VENDOR_TYPE] NVARCHAR(20) DEFAULT 'SUPPLIER',
|
|
[CARD_NAME] NVARCHAR(100),
|
|
[FOREIGN_NAME] NVARCHAR(100),
|
|
[TYPE_OF_BUSINESS] NVARCHAR(50),
|
|
[INDUSTRY] NVARCHAR(100),
|
|
[PRODUCTS] NVARCHAR(500),
|
|
[PAYMENT_TERMS] NVARCHAR(50),
|
|
[CONTACT_FIRST] NVARCHAR(60),
|
|
[CONTACT_LAST] NVARCHAR(60),
|
|
[CONTACT_TITLE] NVARCHAR(60),
|
|
[MOBILE] NVARCHAR(20),
|
|
[ALT_CONTACT] NVARCHAR(20),
|
|
[EMAIL] NVARCHAR(150),
|
|
[BILL_STREET] NVARCHAR(200),
|
|
[BILL_BLOCK] NVARCHAR(100),
|
|
[BILL_CITY] NVARCHAR(100),
|
|
[BILL_ZIP] NVARCHAR(20),
|
|
[BILL_STATE] NVARCHAR(100),
|
|
[BILL_COUNTRY] NVARCHAR(60),
|
|
[GSTIN] NVARCHAR(15),
|
|
[PAN] NVARCHAR(10),
|
|
[TAN] NVARCHAR(10),
|
|
[CURRENCY] NVARCHAR(50),
|
|
[HAS_TDS] NVARCHAR(1) DEFAULT 'N',
|
|
[TDS_CATEGORY] NVARCHAR(100),
|
|
[TDS_RATE] DECIMAL(5,2) DEFAULT 0,
|
|
[TDS_LDC_NO] NVARCHAR(50),
|
|
[HAS_MSME] NVARCHAR(1) DEFAULT 'N',
|
|
[MSME_NO] NVARCHAR(30),
|
|
[MSME_TYPE] NVARCHAR(20),
|
|
[MSME_BTYPE] NVARCHAR(30),
|
|
[FSSAI_NO] NVARCHAR(20),
|
|
[REMARKS] NVARCHAR(MAX),
|
|
[BANK_ACCOUNTS] NVARCHAR(MAX),
|
|
[ATTACHMENTS] NVARCHAR(MAX),
|
|
[ALL_BILL_ADDRS] NVARCHAR(MAX),
|
|
[ALL_SHIP_ADDRS] NVARCHAR(MAX),
|
|
[USER_ID] NVARCHAR(50),
|
|
[USER_TYPE] NVARCHAR(50),
|
|
[USER_DEPT] NVARCHAR(100),
|
|
[SUBMITTED_AT] DATETIME2 DEFAULT GETDATE(),
|
|
[MGR_CARD_CODE_PREFIX] NVARCHAR(20) DEFAULT 'VENDA',
|
|
[MGR_GROUP_CODE] INT,
|
|
[MGR_GROUP] NVARCHAR(100),
|
|
[MGR_PAY_TERMS_CODE] INT,
|
|
[MGR_PAY_TERMS] NVARCHAR(100),
|
|
[MGR_PURCHASE_ACCOUNT] NVARCHAR(20) DEFAULT '2101001',
|
|
[MGR_PURCHASE_ACCT_NAME] NVARCHAR(100),
|
|
[MGR_CURRENCY] NVARCHAR(50) DEFAULT 'Indian Rupee',
|
|
[MGR_LANGUAGE] NVARCHAR(50) DEFAULT 'English (UK)',
|
|
[MGR_CREDIT_LIMIT] DECIMAL(18,2) DEFAULT 0,
|
|
[MGR_NOTES] NVARCHAR(MAX),
|
|
[MGR_BRANCH] NVARCHAR(100),
|
|
[MGR_AREA] NVARCHAR(100),
|
|
[MGR_TERRITORY] NVARCHAR(100),
|
|
[MGR_SALES_PERSON_CODE] INT,
|
|
[MGR_SALES_EMPLOYEE] NVARCHAR(100),
|
|
[MGR_MAIN_GROUP] NVARCHAR(50),
|
|
[MGR_CHAIN] NVARCHAR(50),
|
|
[VERIFIED_BY] NVARCHAR(60),
|
|
[VERIFIED_AT] DATETIME2,
|
|
[APPROVED_BY] NVARCHAR(60),
|
|
[APPROVED_AT] DATETIME2,
|
|
[REJECTED_BY] NVARCHAR(60),
|
|
[REJECTED_AT] DATETIME2,
|
|
[SAP_CARD_CODE] NVARCHAR(30),
|
|
[SAP_ATTACHMENT_ENTRY] INT,
|
|
[COMPANY] NVARCHAR(50) DEFAULT '${DEFAULT_SCHEMA}'
|
|
)
|
|
`).catch(e => {
|
|
if (isAlreadyExists(e)) {
|
|
tableExisted = true;
|
|
console.log('[VENDOR STORE] Table already exists — checking for missing columns...');
|
|
} else {
|
|
throw e;
|
|
}
|
|
});
|
|
|
|
// ── Safe migrations — only add columns that don't exist yet ──────────────
|
|
if (tableExisted) {
|
|
const migrations = [
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='MGR_SALES_PERSON_CODE') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [MGR_SALES_PERSON_CODE] INT`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='MGR_SALES_EMPLOYEE') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [MGR_SALES_EMPLOYEE] NVARCHAR(100)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='MGR_MAIN_GROUP') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [MGR_MAIN_GROUP] NVARCHAR(50)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='MGR_CHAIN') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [MGR_CHAIN] NVARCHAR(50)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='COMPANY') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [COMPANY] NVARCHAR(50) DEFAULT '${DEFAULT_SCHEMA}'`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='MSME_BTYPE') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [MSME_BTYPE] NVARCHAR(30)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='FSSAI_NO') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [FSSAI_NO] NVARCHAR(20)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='ALL_BILL_ADDRS') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [ALL_BILL_ADDRS] NVARCHAR(MAX)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='ALL_SHIP_ADDRS') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [ALL_SHIP_ADDRS] NVARCHAR(MAX)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='VERIFIED_BY') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [VERIFIED_BY] NVARCHAR(60)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='VERIFIED_AT') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [VERIFIED_AT] DATETIME2`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='APPROVED_BY') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [APPROVED_BY] NVARCHAR(60)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='APPROVED_AT') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [APPROVED_AT] DATETIME2`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='REJECTED_BY') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [REJECTED_BY] NVARCHAR(60)`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='REJECTED_AT') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [REJECTED_AT] DATETIME2`,
|
|
`IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ZVENDOR_PORTAL' AND COLUMN_NAME='SAP_ATTACHMENT_ENTRY') ALTER TABLE [dbo].[ZVENDOR_PORTAL] ADD [SAP_ATTACHMENT_ENTRY] INT`,
|
|
];
|
|
for (const migration of migrations) {
|
|
await exec(migration).catch(e => {
|
|
console.warn('[VENDOR STORE] Migration warning:', e.message);
|
|
});
|
|
}
|
|
console.log('[VENDOR STORE] ✅ Column migration check complete');
|
|
}
|
|
|
|
console.log('[VENDOR STORE] ✅ Table ready:', tbl(schema));
|
|
}
|
|
|
|
// ── Row → JS object ───────────────────────────────────────────────────────────
|
|
function rowToVendor(row) {
|
|
if (!row) return null;
|
|
let bankAccounts = [];
|
|
let attachments = {};
|
|
try { bankAccounts = JSON.parse(row.BANK_ACCOUNTS || '[]'); } catch (_) {}
|
|
try { attachments = JSON.parse(row.ATTACHMENTS || '{}'); } catch (_) {}
|
|
|
|
return {
|
|
id: row.ID,
|
|
status: row.STATUS,
|
|
vendorType: row.VENDOR_TYPE || 'SUPPLIER',
|
|
cardName: row.CARD_NAME || '',
|
|
foreignName: row.FOREIGN_NAME || '',
|
|
typeOfBusiness: row.TYPE_OF_BUSINESS || '',
|
|
industry: row.INDUSTRY || '',
|
|
products: row.PRODUCTS || '',
|
|
paymentTerms: row.PAYMENT_TERMS || '30 Days',
|
|
contactFirst: row.CONTACT_FIRST || '',
|
|
contactLast: row.CONTACT_LAST || '',
|
|
contactTitle: row.CONTACT_TITLE || '',
|
|
mobile: row.MOBILE || '',
|
|
altContact: row.ALT_CONTACT || '',
|
|
email: row.EMAIL || '',
|
|
billStreet: row.BILL_STREET || '',
|
|
billBlock: row.BILL_BLOCK || '',
|
|
billCity: row.BILL_CITY || '',
|
|
billZip: row.BILL_ZIP || '',
|
|
billState: row.BILL_STATE || '',
|
|
billCountry: row.BILL_COUNTRY || 'India',
|
|
gstin: row.GSTIN || '',
|
|
pan: row.PAN || '',
|
|
tan: row.TAN || '',
|
|
currency: row.CURRENCY || 'Indian Rupee',
|
|
hasTds: row.HAS_TDS === 'Y',
|
|
tdsCategory: row.TDS_CATEGORY || '',
|
|
tdsRate: Number(row.TDS_RATE) || 0,
|
|
tdsLdcNo: row.TDS_LDC_NO || '',
|
|
hasMsme: row.HAS_MSME === 'Y',
|
|
msmeNo: row.MSME_NO || '',
|
|
msmeType: row.MSME_TYPE || '',
|
|
msmeBType: row.MSME_BTYPE || '',
|
|
fssaiNo: row.FSSAI_NO || '',
|
|
remarks: row.REMARKS || '',
|
|
bankAccounts,
|
|
attachments,
|
|
allBillAddresses: safeJson(row.ALL_BILL_ADDRS, []),
|
|
allShipAddresses: safeJson(row.ALL_SHIP_ADDRS, []),
|
|
userId: row.USER_ID || '',
|
|
userType: row.USER_TYPE || '',
|
|
userDept: row.USER_DEPT || '',
|
|
submittedAt: row.SUBMITTED_AT ? new Date(row.SUBMITTED_AT).toISOString() : null,
|
|
mgrCardCodePrefix: row.MGR_CARD_CODE_PREFIX || 'VENDA',
|
|
mgrGroupCode: row.MGR_GROUP_CODE,
|
|
mgrGroup: row.MGR_GROUP || '',
|
|
mgrPayTermsCode: row.MGR_PAY_TERMS_CODE,
|
|
mgrPayTerms: row.MGR_PAY_TERMS || '',
|
|
mgrPurchaseAccount: row.MGR_PURCHASE_ACCOUNT || '2101001',
|
|
mgrPurchaseAcctName: row.MGR_PURCHASE_ACCT_NAME|| '',
|
|
mgrCurrency: row.MGR_CURRENCY || 'Indian Rupee',
|
|
mgrLanguage: row.MGR_LANGUAGE || 'English (UK)',
|
|
mgrCreditLimit: Number(row.MGR_CREDIT_LIMIT) || 0,
|
|
mgrNotes: row.MGR_NOTES || '',
|
|
mgrBranch: row.MGR_BRANCH || '',
|
|
mgrArea: row.MGR_AREA || '',
|
|
mgrTerritory: row.MGR_TERRITORY || '',
|
|
mgrSalesPersonCode: row.MGR_SALES_PERSON_CODE || null,
|
|
mgrSalesEmployee: row.MGR_SALES_EMPLOYEE || '',
|
|
mgrMainGroup: row.MGR_MAIN_GROUP || '',
|
|
mgrChain: row.MGR_CHAIN || '',
|
|
verifiedBy: row.VERIFIED_BY || null,
|
|
verifiedAt: row.VERIFIED_AT ? new Date(row.VERIFIED_AT).toISOString() : null,
|
|
approvedBy: row.APPROVED_BY || null,
|
|
approvedAt: row.APPROVED_AT ? new Date(row.APPROVED_AT).toISOString() : null,
|
|
rejectedBy: row.REJECTED_BY || null,
|
|
rejectedAt: row.REJECTED_AT ? new Date(row.REJECTED_AT).toISOString() : null,
|
|
sapCardCode: row.SAP_CARD_CODE || null,
|
|
sapAttachmentEntry: row.SAP_ATTACHMENT_ENTRY || null,
|
|
company: row.COMPANY || '',
|
|
};
|
|
}
|
|
|
|
function safeJson(v, fb) {
|
|
if (!v) return fb;
|
|
try { return JSON.parse(v); } catch (_) { return fb; }
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
function toTs(isoStr) {
|
|
if (!isoStr) return null;
|
|
return isoStr.replace('T', ' ').replace('Z', '').substring(0, 23);
|
|
}
|
|
|
|
function s(v, max) {
|
|
if (v === null || v === undefined) return null;
|
|
const str = String(v);
|
|
return max ? str.substring(0, max) : str;
|
|
}
|
|
|
|
// ── INSERT ────────────────────────────────────────────────────────────────────
|
|
async function insertVendor(data, _schema) {
|
|
const now = toTs(new Date().toISOString());
|
|
|
|
// INSERT and SELECT SCOPE_IDENTITY() must be one batch — as two separate
|
|
// exec() calls, a pooled connection can route the second one to a
|
|
// DIFFERENT physical connection than the one that just inserted, where
|
|
// SCOPE_IDENTITY() correctly returns NULL (see workOrderStore.js's
|
|
// insertWorkOrder() for the full write-up of this bug class).
|
|
const idRows = await exec(`
|
|
INSERT INTO ${tbl()} (
|
|
[VENDOR_TYPE],[CARD_NAME],[FOREIGN_NAME],[TYPE_OF_BUSINESS],[INDUSTRY],
|
|
[PRODUCTS],[PAYMENT_TERMS],
|
|
[CONTACT_FIRST],[CONTACT_LAST],[CONTACT_TITLE],
|
|
[MOBILE],[ALT_CONTACT],[EMAIL],
|
|
[BILL_STREET],[BILL_BLOCK],[BILL_CITY],[BILL_ZIP],[BILL_STATE],[BILL_COUNTRY],
|
|
[GSTIN],[PAN],[TAN],[CURRENCY],
|
|
[HAS_TDS],[TDS_CATEGORY],[TDS_RATE],[TDS_LDC_NO],
|
|
[HAS_MSME],[MSME_NO],[MSME_TYPE],[MSME_BTYPE],[FSSAI_NO],
|
|
[REMARKS],[BANK_ACCOUNTS],[ATTACHMENTS],
|
|
[ALL_BILL_ADDRS],[ALL_SHIP_ADDRS],
|
|
[USER_ID],[USER_TYPE],[USER_DEPT],
|
|
[SUBMITTED_AT],
|
|
[MGR_CARD_CODE_PREFIX],[MGR_PURCHASE_ACCOUNT],[MGR_CURRENCY],[MGR_LANGUAGE],
|
|
[COMPANY]
|
|
) VALUES (
|
|
?,?,?,?,?,
|
|
?,?,
|
|
?,?,?,
|
|
?,?,?,
|
|
?,?,?,?,?,?,
|
|
?,?,?,?,
|
|
?,?,?,?,
|
|
?,?,?,?,?,
|
|
?,?,?,
|
|
?,?,
|
|
?,?,?,
|
|
?,
|
|
?,?,?,?,
|
|
?
|
|
);
|
|
SELECT SCOPE_IDENTITY() AS ID;
|
|
`, [
|
|
s(data.vendorType || 'SUPPLIER', 20),
|
|
s(data.cardName, 100),
|
|
s(data.foreignName || '', 100),
|
|
s(data.typeOfBusiness || 'Company', 50),
|
|
s(data.industry || '', 100),
|
|
s(data.products || '', 500),
|
|
s(data.paymentTerms || '30 Days', 50),
|
|
s(data.contactFirst, 60),
|
|
s(data.contactLast, 60),
|
|
s(data.contactTitle || '', 60),
|
|
s(data.mobile || '', 20),
|
|
s(data.altContact || '', 20),
|
|
s(data.email, 150),
|
|
s(data.billStreet || '', 200),
|
|
s(data.billBlock || '', 100),
|
|
s(data.billCity || '', 100),
|
|
s(data.billZip || '', 20),
|
|
s(data.billState || '', 100),
|
|
s(data.billCountry || 'India', 60),
|
|
s(data.gstin || '', 15),
|
|
s(data.pan || '', 10),
|
|
s(data.tan || '', 10),
|
|
s(data.currency || 'Indian Rupee', 50),
|
|
data.hasTds ? 'Y' : 'N',
|
|
s(data.tdsCategory || '', 100),
|
|
parseFloat(data.tdsRate) || 0,
|
|
s(data.tdsLdcNo || '', 50),
|
|
data.hasMsme ? 'Y' : 'N',
|
|
s(data.msmeNo || '', 30),
|
|
s(data.msmeType || '', 20),
|
|
s(data.msmeBType || '', 30),
|
|
s(data.fssaiNo || '', 20),
|
|
data.remarks || '',
|
|
JSON.stringify(data.bankAccounts || []),
|
|
JSON.stringify(data.attachments || {}),
|
|
JSON.stringify(data.allBillAddresses || []),
|
|
JSON.stringify(data.allShipAddresses || []),
|
|
s(data.userId || '', 50),
|
|
s(data.userType || '', 50),
|
|
s(data.userDept || '', 100),
|
|
now,
|
|
s(data.mgrCardCodePrefix || 'VENDA', 20),
|
|
s(data.mgrPurchaseAccount || '2101001', 20),
|
|
s(data.mgrCurrency || 'Indian Rupee', 50),
|
|
s(data.mgrLanguage || 'English (UK)', 50),
|
|
s(data.company || '', 50),
|
|
]);
|
|
const id = idRows[0].ID;
|
|
console.log(`[VENDOR STORE] ✅ Inserted ID=${id} — ${data.cardName}`);
|
|
return { id };
|
|
}
|
|
|
|
// ── FIND BY ID ────────────────────────────────────────────────────────────────
|
|
async function findById(id, companyDB) {
|
|
const rows = await exec(
|
|
`SELECT * FROM ${tbl()} WHERE [ID] = ?`,
|
|
[parseInt(id)]
|
|
);
|
|
return rowToVendor(rows[0]);
|
|
}
|
|
|
|
// ── LIST BY STATUS ────────────────────────────────────────────────────────────
|
|
async function listByStatus(status, companyDB) {
|
|
let rows;
|
|
if (status === 'ALL') {
|
|
rows = await exec(`SELECT * FROM ${tbl()} ORDER BY [SUBMITTED_AT] DESC`);
|
|
} else {
|
|
rows = await exec(
|
|
`SELECT * FROM ${tbl()} WHERE [STATUS] = ? ORDER BY [SUBMITTED_AT] DESC`,
|
|
[status.toUpperCase()]
|
|
);
|
|
}
|
|
let result = rows.map(rowToVendor);
|
|
if (companyDB) result = result.filter(r => r.company === companyDB);
|
|
return result;
|
|
}
|
|
|
|
// ── UPDATE ────────────────────────────────────────────────────────────────────
|
|
async function updateVendor(id, patch, _schema) {
|
|
if (!patch || Object.keys(patch).length === 0) return;
|
|
|
|
const COL_MAP = {
|
|
status: 'STATUS',
|
|
vendorType: 'VENDOR_TYPE',
|
|
cardName: 'CARD_NAME',
|
|
foreignName: 'FOREIGN_NAME',
|
|
typeOfBusiness: 'TYPE_OF_BUSINESS',
|
|
industry: 'INDUSTRY',
|
|
products: 'PRODUCTS',
|
|
paymentTerms: 'PAYMENT_TERMS',
|
|
contactFirst: 'CONTACT_FIRST',
|
|
contactLast: 'CONTACT_LAST',
|
|
contactTitle: 'CONTACT_TITLE',
|
|
mobile: 'MOBILE',
|
|
altContact: 'ALT_CONTACT',
|
|
email: 'EMAIL',
|
|
billStreet: 'BILL_STREET',
|
|
billBlock: 'BILL_BLOCK',
|
|
billCity: 'BILL_CITY',
|
|
billZip: 'BILL_ZIP',
|
|
billState: 'BILL_STATE',
|
|
billCountry: 'BILL_COUNTRY',
|
|
gstin: 'GSTIN',
|
|
pan: 'PAN',
|
|
tan: 'TAN',
|
|
currency: 'CURRENCY',
|
|
hasTds: 'HAS_TDS',
|
|
tdsCategory: 'TDS_CATEGORY',
|
|
tdsRate: 'TDS_RATE',
|
|
tdsLdcNo: 'TDS_LDC_NO',
|
|
hasMsme: 'HAS_MSME',
|
|
msmeNo: 'MSME_NO',
|
|
msmeType: 'MSME_TYPE',
|
|
msmeBType: 'MSME_BTYPE',
|
|
fssaiNo: 'FSSAI_NO',
|
|
remarks: 'REMARKS',
|
|
bankAccounts: 'BANK_ACCOUNTS',
|
|
attachments: 'ATTACHMENTS',
|
|
allBillAddresses: 'ALL_BILL_ADDRS',
|
|
allShipAddresses: 'ALL_SHIP_ADDRS',
|
|
verifiedBy: 'VERIFIED_BY',
|
|
verifiedAt: 'VERIFIED_AT',
|
|
approvedBy: 'APPROVED_BY',
|
|
approvedAt: 'APPROVED_AT',
|
|
rejectedBy: 'REJECTED_BY',
|
|
rejectedAt: 'REJECTED_AT',
|
|
sapCardCode: 'SAP_CARD_CODE',
|
|
sapAttachmentEntry: 'SAP_ATTACHMENT_ENTRY',
|
|
mgrCardCodePrefix: 'MGR_CARD_CODE_PREFIX',
|
|
mgrGroupCode: 'MGR_GROUP_CODE',
|
|
mgrGroup: 'MGR_GROUP',
|
|
mgrPayTermsCode: 'MGR_PAY_TERMS_CODE',
|
|
mgrPayTerms: 'MGR_PAY_TERMS',
|
|
mgrPurchaseAccount: 'MGR_PURCHASE_ACCOUNT',
|
|
mgrPurchaseAcctName: 'MGR_PURCHASE_ACCT_NAME',
|
|
mgrCurrency: 'MGR_CURRENCY',
|
|
mgrLanguage: 'MGR_LANGUAGE',
|
|
mgrCreditLimit: 'MGR_CREDIT_LIMIT',
|
|
mgrNotes: 'MGR_NOTES',
|
|
mgrBranch: 'MGR_BRANCH',
|
|
mgrArea: 'MGR_AREA',
|
|
mgrTerritory: 'MGR_TERRITORY',
|
|
mgrSalesPersonCode: 'MGR_SALES_PERSON_CODE',
|
|
mgrSalesEmployee: 'MGR_SALES_EMPLOYEE',
|
|
mgrMainGroup: 'MGR_MAIN_GROUP',
|
|
mgrChain: 'MGR_CHAIN',
|
|
company: 'COMPANY',
|
|
};
|
|
|
|
const BOOL_COLS = new Set(['HAS_TDS', 'HAS_MSME']);
|
|
const JSON_COLS = new Set(['BANK_ACCOUNTS', 'ATTACHMENTS', 'ALL_BILL_ADDRS', 'ALL_SHIP_ADDRS']);
|
|
const NUMBER_COLS = new Set(['MGR_GROUP_CODE', 'MGR_PAY_TERMS_CODE', 'MGR_CREDIT_LIMIT', 'TDS_RATE', 'SAP_ATTACHMENT_ENTRY', 'MGR_SALES_PERSON_CODE']);
|
|
const TS_COLS = new Set(['VERIFIED_AT', 'APPROVED_AT', 'REJECTED_AT']);
|
|
|
|
const setClauses = [];
|
|
const vals = [];
|
|
|
|
Object.entries(patch).forEach(([k, v]) => {
|
|
const col = COL_MAP[k];
|
|
if (!col) return;
|
|
setClauses.push(`[${col}] = ?`);
|
|
if (BOOL_COLS.has(col)) {
|
|
vals.push(v ? 'Y' : 'N');
|
|
} else if (JSON_COLS.has(col)) {
|
|
vals.push(JSON.stringify(v || (col === 'ATTACHMENTS' ? {} : [])));
|
|
} else if (NUMBER_COLS.has(col)) {
|
|
vals.push(v === null || v === undefined ? null : Number(v));
|
|
} else if (TS_COLS.has(col)) {
|
|
vals.push(v ? toTs(v) : null);
|
|
} else {
|
|
vals.push(v === null || v === undefined ? null : String(v));
|
|
}
|
|
});
|
|
|
|
if (!setClauses.length) return;
|
|
|
|
vals.push(parseInt(id));
|
|
await exec(
|
|
`UPDATE ${tbl()} SET ${setClauses.join(', ')} WHERE [ID] = ?`,
|
|
vals
|
|
);
|
|
console.log(`[VENDOR STORE] ✅ Updated ID=${id} (${setClauses.length} fields)`);
|
|
}
|
|
|
|
module.exports = { bootstrap, insertVendor, findById, listByStatus, updateVendor }; |