diff --git a/src/middlewares/validate.middleware.js b/src/middlewares/validate.middleware.js index 6930850..fed2504 100644 --- a/src/middlewares/validate.middleware.js +++ b/src/middlewares/validate.middleware.js @@ -13,6 +13,13 @@ const validate = return next(new ApiError(422, 'Validation failed', errors)); } + // Preserve the generic `dropdown_call` flag on list queries so every list + // endpoint can return all active rows without each schema declaring it. + if (source === 'query' && req[source] && req[source].dropdown_call !== undefined) { + const raw = req[source].dropdown_call; + value.dropdown_call = raw === true || raw === 'true' || raw === 1 || raw === '1'; + } + req[source] = value; return next(); }; diff --git a/src/modules/assets/assets.service.js b/src/modules/assets/assets.service.js index 2d0447e..6ca806a 100644 --- a/src/modules/assets/assets.service.js +++ b/src/modules/assets/assets.service.js @@ -1,7 +1,7 @@ const prisma = require('../../config/prisma'); const ApiError = require('../../utils/ApiError'); const auditLog = require('../../utils/auditLog'); -const { getPagination } = require('../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../utils/pagination'); const { nextDocumentNumber } = require('../../utils/generateCode'); const { rowsToCsv } = require('../../utils/csv'); const { DISPOSAL_STATUSES, getAssetDropdownOptions } = require('./assets.constants'); @@ -321,9 +321,21 @@ const buildAssetsWhere = (query) => ({ }); const listAssets = async (query) => { - const { page, limit, skip } = getPagination(query); const where = buildAssetsWhere(query); + if (isDropdownCall(query)) { + where.is_active = true; + const rows = await prisma.assets.findMany({ + where, + include: assetInclude, + orderBy: { created_at: 'desc' }, + }); + const data = rows.map(sanitizeAsset); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [rows, total] = await Promise.all([ prisma.assets.findMany({ where, diff --git a/src/modules/grn/grn.service.js b/src/modules/grn/grn.service.js index 4fa2d18..f3dea26 100644 --- a/src/modules/grn/grn.service.js +++ b/src/modules/grn/grn.service.js @@ -1,7 +1,7 @@ const prisma = require('../../config/prisma'); const ApiError = require('../../utils/ApiError'); const auditLog = require('../../utils/auditLog'); -const { getPagination } = require('../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../utils/pagination'); const { nextDocumentNumber } = require('../../utils/generateCode'); const { generatePdf } = require('../../utils/pdf/pdfGenerator'); const { generateGrnHtml } = require('../../utils/pdf/templates/grn.template'); @@ -449,9 +449,21 @@ const buildGrnsWhere = (query) => ({ }); const listGrns = async (query) => { - const { page, limit, skip } = getPagination(query); const where = buildGrnsWhere(query); + if (isDropdownCall(query)) { + where.is_active = true; + const rows = await prisma.grn.findMany({ + where, + include: grnListInclude, + orderBy: { created_at: 'desc' }, + }); + const data = rows.map(sanitizeGrn); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [rows, total] = await Promise.all([ prisma.grn.findMany({ where, diff --git a/src/modules/masters/_shared/master.factory.js b/src/modules/masters/_shared/master.factory.js index 5eb9c5d..093765e 100644 --- a/src/modules/masters/_shared/master.factory.js +++ b/src/modules/masters/_shared/master.factory.js @@ -1,7 +1,7 @@ const prisma = require('../../../config/prisma'); const ApiError = require('../../../utils/ApiError'); const auditLog = require('../../../utils/auditLog'); -const { getPagination } = require('../../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../../utils/pagination'); const { parseId } = require('../../../utils/parseId'); const { rowsToCsv } = require('../../../utils/csv'); @@ -107,10 +107,24 @@ const buildMasterService = ({ return mapped; }; + const hasActiveFlag = fields.some((f) => f.name === 'is_active'); + const list = async (query) => { - const { page, limit, skip } = getPagination(query); const where = buildListWhere(query); + if (isDropdownCall(query)) { + if (hasActiveFlag) where.is_active = true; + const rows = await model.findMany({ + where, + orderBy: { created_at: 'desc' }, + ...withInclude, + }); + const data = rows.map(mapRow); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [rows, total] = await Promise.all([ model.findMany({ where, orderBy: { created_at: 'desc' }, skip, take: limit, ...withInclude }), model.count({ where }), diff --git a/src/modules/masters/items/items.service.js b/src/modules/masters/items/items.service.js index 5a04d94..ee24d0c 100644 --- a/src/modules/masters/items/items.service.js +++ b/src/modules/masters/items/items.service.js @@ -1,7 +1,7 @@ const prisma = require('../../../config/prisma'); const ApiError = require('../../../utils/ApiError'); const auditLog = require('../../../utils/auditLog'); -const { getPagination } = require('../../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../../utils/pagination'); const { nextDocumentNumber } = require('../../../utils/generateCode'); const { parseId } = require('../../../utils/parseId'); const { rowsToCsv } = require('../../../utils/csv'); @@ -207,9 +207,21 @@ const buildItemsWhere = (query) => ({ }); const listItems = async (query) => { - const { page, limit, skip } = getPagination(query); const where = buildItemsWhere(query); + if (isDropdownCall(query)) { + where.is_active = true; + const rows = await prisma.items.findMany({ + where, + include: itemInclude, + orderBy: { created_at: 'desc' }, + }); + const data = rows.map(sanitizeItem); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [rows, total] = await Promise.all([ prisma.items.findMany({ where, diff --git a/src/modules/masters/locations/locations.service.js b/src/modules/masters/locations/locations.service.js index dc883d1..31ab03f 100644 --- a/src/modules/masters/locations/locations.service.js +++ b/src/modules/masters/locations/locations.service.js @@ -1,7 +1,7 @@ const prisma = require('../../../config/prisma'); const ApiError = require('../../../utils/ApiError'); const auditLog = require('../../../utils/auditLog'); -const { getPagination } = require('../../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../../utils/pagination'); const { parseId } = require('../../../utils/parseId'); const { rowsToCsv } = require('../../../utils/csv'); const { @@ -120,22 +120,10 @@ const buildLocationsWhere = (query, type = null) => { }; const listLocations = async (query, type = null) => { - const { page, limit, skip } = getPagination(query); const { where, effectiveType } = buildLocationsWhere(query, type); - const [rows, total] = await Promise.all([ - prisma.locations.findMany({ - where, - include: - effectiveType === LOCATION_TYPES.WAREHOUSE || !effectiveType - ? warehouseInclude - : undefined, - orderBy: { created_at: 'desc' }, - skip, - take: limit, - }), - prisma.locations.count({ where }), - ]); + const locationsInclude = + effectiveType === LOCATION_TYPES.WAREHOUSE || !effectiveType ? warehouseInclude : undefined; const mapRow = (row) => { if (effectiveType === LOCATION_TYPES.PLANT) return toPlantResponse(row); @@ -143,6 +131,30 @@ const listLocations = async (query, type = null) => { return toLocationResponse(row); }; + if (isDropdownCall(query)) { + where.is_active = true; + const rows = await prisma.locations.findMany({ + where, + include: locationsInclude, + orderBy: { created_at: 'desc' }, + }); + const data = rows.map(mapRow); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + + const [rows, total] = await Promise.all([ + prisma.locations.findMany({ + where, + include: locationsInclude, + orderBy: { created_at: 'desc' }, + skip, + take: limit, + }), + prisma.locations.count({ where }), + ]); + return { data: rows.map(mapRow), meta: { page, limit, total } }; }; diff --git a/src/modules/masters/uom/uom.service.js b/src/modules/masters/uom/uom.service.js index 36c7197..bcb88b5 100644 --- a/src/modules/masters/uom/uom.service.js +++ b/src/modules/masters/uom/uom.service.js @@ -1,7 +1,7 @@ const prisma = require('../../../config/prisma'); const ApiError = require('../../../utils/ApiError'); const auditLog = require('../../../utils/auditLog'); -const { getPagination } = require('../../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../../utils/pagination'); const { parseId } = require('../../../utils/parseId'); const { rowsToCsv } = require('../../../utils/csv'); @@ -46,9 +46,16 @@ const createUom = async (payload, userId, requestId) => { }; const listUom = async (query) => { - const { page, limit, skip } = getPagination(query); const where = buildUomWhere(query); + if (isDropdownCall(query)) { + where.is_active = true; + const data = await prisma.uom.findMany({ where, orderBy: { created_at: 'desc' } }); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [data, total] = await Promise.all([ prisma.uom.findMany({ where, orderBy: { created_at: 'desc' }, skip, take: limit }), prisma.uom.count({ where }), diff --git a/src/modules/purchase-orders/purchase-orders.service.js b/src/modules/purchase-orders/purchase-orders.service.js index aa24030..986afc7 100644 --- a/src/modules/purchase-orders/purchase-orders.service.js +++ b/src/modules/purchase-orders/purchase-orders.service.js @@ -1,7 +1,7 @@ const prisma = require('../../config/prisma'); const ApiError = require('../../utils/ApiError'); const auditLog = require('../../utils/auditLog'); -const { getPagination } = require('../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../utils/pagination'); const { nextDocumentNumber } = require('../../utils/generateCode'); const { generatePdf } = require('../../utils/pdf/pdfGenerator'); const { generatePoHtml } = require('../../utils/pdf/templates/po.template'); @@ -492,9 +492,21 @@ const buildPurchaseOrdersWhere = (query) => ({ }); const listPurchaseOrders = async (query) => { - const { page, limit, skip } = getPagination(query); const where = buildPurchaseOrdersWhere(query); + if (isDropdownCall(query)) { + where.is_active = true; + const rows = await prisma.purchase_orders.findMany({ + where, + include: poListInclude, + orderBy: { created_at: 'desc' }, + }); + const data = rows.map(sanitizePo); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [rows, total] = await Promise.all([ prisma.purchase_orders.findMany({ where, diff --git a/src/modules/roles/roles.service.js b/src/modules/roles/roles.service.js index a929ddc..f8a1c35 100644 --- a/src/modules/roles/roles.service.js +++ b/src/modules/roles/roles.service.js @@ -1,7 +1,7 @@ const prisma = require('../../config/prisma'); const ApiError = require('../../utils/ApiError'); const auditLog = require('../../utils/auditLog'); -const { getPagination } = require('../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../utils/pagination'); const PERMISSION_ACTIONS = ['view', 'create', 'edit', 'delete', 'approve', 'export']; @@ -137,8 +137,6 @@ const createRole = async (payload, userId, requestId) => { }; const listRoles = async (query) => { - const { page, limit, skip } = getPagination(query); - const where = { deleted_at: null, ...(query.is_active !== undefined ? { is_active: query.is_active } : {}), @@ -152,13 +150,30 @@ const listRoles = async (query) => { : {}), }; + const rolesInclude = { + role_permissions: { include: permissionSelect }, + _count: { select: { user_roles: true } }, + }; + + const toItem = (role) => ({ ...sanitizeRole(role), user_count: role._count.user_roles }); + + if (isDropdownCall(query)) { + where.is_active = true; + const rows = await prisma.roles.findMany({ + where, + include: rolesInclude, + orderBy: { created_at: 'desc' }, + }); + const data = rows.map(toItem); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [rows, total] = await Promise.all([ prisma.roles.findMany({ where, - include: { - role_permissions: { include: permissionSelect }, - _count: { select: { user_roles: true } }, - }, + include: rolesInclude, orderBy: { created_at: 'desc' }, skip, take: limit, @@ -166,12 +181,7 @@ const listRoles = async (query) => { prisma.roles.count({ where }), ]); - const data = rows.map((role) => ({ - ...sanitizeRole(role), - user_count: role._count.user_roles, - })); - - return { data, meta: { page, limit, total } }; + return { data: rows.map(toItem), meta: { page, limit, total } }; }; const getRoleById = async (id) => { diff --git a/src/modules/users/users.service.js b/src/modules/users/users.service.js index 89c2699..d39383a 100644 --- a/src/modules/users/users.service.js +++ b/src/modules/users/users.service.js @@ -3,7 +3,7 @@ const prisma = require('../../config/prisma'); const env = require('../../config/env'); const ApiError = require('../../utils/ApiError'); const auditLog = require('../../utils/auditLog'); -const { getPagination } = require('../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../utils/pagination'); const { encrypt, decrypt, blindIndex } = require('../../utils/encryption'); const { assertPlant } = require('../../utils/locations'); const { extractUserRoles } = require('../../utils/userPermissions'); @@ -233,9 +233,21 @@ const createUser = async (payload, userId, requestId) => { }; const listUsers = async (query) => { - const { page, limit, skip } = getPagination(query); const where = buildUsersWhere(query); + if (isDropdownCall(query)) { + where.is_active = true; + const rows = await prisma.users.findMany({ + where, + include: userInclude, + orderBy: { created_at: 'desc' }, + }); + const data = rows.map(toUserListItem); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [rows, total] = await Promise.all([ prisma.users.findMany({ where, diff --git a/src/modules/vendors/vendors.service.js b/src/modules/vendors/vendors.service.js index 25683b6..ebe679c 100644 --- a/src/modules/vendors/vendors.service.js +++ b/src/modules/vendors/vendors.service.js @@ -1,7 +1,7 @@ const prisma = require('../../config/prisma'); const ApiError = require('../../utils/ApiError'); const auditLog = require('../../utils/auditLog'); -const { getPagination } = require('../../utils/pagination'); +const { getPagination, isDropdownCall } = require('../../utils/pagination'); const { nextDocumentNumber } = require('../../utils/generateCode'); const { encrypt, decrypt, blindIndex } = require('../../utils/encryption'); const { rowsToCsv } = require('../../utils/csv'); @@ -150,9 +150,21 @@ const createVendor = async (payload, userId, requestId) => { }; const listVendors = async (query) => { - const { page, limit, skip } = getPagination(query); const where = buildVendorsWhere(query); + if (isDropdownCall(query)) { + where.is_active = true; + const rows = await prisma.vendors.findMany({ + where, + include: vendorInclude, + orderBy: { created_at: 'desc' }, + }); + const data = rows.map((row) => sanitizeVendor(row)); + return { data, meta: { page: 1, limit: data.length, total: data.length, dropdown: true } }; + } + + const { page, limit, skip } = getPagination(query); + const [rows, total] = await Promise.all([ prisma.vendors.findMany({ where, diff --git a/src/utils/pagination.js b/src/utils/pagination.js index 93cfeaf..e33ed44 100644 --- a/src/utils/pagination.js +++ b/src/utils/pagination.js @@ -5,4 +5,15 @@ const getPagination = (query) => { return { page, limit, skip }; }; -module.exports = { getPagination }; +/** + * Detects the `dropdown_call` flag on a list query. When true, list endpoints + * should return ALL matching rows (no pagination) restricted to active records, + * which is what dropdowns/selects need instead of page-wise data. + */ +const isDropdownCall = (query) => + query?.dropdown_call === true || + query?.dropdown_call === 'true' || + query?.dropdown_call === 1 || + query?.dropdown_call === '1'; + +module.exports = { getPagination, isDropdownCall };