Merge branch 'feature/dds' into merge/dds-into-dev-latest
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 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 Documentation build / build (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 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 7 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 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 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 - 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 - MariaDB / test (test-core-5) (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

Brings the DDS (Daily Deposit Scheme) backend from feature/dds onto the
current dev-latest:
- Agent settlement (Agent Tran), Close Party account, reports
  (Supplementary-DDS, Master Data Export, Close Register), Party-Savings
  link, savings product category field
- Collection entries held before posting, per-agent GL accounts,
  maturity auto close
- Vouchers, maker-checker approvals, payout & close, Sundry GL, closure
  tab and fixes
- POST_DDS_INTEREST job

No content conflicts. No dev-latest file is removed; only three
dev-latest lines are extended with a DDS condition:
- CommandSourceService: DDS entities join ROLE in the maker-checker
  entity check (isDdsEntity).
- PortfolioCommandSourceWritePlatformServiceImpl: a DDS entry can never
  be approved by the user who made it, even a checker super user. The
  rule for every other entity is unchanged.
- SavingsProductDataValidator: productCategory accepted as a savings
  product parameter.

Files changed on both branches and auto-merged: CommandWrapperBuilder,
CommandSourceService, JobName, SavingsApiConstants, SavingsProductData,
SavingsProduct, FixedDepositProductData, RecurringDepositProductData,
DepositProductReadPlatformServiceImpl. Checked for duplicate methods,
fields and enum names.

Liquibase: 55 DDS changelogs (0470-0525) included as one block at the
end of changelog-tenant.xml. Their numbers overlap dev-latest's files
but no file name is shared, and Liquibase tracks changesets by file
path, so nothing clashes. They keep their names so databases that
already ran them do not run them again. No permission code, job name
or configuration name is inserted by both branches.

Verified with :fineract-provider:compileJava. The full compileJava
fails only in fineract-client (generated CKYC, committee meeting and
election APIs), which feature/dds does not touch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 15:11:14 +05:30
co-authored by Claude Opus 5.5
228 changed files with 23739 additions and 3 deletions
@@ -498,6 +498,7 @@ public final class FixedDepositProductData extends DepositProductData {
private Boolean allowDebitTxnAfterDueDate;
private Boolean accountClosedZeroBalanceInDayClosing;
private String balanceFrequency;
private String productCategory;
private Boolean isPendingApproval;
private Integer renewalGracePeriodDays;
@@ -546,6 +547,7 @@ public final class FixedDepositProductData extends DepositProductData {
public void setAllowDebitTxnAfterDueDate(Boolean v) { this.allowDebitTxnAfterDueDate = v; }
public void setAccountClosedZeroBalanceInDayClosing(Boolean v) { this.accountClosedZeroBalanceInDayClosing = v; }
public void setBalanceFrequency(String v) { this.balanceFrequency = v; }
public void setProductCategory(String v) { this.productCategory = v; }
public void setIsPendingApproval(Boolean v) { this.isPendingApproval = v; }
public void setRenewalGracePeriodDays(Integer v) { this.renewalGracePeriodDays = v; }
@@ -610,6 +612,7 @@ public final class FixedDepositProductData extends DepositProductData {
this.allowDebitTxnAfterDueDate = src.allowDebitTxnAfterDueDate;
this.accountClosedZeroBalanceInDayClosing = src.accountClosedZeroBalanceInDayClosing;
this.balanceFrequency = src.balanceFrequency;
this.productCategory = src.productCategory;
this.withdrawalFeeForTransfers = src.withdrawalFeeForTransfers;
this.renewalGracePeriodDays = src.renewalGracePeriodDays;
this.setOtherGlMappings(src.getOtherGlMappings());
@@ -83,6 +83,7 @@ public final class RecurringDepositProductData extends DepositProductData {
private Boolean enableLockinPeriod;
private String balanceFrequency;
private Boolean accountClosedZeroBalanceInDayClosing;
private String productCategory;
private Boolean balanceSwapOption;
private Boolean msme;
private Boolean autoDebitFacility;
@@ -130,6 +131,7 @@ public final class RecurringDepositProductData extends DepositProductData {
public void setEnableLockinPeriod(Boolean v) { this.enableLockinPeriod = v; }
public void setBalanceFrequency(String v) { this.balanceFrequency = v; }
public void setAccountClosedZeroBalanceInDayClosing(Boolean v) { this.accountClosedZeroBalanceInDayClosing = v; }
public void setProductCategory(String v) { this.productCategory = v; }
public void setBalanceSwapOption(Boolean v) { this.balanceSwapOption = v; }
public void setMsme(Boolean v) { this.msme = v; }
public void setAutoDebitFacility(Boolean v) { this.autoDebitFacility = v; }
@@ -177,6 +179,7 @@ public final class RecurringDepositProductData extends DepositProductData {
this.enableLockinPeriod = src.enableLockinPeriod;
this.balanceFrequency = src.balanceFrequency;
this.accountClosedZeroBalanceInDayClosing = src.accountClosedZeroBalanceInDayClosing;
this.productCategory = src.productCategory;
this.balanceSwapOption = src.balanceSwapOption;
this.msme = src.msme;
this.autoDebitFacility = src.autoDebitFacility;
@@ -125,7 +125,7 @@ public class SavingsProductDataValidator {
SavingsApiConstants.depositLimitKycParamName,
// Additional custom fields
SavingsApiConstants.balanceFrequencyParamName, SavingsApiConstants.accountClosedZeroBalanceInDayClosingParamName,
SavingsApiConstants.glAccountIdParamName));
SavingsApiConstants.glAccountIdParamName, SavingsApiConstants.productCategoryParamName));
public void validateForCreate(final String json) {
@@ -297,6 +297,9 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
@Column(name = "account_closed_zero_balance_in_day_closing")
private Boolean accountClosedZeroBalanceInDayClosing = false;
@Column(name = "product_category", length = 50)
private String productCategory;
// RD-specific flags
@Column(name = "balance_swap_option")
private boolean balanceSwapOption = false;
@@ -912,6 +915,13 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
this.accountClosedZeroBalanceInDayClosing = newValue;
}
// Update productCategory
if (command.isChangeInStringParameterNamed("productCategory", this.productCategory)) {
final String newValue = command.stringValueOfParameterNamed("productCategory");
actualChanges.put("productCategory", newValue);
this.productCategory = newValue;
}
// Update RD-specific flags (0270 migration fields)
if (command.isChangeInBooleanParameterNamed("balanceSwapOption", this.balanceSwapOption)) {
this.balanceSwapOption = command.booleanPrimitiveValueOfParameterNamed("balanceSwapOption");
@@ -1314,6 +1324,14 @@ public class SavingsProduct extends AbstractPersistableCustom<Long> {
this.accountClosedZeroBalanceInDayClosing = accountClosedZeroBalanceInDayClosing;
}
public String getProductCategory() {
return productCategory;
}
public void setProductCategory(String productCategory) {
this.productCategory = productCategory;
}
public boolean isBalanceSwapOption() { return balanceSwapOption; }
public void setBalanceSwapOption(boolean v) { this.balanceSwapOption = v; }
@@ -282,6 +282,9 @@ public class SavingsProductAssembler {
if (command.parameterExists("accountClosedZeroBalanceInDayClosing")) {
product.setAccountClosedZeroBalanceInDayClosing(command.booleanObjectValueOfParameterNamed("accountClosedZeroBalanceInDayClosing"));
}
if (command.parameterExists("productCategory")) {
product.setProductCategory(command.stringValueOfParameterNamed("productCategory"));
}
return product;
}