Merge branch 'origin/dev' into dev
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s

This commit is contained in:
2026-08-25 11:32:55 +05:30
61 changed files with 2828 additions and 312 deletions
@@ -2897,6 +2897,15 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder paymentFixedDepositAccount(final Long accountId) {
this.actionName = "PAYMENT";
this.entityName = "FIXEDDEPOSITACCOUNT";
this.entityId = accountId;
this.savingsId = accountId;
this.href = "/fixeddepositaccounts/" + accountId + "?command=payment";
return this;
}
public CommandWrapperBuilder fixedDepositAccountInterestCalculation(final Long accountId) {
this.actionName = "CALCULATEINTEREST";
this.entityName = "FIXEDDEPOSITACCOUNT";
@@ -130,6 +130,19 @@ public class DataValidatorBuilder {
this.dataValidationErrors.add(error);
}
/**
* Like {@link #failWithCode(String, Object...)}, but the caller's own message is what's actually shown to the
* user (that method always renders the generic "Failed data validation due to: {errorCode}." regardless of the
* args passed to it).
*/
public void failWithCodeAndMessage(final String errorCode, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
String validationErrorCode = "validation.msg." + this.resource + "." + this.parameter + "." + errorCode;
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode, defaultUserMessage, this.parameter,
this.value, defaultUserMessageArgs);
this.dataValidationErrors.add(error);
}
public void failWithCodeNoParameterAddedToErrorCode(final String errorCode, final Object... defaultUserMessageArgs) {
String validationErrorCode = "validation.msg." + this.resource + "." + errorCode;
String defaultEnglishMessage = "Failed data validation due to: " + errorCode + ".";
@@ -72,11 +72,26 @@ public class SavingsAccountSummaryData {
// differentiate deposit accounts Savings, FD and RD accounts
private final EnumOptionData depositType;
/**
* For a Fixed Deposit, the stable customer-facing FD number: the account number of the first account in the renewal
* chain, which stays the same across every renewal even though each term is its own account row. Equals accountNo
* for an FD that has never been renewed, and is null for non-deposit accounts.
*/
private final String fdAccountNumber;
/**
* Which renewal cycle of that FD this row is: 0 for the originally opened account, 1 for the first renewal, and so
* on. Lets a client's account list collapse a renewal chain into one FD instead of listing each term separately.
*/
private final Integer renewalCycleNo;
public SavingsAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId,
final String productName, final String shortProductName, final SavingsAccountStatusEnumData status, final CurrencyData currency,
final BigDecimal accountBalance, final BigDecimal onHoldFunds, final BigDecimal savingsAmountOnHold,
final BigDecimal availableBalance, final EnumOptionData accountType, final SavingsAccountApplicationTimelineData timeline,
final EnumOptionData depositType, final SavingsAccountSubStatusEnumData subStatus, final LocalDate lastActiveTransactionDate) {
final EnumOptionData depositType, final SavingsAccountSubStatusEnumData subStatus, final LocalDate lastActiveTransactionDate,
final String fdAccountNumber, final Integer renewalCycleNo) {
this.fdAccountNumber = fdAccountNumber;
this.renewalCycleNo = renewalCycleNo;
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
@@ -31,7 +31,8 @@ public enum DepositAccountOnClosureType {
WITHDRAW_DEPOSIT(100, "depositAccountClosureType.withdrawDeposit"), //
TRANSFER_TO_SAVINGS(200, "depositAccountClosureType.transferToSavings"), //
REINVEST_PRINCIPAL_AND_INTEREST(300, "depositAccountClosureType.reinvestPrincipalAndInterest"), //
REINVEST_PRINCIPAL_ONLY(400, "depositAccountClosureType.reinvestPrincipalOnly"); //
REINVEST_PRINCIPAL_ONLY(400, "depositAccountClosureType.reinvestPrincipalOnly"), //
PARTIAL_REINVEST(500, "depositAccountClosureType.partialReinvest"); //
private final Integer value;
private final String code;
@@ -51,6 +52,7 @@ public enum DepositAccountOnClosureType {
case 200 -> TRANSFER_TO_SAVINGS;
case 300 -> REINVEST_PRINCIPAL_AND_INTEREST;
case 400 -> REINVEST_PRINCIPAL_ONLY;
case 500 -> PARTIAL_REINVEST;
default -> INVALID;
};
}
@@ -85,6 +87,10 @@ public enum DepositAccountOnClosureType {
return this.equals(INVALID);
}
public boolean isPartialReinvest() {
return this.equals(PARTIAL_REINVEST);
}
// TODO: do we really need this?!?
public static Object[] integerValues() {
return Arrays.stream(values()).filter(value -> !INVALID.equals(value)).map(value -> value.value).toList().toArray();
@@ -208,6 +208,7 @@ public final class DepositsApiConstants {
public static final String onAccountClosureIdParamName = "onAccountClosureId";
public static final String transferDescriptionParamName = "transferDescription";
public static final String toSavingsAccountIdParamName = "toSavingsAccountId";
public static final String reinvestAmountParamName = "reinvestAmount";
public static final String savingsAccounts = "savingsAccounts";
public static final String maturityInstructionIdParamName = "maturityInstructionId";
public static final String transferToSavingsIdParamName = "transferToSavingsId";
@@ -797,6 +797,10 @@ public final class SavingsEnumerations {
optionData = new EnumOptionData(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getValue().longValue(),
DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getCode(), "Re-Invest Principal Only");
break;
case PARTIAL_REINVEST:
optionData = new EnumOptionData(DepositAccountOnClosureType.PARTIAL_REINVEST.getValue().longValue(),
DepositAccountOnClosureType.PARTIAL_REINVEST.getCode(), "Partial Re-Invest");
break;
}
return optionData;
}