Skip to content

Commit e4c4659

Browse files
authored
Merge branch 'main' into main
2 parents dd47616 + b08ed17 commit e4c4659

File tree

4 files changed

+59
-60
lines changed

4 files changed

+59
-60
lines changed

lib/business/business-base.mjs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,13 @@ class BusinessBase {
315315
return data;
316316
}
317317

318-
async save({ id, relations, relationsObject, ...values }) {
318+
async save(options) {
319+
let { id, relations, relationsObject, ...values } = options;
319320
if (this.beforeSave) {
320-
const result = await this.beforeSave({ id, ...values });
321-
values = { ...result };
321+
const result = await this.beforeSave(options);
322+
if (typeof result === 'object') {
323+
values = result;
324+
}
322325
}
323326
const { relations: definedRelations = [], isStandard = true, readOnlyColumns = [], user, clientBased, updateKeyField, multiSelectColumns = {} } = this;
324327
let { keyField } = this;
@@ -391,6 +394,10 @@ class BusinessBase {
391394

392395
const result = await sql.insertUpdate({ tableName, keyField, id, json: requestValues, update: isUpdate, logger: this.logger });
393396

397+
if (this.afterSave) {
398+
await this.afterSave(options);
399+
}
400+
394401
if (result.success) {
395402
if (!isUpdate) {
396403
id = result.data[0].Id;
@@ -528,6 +535,10 @@ class BusinessBase {
528535
}
529536

530537
async delete({ id, values = {} }) {
538+
// Invoke optional beforeDelete hook for custom validation or pre-deletion logic.
539+
if (this.beforeDelete) {
540+
await this.beforeDelete({ id });
541+
}
531542
if (this.softDelete === false) {
532543
return await this.hardDelete({ id });
533544
}

lib/middleware/response-transformer.mjs

Lines changed: 40 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ const toHtmlTable = (rows, columns, summaryRow, exportColumns, isCustomExport, t
4040
const keys = Object.keys(isCustomExport ? exportColumns : rows[0]);
4141
columns = [];
4242
for (const dataKey of keys) {
43-
columns.push({ dataKey, title: isCustomExport ? exportColumns[dataKey]?.name : dataKey });
43+
const { name: title = dataKey, ...others } = exportColumns[dataKey] || {};
44+
columns.push({ dataKey, title, ...others });
4445
}
4546
}
4647

@@ -171,41 +172,45 @@ const updateKeys = function ({ data, keyMapping, columns, isForXML = false, user
171172
const updatedRecord = {};
172173

173174
for (const ele of Object.keys(keyMapping)) {
174-
const keyName = isForXML ? ele : keyMapping[ele];
175-
const { valueType, keepUTC = false } = columns[ele];
176-
177-
let value = record[ele];
178-
179-
const isParsable = columns[ele]?.isParsable !== false ? true : columns[ele]?.isParsable;
180-
181-
if (util.dateTimeFields.includes(valueType)) {
182-
if (value !== null && value !== undefined) {
183-
if (userTimezoneOffset && !keepUTC) {
184-
value = dayjs.utc(value).add(Number(userTimezoneOffset), 'minute')
175+
if (columns[ele]) {
176+
const keyName = isForXML ? ele : keyMapping[ele];
177+
const { valueType, keepUTC = false } = columns[ele];
178+
179+
let value = record[ele];
180+
181+
const isParsable = columns[ele]?.isParsable !== false ? true : columns[ele]?.isParsable;
182+
183+
if (util.dateTimeFields.includes(valueType)) {
184+
if (value !== null && value !== undefined) {
185+
if (userTimezoneOffset && !keepUTC) {
186+
value = dayjs.utc(value).add(Number(userTimezoneOffset), 'minute')
187+
}
188+
value = formatDateTime({ value, format: valueType === 'date' ? userDateFormat : userDateTimeFormat });
189+
} else {
190+
value = '';
185191
}
186-
value = formatDateTime({ value, format: valueType === 'date' ? userDateFormat : userDateTimeFormat });
187-
} else {
188-
value = '';
192+
} else if (valueType === 'boolean') {
193+
value = value === true || value === 1 ? 'Yes' : 'No';
194+
} else if (valueType === 'number' && isParsable) {
195+
value = value !== null ? parseInt(value) : '';
196+
} else if (valueType === 'percentage') {
197+
value = util.percentageFormatter(value);
189198
}
190-
} else if (valueType === 'boolean') {
191-
value = value === true || value === 1 ? 'Yes' : 'No';
192-
} else if (valueType === 'number' && isParsable) {
193-
value = value !== null ? parseInt(value) : '';
194-
}
195199

196-
for (const field in lookupFields) {
197-
const lookupKeyName = lookupFields[field].keyName;
198-
const key = isForXML ? keyName : ele;
199-
if (lookupKeyName.includes(key)) {
200-
const lookupValue = lookupFields[field].lookupKey;
201-
const indexValue = lookups[lookupValue]?.findIndex(e => e.value === value);
202-
if (indexValue > -1) {
203-
value = lookups[lookupValue][indexValue].label;
200+
for (const field in lookupFields) {
201+
const lookupKeyName = lookupFields[field].keyName;
202+
const key = isForXML ? keyName : ele;
203+
if (lookupKeyName.includes(key)) {
204+
const lookupValue = lookupFields[field].lookupKey;
205+
const indexValue = lookups[lookupValue]?.findIndex(e => e.value === value);
206+
if (indexValue > -1) {
207+
value = lookups[lookupValue][indexValue].label;
208+
}
204209
}
205210
}
206-
}
207211

208-
updatedRecord[keyName] = [undefined, null].includes(value) ? '' : value;
212+
updatedRecord[keyName] = [undefined, null].includes(value) ? '' : value;
213+
}
209214
}
210215

211216
return updatedRecord;
@@ -281,7 +286,9 @@ const sanitizeData = ({ data, columns = {}, responseType }) => {
281286
return data.map(record =>
282287
fieldsToKeep.reduce((acc, field) => {
283288
let name = columns[field]?.name || field;
284-
const value = record[field];
289+
// Fallback logic: check both the original field and the renamed column name.
290+
// This handles cases where columns have been renamed and the original field name might not exist in the record.
291+
const value = record[field] ?? record[name];
285292
if (responseType === mimeTypes.xml) {
286293
// Ensure name is a string before calling replace
287294
name = (typeof name === 'string' ? name : String(name || field)).replace(/[^a-zA-Z0-9._-]/g, '');
@@ -302,7 +309,7 @@ const responseTransformer = async function (req, res, next) {
302309
const exportColumns = others?.exportColumns, userDateFormat = others?.userDateFormat, isElastic = others?.isElastic, userTimezoneOffset = others?.userTimezoneOffset, lookups = others?.lookups, lookupFields = others?.lookupFields, addExecutionTimeLogger = others?.addExecutionTimeLogger;
303310
const dateTimeFormat = userDateFormat + util.dateTimeExportFormat;
304311
const isMultiSheetExport = others?.isMultiSheetExport || false;
305-
312+
306313
// Safely handle req.path to prevent TypeError: r.replace is not a function
307314
const safePath = (req.path && typeof req.path === 'string') ? req.path.substr(1) : 'export';
308315
fileName = `${!fileName ? safePath.replace(util.fileNameRegex, '-') : fileName}-${dayjs().format(enums.fullDateFormat)}`;
@@ -374,26 +381,7 @@ const responseTransformer = async function (req, res, next) {
374381
logger.info(`Execution time taken for updateKeys: ${endTimeUpdateKeys - updateKeysStartTime} ms`);
375382
}
376383
}
377-
}
378-
// Iterates over each record to format Date fields using the appropriate date or datetime format.
379-
// Also converts boolean values to strings based on the exportColumns configuration.
380-
if (Array.isArray(data)) {
381-
data = data.map(record => {
382-
const formattedRecord = { ...record }; // create a shallow copy of the record
383-
for (const [key, value] of Object.entries(record)) {
384-
if (exportColumns && !exportColumns[key]) {
385-
continue;
386-
}
387-
if (value && value instanceof Date) {
388-
formattedRecord[key] = formatDateTime({ value, format: dateTimeFormat || enums.dateTimeExportFormat }); // format date or datetime
389-
continue;
390-
}
391-
if (value !== null && value !== undefined && typeof value === 'boolean') {
392-
formattedRecord[key] = value.toString(); // convert boolean to string
393-
}
394-
}
395-
return formattedRecord;
396-
});
384+
data = sheets[0].rows;
397385
}
398386
switch (responseType) {
399387
case mimeTypes.json:
@@ -407,7 +395,6 @@ const responseTransformer = async function (req, res, next) {
407395
}
408396
return Object.keys(others).length === 1 ? res.json(data) : res.json(jsonResponse);
409397
case mimeTypes.xlsx:
410-
data = data.data || data;
411398
res.set('Content-Type', responseType);
412399
res.set('Content-Disposition', `attachment; filename="${fileName}.xlsx"`);
413400
return await toExcel({ sheets, stream: res, exportColumns: true, userDateFormat, userDateTimeFormat: dateTimeFormat, userTimezoneOffset, lookups, lookupFields, addExecutionTimeLogger });

lib/util.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,5 +389,6 @@ export default {
389389
nvarchar: 4000,
390390
decimal_precision: 38,
391391
decimal_scale: 17
392-
}
392+
},
393+
percentageFormatter: (v) => (v !== null && v !== undefined) ? `${parseFloat(v).toFixed(1)}%` : '',
393394
};

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@durlabh/dframework",
3-
"version": "1.0.43",
3+
"version": "1.0.47",
44
"main": "index.js",
55
"license": "MIT",
66
"type": "module",
@@ -58,7 +58,7 @@
5858
"tough-cookie": "^6.0.0"
5959
},
6060
"devDependencies": {
61-
"@azure/identity": "^4.12.0",
61+
"@azure/identity": "^4.12.0",
6262
"@azure/msal-node": "^3.8.0",
6363
"@azure/storage-blob": "^12.28.0",
6464
"dayjs": "^1.11.18",
@@ -117,4 +117,4 @@
117117
"optional": true
118118
}
119119
}
120-
}
120+
}

0 commit comments

Comments
 (0)