rd account save api update

This commit is contained in:
Nitesh Yadav
2026-04-30 16:12:17 +05:30
parent 045437fcb8
commit be220d8778
17 changed files with 64864 additions and 95772 deletions
@@ -60,5 +60,8 @@ public class CurrentAccountJointHolder extends AbstractPersistableCustom<Long> {
@Column(name = "is_primary", nullable = false)
private boolean primary = false;
@Column(name = "is_mandatory", nullable = false)
private boolean mandatory = false;
public CurrentAccountJointHolder() {}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -230,12 +230,14 @@ public class RecurringDepositAccount extends SavingsAccount {
}
final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount();
BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate,
this.client);
BigDecimal applicableInterestRate = BigDecimal.ZERO;
if (this.chart != null) {
applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate, this.client);
if (applyPreMaturePenalty) {
applicableInterestRate = applicableInterestRate.subtract(penalInterest);
applicableInterestRate = applicableInterestRate.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : applicableInterestRate;
if (applyPreMaturePenalty) {
applicableInterestRate = applicableInterestRate.subtract(penalInterest);
applicableInterestRate = applicableInterestRate.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : applicableInterestRate;
}
}
this.nominalAnnualInterestRate = applicableInterestRate;
@@ -1070,10 +1072,8 @@ public class RecurringDepositAccount extends SavingsAccount {
.failWithCodeNoParameterAddedToErrorCode("no.applicable.interest.rate.is.found.based.on.amount.and.deposit.period");
}
} else if (this.nominalAnnualInterestRate == null || this.nominalAnnualInterestRate.compareTo(BigDecimal.ZERO) == 0) {
baseDataValidator.reset().parameter(DepositsApiConstants.nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate)
.failWithCodeNoParameterAddedToErrorCode("interest.chart.or.nominal.interest.rate.required");
}
// CBS: chart is optional — CBS base interest rate chart is used instead
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
@@ -367,7 +367,7 @@ public class SavingsAccountAssembler {
private static final Gson GSON = new Gson();
private void applyCbsFields(final JsonCommand command, final JsonElement element, final SavingsAccount account) {
public void applyCbsFields(final JsonCommand command, final JsonElement element, final SavingsAccount account) {
// Product classification
final String cbsAccountType = this.fromApiJsonHelper.extractStringNamed(accountTypeParamName, element);
if (cbsAccountType != null) account.setCbsAccountType(cbsAccountType);
@@ -458,8 +458,9 @@ public class SavingsAccountAssembler {
role = isPrimary ? "Primary Account Holder" : "Co-Holder";
}
final boolean isMandatory = h.has("isMandatory") && h.get("isMandatory").getAsBoolean();
holders.add(new SavingsAccountJointHolder(account, clientId, cifNumber, displayName,
sharePercentage, signingAuthority, role, isPrimary));
sharePercentage, signingAuthority, role, isPrimary, isMandatory));
}
account.getJointHolders().clear();
account.getJointHolders().addAll(holders);
@@ -88,10 +88,12 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
out.put("groupId", row.get("group_id"));
out.put("productId", row.get("product_id"));
out.put("productName", row.get("product_name"));
out.put("currentAccountProductName", row.get("product_name"));
out.put("shortProductName", row.get("product_short_name"));
out.put("officeId", row.get("office_id"));
out.put("officeName", row.get("office_name"));
out.put("staffId", row.get("staff_id"));
out.put("fieldOfficerId", row.get("staff_id"));
out.put("fieldOfficerName", row.get("staff_name"));
// status
@@ -234,6 +236,12 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
};
s.put("code", "SavingsAccountSubStatusEnum." + value.replace(" ", ""));
s.put("value", value);
s.put("none", subStatusEnum == 0);
s.put("inactive", subStatusEnum == 100);
s.put("dormant", subStatusEnum == 200);
s.put("block", subStatusEnum == 300);
s.put("blockDebit", subStatusEnum == 400);
s.put("blockCredit", subStatusEnum == 500);
return s;
}
@@ -130,6 +130,9 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
if (json.has("introducerName") && !json.get("introducerName").isJsonNull()) {
account.setIntroducerName(json.get("introducerName").getAsString());
}
if (json.has("introducerCifNumber") && !json.get("introducerCifNumber").isJsonNull()) {
account.setIntroducerCifNumber(json.get("introducerCifNumber").getAsString());
}
// UI may send "introducerClientId" or "introducerId"
Long introducerClientId = getLong(json, "introducerClientId");
if (introducerClientId == null) introducerClientId = getLong(json, "introducerId");
@@ -324,6 +327,11 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
if (jo.has("displayName") && !jo.get("displayName").isJsonNull()) holder.setDisplayName(jo.get("displayName").getAsString());
if (jo.has("signingAuthority") && !jo.get("signingAuthority").isJsonNull()) holder.setSigningAuthority(jo.get("signingAuthority").getAsString());
if (jo.has("role") && !jo.get("role").isJsonNull()) holder.setRole(jo.get("role").getAsString());
if (jo.has("sharePercentage") && !jo.get("sharePercentage").isJsonNull()) {
try { holder.setSharePercentage(jo.get("sharePercentage").getAsBigDecimal()); } catch (Exception ignored) {}
}
if (jo.has("isPrimary") && !jo.get("isPrimary").isJsonNull()) holder.setPrimary(jo.get("isPrimary").getAsBoolean());
if (jo.has("isMandatory") && !jo.get("isMandatory").isJsonNull()) holder.setMandatory(jo.get("isMandatory").getAsBoolean());
account.getJointHolders().add(holder);
}
}
@@ -337,6 +345,10 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
if (cid != null) partner.setClientId(cid);
if (jo.has("clientCif") && !jo.get("clientCif").isJsonNull()) partner.setClientCif(jo.get("clientCif").getAsString());
if (jo.has("clientName") && !jo.get("clientName").isJsonNull()) partner.setClientName(jo.get("clientName").getAsString());
if (jo.has("sharePercentage") && !jo.get("sharePercentage").isJsonNull()) {
try { partner.setSharePercentage(jo.get("sharePercentage").getAsBigDecimal()); } catch (Exception ignored) {}
}
if (jo.has("isMandatory") && !jo.get("isMandatory").isJsonNull()) partner.setMandatory(jo.get("isMandatory").getAsBoolean());
account.getPartners().add(partner);
}
}
@@ -143,7 +143,7 @@ public class DepositAccountInterestRateChartReadPlatformServiceImpl implements D
return preparedStatement;
}, this.chartExtractor);
if (chartDatas == null || chartDatas.isEmpty()) {
throw new DepositAccountInterestRateChartNotFoundException(accountId);
return null;
}
return chartDatas.iterator().next();
@@ -275,10 +275,15 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
} else if (depositAccountType.isRecurringDeposit()) {
CalendarData calendar = this.calendarReadPlatformService.retrieveCollctionCalendarByEntity(accountId,
CalendarEntityType.SAVINGS.getValue());
final Integer frequency = calendar.getInterval() == -1 ? 1 : calendar.getInterval();
final CalendarFrequencyType calendarFrequencyType = CalendarFrequencyType.fromInt(calendar.getFrequency().getId().intValue());
final PeriodFrequencyType periodFrequencyType = CalendarFrequencyType.from(calendarFrequencyType);
final EnumOptionData frequencyType = CommonEnumerations.termFrequencyType(periodFrequencyType, "recurring.deposit.frequency.");
Integer frequency = 1;
EnumOptionData frequencyType = null;
if (calendar != null) {
frequency = calendar.getInterval() == -1 ? 1 : calendar.getInterval();
final CalendarFrequencyType calendarFrequencyType = CalendarFrequencyType
.fromInt(calendar.getFrequency().getId().intValue());
final PeriodFrequencyType periodFrequencyType = CalendarFrequencyType.from(calendarFrequencyType);
frequencyType = CommonEnumerations.termFrequencyType(periodFrequencyType, "recurring.deposit.frequency.");
}
depositAccount = RecurringDepositAccountData.withInterestChartAndRecurringDetails((RecurringDepositAccountData) depositAccount,
chart, frequency, frequencyType);
}
@@ -89,6 +89,7 @@ import org.apache.fineract.portfolio.savings.domain.RecurringDepositAccountRepos
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountCharge;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountChargeAssembler;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
@@ -121,6 +122,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
private final ConfigurationDomainService configurationDomainService;
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
private final BusinessEventNotifierService businessEventNotifierService;
private final SavingsAccountAssembler savingsAccountAssembler;
/*
* Guaranteed to throw an exception no matter what the data integrity issue is.
@@ -226,6 +228,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(command,
submittedBy, DepositAccountType.RECURRING_DEPOSIT);
this.savingsAccountAssembler.applyCbsFields(command, command.parsedJson(), account);
this.recurringDepositAccountRepository.save(account);
if (account.isAccountNumberRequiresAutoGeneration()) {
@@ -798,6 +798,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sa.linked_account_id as linkedAccountId, ");
sqlBuilder.append("sa.linked_account_type as linkedAccountType, ");
sqlBuilder.append("sa.linked_account_no as linkedAccountNo, ");
sqlBuilder.append("sp.balance_swap_option as balanceSwapOption, ");
sqlBuilder.append("sp.msme as msme, ");
sqlBuilder.append("sp.auto_debit_facility as autoDebitFacility, ");
// Facility config fields
sqlBuilder.append("safc.atm_facility as atmFacility, ");
sqlBuilder.append("safc.atm_withdrawal_facility as atmWithdrawalFacility, ");
@@ -1049,6 +1052,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
accountData.setLinkedAccountId(JdbcSupport.getLong(rs, "linkedAccountId"));
accountData.setLinkedAccountType(rs.getString("linkedAccountType"));
accountData.setLinkedAccountNo(rs.getString("linkedAccountNo"));
accountData.setBalanceSwapOption(rs.getObject("balanceSwapOption") != null ? rs.getBoolean("balanceSwapOption") : null);
accountData.setMsme(rs.getObject("msme") != null ? rs.getBoolean("msme") : null);
accountData.setAutoDebitFacility(rs.getObject("autoDebitFacility") != null ? rs.getBoolean("autoDebitFacility") : null);
// Nominee as parsed map
final String nomineeDataStr = rs.getString("nomineeData");
@@ -277,13 +277,13 @@ public class SavingsConfiguration {
SavingsAccountChargeAssembler savingsAccountChargeAssembler, AccountAssociationsRepository accountAssociationsRepository,
FromJsonHelper fromJsonHelper, CalendarInstanceRepository calendarInstanceRepository,
ConfigurationDomainService configurationDomainService, AccountNumberFormatRepositoryWrapper accountNumberFormatRepository,
BusinessEventNotifierService businessEventNotifierService) {
BusinessEventNotifierService businessEventNotifierService, SavingsAccountAssembler savingsAccountAssembler) {
return new DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl(context, savingAccountRepository,
fixedDepositAccountRepository, recurringDepositAccountRepository, depositAccountAssembler, depositAccountDataValidator,
accountNumberGenerator, clientRepository, groupRepository, savingsProductRepository, noteRepository, staffRepository,
savingsAccountApplicationTransitionApiJsonValidator, savingsAccountChargeAssembler, accountAssociationsRepository,
fromJsonHelper, calendarInstanceRepository, configurationDomainService, accountNumberFormatRepository,
businessEventNotifierService);
businessEventNotifierService, savingsAccountAssembler);
}
@Bean
@@ -285,4 +285,5 @@
<include file="parts/0273_add_gl_account_id_to_current_product.xml" relativeToChangelogFile="true"/>
<include file="parts/0276_create_current_account_tables.xml" relativeToChangelogFile="true"/>
<include file="parts/0277_fix_current_account_audit_columns.xml" relativeToChangelogFile="true"/>
<include file="parts/0278_add_is_mandatory_to_joint_holder.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<changeSet id="0278_add_is_mandatory_to_joint_holder" author="system">
<addColumn tableName="m_current_account_joint_holder">
<column name="is_mandatory" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<changeSet id="0278b_add_is_mandatory_to_savings_joint_holder" author="system">
<addColumn tableName="m_savings_account_joint_holders">
<column name="is_mandatory" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -47,7 +47,8 @@ public class SavingsAccountConstant extends SavingsApiConstants {
guardianClientIdParamName, minorDateOfBirthParamName,
introducerIdParamName, introducerNameParamName, introducerCifNumberParamName,
nomineeParamName, linkedAccountIdParamName, linkedAccountTypeParamName, linkedAccountNoParamName,
accountFacilityConfigParamName, jointHoldersParamName));
accountFacilityConfigParamName, jointHoldersParamName,
balanceSwapOptionParamName, msmeParamName, autoDebitFacilityParamName));
/**
* These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to
@@ -56,10 +56,13 @@ public class SavingsAccountJointHolder extends AbstractPersistableCustom<Long> {
@Column(name = "is_primary")
private boolean primary = false;
@Column(name = "is_mandatory")
private boolean mandatory = false;
protected SavingsAccountJointHolder() {}
public SavingsAccountJointHolder(SavingsAccount account, Long clientId, String cifNumber,
String displayName, BigDecimal sharePercentage, String signingAuthority, String role, boolean primary) {
String displayName, BigDecimal sharePercentage, String signingAuthority, String role, boolean primary, boolean mandatory) {
this.savingsAccount = account;
this.clientId = clientId;
this.cifNumber = cifNumber;
@@ -68,6 +71,7 @@ public class SavingsAccountJointHolder extends AbstractPersistableCustom<Long> {
this.signingAuthority = signingAuthority;
this.role = role;
this.primary = primary;
this.mandatory = mandatory;
}
public SavingsAccount getSavingsAccount() { return savingsAccount; }
@@ -78,4 +82,5 @@ public class SavingsAccountJointHolder extends AbstractPersistableCustom<Long> {
public String getSigningAuthority() { return signingAuthority; }
public String getRole() { return role; }
public boolean isPrimary() { return primary; }
public boolean isMandatory() { return mandatory; }
}