diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java index d34c559719a..73fb2ef3dfe 100644 --- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java +++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java @@ -195,7 +195,7 @@ private static final class ProvisioningEntryDataMapperWithSumReserved implements entry.id, journal_entry_created, createdby_id, created_date, created.username as createduser, lastmodifiedby_id, modified.username as modifieduser, lastmodified_date, SUM(reserved.reseve_amount) as totalreserved from m_provisioning_history entry - JOIN m_loanproduct_provisioning_entry reserved on entry.id = reserved.history_id + LEFT JOIN m_loanproduct_provisioning_entry reserved on entry.id = reserved.history_id left JOIN m_appuser created ON created.id = entry.createdby_id left JOIN m_appuser modified ON modified.id = entry.lastmodifiedby_id\s"""; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignProvisioningEntryTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignProvisioningEntryTest.java new file mode 100644 index 00000000000..a43d2c9bb59 --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignProvisioningEntryTest.java @@ -0,0 +1,91 @@ +/** + * 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.fineract.integrationtests.client.feign.tests; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.google.gson.Gson; +import io.restassured.builder.RequestSpecBuilder; +import io.restassured.builder.ResponseSpecBuilder; +import io.restassured.http.ContentType; +import io.restassured.specification.RequestSpecification; +import io.restassured.specification.ResponseSpecification; +import java.util.ArrayList; +import java.util.Map; +import org.apache.fineract.client.models.PostProvisioningEntriesResponse; +import org.apache.fineract.client.models.ProvisionEntryRequest; +import org.apache.fineract.client.models.ProvisioningEntryData; +import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase; +import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.accounting.Account; +import org.apache.fineract.integrationtests.common.accounting.AccountHelper; +import org.apache.fineract.integrationtests.common.provisioning.ProvisioningHelper; +import org.apache.fineract.integrationtests.common.provisioning.ProvisioningTransactionHelper; +import org.junit.jupiter.api.Test; + +public class FeignProvisioningEntryTest extends FeignLoanTestBase { + + @Test + public void testRetrieveProvisioningEntryWithNoActiveLoansDoesNotReturn500() { + // Set up REST spec for legacy helpers + Utils.initializeRESTAssured(); + RequestSpecification requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + requestSpec.header("Fineract-Platform-TenantId", "default"); + ResponseSpecification responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + + // Create a loan product to satisfy criteria validation, + // but do NOT disburse any loans — so m_loanproduct_provisioning_entry + // will be empty, which is the scenario that used to cause HTTP 500. + Long loanProductId = createLoanProduct(onePeriod30DaysNoInterest()); + assertNotNull(loanProductId); + + ArrayList loanProducts = new ArrayList<>(); + loanProducts.add(loanProductId.intValue()); + + ProvisioningTransactionHelper transactionHelper = new ProvisioningTransactionHelper(requestSpec, responseSpec); + AccountHelper accountHelper = new AccountHelper(requestSpec, responseSpec); + ArrayList categories = transactionHelper.retrieveAllProvisioningCategories(); + Account liability = accountHelper.createLiabilityAccount(); + Account expense = accountHelper.createExpenseAccount(); + + Map requestCriteria = ProvisioningHelper.createProvisioingCriteriaJson(loanProducts, categories, liability, expense); + String criteriaJson = new Gson().toJson(requestCriteria); + Integer criteriaId = transactionHelper.createProvisioningCriteria(criteriaJson); + assertNotNull(criteriaId); + + // Create the provisioning entry + String today = Utils.dateFormatter.format(Utils.getLocalDateOfTenant()); + ProvisionEntryRequest request = new ProvisionEntryRequest().date(today).dateFormat("dd MMMM yyyy").locale("en") + .createjournalentries(false); + PostProvisioningEntriesResponse created = ok(() -> fineractClient().provisioningEntries().createProvisioningEntries(request)); + assertNotNull(created); + assertNotNull(created.getResourceId()); + + // This GET used to throw 500 when no loans were disbursed (empty join result). + // The LEFT JOIN fix ensures it now returns 200 with totalReserved = null/0. + ProvisioningEntryData entry = ok( + () -> fineractClient().provisioningEntries().retrieveOneProvisioningEntry(created.getResourceId())); + assertNotNull(entry); + assertNotNull(entry.getId()); + + // Cleanup + transactionHelper.deleteProvisioningCriteria(criteriaId); + } +}