From a7cb09f15cb3d30cc77fabd4084489b21f0d10fa Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Thu, 24 Sep 2026 15:54:57 +0530 Subject: [PATCH] feat(fd): add lean FD maturity dashboard read endpoint New GET /v1/fixeddepositaccounts/maturity-dashboard: a narrow, purpose-built query for the FD Dashboard's maturity ladder (account/client/product identity, maturity date, deposit/maturity amounts) instead of routing through the general FixedDepositAccountsApiResource#retrieveAll, whose mapper joins six extra tables for audit/currency/tax fields this screen never shows. Adds the index migration (0571) the dashboard's WHERE clause needs -- neither m_savings_account.deposit_type_enum/status_enum nor m_deposit_account_term_and_preclosure.maturity_date had one before, so the endpoint would otherwise run a full table scan on top of being wide. Co-Authored-By: Claude Sonnet 5 --- ...edDepositMaturityDashboardApiResource.java | 96 +++++++++++++++++++ ..._deposit_type_status_and_maturity_date.xml | 36 +++++++ 2 files changed, 132 insertions(+) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/FixedDepositMaturityDashboardApiResource.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0571_index_savings_account_deposit_type_status_and_maturity_date.xml diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/FixedDepositMaturityDashboardApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/FixedDepositMaturityDashboardApiResource.java new file mode 100644 index 000000000..c1a1c8b51 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/FixedDepositMaturityDashboardApiResource.java @@ -0,0 +1,96 @@ +/** + * 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.portfolio.savings.api; + +import com.google.gson.Gson; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.portfolio.savings.DepositsApiConstants; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +/** + * FD Dashboard's data source: every ACTIVE Fixed Deposit with just the fields the maturity ladder + * needs (account/client/product identity, maturity date, deposit and maturity amounts). + * + * Deliberately not routed through {@link FixedDepositAccountsApiResource#retrieveAll}: that + * endpoint's mapper joins m_savings_account to m_client, m_group, m_staff, m_currency, + * m_code_value and FIVE separate m_appuser joins (submitted/rejected/withdrawn/approved/activated/ + * closed-by), plus a correlated subquery against m_savings_account_transaction per row -- all of it + * needed for the single-account and full-list screens it serves, none of it needed here. Neither + * m_savings_account.deposit_type_enum/status_enum nor + * m_deposit_account_term_and_preclosure.maturity_date had an index before this feature (confirmed + * directly against pg_indexes), so that query was a full table scan on top of being wide. This + * endpoint selects only what the dashboard renders, against the narrow, now-indexed WHERE clause + * this feature added (see migration 0571). + */ +@Path("/v1/fixeddepositaccounts/maturity-dashboard") +@Component +@Tag(name = "Fixed Deposit Accounts", description = "FD Dashboard: active FDs positioned against their maturity date.") +@RequiredArgsConstructor +public class FixedDepositMaturityDashboardApiResource { + + /** Fineract's DepositAccountType.FIXED_DEPOSIT. */ + private static final int FIXED_DEPOSIT = 200; + /** SavingsAccountStatusType.ACTIVE. */ + private static final int ACTIVE = 300; + + private final PlatformSecurityContext context; + private final JdbcTemplate jdbcTemplate; + private final Gson gson = new Gson(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + public String retrieve() { + // Same permission as the single-account/full-list FD endpoints -- this is a narrower read of + // the same resource, not a new one, so no new permission is introduced for it. + this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME); + + final List> rows = this.jdbcTemplate.queryForList(""" + SELECT sa.id AS "id", + sa.account_no AS "accountNo", + sa.client_id AS "clientId", + c.display_name AS "clientName", + sp.name AS "productName", + sp.fd_type AS "fdType", + TO_CHAR(datp.maturity_date, 'YYYY-MM-DD') AS "maturityDate", + datp.deposit_amount AS "depositAmount", + datp.maturity_amount AS "maturityAmount", + datp.on_account_closure_enum AS "onClosureType", + sa.linked_account_id AS "linkedAccountId", + dptp.renewal_grace_period_days AS "renewalGracePeriodDays" + FROM m_savings_account sa + JOIN m_deposit_account_term_and_preclosure datp ON datp.savings_account_id = sa.id + JOIN m_savings_product sp ON sp.id = sa.product_id + LEFT JOIN m_deposit_product_term_and_preclosure dptp ON dptp.savings_product_id = sp.id + LEFT JOIN m_client c ON c.id = sa.client_id + WHERE sa.deposit_type_enum = ? AND sa.status_enum = ? + ORDER BY datp.maturity_date ASC + """, FIXED_DEPOSIT, ACTIVE); + + return this.gson.toJson(rows); + } +} diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0571_index_savings_account_deposit_type_status_and_maturity_date.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0571_index_savings_account_deposit_type_status_and_maturity_date.xml new file mode 100644 index 000000000..4bf61c077 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0571_index_savings_account_deposit_type_status_and_maturity_date.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + +