Refactor query handling in various routes to standardize sort property and order parameters
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good

This commit updates multiple route handlers across the management and inventory modules to replace the `sort` and `order` parameters with `sortProperty` and `sortOrder`. This change enhances consistency in query handling and improves the clarity of the code. Additionally, the `getFilter` function is modified to accommodate these new parameters, ensuring that filtering logic remains intact. Corresponding tests have been updated to verify the correct functionality of the modified routes.
This commit is contained in:
Tom Butcher 2026-08-22 02:36:05 +01:00
parent 4ee5eb7d0a
commit 30d1469204
55 changed files with 703 additions and 253 deletions

View File

@ -2,6 +2,7 @@ import express from 'express';
import { isAuthenticated } from '../../keycloak.js';
import { checkPermissions, hasPermission } from '../../database/permissions.js';
import { getFilter, convertPropertiesString, getSort } from '../../utils.js';
import { invoiceModel } from '../../database/schemas/finance/invoice.schema.js';
const router = express.Router();
@ -11,6 +12,10 @@ const listAllowedFilters = [
'state',
'vendor._id',
'client._id',
'from',
'from._id',
'to',
'to._id',
'order',
'order._id',
'orderType',
@ -22,8 +27,13 @@ const listAllowedSorters = ['createdAt', 'state', 'updatedAt', 'invoiceDate', 'd
const propertiesAllowedFilters = [
'vendor',
'client',
'from',
'from._id',
'to',
'to._id',
'orderType',
'order',
'order._id',
'state.type',
'value',
'vendor._id',
@ -49,8 +59,8 @@ import {
// list of invoices
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters, true, invoiceModel);
listInvoicesRouteHandler(
req,
res,
@ -59,8 +69,8 @@ router.get('/', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});
@ -98,16 +108,16 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters, true, invoiceModel);
getInvoiceNeighborsRouteHandler(
req,
res,
property,
filter,
search,
getSort(sort, listAllowedSorters),
order,
getSort(sortProperty, listAllowedSorters),
sortOrder,
id
);
});

View File

@ -50,9 +50,9 @@ import {
// list of payments
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listPaymentsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listPaymentsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('payment', 'list'), isAuthenticated, async (req, res) => {
@ -90,9 +90,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getPaymentNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getPaymentNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('payment', 'info'), async (req, res) => {

View File

@ -35,9 +35,9 @@ import {
// list of tax records
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listTaxRecordsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listTaxRecordsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('taxRecord', 'list'), isAuthenticated, async (req, res) => {
@ -75,9 +75,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getTaxRecordNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getTaxRecordNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('taxRecord', 'info'), async (req, res) => {

View File

@ -45,9 +45,9 @@ import {
// list of filament stocks
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listFilamentStocksRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listFilamentStocksRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('filamentStock', 'list'), isAuthenticated, async (req, res) => {
@ -85,9 +85,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getFilamentStockNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getFilamentStockNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('filamentStock', 'info'), async (req, res) => {

View File

@ -49,7 +49,7 @@ import {
// list of order items
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listOrderItemsRouteHandler(
req,
@ -59,8 +59,8 @@ router.get('/', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});
@ -98,7 +98,7 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getOrderItemNeighborsRouteHandler(
req,
@ -106,8 +106,8 @@ router.get('/neighbors', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order,
getSort(sortProperty, listAllowedSorters),
sortOrder,
id
);
});

View File

@ -37,9 +37,9 @@ import {
// list of part stocks
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listPartStocksRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listPartStocksRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('partStock', 'list'), isAuthenticated, async (req, res) => {
@ -77,9 +77,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getPartStockNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getPartStockNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('partStock', 'info'), async (req, res) => {

View File

@ -36,9 +36,9 @@ import {
} from '../../services/inventory/productstocks.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listProductStocksRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listProductStocksRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('productStock', 'list'), isAuthenticated, async (req, res) => {
@ -74,9 +74,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getProductStockNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getProductStockNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('productStock', 'info'), async (req, res) => {

View File

@ -63,9 +63,9 @@ import {
// list of purchase orders
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listPurchaseOrdersRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listPurchaseOrdersRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('purchaseOrder', 'list'), isAuthenticated, async (req, res) => {
@ -102,9 +102,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getPurchaseOrderNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getPurchaseOrderNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('purchaseOrder', 'info'), async (req, res) => {

View File

@ -54,7 +54,7 @@ import {
// list of shipments
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listShipmentsRouteHandler(
req,
@ -64,8 +64,8 @@ router.get('/', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});
@ -107,7 +107,7 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getShipmentNeighborsRouteHandler(
req,
@ -115,8 +115,8 @@ router.get('/neighbors', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order,
getSort(sortProperty, listAllowedSorters),
sortOrder,
id
);
});

View File

@ -66,9 +66,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getStockAuditNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getStockAuditNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
// Get specific stock audit

View File

@ -26,9 +26,9 @@ import {
// list of stock events
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, sort, order } = req.query;
const { page, limit, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listStockEventsRouteHandler(req, res, page, limit, filter, getSort(sort, listAllowedSorters), order);
listStockEventsRouteHandler(req, res, page, limit, filter, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('stockEvent', 'list'), isAuthenticated, async (req, res) => {
@ -66,9 +66,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getStockEventNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getStockEventNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('stockEvent', 'info'), async (req, res) => {

View File

@ -25,9 +25,9 @@ import {
} from '../../services/inventory/stocklocations.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listStockLocationsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listStockLocationsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('stockLocation', 'list'), isAuthenticated, async (req, res) => {
@ -63,9 +63,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getStockLocationNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getStockLocationNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('stockLocation', 'info'), async (req, res) => {

View File

@ -34,9 +34,9 @@ import {
} from '../../services/inventory/stocktransfers.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listStockTransfersRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listStockTransfersRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('stockTransfer', 'list'), isAuthenticated, async (req, res) => {
@ -72,9 +72,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getStockTransferNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getStockTransferNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('stockTransfer', 'info'), async (req, res) => {

View File

@ -34,9 +34,9 @@ import {
} from '../../services/management/apppasswords.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listAppPasswordsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listAppPasswordsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('appPassword', 'list'), isAuthenticated, async (req, res) => {
@ -73,9 +73,9 @@ router.post('/:id/regenerateSecret', isAuthenticated, checkPermissions('appPassw
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getAppPasswordNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getAppPasswordNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -23,7 +23,7 @@ const listAllowedFilters = [
const listAllowedSorters = ['createdAt', 'updatedAt'];
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, sort, order } = req.query;
const { page, limit, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listAuditLogsRouteHandler(
req,
@ -31,8 +31,8 @@ router.get('/', isAuthenticated, async (req, res) => {
page,
limit,
filter,
getSort(sort, listAllowedSorters),
order
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});

View File

@ -35,9 +35,9 @@ import {
// list of couriers
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listCouriersRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listCouriersRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('courier', 'list'), isAuthenticated, async (req, res) => {
@ -75,9 +75,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getCourierNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getCourierNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -59,9 +59,9 @@ import {
// list of courier services
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listCourierServicesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listCourierServicesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('courierService', 'list'), isAuthenticated, async (req, res) => {
@ -99,9 +99,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getCourierServiceNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getCourierServiceNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -33,9 +33,9 @@ import {
// list of document jobs
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listDocumentJobsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listDocumentJobsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('documentJob', 'list'), isAuthenticated, async (req, res) => {
@ -73,9 +73,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getDocumentJobNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getDocumentJobNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -34,9 +34,9 @@ import {
// list of document printers
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listDocumentPrintersRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listDocumentPrintersRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('documentPrinter', 'list'), isAuthenticated, async (req, res) => {
@ -74,9 +74,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getDocumentPrinterNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getDocumentPrinterNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -49,9 +49,9 @@ import {
// list of document sizes
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listDocumentSizesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listDocumentSizesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('documentSize', 'list'), isAuthenticated, async (req, res) => {
@ -89,9 +89,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getDocumentSizeNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getDocumentSizeNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -49,9 +49,9 @@ import {
// list of document templates
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listDocumentTemplatesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listDocumentTemplatesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('documentTemplate', 'list'), isAuthenticated, async (req, res) => {
@ -93,9 +93,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getDocumentTemplateNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getDocumentTemplateNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.post('/:id/preview', isAuthenticated, checkPermissions('documentTemplate', 'design'), async (req, res) => {

View File

@ -44,7 +44,7 @@ import {
// list of filaments
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
var filter = {};
@ -57,7 +57,7 @@ router.get('/', isAuthenticated, async (req, res) => {
}
}
listFilamentsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listFilamentsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('filament', 'list'), isAuthenticated, async (req, res) => {
@ -95,9 +95,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getFilamentNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getFilamentNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -44,9 +44,9 @@ import {
} from '../../services/management/filamentskus.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listFilamentSkusRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listFilamentSkusRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('filamentSku', 'list'), isAuthenticated, async (req, res) => {
@ -81,9 +81,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getFilamentSkuNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getFilamentSkuNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -37,9 +37,9 @@ import {
// list of files
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listFilesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listFilesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('file', 'list'), isAuthenticated, async (req, res) => {
@ -81,9 +81,9 @@ router.delete('/:id/flush', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getFileNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getFileNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -33,9 +33,9 @@ import {
// list of hosts
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listHostsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listHostsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('host', 'list'), isAuthenticated, async (req, res) => {
@ -73,9 +73,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getHostNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getHostNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -24,7 +24,7 @@ import {
// list of materials
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
var filter = {};
@ -35,7 +35,7 @@ router.get('/', isAuthenticated, async (req, res) => {
}
}
listMaterialsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listMaterialsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('material', 'list'), isAuthenticated, async (req, res) => {
@ -73,9 +73,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getMaterialNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getMaterialNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -33,9 +33,9 @@ import {
// list of note types
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listNoteTypesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listNoteTypesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('noteType', 'list'), isAuthenticated, async (req, res) => {
@ -73,9 +73,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getNoteTypeNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getNoteTypeNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -34,7 +34,7 @@ import {
// list of parts
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listPartsRouteHandler(
req,
@ -44,8 +44,8 @@ router.get('/', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});
@ -84,7 +84,7 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getPartNeighborsRouteHandler(
req,
@ -92,8 +92,8 @@ router.get('/neighbors', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order,
getSort(sortProperty, listAllowedSorters),
sortOrder,
id
);
});

View File

@ -45,9 +45,9 @@ import {
} from '../../services/management/partskus.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listPartSkusRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listPartSkusRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('partSku', 'list'), isAuthenticated, async (req, res) => {
@ -83,9 +83,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getPartSkuNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getPartSkuNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -23,7 +23,7 @@ import {
} from '../../services/management/permissionsetting.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listPermissionSettingsRouteHandler(
req,
@ -33,8 +33,8 @@ router.get('/', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});
@ -71,7 +71,7 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getPermissionSettingsNeighborsRouteHandler(
req,
@ -79,8 +79,8 @@ router.get('/neighbors', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order,
getSort(sortProperty, listAllowedSorters),
sortOrder,
id
);
});

View File

@ -24,9 +24,9 @@ const listAllowedSorters = ['name', 'createdAt', 'updatedAt', '_id'];
const propertiesAllowedFilters = ['name'];
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listProductCategoriesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listProductCategoriesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('productCategory', 'list'), isAuthenticated, async (req, res) => {
@ -62,9 +62,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getProductCategoryNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getProductCategoryNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -45,9 +45,9 @@ import {
// list of products
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listProductsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listProductsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('product', 'list'), isAuthenticated, async (req, res) => {
@ -85,9 +85,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getProductNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getProductNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -45,9 +45,9 @@ import {
} from '../../services/management/productskus.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listProductSkusRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listProductSkusRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('productSku', 'list'), isAuthenticated, async (req, res) => {
@ -83,9 +83,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getProductSkuNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getProductSkuNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -43,9 +43,9 @@ import {
// list of tax rates
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listTaxRatesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listTaxRatesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('taxRate', 'list'), isAuthenticated, async (req, res) => {
@ -83,9 +83,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getTaxRateNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getTaxRateNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -23,7 +23,7 @@ import {
} from '../../services/management/usergroups.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listUserGroupsRouteHandler(
req,
@ -33,8 +33,8 @@ router.get('/', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});
@ -71,7 +71,7 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getUserGroupNeighborsRouteHandler(
req,
@ -79,8 +79,8 @@ router.get('/neighbors', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order,
getSort(sortProperty, listAllowedSorters),
sortOrder,
id
);
});

View File

@ -35,9 +35,9 @@ import {
// list of document templates
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listUsersRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listUsersRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('user', 'list'), isAuthenticated, async (req, res) => {
@ -71,9 +71,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getUserNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getUserNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -33,9 +33,9 @@ import {
// list of vendors
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listVendorsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listVendorsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('vendor', 'list'), isAuthenticated, async (req, res) => {
@ -73,9 +73,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getVendorNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getVendorNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -35,9 +35,9 @@ const propertiesAllowedFilters = ['parent'];
// list of notes
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listNotesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listNotesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', isAuthenticated, async (req, res) => {
@ -71,9 +71,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getNoteNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getNoteNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, async (req, res) => {

View File

@ -12,10 +12,10 @@ import { getFilter } from '../../utils.js';
const router = express.Router();
router.get('/', isAuthenticated, async (req, res) => {
const { page = 1, limit = 50, sort = 'createdAt', order = 'descend' } = req.query;
const { page = 1, limit = 50, sortProperty = 'createdAt', sortOrder = 'descend' } = req.query;
const allowedFilters = ['user'];
const filter = await getFilter(req.query, allowedFilters);
listNotificationsRouteHandler(req, res, page, limit, filter, sort, order);
listNotificationsRouteHandler(req, res, page, limit, filter, sortProperty, sortOrder);
});
router.put('/read-all', isAuthenticated, async (req, res) => {

View File

@ -12,10 +12,10 @@ import { getFilter } from '../../utils.js';
const router = express.Router();
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const allowedFilters = ['user', 'object', 'objectType'];
const filter = await getFilter(req.query, allowedFilters);
listUserNotifiersRouteHandler(req, res, page, limit, property, filter, search, sort, order);
listUserNotifiersRouteHandler(req, res, page, limit, property, filter, search, sortProperty, sortOrder);
});
router.post('/', isAuthenticated, async (req, res) => {

View File

@ -21,7 +21,7 @@ const listAllowedSorters = ['name', 'createdAt', 'updatedAt'];
const propertiesAllowedFilters = ['name'];
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listFilamentProfilesRouteHandler(
req,
@ -31,8 +31,8 @@ router.get('/', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order
getSort(sortProperty, listAllowedSorters),
sortOrder
);
});
@ -60,7 +60,7 @@ router.post('/', isAuthenticated, checkPermissions('filamentProfile', 'new'), as
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getFilamentProfileNeighborsRouteHandler(
req,
@ -68,8 +68,8 @@ router.get('/neighbors', isAuthenticated, async (req, res) => {
property,
filter,
search,
getSort(sort, listAllowedSorters),
order,
getSort(sortProperty, listAllowedSorters),
sortOrder,
id
);
});

View File

@ -34,9 +34,9 @@ import { convertPropertiesString, getFilter, getSort } from '../../utils.js';
// list of gcodeFiles
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listGCodeFilesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listGCodeFilesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('gcodeFile', 'list'), isAuthenticated, async (req, res) => {
@ -70,9 +70,9 @@ router.get('/stats', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getGCodeFileNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getGCodeFileNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('gcodeFile', 'info'), async (req, res) => {

View File

@ -43,9 +43,9 @@ import { convertPropertiesString, getFilter, getSort } from '../../utils.js';
// list of jobs
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listJobsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listJobsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('job', 'list'), isAuthenticated, async (req, res) => {
@ -82,9 +82,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getJobNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getJobNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('job', 'info'), async (req, res) => {

View File

@ -30,9 +30,9 @@ const listAllowedSorters = [
const propertiesAllowedFilters = ['name'];
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listPrinterProfilesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listPrinterProfilesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('printerProfile', 'list'), isAuthenticated, async (req, res) => {
@ -59,9 +59,9 @@ router.post('/', isAuthenticated, checkPermissions('printer', 'newPrinterProfile
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getPrinterProfileNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getPrinterProfileNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('printerProfile', 'info'), async (req, res) => {

View File

@ -34,9 +34,9 @@ const listAllowedSorters = ['name', 'state', 'connectedAt', 'createdAt', 'update
const propertiesAllowedFilters = ['tags'];
// list of printers
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listPrintersRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listPrintersRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('printer', 'list'), isAuthenticated, async (req, res) => {
@ -69,9 +69,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getPrinterNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getPrinterNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
// get printer stats

View File

@ -41,9 +41,9 @@ import { getFilter, convertPropertiesString, getSort } from '../../utils.js';
// list of sub jobs
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listSubJobsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listSubJobsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('subJob', 'list'), isAuthenticated, async (req, res) => {
@ -76,9 +76,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getSubJobNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getSubJobNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('subJob', 'info'), async (req, res) => {

View File

@ -34,9 +34,9 @@ import {
// list of clients
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listClientsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listClientsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('client', 'list'), isAuthenticated, async (req, res) => {
@ -74,9 +74,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getClientNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getClientNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('client', 'info'), async (req, res) => {

View File

@ -60,9 +60,9 @@ import {
} from '../../services/sales/listings.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listListingsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listListingsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('listing', 'list'), isAuthenticated, async (req, res) => {
@ -105,9 +105,9 @@ router.post('/:id/unpublish', isAuthenticated, checkPermissions('listing', 'unpu
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getListingNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getListingNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('listing', 'info'), async (req, res) => {

View File

@ -47,9 +47,9 @@ import {
} from '../../services/sales/listingvarients.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listListingVarientsRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listListingVarientsRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('listingVarient', 'list'), isAuthenticated, async (req, res) => {
@ -93,9 +93,9 @@ router.post('/:id/unpublish', isAuthenticated, checkPermissions('listingVarient'
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getListingVarientNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getListingVarientNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('listingVarient', 'info'), async (req, res) => {

View File

@ -49,9 +49,9 @@ import {
} from '../../services/sales/marketplaces.js';
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listMarketplacesRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listMarketplacesRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('marketplace', 'list'), isAuthenticated, async (req, res) => {
@ -124,9 +124,9 @@ router.post('/:id/hook', async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getMarketplaceNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getMarketplaceNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('marketplace', 'info'), async (req, res) => {

View File

@ -36,9 +36,9 @@ import {
// list of sales orders
router.get('/', isAuthenticated, async (req, res) => {
const { page, limit, property, search, sort, order } = req.query;
const { page, limit, property, search, sortProperty, sortOrder } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
listSalesOrdersRouteHandler(req, res, page, limit, property, filter, search, getSort(sort, listAllowedSorters), order);
listSalesOrdersRouteHandler(req, res, page, limit, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder);
});
router.get('/properties', checkPermissions('salesOrder', 'list'), isAuthenticated, async (req, res) => {
@ -75,9 +75,9 @@ router.get('/history', isAuthenticated, async (req, res) => {
});
router.get('/neighbors', isAuthenticated, async (req, res) => {
const { property, search, sort, order, id } = req.query;
const { property, search, sortProperty, sortOrder, id } = req.query;
const filter = await getFilter(req.query, listAllowedFilters);
getSalesOrderNeighborsRouteHandler(req, res, property, filter, search, getSort(sort, listAllowedSorters), order, id);
getSalesOrderNeighborsRouteHandler(req, res, property, filter, search, getSort(sortProperty, listAllowedSorters), sortOrder, id);
});
router.get('/:id', isAuthenticated, checkPermissions('salesOrder', 'info'), async (req, res) => {

View File

@ -51,8 +51,7 @@ describe('formatTemplateContent', () => {
});
it('should not add spaces inside parentheses around EJS output', () => {
const input =
'<Text><Bold><%= courierService.name %></Bold> ( <%= courier.name %> )</Text>';
const input = '<Text><Bold><%= courierService.name %></Bold> ( <%= courier.name %> )</Text>';
const result = formatTemplateContent(input);
expect(result.content).toContain('</Bold> (<%= courier.name %>)');

View File

@ -35,6 +35,10 @@ jest.unstable_mockModule('../../database/database.js', () => ({
listObjects: jest.fn(),
}));
jest.unstable_mockModule('../../utils.js', () => ({
getFilter: jest.fn(async (query) => query),
}));
jest.unstable_mockModule('../../database/schemas/management/documenttemplate.schema.js', () => ({
documentTemplateModel: { modelName: 'DocumentTemplate' },
}));
@ -91,11 +95,26 @@ jest.unstable_mockModule('../../config.js', () => ({
},
}));
const { TemplateManager, resolveDocumentPadding } = await import('../templatemanager.js');
const { getObject } = await import('../../database/database.js');
const { TemplateManager, resolveDocumentPadding, stripDocumentTemplateReference } = await import('../templatemanager.js');
const { getObject, listObjects } = await import('../../database/database.js');
const { getFilter } = await import('../../utils.js');
const ejs = (await import('ejs')).default;
const { generatePDF } = await import('../pdffactory.js');
describe('stripDocumentTemplateReference', () => {
it('strips a DTP: prefix', () => {
expect(stripDocumentTemplateReference('DTP:YPZY0P8XYKHZ')).toBe('YPZY0P8XYKHZ');
});
it('strips a dtp: prefix case-insensitively', () => {
expect(stripDocumentTemplateReference('dtp:YPZY0P8XYKHZ')).toBe('YPZY0P8XYKHZ');
});
it('returns a bare reference unchanged', () => {
expect(stripDocumentTemplateReference('YPZY0P8XYKHZ')).toBe('YPZY0P8XYKHZ');
});
});
describe('resolveDocumentPadding', () => {
const paddedSize = {
printPadding: false,
@ -172,6 +191,197 @@ describe('TemplateManager', () => {
expect(result).toEqual({ error: 'Document template not found.', code: 404 });
});
it('exposes fc.renderDocumentTemplate when rendering', async () => {
getObject.mockResolvedValue({
documentSize: { width: 100, height: 100, infiniteHeight: false },
global: false,
objectType: 'printer',
_reference: 'PARENTREF',
});
await templateManager.renderTemplate('temp-id', 'some content', { name: 'Test' });
expect(ejs.render).toHaveBeenCalledWith(
'some content',
expect.objectContaining({
name: 'Test',
fc: expect.objectContaining({
renderDocumentTemplate: expect.any(Function),
}),
}),
expect.anything()
);
});
it('looks up a nested template by reference without the DTP: prefix', async () => {
getObject.mockResolvedValue({
documentSize: { width: 100, height: 100, infiniteHeight: false },
global: false,
objectType: 'printer',
_reference: 'PARENTREF',
});
listObjects.mockResolvedValue([
{
_reference: 'YPZY0P8XYKHZ',
content: '<Text><%= name %></Text>',
objectType: 'printer',
global: false,
},
]);
let capturedFc;
ejs.render.mockImplementation(async (content, data) => {
if (data?.fc?.renderDocumentTemplate) {
capturedFc = data.fc;
}
return `rendered: ${content}`;
});
await templateManager.renderTemplate('temp-id', 'some content', { name: 'Parent' });
const nestedHtml = await capturedFc.renderDocumentTemplate('DTP:YPZY0P8XYKHZ');
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({
filter: { _reference: 'YPZY0P8XYKHZ' },
})
);
expect(ejs.render).toHaveBeenCalledWith(
'<Text><%= name %></Text>',
expect.objectContaining({ name: 'Parent' }),
expect.anything()
);
expect(nestedHtml).toBe('rendered: <Text><%= name %></Text>');
});
it('renders a nested template with a passed object instead of the parent object', async () => {
getObject.mockResolvedValue({
documentSize: { width: 100, height: 100, infiniteHeight: false },
global: false,
objectType: 'printer',
_reference: 'PARENTREF',
});
listObjects.mockResolvedValue([
{
_reference: 'YPZY0P8XYKHZ',
content: '<Text><%= name %></Text>',
objectType: 'printer',
global: false,
},
]);
let capturedFc;
ejs.render.mockImplementation(async (content, data) => {
if (data?.fc?.renderDocumentTemplate) {
capturedFc = data.fc;
}
return `rendered: ${content}`;
});
await templateManager.renderTemplate('temp-id', 'some content', { name: 'Parent' });
await capturedFc.renderDocumentTemplate('YPZY0P8XYKHZ', { name: 'Nested' });
expect(ejs.render).toHaveBeenCalledWith(
'<Text><%= name %></Text>',
expect.objectContaining({ name: 'Nested' }),
expect.anything()
);
});
it('uses the passed object as nested template data without fetching it', async () => {
getObject.mockResolvedValue({
documentSize: { width: 100, height: 100, infiniteHeight: false },
global: false,
objectType: 'printer',
_reference: 'PARENTREF',
});
listObjects.mockResolvedValue([
{
_reference: 'PKEFDZV5X1SU',
content: '<Text><%= totalAmount %></Text>',
objectType: 'invoice',
global: false,
},
]);
let capturedFc;
ejs.render.mockImplementation(async (content, data) => {
if (data?.fc?.renderDocumentTemplate) {
capturedFc = data.fc;
}
return `rendered: ${content}`;
});
await templateManager.renderTemplate('temp-id', 'some content', { name: 'Parent' });
getObject.mockClear();
const invoice = { _id: 'inv-1', totalAmount: 42, orderType: 'salesOrder' };
await capturedFc.renderDocumentTemplate('DTP:PKEFDZV5X1SU', invoice);
expect(getObject).not.toHaveBeenCalled();
expect(ejs.render).toHaveBeenCalledWith(
'<Text><%= totalAmount %></Text>',
expect.objectContaining({
_id: 'inv-1',
totalAmount: 42,
orderType: 'salesOrder',
}),
expect.objectContaining({ async: true })
);
});
it('wraps nested content in its parent template without page pagination chrome', async () => {
getObject.mockResolvedValue({
documentSize: { width: 100, height: 100, infiniteHeight: false },
global: false,
objectType: 'printer',
_reference: 'PARENTREF',
});
listObjects.mockResolvedValue([
{
_reference: 'PKEFDZV5X1SU',
content: '<Text>invoice</Text>',
objectType: 'invoice',
global: false,
parent: {
_reference: 'LETTERHEAD',
content: '<Container><%- content %></Container>',
},
},
]);
let capturedFc;
ejs.render.mockImplementation(async (content, data) => {
if (data?.fc?.renderDocumentTemplate) {
capturedFc = data.fc;
}
return `rendered: ${content}`;
});
await templateManager.renderTemplate('temp-id', 'some content', { name: 'Parent' });
ejs.render.mockClear();
const nestedHtml = await capturedFc.renderDocumentTemplate(
'DTP:PKEFDZV5X1SU',
{ totalAmount: 10 }
);
expect(ejs.render).toHaveBeenCalledWith(
'<Text>invoice</Text>',
expect.objectContaining({ totalAmount: 10 }),
expect.objectContaining({ async: true })
);
expect(ejs.render).toHaveBeenCalledWith(
'<Container><%- content %></Container>',
expect.objectContaining({ content: 'rendered: <Text>invoice</Text>' }),
expect.objectContaining({ async: true })
);
expect(nestedHtml).not.toContain('pagination');
expect(nestedHtml).not.toContain('previewDocument');
expect(nestedHtml).not.toContain('renderDocument');
});
});
describe('validateTemplate', () => {
@ -187,6 +397,44 @@ describe('TemplateManager', () => {
});
});
describe('listObjects', () => {
it('parses a present filter with getFilter before listing', async () => {
const parsedFilter = { name: { $eq: 'Test' } };
getFilter.mockResolvedValue(parsedFilter);
listObjects.mockResolvedValue([{ name: 'Test' }]);
const result = await templateManager.listObjects('printer', { name: '=Test' });
expect(getFilter).toHaveBeenCalledWith(
{ name: '=Test' },
['name'],
true,
expect.anything()
);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({
filter: parsedFilter,
pagination: false,
})
);
expect(result).toEqual([{ name: 'Test' }]);
});
it('does not call getFilter when no filter is provided', async () => {
listObjects.mockResolvedValue([]);
await templateManager.listObjects('printer');
expect(getFilter).not.toHaveBeenCalled();
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({
filter: {},
pagination: false,
})
);
});
});
describe('renderPDF', () => {
it('should render a PDF successfully', async () => {
const mockTemplate = {

View File

@ -14,6 +14,7 @@ import '../database/schemas/management/documentsize.schema.js';
import config from '../config.js';
import { getObject, listObjects } from '../database/database.js';
import { getModelByName } from '../services/misc/model.js';
import { getFilter } from '../utils.js';
import { generatePDF } from './pdffactory.js';
import { convertPDFToImage } from './pdfUtils.js';
@ -40,10 +41,7 @@ async function loadTemplates() {
baseCSS = fs.readFileSync(join(__dirname, '/assets/styles.css'), 'utf8');
previewTemplate = fs.readFileSync(join(__dirname, '/assets/previewtemplate.ejs'), 'utf8');
renderTemplateEjs = fs.readFileSync(join(__dirname, '/assets/rendertemplate.ejs'), 'utf8');
contentPlaceholder = fs.readFileSync(
join(__dirname, '/assets/contentplaceholder.ejs'),
'utf8'
);
contentPlaceholder = fs.readFileSync(join(__dirname, '/assets/contentplaceholder.ejs'), 'utf8');
previewPaginationScript = fs.readFileSync(
join(__dirname, '/assets/previewpagination.js'),
'utf8'
@ -247,7 +245,7 @@ async function transformCustomElements(content) {
}),
(tree) =>
tree.match({ tag: 'DateTime' }, (node) => {
const dateTime = dayjs.utc(node.content[0]);
const dateTime = dayjs.utc(node?.content ? node.content[0] : '');
return {
content: [dateTime.format('YYYY-MM-DD hh:mm:ss')],
tag: 'span',
@ -355,9 +353,7 @@ function normalizeDownloadType(type) {
function imageToSvg(pngBuffer, widthPx, heightPx, documentWidthMm, documentHeightMm) {
const b64 = pngBuffer.toString('base64');
const heightAttr =
documentHeightMm == 'auto' || documentHeightMm == null
? ''
: ` height="${documentHeightMm}mm"`;
documentHeightMm == 'auto' || documentHeightMm == null ? '' : ` height="${documentHeightMm}mm"`;
return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${documentWidthMm}mm"${heightAttr} viewBox="0 0 ${widthPx} ${heightPx}">
<image href="data:image/png;base64,${b64}" width="${widthPx}" height="${heightPx}"/>
@ -390,6 +386,72 @@ export function resolveDocumentPadding(documentSize = {}, paddingRequested = fal
};
}
export function stripDocumentTemplateReference(reference) {
const trimmed = String(reference ?? '').trim();
if (/^DTP:/i.test(trimmed)) {
return trimmed.slice(4).trim();
}
return trimmed;
}
function omitTemplateFc(data) {
if (data == null || typeof data !== 'object' || Array.isArray(data)) {
return data;
}
const rest = { ...data };
delete rest.fc;
return rest;
}
function resolveNestedTemplateObject(object) {
if (typeof object === 'string') {
const trimmed = object.trim();
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
try {
const parsed = JSON.parse(trimmed);
if (parsed != null && typeof parsed === 'object') {
return omitTemplateFc(parsed);
}
} catch {
return object;
}
}
return object;
}
if (object != null && typeof object === 'object' && typeof object.toObject === 'function') {
return omitTemplateFc(object.toObject());
}
return omitTemplateFc(object);
}
function buildTemplateData(documentTemplate, data = {}, fc) {
const objectData = resolveNestedTemplateObject(data) ?? {};
if (documentTemplate?.global == true) {
if (objectData != null && typeof objectData === 'object' && !Array.isArray(objectData)) {
return { ...objectData, fc };
}
return { fc };
}
const objectType = documentTemplate?.objectType;
const modelEntry = getModelByName(objectType);
const model = modelEntry?.model || modelEntry;
const defaultValues = {};
if (model?.schema?.obj) {
for (const key of Object.keys(model.schema.obj)) {
defaultValues[key] = null;
}
}
if (objectData != null && typeof objectData === 'object' && !Array.isArray(objectData)) {
return { ...defaultValues, ...objectData, fc };
}
return { ...defaultValues, fc };
}
export class TemplateManager {
constructor() {
this.fc = {
@ -399,6 +461,108 @@ export class TemplateManager {
};
}
createTemplateFc(getDefaultData, options, visited) {
const fc = {
listObjects: this.listObjects.bind(this),
getObject: this.getObject.bind(this),
formatDate: this.formatDate.bind(this),
};
fc.renderDocumentTemplate = async (reference, object) => {
const dataToUse = object !== undefined ? object : getDefaultData();
return this.renderNestedDocumentTemplate(reference, dataToUse, options, visited);
};
return fc;
}
async getDocumentTemplateByReference(reference) {
const strippedReference = stripDocumentTemplateReference(reference);
if (strippedReference == null || strippedReference === '') {
return { error: 'Document template reference is required.', code: 400 };
}
const results = await listObjects({
model: documentTemplateModel,
filter: { _reference: strippedReference },
populate: [{ path: 'parent', strictPopulate: false }, { path: 'documentSize' }],
pagination: false,
});
if (results == null || results.error) {
return {
error: results?.error || 'Document template not found.',
code: results?.code || 404,
};
}
if (!Array.isArray(results) || results.length === 0) {
return { error: 'Document template not found.', code: 404 };
}
return results[0];
}
async renderNestedDocumentTemplate(reference, object, options = {}, visited = new Set()) {
const strippedReference = stripDocumentTemplateReference(reference);
if (strippedReference == null || strippedReference === '') {
throw new Error('Document template reference is required.');
}
if (visited.has(strippedReference)) {
throw new Error(`Circular document template reference: ${strippedReference}`);
}
const nextVisited = new Set(visited);
nextVisited.add(strippedReference);
const nestedTemplate = await this.getDocumentTemplateByReference(strippedReference);
if (nestedTemplate == null || nestedTemplate.error) {
throw new Error(nestedTemplate?.error || `Document template not found: ${reference}`);
}
if (nestedTemplate.content == null || typeof nestedTemplate.content !== 'string') {
throw new Error(`Document template content is required: ${reference}`);
}
const renderOptions = {
async: true,
...options,
};
let nestedData;
const fc = this.createTemplateFc(() => nestedData, renderOptions, nextVisited);
nestedData = buildTemplateData(nestedTemplate, object, fc);
// Return a content fragment only. Page shells and previewPaginationScript
// already exist on the host document.
let nestedContent = await ejs.render(nestedTemplate.content, nestedData, renderOptions);
let parentTemplate = nestedTemplate.parent;
if (parentTemplate != undefined) {
if (
typeof parentTemplate === 'string' ||
parentTemplate.content == null ||
typeof parentTemplate.content !== 'string'
) {
parentTemplate = await getObject({
model: documentTemplateModel,
id: parentTemplate._id || parentTemplate.id || parentTemplate,
populate: [{ path: 'documentSize' }, { path: 'parent', strictPopulate: false }],
});
}
if (
parentTemplate?.error ||
parentTemplate.content == null ||
typeof parentTemplate.content !== 'string'
) {
throw new Error('Parent template content is required and must be a string.');
}
if (parentTemplate._reference) {
nextVisited.add(String(parentTemplate._reference));
}
const parentData = { content: nestedContent };
parentData.fc = this.createTemplateFc(() => parentData, renderOptions, nextVisited);
nestedContent = await ejs.render(parentTemplate.content, parentData, renderOptions);
}
return nestedContent;
}
async renderTemplate(id, content, data = {}, scale = 1, options = {}, preview = true) {
try {
const { padding: paddingRequested = false, ...ejsOptions } = options;
@ -411,10 +575,7 @@ export class TemplateManager {
const documentTemplate = await getObject({
model: documentTemplateModel,
id,
populate: [
{ path: 'documentSize' },
{ path: 'parent', strictPopulate: false },
],
populate: [{ path: 'documentSize' }, { path: 'parent', strictPopulate: false }],
});
if (documentTemplate == null || documentTemplate.error) {
@ -433,9 +594,14 @@ export class TemplateManager {
return { error: 'Template content is required and must be a string.', code: 400 };
}
const visited = new Set();
if (documentTemplate._reference) {
visited.add(String(documentTemplate._reference));
}
var templateData = {};
if (documentTemplate.global == true) {
templateData = { content: contentPlaceholder, fc: this.fc };
templateData = { content: contentPlaceholder };
} else {
const objectType = documentTemplate?.objectType;
const modelEntry = getModelByName(objectType);
@ -448,8 +614,9 @@ export class TemplateManager {
for (const key of defaultKeys) {
defaultValues[key] = null;
}
templateData = { ...defaultValues, ...data, fc: this.fc };
templateData = { ...defaultValues, ...data };
}
templateData.fc = this.createTemplateFc(() => templateData, defaultOptions, visited);
const templateContent = await ejs.render(templateContentSource, templateData, defaultOptions);
@ -461,13 +628,14 @@ export class TemplateManager {
parentTemplate = await getObject({
model: documentTemplateModel,
id: parentTemplate,
populate: [
{ path: 'documentSize' },
{ path: 'parent', strictPopulate: false },
],
populate: [{ path: 'documentSize' }, { path: 'parent', strictPopulate: false }],
});
}
if (parentTemplate?.error || parentTemplate.content == null || typeof parentTemplate.content !== 'string') {
if (
parentTemplate?.error ||
parentTemplate.content == null ||
typeof parentTemplate.content !== 'string'
) {
logger.error(
'Parent template content is required and must be a string.',
parentTemplate?.content
@ -479,9 +647,14 @@ export class TemplateManager {
code: 400,
};
}
if (parentTemplate._reference) {
visited.add(String(parentTemplate._reference));
}
const parentData = { content: templateContent };
parentData.fc = this.createTemplateFc(() => parentData, defaultOptions, visited);
templateWithParentContent = await ejs.render(
parentTemplate.content,
{ content: templateContent, fc: this.fc },
parentData,
defaultOptions
);
} else {
@ -638,15 +811,7 @@ export class TemplateManager {
for (const image of images) {
const png = await sharp(image).png().toBuffer();
const meta = await sharp(png).metadata();
svgs.push(
imageToSvg(
png,
meta.width,
meta.height,
pdfResult.width,
pdfResult.height
)
);
svgs.push(imageToSvg(png, meta.width, meta.height, pdfResult.width, pdfResult.height));
}
return { svgs };
} catch (error) {
@ -708,9 +873,14 @@ export class TemplateManager {
if (model == undefined) {
throw new Error('Farm Control: Object type not found.');
}
const resolvedFilter =
filter != null && Object.keys(filter).length > 0
? await getFilter(filter, Object.keys(filter), true, model)
: filter;
const objects = await listObjects({
model,
filter,
filter: resolvedFilter,
populate,
pagination: false,
});

View File

@ -162,7 +162,7 @@ function buildRegexOp(pattern, useOptions = true) {
function getSchemaRefName(property, model = null) {
const baseProperty = getBaseProperty(property);
const path = model?.schema?.path(baseProperty) ?? getSchemaPathFromModels(baseProperty);
return path?.options?.ref ?? path?.caster?.options?.ref ?? path?.path ?? null;
return path?.options?.ref ?? path?.caster?.options?.ref ?? null;
}
function getRefModelEntryFromSchemaRef(refName) {
@ -666,10 +666,14 @@ async function resolveRefLeaf(rawToken, fallbackRefName) {
if (token === '') return NO_MATCH_CONDITION;
const refModelEntry = getRefModelEntryForToken(token, fallbackRefName);
if (!refModelEntry?.model) return NO_MATCH_CONDITION;
const lookupToken = stripPrefixFromOperand(token);
if (!refModelEntry?.model) {
const objectId = extractObjectIdFromOperand(lookupToken);
if (objectId) return { value: objectId };
return NO_MATCH_CONDITION;
}
const rangeIdx = lookupToken.indexOf('..');
if (rangeIdx !== -1) {
const ids = await listRefModelIds(refModelEntry, lookupToken);
@ -739,7 +743,7 @@ async function parseFilter(property, value, model = null) {
const trimmed = value.trim();
if (fieldKind.kind === 'objectId' && property != '_id') {
if ((fieldKind.kind === 'objectId' || fieldKind.kind === 'objectRef') && property != '_id') {
const refName = fieldKind.ref ?? getSchemaRefName(property, model);
return resolveObjectRefFilter(filterProperty, trimmed, refName);
}
@ -1580,14 +1584,33 @@ function getSort(sort, allowedSorters, defaultSort = 'createdAt') {
return defaultSort;
}
function normalizeFilterQuery(query = {}) {
const queryClean = { ...query };
// qs turns `order._id=` into `{ order: { _id } }`. Lift it so it can be parsed
// as the order object filter instead of a nested query object.
if (queryClean.order && typeof queryClean.order === 'object' && !Array.isArray(queryClean.order)) {
if (queryClean.order._id !== undefined) {
queryClean['order._id'] = queryClean.order._id;
}
delete queryClean.order;
}
return queryClean;
}
// Returns a filter object based on allowed filters and req.query
async function getFilter(query, allowedFilters, parse = true, model = null) {
const clauses = [];
const queryClean = { ...query };
for (const key of ['sort', 'order', 'page', 'limit']) {
const queryClean = normalizeFilterQuery(query);
for (const key of ['sortProperty', 'sortOrder', 'page', 'limit']) {
if (key in queryClean) delete queryClean[key];
}
logger.info('queryExcludingSortAndOrder', queryClean);
if (!parse && queryClean['order._id'] !== undefined && allowedFilters.includes('order')) {
queryClean.order = queryClean['order._id'];
delete queryClean['order._id'];
}
for (const [key, value] of Object.entries(queryClean)) {
if (allowedFilters.includes(key)) {
clauses.push(parse ? await parseFilter(key, value, model) : { [key]: value });