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/common/ProvisioningIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ProvisioningIntegrationTest.java index 2fac87b68d0..25c8d45c702 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ProvisioningIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ProvisioningIntegrationTest.java @@ -26,12 +26,20 @@ import io.restassured.specification.RequestSpecification; import io.restassured.specification.ResponseSpecification; import java.math.BigDecimal; +import java.text.DateFormat; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import org.apache.fineract.client.models.ProvisionEntryRequest; +import org.apache.fineract.client.models.ProvisioningEntryData; +import org.apache.fineract.client.util.Calls; +import org.apache.fineract.integrationtests.common.FineractClientHelper; import org.apache.fineract.integrationtests.common.accounting.Account; import org.apache.fineract.integrationtests.common.accounting.AccountHelper; import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; @@ -151,6 +159,45 @@ public void testCreateProvisioningCriteria() { Assertions.assertTrue(((ArrayList) provisioningEntry.get("pageItems")).size() > 0); } + @Test + public void testGetProvisioningEntryWithNoActiveLoansShouldReturn200() { + // FINERACT-2530: GET /provisioningentries/{id} threw EmptyResultDataAccessException (HTTP 500) + // when no rows existed in m_loanproduct_provisioning_entry for the given history ID. + ProvisioningTransactionHelper transactionHelper = new ProvisioningTransactionHelper(requestSpec, responseSpec); + + // Create a loan product but do NOT disburse any loans so m_loanproduct_provisioning_entry stays empty + final Integer loanProductID = createLoanProduct(false, NONE); + Assertions.assertNotNull(loanProductID); + ArrayList loanProducts = new ArrayList<>(); + loanProducts.add(loanProductID); + + ArrayList categories = transactionHelper.retrieveAllProvisioningCategories(); + Assertions.assertTrue(categories.size() > 0); + Account liability = accountHelper.createLiabilityAccount(); + Account expense = accountHelper.createExpenseAccount(); + Map requestCriteria = ProvisioningHelper.createProvisioingCriteriaJson(loanProducts, categories, liability, expense); + Integer criteriaId = transactionHelper.createProvisioningCriteria(new Gson().toJson(requestCriteria)); + Assertions.assertNotNull(criteriaId); + + // Create provisioning entry using fineract-client — no active loans so m_loanproduct_provisioning_entry is empty + DateFormat simple = new SimpleDateFormat("dd MMMM yyyy", Locale.US); + String date = simple.format(Date.from(Utils.getLocalDateOfTenant().atStartOfDay(Utils.getZoneIdOfTenant()).toInstant())); + Long entryId = Calls.ok(FineractClientHelper.getFineractClient().provisioningEntries + .createProvisioningEntries(new ProvisionEntryRequest() + .date(date).locale("en").dateFormat("dd MMMM yyyy").createjournalentries("false"))) + .getResourceId(); + Assertions.assertNotNull(entryId); + + // Before fix: INNER JOIN -> 0 rows -> EmptyResultDataAccessException -> HTTP 500 + // After fix: LEFT JOIN -> 1 row with totalReserved=NULL -> HTTP 200 + ProvisioningEntryData entry = Calls.ok(FineractClientHelper.getFineractClient().provisioningEntries + .retrieveProvisioningEntry(entryId)); + Assertions.assertNotNull(entry); + Assertions.assertNotNull(entry.getId()); + + transactionHelper.deleteProvisioningCriteria(criteriaId); + } + private HashMap collaterals(Integer collateralId, BigDecimal quantity) { HashMap collateral = new HashMap(2); collateral.put("clientCollateralId", collateralId.toString());