diff --git a/src/routes/inventory/orderitems.js b/src/routes/inventory/orderitems.js index c32d228..258d6a0 100644 --- a/src/routes/inventory/orderitems.js +++ b/src/routes/inventory/orderitems.js @@ -5,31 +5,31 @@ import { getFilter, convertPropertiesString } from '../../utils.js'; const router = express.Router(); const listAllowedFilters = [ - 'name', - 'itemType', - 'item', - 'item._id', - 'order', - 'order._id', - 'orderType', - 'shipment', - 'shipment._id', - 'state', - 'createdAt', - 'updatedAt', - '_reference' - ]; + 'name', + 'itemType', + 'item', + 'item._id', + 'order', + 'order._id', + 'orderType', + 'shipment', + 'shipment._id', + 'state', + 'createdAt', + 'updatedAt', + '_reference', +]; const propertiesAllowedFilters = [ - 'name', - 'itemType', - 'item', - 'item._id', - 'order', - 'order._id', - 'orderType', - 'shipment', - 'shipment._id', - ]; + 'name', + 'itemType', + 'item', + 'item._id', + 'order', + 'order._id', + 'orderType', + 'shipment', + 'shipment._id', +]; import { listOrderItemsRouteHandler, getOrderItemRouteHandler, @@ -42,14 +42,13 @@ import { getOrderItemHistoryRouteHandler, searchOrderItemsRouteHandler, getOrderItemPropertyValuesRouteHandler, - - getOrderItemNeighborsRouteHandler + getOrderItemNeighborsRouteHandler, } from '../../services/inventory/orderitems.js'; // list of order items router.get('/', isAuthenticated, async (req, res) => { const { page, limit, property, search, sort, order } = req.query; - const filter = await getFilter(req.query, listAllowedFilters); + const filter = await getFilter(req.query, listAllowedFilters); listOrderItemsRouteHandler(req, res, page, limit, property, filter, search, sort, order); }); @@ -72,7 +71,6 @@ router.get('/search', isAuthenticated, async (req, res) => { searchOrderItemsRouteHandler(req, res, search); }); - router.post('/', isAuthenticated, async (req, res) => { newOrderItemRouteHandler(req, res); }); diff --git a/src/utils.js b/src/utils.js index ab37300..6d9d9d2 100644 --- a/src/utils.js +++ b/src/utils.js @@ -9,7 +9,7 @@ import log4js from 'log4js'; import config from './config.js'; import crypto from 'crypto'; import canonicalize from 'canonical-json'; -import { getModelByName } from './services/misc/model.js'; +import { getModelByName, getModelByPrefix } from './services/misc/model.js'; import { models } from './database/schemas/models.js'; import { createEmailRenderAuthCode } from './services/misc/emailRenderAuth.js'; import { Worker } from 'worker_threads'; @@ -185,6 +185,30 @@ function getRefModelEntryFromSchemaRef(refName) { }; } +function getRefModelEntryFromPrefix(prefix) { + const entry = getModelByPrefix(prefix); + if (!entry?.model) return null; + return { + model: entry.model, + idField: entry.idField ?? '_id', + referenceField: entry.referenceField ?? '_reference', + }; +} + +function stripPrefixFromOperand(operand) { + const { suffix, hadPrefix } = parsePrefixedValue(String(operand).trim()); + return hadPrefix ? suffix : String(operand).trim(); +} + +function getRefModelEntryForToken(token, fallbackRefName) { + const { prefix, hadPrefix } = parsePrefixedValue(String(token).trim()); + if (hadPrefix) { + const entry = getRefModelEntryFromPrefix(prefix); + if (entry) return entry; + } + return getRefModelEntryFromSchemaRef(fallbackRefName); +} + function parseBooleanOperand(value) { const lower = String(value).trim().toLowerCase(); if (lower === 'yes') return true; @@ -615,8 +639,10 @@ function combineAndRefIds(children) { } async function resolveRefOperand(operand, refModelEntry, operator = 'eq') { - const operandText = String(operand).trim(); - const objectId = extractObjectIdFromOperand(operandText); + if (!refModelEntry?.model) return NO_MATCH_CONDITION; + + const lookupOperand = String(operand).trim(); + const objectId = extractObjectIdFromOperand(lookupOperand); if (objectId) { if (operator === 'ne') return { op: { $ne: objectId } }; @@ -624,7 +650,7 @@ async function resolveRefOperand(operand, refModelEntry, operator = 'eq') { return NO_MATCH_CONDITION; } - const expression = buildRefListExpression(operator, operandText); + const expression = buildRefListExpression(operator, lookupOperand); const ids = await listRefModelIds(refModelEntry, expression); if (operator === 'ne') { @@ -635,13 +661,18 @@ async function resolveRefOperand(operand, refModelEntry, operator = 'eq') { return idsToCondition(ids); } -async function resolveRefLeaf(rawToken, refModelEntry) { +async function resolveRefLeaf(rawToken, fallbackRefName) { const token = rawToken.trim(); if (token === '') return NO_MATCH_CONDITION; - const rangeIdx = token.indexOf('..'); + const refModelEntry = getRefModelEntryForToken(token, fallbackRefName); + if (!refModelEntry?.model) return NO_MATCH_CONDITION; + + const lookupToken = stripPrefixFromOperand(token); + + const rangeIdx = lookupToken.indexOf('..'); if (rangeIdx !== -1) { - const ids = await listRefModelIds(refModelEntry, token); + const ids = await listRefModelIds(refModelEntry, lookupToken); return idsToCondition(ids); } @@ -654,37 +685,33 @@ async function resolveRefLeaf(rawToken, refModelEntry) { ['=', 'eq'], ]; for (const [symbol, name] of operators) { - if (token.startsWith(symbol)) { - return resolveRefOperand(token.slice(symbol.length), refModelEntry, name); + if (lookupToken.startsWith(symbol)) { + return resolveRefOperand(lookupToken.slice(symbol.length), refModelEntry, name); } } - const objectId = extractObjectIdFromOperand(token); + const objectId = extractObjectIdFromOperand(lookupToken); if (objectId) return { value: objectId }; - const ids = await listRefModelIds(refModelEntry, token); + const ids = await listRefModelIds(refModelEntry, lookupToken); return idsToCondition(ids); } -async function resolveRefCondition(node, refModelEntry) { +async function resolveRefCondition(node, fallbackRefName) { if (node.type === 'leaf') { - return resolveRefLeaf(node.token, refModelEntry); + return resolveRefLeaf(node.token, fallbackRefName); } const children = await Promise.all( - node.items.map((item) => resolveRefCondition(item, refModelEntry)) + node.items.map((item) => resolveRefCondition(item, fallbackRefName)) ); return node.type === 'and' ? combineAndRefIds(children) : combineOrRefIds(children); } -async function resolveObjectRefFilter(filterProperty, expression, refName) { - const refModelEntry = getRefModelEntryFromSchemaRef(refName); - - if (!refModelEntry?.model) return { [filterProperty]: { $in: [] } }; - +async function resolveObjectRefFilter(filterProperty, expression, fallbackRefName) { const tree = parseExpression(expression); - const condition = await resolveRefCondition(tree, refModelEntry); + const condition = await resolveRefCondition(tree, fallbackRefName); return conditionToQuery(condition, filterProperty); } @@ -1524,7 +1551,12 @@ function mergeFilterClauses(clauses) { // Returns a filter object based on allowed filters and req.query async function getFilter(query, allowedFilters, parse = true, model = null) { const clauses = []; - for (const [key, value] of Object.entries(query)) { + const queryClean = { ...query }; + for (const key of ['sort', 'order', 'page', 'limit']) { + if (key in queryClean) delete queryClean[key]; + } + logger.info('queryExcludingSortAndOrder', queryClean); + for (const [key, value] of Object.entries(queryClean)) { if (allowedFilters.includes(key)) { clauses.push(parse ? await parseFilter(key, value, model) : { [key]: value }); }