This is an automated email from the ASF dual-hosted git repository.

diveshdut pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit 125211996c5ee3b3b5910949ddb737300a484f62
Author: diveshdut <[email protected]>
AuthorDate: Tue Aug 11 16:24:22 2026 +0530

    OFBIZ-13422: Use REST paging for production run queries
    
    Refactor production run search to use RestQueryOptions, RestApiUtil paging 
metadata, framework sort resolution, and queryPagedList. Add estimated start 
date range filters and use shared lookup/display helpers while preserving 
batched enrichment.
---
 .../servicedef/services_production_run.xml         |   3 +
 .../jobshopmgt/ProductionRunQueryServices.groovy   | 199 +++++++++------------
 2 files changed, 83 insertions(+), 119 deletions(-)

diff --git a/applications/manufacturing/servicedef/services_production_run.xml 
b/applications/manufacturing/servicedef/services_production_run.xml
index 3ab331e4c3..44969b9155 100644
--- a/applications/manufacturing/servicedef/services_production_run.xml
+++ b/applications/manufacturing/servicedef/services_production_run.xml
@@ -39,8 +39,11 @@ under the License.
         <attribute name="facilityId" type="String" mode="IN" optional="true"/>
         <attribute name="statusId" type="String" mode="IN" optional="true"/>
         <attribute name="currentStatusId" type="String" mode="IN" 
optional="true"/>
+        <attribute name="estimatedStartDateFrom" type="Timestamp" mode="IN" 
optional="true"/>
+        <attribute name="estimatedStartDateThru" type="Timestamp" mode="IN" 
optional="true"/>
         <attribute name="sort" type="String" mode="IN" optional="true"/>
         <attribute name="totalCount" type="Long" mode="OUT" optional="false"/>
+        <attribute name="totalPages" type="Long" mode="OUT" optional="false"/>
         <attribute name="hasNext" type="Boolean" mode="OUT" optional="false"/>
         <attribute name="productionRuns" type="List" mode="OUT" 
optional="false"/>
     </service>
diff --git 
a/applications/manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunQueryServices.groovy
 
b/applications/manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunQueryServices.groovy
index eead9a03eb..aaaa95786b 100644
--- 
a/applications/manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunQueryServices.groovy
+++ 
b/applications/manufacturing/src/main/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunQueryServices.groovy
@@ -18,30 +18,28 @@
  */
 package org.apache.ofbiz.manufacturing.jobshopmgt
 
-import org.apache.ofbiz.base.util.UtilMisc
 import org.apache.ofbiz.base.util.UtilValidate
 import org.apache.ofbiz.entity.GenericValue
 import org.apache.ofbiz.entity.condition.EntityCondition
 import org.apache.ofbiz.entity.condition.EntityOperator
 import org.apache.ofbiz.entity.util.EntityQuery
-import org.apache.ofbiz.entity.util.EntityListIterator
+import org.apache.ofbiz.entity.util.EntityUtil
+import org.apache.ofbiz.manufacturing.api.ManufacturingServiceUtil
 import org.apache.ofbiz.party.party.PartyHelper
 import org.apache.ofbiz.service.ServiceUtil
+import org.apache.ofbiz.ws.rs.util.RestApiUtil
+import org.apache.ofbiz.ws.rs.util.RestQueryOptions
 
 Map findProductionRuns() {
-    // Keep paging defensive so API callers cannot request pathological 
windows.
-    Integer pageIndexValue = UtilMisc.toIntegerObject(parameters.pageIndex)
-    Integer pageSizeValue = UtilMisc.toIntegerObject(parameters.pageSize)
-    int pageIndex = pageIndexValue != null ? pageIndexValue : 0
-    int pageSize = pageSizeValue != null ? pageSizeValue : 20
-    if (pageIndex < 0) {
-        pageIndex = 0
-    }
-    if (pageSize < 1 || pageSize > 100) {
-        pageSize = 20
+    RestQueryOptions queryOptions
+    try {
+        queryOptions = RestQueryOptions.fromParameters(parameters)
+    } catch (IllegalArgumentException e) {
+        return ServiceUtil.returnError(e.message)
     }
+    Map filters = queryOptions.filters
 
-    String productionRunId = parameters.productionRunId ?: 
parameters.workEffortId
+    String productionRunId = filters.productionRunId ?: filters.workEffortId
     List conditions = [
             EntityCondition.makeCondition('workEffortTypeId', 
'PROD_ORDER_HEADER'),
             EntityCondition.makeCondition('workEffortGoodStdTypeId', 
'PRUN_PROD_DELIV')
@@ -49,69 +47,71 @@ Map findProductionRuns() {
     if (UtilValidate.isNotEmpty(productionRunId)) {
         conditions.add(EntityCondition.makeCondition('workEffortId', 
productionRunId))
     }
-    if (UtilValidate.isNotEmpty(parameters.productId)) {
-        conditions.add(EntityCondition.makeCondition('productId', 
parameters.productId))
+    if (UtilValidate.isNotEmpty(filters.productId)) {
+        conditions.add(EntityCondition.makeCondition('productId', 
filters.productId))
     }
-    if (UtilValidate.isNotEmpty(parameters.facilityId)) {
-        conditions.add(EntityCondition.makeCondition('facilityId', 
parameters.facilityId))
+    if (UtilValidate.isNotEmpty(filters.facilityId)) {
+        conditions.add(EntityCondition.makeCondition('facilityId', 
filters.facilityId))
     }
-    String currentStatusId = parameters.statusId ?: parameters.currentStatusId
+    String currentStatusId = filters.statusId ?: filters.currentStatusId
     if (UtilValidate.isNotEmpty(currentStatusId)) {
         conditions.add(EntityCondition.makeCondition('currentStatusId', 
currentStatusId))
     }
+    if (filters.estimatedStartDateFrom != null) {
+        conditions.add(EntityCondition.makeCondition('estimatedStartDate', 
EntityOperator.GREATER_THAN_EQUAL_TO,
+                filters.estimatedStartDateFrom))
+    }
+    if (filters.estimatedStartDateThru != null) {
+        conditions.add(EntityCondition.makeCondition('estimatedStartDate', 
EntityOperator.LESS_THAN_EQUAL_TO,
+                filters.estimatedStartDateThru))
+    }
 
-    if (UtilValidate.isNotEmpty(parameters.workEffortName)) {
-        conditions.add(EntityCondition.makeCondition('workEffortName', 
EntityOperator.LIKE, '%' + parameters.workEffortName + '%'))
+    if (UtilValidate.isNotEmpty(filters.workEffortName)) {
+        conditions.add(EntityUtil.upperLikeAny(['workEffortName'], 
filters.workEffortName))
     }
 
-    String queryText = parameters.query
+    String queryText = filters.query
     if (UtilValidate.isNotEmpty(queryText)) {
-        String likeQuery = '%' + queryText.trim() + '%'
-        List queryConditions = [
-                EntityCondition.makeCondition('workEffortId', 
EntityOperator.LIKE, likeQuery),
-                EntityCondition.makeCondition('workEffortName', 
EntityOperator.LIKE, likeQuery)
-        ]
+        List queryConditions = [EntityUtil.upperLikeAny(['workEffortId', 
'workEffortName'], queryText)]
+        Set matchingProductIds = EntityUtil.searchIds(delegator, 'Product', 
'productId',
+                ['productId', 'productName', 'internalName'], queryText, 500)
+        if (matchingProductIds) {
+            queryConditions.add(EntityCondition.makeCondition('productId', 
EntityOperator.IN, matchingProductIds as List))
+        }
         conditions.add(EntityCondition.makeCondition(queryConditions, 
EntityOperator.OR))
     }
 
-    if (UtilValidate.isNotEmpty(parameters.productName)) {
-        // Product name search is resolved through Product so we can match 
both display and internal names.
-        List productConditions = [
-                EntityCondition.makeCondition('productName', 
EntityOperator.LIKE, '%' + parameters.productName + '%'),
-                EntityCondition.makeCondition('internalName', 
EntityOperator.LIKE, '%' + parameters.productName + '%')
-        ]
-        List productIds = from('Product')
-                .where(EntityCondition.makeCondition(productConditions, 
EntityOperator.OR))
-                .getFieldList('productId')
-                .unique()
+    if (UtilValidate.isNotEmpty(filters.productName)) {
+        Set productIds = EntityUtil.searchIds(delegator, 'Product', 
'productId',
+                ['productId', 'productName', 'internalName'], 
filters.productName, 500)
         if (!productIds) {
-            return success(pageIndex: pageIndex, pageSize: pageSize, 
totalCount: 0L, hasNext: false, productionRuns: [])
+            return success(RestApiUtil.getPagedResult('productionRuns', [], 
queryOptions, 0L, null))
         }
-        conditions.add(EntityCondition.makeCondition('productId', 
EntityOperator.IN, productIds))
+        conditions.add(EntityCondition.makeCondition('productId', 
EntityOperator.IN, productIds as List))
     }
 
     EntityCondition whereCondition = EntityCondition.makeCondition(conditions, 
EntityOperator.AND)
-    List orderBy = resolveProductionRunSort(parameters.sort)
-    if (!orderBy) {
-        return ServiceUtil.returnError('Unsupported production run sort field: 
' + parameters.sort)
+    List orderBy
+    try {
+        orderBy = RestApiUtil.resolveOrderBy(queryOptions.sort, [
+                estimatedStartDate: 'estimatedStartDate',
+                actualStartDate: 'actualStartDate',
+                status: 'currentStatusId',
+                currentStatusId: 'currentStatusId',
+                productId: 'productId',
+                facilityId: 'facilityId',
+                productionRunId: 'workEffortId',
+                workEffortId: 'workEffortId',
+                workEffortName: 'workEffortName'
+        ], ['-estimatedStartDate', 'workEffortId'])
+    } catch (IllegalArgumentException e) {
+        return ServiceUtil.returnError(e.message)
     }
 
     EntityQuery productionRunQuery = 
from('WorkEffortAndGoods').where(whereCondition)
     long totalCount = productionRunQuery.queryCount()
-    int lowIndex = (pageIndex * pageSize) + 1
-    int highIndex = (pageIndex + 1) * pageSize
-    List productionRunValues = []
-    EntityListIterator iterator = null
-    try {
-        iterator = productionRunQuery
-                .orderBy(orderBy)
-                .cursorScrollInsensitive()
-                .maxRows(highIndex)
-                .queryIterator()
-        productionRunValues = iterator.getPartialList(lowIndex, pageSize) ?: []
-    } finally {
-        iterator?.close()
-    }
+    List productionRunValues = productionRunQuery.orderBy(orderBy)
+            .queryPagedList(queryOptions.pageIndex, 
queryOptions.pageSize).getData()
 
     // Batch-load display data once, then map each row in memory to avoid N+1 
lookups.
     Map lookups = buildProductionRunLookups([], productionRunValues, [], [], 
[], [])
@@ -119,8 +119,7 @@ Map findProductionRuns() {
         productionRunSummaryMap(productionRun, lookups)
     }
 
-    return success(pageIndex: pageIndex, pageSize: pageSize, totalCount: 
totalCount,
-            hasNext: ((pageIndex + 1) * pageSize) < totalCount, 
productionRuns: productionRuns)
+    return success(RestApiUtil.getPagedResult('productionRuns', 
productionRuns, queryOptions, totalCount, null))
 }
 
 Map getProductionRunDetails() {
@@ -197,24 +196,6 @@ Map getProductionRunDetails() {
     return success(detail)
 }
 
-List resolveProductionRunSort(Object sortValue) {
-    Map sortFieldMap = [
-            estimatedStartDate: 'estimatedStartDate',
-            actualStartDate: 'actualStartDate',
-            status: 'currentStatusId',
-            productId: 'productId',
-            workEffortId: 'workEffortId'
-    ]
-    if (!sortValue) {
-        return ['-estimatedStartDate', 'workEffortId']
-    }
-    String sort = sortValue
-    boolean descending = sort.startsWith('-')
-    String sortKey = descending ? sort.substring(1) : sort
-    String entityField = sortFieldMap[sortKey]
-    return entityField ? [(descending ? '-' : '') + entityField, 
'workEffortId'] : []
-}
-
 Map buildProductionRunLookups(List productionRuns, List productionRunGoods, 
List tasks, List components,
                               List parties, List fixedAssets, List products = 
[]) {
     // Centralize related lookups so list/detail mapping can reuse one batch 
of reference data.
@@ -288,7 +269,7 @@ Map buildProductionRunLookups(List productionRuns, List 
productionRunGoods, List
     }
 
     // Once the ids are collected, load each reference entity once and reuse 
it everywhere below.
-    Map productMap = lookupById('Product', 'productId', productIds)
+    Map productMap = EntityUtil.lookupById(delegator, 'Product', 'productId', 
productIds)
     productMap.values().each { GenericValue product ->
         if (UtilValidate.isNotEmpty(product.quantityUomId)) {
             uomIds.add(product.quantityUomId)
@@ -297,25 +278,13 @@ Map buildProductionRunLookups(List productionRuns, List 
productionRunGoods, List
 
     return [
             products: productMap,
-            facilities: lookupById('Facility', 'facilityId', facilityIds),
-            statuses: lookupById('StatusItem', 'statusId', statusIds),
-            uoms: lookupById('Uom', 'uomId', uomIds),
-            roles: lookupById('RoleType', 'roleTypeId', roleTypeIds)
+            facilities: EntityUtil.lookupById(delegator, 'Facility', 
'facilityId', facilityIds),
+            statuses: EntityUtil.lookupById(delegator, 'StatusItem', 
'statusId', statusIds),
+            uoms: EntityUtil.lookupById(delegator, 'Uom', 'uomId', uomIds),
+            roles: EntityUtil.lookupById(delegator, 'RoleType', 'roleTypeId', 
roleTypeIds)
     ]
 }
 
-Map lookupById(String entityName, String fieldName, Collection ids) {
-    List idList = new ArrayList(ids.findAll { UtilValidate.isNotEmpty(it) 
}.unique())
-    if (!idList) {
-        return [:]
-    }
-    return from(entityName)
-            .where(EntityCondition.makeCondition(fieldName, EntityOperator.IN, 
idList))
-            .cache(true)
-            .queryList()
-            .collectEntries { GenericValue value -> 
[(value.getString(fieldName)): value] }
-}
-
 Map productionRunSummaryMap(GenericValue productionRun, Map lookups) {
     // List rows are projected into API-friendly values with names and 
descriptions resolved server-side.
     GenericValue product = lookups.products[productionRun.productId]
@@ -326,14 +295,14 @@ Map productionRunSummaryMap(GenericValue productionRun, 
Map lookups) {
             productionRunId: productionRun.workEffortId,
             workEffortId: productionRun.workEffortId,
             productId: productionRun.productId,
-            productName: productDisplayName(product),
+            productName: ManufacturingServiceUtil.displayProductName(product),
             facilityId: productionRun.facilityId,
-            facilityName: facility?.facilityName,
+            facilityName: 
ManufacturingServiceUtil.displayFacilityName(facility),
             statusId: productionRun.currentStatusId,
-            statusDescription: status?.description,
+            statusDescription: 
ManufacturingServiceUtil.displayStatusDescription(status),
             quantity: productionRun.estimatedQuantity,
             quantityUomId: product?.quantityUomId,
-            quantityUomDescription: uom?.description,
+            quantityUomDescription: 
ManufacturingServiceUtil.displayUomDescription(uom),
             estimatedStartDate: productionRun.estimatedStartDate,
             actualStartDate: productionRun.actualStartDate,
             estimatedCompletionDate: productionRun.estimatedCompletionDate,
@@ -351,18 +320,18 @@ Map productionRunHeaderMap(GenericValue productionRun, 
GenericValue producedProd
             productionRunId: productionRun.workEffortId,
             workEffortId: productionRun.workEffortId,
             productId: product?.productId,
-            productName: productDisplayName(product),
+            productName: ManufacturingServiceUtil.displayProductName(product),
             facilityId: productionRun.facilityId,
-            facilityName: facility?.facilityName,
+            facilityName: 
ManufacturingServiceUtil.displayFacilityName(facility),
             statusId: productionRun.currentStatusId,
-            statusDescription: status?.description,
+            statusDescription: 
ManufacturingServiceUtil.displayStatusDescription(status),
             workEffortName: productionRun.workEffortName,
             description: productionRun.description,
             quantity: productionRunQuantity ?: productionRun.quantityToProduce,
             quantityProduced: productionRun.quantityProduced ?: 
BigDecimal.ZERO,
             quantityRejected: productionRun.quantityRejected ?: 
BigDecimal.ZERO,
             quantityUomId: product?.quantityUomId,
-            quantityUomDescription: uom?.description,
+            quantityUomDescription: 
ManufacturingServiceUtil.displayUomDescription(uom),
             estimatedStartDate: productionRun.estimatedStartDate,
             estimatedCompletionDate: productionRun.estimatedCompletionDate,
             actualStartDate: productionRun.actualStartDate,
@@ -381,9 +350,9 @@ Map taskMap(GenericValue task, String productionRunId, List 
parties, List fixedA
             workEffortName: task.workEffortName,
             description: task.description,
             statusId: task.currentStatusId,
-            statusDescription: status?.description,
+            statusDescription: 
ManufacturingServiceUtil.displayStatusDescription(status),
             facilityId: task.facilityId,
-            facilityName: facility?.facilityName,
+            facilityName: 
ManufacturingServiceUtil.displayFacilityName(facility),
             estimatedStartDate: task.estimatedStartDate,
             estimatedCompletionDate: task.estimatedCompletionDate,
             actualStartDate: task.actualStartDate,
@@ -405,14 +374,14 @@ Map componentMap(GenericValue component, GenericValue 
task, Map issuedQuantityBy
     BigDecimal issuedQuantity = 
issuedQuantityByTaskProduct[component.workEffortId + '::' + 
component.productId] ?: BigDecimal.ZERO
     return [
             componentProductId: component.productId,
-            componentProductName: productDisplayName(product),
+            componentProductName: 
ManufacturingServiceUtil.displayProductName(product),
             requiredQuantity: requiredQuantity,
             issuedQuantity: issuedQuantity,
             remainingQuantity: requiredQuantity - issuedQuantity,
             quantityUomId: product?.quantityUomId,
-            quantityUomDescription: uom?.description,
+            quantityUomDescription: 
ManufacturingServiceUtil.displayUomDescription(uom),
             statusId: component.statusId,
-            statusDescription: status?.description,
+            statusDescription: 
ManufacturingServiceUtil.displayStatusDescription(status),
             workEffortId: component.workEffortId,
             taskSequence: task?.priority,
             taskName: task?.workEffortName,
@@ -430,11 +399,11 @@ Map partyMap(GenericValue party, Map lookups) {
             partyId: party.partyId,
             partyName: PartyHelper.getPartyName(party),
             roleTypeId: party.roleTypeId,
-            roleTypeDescription: role?.description,
+            roleTypeDescription: 
ManufacturingServiceUtil.displayRoleTypeDescription(role),
             statusId: party.assignmentStatusId,
-            statusDescription: status?.description,
+            statusDescription: 
ManufacturingServiceUtil.displayStatusDescription(status),
             facilityId: party.facilityId,
-            facilityName: facility?.facilityName,
+            facilityName: 
ManufacturingServiceUtil.displayFacilityName(facility),
             fromDate: party.fromDate,
             thruDate: party.thruDate
     ]
@@ -447,11 +416,11 @@ Map fixedAssetMap(GenericValue fixedAsset, Map lookups) {
     return [
             workEffortId: fixedAsset.workEffortId,
             fixedAssetId: fixedAsset.fixedAssetId,
-            fixedAssetName: fixedAsset.fixedAssetName,
+            fixedAssetName: 
ManufacturingServiceUtil.displayFixedAssetName(fixedAsset),
             statusId: fixedAsset.statusId,
-            statusDescription: status?.description,
+            statusDescription: 
ManufacturingServiceUtil.displayStatusDescription(status),
             availabilityStatusId: fixedAsset.availabilityStatusId,
-            availabilityStatusDescription: availabilityStatus?.description,
+            availabilityStatusDescription: 
ManufacturingServiceUtil.displayStatusDescription(availabilityStatus),
             fromDate: fixedAsset.fromDate,
             thruDate: fixedAsset.thruDate
     ]
@@ -468,11 +437,3 @@ Map noteMap(GenericValue note) {
             internalNote: note.internalNote
     ]
 }
-
-String productDisplayName(GenericValue product) {
-    if (!product) {
-        return null
-    }
-    // Prefer the product display name, then fall back to internal name and 
finally the id.
-    return product.productName ?: product.internalName ?: product.productId
-}

Reply via email to