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 318e06c152eecde932f7fda2182d07c859945ffd Author: diveshdut <[email protected]> AuthorDate: Thu Aug 13 15:19:10 2026 +0530 OFBIZ-13422 Align production run lists with REST query helpers Adds framework-compatible list metadata to the production run query service contract and wires findProductionRuns to resolve public sort expressions into EntityQuery order-by fields. The service now preserves pagination links when invoked through REST, rejects unsupported sort fields before returning empty result sets, and has focused tests for sort mapping, default ordering, unsupported sort handling, and pagination navigation metadata. --- .../servicedef/services_production_run.xml | 3 + .../jobshopmgt/ProductionRunQueryServices.groovy | 42 ++++++---- .../ProductionRunQueryServicesTests.groovy | 98 ++++++++++++++++++++++ 3 files changed, 125 insertions(+), 18 deletions(-) diff --git a/applications/manufacturing/servicedef/services_production_run.xml b/applications/manufacturing/servicedef/services_production_run.xml index f54d921c45..935ff21175 100644 --- a/applications/manufacturing/servicedef/services_production_run.xml +++ b/applications/manufacturing/servicedef/services_production_run.xml @@ -45,6 +45,9 @@ under the License. <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="previousPageCount" type="Long" mode="OUT" optional="true"/> + <attribute name="nextPageCount" type="Long" mode="OUT" optional="true"/> + <attribute name="links" type="Map" mode="OUT" optional="true"/> <attribute name="productionRuns" type="List" mode="OUT" optional="false"/> </service> <service name="getProductionRunDetails" engine="groovy" 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 aaaa95786b..aab715e27d 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 @@ -38,6 +38,12 @@ Map findProductionRuns() { return ServiceUtil.returnError(e.message) } Map filters = queryOptions.filters + List orderBy + try { + orderBy = productionRunOrderBy(queryOptions.sort) + } catch (IllegalArgumentException e) { + return ServiceUtil.returnError(e.message) + } String productionRunId = filters.productionRunId ?: filters.workEffortId List conditions = [ @@ -85,28 +91,13 @@ Map findProductionRuns() { Set productIds = EntityUtil.searchIds(delegator, 'Product', 'productId', ['productId', 'productName', 'internalName'], filters.productName, 500) if (!productIds) { - return success(RestApiUtil.getPagedResult('productionRuns', [], queryOptions, 0L, null)) + return success(RestApiUtil.getPagedResult('productionRuns', [], queryOptions, 0L, + RestApiUtil.getRelativeRequestPath(binding))) } conditions.add(EntityCondition.makeCondition('productId', EntityOperator.IN, productIds as List)) } EntityCondition whereCondition = EntityCondition.makeCondition(conditions, EntityOperator.AND) - 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() @@ -119,7 +110,8 @@ Map findProductionRuns() { productionRunSummaryMap(productionRun, lookups) } - return success(RestApiUtil.getPagedResult('productionRuns', productionRuns, queryOptions, totalCount, null)) + return success(RestApiUtil.getPagedResult('productionRuns', productionRuns, queryOptions, totalCount, + RestApiUtil.getRelativeRequestPath(binding))) } Map getProductionRunDetails() { @@ -285,6 +277,20 @@ Map buildProductionRunLookups(List productionRuns, List productionRunGoods, List ] } +List productionRunOrderBy(String sortExpression) { + return RestApiUtil.resolveOrderBy(sortExpression, [ + estimatedStartDate: 'estimatedStartDate', + actualStartDate: 'actualStartDate', + status: 'currentStatusId', + currentStatusId: 'currentStatusId', + productId: 'productId', + facilityId: 'facilityId', + productionRunId: 'workEffortId', + workEffortId: 'workEffortId', + workEffortName: 'workEffortName' + ], ['-estimatedStartDate', 'workEffortId']) +} + 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] diff --git a/applications/manufacturing/src/test/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunQueryServicesTests.groovy b/applications/manufacturing/src/test/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunQueryServicesTests.groovy new file mode 100644 index 0000000000..7dfbed1966 --- /dev/null +++ b/applications/manufacturing/src/test/groovy/org/apache/ofbiz/manufacturing/jobshopmgt/ProductionRunQueryServicesTests.groovy @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ofbiz.manufacturing.jobshopmgt + +import static org.junit.jupiter.api.Assertions.assertThrows + +import org.apache.ofbiz.service.ServiceUtil +import org.apache.ofbiz.ws.rs.util.RestApiUtil +import org.apache.ofbiz.ws.rs.util.RestQueryOptions +import org.junit.jupiter.api.Test +import org.springframework.mock.web.MockHttpServletRequest + +class ProductionRunQueryServicesTests { + + @Test + void testProductionRunSortMapsFrameworkFields() { + ProductionRunQueryServices services = new ProductionRunQueryServices() + + assert services.productionRunOrderBy('-status') == ['-currentStatusId', '-estimatedStartDate', 'workEffortId'] + assert services.productionRunOrderBy('productionRunId') == ['workEffortId', '-estimatedStartDate'] + } + + @Test + void testProductionRunDefaultSortMatchesContract() { + ProductionRunQueryServices services = new ProductionRunQueryServices() + + assert services.productionRunOrderBy(null) == ['-estimatedStartDate', 'workEffortId'] + } + + @Test + void testProductionRunRejectsUnsupportedSortField() { + ProductionRunQueryServices services = new ProductionRunQueryServices() + + IllegalArgumentException exception = assertThrows(IllegalArgumentException) { + services.productionRunOrderBy('productName') + } + assert exception.message == 'Unsupported sort field: productName' + } + + @Test + void testFindProductionRunsRejectsUnsupportedSortBeforeEmptyProductNameResults() { + ProductionRunQueryServices services = new ProductionRunQueryServices() + services.binding.setVariable('parameters', [sort: 'productName', productName: 'NO_MATCH']) + + Map result = services.findProductionRuns() + + assert ServiceUtil.isError(result) + assert ServiceUtil.getErrorMessage(result) == 'Unsupported sort field: productName' + } + + @Test + void testFrameworkRequestPathReturnsNullWithoutRequestBinding() { + ProductionRunQueryServices services = new ProductionRunQueryServices() + + assert RestApiUtil.getRelativeRequestPath(services.binding) == null + } + + @Test + void testFrameworkNavigationMetadataAndLinksUseRequestPath() { + ProductionRunQueryServices services = new ProductionRunQueryServices() + MockHttpServletRequest request = new MockHttpServletRequest() + request.setRequestURI('/rest/manufacturing/production-runs') + request.setQueryString('pageIndex=1&pageSize=1&statusId=PRUN_CREATED') + services.binding.setVariable('request', request) + RestQueryOptions queryOptions = RestQueryOptions.fromParameters([ + pageIndex: 1, + pageSize: 1, + statusId: 'PRUN_CREATED' + ]) + + Map result = RestApiUtil.getPagedResult('productionRuns', [], queryOptions, 3L, + RestApiUtil.getRelativeRequestPath(services.binding)) + + assert result.previousPageCount == 1L + assert result.nextPageCount == 1L + assert result.links instanceof Map + assert result.links.self.href == '/rest/manufacturing/production-runs?statusId=PRUN_CREATED&pageIndex=1&pageSize=1' + assert result.links.prev.href == '/rest/manufacturing/production-runs?statusId=PRUN_CREATED&pageIndex=0&pageSize=1' + assert result.links.next.href == '/rest/manufacturing/production-runs?statusId=PRUN_CREATED&pageIndex=2&pageSize=1' + } + +}

