Merge remote-tracking branch 'self-hosted/feature/merge-updates' into feature/reports-products
# Conflicts: # fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
This commit is contained in:
+97
-6
@@ -24,6 +24,7 @@ import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
|
||||
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormat;
|
||||
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatEnumerations.AccountNumberPrefixType;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
@@ -35,9 +36,12 @@ import org.apache.fineract.portfolio.client.domain.ClientRepository;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@@ -58,10 +62,16 @@ public class AccountNumberGenerator {
|
||||
private static final String SAVINGS_PRODUCT_SHORT_NAME = "savingsProductShortName";
|
||||
private static final String SHARE_PRODUCT_SHORT_NAME = "sharesProductShortName";
|
||||
private static final String PREFIX_SHORT_NAME = "prefixShortName";
|
||||
private static final int CBS_ACCOUNT_NUMBER_LENGTH = 15;
|
||||
private static final String CBS_FIXED_PREFIX = "00";
|
||||
|
||||
private final ConfigurationReadPlatformService configurationReadPlatformService;
|
||||
private final ClientRepository clientRepository;
|
||||
private final LoanRepository loanRepository;
|
||||
private final SavingsAccountRepository savingsAccountRepository;
|
||||
private final GLAccountRepository glAccountRepository;
|
||||
private final SavingsProductRepository savingsProductRepository;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public String generate(Client client, AccountNumberFormat accountNumberFormat) {
|
||||
Map<String, String> propertyMap = new HashMap<>();
|
||||
@@ -85,12 +95,93 @@ public class AccountNumberGenerator {
|
||||
}
|
||||
|
||||
public String generate(SavingsAccount savingsAccount, AccountNumberFormat accountNumberFormat) {
|
||||
Map<String, String> propertyMap = new HashMap<>();
|
||||
propertyMap.put(ID, savingsAccount.getId().toString());
|
||||
propertyMap.put(OFFICE_NAME, savingsAccount.office().getName());
|
||||
propertyMap.put(SAVINGS_PRODUCT_SHORT_NAME, savingsAccount.savingsProduct().getShortName());
|
||||
propertyMap.put(ENTITY_TYPE, "savingsAccount");
|
||||
return generateAccountNumber(propertyMap, accountNumberFormat);
|
||||
return generateCbsAccountNumber(savingsAccount);
|
||||
}
|
||||
|
||||
private String generateCbsAccountNumber(SavingsAccount savingsAccount) {
|
||||
// Format: [00][GL_CODE 6 digits][OFFICE_ID 2 digits][ACCOUNT_ID 5 digits] = 15 digits
|
||||
Long glAccountId = savingsAccount.savingsProduct().getGlAccountId();
|
||||
String glCode = "000000";
|
||||
if (glAccountId != null) {
|
||||
glCode = glAccountRepository.findById(glAccountId)
|
||||
.map(gl -> StringUtils.leftPad(gl.getGlCode(), 6, '0'))
|
||||
.orElse("000000");
|
||||
}
|
||||
glCode = glCode.length() > 6 ? glCode.substring(glCode.length() - 6) : StringUtils.leftPad(glCode, 6, '0');
|
||||
|
||||
String officeId = StringUtils.leftPad(savingsAccount.office().getId().toString(), 2, '0');
|
||||
if (officeId.length() > 2) {
|
||||
officeId = officeId.substring(officeId.length() - 2);
|
||||
}
|
||||
|
||||
String accountId = StringUtils.leftPad(savingsAccount.getId().toString(), 5, '0');
|
||||
if (accountId.length() > 5) {
|
||||
accountId = accountId.substring(accountId.length() - 5);
|
||||
}
|
||||
|
||||
return CBS_FIXED_PREFIX + glCode + officeId + accountId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate CBS account number for a current account.
|
||||
* GL code is fetched directly from m_current_product using the current account's product_id.
|
||||
* Format: 00 + GL_CODE(6) + OFFICE_ID(2) + ACCOUNT_ID(5) = 15 digits
|
||||
*/
|
||||
public String generateForCurrentAccount(CurrentAccount currentAccount) {
|
||||
Long productId = currentAccount.getProductId();
|
||||
Long glAccountId = jdbcTemplate.query(
|
||||
"SELECT gl_account_id FROM m_current_product WHERE id = ?",
|
||||
rs -> rs.next() ? rs.getLong("gl_account_id") : null,
|
||||
productId);
|
||||
|
||||
String glCode = "000000";
|
||||
if (glAccountId != null && glAccountId > 0) {
|
||||
glCode = glAccountRepository.findById(glAccountId)
|
||||
.map(gl -> StringUtils.leftPad(gl.getGlCode(), 6, '0'))
|
||||
.orElse("000000");
|
||||
}
|
||||
glCode = glCode.length() > 6 ? glCode.substring(glCode.length() - 6) : StringUtils.leftPad(glCode, 6, '0');
|
||||
|
||||
String officeId = StringUtils.leftPad(currentAccount.getOffice().getId().toString(), 2, '0');
|
||||
if (officeId.length() > 2) officeId = officeId.substring(officeId.length() - 2);
|
||||
|
||||
String accountId = StringUtils.leftPad(currentAccount.getId().toString(), 5, '0');
|
||||
if (accountId.length() > 5) accountId = accountId.substring(accountId.length() - 5);
|
||||
|
||||
return CBS_FIXED_PREFIX + glCode + officeId + accountId;
|
||||
}
|
||||
|
||||
public String previewAccountNumber(Long productId, Long officeId) {
|
||||
String glCode = "000000";
|
||||
if (productId != null) {
|
||||
glCode = savingsProductRepository.findById(productId).map(p -> {
|
||||
Long glAccId = p.getGlAccountId();
|
||||
if (glAccId == null) return "000000";
|
||||
return glAccountRepository.findById(glAccId).map(gl -> gl.getGlCode()).orElse("000000");
|
||||
}).orElse("000000");
|
||||
}
|
||||
return buildPreview(glCode, officeId);
|
||||
}
|
||||
|
||||
public String previewAccountNumberByGlAccountId(Long glAccountId, Long officeId) {
|
||||
String glCode = "000000";
|
||||
if (glAccountId != null) {
|
||||
glCode = glAccountRepository.findById(glAccountId).map(gl -> gl.getGlCode()).orElse("000000");
|
||||
}
|
||||
return buildPreview(glCode, officeId);
|
||||
}
|
||||
|
||||
private String buildPreview(String glCode, Long officeId) {
|
||||
glCode = glCode.length() > 6 ? glCode.substring(glCode.length() - 6) : StringUtils.leftPad(glCode, 6, '0');
|
||||
|
||||
String officeIdStr = officeId != null ? StringUtils.leftPad(officeId.toString(), 2, '0') : "00";
|
||||
if (officeIdStr.length() > 2) officeIdStr = officeIdStr.substring(officeIdStr.length() - 2);
|
||||
|
||||
Long maxId = savingsAccountRepository.findMaxId().orElse(0L);
|
||||
String nextSeq = StringUtils.leftPad(String.valueOf(maxId + 1), 5, '0');
|
||||
if (nextSeq.length() > 5) nextSeq = nextSeq.substring(nextSeq.length() - 5);
|
||||
|
||||
return CBS_FIXED_PREFIX + glCode + officeIdStr + nextSeq;
|
||||
}
|
||||
|
||||
public String generate(ShareAccount shareaccount, AccountNumberFormat accountNumberFormat) {
|
||||
|
||||
+20
@@ -31,6 +31,7 @@ public class AccountSummaryCollectionData {
|
||||
private final Collection<SavingsAccountSummaryData> savingsAccounts;
|
||||
private final Collection<ShareAccountSummaryData> shareAccounts;
|
||||
private final Collection<GuarantorAccountSummaryData> guarantorAccounts;
|
||||
private final Collection<SavingsAccountSummaryData> currentAccounts;
|
||||
|
||||
private final Collection<LoanAccountSummaryData> memberLoanAccounts;
|
||||
private final Collection<SavingsAccountSummaryData> memberSavingsAccounts;
|
||||
@@ -49,6 +50,24 @@ public class AccountSummaryCollectionData {
|
||||
this.savingsAccounts = defaultSavingsAccountsIfEmpty(savingsAccounts);
|
||||
this.shareAccounts = defaultShareAccountsIfEmpty(shareAccounts);
|
||||
this.guarantorAccounts = guarantorAccounts;
|
||||
this.currentAccounts = null;
|
||||
this.memberLoanAccounts = null;
|
||||
this.memberSavingsAccounts = null;
|
||||
this.memberGuarantorAccounts = null;
|
||||
}
|
||||
|
||||
public AccountSummaryCollectionData(final Collection<LoanAccountSummaryData> loanAccounts,
|
||||
final Collection<LoanAccountSummaryData> groupLoanIndividualMonitoringAccounts,
|
||||
final Collection<SavingsAccountSummaryData> savingsAccounts, final Collection<ShareAccountSummaryData> shareAccounts,
|
||||
final Collection<GuarantorAccountSummaryData> guarantorAccounts,
|
||||
final Collection<SavingsAccountSummaryData> currentAccounts) {
|
||||
|
||||
this.loanAccounts = defaultLoanAccountsIfEmpty(loanAccounts);
|
||||
this.groupLoanIndividualMonitoringAccounts = groupLoanIndividualMonitoringAccounts;
|
||||
this.savingsAccounts = defaultSavingsAccountsIfEmpty(savingsAccounts);
|
||||
this.shareAccounts = defaultShareAccountsIfEmpty(shareAccounts);
|
||||
this.guarantorAccounts = guarantorAccounts;
|
||||
this.currentAccounts = defaultSavingsAccountsIfEmpty(currentAccounts);
|
||||
this.memberLoanAccounts = null;
|
||||
this.memberSavingsAccounts = null;
|
||||
this.memberGuarantorAccounts = null;
|
||||
@@ -66,6 +85,7 @@ public class AccountSummaryCollectionData {
|
||||
this.savingsAccounts = defaultSavingsAccountsIfEmpty(savingsAccounts);
|
||||
this.guarantorAccounts = guarantorAccounts;
|
||||
this.shareAccounts = null;
|
||||
this.currentAccounts = null;
|
||||
this.memberLoanAccounts = defaultLoanAccountsIfEmpty(memberLoanAccounts);
|
||||
this.memberSavingsAccounts = defaultSavingsAccountsIfEmpty(memberSavingsAccounts);
|
||||
this.memberGuarantorAccounts = defaultGuarantorAccountsIfEmpty(memberGuarantorAccounts);
|
||||
|
||||
+121
-1
@@ -79,7 +79,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
final List<ShareAccountSummaryData> shareAccounts = retrieveShareAccountDetails(clientId);
|
||||
final List<GuarantorAccountSummaryData> guarantorloanAccounts = retrieveGuarantorLoanAccountDetails(guarantorWhereClause,
|
||||
new Object[] { clientId });
|
||||
return new AccountSummaryCollectionData(loanAccounts, glimAccounts, savingsAccounts, shareAccounts, guarantorloanAccounts);
|
||||
final List<SavingsAccountSummaryData> currentAccounts = retrieveCurrentAccountDetails(clientId);
|
||||
return new AccountSummaryCollectionData(loanAccounts, glimAccounts, savingsAccounts, shareAccounts, guarantorloanAccounts, currentAccounts);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,6 +177,12 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
return retrieveLoanAccountDetails(loanWhereClause, new Object[] { groupId });
|
||||
}
|
||||
|
||||
private List<SavingsAccountSummaryData> retrieveCurrentAccountDetails(final Long clientId) {
|
||||
final CurrentAccountSummaryDataMapper mapper = new CurrentAccountSummaryDataMapper();
|
||||
final String sql = "select " + mapper.schema() + " where ca.client_id = ? order by ca.status_enum ASC, ca.account_no ASC";
|
||||
return this.jdbcTemplate.query(sql, mapper, new Object[] { clientId }); // NOSONAR
|
||||
}
|
||||
|
||||
private List<LoanAccountSummaryData> retrieveLoanAccountDetails(final String loanwhereClause, final Object[] inputs) {
|
||||
final LoanAccountSummaryDataMapper rm = new LoanAccountSummaryDataMapper();
|
||||
final String sql = "select " + rm.loanAccountSummarySchema() + loanwhereClause;
|
||||
@@ -452,6 +459,119 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CurrentAccountSummaryDataMapper implements RowMapper<SavingsAccountSummaryData> {
|
||||
|
||||
private final String schemaSql;
|
||||
|
||||
CurrentAccountSummaryDataMapper() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("ca.id as id, ca.account_no as accountNo, ca.external_id as externalId, ");
|
||||
sb.append("ca.status_enum as statusEnum, ca.sub_status_enum as subStatusEnum, ");
|
||||
sb.append("ca.account_type_enum as accountType, ");
|
||||
sb.append("ca.account_balance_derived as accountBalance, ");
|
||||
sb.append("ca.on_hold_funds_derived as onHoldFunds, ");
|
||||
sb.append("ca.submittedon_date as submittedOnDate, ");
|
||||
sb.append("sbu.username as submittedByUsername, sbu.firstname as submittedByFirstname, sbu.lastname as submittedByLastname, ");
|
||||
sb.append("ca.rejectedon_date as rejectedOnDate, ");
|
||||
sb.append("rbu.username as rejectedByUsername, rbu.firstname as rejectedByFirstname, rbu.lastname as rejectedByLastname, ");
|
||||
sb.append("ca.withdrawnon_date as withdrawnOnDate, ");
|
||||
sb.append("wbu.username as withdrawnByUsername, wbu.firstname as withdrawnByFirstname, wbu.lastname as withdrawnByLastname, ");
|
||||
sb.append("ca.approvedon_date as approvedOnDate, ");
|
||||
sb.append("abu.username as approvedByUsername, abu.firstname as approvedByFirstname, abu.lastname as approvedByLastname, ");
|
||||
sb.append("ca.activatedon_date as activatedOnDate, ");
|
||||
sb.append("avbu.username as activatedByUsername, avbu.firstname as activatedByFirstname, avbu.lastname as activatedByLastname, ");
|
||||
sb.append("ca.closedon_date as closedOnDate, ");
|
||||
sb.append("cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname, ");
|
||||
sb.append("ca.currency_code as currencyCode, ca.currency_digits as currencyDigits, ca.currency_multiplesof as inMultiplesOf, ");
|
||||
sb.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
||||
sb.append("curr.display_symbol as currencyDisplaySymbol, ");
|
||||
sb.append("ca.product_id as productId, cp.name as productName, cp.short_name as shortProductName ");
|
||||
sb.append("from m_current_account ca ");
|
||||
sb.append("join m_current_product cp on cp.id = ca.product_id ");
|
||||
sb.append("join m_currency curr on curr.code = ca.currency_code ");
|
||||
sb.append("left join m_appuser sbu on sbu.id = ca.submittedon_userid ");
|
||||
sb.append("left join m_appuser rbu on rbu.id = ca.rejectedon_userid ");
|
||||
sb.append("left join m_appuser wbu on wbu.id = ca.withdrawnon_userid ");
|
||||
sb.append("left join m_appuser abu on abu.id = ca.approvedon_userid ");
|
||||
sb.append("left join m_appuser avbu on avbu.id = ca.activatedon_userid ");
|
||||
sb.append("left join m_appuser cbu on cbu.id = ca.closedon_userid ");
|
||||
this.schemaSql = sb.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsAccountSummaryData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||
final Long id = JdbcSupport.getLong(rs, "id");
|
||||
final String accountNo = rs.getString("accountNo");
|
||||
final String externalId = rs.getString("externalId");
|
||||
final Long productId = JdbcSupport.getLong(rs, "productId");
|
||||
final String productName = rs.getString("productName");
|
||||
final String shortProductName = rs.getString("shortProductName");
|
||||
final Integer statusId = JdbcSupport.getInteger(rs, "statusEnum");
|
||||
final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusId);
|
||||
final Integer subStatusEnum = JdbcSupport.getInteger(rs, "subStatusEnum");
|
||||
final SavingsAccountSubStatusEnumData subStatus = SavingsEnumerations.subStatus(subStatusEnum);
|
||||
final Integer accountType = JdbcSupport.getInteger(rs, "accountType");
|
||||
final EnumOptionData accountTypeData = AccountEnumerations.loanType(accountType);
|
||||
final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accountBalance");
|
||||
final BigDecimal onHoldFunds = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "onHoldFunds");
|
||||
|
||||
final String currencyCode = rs.getString("currencyCode");
|
||||
final String currencyName = rs.getString("currencyName");
|
||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
|
||||
currencyDisplaySymbol, currencyNameCode);
|
||||
|
||||
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
|
||||
final String submittedByUsername = rs.getString("submittedByUsername");
|
||||
final String submittedByFirstname = rs.getString("submittedByFirstname");
|
||||
final String submittedByLastname = rs.getString("submittedByLastname");
|
||||
|
||||
final LocalDate rejectedOnDate = JdbcSupport.getLocalDate(rs, "rejectedOnDate");
|
||||
final String rejectedByUsername = rs.getString("rejectedByUsername");
|
||||
final String rejectedByFirstname = rs.getString("rejectedByFirstname");
|
||||
final String rejectedByLastname = rs.getString("rejectedByLastname");
|
||||
|
||||
final LocalDate withdrawnOnDate = JdbcSupport.getLocalDate(rs, "withdrawnOnDate");
|
||||
final String withdrawnByUsername = rs.getString("withdrawnByUsername");
|
||||
final String withdrawnByFirstname = rs.getString("withdrawnByFirstname");
|
||||
final String withdrawnByLastname = rs.getString("withdrawnByLastname");
|
||||
|
||||
final LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedOnDate");
|
||||
final String approvedByUsername = rs.getString("approvedByUsername");
|
||||
final String approvedByFirstname = rs.getString("approvedByFirstname");
|
||||
final String approvedByLastname = rs.getString("approvedByLastname");
|
||||
|
||||
final LocalDate activatedOnDate = JdbcSupport.getLocalDate(rs, "activatedOnDate");
|
||||
final String activatedByUsername = rs.getString("activatedByUsername");
|
||||
final String activatedByFirstname = rs.getString("activatedByFirstname");
|
||||
final String activatedByLastname = rs.getString("activatedByLastname");
|
||||
|
||||
final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate");
|
||||
final String closedByUsername = rs.getString("closedByUsername");
|
||||
final String closedByFirstname = rs.getString("closedByFirstname");
|
||||
final String closedByLastname = rs.getString("closedByLastname");
|
||||
|
||||
final SavingsAccountApplicationTimelineData timeline = new SavingsAccountApplicationTimelineData(submittedOnDate,
|
||||
submittedByUsername, submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername,
|
||||
rejectedByFirstname, rejectedByLastname, withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname,
|
||||
withdrawnByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname, activatedOnDate,
|
||||
activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername, closedByFirstname,
|
||||
closedByLastname);
|
||||
|
||||
final EnumOptionData depositTypeData = SavingsEnumerations.depositType(400);
|
||||
|
||||
return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, shortProductName, status, currency,
|
||||
accountBalance, onHoldFunds, null, accountBalance, accountTypeData, timeline, depositTypeData, subStatus, activatedOnDate);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LoanAccountSummaryDataMapper implements RowMapper<LoanAccountSummaryData> {
|
||||
|
||||
public String loanAccountSummarySchema() {
|
||||
|
||||
+15
@@ -304,6 +304,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
return chargeSchema() + " join m_share_product_charge mspc on mspc.charge_id = c.id";
|
||||
}
|
||||
|
||||
public String currentProductChargeSchema() {
|
||||
return chargeSchema() + " join m_current_product_charge cpc on cpc.charge_id = c.id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
@@ -438,6 +442,17 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { shareProductId }); // NOSONAR
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChargeData> retrieveCurrentProductCharges(final Long currentProductId) {
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
|
||||
String sql = "select " + rm.currentProductChargeSchema()
|
||||
+ " where c.is_deleted=false and c.is_active=true and cpc.current_product_id=? ";
|
||||
sql += addInClauseToSQL_toLimitChargesMappedToOffice_ifOfficeSpecificProductsEnabled();
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { currentProductId }); // NOSONAR
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChargeData> retrieveSavingsAccountApplicableCharges(Long savingsAccountId) {
|
||||
|
||||
|
||||
+37
-124
@@ -35,35 +35,26 @@ import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Path("/v1/currentaccounts")
|
||||
@Component
|
||||
@@ -71,14 +62,15 @@ import org.springframework.util.CollectionUtils;
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountsApiResource {
|
||||
|
||||
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
|
||||
private final SavingsAccountTemplateReadPlatformService savingsAccountTemplateReadPlatformService;
|
||||
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
private final CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||
private final CurrentAccountReadPlatformService currentAccountReadPlatformService;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -93,7 +85,7 @@ public class CurrentAccountsApiResource {
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
final SavingsAccountData savingsAccount = savingsAccountTemplateReadPlatformService.retrieveTemplate(clientId, groupId, productId,
|
||||
final SavingsAccountData savingsAccount = currentAccountTemplateReadPlatformService.retrieveTemplate(clientId, groupId, productId,
|
||||
staffInSelectedOfficeOnly);
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiSetConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
@@ -112,14 +104,8 @@ public class CurrentAccountsApiResource {
|
||||
@QueryParam("sortOrder") final String sortOrder) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final Page<SavingsAccountData> products = savingsAccountReadPlatformService.retrieveAll(searchParameters);
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, products, SavingsApiSetConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
final List<Map<String, Object>> accounts = currentAccountReadPlatformService.retrieveAll();
|
||||
return GSON.toJson(accounts);
|
||||
}
|
||||
|
||||
@POST
|
||||
@@ -128,8 +114,10 @@ public class CurrentAccountsApiResource {
|
||||
@Operation(summary = "Submit new current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createCurrentAccount().withJson(apiRequestBodyAsJson).build();
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.submitApplication(command);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -145,13 +133,8 @@ public class CurrentAccountsApiResource {
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
if (!(is(chargeStatus, "all") || is(chargeStatus, "active") || is(chargeStatus, "inactive"))) {
|
||||
throw new UnrecognizedQueryParamException("status", chargeStatus, new Object[] { "all", "active", "inactive" });
|
||||
}
|
||||
|
||||
final SavingsAccountData savingsAccount = savingsAccountReadPlatformService.retrieveOne(accountId);
|
||||
return populateTemplateAndAssociations(accountId, savingsAccount, staffInSelectedOfficeOnly, chargeStatus, uriInfo);
|
||||
final Map<String, Object> account = currentAccountReadPlatformService.retrieveOne(accountId);
|
||||
return GSON.toJson(account);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@@ -164,16 +147,10 @@ public class CurrentAccountsApiResource {
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson,
|
||||
@QueryParam("command") final String commandParam) {
|
||||
|
||||
if (is(commandParam, "updateWithHoldTax")) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson)
|
||||
.updateWithHoldTaxCurrentAccount(accountId).build();
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateCurrentAccount(accountId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.modifyApplication(accountId, command);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -192,53 +169,28 @@ public class CurrentAccountsApiResource {
|
||||
jsonApiRequest = "{}";
|
||||
}
|
||||
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest);
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(jsonApiRequest));
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "reject")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.rejectCurrentAccountApplication(accountId).build());
|
||||
result = currentAccountWritePlatformService.rejectApplication(accountId, command);
|
||||
} else if (is(commandParam, "withdrawnByApplicant")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.withdrawCurrentAccountApplication(accountId).build());
|
||||
result = currentAccountWritePlatformService.withdrawApplication(accountId, command);
|
||||
} else if (is(commandParam, "approve")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.approveCurrentAccountApplication(accountId).build());
|
||||
result = currentAccountWritePlatformService.approveApplication(accountId, command);
|
||||
} else if (is(commandParam, "undoapproval")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.undoCurrentAccountApplication(accountId).build());
|
||||
result = currentAccountWritePlatformService.undoApproval(accountId, command);
|
||||
} else if (is(commandParam, "activate")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.currentAccountActivation(accountId).build());
|
||||
} else if (is(commandParam, "calculateInterest")) {
|
||||
result = commandsSourceWritePlatformService
|
||||
.logCommandSource(builder.withNoJsonBody().currentAccountInterestCalculation(accountId).build());
|
||||
} else if (is(commandParam, "postInterest")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.currentAccountInterestPosting(accountId).build());
|
||||
} else if (is(commandParam, "applyAnnualFees")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.currentAccountApplyAnnualFees(accountId).build());
|
||||
result = currentAccountWritePlatformService.activate(accountId, command);
|
||||
} else if (is(commandParam, "close")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.closeCurrentAccountApplication(accountId).build());
|
||||
} else if (is(commandParam, "assignSavingsOfficer")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.assignCurrentAccountOfficer(accountId).build());
|
||||
} else if (is(commandParam, "unassignSavingsOfficer")) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.unassignCurrentAccountOfficer(accountId).build());
|
||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_BLOCK_DEBIT)) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.blockDebitsFromCurrentAccount(accountId).build());
|
||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_UNBLOCK_DEBIT)) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.unblockDebitsFromCurrentAccount(accountId).build());
|
||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_BLOCK_CREDIT)) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.blockCreditsToCurrentAccount(accountId).build());
|
||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_UNBLOCK_CREDIT)) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.unblockCreditsToCurrentAccount(accountId).build());
|
||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_BLOCK_ACCOUNT)) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.blockCurrentAccount(accountId).build());
|
||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_UNBLOCK_ACCOUNT)) {
|
||||
result = commandsSourceWritePlatformService.logCommandSource(builder.unblockCurrentAccount(accountId).build());
|
||||
result = currentAccountWritePlatformService.closeAccount(accountId, command);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest",
|
||||
"postInterest", "close", "assignSavingsOfficer", "unassignSavingsOfficer",
|
||||
SavingsApiConstants.COMMAND_BLOCK_DEBIT, SavingsApiConstants.COMMAND_UNBLOCK_DEBIT,
|
||||
SavingsApiConstants.COMMAND_BLOCK_CREDIT, SavingsApiConstants.COMMAND_UNBLOCK_CREDIT,
|
||||
SavingsApiConstants.COMMAND_BLOCK_ACCOUNT, SavingsApiConstants.COMMAND_UNBLOCK_ACCOUNT });
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "close" });
|
||||
}
|
||||
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
@@ -251,8 +203,7 @@ public class CurrentAccountsApiResource {
|
||||
@Operation(summary = "Delete a current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String delete(@PathParam("accountId") final Long accountId) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteCurrentAccount(accountId).build();
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.deleteApplication(accountId);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -260,42 +211,4 @@ public class CurrentAccountsApiResource {
|
||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||
}
|
||||
|
||||
private String populateTemplateAndAssociations(final Long accountId, final SavingsAccountData savingsAccount,
|
||||
final boolean staffInSelectedOfficeOnly, final String chargeStatus, final UriInfo uriInfo) {
|
||||
|
||||
Collection<SavingsAccountTransactionData> transactions = null;
|
||||
Collection<SavingsAccountChargeData> charges = null;
|
||||
|
||||
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (!associationParameters.isEmpty()) {
|
||||
if (associationParameters.contains("all")) {
|
||||
associationParameters.addAll(Arrays.asList(SavingsApiConstants.transactions, SavingsApiConstants.charges));
|
||||
}
|
||||
if (associationParameters.contains(SavingsApiConstants.transactions)) {
|
||||
final Collection<SavingsAccountTransactionData> currentTransactions = savingsAccountReadPlatformService
|
||||
.retrieveAllTransactions(accountId, DepositAccountType.CURRENT_DEPOSIT);
|
||||
if (!CollectionUtils.isEmpty(currentTransactions)) {
|
||||
transactions = currentTransactions;
|
||||
}
|
||||
}
|
||||
if (associationParameters.contains(SavingsApiConstants.charges)) {
|
||||
final Collection<SavingsAccountChargeData> currentCharges = savingsAccountChargeReadPlatformService
|
||||
.retrieveSavingsAccountCharges(accountId, chargeStatus);
|
||||
if (!CollectionUtils.isEmpty(currentCharges)) {
|
||||
charges = currentCharges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SavingsAccountData templateData = null;
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
if (settings.isTemplate()) {
|
||||
templateData = savingsAccountTemplateReadPlatformService.retrieveTemplate(savingsAccount.getClientId(),
|
||||
savingsAccount.getGroupId(), savingsAccount.getSavingsProductId(), staffInSelectedOfficeOnly);
|
||||
}
|
||||
|
||||
return toApiJsonSerializer.serialize(settings,
|
||||
SavingsAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges),
|
||||
SavingsApiSetConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
}
|
||||
|
||||
+59
-9
@@ -36,6 +36,7 @@ import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
|
||||
@@ -49,6 +50,8 @@ import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
@@ -62,16 +65,18 @@ import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadService;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -81,7 +86,7 @@ import org.springframework.util.CollectionUtils;
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentProductsApiResource {
|
||||
|
||||
private final SavingsProductReadPlatformService savingProductReadPlatformService;
|
||||
private final CurrentProductReadPlatformService savingProductReadPlatformService;
|
||||
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final CurrencyReadPlatformService currencyReadPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
@@ -94,6 +99,24 @@ public class CurrentProductsApiResource {
|
||||
private final PaymentTypeReadService paymentTypeReadPlatformService;
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
private String generateNextProductCode(final String tableName, final String prefix) {
|
||||
try {
|
||||
String maxCode = jdbcTemplate.queryForObject(
|
||||
"SELECT MAX(product_code) FROM " + tableName + " WHERE product_code LIKE ?",
|
||||
String.class, prefix + "%");
|
||||
if (maxCode == null || maxCode.isBlank()) {
|
||||
return prefix + "001";
|
||||
}
|
||||
String numStr = maxCode.substring(prefix.length());
|
||||
int num = Integer.parseInt(numStr);
|
||||
return prefix + String.format("%03d", num + 1);
|
||||
} catch (Exception e) {
|
||||
return prefix + "001";
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -142,7 +165,7 @@ public class CurrentProductsApiResource {
|
||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
SavingsProductData savingProductData = savingProductReadPlatformService.retrieveOne(productId);
|
||||
final Collection<ChargeData> charges = chargeReadPlatformService.retrieveSavingsProductCharges(productId);
|
||||
final Collection<ChargeData> charges = chargeReadPlatformService.retrieveCurrentProductCharges(productId);
|
||||
savingProductData = SavingsProductData.withCharges(savingProductData, charges);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
@@ -181,6 +204,21 @@ public class CurrentProductsApiResource {
|
||||
return toApiJsonSerializer.serialize(settings, savingProduct, SavingsApiSetConstants.SAVINGS_PRODUCT_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
private Collection<EnumOptionData> retrievePeriodFrequencyOptions() {
|
||||
return this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.PERIOD_FREQUENCY).stream()
|
||||
.map(codeValue -> new EnumOptionData(codeValue.getId(),
|
||||
"periodFrequencyType." + codeValue.getName().toLowerCase(Locale.ROOT).replace(' ', '_'), codeValue.getName()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Collection<EnumOptionData> retrieveInterestCalculationTypeOption() {
|
||||
return this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.INTEREST_CALCULATION_TYPE_OPTION).stream()
|
||||
.map(codeValue -> new EnumOptionData(codeValue.getId(),
|
||||
"interestCalculationTypeOption." + codeValue.getName().toLowerCase(Locale.ROOT).replace(' ', '_'),
|
||||
codeValue.getName()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{productId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -221,11 +259,13 @@ public class CurrentProductsApiResource {
|
||||
.retrieveInterestPostingPeriodTypeOptions();
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = dropdownReadPlatformService
|
||||
.retrieveInterestCalculationTypeOptions();
|
||||
final Collection<EnumOptionData> interestCalculationTypeOption = retrieveInterestCalculationTypeOption();
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = dropdownReadPlatformService
|
||||
.retrieveInterestCalculationDaysInYearTypeOptions();
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = dropdownReadPlatformService
|
||||
.retrieveLockinPeriodFrequencyTypeOptions();
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
|
||||
final Collection<EnumOptionData> periodFrequencyOptions = retrievePeriodFrequencyOptions();
|
||||
|
||||
final Collection<PaymentTypeData> paymentTypeOptions = paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
||||
|
||||
@@ -244,19 +284,29 @@ public class CurrentProductsApiResource {
|
||||
penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions;
|
||||
final Collection<TaxGroupData> taxGroupOptions = taxReadPlatformService.retrieveTaxGroupsForLookUp();
|
||||
|
||||
final List<CodeValueData> yesNoOptions = new ArrayList<>(
|
||||
codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.YES_NO));
|
||||
final List<CodeValueData> accountTypeOptions = new ArrayList<>(
|
||||
codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.CURRENT_PRODUCT_ACCOUNT_TYPE));
|
||||
final List<CodeValueData> customerSegmentOptions = new ArrayList<>(
|
||||
codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.CURRENT_PRODUCT_CUSTOMER_SEGMENT));
|
||||
|
||||
SavingsProductData savingsProductToReturn = null;
|
||||
if (savingsProduct != null) {
|
||||
savingsProductToReturn = SavingsProductData.withTemplate(savingsProduct, currencyOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions, accountMappingForPayment);
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationTypeOption,
|
||||
interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, periodFrequencyOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions, yesNoOptions,
|
||||
accountTypeOptions, customerSegmentOptions, accountMappingForPayment);
|
||||
} else {
|
||||
savingsProductToReturn = SavingsProductData.template(currency, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions,
|
||||
accountMappingForPayment);
|
||||
interestCalculationTypeOption, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
periodFrequencyOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions,
|
||||
penaltyOptions, taxGroupOptions, yesNoOptions, accountTypeOptions, customerSegmentOptions, accountMappingForPayment);
|
||||
savingsProductToReturn.setProductCode(generateNextProductCode("m_current_product", "CA-IND-"));
|
||||
}
|
||||
|
||||
return savingsProductToReturn;
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.api;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Request payload for saving a new RD interest rate version.
|
||||
* JSON structure:
|
||||
* {
|
||||
* "versionLabel": "Rate Version 2025-Q2",
|
||||
* "remarks": "Revised rates",
|
||||
* "slabs": [
|
||||
* { "srNo": 1, "description": "15 Days to 29 Days", "periodTypeEnum": 1,
|
||||
* "fromPeriod": 15, "toPeriod": 29, "annualInterestRate": 3.00 },
|
||||
* ...
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class RdInterestRateSaveRequest {
|
||||
|
||||
private String versionLabel;
|
||||
private String remarks;
|
||||
private List<SlabRequest> slabs;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public static class SlabRequest {
|
||||
private Integer srNo;
|
||||
private String description;
|
||||
private Integer periodTypeEnum;
|
||||
private Integer fromPeriod;
|
||||
private Integer toPeriod;
|
||||
private BigDecimal annualInterestRate;
|
||||
}
|
||||
}
|
||||
+65
@@ -41,6 +41,7 @@ import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
|
||||
@@ -68,12 +69,21 @@ import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartDat
|
||||
import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartReadPlatformService;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadService;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
|
||||
@@ -110,6 +120,12 @@ public class RecurringDepositProductsApiResource {
|
||||
private final DropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final PaymentTypeReadService paymentTypeReadPlatformService;
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(LocalDate.class,
|
||||
(JsonSerializer<LocalDate>) (src, typeOfSrc, ctx) -> new JsonPrimitive(src.toString()))
|
||||
.create();
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -294,6 +310,15 @@ public class RecurringDepositProductsApiResource {
|
||||
final InterestRateChartData chartTemplate = this.interestRateChartReadPlatformService.template();
|
||||
final Collection<TaxGroupData> taxGroupOptions = this.taxReadPlatformService.retrieveTaxGroupsForLookUp();
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationTypeOption = retrieveInterestCalculationTypeOption();
|
||||
final Collection<EnumOptionData> periodFrequencyOptions = retrievePeriodFrequencyOptions();
|
||||
final List<CodeValueData> yesNoOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.YES_NO));
|
||||
final List<CodeValueData> accountTypeOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.RD_PRODUCT_ACCOUNT_TYPE));
|
||||
final List<CodeValueData> customerSegmentOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.RD_PRODUCT_CUSTOMER_SEGMENT));
|
||||
|
||||
RecurringDepositProductData recurringDepositProductToReturn = null;
|
||||
if (savingsProduct != null) {
|
||||
recurringDepositProductToReturn = RecurringDepositProductData.withTemplate(savingsProduct, currencyOptions,
|
||||
@@ -308,11 +333,51 @@ public class RecurringDepositProductsApiResource {
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate,
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions, taxGroupOptions);
|
||||
recurringDepositProductToReturn.setProductCode(generateNextProductCode("m_savings_product", "RD-IND-"));
|
||||
}
|
||||
|
||||
recurringDepositProductToReturn.setInterestCalculationTypeOption(interestCalculationTypeOption);
|
||||
recurringDepositProductToReturn.setPeriodFrequencyOptions(periodFrequencyOptions);
|
||||
recurringDepositProductToReturn.setYesNoOptions(yesNoOptions);
|
||||
recurringDepositProductToReturn.setAccountTypeOptions(accountTypeOptions);
|
||||
recurringDepositProductToReturn.setCustomerSegmentOptions(customerSegmentOptions);
|
||||
|
||||
return recurringDepositProductToReturn;
|
||||
}
|
||||
|
||||
private Collection<EnumOptionData> retrievePeriodFrequencyOptions() {
|
||||
return this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.PERIOD_FREQUENCY).stream()
|
||||
.map(codeValue -> new EnumOptionData(codeValue.getId(),
|
||||
"periodFrequencyType." + codeValue.getName().toLowerCase(Locale.ROOT).replace(' ', '_'), codeValue.getName()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Collection<EnumOptionData> retrieveInterestCalculationTypeOption() {
|
||||
return this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.INTEREST_CALCULATION_TYPE_OPTION).stream()
|
||||
.map(codeValue -> new EnumOptionData(codeValue.getId(),
|
||||
"interestCalculationTypeOption." + codeValue.getName().toLowerCase(Locale.ROOT).replace(' ', '_'),
|
||||
codeValue.getName()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private String generateNextProductCode(final String tableName, final String prefix) {
|
||||
try {
|
||||
String maxCode = jdbcTemplate.queryForObject(
|
||||
"SELECT MAX(product_code) FROM " + tableName + " WHERE product_code LIKE ? AND deposit_type_enum = 300",
|
||||
String.class, prefix + "%");
|
||||
if (maxCode == null || maxCode.isBlank()) {
|
||||
return prefix + "001";
|
||||
}
|
||||
String numStr = maxCode.substring(prefix.length());
|
||||
int num = Integer.parseInt(numStr);
|
||||
return prefix + String.format("%03d", num + 1);
|
||||
} catch (Exception e) {
|
||||
return prefix + "001";
|
||||
}
|
||||
}
|
||||
|
||||
// ── RD Interest Rate Chart endpoints ────────────────────────────────────
|
||||
|
||||
@DELETE
|
||||
@Path("{productId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
import org.apache.fineract.portfolio.savings.service.RdBaseInterestRateReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/recurringdepositproducts/baseinterestrates")
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RecurringDepositProductsBaseInterestRatesApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final RdBaseInterestRateReadPlatformService rdBaseInterestRateReadPlatformService;
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(LocalDate.class,
|
||||
(JsonSerializer<LocalDate>) (src, typeOfSrc, ctx) -> new JsonPrimitive(src.toString()))
|
||||
.create();
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Get all active RD base interest rate charts", description = "Returns the current CBS base interest-rate version with slabs.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public String retrieveAllActiveInterestRates() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final List<RdInterestRateEffectiveData> data = this.rdBaseInterestRateReadPlatformService.retrieveAllActiveInterestRates();
|
||||
return gson.toJson(data);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("history")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Get all RD base interest rate charts", description = "Returns the full CBS base chart history.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public String retrieveHistory() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final List<RdInterestRateEffectiveData> data = this.rdBaseInterestRateReadPlatformService.retrieveAllInterestRates();
|
||||
return gson.toJson(data);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save new RD base interest rate version", description = "Creates a new CBS base chart version and deactivates the previous active version.")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public String saveInterestRates(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final RdInterestRateSaveRequest request = gson.fromJson(apiRequestBodyAsJson, RdInterestRateSaveRequest.class);
|
||||
final Long createdById = this.context.authenticatedUser().getId();
|
||||
final RdInterestRateEffectiveData saved = this.rdBaseInterestRateReadPlatformService.saveInterestRates(request, createdById);
|
||||
return gson.toJson(saved);
|
||||
}
|
||||
}
|
||||
+55
-5
@@ -40,6 +40,7 @@ import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
|
||||
@@ -56,6 +57,8 @@ import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDoma
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
@@ -76,6 +79,7 @@ import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -91,6 +95,7 @@ public class SavingsProductsApiResource {
|
||||
private final CurrencyReadPlatformService currencyReadPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductData> toApiJsonSerializer;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
|
||||
@@ -99,6 +104,23 @@ public class SavingsProductsApiResource {
|
||||
private final PaymentTypeReadService paymentTypeReadPlatformService;
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
private String generateNextProductCode(final String tableName, final String prefix) {
|
||||
try {
|
||||
String maxCode = jdbcTemplate.queryForObject(
|
||||
"SELECT MAX(product_code) FROM " + tableName + " WHERE product_code LIKE ?",
|
||||
String.class, prefix + "%");
|
||||
if (maxCode == null || maxCode.isBlank()) {
|
||||
return prefix + "001";
|
||||
}
|
||||
String numStr = maxCode.substring(prefix.length());
|
||||
int num = Integer.parseInt(numStr);
|
||||
return prefix + String.format("%03d", num + 1);
|
||||
} catch (Exception e) {
|
||||
return prefix + "001";
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -242,6 +264,7 @@ public class SavingsProductsApiResource {
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestCalculationTypeOptions();
|
||||
final Collection<EnumOptionData> interestCalculationTypeOption = retrieveInterestCalculationTypeOption();
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestCalculationDaysInYearTypeOptions();
|
||||
@@ -250,6 +273,7 @@ public class SavingsProductsApiResource {
|
||||
.retrieveLockinPeriodFrequencyTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
|
||||
final Collection<EnumOptionData> periodFrequencyOptions = retrievePeriodFrequencyOptions();
|
||||
|
||||
final Collection<PaymentTypeData> paymentTypeOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
||||
|
||||
@@ -268,24 +292,50 @@ public class SavingsProductsApiResource {
|
||||
Collection<ChargeData> penaltyOptions = this.chargeReadPlatformService.retrieveSavingsApplicablePenalties();
|
||||
penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions;
|
||||
final Collection<TaxGroupData> taxGroupOptions = this.taxReadPlatformService.retrieveTaxGroupsForLookUp();
|
||||
final List<CodeValueData> yesNoOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.YES_NO));
|
||||
final List<CodeValueData> accountTypeOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.SAVINGS_PRODUCT_ACCOUNT_TYPE));
|
||||
final List<CodeValueData> customerSegmentOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.SAVINGS_PRODUCT_CUSTOMER_SEGMENT));
|
||||
SavingsProductData savingsProductToReturn = null;
|
||||
if (savingsProduct != null) {
|
||||
savingsProductToReturn = SavingsProductData.withTemplate(savingsProduct, currencyOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions, accountMappingForPayment);
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationTypeOption,
|
||||
interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, periodFrequencyOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions, yesNoOptions,
|
||||
accountTypeOptions, customerSegmentOptions,
|
||||
accountMappingForPayment);
|
||||
} else {
|
||||
savingsProductToReturn = SavingsProductData.template(currency, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions,
|
||||
interestCalculationTypeOption, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
periodFrequencyOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions,
|
||||
penaltyOptions, taxGroupOptions, yesNoOptions, accountTypeOptions, customerSegmentOptions,
|
||||
accountMappingForPayment);
|
||||
savingsProductToReturn.setProductCode(generateNextProductCode("m_savings_product", "SB-IND-"));
|
||||
}
|
||||
|
||||
return savingsProductToReturn;
|
||||
}
|
||||
|
||||
private Collection<EnumOptionData> retrievePeriodFrequencyOptions() {
|
||||
return this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.PERIOD_FREQUENCY).stream()
|
||||
.map(codeValue -> new EnumOptionData(codeValue.getId(),
|
||||
"periodFrequencyType." + codeValue.getName().toLowerCase(Locale.ROOT).replace(' ', '_'), codeValue.getName()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Collection<EnumOptionData> retrieveInterestCalculationTypeOption() {
|
||||
return this.codeValueReadPlatformService.retrieveCodeValuesByCode(SavingsApiConstants.INTEREST_CALCULATION_TYPE_OPTION).stream()
|
||||
.map(codeValue -> new EnumOptionData(codeValue.getId(),
|
||||
"interestCalculationTypeOption." + codeValue.getName().toLowerCase(Locale.ROOT).replace(' ', '_'),
|
||||
codeValue.getName()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{productId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+51
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.savings.api;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -82,6 +83,7 @@ final class SavingsProductsApiResourceSwagger {
|
||||
public Boolean withHoldTax;
|
||||
@Schema(example = "false")
|
||||
public Boolean isDormancyTrackingActive;
|
||||
public List<Map<String, Object>> otherGlMappings;
|
||||
}
|
||||
|
||||
@Schema(description = "PostSavingsProductsResponse")
|
||||
@@ -128,6 +130,25 @@ final class SavingsProductsApiResourceSwagger {
|
||||
public PutSavingsChanges changes;
|
||||
}
|
||||
|
||||
@Schema(description = "GetSavingsProductsProductClassificationOptions")
|
||||
static final class GetSavingsProductsProductClassificationOptions {
|
||||
|
||||
private GetSavingsProductsProductClassificationOptions() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Long id;
|
||||
@Schema(example = "Savings Account Minor")
|
||||
public String name;
|
||||
@Schema(example = "1")
|
||||
public Integer position;
|
||||
@Schema(example = "Savings Account Minor")
|
||||
public String description;
|
||||
@Schema(example = "true")
|
||||
public Boolean active;
|
||||
@Schema(example = "false")
|
||||
public Boolean mandatory;
|
||||
}
|
||||
|
||||
@Schema(description = "GetSavingsProductsResponse")
|
||||
public static final class GetSavingsProductsResponse {
|
||||
|
||||
@@ -377,6 +398,7 @@ final class SavingsProductsApiResourceSwagger {
|
||||
public Set<GetSavingsProductsPenaltyToIncomeAccountMappings> penaltyToIncomeAccountMappings;
|
||||
@Schema(example = "[]")
|
||||
public List<Long> charges;
|
||||
public List<Map<String, Object>> otherGlMappings;
|
||||
}
|
||||
|
||||
@Schema(description = "GetSavingsProductsTemplateResponse")
|
||||
@@ -408,6 +430,30 @@ final class SavingsProductsApiResourceSwagger {
|
||||
public String value;
|
||||
}
|
||||
|
||||
static final class GetSavingsProductsPeriodFrequencyOptions {
|
||||
|
||||
private GetSavingsProductsPeriodFrequencyOptions() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
@Schema(example = "periodFrequencyType.monthly")
|
||||
public String code;
|
||||
@Schema(example = "Monthly")
|
||||
public String value;
|
||||
}
|
||||
|
||||
static final class GetSavingsProductsInterestCalculationTypeOption {
|
||||
|
||||
private GetSavingsProductsInterestCalculationTypeOption() {}
|
||||
|
||||
@Schema(example = "1")
|
||||
public Integer id;
|
||||
@Schema(example = "interestCalculationTypeOption.compounding")
|
||||
public String code;
|
||||
@Schema(example = "Compounding")
|
||||
public String value;
|
||||
}
|
||||
|
||||
static final class GetSavingsProductsWithdrawalFeeTypeOptions {
|
||||
|
||||
private GetSavingsProductsWithdrawalFeeTypeOptions() {}
|
||||
@@ -677,13 +723,18 @@ final class SavingsProductsApiResourceSwagger {
|
||||
public Set<GetSavingsProductsResponse.GetSavingsProductsInterestCompoundingPeriodType> interestCompoundingPeriodTypeOptions;
|
||||
public Set<GetSavingsProductsResponse.GetSavingsProductsInterestPostingPeriodType> interestPostingPeriodTypeOptions;
|
||||
public Set<GetSavingsProductsResponse.GetSavingsProductsInterestCalculationType> interestCalculationTypeOptions;
|
||||
public Set<GetSavingsProductsInterestCalculationTypeOption> interestCalculationTypeOption;
|
||||
public Set<GetSavingsProductsResponse.GetSavingsProductsInterestCalculationDaysInYearType> interestCalculationDaysInYearTypeOptions;
|
||||
public Set<GetSavingsProductsLockinPeriodFrequencyTypeOptions> lockinPeriodFrequencyTypeOptions;
|
||||
public Set<GetSavingsProductsWithdrawalFeeTypeOptions> withdrawalFeeTypeOptions;
|
||||
public Set<GetSavingsProductsPeriodFrequencyOptions> periodFrequencyOptions;
|
||||
public Set<GetSavingsProductsPaymentTypeOptions> paymentTypeOptions;
|
||||
public Set<GetSavingsProductsTemplateAccountingRule> accountingRuleOptions;
|
||||
public GetSavingsProductsAccountingMappingOptions accountingMappingOptions;
|
||||
public Set<GetSavingsProductsChargeOptions> chargeOptions;
|
||||
public Set<GetSavingsProductsProductClassificationOptions> yesNoOptions;
|
||||
public Set<GetSavingsProductsProductClassificationOptions> accountTypeOptions;
|
||||
public Set<GetSavingsProductsProductClassificationOptions> customerSegmentOptions;
|
||||
public GetSavingsProductsResponse.GetSavingsCurrency accountMapping;
|
||||
|
||||
}
|
||||
|
||||
+24
-8
@@ -170,13 +170,21 @@ public class DepositProductDataValidator {
|
||||
|
||||
validatePreClosureDetailForCreate(element, baseDataValidator);
|
||||
|
||||
validateDepositTermDeatilForCreate(element, baseDataValidator);
|
||||
// When custom rdSlabs are provided, minDepositTerm / charts / depositAmount are not required
|
||||
final boolean hasRdSlabs = this.fromApiJsonHelper.parameterExists("rdSlabs", element)
|
||||
&& this.fromApiJsonHelper.extractJsonArrayNamed("rdSlabs", element) != null
|
||||
&& this.fromApiJsonHelper.extractJsonArrayNamed("rdSlabs", element).size() > 0;
|
||||
|
||||
if (!hasRdSlabs) {
|
||||
validateDepositTermDeatilForCreate(element, baseDataValidator);
|
||||
}
|
||||
|
||||
validateRecurringDetailForCreate(element, baseDataValidator);
|
||||
|
||||
validateChartsData(element, baseDataValidator);
|
||||
|
||||
validateDepositAmountForCreate(element, baseDataValidator);
|
||||
if (!hasRdSlabs) {
|
||||
validateChartsData(element, baseDataValidator);
|
||||
validateDepositAmountForCreate(element, baseDataValidator);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
@@ -198,13 +206,21 @@ public class DepositProductDataValidator {
|
||||
|
||||
validatePreClosureDetailForUpdate(element, baseDataValidator);
|
||||
|
||||
validateDepositTermDetailForUpdate(element, baseDataValidator);
|
||||
final boolean hasRdSlabsForUpdate = this.fromApiJsonHelper.parameterExists("rdSlabs", element)
|
||||
&& this.fromApiJsonHelper.extractJsonArrayNamed("rdSlabs", element) != null
|
||||
&& this.fromApiJsonHelper.extractJsonArrayNamed("rdSlabs", element).size() > 0;
|
||||
|
||||
if (!hasRdSlabsForUpdate) {
|
||||
validateDepositTermDetailForUpdate(element, baseDataValidator);
|
||||
}
|
||||
|
||||
validateRecurringDepositUpdate(element, baseDataValidator);
|
||||
|
||||
validateChartsData(element, baseDataValidator);
|
||||
|
||||
validateDepositAmountForUpdate(element, baseDataValidator);
|
||||
// RD products now use the CBS base chart as a read-only template, so
|
||||
// product updates no longer require a charts payload.
|
||||
if (!hasRdSlabsForUpdate) {
|
||||
validateDepositAmountForUpdate(element, baseDataValidator);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class RdInterestRateEffectiveData {
|
||||
|
||||
private Long id;
|
||||
private Long productId;
|
||||
private String versionLabel;
|
||||
private LocalDate effectiveFromDate;
|
||||
private LocalDate effectiveToDate;
|
||||
private Boolean isActive;
|
||||
private String remarks;
|
||||
private List<RdInterestRateSlabData> slabs;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class RdInterestRateSlabData {
|
||||
|
||||
private Long id;
|
||||
private Integer srNo;
|
||||
private String description;
|
||||
private Integer periodTypeEnum;
|
||||
private String periodTypeLabel;
|
||||
private Integer fromPeriod;
|
||||
private Integer toPeriod;
|
||||
private BigDecimal annualInterestRate;
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_rd_base_interest_rate_effective")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdBaseInterestRateEffective {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "version_label")
|
||||
private String versionLabel;
|
||||
|
||||
@Column(name = "effective_from_date")
|
||||
private LocalDate effectiveFromDate;
|
||||
|
||||
@Column(name = "effective_to_date")
|
||||
private LocalDate effectiveToDate;
|
||||
|
||||
@Column(name = "is_active")
|
||||
private Boolean isActive;
|
||||
|
||||
@Column(name = "remarks")
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "created_by_id")
|
||||
private Long createdById;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDate createdDate;
|
||||
|
||||
public static RdBaseInterestRateEffective create(String versionLabel, LocalDate effectiveFromDate, String remarks, Long createdById) {
|
||||
RdBaseInterestRateEffective e = new RdBaseInterestRateEffective();
|
||||
e.versionLabel = versionLabel;
|
||||
e.effectiveFromDate = effectiveFromDate;
|
||||
e.isActive = true;
|
||||
e.remarks = remarks;
|
||||
e.createdById = createdById;
|
||||
e.createdDate = LocalDate.now();
|
||||
return e;
|
||||
}
|
||||
|
||||
public void deactivate(LocalDate toDate) {
|
||||
this.isActive = false;
|
||||
this.effectiveToDate = toDate;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RdBaseInterestRateEffectiveRepository extends JpaRepository<RdBaseInterestRateEffective, Long> {
|
||||
|
||||
List<RdBaseInterestRateEffective> findByIsActiveTrueOrderByEffectiveFromDateDesc();
|
||||
|
||||
List<RdBaseInterestRateEffective> findByOrderByEffectiveFromDateDesc();
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_rd_base_interest_rate_slab_history")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdBaseInterestRateSlabHistory {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "effective_id")
|
||||
private Long effectiveId;
|
||||
|
||||
@Column(name = "sr_no")
|
||||
private Integer srNo;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "period_type_enum")
|
||||
private Integer periodTypeEnum;
|
||||
|
||||
@Column(name = "from_period")
|
||||
private Integer fromPeriod;
|
||||
|
||||
@Column(name = "to_period")
|
||||
private Integer toPeriod;
|
||||
|
||||
@Column(name = "annual_interest_rate")
|
||||
private BigDecimal annualInterestRate;
|
||||
|
||||
public static RdBaseInterestRateSlabHistory create(Long effectiveId, Integer srNo,
|
||||
String description, Integer periodTypeEnum, Integer fromPeriod, Integer toPeriod, BigDecimal annualInterestRate) {
|
||||
RdBaseInterestRateSlabHistory s = new RdBaseInterestRateSlabHistory();
|
||||
s.effectiveId = effectiveId;
|
||||
s.srNo = srNo;
|
||||
s.description = description;
|
||||
s.periodTypeEnum = periodTypeEnum != null ? periodTypeEnum : 1;
|
||||
s.fromPeriod = fromPeriod;
|
||||
s.toPeriod = toPeriod;
|
||||
s.annualInterestRate = annualInterestRate;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RdBaseInterestRateSlabHistoryRepository extends JpaRepository<RdBaseInterestRateSlabHistory, Long> {
|
||||
|
||||
List<RdBaseInterestRateSlabHistory> findByEffectiveIdOrderBySrNo(Long effectiveId);
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Maps to: m_rd_interest_rate_effective
|
||||
* Stores one "version" of the bank's RD interest rate chart for a product.
|
||||
* When a new version is saved the old one is deactivated (is_active=0) but kept for history.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_rd_interest_rate_effective")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdInterestRateEffective {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "product_id")
|
||||
private Long productId;
|
||||
|
||||
@Column(name = "version_label")
|
||||
private String versionLabel;
|
||||
|
||||
@Column(name = "effective_from_date")
|
||||
private LocalDate effectiveFromDate;
|
||||
|
||||
@Column(name = "effective_to_date")
|
||||
private LocalDate effectiveToDate;
|
||||
|
||||
@Column(name = "is_active")
|
||||
private Boolean isActive;
|
||||
|
||||
@Column(name = "remarks")
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "created_by_id")
|
||||
private Long createdById;
|
||||
|
||||
@Column(name = "created_date")
|
||||
private LocalDate createdDate;
|
||||
|
||||
public static RdInterestRateEffective create(Long productId, String versionLabel,
|
||||
LocalDate effectiveFromDate, String remarks, Long createdById) {
|
||||
RdInterestRateEffective e = new RdInterestRateEffective();
|
||||
e.productId = productId;
|
||||
e.versionLabel = versionLabel;
|
||||
e.effectiveFromDate = effectiveFromDate;
|
||||
e.isActive = true;
|
||||
e.remarks = remarks;
|
||||
e.createdById = createdById;
|
||||
e.createdDate = LocalDate.now();
|
||||
return e;
|
||||
}
|
||||
|
||||
public void deactivate(LocalDate toDate) {
|
||||
this.isActive = false;
|
||||
this.effectiveToDate = toDate;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RdInterestRateEffectiveRepository extends JpaRepository<RdInterestRateEffective, Long> {
|
||||
|
||||
List<RdInterestRateEffective> findByProductIdOrderByEffectiveFromDateDesc(Long productId);
|
||||
|
||||
List<RdInterestRateEffective> findByProductIdAndIsActiveTrueOrderByEffectiveFromDateDesc(Long productId);
|
||||
|
||||
List<RdInterestRateEffective> findByIsActiveTrueOrderByEffectiveFromDateDesc();
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Maps to: m_rd_interest_rate_slab_history
|
||||
* One row per slab per version. All historical slabs are preserved.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_rd_interest_rate_slab_history")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RdInterestRateSlabHistory {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "effective_id")
|
||||
private Long effectiveId;
|
||||
|
||||
@Column(name = "sr_no")
|
||||
private Integer srNo;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "period_type_enum")
|
||||
private Integer periodTypeEnum;
|
||||
|
||||
@Column(name = "from_period")
|
||||
private Integer fromPeriod;
|
||||
|
||||
@Column(name = "to_period")
|
||||
private Integer toPeriod;
|
||||
|
||||
@Column(name = "annual_interest_rate")
|
||||
private BigDecimal annualInterestRate;
|
||||
|
||||
public static RdInterestRateSlabHistory create(Long effectiveId, Integer srNo,
|
||||
String description, Integer periodTypeEnum,
|
||||
Integer fromPeriod, Integer toPeriod, BigDecimal annualInterestRate) {
|
||||
RdInterestRateSlabHistory s = new RdInterestRateSlabHistory();
|
||||
s.effectiveId = effectiveId;
|
||||
s.srNo = srNo;
|
||||
s.description = description;
|
||||
s.periodTypeEnum = periodTypeEnum != null ? periodTypeEnum : 1;
|
||||
s.fromPeriod = fromPeriod;
|
||||
s.toPeriod = toPeriod;
|
||||
s.annualInterestRate = annualInterestRate;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RdInterestRateSlabHistoryRepository extends JpaRepository<RdInterestRateSlabHistory, Long> {
|
||||
|
||||
List<RdInterestRateSlabHistory> findByEffectiveIdOrderBySrNo(Long effectiveId);
|
||||
}
|
||||
+128
@@ -19,7 +19,27 @@
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountFacilityConfigParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountNoParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountStatusParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.guardianClientIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.modeOfOperationParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountTypeParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.customerSegmentParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.guardianCifNumberParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.guardianNameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.guardianRelationshipParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerCifNumberParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerNameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.jointHoldersParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.linkedAccountIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.linkedAccountNoParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.linkedAccountTypeParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minorDateOfBirthParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nomineeParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.specialCategoryParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.weakerSectionParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.clientIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName;
|
||||
@@ -45,7 +65,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.submitte
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
@@ -336,9 +359,114 @@ public class SavingsAccountAssembler {
|
||||
|
||||
account.validateAccountValuesWithProduct();
|
||||
|
||||
// CBS extended fields
|
||||
applyCbsFields(command, element, account);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private 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);
|
||||
|
||||
final String customerSegment = this.fromApiJsonHelper.extractStringNamed(customerSegmentParamName, element);
|
||||
if (customerSegment != null) account.setCustomerSegment(customerSegment);
|
||||
|
||||
// Status of Account
|
||||
final String accountStatus = this.fromApiJsonHelper.extractStringNamed(accountStatusParamName, element);
|
||||
if (accountStatus != null) account.setAccountStatus(accountStatus);
|
||||
|
||||
final String modeOfOperation = this.fromApiJsonHelper.extractStringNamed(modeOfOperationParamName, element);
|
||||
if (modeOfOperation != null) account.setModeOfOperation(modeOfOperation);
|
||||
|
||||
account.setSpecialCategory(this.fromApiJsonHelper.extractStringNamed(specialCategoryParamName, element));
|
||||
account.setWeakerSection(this.fromApiJsonHelper.extractStringNamed(weakerSectionParamName, element));
|
||||
account.setGuardianName(this.fromApiJsonHelper.extractStringNamed(guardianNameParamName, element));
|
||||
account.setGuardianCifNumber(this.fromApiJsonHelper.extractStringNamed(guardianCifNumberParamName, element));
|
||||
account.setGuardianRelationship(this.fromApiJsonHelper.extractStringNamed(guardianRelationshipParamName, element));
|
||||
|
||||
if (command.parameterExists(minorDateOfBirthParamName)) {
|
||||
final LocalDate minorDob = this.fromApiJsonHelper.extractLocalDateNamed(minorDateOfBirthParamName, element);
|
||||
account.setMinorDateOfBirth(minorDob);
|
||||
}
|
||||
|
||||
final Long guardianClientId = this.fromApiJsonHelper.extractLongNamed(guardianClientIdParamName, element);
|
||||
if (guardianClientId != null) account.setGuardianClientId(guardianClientId);
|
||||
|
||||
// Introducer
|
||||
final Long introducerId = this.fromApiJsonHelper.extractLongNamed(introducerIdParamName, element);
|
||||
if (introducerId != null) account.setIntroducerClientId(introducerId);
|
||||
account.setIntroducerName(this.fromApiJsonHelper.extractStringNamed(introducerNameParamName, element));
|
||||
account.setIntroducerCifNumber(this.fromApiJsonHelper.extractStringNamed(introducerCifNumberParamName, element));
|
||||
|
||||
// Nominee (stored as JSON string)
|
||||
if (element.getAsJsonObject().has(nomineeParamName)) {
|
||||
final JsonElement nomineeEl = element.getAsJsonObject().get(nomineeParamName);
|
||||
if (nomineeEl != null && !nomineeEl.isJsonNull()) {
|
||||
account.setNomineeData(nomineeEl.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Deposit Account Link
|
||||
final Long linkedAccountId = this.fromApiJsonHelper.extractLongNamed(linkedAccountIdParamName, element);
|
||||
if (linkedAccountId != null) account.setLinkedAccountId(linkedAccountId);
|
||||
account.setLinkedAccountType(this.fromApiJsonHelper.extractStringNamed(linkedAccountTypeParamName, element));
|
||||
account.setLinkedAccountNo(this.fromApiJsonHelper.extractStringNamed(linkedAccountNoParamName, element));
|
||||
|
||||
// Account Facility Config
|
||||
if (element.getAsJsonObject().has(accountFacilityConfigParamName)) {
|
||||
final JsonElement configEl = element.getAsJsonObject().get(accountFacilityConfigParamName);
|
||||
if (configEl != null && !configEl.isJsonNull() && configEl.isJsonObject()) {
|
||||
SavingsAccountFacilityConfig facilityConfig = account.getFacilityConfig();
|
||||
if (facilityConfig == null) {
|
||||
facilityConfig = new SavingsAccountFacilityConfig(account);
|
||||
account.setFacilityConfig(facilityConfig);
|
||||
}
|
||||
facilityConfig.updateFrom(configEl.getAsJsonObject());
|
||||
}
|
||||
}
|
||||
|
||||
// Joint holders
|
||||
if (element.getAsJsonObject().has(jointHoldersParamName)) {
|
||||
final JsonElement holdersEl = element.getAsJsonObject().get(jointHoldersParamName);
|
||||
if (holdersEl != null && !holdersEl.isJsonNull() && holdersEl.isJsonArray()) {
|
||||
final com.google.gson.JsonArray holdersArray = holdersEl.getAsJsonArray();
|
||||
final List<SavingsAccountJointHolder> holders = new ArrayList<>();
|
||||
for (int i = 0; i < holdersArray.size(); i++) {
|
||||
final JsonObject h = holdersArray.get(i).getAsJsonObject();
|
||||
final Long clientId = h.has("clientId") && !h.get("clientId").isJsonNull()
|
||||
? h.get("clientId").getAsLong() : null;
|
||||
final String cifNumber = h.has("cifNumber") && !h.get("cifNumber").isJsonNull()
|
||||
? h.get("cifNumber").getAsString() : null;
|
||||
final String displayName = h.has("displayName") && !h.get("displayName").isJsonNull()
|
||||
? h.get("displayName").getAsString() : null;
|
||||
final BigDecimal sharePercentage = h.has("sharePercentage") && !h.get("sharePercentage").isJsonNull()
|
||||
? h.get("sharePercentage").getAsBigDecimal() : null;
|
||||
final boolean isPrimary = h.has("isPrimary") && h.get("isPrimary").getAsBoolean();
|
||||
String signingAuthority = h.has("signingAuthority") && !h.get("signingAuthority").isJsonNull()
|
||||
? h.get("signingAuthority").getAsString() : null;
|
||||
String role = h.has("role") && !h.get("role").isJsonNull()
|
||||
? h.get("role").getAsString() : null;
|
||||
|
||||
if (signingAuthority == null || signingAuthority.trim().isEmpty()) {
|
||||
signingAuthority = isPrimary ? "Mandatory" : "Optional";
|
||||
}
|
||||
if (role == null || role.trim().isEmpty()) {
|
||||
role = isPrimary ? "Primary Account Holder" : "Co-Holder";
|
||||
}
|
||||
|
||||
holders.add(new SavingsAccountJointHolder(account, clientId, cifNumber, displayName,
|
||||
sharePercentage, signingAuthority, role, isPrimary));
|
||||
}
|
||||
account.getJointHolders().clear();
|
||||
account.getJointHolders().addAll(holders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SavingsAccount assembleFrom(final Long savingsId, final boolean backdatedTxnsAllowedTill) {
|
||||
SavingsAccount account = this.savingsAccountRepository.findSavingsWithNotFoundDetection(savingsId, backdatedTxnsAllowedTill);
|
||||
return loadTransactionsToSavingsAccount(account, backdatedTxnsAllowedTill);
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTPRODUCT", action = "CREATE")
|
||||
public class CreateCurrentProductCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsProductWritePlatformService productWritePlatformService;
|
||||
private final CurrentProductWritePlatformService productWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CreateCurrentProductCommandHandler(final SavingsProductWritePlatformService productWritePlatformService) {
|
||||
public CreateCurrentProductCommandHandler(final CurrentProductWritePlatformService productWritePlatformService) {
|
||||
this.productWritePlatformService = productWritePlatformService;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTPRODUCT", action = "DELETE")
|
||||
public class DeleteCurrentProductCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsProductWritePlatformService productWritePlatformService;
|
||||
private final CurrentProductWritePlatformService productWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public DeleteCurrentProductCommandHandler(final SavingsProductWritePlatformService productWritePlatformService) {
|
||||
public DeleteCurrentProductCommandHandler(final CurrentProductWritePlatformService productWritePlatformService) {
|
||||
this.productWritePlatformService = productWritePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.productWritePlatformService.delete(command.getProductId());
|
||||
return this.productWritePlatformService.delete(command.entityId());
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTPRODUCT", action = "UPDATE")
|
||||
public class UpdateCurrentProductCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsProductWritePlatformService productWritePlatformService;
|
||||
private final CurrentProductWritePlatformService productWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateCurrentProductCommandHandler(final SavingsProductWritePlatformService productWritePlatformService) {
|
||||
public UpdateCurrentProductCommandHandler(final CurrentProductWritePlatformService productWritePlatformService) {
|
||||
this.productWritePlatformService = productWritePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.productWritePlatformService.update(command.getProductId(), command);
|
||||
return this.productWritePlatformService.update(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+375
@@ -0,0 +1,375 @@
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountReadPlatformService {
|
||||
|
||||
private static final String BASE_SQL =
|
||||
"SELECT ca.*, "
|
||||
+ "c.display_name AS client_name, c.account_no AS client_account_no, "
|
||||
+ "o.name AS office_name, "
|
||||
+ "s.display_name AS staff_name, "
|
||||
+ "cp.name AS product_name, cp.short_name AS product_short_name, "
|
||||
+ "curr.name AS currency_name, curr.display_symbol AS currency_display_symbol, "
|
||||
+ "curr.internationalized_name_code AS currency_name_code "
|
||||
+ "FROM m_current_account ca "
|
||||
+ "LEFT JOIN m_client c ON c.id = ca.client_id "
|
||||
+ "LEFT JOIN m_office o ON o.id = ca.office_id "
|
||||
+ "LEFT JOIN m_staff s ON s.id = ca.staff_id "
|
||||
+ "LEFT JOIN m_current_product cp ON cp.id = ca.product_id "
|
||||
+ "LEFT JOIN m_currency curr ON curr.code = ca.currency_code ";
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> retrieveOne(Long accountId) {
|
||||
List<Map<String, Object>> rows = jdbcTemplate.query(
|
||||
BASE_SQL + "WHERE ca.id = ?",
|
||||
new FlatRowMapper(),
|
||||
accountId);
|
||||
if (rows.isEmpty()) {
|
||||
throw new CurrentAccountNotFoundException(accountId);
|
||||
}
|
||||
return buildStructuredResponse(rows.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> retrieveAll() {
|
||||
List<Map<String, Object>> rows = jdbcTemplate.query(BASE_SQL, new FlatRowMapper());
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
result.add(buildStructuredResponse(row));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildStructuredResponse(Map<String, Object> row) {
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
|
||||
out.put("id", row.get("id"));
|
||||
out.put("accountNo", row.get("account_no"));
|
||||
out.put("externalId", row.get("external_id"));
|
||||
out.put("clientId", row.get("client_id"));
|
||||
out.put("clientName", row.get("client_name"));
|
||||
out.put("clientAccountNo", row.get("client_account_no"));
|
||||
out.put("groupId", row.get("group_id"));
|
||||
out.put("productId", row.get("product_id"));
|
||||
out.put("productName", 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("fieldOfficerName", row.get("staff_name"));
|
||||
|
||||
// status
|
||||
int statusEnum = toInt(row.get("status_enum"), 100);
|
||||
out.put("status", buildStatus(statusEnum));
|
||||
|
||||
// subStatus
|
||||
int subStatusEnum = toInt(row.get("sub_status_enum"), 0);
|
||||
out.put("subStatus", buildSubStatus(subStatusEnum));
|
||||
|
||||
// accountType
|
||||
int accountTypeEnum = toInt(row.get("account_type_enum"), 1);
|
||||
out.put("accountType", buildAccountType(accountTypeEnum));
|
||||
|
||||
// timeline
|
||||
out.put("timeline", buildTimeline(row));
|
||||
|
||||
// currency
|
||||
out.put("currency", buildCurrency(row));
|
||||
|
||||
// interest settings
|
||||
out.put("nominalAnnualInterestRate", row.get("nominal_annual_interest_rate"));
|
||||
|
||||
int compPeriod = toInt(row.get("interest_compounding_period_enum"), 4);
|
||||
out.put("interestCompoundingPeriodType", buildEnumOption(compPeriod, interestCompoundingPeriodValue(compPeriod)));
|
||||
|
||||
int postingPeriod = toInt(row.get("interest_posting_period_enum"), 4);
|
||||
out.put("interestPostingPeriodType", buildEnumOption(postingPeriod, interestPostingPeriodValue(postingPeriod)));
|
||||
|
||||
int calcType = toInt(row.get("interest_calculation_type_enum"), 1);
|
||||
out.put("interestCalculationType", buildEnumOption(calcType, interestCalcTypeValue(calcType)));
|
||||
|
||||
int daysInYear = toInt(row.get("interest_calculation_days_in_year_type_enum"), 365);
|
||||
out.put("interestCalculationDaysInYearType", buildEnumOption(daysInYear, daysInYearValue(daysInYear)));
|
||||
|
||||
out.put("minBalanceForInterestCalculation", row.get("min_balance_for_interest_calculation"));
|
||||
|
||||
// balance/overdraft
|
||||
out.put("allowOverdraft", row.get("allow_overdraft"));
|
||||
out.put("overdraftLimit", row.get("overdraft_limit"));
|
||||
out.put("minOverdraftForInterestCalculation", row.get("min_overdraft_for_interest_calculation"));
|
||||
out.put("minRequiredBalance", row.get("min_required_balance"));
|
||||
out.put("enforceMinRequiredBalance", row.get("enforce_min_required_balance"));
|
||||
out.put("onHoldFunds", row.get("on_hold_funds_derived"));
|
||||
out.put("withHoldTax", row.get("withhold_tax"));
|
||||
out.put("taxGroup", null);
|
||||
|
||||
// summary
|
||||
out.put("summary", buildSummary(row));
|
||||
|
||||
// CBS-specific fields
|
||||
out.put("cbsAccountType", row.get("cbs_account_type"));
|
||||
out.put("customerSegment", row.get("customer_segment"));
|
||||
out.put("accountStatus", row.get("account_status"));
|
||||
out.put("modeOfOperation", row.get("mode_of_operation"));
|
||||
out.put("specialCategory", row.get("special_category"));
|
||||
out.put("msme", row.get("msme"));
|
||||
out.put("autoDebitFacility", row.get("auto_debit_facility"));
|
||||
out.put("balanceSwapOption", row.get("balance_swap_option"));
|
||||
out.put("establishmentDate", row.get("establishment_date"));
|
||||
out.put("startOperationDate", row.get("start_operation_date"));
|
||||
out.put("nonIndividualClient", row.get("is_non_individual_client"));
|
||||
out.put("signatoryRights", row.get("signatory_rights"));
|
||||
out.put("introducerClientId", row.get("introducer_client_id"));
|
||||
out.put("introducerName", row.get("introducer_name"));
|
||||
out.put("introducerCifNumber", row.get("introducer_cif_number"));
|
||||
out.put("nomineeData", row.get("nominee_data"));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildStatus(int statusEnum) {
|
||||
Map<String, Object> s = new LinkedHashMap<>();
|
||||
s.put("id", statusEnum);
|
||||
switch (statusEnum) {
|
||||
case 100 -> {
|
||||
s.put("code", "savingsAccountStatusType.submittedAndPendingApproval");
|
||||
s.put("value", "Submitted and Pending Approval");
|
||||
s.put("submittedAndPendingApproval", true);
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", false);
|
||||
}
|
||||
case 200 -> {
|
||||
s.put("code", "savingsAccountStatusType.approved");
|
||||
s.put("value", "Approved");
|
||||
s.put("submittedAndPendingApproval", false);
|
||||
s.put("approved", true); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", false);
|
||||
}
|
||||
case 300 -> {
|
||||
s.put("code", "savingsAccountStatusType.active");
|
||||
s.put("value", "Active");
|
||||
s.put("submittedAndPendingApproval", false);
|
||||
s.put("approved", false); s.put("active", true);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", false);
|
||||
}
|
||||
case 400 -> {
|
||||
s.put("code", "savingsAccountStatusType.withdrawn");
|
||||
s.put("value", "Withdrawn by Applicant");
|
||||
s.put("submittedAndPendingApproval", false);
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", true); s.put("closed", false);
|
||||
}
|
||||
case 500 -> {
|
||||
s.put("code", "savingsAccountStatusType.rejected");
|
||||
s.put("value", "Rejected");
|
||||
s.put("submittedAndPendingApproval", false);
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", true); s.put("withdrawnByApplicant", false); s.put("closed", false);
|
||||
}
|
||||
case 600 -> {
|
||||
s.put("code", "savingsAccountStatusType.closed");
|
||||
s.put("value", "Closed");
|
||||
s.put("submittedAndPendingApproval", false);
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", true);
|
||||
}
|
||||
default -> {
|
||||
s.put("code", "savingsAccountStatusType.invalid");
|
||||
s.put("value", "Invalid");
|
||||
s.put("submittedAndPendingApproval", false);
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", false);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildSubStatus(int subStatusEnum) {
|
||||
Map<String, Object> s = new LinkedHashMap<>();
|
||||
s.put("id", subStatusEnum);
|
||||
String value = switch (subStatusEnum) {
|
||||
case 0 -> "None";
|
||||
case 100 -> "Inactive";
|
||||
case 200 -> "Dormant";
|
||||
case 300 -> "Block";
|
||||
case 400 -> "Block Debit";
|
||||
case 500 -> "Block Credit";
|
||||
default -> "None";
|
||||
};
|
||||
s.put("code", "SavingsAccountSubStatusEnum." + value.replace(" ", ""));
|
||||
s.put("value", value);
|
||||
return s;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildAccountType(int accountTypeEnum) {
|
||||
Map<String, Object> s = new LinkedHashMap<>();
|
||||
s.put("id", accountTypeEnum);
|
||||
String value = switch (accountTypeEnum) {
|
||||
case 1 -> "Individual";
|
||||
case 2 -> "Group";
|
||||
case 3 -> "JLG";
|
||||
default -> "Individual";
|
||||
};
|
||||
s.put("code", "loanType." + value.toLowerCase());
|
||||
s.put("value", value);
|
||||
return s;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildTimeline(Map<String, Object> row) {
|
||||
Map<String, Object> t = new LinkedHashMap<>();
|
||||
t.put("submittedOnDate", row.get("submittedon_date"));
|
||||
t.put("approvedOnDate", row.get("approvedon_date"));
|
||||
t.put("activatedOnDate", row.get("activatedon_date"));
|
||||
t.put("rejectedOnDate", row.get("rejectedon_date"));
|
||||
t.put("withdrawnOnDate", row.get("withdrawnon_date"));
|
||||
t.put("closedOnDate", row.get("closedon_date"));
|
||||
return t;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildCurrency(Map<String, Object> row) {
|
||||
Map<String, Object> c = new LinkedHashMap<>();
|
||||
c.put("code", row.get("currency_code"));
|
||||
c.put("name", row.get("currency_name"));
|
||||
c.put("decimalPlaces", row.get("currency_digits"));
|
||||
c.put("inMultiplesOf", row.get("currency_multiplesof"));
|
||||
c.put("displaySymbol", row.get("currency_display_symbol"));
|
||||
c.put("nameCode", row.get("currency_name_code"));
|
||||
c.put("displayLabel", row.get("currency_name") + " [" + row.get("currency_code") + "]");
|
||||
return c;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildSummary(Map<String, Object> row) {
|
||||
Map<String, Object> s = new LinkedHashMap<>();
|
||||
s.put("accountBalance", row.get("account_balance_derived"));
|
||||
s.put("availableBalance", row.get("account_balance_derived"));
|
||||
s.put("totalDeposits", row.get("total_deposits_derived"));
|
||||
s.put("totalWithdrawals", row.get("total_withdrawals_derived"));
|
||||
s.put("totalInterestEarned", row.get("total_interest_earned_derived"));
|
||||
s.put("totalInterestPosted", row.get("total_interest_posted_derived"));
|
||||
s.put("interestNotPosted", computeInterestNotPosted(row));
|
||||
s.put("totalOverdraftInterestDerived", row.get("total_overdraft_interest_derived"));
|
||||
s.put("totalWithholdTax", row.get("total_withhold_tax_derived"));
|
||||
s.put("totalWithdrawalFees", null);
|
||||
s.put("totalAnnualFees", null);
|
||||
s.put("lastInterestCalculationDate", row.get("last_interest_calculation_date"));
|
||||
s.put("onHoldFunds", row.get("on_hold_funds_derived"));
|
||||
return s;
|
||||
}
|
||||
|
||||
private BigDecimal computeInterestNotPosted(Map<String, Object> row) {
|
||||
BigDecimal earned = toBigDecimal(row.get("total_interest_earned_derived"));
|
||||
BigDecimal posted = toBigDecimal(row.get("total_interest_posted_derived"));
|
||||
if (earned == null) return BigDecimal.ZERO;
|
||||
if (posted == null) return earned;
|
||||
return earned.subtract(posted);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildEnumOption(int id, String value) {
|
||||
Map<String, Object> e = new LinkedHashMap<>();
|
||||
e.put("id", id);
|
||||
e.put("code", value.replace(" ", ""));
|
||||
e.put("value", value);
|
||||
return e;
|
||||
}
|
||||
|
||||
private String interestCompoundingPeriodValue(int id) {
|
||||
return switch (id) {
|
||||
case 1 -> "Daily";
|
||||
case 2 -> "Weekly";
|
||||
case 3 -> "Bi-Weekly";
|
||||
case 4 -> "Monthly";
|
||||
case 5 -> "Quarterly";
|
||||
case 6 -> "Semi-Annual";
|
||||
case 7 -> "Annually";
|
||||
default -> "Monthly";
|
||||
};
|
||||
}
|
||||
|
||||
private String interestPostingPeriodValue(int id) {
|
||||
return switch (id) {
|
||||
case 1 -> "Monthly";
|
||||
case 2 -> "Quarterly";
|
||||
case 3 -> "Biannual";
|
||||
case 4 -> "Annually";
|
||||
default -> "Annually";
|
||||
};
|
||||
}
|
||||
|
||||
private String interestCalcTypeValue(int id) {
|
||||
return switch (id) {
|
||||
case 1 -> "Daily Balance";
|
||||
case 2 -> "Average Daily Balance";
|
||||
default -> "Daily Balance";
|
||||
};
|
||||
}
|
||||
|
||||
private String daysInYearValue(int id) {
|
||||
return switch (id) {
|
||||
case 360 -> "360 Days";
|
||||
case 365 -> "365 Days";
|
||||
case 366 -> "Actual Days";
|
||||
default -> "365 Days";
|
||||
};
|
||||
}
|
||||
|
||||
private int toInt(Object val, int defaultVal) {
|
||||
if (val == null) return defaultVal;
|
||||
if (val instanceof Number) return ((Number) val).intValue();
|
||||
try { return Integer.parseInt(val.toString().trim()); } catch (Exception e) { return defaultVal; }
|
||||
}
|
||||
|
||||
private BigDecimal toBigDecimal(Object val) {
|
||||
if (val == null) return null;
|
||||
if (val instanceof BigDecimal) return (BigDecimal) val;
|
||||
try { return new BigDecimal(val.toString().trim()); } catch (Exception e) { return null; }
|
||||
}
|
||||
|
||||
private static class FlatRowMapper implements RowMapper<Map<String, Object>> {
|
||||
@Override
|
||||
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
ResultSetMetaData meta = rs.getMetaData();
|
||||
int cols = meta.getColumnCount();
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
for (int i = 1; i <= cols; i++) {
|
||||
row.put(meta.getColumnLabel(i), rs.getObject(i));
|
||||
}
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
+482
@@ -0,0 +1,482 @@
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.staff.data.StaffData;
|
||||
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.charge.util.ConvertChargeDataToSpecificChargeData;
|
||||
import org.apache.fineract.portfolio.client.data.ClientData;
|
||||
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
|
||||
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimelineData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Slf4j
|
||||
public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAccountTemplateReadPlatformService {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final ClientReadPlatformService clientReadPlatformService;
|
||||
private final GroupReadPlatformService groupReadPlatformService;
|
||||
private final CurrentProductReadPlatformService currentProductReadPlatformService;
|
||||
private final StaffReadPlatformService staffReadPlatformService;
|
||||
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
|
||||
private final AccountNumberGenerator accountNumberGenerator;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
|
||||
public CurrentAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
|
||||
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
|
||||
final CurrentProductReadPlatformService currentProductReadPlatformService,
|
||||
final StaffReadPlatformService staffReadPlatformService,
|
||||
final SavingsDropdownReadPlatformService dropdownReadPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService,
|
||||
final EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
final ColumnValidator columnValidator,
|
||||
final AccountNumberGenerator accountNumberGenerator,
|
||||
final CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.clientReadPlatformService = clientReadPlatformService;
|
||||
this.groupReadPlatformService = groupReadPlatformService;
|
||||
this.currentProductReadPlatformService = currentProductReadPlatformService;
|
||||
this.staffReadPlatformService = staffReadPlatformService;
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
|
||||
this.accountNumberGenerator = accountNumberGenerator;
|
||||
this.codeValueReadPlatformService = codeValueReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsAccountData retrieveTemplate(final Long clientId, final Long groupId, final Long productId,
|
||||
final boolean staffInSelectedOfficeOnly) {
|
||||
|
||||
final AppUser loggedInUser = this.context.authenticatedUser();
|
||||
Long officeId = loggedInUser.getOffice().getId();
|
||||
|
||||
ClientData client = null;
|
||||
if (clientId != null) {
|
||||
client = this.clientReadPlatformService.retrieveOne(clientId);
|
||||
officeId = client.getOfficeId();
|
||||
}
|
||||
|
||||
GroupGeneralData group = null;
|
||||
if (groupId != null) {
|
||||
group = this.groupReadPlatformService.retrieveOne(groupId);
|
||||
officeId = group.getOfficeId();
|
||||
}
|
||||
|
||||
final Collection<SavingsProductData> productOptions = this.currentProductReadPlatformService.retrieveAllForLookup();
|
||||
SavingsAccountData template = null;
|
||||
if (productId != null) {
|
||||
|
||||
final CurrentAccountTemplateMapper mapper = new CurrentAccountTemplateMapper(client, group);
|
||||
|
||||
final String sql = "select " + mapper.schema() + " where cp.id = ?";
|
||||
template = this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { productId }); // NOSONAR
|
||||
|
||||
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveCompoundingInterestPeriodTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestPostingPeriodTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestCalculationTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestCalculationDaysInYearTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveLockinPeriodFrequencyTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
|
||||
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
final Collection<ChargeData> productCharges = this.chargeReadPlatformService.retrieveCurrentProductCharges(productId);
|
||||
final Collection<SavingsAccountChargeData> charges = fromChargesToSavingsCharges(productCharges);
|
||||
|
||||
final boolean feeChargesOnly = false;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
|
||||
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
|
||||
|
||||
Collection<StaffData> fieldOfficerOptions = null;
|
||||
|
||||
if (officeId != null) {
|
||||
if (staffInSelectedOfficeOnly) {
|
||||
final Collection<StaffData> fieldOfficersInBranch = this.staffReadPlatformService
|
||||
.retrieveAllLoanOfficersInOfficeById(officeId);
|
||||
if (!CollectionUtils.isEmpty(fieldOfficersInBranch)) {
|
||||
fieldOfficerOptions = new ArrayList<>(fieldOfficersInBranch);
|
||||
}
|
||||
} else {
|
||||
final boolean restrictToLoanOfficersOnly = true;
|
||||
final Collection<StaffData> loanOfficersInHierarchy = this.staffReadPlatformService
|
||||
.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, restrictToLoanOfficersOnly);
|
||||
if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) {
|
||||
fieldOfficerOptions = new ArrayList<>(loanOfficersInHierarchy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
|
||||
charges, chargeOptions);
|
||||
|
||||
// Re-apply CBS fields from full product data (withTemplateOptions creates a new object and loses setter fields)
|
||||
final SavingsProductData productData = this.currentProductReadPlatformService.retrieveOne(productId);
|
||||
if (productData != null) {
|
||||
// Feature flags
|
||||
template.setDormancyAlert(productData.getDormancyAlert() != null && productData.getDormancyAlert());
|
||||
template.setIsDormancyTrackingActiveFromProduct(productData.getIsDormancyTrackingActive());
|
||||
template.setDaysToInactiveFromProduct(productData.getDaysToInactive());
|
||||
template.setDaysToDormancyFromProduct(productData.getDaysToDormancy());
|
||||
template.setDaysToEscheatFromProduct(productData.getDaysToEscheat());
|
||||
template.setBalanceSwapOption(productData.getBalanceSwapOption() != null && productData.getBalanceSwapOption());
|
||||
template.setMsme(productData.getMsme() != null && productData.getMsme());
|
||||
template.setNominationFacility(productData.getNominationFacility() != null && productData.getNominationFacility());
|
||||
template.setAutoDebitFacility(productData.getAutoDebitFacility() != null && productData.getAutoDebitFacility());
|
||||
template.setAutoPost(productData.getAutoPost() != null && productData.getAutoPost());
|
||||
template.setFrequencyFixDay(productData.getFrequencyFixDay());
|
||||
template.setLastInterestDate(productData.getLastInterestDate());
|
||||
template.setInterestCalculateSelector(productData.getInterestCalculateSelector());
|
||||
template.setAveragePeriod(productData.getAveragePeriod());
|
||||
template.setAccountClosedZeroBalanceInDayClosing(
|
||||
productData.getAccountClosedZeroBalanceInDayClosing() != null && productData.getAccountClosedZeroBalanceInDayClosing());
|
||||
// Facility data
|
||||
template.setAccountFacilityConfig(productData.getAccountFacilityConfig());
|
||||
template.setAccountFacilityLimits(productData.getAccountFacilityLimits());
|
||||
template.setDepositLimitKyc(productData.getDepositLimitKyc());
|
||||
// Classification fields
|
||||
template.setAccountType(productData.getAccountType());
|
||||
template.setCustomerSegment(productData.getCustomerSegment());
|
||||
final Collection<CodeValueData> accountTypeOpts = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(SavingsApiConstants.CURRENT_PRODUCT_ACCOUNT_TYPE);
|
||||
final Collection<CodeValueData> customerSegmentOpts = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(SavingsApiConstants.CURRENT_PRODUCT_CUSTOMER_SEGMENT);
|
||||
template.setAccountTypeOptions(accountTypeOpts);
|
||||
template.setCustomerSegmentOptions(customerSegmentOpts);
|
||||
template.setProductCode(productData.getProductCode());
|
||||
template.setEffectiveDate(productData.getEffectiveDate());
|
||||
template.setEffectiveTo(productData.getEffectiveTo());
|
||||
template.setEndDate(productData.getEndDate());
|
||||
template.setIntroducerRequired(productData.getIntroducerRequired());
|
||||
template.setShareLinkingEnabled(productData.getShareLinkingEnabled());
|
||||
// Settings fields
|
||||
template.setEnableLockinPeriod(productData.getEnableLockinPeriod());
|
||||
template.setBalanceFrequency(productData.getBalanceFrequency());
|
||||
template.setAvgBalanceCalcPeriod(productData.getAvgBalanceCalcPeriod());
|
||||
template.setValidForPeriod(productData.getValidForPeriod());
|
||||
template.setOdAccountClosedZeroBalanceInDayClosing(
|
||||
productData.getOdAccountClosedZeroBalanceInDayClosing() != null && productData.getOdAccountClosedZeroBalanceInDayClosing());
|
||||
// Generate preview account number using current product's own GL account ID
|
||||
try {
|
||||
final String preview = accountNumberGenerator.previewAccountNumberByGlAccountId(productData.getGlAccountId(), officeId);
|
||||
template.setPreviewAccountNo(preview);
|
||||
} catch (final Exception ex) {
|
||||
log.warn("Failed to generate preview account number for productId={}: {}", productId, ex.getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
String clientName = null;
|
||||
if (client != null) {
|
||||
clientName = client.getDisplayName();
|
||||
}
|
||||
|
||||
String groupName = null;
|
||||
if (group != null) {
|
||||
groupName = group.getName();
|
||||
}
|
||||
|
||||
template = SavingsAccountData.withClientTemplate(clientId, clientName, groupId, groupName);
|
||||
|
||||
final Collection<StaffData> fieldOfficerOptions = null;
|
||||
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
|
||||
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
|
||||
final boolean feeChargesOnly = false;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
|
||||
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
|
||||
|
||||
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
|
||||
charges, chargeOptions);
|
||||
}
|
||||
|
||||
final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService.retrieveTemplates(StatusEnum.CREATE.getValue(),
|
||||
EntityTables.SAVINGS.getName(), productId);
|
||||
template.setDatatables(datatableTemplates);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private static final class CurrentAccountTemplateMapper implements RowMapper<SavingsAccountData> {
|
||||
|
||||
private final ClientData client;
|
||||
private final GroupGeneralData group;
|
||||
private final String schemaSql;
|
||||
|
||||
CurrentAccountTemplateMapper(final ClientData client, final GroupGeneralData group) {
|
||||
this.client = client;
|
||||
this.group = group;
|
||||
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("cp.id as productId, cp.name as productName, ");
|
||||
sqlBuilder.append(
|
||||
"cp.currency_code as currencyCode, cp.currency_digits as currencyDigits, cp.currency_multiplesof as inMultiplesOf, ");
|
||||
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
||||
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
|
||||
sqlBuilder.append("cp.nominal_annual_interest_rate as nominalAnnualIterestRate, ");
|
||||
sqlBuilder.append("cp.interest_compounding_period_enum as interestCompoundingPeriodType, ");
|
||||
sqlBuilder.append("cp.interest_posting_period_enum as interestPostingPeriodType, ");
|
||||
sqlBuilder.append("cp.interest_calculation_type_enum as interestCalculationType, ");
|
||||
sqlBuilder.append("cp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
|
||||
sqlBuilder.append("cp.min_required_opening_balance as minRequiredOpeningBalance, ");
|
||||
sqlBuilder.append("cp.lockin_period_frequency as lockinPeriodFrequency,");
|
||||
sqlBuilder.append("cp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
|
||||
sqlBuilder.append("cp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
|
||||
sqlBuilder.append("cp.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
|
||||
sqlBuilder.append("cp.allow_overdraft as allowOverdraft, ");
|
||||
sqlBuilder.append("cp.overdraft_limit as overdraftLimit, ");
|
||||
sqlBuilder.append("cp.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, ");
|
||||
sqlBuilder.append("cp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
|
||||
sqlBuilder.append("cp.withhold_tax as withHoldTax,");
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
|
||||
sqlBuilder.append("cp.min_required_balance as minRequiredBalance, ");
|
||||
sqlBuilder.append("cp.enforce_min_required_balance as enforceMinRequiredBalance, ");
|
||||
sqlBuilder.append("0 as maxAllowedLienLimit, ");
|
||||
sqlBuilder.append("0 as lienAllowed, ");
|
||||
sqlBuilder.append("cp.dormancy_alert as dormancyAlert, ");
|
||||
sqlBuilder.append("cp.balance_swap_option as balanceSwapOption, ");
|
||||
sqlBuilder.append("cp.msme as msme, ");
|
||||
sqlBuilder.append("cp.nomination_facility as nominationFacility, ");
|
||||
sqlBuilder.append("cp.auto_debit_facility as autoDebitFacility, ");
|
||||
sqlBuilder.append("cp.auto_post as autoPost, ");
|
||||
sqlBuilder.append("cp.frequency_fix_day as frequencyFixDay, ");
|
||||
sqlBuilder.append("cp.last_interest_date as lastInterestDate, ");
|
||||
sqlBuilder.append("cp.interest_calculate_selector as interestCalculateSelector, ");
|
||||
sqlBuilder.append("cp.average_period as averagePeriod, ");
|
||||
sqlBuilder.append("cp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
||||
sqlBuilder.append("cp.is_dormancy_tracking_active as isDormancyTrackingActive, ");
|
||||
sqlBuilder.append("cp.days_to_inactive as daysToInactive, ");
|
||||
sqlBuilder.append("cp.days_to_dormancy as daysToDormancy, ");
|
||||
sqlBuilder.append("cp.days_to_escheat as daysToEscheat ");
|
||||
sqlBuilder.append("from m_current_product cp ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = cp.currency_code ");
|
||||
sqlBuilder.append("left join m_tax_group tg on tg.id = cp.tax_group_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long productId = rs.getLong("productId");
|
||||
final String productName = rs.getString("productName");
|
||||
|
||||
final String currencyCode = rs.getString("currencyCode");
|
||||
final String currencyName = rs.getString("currencyName");
|
||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol,
|
||||
currencyNameCode);
|
||||
|
||||
final BigDecimal nominalAnnualIterestRate = rs.getBigDecimal("nominalAnnualIterestRate");
|
||||
|
||||
final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations.compoundingInterestPeriodType(
|
||||
SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestCompoundingPeriodType")));
|
||||
|
||||
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(
|
||||
SavingsPostingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
|
||||
|
||||
final EnumOptionData interestCalculationType = SavingsEnumerations
|
||||
.interestCalculationType(SavingsInterestCalculationType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));
|
||||
|
||||
final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations.interestCalculationDaysInYearType(
|
||||
SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType")));
|
||||
|
||||
final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredOpeningBalance");
|
||||
|
||||
final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
|
||||
EnumOptionData lockinPeriodFrequencyType = null;
|
||||
final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
|
||||
if (lockinPeriodFrequencyTypeValue != null) {
|
||||
final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
|
||||
lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType);
|
||||
}
|
||||
|
||||
final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
|
||||
|
||||
final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
|
||||
final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"nominalAnnualInterestRateOverdraft");
|
||||
final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"minOverdraftForInterestCalculation");
|
||||
|
||||
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
|
||||
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
|
||||
final BigDecimal maxAllowedLienLimit = null;
|
||||
final boolean lienAllowed = false;
|
||||
final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"minBalanceForInterestCalculation");
|
||||
|
||||
final boolean withHoldTax = rs.getBoolean("withHoldTax");
|
||||
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
|
||||
final String taxGroupName = rs.getString("taxGroupName");
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
Long clientId = null;
|
||||
String clientName = null;
|
||||
if (this.client != null) {
|
||||
clientId = this.client.getId();
|
||||
clientName = this.client.getDisplayName();
|
||||
}
|
||||
|
||||
Long groupId = null;
|
||||
String groupName = null;
|
||||
if (this.group != null) {
|
||||
groupId = this.group.getId();
|
||||
groupName = this.group.getName();
|
||||
}
|
||||
|
||||
final Long fieldOfficerId = null;
|
||||
final String fieldOfficerName = null;
|
||||
final SavingsAccountStatusEnumData status = null;
|
||||
final SavingsAccountSummaryData summary = null;
|
||||
final BigDecimal onHoldFunds = null;
|
||||
final BigDecimal savingsAmountOnHold = null;
|
||||
|
||||
final SavingsAccountSubStatusEnumData subStatus = null;
|
||||
final String reasonForBlock = null;
|
||||
final LocalDate lastActiveTransactionDate = null;
|
||||
final boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive");
|
||||
final Integer daysToInactive = JdbcSupport.getInteger(rs, "daysToInactive");
|
||||
final Integer daysToDormancy = JdbcSupport.getInteger(rs, "daysToDormancy");
|
||||
final Integer daysToEscheat = JdbcSupport.getInteger(rs, "daysToEscheat");
|
||||
|
||||
final boolean dormancyAlert = rs.getBoolean("dormancyAlert");
|
||||
final boolean balanceSwapOption = rs.getBoolean("balanceSwapOption");
|
||||
final boolean msme = rs.getBoolean("msme");
|
||||
final boolean nominationFacility = rs.getBoolean("nominationFacility");
|
||||
final boolean autoDebitFacility = rs.getBoolean("autoDebitFacility");
|
||||
final boolean autoPost = rs.getBoolean("autoPost");
|
||||
final Integer frequencyFixDay = JdbcSupport.getInteger(rs, "frequencyFixDay");
|
||||
final LocalDate lastInterestDate = JdbcSupport.getLocalDate(rs, "lastInterestDate");
|
||||
final Integer interestCalculateSelector = JdbcSupport.getInteger(rs, "interestCalculateSelector");
|
||||
final String averagePeriod = rs.getString("averagePeriod");
|
||||
final boolean accountClosedZeroBalanceInDayClosing = rs.getBoolean("accountClosedZeroBalanceInDayClosing");
|
||||
|
||||
final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault();
|
||||
final EnumOptionData depositType = null;
|
||||
final SavingsAccountData accountData = SavingsAccountData.instance(null, null, depositType, null, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, subStatus, reasonForBlock, timeline, currency,
|
||||
nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
|
||||
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
|
||||
daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
|
||||
|
||||
accountData.setDormancyAlert(dormancyAlert);
|
||||
accountData.setBalanceSwapOption(balanceSwapOption);
|
||||
accountData.setMsme(msme);
|
||||
accountData.setNominationFacility(nominationFacility);
|
||||
accountData.setAutoDebitFacility(autoDebitFacility);
|
||||
accountData.setAutoPost(autoPost);
|
||||
accountData.setFrequencyFixDay(frequencyFixDay);
|
||||
accountData.setLastInterestDate(lastInterestDate);
|
||||
accountData.setInterestCalculateSelector(interestCalculateSelector);
|
||||
accountData.setAveragePeriod(averagePeriod);
|
||||
accountData.setAccountClosedZeroBalanceInDayClosing(accountClosedZeroBalanceInDayClosing);
|
||||
return accountData;
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<SavingsAccountChargeData> fromChargesToSavingsCharges(final Collection<ChargeData> productCharges) {
|
||||
final Collection<SavingsAccountChargeData> savingsCharges = new ArrayList<>();
|
||||
for (final ChargeData chargeData : productCharges) {
|
||||
final SavingsAccountChargeData savingsCharge = ConvertChargeDataToSpecificChargeData.toSavingsAccountChargeData(chargeData);
|
||||
savingsCharges.add(savingsCharge);
|
||||
}
|
||||
return savingsCharges;
|
||||
}
|
||||
}
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
/**
|
||||
* 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.service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfig;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountJointHolder;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountPartner;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWritePlatformService {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final CurrentAccountRepository currentAccountRepository;
|
||||
private final CurrentProductRepository currentProductRepository;
|
||||
private final ClientRepositoryWrapper clientRepository;
|
||||
private final OfficeRepositoryWrapper officeRepository;
|
||||
private final CurrentAccountNumberGenerator accountNumberGenerator;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult submitApplication(final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
|
||||
final Long clientId = getLong(json, "clientId");
|
||||
final Long productId = getLong(json, "productId");
|
||||
final Long officeId = getLong(json, "officeId");
|
||||
|
||||
final Client client = clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
final CurrentProduct product = currentProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new SavingsProductNotFoundException(productId));
|
||||
final Office office = officeId != null
|
||||
? officeRepository.findOneWithNotFoundDetection(officeId)
|
||||
: client.getOffice();
|
||||
|
||||
final String dateFormat = getString(json, "dateFormat");
|
||||
final String locale = getString(json, "locale");
|
||||
|
||||
LocalDate submittedOnDate = parseDate(getString(json, "submittedOnDate"), dateFormat, locale);
|
||||
if (submittedOnDate == null) submittedOnDate = LocalDate.now();
|
||||
|
||||
// accountType may arrive as integer or descriptive string — store null if unparseable
|
||||
Integer accountTypeEnum = null;
|
||||
if (json.has("accountType") && !json.get("accountType").isJsonNull()) {
|
||||
try {
|
||||
accountTypeEnum = json.get("accountType").getAsInt();
|
||||
} catch (Exception ignored) { }
|
||||
}
|
||||
|
||||
boolean msme = json.has("msme") && !json.get("msme").isJsonNull()
|
||||
? json.get("msme").getAsBoolean() : product.isMsme();
|
||||
boolean autoDebitFacility = json.has("autoDebitFacility") && !json.get("autoDebitFacility").isJsonNull()
|
||||
? json.get("autoDebitFacility").getAsBoolean() : product.isAutoDebitFacility();
|
||||
boolean balanceSwapOption = json.has("balanceSwapOption") && !json.get("balanceSwapOption").isJsonNull()
|
||||
? json.get("balanceSwapOption").getAsBoolean() : product.isBalanceSwapOption();
|
||||
|
||||
final CurrentAccount account = CurrentAccount.createNew(
|
||||
client, productId, office, null, "TEMP", submittedOnDate, accountTypeEnum,
|
||||
product.currency().getCode(), product.currency().getDigitsAfterDecimal(),
|
||||
product.currency().getInMultiplesOf(),
|
||||
product.nominalAnnualInterestRate(),
|
||||
product.getInterestCompoundingPeriodType(), product.getInterestPostingPeriodType(),
|
||||
product.getInterestCalculationType(), product.getInterestCalculationDaysInYearType(),
|
||||
product.isAllowOverdraft(), product.getOverdraftLimit(),
|
||||
msme, autoDebitFacility, balanceSwapOption,
|
||||
product.getCustomerSegment(), product.isAutoPost(),
|
||||
product.getInterestCalculateSelector(), product.getAveragePeriod(),
|
||||
product.getAvgBalanceCalcPeriod());
|
||||
|
||||
if (json.has("establishmentDate") && !json.get("establishmentDate").isJsonNull()) {
|
||||
account.setEstablishmentDate(parseDate(json.get("establishmentDate").getAsString(), dateFormat, locale));
|
||||
}
|
||||
if (json.has("startOperationDate") && !json.get("startOperationDate").isJsonNull()) {
|
||||
account.setStartOperationDate(parseDate(json.get("startOperationDate").getAsString(), dateFormat, locale));
|
||||
}
|
||||
if (json.has("isNonIndividualClient") && !json.get("isNonIndividualClient").isJsonNull()) {
|
||||
account.setNonIndividualClient(json.get("isNonIndividualClient").getAsBoolean());
|
||||
}
|
||||
if (json.has("signatoryRights") && !json.get("signatoryRights").isJsonNull()) {
|
||||
account.setSignatoryRights(json.get("signatoryRights").getAsString());
|
||||
}
|
||||
if (json.has("modeOfOperation") && !json.get("modeOfOperation").isJsonNull()) {
|
||||
account.setModeOfOperation(json.get("modeOfOperation").getAsString());
|
||||
}
|
||||
if (json.has("introducerName") && !json.get("introducerName").isJsonNull()) {
|
||||
account.setIntroducerName(json.get("introducerName").getAsString());
|
||||
}
|
||||
// UI may send "introducerClientId" or "introducerId"
|
||||
Long introducerClientId = getLong(json, "introducerClientId");
|
||||
if (introducerClientId == null) introducerClientId = getLong(json, "introducerId");
|
||||
if (introducerClientId != null) account.setIntroducerClientId(introducerClientId);
|
||||
|
||||
if (json.has("externalId") && !json.get("externalId").isJsonNull()) {
|
||||
account.setExternalId(json.get("externalId").getAsString());
|
||||
}
|
||||
if (json.has("accountStatus") && !json.get("accountStatus").isJsonNull()) {
|
||||
account.setAccountStatus(json.get("accountStatus").getAsString());
|
||||
}
|
||||
// Override customerSegment from request if provided
|
||||
if (json.has("customerSegment") && !json.get("customerSegment").isJsonNull()) {
|
||||
account.setCustomerSegment(json.get("customerSegment").getAsString());
|
||||
}
|
||||
|
||||
// Save first to get ID
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
|
||||
// Generate account number: 00 + GL_CODE(6) + OFFICE_ID(2) + ACCOUNT_ID(5)
|
||||
final String accountNo = accountNumberGenerator.generate(account);
|
||||
account.updateAccountNo(accountNo);
|
||||
|
||||
// Facility config — UI sends "accountFacilityConfig", internal uses "facilityConfig"
|
||||
JsonObject fcJson = json.getAsJsonObject("accountFacilityConfig");
|
||||
if (fcJson == null) fcJson = json.getAsJsonObject("facilityConfig");
|
||||
if (fcJson != null) {
|
||||
account.setFacilityConfig(assembleFacilityConfig(account, fcJson));
|
||||
}
|
||||
|
||||
// Joint holders
|
||||
if (json.has("jointHolders") && json.get("jointHolders").isJsonArray()) {
|
||||
assembleJointHolders(account, json.getAsJsonArray("jointHolders"));
|
||||
}
|
||||
|
||||
// Partners (non-individual)
|
||||
if (json.has("partners") && json.get("partners").isJsonArray()) {
|
||||
assemblePartners(account, json.getAsJsonArray("partners"));
|
||||
}
|
||||
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(account.getId())
|
||||
.withClientId(clientId)
|
||||
.withOfficeId(office.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult approveApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate approvedOnDate = parseDate(getString(json, "approvedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (approvedOnDate == null) approvedOnDate = LocalDate.now();
|
||||
account.approve(approvedOnDate);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult undoApproval(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
account.setStatusEnum(100);
|
||||
account.setApprovedOnDate(null);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult activate(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate activatedOnDate = parseDate(getString(json, "activatedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (activatedOnDate == null) activatedOnDate = LocalDate.now();
|
||||
account.activate(activatedOnDate);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult rejectApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate rejectedOnDate = parseDate(getString(json, "rejectedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (rejectedOnDate == null) rejectedOnDate = LocalDate.now();
|
||||
account.reject(rejectedOnDate);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult withdrawApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate withdrawnOnDate = parseDate(getString(json, "withdrawnOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (withdrawnOnDate == null) withdrawnOnDate = LocalDate.now();
|
||||
account.withdraw(withdrawnOnDate);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult closeAccount(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate closedOnDate = parseDate(getString(json, "closedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (closedOnDate == null) closedOnDate = LocalDate.now();
|
||||
account.close(closedOnDate);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult modifyApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
if (json.has("msme") && !json.get("msme").isJsonNull()) {
|
||||
account.setMsme(json.get("msme").getAsBoolean());
|
||||
}
|
||||
if (json.has("signatoryRights") && !json.get("signatoryRights").isJsonNull()) {
|
||||
account.setSignatoryRights(json.get("signatoryRights").getAsString());
|
||||
}
|
||||
if (json.has("modeOfOperation") && !json.get("modeOfOperation").isJsonNull()) {
|
||||
account.setModeOfOperation(json.get("modeOfOperation").getAsString());
|
||||
}
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult deleteApplication(final Long accountId) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
currentAccountRepository.delete(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
// ─── helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
private CurrentAccount findAccount(Long accountId) {
|
||||
return currentAccountRepository.findById(accountId)
|
||||
.orElseThrow(() -> new SavingsAccountNotFoundException(accountId));
|
||||
}
|
||||
|
||||
private CurrentAccountFacilityConfig assembleFacilityConfig(CurrentAccount account, JsonObject fcJson) {
|
||||
final CurrentAccountFacilityConfig cfg = CurrentAccountFacilityConfig.createFor(account);
|
||||
cfg.setAtmFacility(getBool(fcJson, "atmFacility"));
|
||||
cfg.setAtmWithdrawalFacility(getBool(fcJson, "atmWithdrawalFacility"));
|
||||
cfg.setUpiFacility(getBool(fcJson, "upiFacility"));
|
||||
cfg.setPosTransactionsFacility(getBool(fcJson, "posTransactionsFacility"));
|
||||
cfg.setChequeBook(getBool(fcJson, "chequeBook"));
|
||||
cfg.setImpsFacility(getBool(fcJson, "impsFacility"));
|
||||
cfg.setSmsAlerts(getBool(fcJson, "smsAlerts"));
|
||||
cfg.setBbpsFacility(getBool(fcJson, "bbpsFacility"));
|
||||
cfg.setInsuranceCover(getBool(fcJson, "insuranceCover"));
|
||||
cfg.setBepgFacility(getBool(fcJson, "bepgFacility"));
|
||||
cfg.setNeftFacility(getBool(fcJson, "neftFacility"));
|
||||
cfg.setRtgsFacility(getBool(fcJson, "rtgsFacility"));
|
||||
cfg.setInterBankTotalCapFacility(getBool(fcJson, "interBankTotalCapFacility"));
|
||||
cfg.setWithdrawalFacility(getBool(fcJson, "withdrawalFacility"));
|
||||
cfg.setDepositFacilityKyc(getBool(fcJson, "depositFacilityKyc"));
|
||||
cfg.setVirtualDebitCard(getBool(fcJson, "virtualDebitCard"));
|
||||
cfg.setInternetBanking(getBool(fcJson, "internetBanking"));
|
||||
cfg.setMobileBanking(getBool(fcJson, "mobileBanking"));
|
||||
return cfg;
|
||||
}
|
||||
|
||||
private void assembleJointHolders(CurrentAccount account, JsonArray arr) {
|
||||
for (JsonElement el : arr) {
|
||||
final JsonObject jo = el.getAsJsonObject();
|
||||
final CurrentAccountJointHolder holder = new CurrentAccountJointHolder();
|
||||
holder.setCurrentAccount(account);
|
||||
Long cid = getLong(jo, "clientId");
|
||||
if (cid != null) holder.setClientId(cid);
|
||||
if (jo.has("cifNumber") && !jo.get("cifNumber").isJsonNull()) holder.setCifNumber(jo.get("cifNumber").getAsString());
|
||||
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());
|
||||
account.getJointHolders().add(holder);
|
||||
}
|
||||
}
|
||||
|
||||
private void assemblePartners(CurrentAccount account, JsonArray arr) {
|
||||
for (JsonElement el : arr) {
|
||||
final JsonObject jo = el.getAsJsonObject();
|
||||
final CurrentAccountPartner partner = new CurrentAccountPartner();
|
||||
partner.setCurrentAccount(account);
|
||||
Long cid = getLong(jo, "clientId");
|
||||
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());
|
||||
account.getPartners().add(partner);
|
||||
}
|
||||
}
|
||||
|
||||
private Long getLong(JsonObject obj, String key) {
|
||||
if (!obj.has(key) || obj.get(key).isJsonNull()) return null;
|
||||
try {
|
||||
String val = obj.get(key).getAsString().trim();
|
||||
if (val.isEmpty()) return null;
|
||||
return Long.parseLong(val);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getString(JsonObject obj, String key) {
|
||||
if (!obj.has(key) || obj.get(key).isJsonNull()) return null;
|
||||
String val = obj.get(key).getAsString().trim();
|
||||
return val.isEmpty() ? null : val;
|
||||
}
|
||||
|
||||
private boolean getBool(JsonObject obj, String key) {
|
||||
return obj.has(key) && !obj.get(key).isJsonNull() && obj.get(key).getAsBoolean();
|
||||
}
|
||||
|
||||
private LocalDate parseDate(String dateStr, String dateFormat, String locale) {
|
||||
if (dateStr == null || dateStr.isBlank()) return null;
|
||||
try {
|
||||
String fmt = dateFormat != null ? dateFormat : "dd MMMM yyyy";
|
||||
return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(fmt,
|
||||
locale != null ? new java.util.Locale(locale) : java.util.Locale.ENGLISH));
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("dd MMMM yyyy", java.util.Locale.ENGLISH));
|
||||
} catch (Exception ex) {
|
||||
log.warn("Could not parse date: {}", dateStr);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
-4
@@ -32,6 +32,8 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
|
||||
import org.apache.fineract.infrastructure.core.data.PaginationParametersDataValidator;
|
||||
@@ -46,6 +48,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.staff.data.StaffData;
|
||||
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferData;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
||||
@@ -69,6 +72,7 @@ import org.apache.fineract.portfolio.interestratechart.service.InterestRateChart
|
||||
import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadService;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
|
||||
@@ -96,8 +100,10 @@ import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class DepositAccountReadPlatformServiceImpl implements DepositAccountReadPlatformService {
|
||||
|
||||
@@ -128,6 +134,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
private final DropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final CalendarReadPlatformService calendarReadPlatformService;
|
||||
private final PaymentTypeReadService paymentTypeReadPlatformService;
|
||||
private final AccountNumberGenerator accountNumberGenerator;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
|
||||
@Override
|
||||
public Collection<DepositAccountData> retrieveAll(final DepositAccountType depositAccountType,
|
||||
@@ -382,7 +390,9 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
|
||||
// retrieve chart Slabs
|
||||
final InterestRateChartData productChartData = this.productChartReadPlatformService.retrieveActiveChartWithTemplate(productId);
|
||||
final DepositAccountInterestRateChartData accountChart = DepositAccountInterestRateChartData.from(productChartData);
|
||||
final DepositAccountInterestRateChartData accountChart = (productChartData != null)
|
||||
? DepositAccountInterestRateChartData.from(productChartData)
|
||||
: null;
|
||||
|
||||
if (depositAccountType.isFixedDeposit()) {
|
||||
|
||||
@@ -394,14 +404,37 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
|
||||
template = FixedDepositAccountData.withInterestChart((FixedDepositAccountData) template, accountChart);
|
||||
} else if (depositAccountType.isRecurringDeposit()) {
|
||||
template = RecurringDepositAccountData.withTemplateOptions((RecurringDepositAccountData) template, productOptions,
|
||||
// Save CBS fields from mapper result before factory methods create new objects
|
||||
final RecurringDepositAccountData rdTemplateWithCbs = (RecurringDepositAccountData) template;
|
||||
template = RecurringDepositAccountData.withTemplateOptions(rdTemplateWithCbs, productOptions,
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, transactions, charges, chargeOptions, preClosurePenalInterestOnTypeOptions,
|
||||
periodFrequencyTypeOptions);
|
||||
template = RecurringDepositAccountData.withInterestChartAndRecurringDetails((RecurringDepositAccountData) template,
|
||||
accountChart, null, null);
|
||||
// Re-apply CBS product fields lost during factory method object creation
|
||||
((RecurringDepositAccountData) template).copyCbsFieldsFrom(rdTemplateWithCbs);
|
||||
|
||||
// Populate account type / customer segment options from code values
|
||||
try {
|
||||
final Collection<CodeValueData> accountTypeOpts = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(SavingsApiConstants.RD_PRODUCT_ACCOUNT_TYPE);
|
||||
final Collection<CodeValueData> customerSegmentOpts = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(SavingsApiConstants.RD_PRODUCT_CUSTOMER_SEGMENT);
|
||||
((RecurringDepositAccountData) template).setAccountTypeOptions(accountTypeOpts);
|
||||
((RecurringDepositAccountData) template).setCustomerSegmentOptions(customerSegmentOpts);
|
||||
} catch (final Exception ex) {
|
||||
log.warn("Failed to load RD account type/customer segment options: {}", ex.getMessage());
|
||||
}
|
||||
|
||||
// Generate preview account number
|
||||
try {
|
||||
final String preview = accountNumberGenerator.previewAccountNumber(productId, officeId);
|
||||
template.setPreviewAccountNo(preview);
|
||||
} catch (final Exception ex) {
|
||||
log.warn("Failed to generate preview account number for RD productId={}: {}", productId, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -1473,10 +1506,37 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
sqlBuilder.append("dptp.min_deposit_term_type_enum as minDepositTermTypeId, ");
|
||||
sqlBuilder.append("dptp.max_deposit_term_type_enum as maxDepositTermTypeId, ");
|
||||
sqlBuilder.append("dptp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, ");
|
||||
sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId ");
|
||||
sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId, ");
|
||||
sqlBuilder.append("sa.account_type as accountType, sa.customer_segment as customerSegment, ");
|
||||
sqlBuilder.append("sa.product_code as productCode, ");
|
||||
sqlBuilder.append("sa.effective_date as effectiveDate, sa.effective_to as effectiveTo, sa.end_date as endDate, ");
|
||||
sqlBuilder.append("sa.introducer_required as introducerRequired, sa.share_linking_enabled as shareLinkingEnabled, ");
|
||||
sqlBuilder.append("sa.nomination_facility as nominationFacility, sa.dormancy_alert as dormancyAlert, ");
|
||||
sqlBuilder.append("sa.is_dormancy_tracking_active as isDormancyTrackingActive, ");
|
||||
sqlBuilder.append("sa.days_to_inactive as daysToInactive, ");
|
||||
sqlBuilder.append("sa.days_to_dormancy as daysToDormancy, ");
|
||||
sqlBuilder.append("sa.days_to_escheat as daysToEscheat, ");
|
||||
sqlBuilder.append("sa.interest_calculate_selector as interestCalculateSelector, ");
|
||||
sqlBuilder.append("icv.code_value as interestCalculationTypeName, ");
|
||||
sqlBuilder.append("sa.average_period as averagePeriod, ");
|
||||
sqlBuilder.append("sa.auto_post as autoPost, sa.frequency_fix_day as frequencyFixDay, ");
|
||||
sqlBuilder.append("sa.last_interest_date as lastInterestDate, sa.enable_lockin_period as enableLockinPeriod, ");
|
||||
sqlBuilder.append("sa.balance_frequency as balanceFrequency, ");
|
||||
sqlBuilder.append("sa.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
||||
sqlBuilder.append("sa.balance_swap_option as balanceSwapOption, sa.msme as msme, ");
|
||||
sqlBuilder.append("sa.auto_debit_facility as autoDebitFacility, sa.loan_facility as loanFacility, ");
|
||||
sqlBuilder.append("sa.premature_withdrawal_option as prematureWithdrawalOption, sa.tenure_flexibility as tenureFlexibility, ");
|
||||
sqlBuilder.append("sa.lien_marked as lienMarked, sa.maturity_instructions as maturityInstructions, ");
|
||||
sqlBuilder.append("sa.credit_maturity_proceeds as creditMaturityProceeds, ");
|
||||
sqlBuilder.append("sa.penalty_grace_period_missed_installments as penaltyGracePeriodMissedInstallments, ");
|
||||
sqlBuilder.append("sa.panel_request_applicable as panelRequestApplicable, ");
|
||||
sqlBuilder.append("sa.no_penalty_nominee_heir_closure as noPenaltyNomineeHeirClosure, ");
|
||||
sqlBuilder.append("sa.deceased_settlement as deceasedSettlement, ");
|
||||
sqlBuilder.append("sa.allow_debit_txn_after_due_date as allowDebitTxnAfterDueDate ");
|
||||
|
||||
sqlBuilder.append(this.selectTablesSql());
|
||||
sqlBuilder.append("left join m_deposit_product_recurring_detail dprd on sa.id = dprd.savings_product_id ");
|
||||
sqlBuilder.append("left join m_code_value icv on icv.id = sa.interest_calculate_selector ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -1525,12 +1585,56 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
final BigDecimal totalOverdueAmount = null;
|
||||
final Integer noOfOverdueInstallments = null;
|
||||
|
||||
return RecurringDepositAccountData.instance(depositAccountData, preClosurePenalApplicable, preClosurePenalInterest,
|
||||
final RecurringDepositAccountData result = RecurringDepositAccountData.instance(depositAccountData,
|
||||
preClosurePenalApplicable, preClosurePenalInterest,
|
||||
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
|
||||
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
|
||||
depositPeriodFrequencyType, mandatoryRecommendedDepositAmount, onAccountClosureType, expectedFirstDepositOnDate,
|
||||
totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit, allowWithdrawal, adjustAdvanceTowardsFuturePayments,
|
||||
isCalendarInherited);
|
||||
|
||||
// CBS product fields
|
||||
result.setCbsProductFields(
|
||||
rs.getString("accountType"),
|
||||
rs.getString("customerSegment"),
|
||||
rs.getString("productCode"),
|
||||
JdbcSupport.getLocalDate(rs, "effectiveDate"),
|
||||
JdbcSupport.getLocalDate(rs, "effectiveTo"),
|
||||
JdbcSupport.getLocalDate(rs, "endDate"),
|
||||
rs.getBoolean("introducerRequired"),
|
||||
rs.getBoolean("shareLinkingEnabled"),
|
||||
rs.getBoolean("nominationFacility"),
|
||||
rs.getBoolean("dormancyAlert"),
|
||||
rs.getBoolean("isDormancyTrackingActive"),
|
||||
JdbcSupport.getInteger(rs, "daysToInactive"),
|
||||
JdbcSupport.getInteger(rs, "daysToDormancy"),
|
||||
JdbcSupport.getInteger(rs, "daysToEscheat"),
|
||||
JdbcSupport.getInteger(rs, "interestCalculateSelector"),
|
||||
rs.getString("interestCalculationTypeName"),
|
||||
rs.getString("averagePeriod"),
|
||||
rs.getBoolean("autoPost"),
|
||||
JdbcSupport.getInteger(rs, "frequencyFixDay"),
|
||||
JdbcSupport.getLocalDate(rs, "lastInterestDate"),
|
||||
rs.getBoolean("enableLockinPeriod"),
|
||||
rs.getString("balanceFrequency"),
|
||||
rs.getBoolean("accountClosedZeroBalanceInDayClosing"),
|
||||
rs.getBoolean("balanceSwapOption"),
|
||||
rs.getBoolean("msme"),
|
||||
rs.getBoolean("autoDebitFacility"),
|
||||
rs.getBoolean("loanFacility"),
|
||||
rs.getBoolean("prematureWithdrawalOption"),
|
||||
rs.getBoolean("tenureFlexibility"),
|
||||
rs.getBoolean("lienMarked"),
|
||||
rs.getBoolean("maturityInstructions"),
|
||||
rs.getBoolean("creditMaturityProceeds"),
|
||||
rs.getBoolean("penaltyGracePeriodMissedInstallments"),
|
||||
rs.getBoolean("panelRequestApplicable"),
|
||||
rs.getBoolean("noPenaltyNomineeHeirClosure"),
|
||||
rs.getBoolean("deceasedSettlement"),
|
||||
rs.getBoolean("allowDebitTxnAfterDueDate")
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+99
-3
@@ -298,12 +298,36 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("dptp.min_deposit_term_type_enum as minDepositTermTypeId, ");
|
||||
sqlBuilder.append("dptp.max_deposit_term_type_enum as maxDepositTermTypeId, ");
|
||||
sqlBuilder.append("dptp.in_multiples_of_deposit_term as inMultiplesOfDepositTerm, ");
|
||||
sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId ");
|
||||
sqlBuilder.append("dptp.in_multiples_of_deposit_term_type_enum as inMultiplesOfDepositTermTypeId, ");
|
||||
sqlBuilder.append("sp.account_type as accountType, sp.customer_segment as customerSegment, ");
|
||||
sqlBuilder.append("sp.product_code as productCode, sp.gl_account_id as glAccountId, gla.name as glAccountName, ");
|
||||
sqlBuilder.append("sp.effective_date as effectiveDate, sp.effective_to as effectiveTo, sp.end_date as endDate, ");
|
||||
sqlBuilder.append("sp.introducer_required as introducerRequired, sp.share_linking_enabled as shareLinkingEnabled, ");
|
||||
sqlBuilder.append("sp.nomination_facility as nominationFacility, sp.dormancy_alert as dormancyAlert, ");
|
||||
sqlBuilder.append("sp.interest_calculate_selector as interestCalculateSelector, icv.code_value as interestCalculationTypeName, ");
|
||||
sqlBuilder.append("sp.average_period as averagePeriod, ");
|
||||
sqlBuilder.append("sp.auto_post as autoPost, sp.frequency_fix_day as frequencyFixDay, ");
|
||||
sqlBuilder.append("sp.last_interest_date as lastInterestDate, sp.enable_lockin_period as enableLockinPeriod, ");
|
||||
sqlBuilder.append("sp.balance_frequency as balanceFrequency, ");
|
||||
sqlBuilder.append("sp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
||||
sqlBuilder.append("sp.balance_swap_option as balanceSwapOption, sp.msme as msme, ");
|
||||
sqlBuilder.append("sp.auto_debit_facility as autoDebitFacility, sp.loan_facility as loanFacility, ");
|
||||
sqlBuilder.append("sp.premature_withdrawal_option as prematureWithdrawalOption, sp.tenure_flexibility as tenureFlexibility, ");
|
||||
sqlBuilder.append("sp.lien_marked as lienMarked, sp.maturity_instructions as maturityInstructions, ");
|
||||
sqlBuilder.append("sp.credit_maturity_proceeds as creditMaturityProceeds, ");
|
||||
sqlBuilder.append("sp.penalty_grace_period_missed_installments as penaltyGracePeriodMissedInstallments, ");
|
||||
sqlBuilder.append("sp.panel_request_applicable as panelRequestApplicable, ");
|
||||
sqlBuilder.append("sp.no_penalty_nominee_heir_closure as noPenaltyNomineeHeirClosure, ");
|
||||
sqlBuilder.append("sp.deceased_settlement as deceasedSettlement, ");
|
||||
sqlBuilder.append("sp.allow_debit_txn_after_due_date as allowDebitTxnAfterDueDate, ");
|
||||
sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers ");
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id ");
|
||||
sqlBuilder.append("left join m_deposit_product_recurring_detail dprd on sp.id=dprd.savings_product_id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
sqlBuilder.append(" left join m_tax_group tg on tg.id = sp.tax_group_id ");
|
||||
sqlBuilder.append(" left join m_tax_group tg on tg.id = sp.tax_group_id ");
|
||||
sqlBuilder.append("left join m_code_value icv on icv.id = sp.interest_calculate_selector ");
|
||||
sqlBuilder.append("left join acc_gl_account gla on gla.id = sp.gl_account_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -342,10 +366,82 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
final EnumOptionData inMultiplesOfDepositTermType = (inMultiplesOfDepositTermTypeId == null) ? null
|
||||
: SavingsEnumerations.depositTermFrequencyType(inMultiplesOfDepositTermTypeId);
|
||||
|
||||
return RecurringDepositProductData.instance(depositProductData, preClosurePenalApplicable, preClosurePenalInterest,
|
||||
final String accountType = rs.getString("accountType");
|
||||
final String customerSegment = rs.getString("customerSegment");
|
||||
final String productCode = rs.getString("productCode");
|
||||
final java.time.LocalDate effectiveDate = JdbcSupport.getLocalDate(rs, "effectiveDate");
|
||||
final java.time.LocalDate effectiveTo = JdbcSupport.getLocalDate(rs, "effectiveTo");
|
||||
final java.time.LocalDate endDate = JdbcSupport.getLocalDate(rs, "endDate");
|
||||
final boolean introducerRequired = rs.getBoolean("introducerRequired");
|
||||
final boolean shareLinkingEnabled = rs.getBoolean("shareLinkingEnabled");
|
||||
final boolean nominationFacility = rs.getBoolean("nominationFacility");
|
||||
final boolean dormancyAlert = rs.getBoolean("dormancyAlert");
|
||||
final Integer interestCalculateSelector = JdbcSupport.getInteger(rs, "interestCalculateSelector");
|
||||
final String averagePeriod = rs.getString("averagePeriod");
|
||||
final boolean autoPost = rs.getBoolean("autoPost");
|
||||
final Integer frequencyFixDay = JdbcSupport.getInteger(rs, "frequencyFixDay");
|
||||
final java.time.LocalDate lastInterestDate = JdbcSupport.getLocalDate(rs, "lastInterestDate");
|
||||
final boolean enableLockinPeriod = rs.getBoolean("enableLockinPeriod");
|
||||
final String balanceFrequency = rs.getString("balanceFrequency");
|
||||
final boolean accountClosedZeroBalanceInDayClosing = rs.getBoolean("accountClosedZeroBalanceInDayClosing");
|
||||
final boolean balanceSwapOption = rs.getBoolean("balanceSwapOption");
|
||||
final boolean msme = rs.getBoolean("msme");
|
||||
final boolean autoDebitFacility = rs.getBoolean("autoDebitFacility");
|
||||
final boolean loanFacility = rs.getBoolean("loanFacility");
|
||||
final boolean prematureWithdrawalOption = rs.getBoolean("prematureWithdrawalOption");
|
||||
final boolean tenureFlexibility = rs.getBoolean("tenureFlexibility");
|
||||
final boolean lienMarked = rs.getBoolean("lienMarked");
|
||||
final boolean maturityInstructions = rs.getBoolean("maturityInstructions");
|
||||
final boolean creditMaturityProceeds = rs.getBoolean("creditMaturityProceeds");
|
||||
final boolean penaltyGracePeriodMissedInstallments = rs.getBoolean("penaltyGracePeriodMissedInstallments");
|
||||
final boolean panelRequestApplicable = rs.getBoolean("panelRequestApplicable");
|
||||
final boolean noPenaltyNomineeHeirClosure = rs.getBoolean("noPenaltyNomineeHeirClosure");
|
||||
final boolean deceasedSettlement = rs.getBoolean("deceasedSettlement");
|
||||
final boolean allowDebitTxnAfterDueDate = rs.getBoolean("allowDebitTxnAfterDueDate");
|
||||
final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
|
||||
|
||||
RecurringDepositProductData result = RecurringDepositProductData.instance(depositProductData,
|
||||
preClosurePenalApplicable, preClosurePenalInterest,
|
||||
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
|
||||
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, isMandatoryDeposit, allowWithdrawal,
|
||||
adjustAdvanceTowardsFuturePayments, minDepositAmount, depositAmount, maxDepositAmount);
|
||||
result.setAccountType(accountType);
|
||||
result.setCustomerSegment(customerSegment);
|
||||
result.setProductCode(productCode);
|
||||
result.setGlAccountId(JdbcSupport.getLong(rs, "glAccountId"));
|
||||
result.setGlAccountName(rs.getString("glAccountName"));
|
||||
result.setEffectiveDate(effectiveDate);
|
||||
result.setEffectiveTo(effectiveTo);
|
||||
result.setEndDate(endDate);
|
||||
result.setIntroducerRequired(introducerRequired);
|
||||
result.setShareLinkingEnabled(shareLinkingEnabled);
|
||||
result.setNominationFacility(nominationFacility);
|
||||
result.setDormancyAlert(dormancyAlert);
|
||||
result.setInterestCalculateSelector(interestCalculateSelector);
|
||||
result.setInterestCalculationTypeName(rs.getString("interestCalculationTypeName"));
|
||||
result.setAveragePeriod(averagePeriod);
|
||||
result.setAutoPost(autoPost);
|
||||
result.setFrequencyFixDay(frequencyFixDay);
|
||||
result.setLastInterestDate(lastInterestDate);
|
||||
result.setEnableLockinPeriod(enableLockinPeriod);
|
||||
result.setBalanceFrequency(balanceFrequency);
|
||||
result.setAccountClosedZeroBalanceInDayClosing(accountClosedZeroBalanceInDayClosing);
|
||||
result.setBalanceSwapOption(balanceSwapOption);
|
||||
result.setMsme(msme);
|
||||
result.setAutoDebitFacility(autoDebitFacility);
|
||||
result.setLoanFacility(loanFacility);
|
||||
result.setPrematureWithdrawalOption(prematureWithdrawalOption);
|
||||
result.setTenureFlexibility(tenureFlexibility);
|
||||
result.setLienMarked(lienMarked);
|
||||
result.setMaturityInstructions(maturityInstructions);
|
||||
result.setCreditMaturityProceeds(creditMaturityProceeds);
|
||||
result.setPenaltyGracePeriodMissedInstallments(penaltyGracePeriodMissedInstallments);
|
||||
result.setPanelRequestApplicable(panelRequestApplicable);
|
||||
result.setNoPenaltyNomineeHeirClosure(noPenaltyNomineeHeirClosure);
|
||||
result.setDeceasedSettlement(deceasedSettlement);
|
||||
result.setAllowDebitTxnAfterDueDate(allowDebitTxnAfterDueDate);
|
||||
result.setWithdrawalFeeForTransfers(withdrawalFeeForTransfers);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.savings.api.RdInterestRateSaveRequest;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
|
||||
public interface RdBaseInterestRateReadPlatformService {
|
||||
|
||||
List<RdInterestRateEffectiveData> retrieveAllInterestRates();
|
||||
|
||||
List<RdInterestRateEffectiveData> retrieveAllActiveInterestRates();
|
||||
|
||||
RdInterestRateEffectiveData saveInterestRates(RdInterestRateSaveRequest request, Long createdById);
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.portfolio.savings.api.RdInterestRateSaveRequest;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateSlabData;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffective;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistory;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class RdBaseInterestRateReadPlatformServiceImpl implements RdBaseInterestRateReadPlatformService {
|
||||
|
||||
private final RdBaseInterestRateEffectiveRepository effectiveRepository;
|
||||
private final RdBaseInterestRateSlabHistoryRepository slabRepository;
|
||||
|
||||
private static final String[] PERIOD_LABELS = { "", "Days", "Months", "Years" };
|
||||
|
||||
@Override
|
||||
public List<RdInterestRateEffectiveData> retrieveAllInterestRates() {
|
||||
return effectiveRepository.findByOrderByEffectiveFromDateDesc().stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RdInterestRateEffectiveData> retrieveAllActiveInterestRates() {
|
||||
return effectiveRepository.findByIsActiveTrueOrderByEffectiveFromDateDesc().stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public RdInterestRateEffectiveData saveInterestRates(RdInterestRateSaveRequest request, Long createdById) {
|
||||
validateRequest(request);
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> normalizedSlabs = normalizeSlabs(request.getSlabs());
|
||||
|
||||
final LocalDate today = LocalDate.now();
|
||||
final List<RdBaseInterestRateEffective> activeVersions = effectiveRepository.findByIsActiveTrueOrderByEffectiveFromDateDesc();
|
||||
for (RdBaseInterestRateEffective version : activeVersions) {
|
||||
version.deactivate(today);
|
||||
}
|
||||
if (!activeVersions.isEmpty()) {
|
||||
effectiveRepository.saveAll(activeVersions);
|
||||
}
|
||||
|
||||
final String label = request.getVersionLabel() != null && !request.getVersionLabel().isBlank()
|
||||
? request.getVersionLabel()
|
||||
: "CBS Base Rate Version " + LocalDate.now();
|
||||
|
||||
RdBaseInterestRateEffective newVersion = RdBaseInterestRateEffective.create(label, LocalDate.now(), request.getRemarks(), createdById);
|
||||
newVersion = effectiveRepository.saveAndFlush(newVersion);
|
||||
|
||||
final Long versionId = newVersion.getId();
|
||||
final AtomicInteger srCounter = new AtomicInteger(1);
|
||||
for (RdInterestRateSaveRequest.SlabRequest slabRequest : normalizedSlabs) {
|
||||
final int sr = slabRequest.getSrNo() != null ? slabRequest.getSrNo() : srCounter.getAndIncrement();
|
||||
final RdBaseInterestRateSlabHistory slab = RdBaseInterestRateSlabHistory.create(versionId, sr, slabRequest.getDescription(),
|
||||
slabRequest.getPeriodTypeEnum(), slabRequest.getFromPeriod(), slabRequest.getToPeriod(),
|
||||
slabRequest.getAnnualInterestRate());
|
||||
slabRepository.save(slab);
|
||||
}
|
||||
|
||||
return toData(newVersion);
|
||||
}
|
||||
|
||||
private void validateRequest(final RdInterestRateSaveRequest request) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder validator = new DataValidatorBuilder(errors).resource("RD_BASE_INTEREST_RATE_VERSION");
|
||||
|
||||
if (request == null) {
|
||||
validator.failWithCodeNoParameterAddedToErrorCode("request.cannot.be.null");
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.getSlabs() == null || request.getSlabs().isEmpty()) {
|
||||
validator.parameter("slabs").failWithCode("cannot.be.blank");
|
||||
}
|
||||
|
||||
if (request.getVersionLabel() != null && request.getVersionLabel().length() > 100) {
|
||||
validator.parameter("versionLabel").value(request.getVersionLabel()).failWithCode("exceeds.max.length");
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
private List<RdInterestRateSaveRequest.SlabRequest> normalizeSlabs(final List<RdInterestRateSaveRequest.SlabRequest> slabs) {
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> sorted = new ArrayList<>(slabs);
|
||||
sorted.sort(Comparator.comparing((RdInterestRateSaveRequest.SlabRequest slab) -> slab.getSrNo() == null ? Integer.MAX_VALUE : slab.getSrNo())
|
||||
.thenComparing(slab -> slab.getFromPeriod() == null ? Integer.MAX_VALUE : slab.getFromPeriod()));
|
||||
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> normalized = new ArrayList<>(sorted.size());
|
||||
Integer expectedFrom = 0;
|
||||
for (int i = 0; i < sorted.size(); i++) {
|
||||
final RdInterestRateSaveRequest.SlabRequest source = sorted.get(i);
|
||||
final RdInterestRateSaveRequest.SlabRequest target = new RdInterestRateSaveRequest.SlabRequest();
|
||||
|
||||
final Integer fromPeriod = source.getFromPeriod();
|
||||
if (fromPeriod == null || !fromPeriod.equals(expectedFrom)) {
|
||||
throwValidationError("fromPeriod", "must.start.from.previous.range.end");
|
||||
}
|
||||
target.setFromPeriod(fromPeriod);
|
||||
|
||||
final Integer toPeriod = source.getToPeriod();
|
||||
if (i < sorted.size() - 1) {
|
||||
if (toPeriod == null) {
|
||||
throwValidationError("toPeriod", "cannot.be.blank.for.intermediate.slab");
|
||||
}
|
||||
if (toPeriod < fromPeriod) {
|
||||
throwValidationError("toPeriod", "cannot.be.less.than.fromPeriod");
|
||||
}
|
||||
expectedFrom = toPeriod + 1;
|
||||
target.setToPeriod(toPeriod);
|
||||
} else {
|
||||
target.setToPeriod(null);
|
||||
}
|
||||
|
||||
target.setSrNo(source.getSrNo() == null ? i + 1 : source.getSrNo());
|
||||
target.setDescription(source.getDescription());
|
||||
target.setPeriodTypeEnum(source.getPeriodTypeEnum() == null ? 1 : source.getPeriodTypeEnum());
|
||||
target.setAnnualInterestRate(source.getAnnualInterestRate());
|
||||
|
||||
if (target.getAnnualInterestRate() == null) {
|
||||
throwValidationError("annualInterestRate", "cannot.be.blank");
|
||||
}
|
||||
if (target.getAnnualInterestRate().signum() < 0) {
|
||||
throwValidationError("annualInterestRate", "must.be.non.negative");
|
||||
}
|
||||
|
||||
normalized.add(target);
|
||||
}
|
||||
|
||||
if (!normalized.isEmpty() && normalized.get(0).getFromPeriod() != 0) {
|
||||
throwValidationError("fromPeriod", "must.start.from.zero");
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void throwValidationError(final String parameterName, final String code) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
new DataValidatorBuilder(errors).resource("RD_BASE_INTEREST_RATE_VERSION").parameter(parameterName).failWithCode(code);
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
private RdInterestRateEffectiveData toData(RdBaseInterestRateEffective e) {
|
||||
List<RdInterestRateSlabData> slabs = slabRepository.findByEffectiveIdOrderBySrNo(e.getId()).stream()
|
||||
.map(s -> RdInterestRateSlabData.builder()
|
||||
.id(s.getId())
|
||||
.srNo(s.getSrNo())
|
||||
.description(s.getDescription())
|
||||
.periodTypeEnum(s.getPeriodTypeEnum())
|
||||
.periodTypeLabel(periodLabel(s.getPeriodTypeEnum()))
|
||||
.fromPeriod(s.getFromPeriod())
|
||||
.toPeriod(s.getToPeriod())
|
||||
.annualInterestRate(s.getAnnualInterestRate())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return RdInterestRateEffectiveData.builder()
|
||||
.id(e.getId())
|
||||
.productId(null)
|
||||
.versionLabel(e.getVersionLabel())
|
||||
.effectiveFromDate(e.getEffectiveFromDate())
|
||||
.effectiveToDate(e.getEffectiveToDate())
|
||||
.isActive(e.getIsActive())
|
||||
.remarks(e.getRemarks())
|
||||
.slabs(slabs)
|
||||
.build();
|
||||
}
|
||||
|
||||
private String periodLabel(Integer code) {
|
||||
if (code == null || code < 1 || code >= PERIOD_LABELS.length) return "Days";
|
||||
return PERIOD_LABELS[code];
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.savings.api.RdInterestRateSaveRequest;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
|
||||
public interface RdInterestRateReadPlatformService {
|
||||
|
||||
/** All versions (with slabs) for a specific product, newest first. */
|
||||
List<RdInterestRateEffectiveData> retrieveInterestRates(Long productId);
|
||||
|
||||
/** All active versions across all RD products – used as reference on the create-product page. */
|
||||
List<RdInterestRateEffectiveData> retrieveAllActiveInterestRates();
|
||||
|
||||
/**
|
||||
* Saves a NEW version of the rate chart for the given product.
|
||||
* Deactivates the previous active version first (immutable history).
|
||||
* Returns the newly created version's data.
|
||||
*/
|
||||
RdInterestRateEffectiveData saveInterestRates(Long productId, RdInterestRateSaveRequest request, Long createdById);
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.portfolio.savings.api.RdInterestRateSaveRequest;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateEffectiveData;
|
||||
import org.apache.fineract.portfolio.savings.data.RdInterestRateSlabData;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdInterestRateEffective;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdInterestRateEffectiveRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistory;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistoryRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class RdInterestRateReadPlatformServiceImpl implements RdInterestRateReadPlatformService {
|
||||
|
||||
private final RdInterestRateEffectiveRepository effectiveRepository;
|
||||
private final RdInterestRateSlabHistoryRepository slabRepository;
|
||||
|
||||
private static final String[] PERIOD_LABELS = { "", "Days", "Months", "Years" };
|
||||
|
||||
@Override
|
||||
public List<RdInterestRateEffectiveData> retrieveInterestRates(Long productId) {
|
||||
return effectiveRepository.findByProductIdOrderByEffectiveFromDateDesc(productId)
|
||||
.stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RdInterestRateEffectiveData> retrieveAllActiveInterestRates() {
|
||||
return effectiveRepository.findByIsActiveTrueOrderByEffectiveFromDateDesc()
|
||||
.stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public RdInterestRateEffectiveData saveInterestRates(Long productId,
|
||||
RdInterestRateSaveRequest request, Long createdById) {
|
||||
|
||||
validateRequest(request);
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> normalizedSlabs = normalizeSlabs(request.getSlabs());
|
||||
|
||||
// 1. Deactivate all current active versions for this product, preserving end dates for audit/history.
|
||||
final LocalDate today = LocalDate.now();
|
||||
final List<RdInterestRateEffective> activeVersions = effectiveRepository
|
||||
.findByProductIdAndIsActiveTrueOrderByEffectiveFromDateDesc(productId);
|
||||
for (RdInterestRateEffective version : activeVersions) {
|
||||
version.deactivate(today);
|
||||
}
|
||||
if (!activeVersions.isEmpty()) {
|
||||
effectiveRepository.saveAll(activeVersions);
|
||||
}
|
||||
|
||||
// 2. Create the new version header
|
||||
String label = request.getVersionLabel() != null && !request.getVersionLabel().isBlank()
|
||||
? request.getVersionLabel()
|
||||
: "Rate Version " + LocalDate.now();
|
||||
|
||||
RdInterestRateEffective newVersion = RdInterestRateEffective.create(
|
||||
productId, label, LocalDate.now(), request.getRemarks(), createdById);
|
||||
newVersion = effectiveRepository.saveAndFlush(newVersion);
|
||||
|
||||
// 3. Insert slabs for the new version
|
||||
final Long versionId = newVersion.getId();
|
||||
AtomicInteger srCounter = new AtomicInteger(1);
|
||||
for (RdInterestRateSaveRequest.SlabRequest s : normalizedSlabs) {
|
||||
int sr = s.getSrNo() != null ? s.getSrNo() : srCounter.getAndIncrement();
|
||||
RdInterestRateSlabHistory slab = RdInterestRateSlabHistory.create(
|
||||
versionId, sr, s.getDescription(),
|
||||
s.getPeriodTypeEnum(), s.getFromPeriod(), s.getToPeriod(),
|
||||
s.getAnnualInterestRate());
|
||||
slabRepository.save(slab);
|
||||
}
|
||||
|
||||
return toData(newVersion);
|
||||
}
|
||||
|
||||
private void validateRequest(final RdInterestRateSaveRequest request) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder validator = new DataValidatorBuilder(errors).resource("RD_INTEREST_RATE_VERSION");
|
||||
|
||||
if (request == null) {
|
||||
validator.failWithCodeNoParameterAddedToErrorCode("request.cannot.be.null");
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.getSlabs() == null || request.getSlabs().isEmpty()) {
|
||||
validator.parameter("slabs").failWithCode("cannot.be.blank");
|
||||
}
|
||||
|
||||
if (request.getVersionLabel() != null && request.getVersionLabel().length() > 100) {
|
||||
validator.parameter("versionLabel").value(request.getVersionLabel()).failWithCode("exceeds.max.length");
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
private List<RdInterestRateSaveRequest.SlabRequest> normalizeSlabs(final List<RdInterestRateSaveRequest.SlabRequest> slabs) {
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> sorted = new ArrayList<>(slabs);
|
||||
sorted.sort(Comparator.comparing((RdInterestRateSaveRequest.SlabRequest slab) -> slab.getSrNo() == null ? Integer.MAX_VALUE : slab.getSrNo())
|
||||
.thenComparing(slab -> slab.getFromPeriod() == null ? Integer.MAX_VALUE : slab.getFromPeriod()));
|
||||
|
||||
final List<RdInterestRateSaveRequest.SlabRequest> normalized = new ArrayList<>(sorted.size());
|
||||
Integer expectedFrom = 0;
|
||||
for (int i = 0; i < sorted.size(); i++) {
|
||||
final RdInterestRateSaveRequest.SlabRequest source = sorted.get(i);
|
||||
final RdInterestRateSaveRequest.SlabRequest target = new RdInterestRateSaveRequest.SlabRequest();
|
||||
|
||||
final Integer fromPeriod = source.getFromPeriod();
|
||||
if (fromPeriod == null || !fromPeriod.equals(expectedFrom)) {
|
||||
throwValidationError("fromPeriod", "must.start.from.previous.range.end");
|
||||
}
|
||||
target.setFromPeriod(fromPeriod);
|
||||
|
||||
final Integer toPeriod = source.getToPeriod();
|
||||
if (i < sorted.size() - 1) {
|
||||
if (toPeriod == null) {
|
||||
throwValidationError("toPeriod", "cannot.be.blank.for.intermediate.slab");
|
||||
}
|
||||
if (toPeriod < fromPeriod) {
|
||||
throwValidationError("toPeriod", "cannot.be.less.than.fromPeriod");
|
||||
}
|
||||
expectedFrom = toPeriod + 1;
|
||||
target.setToPeriod(toPeriod);
|
||||
} else {
|
||||
target.setToPeriod(null);
|
||||
}
|
||||
|
||||
target.setSrNo(source.getSrNo() == null ? i + 1 : source.getSrNo());
|
||||
target.setDescription(source.getDescription());
|
||||
target.setPeriodTypeEnum(source.getPeriodTypeEnum() == null ? 1 : source.getPeriodTypeEnum());
|
||||
target.setAnnualInterestRate(source.getAnnualInterestRate());
|
||||
|
||||
if (target.getAnnualInterestRate() == null) {
|
||||
throwValidationError("annualInterestRate", "cannot.be.blank");
|
||||
}
|
||||
if (target.getAnnualInterestRate().signum() < 0) {
|
||||
throwValidationError("annualInterestRate", "must.be.non.negative");
|
||||
}
|
||||
|
||||
normalized.add(target);
|
||||
}
|
||||
|
||||
if (!normalized.isEmpty() && normalized.get(0).getFromPeriod() != 0) {
|
||||
throwValidationError("fromPeriod", "must.start.from.zero");
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void throwValidationError(final String parameterName, final String code) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
new DataValidatorBuilder(errors).resource("RD_INTEREST_RATE_VERSION").parameter(parameterName).failWithCode(code);
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
// ── helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
private RdInterestRateEffectiveData toData(RdInterestRateEffective e) {
|
||||
List<RdInterestRateSlabData> slabs = slabRepository
|
||||
.findByEffectiveIdOrderBySrNo(e.getId())
|
||||
.stream()
|
||||
.map(s -> RdInterestRateSlabData.builder()
|
||||
.id(s.getId())
|
||||
.srNo(s.getSrNo())
|
||||
.description(s.getDescription())
|
||||
.periodTypeEnum(s.getPeriodTypeEnum())
|
||||
.periodTypeLabel(periodLabel(s.getPeriodTypeEnum()))
|
||||
.fromPeriod(s.getFromPeriod())
|
||||
.toPeriod(s.getToPeriod())
|
||||
.annualInterestRate(s.getAnnualInterestRate())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return RdInterestRateEffectiveData.builder()
|
||||
.id(e.getId())
|
||||
.productId(e.getProductId())
|
||||
.versionLabel(e.getVersionLabel())
|
||||
.effectiveFromDate(e.getEffectiveFromDate())
|
||||
.effectiveToDate(e.getEffectiveToDate())
|
||||
.isActive(e.getIsActive())
|
||||
.remarks(e.getRemarks())
|
||||
.slabs(slabs)
|
||||
.build();
|
||||
}
|
||||
|
||||
private String periodLabel(Integer code) {
|
||||
if (code == null || code < 1 || code >= PERIOD_LABELS.length) return "Days";
|
||||
return PERIOD_LABELS[code];
|
||||
}
|
||||
}
|
||||
+118
-10
@@ -215,7 +215,22 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
try {
|
||||
final String sql = "select " + this.savingAccountMapper.schema() + " where sa.id = ?";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, this.savingAccountMapper, new Object[] { accountId }); // NOSONAR
|
||||
final SavingsAccountData accountData = this.jdbcTemplate.queryForObject(sql, this.savingAccountMapper,
|
||||
new Object[] { accountId }); // NOSONAR
|
||||
|
||||
// Load joint holders
|
||||
if (accountData != null) {
|
||||
final String jointSql = "SELECT jh.id as id, jh.client_id as clientId, jh.cif_number as cifNumber, "
|
||||
+ "jh.display_name as displayName, jh.share_percentage as sharePercentage, "
|
||||
+ "jh.signing_authority as signingAuthority, jh.role as role, jh.is_primary as isPrimary "
|
||||
+ "FROM m_savings_account_joint_holders jh WHERE jh.savings_account_id = ?";
|
||||
final List<java.util.Map<String, Object>> jointHolders = this.jdbcTemplate.queryForList(jointSql, accountId);
|
||||
if (!jointHolders.isEmpty()) {
|
||||
accountData.setJointHolders(jointHolders);
|
||||
}
|
||||
}
|
||||
|
||||
return accountData;
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new SavingsAccountNotFoundException(accountId, e);
|
||||
}
|
||||
@@ -763,7 +778,43 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sp.is_dormancy_tracking_active as isDormancyTrackingActive, ");
|
||||
sqlBuilder.append("sp.days_to_inactive as daysToInactive, ");
|
||||
sqlBuilder.append("sp.days_to_dormancy as daysToDormancy, ");
|
||||
sqlBuilder.append("sp.days_to_escheat as daysToEscheat ");
|
||||
sqlBuilder.append("sp.days_to_escheat as daysToEscheat, ");
|
||||
// CBS extended fields
|
||||
sqlBuilder.append("sa.cbs_account_type as cbsAccountType, ");
|
||||
sqlBuilder.append("sa.customer_segment as customerSegment, ");
|
||||
sqlBuilder.append("sa.account_status as accountStatus, ");
|
||||
sqlBuilder.append("sa.mode_of_operation as modeOfOperation, ");
|
||||
sqlBuilder.append("sa.special_category as specialCategory, ");
|
||||
sqlBuilder.append("sa.weaker_section as weakerSection, ");
|
||||
sqlBuilder.append("sa.guardian_name as guardianName, ");
|
||||
sqlBuilder.append("sa.guardian_cif_number as guardianCifNumber, ");
|
||||
sqlBuilder.append("sa.guardian_relationship as guardianRelationship, ");
|
||||
sqlBuilder.append("sa.minor_date_of_birth as minorDateOfBirth, ");
|
||||
sqlBuilder.append("sa.guardian_client_id as guardianClientId, ");
|
||||
sqlBuilder.append("sa.introducer_client_id as introducerClientId, ");
|
||||
sqlBuilder.append("sa.introducer_name as introducerName, ");
|
||||
sqlBuilder.append("sa.introducer_cif_number as introducerCifNumber, ");
|
||||
sqlBuilder.append("sa.nominee_data as nomineeData, ");
|
||||
sqlBuilder.append("sa.linked_account_id as linkedAccountId, ");
|
||||
sqlBuilder.append("sa.linked_account_type as linkedAccountType, ");
|
||||
sqlBuilder.append("sa.linked_account_no as linkedAccountNo, ");
|
||||
// Facility config fields
|
||||
sqlBuilder.append("safc.atm_facility as atmFacility, ");
|
||||
sqlBuilder.append("safc.atm_withdrawal_facility as atmWithdrawalFacility, ");
|
||||
sqlBuilder.append("safc.upi_facility as upiFacility, ");
|
||||
sqlBuilder.append("safc.pos_transactions_facility as posTransactionsFacility, ");
|
||||
sqlBuilder.append("safc.cheque_book as chequeBook, ");
|
||||
sqlBuilder.append("safc.imps_facility as impsFacility, ");
|
||||
sqlBuilder.append("safc.sms_alerts as smsAlerts, ");
|
||||
sqlBuilder.append("safc.bbps_facility as bbpsFacility, ");
|
||||
sqlBuilder.append("safc.insurance_cover as insuranceCover, ");
|
||||
sqlBuilder.append("safc.bepg_facility as bepgFacility, ");
|
||||
sqlBuilder.append("safc.neft_facility as neftFacility, ");
|
||||
sqlBuilder.append("safc.rtgs_facility as rtgsFacility, ");
|
||||
sqlBuilder.append("safc.inter_bank_total_cap_facility as interBankTotalCapFacility, ");
|
||||
sqlBuilder.append("safc.withdrawal_facility as withdrawalFacility, ");
|
||||
sqlBuilder.append("safc.deposit_facility_kyc as depositFacilityKyc, ");
|
||||
sqlBuilder.append("safc.virtual_debit_card as virtualDebitCard ");
|
||||
sqlBuilder.append("from m_savings_account sa ");
|
||||
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
|
||||
@@ -777,6 +828,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("left join m_appuser avbu on avbu.id = sa.activatedon_userid ");
|
||||
sqlBuilder.append("left join m_appuser cbu on cbu.id = sa.closedon_userid ");
|
||||
sqlBuilder.append("left join m_tax_group tg on tg.id = sa.tax_group_id ");
|
||||
sqlBuilder.append("left join m_savings_account_facility_config safc on safc.savings_account_id = sa.id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -970,14 +1022,70 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
return SavingsAccountData.instance(id, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, subStatus, reasonForBlock, timeline, currency,
|
||||
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
|
||||
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
|
||||
daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
|
||||
final SavingsAccountData accountData = SavingsAccountData.instance(id, accountNo, depositType, externalId, groupId, groupName,
|
||||
clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, subStatus, reasonForBlock,
|
||||
timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData,
|
||||
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
|
||||
|
||||
// CBS extended fields
|
||||
accountData.setCbsAccountType(rs.getString("cbsAccountType"));
|
||||
accountData.setCustomerSegment(rs.getString("customerSegment"));
|
||||
accountData.setAccountStatus(rs.getString("accountStatus"));
|
||||
accountData.setModeOfOperation(rs.getString("modeOfOperation"));
|
||||
accountData.setSpecialCategory(rs.getString("specialCategory"));
|
||||
accountData.setWeakerSection(rs.getString("weakerSection"));
|
||||
accountData.setGuardianName(rs.getString("guardianName"));
|
||||
accountData.setGuardianCifNumber(rs.getString("guardianCifNumber"));
|
||||
accountData.setGuardianRelationship(rs.getString("guardianRelationship"));
|
||||
accountData.setMinorDateOfBirth(JdbcSupport.getLocalDate(rs, "minorDateOfBirth"));
|
||||
accountData.setGuardianClientId(JdbcSupport.getLong(rs, "guardianClientId"));
|
||||
accountData.setIntroducerClientId(JdbcSupport.getLong(rs, "introducerClientId"));
|
||||
accountData.setIntroducerName(rs.getString("introducerName"));
|
||||
accountData.setIntroducerCifNumber(rs.getString("introducerCifNumber"));
|
||||
accountData.setLinkedAccountId(JdbcSupport.getLong(rs, "linkedAccountId"));
|
||||
accountData.setLinkedAccountType(rs.getString("linkedAccountType"));
|
||||
accountData.setLinkedAccountNo(rs.getString("linkedAccountNo"));
|
||||
|
||||
// Nominee as parsed map
|
||||
final String nomineeDataStr = rs.getString("nomineeData");
|
||||
if (nomineeDataStr != null && !nomineeDataStr.isEmpty()) {
|
||||
try {
|
||||
com.google.gson.Gson gson = new com.google.gson.Gson();
|
||||
@SuppressWarnings("unchecked")
|
||||
java.util.Map<String, Object> nomineeMap = gson.fromJson(nomineeDataStr,
|
||||
new com.google.gson.reflect.TypeToken<java.util.Map<String, Object>>() {}.getType());
|
||||
accountData.setNomineeData(nomineeMap);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
// Facility config
|
||||
final boolean hasFacilityConfig = rs.getObject("atmFacility") != null;
|
||||
if (hasFacilityConfig) {
|
||||
final java.util.Map<String, Object> facilityConfigMap = new java.util.LinkedHashMap<>();
|
||||
facilityConfigMap.put("atmFacility", rs.getBoolean("atmFacility"));
|
||||
facilityConfigMap.put("atmWithdrawalFacility", rs.getBoolean("atmWithdrawalFacility"));
|
||||
facilityConfigMap.put("upiFacility", rs.getBoolean("upiFacility"));
|
||||
facilityConfigMap.put("posTransactionsFacility", rs.getBoolean("posTransactionsFacility"));
|
||||
facilityConfigMap.put("chequeBook", rs.getBoolean("chequeBook"));
|
||||
facilityConfigMap.put("impsFacility", rs.getBoolean("impsFacility"));
|
||||
facilityConfigMap.put("smsAlerts", rs.getBoolean("smsAlerts"));
|
||||
facilityConfigMap.put("bbpsFacility", rs.getBoolean("bbpsFacility"));
|
||||
facilityConfigMap.put("insuranceCover", rs.getBoolean("insuranceCover"));
|
||||
facilityConfigMap.put("bepgFacility", rs.getBoolean("bepgFacility"));
|
||||
facilityConfigMap.put("neftFacility", rs.getBoolean("neftFacility"));
|
||||
facilityConfigMap.put("rtgsFacility", rs.getBoolean("rtgsFacility"));
|
||||
facilityConfigMap.put("interBankTotalCapFacility", rs.getBoolean("interBankTotalCapFacility"));
|
||||
facilityConfigMap.put("withdrawalFacility", rs.getBoolean("withdrawalFacility"));
|
||||
facilityConfigMap.put("depositFacilityKyc", rs.getBoolean("depositFacilityKyc"));
|
||||
facilityConfigMap.put("virtualDebitCard", rs.getBoolean("virtualDebitCard"));
|
||||
accountData.setAccountFacilityConfig(facilityConfigMap);
|
||||
}
|
||||
|
||||
return accountData;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+83
-1
@@ -25,6 +25,10 @@ import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
|
||||
@@ -43,6 +47,8 @@ import org.apache.fineract.portfolio.client.data.ClientData;
|
||||
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
|
||||
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
|
||||
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
@@ -62,6 +68,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Slf4j
|
||||
public class SavingsAccountTemplateReadPlatformServiceImpl implements SavingsAccountTemplateReadPlatformService {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
@@ -72,15 +79,19 @@ public class SavingsAccountTemplateReadPlatformServiceImpl implements SavingsAcc
|
||||
private final StaffReadPlatformService staffReadPlatformService;
|
||||
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
|
||||
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
|
||||
private final AccountNumberGenerator accountNumberGenerator;
|
||||
|
||||
public SavingsAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
|
||||
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
|
||||
final SavingsProductReadPlatformService savingProductReadPlatformService,
|
||||
final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService,
|
||||
final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator) {
|
||||
final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator,
|
||||
final CodeValueReadPlatformService codeValueReadPlatformService,
|
||||
final AccountNumberGenerator accountNumberGenerator) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.clientReadPlatformService = clientReadPlatformService;
|
||||
@@ -90,6 +101,8 @@ public class SavingsAccountTemplateReadPlatformServiceImpl implements SavingsAcc
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
|
||||
this.codeValueReadPlatformService = codeValueReadPlatformService;
|
||||
this.accountNumberGenerator = accountNumberGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,6 +189,75 @@ public class SavingsAccountTemplateReadPlatformServiceImpl implements SavingsAcc
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
|
||||
charges, chargeOptions);
|
||||
|
||||
// Enrich template with full CBS product data (classification, digital facilities, etc.)
|
||||
try {
|
||||
final SavingsProductData fullProduct = this.savingsProductReadPlatformService.retrieveOne(productId);
|
||||
template.setAccountType(fullProduct.getAccountType());
|
||||
template.setCustomerSegment(fullProduct.getCustomerSegment());
|
||||
final Collection<CodeValueData> accountTypeOpts = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(SavingsApiConstants.SAVINGS_PRODUCT_ACCOUNT_TYPE);
|
||||
final Collection<CodeValueData> customerSegmentOpts = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(SavingsApiConstants.SAVINGS_PRODUCT_CUSTOMER_SEGMENT);
|
||||
template.setAccountTypeOptions(accountTypeOpts);
|
||||
template.setCustomerSegmentOptions(customerSegmentOpts);
|
||||
template.setProductCode(fullProduct.getProductCode());
|
||||
template.setEffectiveDate(fullProduct.getEffectiveDate());
|
||||
template.setEffectiveTo(fullProduct.getEffectiveTo());
|
||||
template.setEndDate(fullProduct.getEndDate());
|
||||
template.setIntroducerRequired(fullProduct.getIntroducerRequired());
|
||||
template.setShareLinkingEnabled(fullProduct.getShareLinkingEnabled());
|
||||
template.setNominationFacility(fullProduct.getNominationFacility());
|
||||
template.setDormancyAlert(fullProduct.getDormancyAlert());
|
||||
template.setIsDormancyTrackingActiveFromProduct(fullProduct.getIsDormancyTrackingActive());
|
||||
template.setDaysToInactiveFromProduct(fullProduct.getDaysToInactive());
|
||||
template.setDaysToDormancyFromProduct(fullProduct.getDaysToDormancy());
|
||||
template.setDaysToEscheatFromProduct(fullProduct.getDaysToEscheat());
|
||||
template.setInterestCalculationTypeName(fullProduct.getInterestCalculationTypeName());
|
||||
template.setLastInterestDate(fullProduct.getLastInterestDate());
|
||||
template.setEnableLockinPeriod(fullProduct.getEnableLockinPeriod());
|
||||
template.setBalanceFrequency(fullProduct.getBalanceFrequency());
|
||||
template.setAccountClosedZeroBalanceInDayClosing(fullProduct.getAccountClosedZeroBalanceInDayClosing());
|
||||
template.setBalanceSwapOption(fullProduct.getBalanceSwapOption());
|
||||
template.setMsme(fullProduct.getMsme());
|
||||
template.setAutoDebitFacility(fullProduct.getAutoDebitFacility());
|
||||
template.setAvgBalanceCalcPeriod(fullProduct.getAvgBalanceCalcPeriod());
|
||||
template.setValidForPeriod(fullProduct.getValidForPeriod());
|
||||
template.setOdAccountClosedZeroBalanceInDayClosing(fullProduct.getOdAccountClosedZeroBalanceInDayClosing());
|
||||
template.setAccountFacilityConfig(fullProduct.getAccountFacilityConfig());
|
||||
template.setAccountFacilityLimits(fullProduct.getAccountFacilityLimits());
|
||||
template.setDepositLimitKyc(fullProduct.getDepositLimitKyc());
|
||||
// CBS Term fields
|
||||
template.setInterestCalculateSelector(fullProduct.getInterestCalculateSelector());
|
||||
template.setAveragePeriod(fullProduct.getAveragePeriod());
|
||||
template.setAutoPost(fullProduct.getAutoPost());
|
||||
template.setFrequencyFixDay(fullProduct.getFrequencyFixDay());
|
||||
// CBS dropdown options (fetched from code values, same as product template page)
|
||||
final Collection<EnumOptionData> interestCalculationTypeOption = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(SavingsApiConstants.INTEREST_CALCULATION_TYPE_OPTION).stream()
|
||||
.map(cv -> new EnumOptionData(cv.getId(),
|
||||
"interestCalculationTypeOption." + cv.getName().toLowerCase(Locale.ROOT).replace(' ', '_'),
|
||||
cv.getName()))
|
||||
.toList();
|
||||
template.setInterestCalculationTypeOption(interestCalculationTypeOption);
|
||||
|
||||
final Collection<EnumOptionData> periodFrequencyOptions = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode(SavingsApiConstants.PERIOD_FREQUENCY).stream()
|
||||
.map(cv -> new EnumOptionData(cv.getId(),
|
||||
"periodFrequencyType." + cv.getName().toLowerCase(Locale.ROOT).replace(' ', '_'),
|
||||
cv.getName()))
|
||||
.toList();
|
||||
template.setPeriodFrequencyOptions(periodFrequencyOptions);
|
||||
// Generate preview account number based on product's GL account and office
|
||||
try {
|
||||
final String preview = accountNumberGenerator.previewAccountNumber(productId, officeId);
|
||||
template.setPreviewAccountNo(preview);
|
||||
} catch (final Exception ex) {
|
||||
log.warn("Failed to generate preview account number for productId={}: {}", productId, ex.getMessage());
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to enrich savings account template with CBS product data for productId={}: {}", productId, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
|
||||
String clientName = null;
|
||||
|
||||
+151
-6
@@ -18,10 +18,16 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.math.BigDecimal;
|
||||
import java.lang.reflect.Type;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
@@ -32,6 +38,12 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
@@ -46,6 +58,12 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
private final SavingProductMapper savingsProductRowMapper = new SavingProductMapper();
|
||||
private final SavingProductLookupMapper savingsProductLookupsRowMapper = new SavingProductLookupMapper();
|
||||
private final FineractEntityAccessUtil fineractEntityAccessUtil;
|
||||
private final SavingsProductFacilityConfigRepository facilityConfigRepository;
|
||||
private final SavingsProductFacilityLimitRepository facilityLimitRepository;
|
||||
private final SavingsProductDepositLimitKycRepository depositLimitKycRepository;
|
||||
private static final Gson GSON = new Gson();
|
||||
private static final Type OTHER_GL_MAPPINGS_TYPE = new TypeToken<List<Map<String, Object>>>() {}.getType();
|
||||
private static final Type OTHER_GL_MAPPINGS_MAP_TYPE = new TypeToken<Map<String, List<Map<String, Object>>>>() {}.getType();
|
||||
|
||||
@Override
|
||||
public Collection<SavingsProductData> retrieveAll() {
|
||||
@@ -87,8 +105,66 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
final String sql = "select " + this.savingsProductRowMapper.schema() + " where sp.id = ? and sp.deposit_type_enum = ?";
|
||||
return this.jdbcTemplate.queryForObject(sql, this.savingsProductRowMapper, // NOSONAR
|
||||
SavingsProductData product = this.jdbcTemplate.queryForObject(sql, this.savingsProductRowMapper, // NOSONAR
|
||||
new Object[] { savingProductId, DepositAccountType.SAVINGS_DEPOSIT.getValue() });
|
||||
|
||||
// Fetch related data using repositories
|
||||
if (product != null) {
|
||||
// Fetch Facility Config using repository
|
||||
SavingsProductFacilityConfig facilityConfig = facilityConfigRepository.findBySavingsProductId(savingProductId);
|
||||
if (facilityConfig != null) {
|
||||
Map<String, Object> facilityConfigMap = new HashMap<>();
|
||||
facilityConfigMap.put("atmFacility", facilityConfig.getAtmFacility());
|
||||
facilityConfigMap.put("atmWithdrawalFacility", facilityConfig.getAtmWithdrawalFacility());
|
||||
facilityConfigMap.put("upiFacility", facilityConfig.getUpiFacility());
|
||||
facilityConfigMap.put("posTransactionsFacility", facilityConfig.getPosTransactionsFacility());
|
||||
facilityConfigMap.put("chequeBook", facilityConfig.getChequeBook());
|
||||
facilityConfigMap.put("impsFacility", facilityConfig.getImpsFacility());
|
||||
facilityConfigMap.put("smsAlerts", facilityConfig.getSmsAlerts());
|
||||
facilityConfigMap.put("bbpsFacility", facilityConfig.getBbpsFacility());
|
||||
facilityConfigMap.put("insuranceCover", facilityConfig.getInsuranceCover());
|
||||
facilityConfigMap.put("bepgFacility", facilityConfig.getBepgFacility());
|
||||
facilityConfigMap.put("neftFacility", facilityConfig.getNeftFacility());
|
||||
facilityConfigMap.put("rtgsFacility", facilityConfig.getRtgsFacility());
|
||||
facilityConfigMap.put("interBankTotalCapFacility", facilityConfig.getInterBankTotalCapFacility());
|
||||
facilityConfigMap.put("withdrawalFacility", facilityConfig.getWithdrawalFacility());
|
||||
facilityConfigMap.put("depositFacilityKyc", facilityConfig.getDepositFacilityKyc());
|
||||
facilityConfigMap.put("virtualDebitCard", facilityConfig.getVirtualDebitCard());
|
||||
product.setAccountFacilityConfig(facilityConfigMap);
|
||||
}
|
||||
|
||||
// Fetch Facility Limits using repository (1-to-Many Array)
|
||||
List<SavingsProductFacilityLimit> facilityLimits = facilityLimitRepository.findBySavingsProductId(savingProductId);
|
||||
if (facilityLimits != null && !facilityLimits.isEmpty()) {
|
||||
List<Map<String, Object>> limits = facilityLimits.stream().map(limit -> {
|
||||
Map<String, Object> limitMap = new HashMap<>();
|
||||
limitMap.put("limitType", limit.getLimitType());
|
||||
limitMap.put("generalLimit", limit.getGeneralLimit());
|
||||
limitMap.put("minorAccountLimit", limit.getMinorAccountLimit());
|
||||
limitMap.put("smallSavingLimit", limit.getSmallSavingLimit());
|
||||
return limitMap;
|
||||
}).toList();
|
||||
product.setAccountFacilityLimits(limits);
|
||||
}
|
||||
|
||||
// Fetch Deposit Limit KYC using repository
|
||||
SavingsProductDepositLimitKyc depositLimitKyc = depositLimitKycRepository.findBySavingsProductId(savingProductId);
|
||||
if (depositLimitKyc != null) {
|
||||
Map<String, Object> depositLimitKycMap = new HashMap<>();
|
||||
depositLimitKycMap.put("general", Map.of(
|
||||
"kycType", depositLimitKyc.getGeneralKycType() != null ? depositLimitKyc.getGeneralKycType() : "",
|
||||
"limit", depositLimitKyc.getGeneralLimit() != null ? depositLimitKyc.getGeneralLimit() : ""));
|
||||
depositLimitKycMap.put("minor", Map.of(
|
||||
"kycType", depositLimitKyc.getMinorKycType() != null ? depositLimitKyc.getMinorKycType() : "",
|
||||
"limit", depositLimitKyc.getMinorLimit()));
|
||||
depositLimitKycMap.put("smallSaving", Map.of(
|
||||
"kycType", depositLimitKyc.getSmallSavingKycType() != null ? depositLimitKyc.getSmallSavingKycType() : "",
|
||||
"limit", depositLimitKyc.getSmallSavingLimit()));
|
||||
product.setDepositLimitKyc(depositLimitKycMap);
|
||||
}
|
||||
}
|
||||
|
||||
return product;
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new SavingsProductNotFoundException(savingProductId, e);
|
||||
}
|
||||
@@ -99,7 +175,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
private final String schemaSql;
|
||||
|
||||
SavingProductMapper() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
final StringBuilder sqlBuilder = new StringBuilder(1000);
|
||||
sqlBuilder.append("sp.id as id, sp.name as name, sp.short_name as shortName, sp.description as description, ");
|
||||
sqlBuilder.append(
|
||||
"sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMultiplesOf, ");
|
||||
@@ -129,10 +205,25 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
sqlBuilder.append("sp.is_dormancy_tracking_active as isDormancyTrackingActive,");
|
||||
sqlBuilder.append("sp.days_to_inactive as daysToInactive,");
|
||||
sqlBuilder.append("sp.days_to_dormancy as daysToDormancy,");
|
||||
sqlBuilder.append("sp.days_to_escheat as daysToEscheat ");
|
||||
sqlBuilder.append("sp.days_to_escheat as daysToEscheat, ");
|
||||
sqlBuilder.append("sp.other_gl_mappings as otherGlMappings, ");
|
||||
|
||||
// Enhanced Field Selections
|
||||
sqlBuilder.append("sp.account_type as accountType, sp.customer_segment as customerSegment, sp.product_code as productCode, sp.gl_account_id as glAccountId, gla.name as glAccountName, ");
|
||||
sqlBuilder.append("sp.effective_date as effectiveDate, sp.effective_to as effectiveTo, sp.end_date as endDate, ");
|
||||
sqlBuilder.append("sp.introducer_required as introducerRequired, sp.share_linking_enabled as shareLinkingEnabled, ");
|
||||
sqlBuilder.append("sp.nomination_facility as nominationFacility, sp.dormancy_alert as dormancyAlert, ");
|
||||
sqlBuilder.append("sp.interest_calculate_selector as interestCalculateSelector, icv.code_value as interestCalculationTypeName, sp.average_period as averagePeriod, ");
|
||||
sqlBuilder.append("sp.auto_post as autoPost, sp.frequency_fix_day as frequencyFixDay, sp.last_interest_date as lastInterestDate, ");
|
||||
sqlBuilder.append("sp.enable_lockin_period as enableLockinPeriod, ");
|
||||
sqlBuilder.append("sp.balance_frequency as balanceFrequency, ");
|
||||
sqlBuilder.append("sp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing ");
|
||||
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
sqlBuilder.append("left join m_tax_group tg on tg.id = sp.tax_group_id ");
|
||||
sqlBuilder.append("left join m_tax_group tg on tg.id = sp.tax_group_id ");
|
||||
sqlBuilder.append("left join m_code_value icv on icv.id = sp.interest_calculate_selector ");
|
||||
sqlBuilder.append("left join acc_gl_account gla on gla.id = sp.gl_account_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -212,13 +303,45 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
final Long daysToInactive = JdbcSupport.getLong(rs, "daysToInactive");
|
||||
final Long daysToDormancy = JdbcSupport.getLong(rs, "daysToDormancy");
|
||||
final Long daysToEscheat = JdbcSupport.getLong(rs, "daysToEscheat");
|
||||
final String otherGlMappingsJson = rs.getString("otherGlMappings");
|
||||
final List<Map<String, Object>> otherGlMappings = parseOtherGlMappings(otherGlMappingsJson);
|
||||
final Map<String, List<Map<String, Object>>> otherGlMappingsMap = parseOtherGlMappingsAsMap(otherGlMappingsJson);
|
||||
|
||||
return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
SavingsProductData productData = SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
|
||||
accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit,
|
||||
lienAllowed, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation,
|
||||
withHoldTax, taxGroupData, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
withHoldTax, taxGroupData, null, null, null, isDormancyTrackingActive, daysToInactive, daysToDormancy,
|
||||
daysToEscheat, otherGlMappings);
|
||||
if (otherGlMappingsMap != null) {
|
||||
productData.setOtherGlMappingsMap(otherGlMappingsMap);
|
||||
}
|
||||
|
||||
// Populate Enhanced Fields
|
||||
productData.setAccountType(rs.getString("accountType"));
|
||||
productData.setCustomerSegment(rs.getString("customerSegment"));
|
||||
productData.setProductCode(rs.getString("productCode"));
|
||||
productData.setGlAccountId(JdbcSupport.getLong(rs, "glAccountId"));
|
||||
productData.setGlAccountName(rs.getString("glAccountName"));
|
||||
productData.setEffectiveDate(JdbcSupport.getLocalDate(rs, "effectiveDate"));
|
||||
productData.setEffectiveTo(JdbcSupport.getLocalDate(rs, "effectiveTo"));
|
||||
productData.setEndDate(JdbcSupport.getLocalDate(rs, "endDate"));
|
||||
productData.setIntroducerRequired(rs.getBoolean("introducerRequired"));
|
||||
productData.setShareLinkingEnabled(rs.getBoolean("shareLinkingEnabled"));
|
||||
productData.setNominationFacility(rs.getBoolean("nominationFacility"));
|
||||
productData.setDormancyAlert(rs.getBoolean("dormancyAlert"));
|
||||
productData.setInterestCalculateSelector(JdbcSupport.getInteger(rs, "interestCalculateSelector"));
|
||||
productData.setInterestCalculationTypeName(rs.getString("interestCalculationTypeName"));
|
||||
productData.setAveragePeriod(rs.getString("averagePeriod"));
|
||||
productData.setAutoPost(rs.getBoolean("autoPost"));
|
||||
productData.setFrequencyFixDay(JdbcSupport.getInteger(rs, "frequencyFixDay"));
|
||||
productData.setLastInterestDate(JdbcSupport.getLocalDate(rs, "lastInterestDate"));
|
||||
productData.setEnableLockinPeriod(rs.getBoolean("enableLockinPeriod"));
|
||||
productData.setBalanceFrequency(rs.getString("balanceFrequency"));
|
||||
productData.setAccountClosedZeroBalanceInDayClosing(rs.getBoolean("accountClosedZeroBalanceInDayClosing"));
|
||||
|
||||
return productData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +361,28 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Map<String, Object>> parseOtherGlMappings(final String otherGlMappingsJson) {
|
||||
if (otherGlMappingsJson == null || otherGlMappingsJson.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
com.google.gson.JsonElement element = GSON.fromJson(otherGlMappingsJson, com.google.gson.JsonElement.class);
|
||||
if (!element.isJsonArray()) {
|
||||
return null;
|
||||
}
|
||||
return GSON.fromJson(element, OTHER_GL_MAPPINGS_TYPE);
|
||||
}
|
||||
|
||||
private static Map<String, List<Map<String, Object>>> parseOtherGlMappingsAsMap(final String otherGlMappingsJson) {
|
||||
if (otherGlMappingsJson == null || otherGlMappingsJson.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
com.google.gson.JsonElement element = GSON.fromJson(otherGlMappingsJson, com.google.gson.JsonElement.class);
|
||||
if (!element.isJsonObject()) {
|
||||
return null;
|
||||
}
|
||||
return GSON.fromJson(element, OTHER_GL_MAPPINGS_MAP_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SavingsProductData> retrieveAllForLookupByType(Boolean isOverdraftType) {
|
||||
String sql = "select " + this.savingsProductLookupsRowMapper.schema();
|
||||
|
||||
+95
-1
@@ -28,6 +28,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
@@ -46,11 +47,18 @@ import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductDataValidator;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -64,6 +72,9 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final SavingsProductAssembler savingsProductAssembler;
|
||||
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
|
||||
private final FineractEntityAccessUtil fineractEntityAccessUtil;
|
||||
private final SavingsProductFacilityConfigRepository facilityConfigRepository;
|
||||
private final SavingsProductFacilityLimitRepository facilityLimitRepository;
|
||||
private final SavingsProductDepositLimitKycRepository depositLimitKycRepository;
|
||||
|
||||
/*
|
||||
* Guaranteed to throw an exception no matter what the data integrity issue is.
|
||||
@@ -86,8 +97,15 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
msg = "Savings product with short name `" + shortName + "` already exists";
|
||||
param = "shortName";
|
||||
msgArgs = new Object[] { shortName, dae };
|
||||
} else if (checkEx.getMessage().contains("product_code") || checkEx.getMessage().contains("sp_unq_product_code")) {
|
||||
final String productCode = command.stringValueOfParameterNamed("productCode");
|
||||
msgCode += ".duplicate.product.code";
|
||||
msg = "Savings product with product code `" + productCode + "` already exists";
|
||||
param = "productCode";
|
||||
msgArgs = new Object[] { productCode, dae };
|
||||
} else {
|
||||
msgCode += ".unknown.data.integrity.issue";
|
||||
msg = "Unknown data integrity issue with savings product. Details: " + checkEx.getMessage();
|
||||
msgArgs = new Object[] { dae };
|
||||
}
|
||||
log.error("Error occured.", dae);
|
||||
@@ -103,8 +121,29 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
final SavingsProduct product = this.savingsProductAssembler.assemble(command);
|
||||
|
||||
// Assemble and save new complex objects
|
||||
final SavingsProductFacilityConfig facilityConfig =
|
||||
this.savingsProductAssembler.assembleAccountFacilityConfig(command, product);
|
||||
final Set<SavingsProductFacilityLimit> facilityLimits =
|
||||
this.savingsProductAssembler.assembleAccountFacilityLimits(command, product);
|
||||
final SavingsProductDepositLimitKyc depositLimitKyc =
|
||||
this.savingsProductAssembler.assembleDepositLimitKyc(command, product);
|
||||
|
||||
this.savingProductRepository.saveAndFlush(product);
|
||||
|
||||
// Save related entities
|
||||
if (facilityConfig != null) {
|
||||
this.facilityConfigRepository.saveAndFlush(facilityConfig);
|
||||
}
|
||||
|
||||
if (!facilityLimits.isEmpty()) {
|
||||
this.facilityLimitRepository.saveAll(facilityLimits);
|
||||
}
|
||||
|
||||
if (depositLimitKyc != null) {
|
||||
this.depositLimitKycRepository.saveAndFlush(depositLimitKyc);
|
||||
}
|
||||
|
||||
// save accounting mappings
|
||||
this.accountMappingWritePlatformService.createSavingProductToGLAccountMapping(product.getId(), command,
|
||||
DepositAccountType.SAVINGS_DEPOSIT);
|
||||
@@ -162,6 +201,61 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
}
|
||||
}
|
||||
|
||||
// Update facility config
|
||||
final SavingsProductFacilityConfig existingFacilityConfig = product.getFacilityConfig();
|
||||
final SavingsProductFacilityConfig newFacilityConfig =
|
||||
this.savingsProductAssembler.assembleAccountFacilityConfig(command, product);
|
||||
|
||||
if (newFacilityConfig != null) {
|
||||
if (existingFacilityConfig != null) {
|
||||
// Update existing config
|
||||
existingFacilityConfig.updateFrom(newFacilityConfig);
|
||||
this.facilityConfigRepository.saveAndFlush(existingFacilityConfig);
|
||||
} else {
|
||||
// Create new config
|
||||
this.facilityConfigRepository.saveAndFlush(newFacilityConfig);
|
||||
}
|
||||
} else if (existingFacilityConfig != null) {
|
||||
// Delete existing config if not provided in update
|
||||
this.facilityConfigRepository.delete(existingFacilityConfig);
|
||||
}
|
||||
|
||||
// Update facility limits (delete old, insert new)
|
||||
final Set<SavingsProductFacilityLimit> newFacilityLimits =
|
||||
this.savingsProductAssembler.assembleAccountFacilityLimits(command, product);
|
||||
|
||||
if (!newFacilityLimits.isEmpty() || command.hasParameter("accountFacilityLimits")) {
|
||||
// Delete existing limits
|
||||
if (product.getFacilityLimits() != null && !product.getFacilityLimits().isEmpty()) {
|
||||
this.facilityLimitRepository.deleteAll(product.getFacilityLimits());
|
||||
product.getFacilityLimits().clear();
|
||||
}
|
||||
// Save new limits
|
||||
if (!newFacilityLimits.isEmpty()) {
|
||||
this.facilityLimitRepository.saveAll(newFacilityLimits);
|
||||
product.setFacilityLimits(newFacilityLimits);
|
||||
}
|
||||
}
|
||||
|
||||
// Update deposit limit KYC
|
||||
final SavingsProductDepositLimitKyc existingDepositLimitKyc = product.getDepositLimitKyc();
|
||||
final SavingsProductDepositLimitKyc newDepositLimitKyc =
|
||||
this.savingsProductAssembler.assembleDepositLimitKyc(command, product);
|
||||
|
||||
if (newDepositLimitKyc != null) {
|
||||
if (existingDepositLimitKyc != null) {
|
||||
// Update existing KYC
|
||||
existingDepositLimitKyc.updateFrom(newDepositLimitKyc);
|
||||
this.depositLimitKycRepository.saveAndFlush(existingDepositLimitKyc);
|
||||
} else {
|
||||
// Create new KYC
|
||||
this.depositLimitKycRepository.saveAndFlush(newDepositLimitKyc);
|
||||
}
|
||||
} else if (existingDepositLimitKyc != null) {
|
||||
// Delete existing KYC if not provided in update
|
||||
this.depositLimitKycRepository.delete(existingDepositLimitKyc);
|
||||
}
|
||||
|
||||
// accounting related changes
|
||||
final boolean accountingTypeChanged = changes.containsKey(accountingRuleParamName);
|
||||
final Map<String, Object> accountingMappingChanges = this.accountMappingWritePlatformService
|
||||
|
||||
+55
-10
@@ -91,6 +91,11 @@ import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrap
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsHelper;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositAccountInterestRateChartReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositAccountInterestRateChartReadPlatformServiceImpl;
|
||||
@@ -124,6 +129,9 @@ import org.apache.fineract.portfolio.savings.service.SavingsAccountInterestPosti
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountInterestPostingServiceImpl;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
@@ -136,6 +144,10 @@ import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformS
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformServiceJpaRepositoryImpl;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository;
|
||||
import org.apache.fineract.portfolio.savings.service.RdBaseInterestRateReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.RdBaseInterestRateReadPlatformServiceImpl;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsSchedularInterestPoster;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsSchedularInterestPosterTask;
|
||||
import org.apache.fineract.portfolio.savings.service.search.SavingsAccountTransactionSearchService;
|
||||
@@ -151,6 +163,14 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@Configuration
|
||||
public class SavingsConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RdBaseInterestRateReadPlatformService.class)
|
||||
public RdBaseInterestRateReadPlatformService rdBaseInterestRateReadPlatformService(
|
||||
RdBaseInterestRateEffectiveRepository effectiveRepository,
|
||||
RdBaseInterestRateSlabHistoryRepository slabRepository) {
|
||||
return new RdBaseInterestRateReadPlatformServiceImpl(effectiveRepository, slabRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SavingsAccountTransactionSearchService.class)
|
||||
public SavingsAccountTransactionSearchService savingsAccountTransactionSearchService(PlatformSecurityContext context,
|
||||
@@ -207,12 +227,13 @@ public class SavingsConfiguration {
|
||||
SavingsDropdownReadPlatformService savingsDropdownReadPlatformService, ChargeReadPlatformService chargeReadPlatformService,
|
||||
StaffReadPlatformService staffReadPlatformService, DepositsDropdownReadPlatformService depositsDropdownReadPlatformService,
|
||||
SavingsAccountReadPlatformService savingsAccountReadPlatformService, DropdownReadPlatformService dropdownReadPlatformService,
|
||||
CalendarReadPlatformService calendarReadPlatformService, PaymentTypeReadService paymentTypeReadPlatformService) {
|
||||
CalendarReadPlatformService calendarReadPlatformService, PaymentTypeReadService paymentTypeReadPlatformService,
|
||||
AccountNumberGenerator accountNumberGenerator, CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||
return new DepositAccountReadPlatformServiceImpl(context, jdbcTemplate, chartReadPlatformService, productChartReadPlatformService,
|
||||
paginationParametersDataValidator, sqlGenerator, paginationHelper, clientReadPlatformService, groupReadPlatformService,
|
||||
depositProductReadPlatformService, savingsDropdownReadPlatformService, chargeReadPlatformService, staffReadPlatformService,
|
||||
depositsDropdownReadPlatformService, savingsAccountReadPlatformService, dropdownReadPlatformService,
|
||||
calendarReadPlatformService, paymentTypeReadPlatformService);
|
||||
calendarReadPlatformService, paymentTypeReadPlatformService, accountNumberGenerator, codeValueReadPlatformService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -350,10 +371,25 @@ public class SavingsConfiguration {
|
||||
GroupReadPlatformService groupReadPlatformService, SavingsProductReadPlatformService savingProductReadPlatformService,
|
||||
StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService,
|
||||
ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
ColumnValidator columnValidator) {
|
||||
ColumnValidator columnValidator, CodeValueReadPlatformService codeValueReadPlatformService,
|
||||
AccountNumberGenerator accountNumberGenerator) {
|
||||
return new SavingsAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService,
|
||||
savingProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService,
|
||||
entityDatatableChecksReadService, columnValidator);
|
||||
entityDatatableChecksReadService, columnValidator, codeValueReadPlatformService, accountNumberGenerator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CurrentAccountTemplateReadPlatformService.class)
|
||||
public CurrentAccountTemplateReadPlatformService currentAccountTemplateReadPlatformService(PlatformSecurityContext context,
|
||||
JdbcTemplate jdbcTemplate, ClientReadPlatformService clientReadPlatformService,
|
||||
GroupReadPlatformService groupReadPlatformService, CurrentProductReadPlatformService currentProductReadPlatformService,
|
||||
StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService,
|
||||
ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
ColumnValidator columnValidator, AccountNumberGenerator accountNumberGenerator,
|
||||
CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||
return new CurrentAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService,
|
||||
currentProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService,
|
||||
entityDatatableChecksReadService, columnValidator, accountNumberGenerator, codeValueReadPlatformService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -414,19 +450,27 @@ public class SavingsConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SavingsProductReadPlatformService.class)
|
||||
public SavingsProductReadPlatformService savingsProductReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate,
|
||||
FineractEntityAccessUtil fineractEntityAccessUtil) {
|
||||
return new SavingsProductReadPlatformServiceImpl(context, jdbcTemplate, fineractEntityAccessUtil);
|
||||
FineractEntityAccessUtil fineractEntityAccessUtil, SavingsProductFacilityConfigRepository facilityConfigRepository,
|
||||
SavingsProductFacilityLimitRepository facilityLimitRepository, SavingsProductDepositLimitKycRepository depositLimitKycRepository) {
|
||||
return new SavingsProductReadPlatformServiceImpl(context, jdbcTemplate, fineractEntityAccessUtil, facilityConfigRepository,
|
||||
facilityLimitRepository, depositLimitKycRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SavingsProductWritePlatformService.class)
|
||||
public SavingsProductWritePlatformService savingsProductWritePlatformService(PlatformSecurityContext context,
|
||||
SavingsProductRepository savingProductRepository, SavingsProductDataValidator fromApiJsonDataValidator,
|
||||
public SavingsProductWritePlatformService savingsProductWritePlatformService(
|
||||
PlatformSecurityContext context,
|
||||
SavingsProductRepository savingProductRepository,
|
||||
SavingsProductDataValidator fromApiJsonDataValidator,
|
||||
SavingsProductAssembler savingsProductAssembler,
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
||||
FineractEntityAccessUtil fineractEntityAccessUtil) {
|
||||
FineractEntityAccessUtil fineractEntityAccessUtil,
|
||||
SavingsProductFacilityConfigRepository facilityConfigRepository,
|
||||
SavingsProductFacilityLimitRepository facilityLimitRepository,
|
||||
SavingsProductDepositLimitKycRepository depositLimitKycRepository) {
|
||||
return new SavingsProductWritePlatformServiceJpaRepositoryImpl(context, savingProductRepository, fromApiJsonDataValidator,
|
||||
savingsProductAssembler, accountMappingWritePlatformService, fineractEntityAccessUtil);
|
||||
savingsProductAssembler, accountMappingWritePlatformService, fineractEntityAccessUtil, facilityConfigRepository,
|
||||
facilityLimitRepository, depositLimitKycRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -447,4 +491,5 @@ public class SavingsConfiguration {
|
||||
public SavingsSchedularInterestPosterTask savingsSchedularInterestPosterTask(SavingsSchedularInterestPoster interestPoster) {
|
||||
return new SavingsSchedularInterestPosterTask(interestPoster);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ fineract.security.oauth2.client.registrations.frontend-client.require-authorizat
|
||||
fineract.tenant.host=${FINERACT_DEFAULT_TENANTDB_HOSTNAME:localhost}
|
||||
fineract.tenant.port=${FINERACT_DEFAULT_TENANTDB_PORT:5432}
|
||||
fineract.tenant.username=${FINERACT_DEFAULT_TENANTDB_UID:postgres}
|
||||
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:root}
|
||||
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:313863}
|
||||
fineract.tenant.parameters=${FINERACT_DEFAULT_TENANTDB_CONN_PARAMS:}
|
||||
fineract.tenant.timezone=${FINERACT_DEFAULT_TENANTDB_TIMEZONE:Asia/Kolkata}
|
||||
fineract.tenant.identifier=${FINERACT_DEFAULT_TENANTDB_IDENTIFIER:default}
|
||||
@@ -405,7 +405,7 @@ server.tomcat.mbeanregistry.enabled=${FINERACT_SERVER_TOMCAT_MBEANREGISTRY_ENABL
|
||||
spring.datasource.hikari.driverClassName=${FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME:org.postgresql.Driver}
|
||||
spring.datasource.hikari.jdbcUrl=${FINERACT_HIKARI_JDBC_URL:jdbc:postgresql://localhost:5432/fineract_tenants}
|
||||
spring.datasource.hikari.username=${FINERACT_HIKARI_USERNAME:postgres}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:root}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:313863}
|
||||
spring.datasource.hikari.minimumIdle=${FINERACT_HIKARI_MINIMUM_IDLE:3}
|
||||
spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10}
|
||||
spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000}
|
||||
|
||||
@@ -268,6 +268,14 @@
|
||||
<include file="parts/0247_add_huf_client_classification.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0248_add_cin_din_verification_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0249_add_non_person_extended_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0251_add_savings_product_enhanced_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0255_create_current_product_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0256_add_recurring_deposit_product_codes.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0257_create_rd_interest_rate_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0267_seed_rd_interest_rate_data.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0270_add_rd_product_extra_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0271_create_rd_base_interest_rate_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0272_seed_rd_base_interest_rate_data.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0250_add_kyc_director_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0251_add_kyc_proof_type_codes.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0252_add_ckyc_uploaded_on.xml" relativeToChangelogFile="true"/>
|
||||
@@ -278,4 +286,7 @@
|
||||
<include file="parts/0257_add_religion_social_category_codes.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0258_add_agriculture_non_agriculture_income.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0259_add_ckyc_report_indexes.xml" relativeToChangelogFile="true"/>
|
||||
<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"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<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.1.xsd">
|
||||
<changeSet author="fineract" id="0250_add_period_frequency_options">
|
||||
<insert tableName="m_code">
|
||||
<column name="code_name" value="PeriodFrequency"/>
|
||||
<column name="is_system_defined" valueBoolean="true"/>
|
||||
</insert>
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'PeriodFrequency')"/>
|
||||
<column name="code_value" value="Monthly"/>
|
||||
<column name="code_description" value="Monthly"/>
|
||||
<column name="order_position" valueNumeric="1"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'PeriodFrequency')"/>
|
||||
<column name="code_value" value="Quarterly"/>
|
||||
<column name="code_description" value="Quarterly"/>
|
||||
<column name="order_position" valueNumeric="2"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'PeriodFrequency')"/>
|
||||
<column name="code_value" value="Half Yearly"/>
|
||||
<column name="code_description" value="Half Yearly"/>
|
||||
<column name="order_position" valueNumeric="3"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'PeriodFrequency')"/>
|
||||
<column name="code_value" value="Yearly"/>
|
||||
<column name="code_description" value="Yearly"/>
|
||||
<column name="order_position" valueNumeric="4"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<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.1.xsd">
|
||||
|
||||
<!-- Add new columns to m_savings_product table -->
|
||||
<changeSet id="0251_add_savings_product_enhanced_fields" author="system">
|
||||
<addColumn tableName="m_savings_product">
|
||||
<column name="account_type" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="customer_segment" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="product_code" type="varchar(20)">
|
||||
<constraints nullable="true" unique="true"/>
|
||||
</column>
|
||||
<column name="effective_date" type="date">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="effective_to" type="date">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="end_date" type="date">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="introducer_required" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="share_linking_enabled" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="nomination_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="dormancy_alert" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="interest_calculate_selector" type="int">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="average_period" type="varchar(20)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="auto_post" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="frequency_fix_day" type="int">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="last_interest_date" type="date">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="enable_lockin_period" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- Create m_savings_product_facility_config table -->
|
||||
<changeSet id="0252_create_savings_product_facility_config_table" author="system">
|
||||
<createTable tableName="m_savings_product_facility_config">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="savings_product_id" type="bigint">
|
||||
<constraints nullable="false" unique="true"/>
|
||||
</column>
|
||||
<column name="atm_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="atm_withdrawal_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="upi_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="pos_transactions_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="cheque_book" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="imps_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="sms_alerts" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="bbps_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="insurance_cover" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="bepg_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="neft_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="rtgs_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="inter_bank_total_cap_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="withdrawal_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="deposit_facility_kyc" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="virtual_debit_card" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_date" type="datetime" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_savings_product_facility_config"
|
||||
baseColumnNames="savings_product_id"
|
||||
referencedTableName="m_savings_product"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_savings_product_facility_config_product_id"
|
||||
onDelete="CASCADE"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- Create m_savings_product_facility_limits table -->
|
||||
<changeSet id="0253_create_savings_product_facility_limits_table" author="system">
|
||||
<createTable tableName="m_savings_product_facility_limits">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="savings_product_id" type="bigint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="limit_type" type="varchar(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="general_limit" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="minor_account_limit" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="small_saving_limit" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="created_date" type="datetime" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_savings_product_facility_limits"
|
||||
baseColumnNames="savings_product_id"
|
||||
referencedTableName="m_savings_product"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_savings_product_facility_limits_product_id"
|
||||
onDelete="CASCADE"/>
|
||||
|
||||
<createIndex tableName="m_savings_product_facility_limits" indexName="idx_savings_product_facility_limits_product_type">
|
||||
<column name="savings_product_id"/>
|
||||
<column name="limit_type"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<!-- Create m_savings_product_deposit_limit_kyc table -->
|
||||
<changeSet id="0254_create_savings_product_deposit_limit_kyc_table" author="system">
|
||||
<createTable tableName="m_savings_product_deposit_limit_kyc">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="savings_product_id" type="bigint">
|
||||
<constraints nullable="false" unique="true"/>
|
||||
</column>
|
||||
<column name="general_kyc_type" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="general_limit" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="minor_kyc_type" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="minor_limit" type="bigint">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="small_saving_kyc_type" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="small_saving_limit" type="bigint">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="created_date" type="datetime" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_savings_product_deposit_limit_kyc"
|
||||
baseColumnNames="savings_product_id"
|
||||
referencedTableName="m_savings_product"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_savings_product_deposit_limit_kyc_product_id"
|
||||
onDelete="CASCADE"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+390
@@ -0,0 +1,390 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<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.1.xsd">
|
||||
|
||||
<!-- Create m_current_product table (same structure as m_savings_product) -->
|
||||
<changeSet id="0255_create_current_product_table" author="system">
|
||||
<createTable tableName="m_current_product">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="name" type="varchar(100)">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="cp_unq_name"/>
|
||||
</column>
|
||||
<column name="short_name" type="varchar(4)">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="cp_unq_short_name"/>
|
||||
</column>
|
||||
<column name="description" type="varchar(500)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="deposit_type_enum" type="smallint" defaultValue="400">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="currency_code" type="varchar(3)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="currency_digits" type="smallint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="currency_multiplesof" type="smallint">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="nominal_annual_interest_rate" type="decimal(19,6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="interest_compounding_period_enum" type="smallint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="interest_posting_period_enum" type="smallint" defaultValue="4">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="interest_calculation_type_enum" type="smallint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="interest_calculation_days_in_year_type_enum" type="smallint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="min_required_opening_balance" type="decimal(19,6)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="lockin_period_frequency" type="int">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="lockin_period_frequency_enum" type="smallint">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="accounting_type" type="smallint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="withdrawal_fee_for_transfer" type="boolean" defaultValue="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="allow_overdraft" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="overdraft_limit" type="decimal(19,6)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="nominal_annual_interest_rate_overdraft" type="decimal(19,6)" defaultValue="0.000000">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="min_overdraft_for_interest_calculation" type="decimal(19,6)" defaultValue="0.000000">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="enforce_min_required_balance" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="min_required_balance" type="decimal(19,6)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="is_lien_allowed" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="max_allowed_lien_limit" type="decimal(19,6)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="min_balance_for_interest_calculation" type="decimal(19,6)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="withhold_tax" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="tax_group_id" type="bigint">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="is_dormancy_tracking_active" type="boolean">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="days_to_inactive" type="int">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="days_to_dormancy" type="int">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="days_to_escheat" type="int">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="other_gl_mappings" type="longtext">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<!-- Enhanced fields -->
|
||||
<column name="account_type" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="customer_segment" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="product_code" type="varchar(20)">
|
||||
<constraints nullable="true" unique="true" uniqueConstraintName="cp_unq_product_code"/>
|
||||
</column>
|
||||
<column name="effective_date" type="date">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="effective_to" type="date">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="end_date" type="date">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="introducer_required" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="share_linking_enabled" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="nomination_facility" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="dormancy_alert" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="interest_calculate_selector" type="int">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="average_period" type="varchar(20)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="auto_post" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="frequency_fix_day" type="int">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="last_interest_date" type="date">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="enable_lockin_period" type="boolean" defaultValue="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="balance_frequency" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="account_closed_zero_balance_in_day_closing" type="boolean" defaultValue="false">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<addForeignKeyConstraint baseTableName="m_current_product" baseColumnNames="tax_group_id"
|
||||
constraintName="fk_current_product_tax_group"
|
||||
referencedTableName="m_tax_group" referencedColumnNames="id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- Create m_current_product_charge join table -->
|
||||
<changeSet id="0256_create_current_product_charge_table" author="system">
|
||||
<createTable tableName="m_current_product_charge">
|
||||
<column name="current_product_id" type="bigint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="charge_id" type="bigint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addPrimaryKey tableName="m_current_product_charge" columnNames="current_product_id,charge_id"
|
||||
constraintName="pk_current_product_charge"/>
|
||||
<addForeignKeyConstraint baseTableName="m_current_product_charge" baseColumnNames="current_product_id"
|
||||
constraintName="fk_current_product_charge_product"
|
||||
referencedTableName="m_current_product" referencedColumnNames="id"/>
|
||||
<addForeignKeyConstraint baseTableName="m_current_product_charge" baseColumnNames="charge_id"
|
||||
constraintName="fk_current_product_charge_charge"
|
||||
referencedTableName="m_charge" referencedColumnNames="id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- Create m_current_product_facility_config table -->
|
||||
<changeSet id="0257_create_current_product_facility_config_table" author="system">
|
||||
<createTable tableName="m_current_product_facility_config">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_product_id" type="bigint">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="uq_current_product_facility_config_product_id"/>
|
||||
</column>
|
||||
<column name="atm_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="atm_withdrawal_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="upi_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="pos_transactions_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="cheque_book" type="boolean" defaultValue="false"/>
|
||||
<column name="imps_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="sms_alerts" type="boolean" defaultValue="false"/>
|
||||
<column name="bbps_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="insurance_cover" type="boolean" defaultValue="false"/>
|
||||
<column name="bepg_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="neft_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="rtgs_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="inter_bank_total_cap_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="withdrawal_facility" type="boolean" defaultValue="false"/>
|
||||
<column name="deposit_facility_kyc" type="boolean" defaultValue="false"/>
|
||||
<column name="virtual_debit_card" type="boolean" defaultValue="false"/>
|
||||
<column name="created_date" type="datetime">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_current_product_facility_config" baseColumnNames="current_product_id"
|
||||
constraintName="fk_current_product_facility_config_product_id"
|
||||
referencedTableName="m_current_product" referencedColumnNames="id"
|
||||
onDelete="CASCADE"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- Create m_current_product_facility_limits table -->
|
||||
<changeSet id="0258_create_current_product_facility_limits_table" author="system">
|
||||
<createTable tableName="m_current_product_facility_limits">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_product_id" type="bigint">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="limit_type" type="varchar(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="general_limit" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="minor_account_limit" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="small_saving_limit" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="created_date" type="datetime">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_current_product_facility_limits" baseColumnNames="current_product_id"
|
||||
constraintName="fk_current_product_facility_limits_product_id"
|
||||
referencedTableName="m_current_product" referencedColumnNames="id"
|
||||
onDelete="CASCADE"/>
|
||||
<createIndex tableName="m_current_product_facility_limits" indexName="idx_current_product_facility_limits_product_type">
|
||||
<column name="current_product_id"/>
|
||||
<column name="limit_type"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<!-- Create m_current_product_deposit_limit_kyc table -->
|
||||
<changeSet id="0259_create_current_product_deposit_limit_kyc_table" author="system">
|
||||
<createTable tableName="m_current_product_deposit_limit_kyc">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_product_id" type="bigint">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="uq_current_product_deposit_limit_kyc_product_id"/>
|
||||
</column>
|
||||
<column name="general_kyc_type" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="general_limit" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="minor_kyc_type" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="minor_limit" type="bigint">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="small_saving_kyc_type" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="small_saving_limit" type="bigint">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="created_date" type="datetime">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_current_product_deposit_limit_kyc" baseColumnNames="current_product_id"
|
||||
constraintName="fk_current_product_deposit_limit_kyc_product_id"
|
||||
referencedTableName="m_current_product" referencedColumnNames="id"
|
||||
onDelete="CASCADE"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- Add new UI fields to m_current_product -->
|
||||
<changeSet id="0262_add_current_product_new_ui_fields" author="system">
|
||||
<addColumn tableName="m_current_product">
|
||||
<column name="balance_swap_option" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="msme" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="auto_debit_facility" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="avg_balance_calc_period" type="varchar(10)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="valid_for_period" type="varchar(10)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="od_account_closed_zero_balance_in_day_closing" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- Insert CurrentProductAccountType code and values (separate from SavingsProductAccountType) -->
|
||||
<changeSet id="0260_add_current_product_account_type_code" author="system">
|
||||
<sql>
|
||||
INSERT INTO m_code (code_name, is_system_defined)
|
||||
SELECT 'CurrentProductAccountType', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'CurrentProductAccountType');
|
||||
</sql>
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
|
||||
SELECT c.id, v.code_value, v.order_position, true, false
|
||||
FROM m_code c,
|
||||
(VALUES
|
||||
('Standard Account', 1),
|
||||
('Basic Account', 2),
|
||||
('Premium Current Account', 3)
|
||||
) AS v(code_value, order_position)
|
||||
WHERE c.code_name = 'CurrentProductAccountType'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_code_value cv
|
||||
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
|
||||
);
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<!-- Insert CurrentProductCustomerSegment code and values (separate from SavingsProductCustomerSegment) -->
|
||||
<changeSet id="0261_add_current_product_customer_segment_code" author="system">
|
||||
<sql>
|
||||
INSERT INTO m_code (code_name, is_system_defined)
|
||||
SELECT 'CurrentProductCustomerSegment', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'CurrentProductCustomerSegment');
|
||||
</sql>
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
|
||||
SELECT c.id, v.code_value, v.order_position, true, false
|
||||
FROM m_code c,
|
||||
(VALUES
|
||||
('Non Individual', 1)
|
||||
) AS v(code_value, order_position)
|
||||
WHERE c.code_name = 'CurrentProductCustomerSegment'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_code_value cv
|
||||
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
|
||||
);
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?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.1.xsd">
|
||||
|
||||
<!-- RecurringDepositProductAccountType code and values -->
|
||||
<changeSet id="0262_add_rd_product_account_type_code" author="system">
|
||||
<sql>
|
||||
INSERT INTO m_code (code_name, is_system_defined)
|
||||
SELECT 'RecurringDepositProductAccountType', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'RecurringDepositProductAccountType');
|
||||
</sql>
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
|
||||
SELECT c.id, v.code_value, v.order_position, true, false
|
||||
FROM m_code c,
|
||||
(VALUES
|
||||
('Rd Account', 1)
|
||||
) AS v(code_value, order_position)
|
||||
WHERE c.code_name = 'RecurringDepositProductAccountType'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_code_value cv
|
||||
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
|
||||
);
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<!-- RecurringDepositProductCustomerSegment code and values -->
|
||||
<changeSet id="0263_add_rd_product_customer_segment_code" author="system">
|
||||
<sql>
|
||||
INSERT INTO m_code (code_name, is_system_defined)
|
||||
SELECT 'RecurringDepositProductCustomerSegment', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'RecurringDepositProductCustomerSegment');
|
||||
</sql>
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
|
||||
SELECT c.id, v.code_value, v.order_position, true, false
|
||||
FROM m_code c,
|
||||
(VALUES
|
||||
('Individual', 1),
|
||||
('Non-Individual', 2)
|
||||
) AS v(code_value, order_position)
|
||||
WHERE c.code_name = 'RecurringDepositProductCustomerSegment'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_code_value cv
|
||||
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
|
||||
);
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
<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.1.xsd">
|
||||
|
||||
<!--
|
||||
m_rd_interest_rate_effective — one row per rate-version per product.
|
||||
m_rd_interest_rate_slab_history — slabs for every version (immutable once written).
|
||||
|
||||
Period Type Enum: 1 = Days, 2 = Months, 3 = Years
|
||||
-->
|
||||
|
||||
<changeSet id="0257a_create_rd_interest_rate_effective" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_rd_interest_rate_effective"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_rd_interest_rate_effective">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="product_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="version_label" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_from_date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_to_date" type="DATE"/>
|
||||
<column name="is_active" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="remarks" type="VARCHAR(500)"/>
|
||||
<column name="created_by_id" type="BIGINT"/>
|
||||
<column name="created_date" type="DATE"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_rd_interest_rate_effective"
|
||||
baseColumnNames="product_id"
|
||||
referencedTableName="m_savings_product"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_rd_rate_effective_product"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0257b_create_rd_interest_rate_slab_history" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_rd_interest_rate_slab_history"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_rd_interest_rate_slab_history">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="sr_no" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="description" type="VARCHAR(200)"/>
|
||||
<column name="period_type_enum" type="INT" defaultValueNumeric="1">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="from_period" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="to_period" type="INT"/>
|
||||
<column name="annual_interest_rate" type="DECIMAL(19,6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_date" type="DATE"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_rd_interest_rate_slab_history"
|
||||
baseColumnNames="effective_id"
|
||||
referencedTableName="m_rd_interest_rate_effective"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_rd_rate_slab_effective"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
<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.1.xsd">
|
||||
|
||||
<!--
|
||||
Seed RD Interest Rate Data (runs only if both tables are empty)
|
||||
Sr Particular Rate
|
||||
1 0 Days to 29 Days 3.00%
|
||||
2 30 Days to 45 Days 3.00%
|
||||
3 46 Days to 90 Days 3.00%
|
||||
4 91 Days to 180 Days 4.50%
|
||||
5 181 Days to below 1 Year 5.50%
|
||||
6 1 Year to Below 2 Year 7.00%
|
||||
7 444 Days 7.00%
|
||||
8 400 Days 7.00%
|
||||
9 500 Days 7.00%
|
||||
10 2 Year to Below 3 Year 7.00%
|
||||
11 3 Year to Below 5 Year 7.25%
|
||||
12 5 Year & Above 6.50%
|
||||
-->
|
||||
|
||||
<changeSet id="0267_insert_rd_rate_version" author="fineract">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_rd_interest_rate_effective</sqlCheck>
|
||||
</preConditions>
|
||||
<sql>
|
||||
INSERT INTO m_rd_interest_rate_effective
|
||||
(product_id, version_label, effective_from_date, effective_to_date, is_active, remarks, created_by_id, created_date)
|
||||
SELECT id, 'Initial Rate Version', '2025-01-01', NULL, TRUE,
|
||||
'Standard interest rate card published for RD products', 1, CURRENT_DATE
|
||||
FROM m_savings_product
|
||||
WHERE deposit_type_enum = 300
|
||||
ORDER BY id LIMIT 1;
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0268_insert_rd_rate_slabs" author="fineract">
|
||||
<validCheckSum>9:e35a0696046940f370f7cfb55c20740b</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_rd_interest_rate_slab_history</sqlCheck>
|
||||
</preConditions>
|
||||
<sql>
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 1, '0 Days to 29 Days', 1, 0, 29, 3.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 2, '30 Days to 45 Days', 1, 30, 45, 3.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 3, '46 Days to 90 Days', 1, 46, 90, 3.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 4, '91 Days to 180 Days', 1, 91, 180, 4.500000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 5, '181 Days to below 1 Year', 1, 181, 364, 5.500000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 6, '1 Year to Below 2 Year', 1, 365, 729, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 7, '444 Days', 1, 444, 444, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 8, '400 Days', 1, 400, 400, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 9, '500 Days', 1, 500, 500, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 10, '2 Year to Below 3 Year', 1, 730, 1094, 7.000000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 11, '3 Year to Below 5 Year', 1, 1095, 1824, 7.250000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
|
||||
INSERT INTO m_rd_interest_rate_slab_history (effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT e.id, 12, '5 Year & Above', 1, 1825, NULL, 6.500000, CURRENT_DATE FROM m_rd_interest_rate_effective e WHERE e.version_label='Initial Rate Version' AND e.product_id=(SELECT id FROM m_savings_product WHERE deposit_type_enum=300 ORDER BY id LIMIT 1);
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
<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.1.xsd">
|
||||
|
||||
<!-- balance_frequency — may already exist from a prior manual migration -->
|
||||
<changeSet id="0270a_add_balance_frequency" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_savings_product" columnName="balance_frequency"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_savings_product">
|
||||
<column name="balance_frequency" type="varchar(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- account_closed_zero_balance_in_day_closing — may already exist -->
|
||||
<changeSet id="0270b_add_account_closed_zero_balance" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_savings_product" columnName="account_closed_zero_balance_in_day_closing"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_savings_product">
|
||||
<column name="account_closed_zero_balance_in_day_closing" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- 14 new RD-specific boolean flags -->
|
||||
<changeSet id="0270c_add_rd_product_flags" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_savings_product" columnName="balance_swap_option"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_savings_product">
|
||||
<column name="balance_swap_option" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="msme" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="auto_debit_facility" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="loan_facility" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="premature_withdrawal_option" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="tenure_flexibility" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="lien_marked" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="maturity_instructions" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="credit_maturity_proceeds" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="penalty_grace_period_missed_installments" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="panel_request_applicable" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="no_penalty_nominee_heir_closure" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="deceased_settlement" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="allow_debit_txn_after_due_date" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?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.1.xsd">
|
||||
|
||||
<changeSet id="0271a_create_rd_base_interest_rate_effective" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_rd_base_interest_rate_effective"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_rd_base_interest_rate_effective">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="version_label" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_from_date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_to_date" type="DATE"/>
|
||||
<column name="is_active" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="remarks" type="VARCHAR(500)"/>
|
||||
<column name="created_by_id" type="BIGINT"/>
|
||||
<column name="created_date" type="DATE"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0271b_create_rd_base_interest_rate_slab_history" author="fineract">
|
||||
<validCheckSum>ANY</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_rd_base_interest_rate_slab_history"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_rd_base_interest_rate_slab_history">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="effective_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="sr_no" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="description" type="VARCHAR(200)"/>
|
||||
<column name="period_type_enum" type="INT" defaultValueNumeric="1">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="from_period" type="INT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="to_period" type="INT"/>
|
||||
<column name="annual_interest_rate" type="DECIMAL(19,6)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_date" type="DATE"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_rd_base_interest_rate_slab_history"
|
||||
baseColumnNames="effective_id"
|
||||
referencedTableName="m_rd_base_interest_rate_effective"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_rd_base_rate_slab_effective"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?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.1.xsd">
|
||||
|
||||
<changeSet id="0272_insert_rd_base_rate_version" author="fineract">
|
||||
<validCheckSum>9:4cb4d062d4ee9464fe5a4e32023580c2</validCheckSum>
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_rd_base_interest_rate_effective</sqlCheck>
|
||||
</preConditions>
|
||||
<sql>
|
||||
INSERT INTO m_rd_base_interest_rate_effective
|
||||
(version_label, effective_from_date, effective_to_date, is_active, remarks, created_by_id, created_date)
|
||||
VALUES
|
||||
('CBS Base Rate Version', '2025-01-01', NULL, TRUE, 'Central RD base chart', 1, CURRENT_DATE);
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 1, '0 Days to 29 Days', 1, 0, 29, 3.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 2, '30 Days to 45 Days', 1, 30, 45, 3.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 3, '46 Days to 90 Days', 1, 46, 90, 3.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 4, '91 Days to 180 Days', 1, 91, 180, 4.500000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 5, '181 Days to below 1 Year', 1, 181, 364, 5.500000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 6, '1 Year to Below 2 Year', 1, 365, 729, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 7, '444 Days', 1, 444, 444, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 8, '400 Days', 1, 400, 400, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 9, '500 Days', 1, 500, 500, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 10, '2 Year to Below 3 Year', 1, 730, 1094, 7.000000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 11, '3 Year to Below 5 Year', 1, 1095, 1824, 7.250000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
|
||||
INSERT INTO m_rd_base_interest_rate_slab_history
|
||||
(effective_id, sr_no, description, period_type_enum, from_period, to_period, annual_interest_rate, created_date)
|
||||
SELECT id, 12, '5 Year & Above', 1, 1825, NULL, 6.500000, CURRENT_DATE
|
||||
FROM m_rd_base_interest_rate_effective WHERE version_label = 'CBS Base Rate Version';
|
||||
</sql>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<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.1.xsd">
|
||||
<changeSet author="fineract" id="0273-add-gl-account-id-to-current-product">
|
||||
<addColumn tableName="m_current_product">
|
||||
<column name="gl_account_id" type="BIGINT">
|
||||
<constraints nullable="true"
|
||||
foreignKeyName="fk_current_product_gl_account"
|
||||
references="acc_gl_account(id)"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
<?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">
|
||||
|
||||
<!-- =========================================================
|
||||
m_current_account — main current account table
|
||||
(fully separate from m_savings_account)
|
||||
========================================================= -->
|
||||
<changeSet id="0276_create_current_account_tables" author="system">
|
||||
|
||||
<createTable tableName="m_current_account">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="account_no" type="varchar(20)">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="ca_unq_account_no"/>
|
||||
</column>
|
||||
<column name="external_id" type="varchar(100)">
|
||||
<constraints nullable="true" unique="true" uniqueConstraintName="ca_unq_external_id"/>
|
||||
</column>
|
||||
|
||||
<!-- Ownership -->
|
||||
<column name="client_id" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_client" referencedTableName="m_client" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="group_id" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_group" referencedTableName="m_group" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="product_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_ca_product" referencedTableName="m_current_product" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="office_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_ca_office" referencedTableName="m_office" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="staff_id" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_staff" referencedTableName="m_staff" referencedColumnNames="id"/>
|
||||
</column>
|
||||
|
||||
<!-- Status -->
|
||||
<column name="status_enum" type="smallint" defaultValue="100">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<!-- 100=Submitted, 200=Approved, 300=Active, 400=Withdrawn, 500=Rejected, 600=Closed -->
|
||||
<column name="sub_status_enum" type="smallint" defaultValue="0">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<!-- 0=None, 100=Inactive, 200=Dormant, 300=Escheat, 400=Block, 500=BlockDebit, 600=BlockCredit -->
|
||||
<column name="account_type_enum" type="smallint" defaultValue="1">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<!-- 1=Individual, 2=Group, 3=JLG -->
|
||||
|
||||
<!-- Lifecycle dates -->
|
||||
<column name="submittedon_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="submittedon_userid" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_submit_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="approvedon_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="approvedon_userid" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_approve_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="activatedon_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="activatedon_userid" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_activate_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="rejectedon_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="rejectedon_userid" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_reject_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="withdrawnon_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="withdrawnon_userid" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_withdraw_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="closedon_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="closedon_userid" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_close_user" referencedTableName="m_appuser" referencedColumnNames="id"/>
|
||||
</column>
|
||||
|
||||
<!-- Currency (copied from product at account creation) -->
|
||||
<column name="currency_code" type="varchar(3)"><constraints nullable="false"/></column>
|
||||
<column name="currency_digits" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="currency_multiplesof" type="smallint"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Interest settings (copied from product) -->
|
||||
<column name="nominal_annual_interest_rate" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="interest_compounding_period_enum" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="interest_posting_period_enum" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="interest_calculation_type_enum" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="interest_calculation_days_in_year_type_enum" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="min_balance_for_interest_calculation" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="start_interest_calculation_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="accrued_till_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="last_interest_calculation_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="last_closed_business_date" type="date"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Tax -->
|
||||
<column name="withhold_tax" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="tax_group_id" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_ca_tax_group" referencedTableName="m_tax_group" referencedColumnNames="id"/>
|
||||
</column>
|
||||
|
||||
<!-- Balance rules (copied from product) -->
|
||||
<column name="min_required_opening_balance" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="lockin_period_frequency" type="smallint"><constraints nullable="true"/></column>
|
||||
<column name="lockin_period_frequency_enum" type="smallint"><constraints nullable="true"/></column>
|
||||
<column name="lockedin_until_date_derived" type="date"><constraints nullable="true"/></column>
|
||||
<column name="enforce_min_required_balance" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="min_required_balance" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Overdraft (copied from product) -->
|
||||
<column name="allow_overdraft" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="overdraft_limit" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="nominal_annual_interest_rate_overdraft" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="min_overdraft_for_interest_calculation" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Lien -->
|
||||
<column name="is_lien_allowed" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="max_allowed_lien_limit" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="on_hold_funds_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
|
||||
<!-- Running balance summary -->
|
||||
<column name="account_balance_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="total_deposits_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="total_withdrawals_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="total_interest_earned_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="total_interest_posted_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="total_overdraft_interest_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="total_withhold_tax_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="total_savings_amount_on_hold" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="withdrawal_fee_for_transfer" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
|
||||
<!-- Block reason -->
|
||||
<column name="reason_for_block" type="varchar(500)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Current-account-specific fields -->
|
||||
<column name="msme" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="auto_debit_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="balance_swap_option" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="establishment_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="start_operation_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="is_non_individual_client" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="signatory_rights" type="varchar(100)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- CBS account type / segment -->
|
||||
<column name="cbs_account_type" type="varchar(100)"><constraints nullable="true"/></column>
|
||||
<column name="customer_segment" type="varchar(100)"><constraints nullable="true"/></column>
|
||||
<column name="account_status" type="varchar(50)"><constraints nullable="true"/></column>
|
||||
<column name="mode_of_operation" type="varchar(50)"><constraints nullable="true"/></column>
|
||||
<column name="special_category" type="varchar(100)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Introducer -->
|
||||
<column name="introducer_client_id" type="bigint"><constraints nullable="true"/></column>
|
||||
<column name="introducer_name" type="varchar(200)"><constraints nullable="true"/></column>
|
||||
<column name="introducer_cif_number" type="varchar(50)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Nominee (JSON) -->
|
||||
<column name="nominee_data" type="text"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Interest calculation settings from product -->
|
||||
<column name="interest_calculate_selector" type="smallint"><constraints nullable="true"/></column>
|
||||
<column name="average_period" type="varchar(20)"><constraints nullable="true"/></column>
|
||||
<column name="auto_post" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="frequency_fix_day" type="smallint"><constraints nullable="true"/></column>
|
||||
<column name="enable_lockin_period" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="balance_frequency" type="varchar(50)"><constraints nullable="true"/></column>
|
||||
<column name="avg_balance_calc_period" type="varchar(10)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Audit -->
|
||||
<column name="created_by" type="bigint"><constraints nullable="true"/></column>
|
||||
<column name="created_date" type="datetime"><constraints nullable="true"/></column>
|
||||
<column name="last_modified_by" type="bigint"><constraints nullable="true"/></column>
|
||||
<column name="last_modified_date" type="datetime"><constraints nullable="true"/></column>
|
||||
</createTable>
|
||||
|
||||
<!-- =========================================================
|
||||
m_current_account_transaction
|
||||
========================================================= -->
|
||||
<createTable tableName="m_current_account_transaction">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_account_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_cat_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="office_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_cat_office" referencedTableName="m_office" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="payment_detail_id" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_cat_payment" referencedTableName="m_payment_detail" referencedColumnNames="id"/>
|
||||
</column>
|
||||
|
||||
<!-- Type: 1=Deposit, 2=Withdrawal, 3=Interest, 4=Charge, 5=ChargeWaiver, 6=Transfer, 7=Reversal -->
|
||||
<column name="transaction_type_enum" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="transaction_date" type="date"><constraints nullable="false"/></column>
|
||||
<column name="amount" type="decimal(19,6)"><constraints nullable="false"/></column>
|
||||
<column name="ref_no" type="varchar(100)"><constraints nullable="true"/></column>
|
||||
<column name="remarks" type="varchar(500)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- State -->
|
||||
<column name="is_reversed" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="is_reversal" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="original_transaction_id" type="bigint"><constraints nullable="true"/></column>
|
||||
<column name="is_manual" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
|
||||
<!-- Balance -->
|
||||
<column name="running_balance_derived" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="cumulative_balance_derived" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="balance_end_date_derived" type="date"><constraints nullable="true"/></column>
|
||||
<column name="balance_number_of_days_derived" type="integer"><constraints nullable="true"/></column>
|
||||
<column name="overdraft_amount_derived" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
|
||||
<!-- Lien / Hold -->
|
||||
<column name="is_lien_transaction" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="release_id_of_hold_amount_transaction" type="bigint"><constraints nullable="true"/></column>
|
||||
<column name="reason_for_block" type="varchar(200)"><constraints nullable="true"/></column>
|
||||
|
||||
<column name="submitted_on_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="created_date" type="datetime"><constraints nullable="true"/></column>
|
||||
<column name="created_by" type="bigint"><constraints nullable="true"/></column>
|
||||
</createTable>
|
||||
|
||||
<!-- =========================================================
|
||||
m_current_account_charge
|
||||
========================================================= -->
|
||||
<createTable tableName="m_current_account_charge">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_account_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_cac_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="charge_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_cac_charge" referencedTableName="m_charge" referencedColumnNames="id"/>
|
||||
</column>
|
||||
|
||||
<column name="charge_time_enum" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="charge_due_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="fee_on_month" type="smallint"><constraints nullable="true"/></column>
|
||||
<column name="fee_on_day" type="smallint"><constraints nullable="true"/></column>
|
||||
<column name="fee_interval" type="smallint"><constraints nullable="true"/></column>
|
||||
|
||||
<column name="charge_calculation_enum" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="calculation_percentage" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="calculation_on_amount" type="decimal(19,6)"><constraints nullable="true"/></column>
|
||||
<column name="amount" type="decimal(19,6)"><constraints nullable="false"/></column>
|
||||
|
||||
<column name="amount_paid_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="amount_waived_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="amount_writtenoff_derived" type="decimal(19,6)" defaultValue="0"><constraints nullable="false"/></column>
|
||||
<column name="amount_outstanding_derived" type="decimal(19,6)"><constraints nullable="false"/></column>
|
||||
|
||||
<column name="is_paid_derived" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="waived" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="is_penalty" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="is_active" type="boolean" defaultValueBoolean="true"><constraints nullable="false"/></column>
|
||||
<column name="inactivated_on_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="free_withdrawal_count" type="integer"><constraints nullable="true"/></column>
|
||||
<column name="charge_reset_date" type="date"><constraints nullable="true"/></column>
|
||||
</createTable>
|
||||
|
||||
<!-- =========================================================
|
||||
m_current_account_facility_config
|
||||
ATM / NEFT / RTGS / Cheque Book etc.
|
||||
========================================================= -->
|
||||
<createTable tableName="m_current_account_facility_config">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_account_id" type="bigint">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="ca_fac_unq_account"
|
||||
foreignKeyName="fk_cafac_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
|
||||
</column>
|
||||
|
||||
<column name="atm_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="atm_withdrawal_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="upi_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="pos_transactions_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="cheque_book" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="imps_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="sms_alerts" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="bbps_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="insurance_cover" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="bepg_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="neft_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="rtgs_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="inter_bank_total_cap_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="withdrawal_facility" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="deposit_facility_kyc" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="virtual_debit_card" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="internet_banking" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
<column name="mobile_banking" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
</createTable>
|
||||
|
||||
<!-- =========================================================
|
||||
m_current_account_joint_holder
|
||||
Joint account holders
|
||||
========================================================= -->
|
||||
<createTable tableName="m_current_account_joint_holder">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_account_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_cajh_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="client_id" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_cajh_client" referencedTableName="m_client" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="cif_number" type="varchar(50)"><constraints nullable="true"/></column>
|
||||
<column name="display_name" type="varchar(200)"><constraints nullable="true"/></column>
|
||||
<column name="share_percentage" type="decimal(5,2)"><constraints nullable="true"/></column>
|
||||
<column name="signing_authority" type="varchar(100)"><constraints nullable="true"/></column>
|
||||
<column name="role" type="varchar(100)"><constraints nullable="true"/></column>
|
||||
<column name="is_primary" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
</createTable>
|
||||
|
||||
<!-- =========================================================
|
||||
m_current_account_partner
|
||||
Partners for non-individual (business) current accounts
|
||||
========================================================= -->
|
||||
<createTable tableName="m_current_account_partner">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_account_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_cap_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="client_id" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_cap_client" referencedTableName="m_client" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="client_cif" type="varchar(50)"><constraints nullable="true"/></column>
|
||||
<column name="client_name" type="varchar(200)"><constraints nullable="true"/></column>
|
||||
<column name="share_percentage" type="decimal(5,2)"><constraints nullable="true"/></column>
|
||||
<column name="is_mandatory" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
</createTable>
|
||||
|
||||
<!-- =========================================================
|
||||
m_current_account_officer_history
|
||||
Officer assignment history
|
||||
========================================================= -->
|
||||
<createTable tableName="m_current_account_officer_history">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_account_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_caoh_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="staff_id" type="bigint">
|
||||
<constraints nullable="true" foreignKeyName="fk_caoh_staff" referencedTableName="m_staff" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="start_date" type="date"><constraints nullable="false"/></column>
|
||||
<column name="end_date" type="date"><constraints nullable="true"/></column>
|
||||
<column name="created_by_id" type="bigint"><constraints nullable="true"/></column>
|
||||
</createTable>
|
||||
|
||||
<!-- =========================================================
|
||||
m_current_account_on_hold_transaction
|
||||
On-hold / lien funds
|
||||
========================================================= -->
|
||||
<createTable tableName="m_current_account_on_hold_transaction">
|
||||
<column name="id" type="bigint" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="current_account_id" type="bigint">
|
||||
<constraints nullable="false" foreignKeyName="fk_caoht_account" referencedTableName="m_current_account" referencedColumnNames="id"/>
|
||||
</column>
|
||||
<column name="amount" type="decimal(19,6)"><constraints nullable="false"/></column>
|
||||
<!-- 1=Hold, 2=Release -->
|
||||
<column name="transaction_type_enum" type="smallint"><constraints nullable="false"/></column>
|
||||
<column name="transaction_date" type="date"><constraints nullable="false"/></column>
|
||||
<column name="is_reversed" type="boolean" defaultValueBoolean="false"><constraints nullable="false"/></column>
|
||||
</createTable>
|
||||
|
||||
<!-- Indexes for fast lookup -->
|
||||
<createIndex indexName="idx_ca_client_id" tableName="m_current_account">
|
||||
<column name="client_id"/>
|
||||
</createIndex>
|
||||
<createIndex indexName="idx_ca_product_id" tableName="m_current_account">
|
||||
<column name="product_id"/>
|
||||
</createIndex>
|
||||
<createIndex indexName="idx_ca_status" tableName="m_current_account">
|
||||
<column name="status_enum"/>
|
||||
</createIndex>
|
||||
<createIndex indexName="idx_cat_account_id" tableName="m_current_account_transaction">
|
||||
<column name="current_account_id"/>
|
||||
</createIndex>
|
||||
<createIndex indexName="idx_cat_date" tableName="m_current_account_transaction">
|
||||
<column name="transaction_date"/>
|
||||
</createIndex>
|
||||
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?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">
|
||||
|
||||
<!-- Rename audit columns in m_current_account to match AbstractAuditableCustom mappings -->
|
||||
<changeSet id="0277_fix_current_account_audit_columns" author="system">
|
||||
<renameColumn tableName="m_current_account" oldColumnName="created_by" newColumnName="createdby_id" columnDataType="bigint"/>
|
||||
<renameColumn tableName="m_current_account" oldColumnName="last_modified_by" newColumnName="lastmodifiedby_id" columnDataType="bigint"/>
|
||||
<renameColumn tableName="m_current_account" oldColumnName="last_modified_date" newColumnName="lastmodified_date" columnDataType="datetime"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+14
@@ -151,6 +151,15 @@
|
||||
<class>org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMapping</class>
|
||||
<class>org.apache.fineract.portfolio.self.registration.domain.SelfServiceRegistration</class>
|
||||
|
||||
<!-- Current Account Entities -->
|
||||
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount</class>
|
||||
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction</class>
|
||||
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountCharge</class>
|
||||
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfig</class>
|
||||
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountJointHolder</class>
|
||||
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountPartner</class>
|
||||
<class>org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountOfficerHistory</class>
|
||||
|
||||
<!-- Charge Module Entities -->
|
||||
<class>org.apache.fineract.portfolio.charge.domain.Charge</class>
|
||||
|
||||
@@ -260,6 +269,11 @@
|
||||
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionTaxDetails</class>
|
||||
<class>org.apache.fineract.portfolio.savings.domain.SavingsOfficerAssignmentHistory</class>
|
||||
<class>org.apache.fineract.portfolio.savings.domain.SavingsProduct</class>
|
||||
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc</class>
|
||||
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig</class>
|
||||
<class>org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit</class>
|
||||
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfig</class>
|
||||
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountJointHolder</class>
|
||||
|
||||
<!-- Progressive Loan Module Entities -->
|
||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance</class>
|
||||
|
||||
@@ -4667,6 +4667,7 @@ CREATE TABLE IF NOT EXISTS `m_savings_product` (
|
||||
`days_to_inactive` INT DEFAULT NULL,
|
||||
`days_to_dormancy` INT DEFAULT NULL,
|
||||
`days_to_escheat` INT DEFAULT NULL,
|
||||
`other_gl_mappings` LONGTEXT DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `sp_unq_name` (`name`),
|
||||
UNIQUE KEY `sp_unq_short_name` (`short_name`),
|
||||
|
||||
+3
-2
@@ -4918,6 +4918,7 @@ CREATE TABLE IF NOT EXISTS `m_savings_product` (
|
||||
`days_to_inactive` INT DEFAULT NULL,
|
||||
`days_to_dormancy` INT DEFAULT NULL,
|
||||
`days_to_escheat` INT DEFAULT NULL,
|
||||
`other_gl_mappings` LONGTEXT DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `sp_unq_name` (`name`),
|
||||
UNIQUE KEY `sp_unq_short_name` (`short_name`),
|
||||
@@ -4927,8 +4928,8 @@ CREATE TABLE IF NOT EXISTS `m_savings_product` (
|
||||
|
||||
-- Dumping data for table mifostenant-reference.m_savings_product: ~1 rows (approximately)
|
||||
/*!40000 ALTER TABLE `m_savings_product` DISABLE KEYS */;
|
||||
INSERT INTO `m_savings_product` (`id`, `name`, `short_name`, `description`, `deposit_type_enum`, `currency_code`, `currency_digits`, `currency_multiplesof`, `nominal_annual_interest_rate`, `interest_compounding_period_enum`, `interest_posting_period_enum`, `interest_calculation_type_enum`, `interest_calculation_days_in_year_type_enum`, `min_required_opening_balance`, `lockin_period_frequency`, `lockin_period_frequency_enum`, `accounting_type`, `withdrawal_fee_amount`, `withdrawal_fee_type_enum`, `withdrawal_fee_for_transfer`, `allow_overdraft`, `overdraft_limit`, `nominal_annual_interest_rate_overdraft`, `min_overdraft_for_interest_calculation`, `min_required_balance`, `enforce_min_required_balance`, `min_balance_for_interest_calculation`, `withhold_tax`, `tax_group_id`, `is_dormancy_tracking_active`, `days_to_inactive`, `days_to_dormancy`, `days_to_escheat`) VALUES
|
||||
(1, 'Voluntary savings', 'VS', 'Save money', 100, 'USD', 2, 0, 9.500000, 1, 4, 1, 365, 1000.000000, 1.000000, 1, 2, NULL, NULL, 0, 0, NULL, 0.000000, 0.000000, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `m_savings_product` (`id`, `name`, `short_name`, `description`, `deposit_type_enum`, `currency_code`, `currency_digits`, `currency_multiplesof`, `nominal_annual_interest_rate`, `interest_compounding_period_enum`, `interest_posting_period_enum`, `interest_calculation_type_enum`, `interest_calculation_days_in_year_type_enum`, `min_required_opening_balance`, `lockin_period_frequency`, `lockin_period_frequency_enum`, `accounting_type`, `withdrawal_fee_amount`, `withdrawal_fee_type_enum`, `withdrawal_fee_for_transfer`, `allow_overdraft`, `overdraft_limit`, `nominal_annual_interest_rate_overdraft`, `min_overdraft_for_interest_calculation`, `min_required_balance`, `enforce_min_required_balance`, `min_balance_for_interest_calculation`, `withhold_tax`, `tax_group_id`, `is_dormancy_tracking_active`, `days_to_inactive`, `days_to_dormancy`, `days_to_escheat`, `other_gl_mappings`) VALUES
|
||||
(1, 'Voluntary savings', 'VS', 'Save money', 100, 'USD', 2, 0, 9.500000, 1, 4, 1, 365, 1000.000000, 1.000000, 1, 2, NULL, NULL, 0, 0, NULL, 0.000000, 0.000000, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
/*!40000 ALTER TABLE `m_savings_product` ENABLE KEYS */;
|
||||
|
||||
|
||||
|
||||
+7
-1
@@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationC
|
||||
import org.apache.fineract.infrastructure.configuration.data.GlobalConfigurationPropertyData;
|
||||
import org.apache.fineract.infrastructure.configuration.service.ConfigurationReadPlatformService;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientRepository;
|
||||
@@ -39,6 +40,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -50,6 +52,8 @@ public class AccountNumberGeneratorTest {
|
||||
private ClientRepository clientRepo;
|
||||
private LoanRepository loanRepo;
|
||||
private SavingsAccountRepository savingsRepo;
|
||||
private GLAccountRepository glAccountRepo;
|
||||
private SavingsProductRepository savingsProductRepo;
|
||||
|
||||
private AccountNumberGenerator generator;
|
||||
|
||||
@@ -59,8 +63,10 @@ public class AccountNumberGeneratorTest {
|
||||
clientRepo = mock(ClientRepository.class);
|
||||
loanRepo = mock(LoanRepository.class);
|
||||
savingsRepo = mock(SavingsAccountRepository.class);
|
||||
glAccountRepo = mock(GLAccountRepository.class);
|
||||
savingsProductRepo = mock(SavingsProductRepository.class);
|
||||
|
||||
generator = new AccountNumberGenerator(configService, clientRepo, loanRepo, savingsRepo);
|
||||
generator = new AccountNumberGenerator(configService, clientRepo, loanRepo, savingsRepo, glAccountRepo, savingsProductRepo);
|
||||
|
||||
GlobalConfigurationPropertyData accountLengthConfig = mock(GlobalConfigurationPropertyData.class);
|
||||
when(accountLengthConfig.getValue()).thenReturn(Long.valueOf("9"));
|
||||
|
||||
Reference in New Issue
Block a user