FINERACT-893-fixed-deposit-rollover

This commit is contained in:
Manoj
2020-05-23 22:52:39 +05:30
parent dc7c1f8404
commit bd99138aad
23 changed files with 597 additions and 66 deletions
@@ -2400,6 +2400,175 @@ public class FixedDepositTest {
Assert.assertEquals("Verifying Pre-Closure maturity amount", expectedPrematureAmount, maturityAmount);
}
/***
* Test case for Fixed Deposit Account rollover with maturity
* instruction as re invest maturity amount(principal+interest)
*/
@Test
public void testFixedDepositAccountWithRolloverMaturityAmount() {
this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec);
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec);
/***
* Create GL Accounts for product account mapping
*/
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account liabilityAccount = this.accountHelper.createLiabilityAccount();
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US);
DateFormat currentDateFormat = new SimpleDateFormat("dd");
Calendar todaysDate = Calendar.getInstance();
todaysDate.add(Calendar.MONTH, -3);
final String VALID_FROM = dateFormat.format(todaysDate.getTime());
todaysDate.add(Calendar.YEAR, 10);
final String VALID_TO = dateFormat.format(todaysDate.getTime());
todaysDate = Calendar.getInstance();
todaysDate.add(Calendar.MONTH, -1);
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime());
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime());
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime());
final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime());
Integer currentDate = Integer.valueOf(currentDateFormat.format(todaysDate.getTime()));
Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE);
Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1;
todaysDate.add(Calendar.DATE, numberOfDaysLeft);
final String INTEREST_POSTED_DATE = dateFormat.format(todaysDate.getTime());
final String CLOSED_ON_DATE = dateFormat.format(Calendar.getInstance().getTime());
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
Assert.assertNotNull(clientId);
/***
* Create FD product with CashBased accounting enabled
*/
final String accountingRule = CASH_BASED;
Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule, assetAccount, liabilityAccount,
incomeAccount, expenseAccount);
Assert.assertNotNull(fixedDepositProductId);
/***
* Set maturityInstructionId as re-invest principal+interest
* */
final Integer maturityInstructionId = 300;
/***
* Apply for FD account with created product and verify status
*/
Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE,
WHOLE_TERM, maturityInstructionId);
Assert.assertNotNull(fixedDepositAccountId);
HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
this.responseSpec, fixedDepositAccountId.toString());
FixedDepositAccountStatusChecker.verifyFixedDepositIsPending(fixedDepositAccountStatusHashMap);
/***
* Approve the FD account and verify whether account is approved
*/
fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId, APPROVED_ON_DATE);
FixedDepositAccountStatusChecker.verifyFixedDepositIsApproved(fixedDepositAccountStatusHashMap);
/***
* Activate the FD Account and verify whether account is activated
*/
fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE);
FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap);
}
/***
* Test case for Fixed Deposit Account rollover with maturity
* instruction as re invest principal only
*/
@Test
public void testFixedDepositAccountWithRolloverPrincipal() {
this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec);
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec);
/***
* Create GL Accounts for product account mapping
*/
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account liabilityAccount = this.accountHelper.createLiabilityAccount();
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US);
DateFormat currentDateFormat = new SimpleDateFormat("dd");
Calendar todaysDate = Calendar.getInstance();
todaysDate.add(Calendar.MONTH, -3);
final String VALID_FROM = dateFormat.format(todaysDate.getTime());
todaysDate.add(Calendar.YEAR, 10);
final String VALID_TO = dateFormat.format(todaysDate.getTime());
todaysDate = Calendar.getInstance();
todaysDate.add(Calendar.MONTH, -1);
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime());
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime());
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime());
final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime());
Integer currentDate = Integer.valueOf(currentDateFormat.format(todaysDate.getTime()));
Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE);
Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1;
todaysDate.add(Calendar.DATE, numberOfDaysLeft);
final String INTEREST_POSTED_DATE = dateFormat.format(todaysDate.getTime());
final String CLOSED_ON_DATE = dateFormat.format(Calendar.getInstance().getTime());
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
Assert.assertNotNull(clientId);
/***
* Create FD product with CashBased accounting enabled
*/
final String accountingRule = CASH_BASED;
Integer fixedDepositProductId = createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule, assetAccount, liabilityAccount,
incomeAccount, expenseAccount);
Assert.assertNotNull(fixedDepositProductId);
/***
* Set maturityInstructionId as re-invest principal
* */
final Integer maturityInstructionId = 400;
/***
* Apply for FD account with created product and verify status
*/
Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(), SUBMITTED_ON_DATE,
WHOLE_TERM, maturityInstructionId);
Assert.assertNotNull(fixedDepositAccountId);
HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
this.responseSpec, fixedDepositAccountId.toString());
FixedDepositAccountStatusChecker.verifyFixedDepositIsPending(fixedDepositAccountStatusHashMap);
/***
* Approve the FD account and verify whether account is approved
*/
fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId, APPROVED_ON_DATE);
FixedDepositAccountStatusChecker.verifyFixedDepositIsApproved(fixedDepositAccountStatusHashMap);
/***
* Activate the FD Account and verify whether account is activated
*/
fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE);
FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap);
}
private Integer createFixedDepositProduct(final String validFrom, final String validTo, final String accountingRule,
Account... accounts) {
LOG.info("------------------------------CREATING NEW FIXED DEPOSIT PRODUCT ---------------------------------------");
@@ -2470,6 +2639,17 @@ public class FixedDepositTest {
.applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec);
}
private Integer applyForFixedDepositApplication(final String clientID, final String productID, final String submittedOnDate,
final String penalInterestType, final Integer maturityInstructionId) {
LOG.info("--------------------------------APPLYING FOR FIXED DEPOSIT ACCOUNT --------------------------------");
final String fixedDepositApplicationJSON = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec) //
.withSubmittedOnDate(submittedOnDate)
.withMaturityInstructionId(maturityInstructionId)
.build(clientID, productID, penalInterestType);
return this.fixedDepositAccountHelper
.applyFixedDepositApplication(fixedDepositApplicationJSON, this.requestSpec, this.responseSpec);
}
private Integer applyForFixedDepositApplication(final String clientID, final String productID, final String submittedOnDate,
final String penalInterestType, final String depositAmount, final String depositPeriod) {
LOG.info("--------------------------------APPLYING FOR FIXED DEPOSIT ACCOUNT --------------------------------");
@@ -96,6 +96,7 @@ public class FixedDepositAccountHelper {
private String submittedOnDate = "";
private String savingsId = null;
private boolean transferInterest = false;
private Integer maturityInstructionId;
public String build(final String clientId, final String productId, final String penalInterestType) {
final HashMap<String, Object> map = new HashMap<>();
@@ -127,6 +128,7 @@ public class FixedDepositAccountHelper {
map.put("submittedOnDate", this.submittedOnDate);
map.put("linkAccountId", savingsId);
map.put("transferInterestToSavings", transferInterest);
map.put("maturityInstructionId", maturityInstructionId);
String fixedDepositAccountJson = new Gson().toJson(map);
LOG.info("{}", fixedDepositAccountJson);
@@ -489,4 +491,9 @@ public class FixedDepositAccountHelper {
}
return this.newDepositAmount;
}
public FixedDepositAccountHelper withMaturityInstructionId(Integer maturityInstructionId){
this.maturityInstructionId = maturityInstructionId;
return this;
}
}
@@ -131,6 +131,10 @@ public final class JsonCommand {
return new JsonCommand(resourceId, parsedCommand);
}
public static JsonCommand fromJsonElement(final Long resourceId, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper) {
return new JsonCommand(resourceId, parsedCommand, fromApiJsonHelper);
}
public JsonCommand(final Long resourceId, final JsonElement parsedCommand) {
this.parsedCommand = parsedCommand;
this.resourceId = resourceId;
@@ -150,6 +154,25 @@ public final class JsonCommand {
this.organisationCreditBureauId=null;
}
public JsonCommand(final Long resourceId, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper) {
this.parsedCommand = parsedCommand;
this.resourceId = resourceId;
this.commandId = null;
this.jsonCommand = null;
this.fromApiJsonHelper = fromApiJsonHelper;
this.entityName = null;
this.subresourceId = null;
this.groupId = null;
this.clientId = null;
this.loanId = null;
this.savingsId = null;
this.transactionId = null;
this.url = null;
this.productId = null;
this.creditBureauId=null;
this.organisationCreditBureauId=null;
}
public Long getOrganisationCreditBureauId() {
return this.organisationCreditBureauId;
}
@@ -31,4 +31,6 @@ public interface AccountAssociationsReadPlatformService {
public PortfolioAccountData retriveSavingsLinkedAssociation(final Long savingsId);
public Collection<AccountAssociationsData> retriveLoanAssociations(Long loanId, Integer associationType);
PortfolioAccountData retriveSavingsAccount(Long savingsId);
}
@@ -191,4 +191,9 @@ public class AccountAssociationsReadPlatformServiceImpl implements AccountAssoci
}
}
@Override
public PortfolioAccountData retriveSavingsAccount(final Long savingsId){
String accountNo = jdbcTemplate.queryForObject("select account_no from m_savings_account where id = ?", String.class, savingsId);
return PortfolioAccountData.lookup(savingsId, accountNo);
}
}
@@ -32,7 +32,8 @@ public enum DepositAccountOnClosureType {
INVALID(0, "depositAccountClosureType.invalid"), //
WITHDRAW_DEPOSIT(100, "depositAccountClosureType.withdrawDeposit"), //
TRANSFER_TO_SAVINGS(200, "depositAccountClosureType.transferToSavings"), //
REINVEST(300, "depositAccountClosureType.reinvest"); //
REINVEST_PRINCIPAL_AND_INTEREST(300, "depositAccountClosureType.reinvestPrincipalAndInterest"),
REINVEST_PRINCIPAL_ONLY(400, "depositAccountClosureType.reinvestPrincipalOnly"); //
private final Integer value;
private final String code;
@@ -63,7 +64,10 @@ public enum DepositAccountOnClosureType {
accountOnClosureType = DepositAccountOnClosureType.TRANSFER_TO_SAVINGS;
break;
case 300:
accountOnClosureType = DepositAccountOnClosureType.REINVEST;
accountOnClosureType = DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST;
break;
case 400:
accountOnClosureType = DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY;
break;
}
return accountOnClosureType;
@@ -78,7 +82,16 @@ public enum DepositAccountOnClosureType {
}
public boolean isReinvest() {
return this.value.equals(DepositAccountOnClosureType.REINVEST.getValue());
return this.value.equals(DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST.getValue()) ||
this.value.equals(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getValue());
}
public boolean isReinvestPrincipal() {
return this.value.equals(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getValue());
}
public boolean isReinvestPrincipalAndInterest() {
return this.value.equals(DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST.getValue());
}
public boolean isInvalid() {
@@ -190,6 +190,8 @@ public class DepositsApiConstants {
public static final String transferDescriptionParamName = "transferDescription";
public static final String toSavingsAccountIdParamName = "toSavingsAccountId";
public static final String savingsAccounts = "savingsAccounts";
public static final String maturityInstructionIdParamName = "maturityInstructionId";
public static final String transferToSavingsIdParamName = "transferToSavingsId";
public static final String preMatureCloseOnDateParamName = "preMatureCloseOnDate";
@@ -298,7 +300,8 @@ public class DepositsApiConstants {
interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, chargesParamName,
chartsParamName, depositAmountParamName, depositPeriodParamName, depositPeriodFrequencyIdParamName,
savingsAccounts, expectedFirstDepositOnDateParamName, SavingsApiConstants.withHoldTaxParamName));
savingsAccounts, expectedFirstDepositOnDateParamName, SavingsApiConstants.withHoldTaxParamName,
maturityInstructionIdParamName, transferToSavingsIdParamName));
public static final Set<String> FIXED_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS = fixedDepositAccountRequestData();
public static final Set<String> FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS = fixedDepositAccountResponseData();
@@ -239,6 +239,7 @@ public class FixedDepositAccountsApiResource {
Collection<SavingsAccountTransactionData> transactions = null;
Collection<SavingsAccountChargeData> charges = null;
PortfolioAccountData linkedAccount = null;
PortfolioAccountData transferToSavingsAccount = null;
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
if (!associationParameters.isEmpty()) {
@@ -272,6 +273,10 @@ public class FixedDepositAccountsApiResource {
}
}
if(savingsAccount.getTransferToSavingsId() !=null){
transferToSavingsAccount = this.accountAssociationsReadPlatformService.retriveSavingsAccount(savingsAccount.getTransferToSavingsId());
}
FixedDepositAccountData templateData = null;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -281,7 +286,7 @@ public class FixedDepositAccountsApiResource {
staffInSelectedOfficeOnly);
}
return FixedDepositAccountData.associationsAndTemplate(savingsAccount, templateData, transactions, charges, linkedAccount);
return FixedDepositAccountData.associationsAndTemplate(savingsAccount, templateData, transactions, charges, linkedAccount, transferToSavingsAccount);
}
@PUT
@@ -29,6 +29,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isCalen
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isMandatoryDepositParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.linkedAccountParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.mandatoryRecommendedDepositAmountParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maxDepositTermTypeIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.minDepositTermParamName;
@@ -39,6 +40,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClos
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.recurringFrequencyParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.recurringFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferToSavingsIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName;
@@ -77,6 +79,7 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.savings.PreClosurePenalInterestOnType;
@@ -308,6 +311,19 @@ public class DepositAccountDataValidator {
} else {
baseDataValidator.reset().parameter(linkedAccountParamName).value(linkAccountId).ignoreIfNull().longGreaterThanZero();
}
if (this.fromApiJsonHelper.parameterExists(maturityInstructionIdParamName, element)) {
final Integer depositRolloverId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
maturityInstructionIdParamName, element);
baseDataValidator.reset().parameter(maturityInstructionIdParamName).value(depositRolloverId).notNull()
.isOneOfTheseValues(DepositAccountOnClosureType.integerValues());
if(depositRolloverId.equals(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getValue())){
final Long transferToSavingsId = this.fromApiJsonHelper.extractLongNamed(
transferToSavingsIdParamName, element);
baseDataValidator.reset().parameter(transferToSavingsIdParamName).value(transferToSavingsId).notNull().longGreaterThanZero();
}
}
}
private void validateDepositDetailsForUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
@@ -439,6 +455,19 @@ public class DepositAccountDataValidator {
}
}
if (this.fromApiJsonHelper.parameterExists(maturityInstructionIdParamName, element)) {
final Integer depositRolloverId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
maturityInstructionIdParamName, element);
baseDataValidator.reset().parameter(maturityInstructionIdParamName).value(depositRolloverId).notNull()
.isOneOfTheseValues(DepositAccountOnClosureType.integerValues());
if(depositRolloverId.equals(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getValue())){
final Long transferToSavingsId = this.fromApiJsonHelper.extractLongNamed(
transferToSavingsIdParamName, element);
baseDataValidator.reset().parameter(transferToSavingsIdParamName).value(transferToSavingsId).notNull().longGreaterThanZero();
}
}
}
private void validatePreClosureDetailForSubmit(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
@@ -53,12 +53,14 @@ public class FixedDepositAccountData extends DepositAccountData {
private Integer depositPeriod;
private EnumOptionData depositPeriodFrequency;
private BigDecimal activationCharge;
private Long transferToSavingsId;
// used for account close
private EnumOptionData onAccountClosure;
private final PortfolioAccountData linkedAccount;
private final Boolean transferInterestToSavings;
private final PortfolioAccountData transferToSavingsAccount;
private Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions;
private Collection<EnumOptionData> periodFrequencyTypeOptions;
@@ -67,6 +69,7 @@ public class FixedDepositAccountData extends DepositAccountData {
// for account close
private Collection<EnumOptionData> onAccountClosureOptions;
private Collection<PaymentTypeData> paymentTypeOptions;
private final Collection<EnumOptionData> maturityInstructionOptions;
//import fields
private transient Integer rowIndex;
@@ -114,6 +117,7 @@ public class FixedDepositAccountData extends DepositAccountData {
this.activationCharge = null;
this.onAccountClosure = null;
this.linkedAccount = null;
this.transferToSavingsAccount = null;
this.transferInterestToSavings = null;
this.preClosurePenalInterestOnTypeOptions = null;
this.periodFrequencyTypeOptions = null;
@@ -125,6 +129,7 @@ public class FixedDepositAccountData extends DepositAccountData {
this.locale= locale;
this.submittedOnDate = submittedOnDate;
this.depositPeriodFrequencyId = depositPeriodFrequencyId;
this.maturityInstructionOptions = null;
}
public Integer getRowIndex() {
@@ -136,11 +141,14 @@ public class FixedDepositAccountData extends DepositAccountData {
final Integer maxDepositTerm, final EnumOptionData minDepositTermType, final EnumOptionData maxDepositTermType,
final Integer inMultiplesOfDepositTerm, final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount,
final BigDecimal maturityAmount, final LocalDate maturityDate, final Integer depositPeriod,
final EnumOptionData depositPeriodFrequency, final EnumOptionData onAccountClosure, final Boolean transferInterestToSavings) {
final EnumOptionData depositPeriodFrequency, final EnumOptionData onAccountClosure, final Boolean transferInterestToSavings,
final Long transferToSavingsId) {
final PortfolioAccountData linkedAccount = null;
final PortfolioAccountData transferToSavingsAccount = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
final Collection<EnumOptionData> maturityInstructionOptions = null;
final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue());
final Collection<EnumOptionData> onAccountClosureOptions = null;
@@ -166,7 +174,7 @@ public class FixedDepositAccountData extends DepositAccountData {
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate,
depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions,
paymentTypeOptions, savingsAccountDatas, linkedAccount, transferInterestToSavings, depositAccountData.withHoldTax,
depositAccountData.taxGroup);
depositAccountData.taxGroup, maturityInstructionOptions, transferToSavingsId, transferToSavingsAccount);
}
public static FixedDepositAccountData withInterestChart(final FixedDepositAccountData account,
@@ -187,12 +195,13 @@ public class FixedDepositAccountData extends DepositAccountData {
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, account.periodFrequencyTypeOptions, account.depositType,
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts,
account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup);
account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup, account.maturityInstructionOptions,
account.transferToSavingsId, account.transferToSavingsAccount);
}
public static FixedDepositAccountData associationsAndTemplate(final FixedDepositAccountData account, FixedDepositAccountData template,
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges,
final PortfolioAccountData linkedAccount) {
final PortfolioAccountData linkedAccount, PortfolioAccountData transferToSavingsAccount ) {
if (template == null) {
template = account;
@@ -214,7 +223,8 @@ public class FixedDepositAccountData extends DepositAccountData {
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, template.periodFrequencyTypeOptions, account.depositType,
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, template.savingsAccounts,
linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup);
linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup, account.maturityInstructionOptions,
account.transferToSavingsId, transferToSavingsAccount);
}
public static FixedDepositAccountData withTemplateOptions(final FixedDepositAccountData account,
@@ -226,7 +236,8 @@ public class FixedDepositAccountData extends DepositAccountData {
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsAccountChargeData> charges,
final Collection<ChargeData> chargeOptions, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<SavingsAccountData> savingsAccounts) {
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<SavingsAccountData> savingsAccounts,
final Collection<EnumOptionData> maturityInstructionOptions) {
return new FixedDepositAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.depositProductId, account.depositProductName, account.fieldOfficerId,
@@ -243,7 +254,8 @@ public class FixedDepositAccountData extends DepositAccountData {
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, periodFrequencyTypeOptions, account.depositType,
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, savingsAccounts,
account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup);
account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup, maturityInstructionOptions,
account.transferToSavingsId, account.transferToSavingsAccount);
}
public static FixedDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -304,6 +316,7 @@ public class FixedDepositAccountData extends DepositAccountData {
final EnumOptionData depositPeriodFrequency = null;
final EnumOptionData onAccountClosure = null;
final PortfolioAccountData linkedAccount = null;
final PortfolioAccountData transferToSavingsAccount = null;
final Boolean transferInterestToSavings = null;
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
@@ -312,6 +325,8 @@ public class FixedDepositAccountData extends DepositAccountData {
final Collection<EnumOptionData> onAccountClosureOptions = null;
final Collection<PaymentTypeData> paymentTypeOptions = null;
final Collection<SavingsAccountData> savingsAccountDatas = null;
final Collection<EnumOptionData> maturityInstructionOptions = null;
final Long transferToSavingsId = null;
return new FixedDepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
@@ -324,7 +339,7 @@ public class FixedDepositAccountData extends DepositAccountData {
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions,
depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount,
transferInterestToSavings, withHoldTax, taxGroup);
transferInterestToSavings, withHoldTax, taxGroup, maturityInstructionOptions, transferToSavingsId, transferToSavingsAccount);
}
public static FixedDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount,
@@ -393,6 +408,9 @@ public class FixedDepositAccountData extends DepositAccountData {
final PortfolioAccountData linkedAccount = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
final Collection<EnumOptionData> maturityInstructionOptions = null;
final Long transferToSavingsId = null;
final PortfolioAccountData transferToSavingsAccount = null;
return new FixedDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
@@ -405,7 +423,7 @@ public class FixedDepositAccountData extends DepositAccountData {
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions,
depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount,
transferInterestToSavings, withHoldTax, taxGroup);
transferInterestToSavings, withHoldTax, taxGroup, maturityInstructionOptions, transferToSavingsId, transferToSavingsAccount);
}
public static FixedDepositAccountData withClosureTemplateDetails(final FixedDepositAccountData account,
@@ -428,38 +446,40 @@ public class FixedDepositAccountData extends DepositAccountData {
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
account.depositPeriod, account.depositPeriodFrequency, account.periodFrequencyTypeOptions, account.depositType,
account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, account.linkedAccount,
account.transferInterestToSavings, account.withHoldTax, account.taxGroup);
account.transferInterestToSavings, account.withHoldTax, account.taxGroup, account.maturityInstructionOptions,
account.transferToSavingsId, account.transferToSavingsAccount);
}
private FixedDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId,
final String groupName, final Long clientId, final String clientName, final Long productId, final String productName,
final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status,
final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate,
final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType,
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers,
final BigDecimal minBalanceForInterestCalculation, final SavingsAccountSummaryData summary,
final Collection<SavingsAccountTransactionData> transactions, final Collection<DepositProductData> productOptions,
final Collection<StaffData> fieldOfficerOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions,
final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate,
final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest,
final EnumOptionData preClosurePenalInterestOnType, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Integer minDepositTerm, final Integer maxDepositTerm, final EnumOptionData minDepositTermType,
final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, final BigDecimal maturityAmount,
final LocalDate maturityDate, final Integer depositPeriod, final EnumOptionData depositPeriodFrequency,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final EnumOptionData depositType,
final EnumOptionData onAccountClosure, final Collection<EnumOptionData> onAccountClosureOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas,
final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings, final boolean withHoldTax,
final TaxGroupData taxGroup) {
final String groupName, final Long clientId, final String clientName, final Long productId, final String productName,
final Long fieldofficerId, final String fieldofficerName, final SavingsAccountStatusEnumData status,
final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate,
final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType,
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers,
final BigDecimal minBalanceForInterestCalculation, final SavingsAccountSummaryData summary,
final Collection<SavingsAccountTransactionData> transactions, final Collection<DepositProductData> productOptions,
final Collection<StaffData> fieldOfficerOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions,
final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate,
final boolean preClosurePenalApplicable, final BigDecimal preClosurePenalInterest,
final EnumOptionData preClosurePenalInterestOnType, final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
final Integer minDepositTerm, final Integer maxDepositTerm, final EnumOptionData minDepositTermType,
final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal depositAmount, final BigDecimal maturityAmount,
final LocalDate maturityDate, final Integer depositPeriod, final EnumOptionData depositPeriodFrequency,
final Collection<EnumOptionData> periodFrequencyTypeOptions, final EnumOptionData depositType,
final EnumOptionData onAccountClosure, final Collection<EnumOptionData> onAccountClosureOptions,
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas,
final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings, final boolean withHoldTax,
final TaxGroupData taxGroup, final Collection<EnumOptionData> maturityInstructionOptions, final Long transferToSavingsId,
final PortfolioAccountData transferToSavingsAccount) {
super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId,
fieldofficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
@@ -485,6 +505,7 @@ public class FixedDepositAccountData extends DepositAccountData {
this.depositPeriodFrequency = depositPeriodFrequency;
this.onAccountClosure = onAccountClosure;
this.linkedAccount = linkedAccount;
this.transferToSavingsAccount = transferToSavingsAccount;
this.transferInterestToSavings = transferInterestToSavings;
// template
@@ -495,6 +516,8 @@ public class FixedDepositAccountData extends DepositAccountData {
this.onAccountClosureOptions = onAccountClosureOptions;
this.paymentTypeOptions = paymentTypeOptions;
this.savingsAccounts = savingsAccountDatas;
this.maturityInstructionOptions = maturityInstructionOptions;
this.transferToSavingsId = transferToSavingsId;
}
@Override
@@ -521,4 +544,8 @@ public class FixedDepositAccountData extends DepositAccountData {
public void setActivationCharge(BigDecimal activationCharge) {
this.activationCharge = activationCharge;
}
public Long getTransferToSavingsId() {
return transferToSavingsId;
}
}
@@ -32,7 +32,9 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.expecte
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isCalendarInheritedParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isMandatoryDepositParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.mandatoryRecommendedDepositAmountParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferToSavingsIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.externalIdParamName;
@@ -298,6 +300,10 @@ public class DepositAccountAssembler {
throw new UnsupportedParameterException(Arrays.asList(withHoldTaxParamName));
}
}
Integer depositRolloverId = null;
if(command.parameterExists(maturityInstructionIdParamName)){
depositRolloverId = command.integerValueOfParameterNamed(maturityInstructionIdParamName);
}
SavingsAccount account = null;
if (depositAccountType.isFixedDeposit()) {
@@ -375,10 +381,12 @@ public class DepositAccountAssembler {
final BigDecimal maturityAmount = null;// calculated and updated in
// account
final LocalDate maturityDate = null;// calculated and updated in account
final DepositAccountOnClosureType accountOnClosureType = null;
final Integer accountOnClosureTypeId = command.integerValueOfParameterNamed(maturityInstructionIdParamName);
final DepositAccountOnClosureType accountOnClosureType = accountOnClosureTypeId != null ? DepositAccountOnClosureType.fromInt(accountOnClosureTypeId) :null;
final Long transferToSavingsId = command.longValueOfParameterNamed(transferToSavingsIdParamName);
return DepositAccountTermAndPreClosure.createNew(updatedProductPreClosure, updatedProductTerm, account, depositAmount,
maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType,
trasferInterest);
trasferInterest, transferToSavingsId);
}
public DepositAccountRecurringDetail assembleAccountRecurringDetail(final JsonCommand command,
@@ -25,6 +25,7 @@ import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.transaction.annotation.Transactional;
public interface DepositAccountDomainService {
@@ -43,8 +44,14 @@ public interface DepositAccountDomainService {
Long handleFDAccountClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command,
LocalDate tenantsTodayDate, Map<String, Object> changes);
@Transactional
Long handleFDAccountMaturityClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user,
LocalDate tenantsTodayDate, DateTimeFormatter fmt,
LocalDate closedDate, Integer onAccountClosureId,
Long toSavingsId, String transferDescription, Map<String, Object> changes);
Long handleRDAccountClosure(RecurringDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command,
LocalDate tenantsTodayDate, Map<String, Object> changes);
LocalDate tenantsTodayDate, Map<String, Object> changes);
Long handleFDAccountPreMatureClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command,
LocalDate tenantsTodayDate, Map<String, Object> changes);
@@ -189,7 +189,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Transactional
@Override
public Long handleFDAccountClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) {
final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -261,6 +261,85 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
return savingsTransactionId;
}
@Transactional
@Override
public Long handleFDAccountMaturityClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final LocalDate tenantsTodayDate, final DateTimeFormatter fmt,
final LocalDate closedDate, final Integer onAccountClosureId,
final Long toSavingsId, final String transferDescription,
Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
boolean isRegularTransaction = false;
boolean isAccountTransfer = false;
final boolean isPreMatureClosure = false;
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
/***
* Update account transactionIds for post journal entries.
*/
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final MathContext mc = MathContext.DECIMAL64;
Long savingsTransactionId = null;
account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId);
if (onClosureType.isReinvest()) {
BigDecimal reInvestAmount;
if(onClosureType.isReinvestPrincipal())
reInvestAmount = account.getDepositAmount();
else
reInvestAmount = account.getAccountBalance();
FixedDepositAccount reinvestedDeposit = account.reInvest(reInvestAmount);
this.depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit);
reinvestedDeposit.updateMaturityDateAndAmountBeforeAccountActivation(mc, isPreMatureClosure,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
this.savingsAccountRepository.save(reinvestedDeposit);
autoGenerateAccountNumber(reinvestedDeposit);
final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, reInvestAmount,
paymentDetail, false, isRegularTransaction);
savingsTransactionId = withdrawal.getId();
if (onClosureType.isReinvestPrincipalAndInterest()) {
account.updateClosedStatus();
account.updateOnAccountClosureStatus(onClosureType);
}
changes.put("reinvestedDepositId", reinvestedDeposit.getId());
reinvestedDeposit.approveAndActivateApplication(closedDate.toDate(), user);
this.savingsAccountRepository.save(reinvestedDeposit);
} else if (onClosureType.isTransferToSavings()) {
final SavingsAccount toSavingsAccount = this.depositAccountAssembler.assembleFrom(toSavingsId,
DepositAccountType.SAVINGS_DEPOSIT);
final boolean isExceptionForBalanceCheck = false;
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(closedDate, account.getAccountBalance(),
PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, null, null, transferDescription, null, fmt, null, null,
null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account,
isAccountTransfer, isExceptionForBalanceCheck);
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
updateAlreadyPostedTransactions(existingTransactionIds, account);
account.updateClosedStatus();
account.updateOnAccountClosureStatus(onClosureType);
} else {
final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(),
paymentDetail, false, isRegularTransaction);
savingsTransactionId = withdrawal.getId();
}
//if(!processMaturityInstructionOnly)
// account.close(user, command, tenantsTodayDate, changes);
this.savingsAccountRepository.save(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
return savingsTransactionId;
}
@Transactional
@Override
public Long handleRDAccountClosure(final RecurringDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
@@ -290,7 +369,12 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName);
final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId);
if (onClosureType.isReinvest()) {
RecurringDepositAccount reinvestedDeposit = account.reInvest(transactionAmount);
BigDecimal reInvestAmount;
if(onClosureType.isReinvestPrincipal())
reInvestAmount = account.getDepositAmount();
else
reInvestAmount = account.getAccountBalance();
RecurringDepositAccount reinvestedDeposit = account.reInvest(reInvestAmount);
depositAccountAssembler.assignSavingAccountHelpers(reinvestedDeposit);
this.savingsAccountRepository.save(reinvestedDeposit);
final CalendarInstance calendarInstance = getCalendarInstance(account, reinvestedDeposit);
@@ -24,7 +24,9 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.deposit
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.depositPeriodParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.expectedFirstDepositOnDateParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.localeParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.maturityInstructionIdParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferInterestToSavingsParamName;
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transferToSavingsIdParamName;
import java.math.BigDecimal;
import java.util.Date;
@@ -90,6 +92,9 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
@Column(name = "transfer_interest_to_linked_account", nullable = false)
private boolean transferInterestToLinkedAccount;
@Column(name = "transfer_to_savings_account_id")
private Long transferToSavingsAccountId;
protected DepositAccountTermAndPreClosure() {
super();
}
@@ -97,16 +102,16 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
public static DepositAccountTermAndPreClosure createNew(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, final LocalDate expectedFirstDepositOnDate,
final DepositAccountOnClosureType accountOnClosureType, Boolean trasferInterest) {
final DepositAccountOnClosureType accountOnClosureType, Boolean trasferInterest, Long transferToSavingsId) {
return new DepositAccountTermAndPreClosure(preClosureDetail, depositTermDetail, account, depositAmount, maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType, trasferInterest);
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType, trasferInterest, transferToSavingsId);
}
private DepositAccountTermAndPreClosure(DepositPreClosureDetail preClosureDetail, DepositTermDetail depositTermDetail,
SavingsAccount account, BigDecimal depositAmount, BigDecimal maturityAmount, final LocalDate maturityDate,
Integer depositPeriod, final SavingsPeriodFrequencyType depositPeriodFrequency, final LocalDate expectedFirstDepositOnDate,
final DepositAccountOnClosureType accountOnClosureType, Boolean transferInterest) {
final DepositAccountOnClosureType accountOnClosureType, Boolean transferInterest, Long transferToSavingsId) {
this.depositAmount = depositAmount;
this.maturityAmount = maturityAmount;
this.maturityDate = (maturityDate == null) ? null : maturityDate.toDate();
@@ -118,6 +123,7 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
this.expectedFirstDepositOnDate = expectedFirstDepositOnDate == null ? null : expectedFirstDepositOnDate.toDate();
this.onAccountClosureType = (accountOnClosureType == null) ? null : accountOnClosureType.getValue();
this.transferInterestToLinkedAccount = transferInterest;
this.transferToSavingsAccountId = transferToSavingsId;
}
public Map<String, Object> update(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
@@ -158,6 +164,20 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
this.transferInterestToLinkedAccount = newValue;
}
if (command.isChangeInIntegerParameterNamed(maturityInstructionIdParamName, this.onAccountClosureType) || command.integerValueOfParameterNamed(
maturityInstructionIdParamName) == null) {
final Integer newValue = command.integerValueOfParameterNamed(maturityInstructionIdParamName);
actualChanges.put(maturityInstructionIdParamName, newValue);
this.onAccountClosureType = newValue != null ? DepositAccountOnClosureType.fromInt(newValue).getValue() : null;
}
if (command.isChangeInLongParameterNamed(transferToSavingsIdParamName, this.transferToSavingsAccountId) || command.integerValueOfParameterNamed(
transferToSavingsIdParamName) == null) {
final Long newValue = command.longValueOfParameterNamed(transferToSavingsIdParamName);
actualChanges.put(transferToSavingsIdParamName, newValue);
this.transferToSavingsAccountId = newValue ;
}
if (this.preClosureDetail != null) {
actualChanges.putAll(this.preClosureDetail.update(command, baseDataValidator));
}
@@ -291,9 +311,10 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
final Boolean transferInterestToLinkedAccount = false;
final DepositAccountOnClosureType accountOnClosureType = null;
final Long transferToSavingsId = null;
return DepositAccountTermAndPreClosure.createNew(preClosureDetail, depositTermDetail, account, actualDepositAmount, maturityAmount,
maturityDate, depositPeriod, depositPeriodFrequency, expectedFirstDepositOnDate, accountOnClosureType,
transferInterestToLinkedAccount);
transferInterestToLinkedAccount, transferToSavingsId);
}
public void updateExpectedFirstDepositDate(final LocalDate expectedFirstDepositOnDate) {
@@ -311,4 +332,12 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
}
return isAfterExpectedFirstDepositDate;
}
public Integer getOnAccountClosureType() {
return onAccountClosureType;
}
public Long getTransferToSavingsAccountId() {
return transferToSavingsAccountId;
}
}
@@ -259,6 +259,8 @@ public class FixedDepositAccount extends SavingsAccount {
}
}
public LocalDate calculateMaturityDate() {
final LocalDate startDate = accountSubmittedOrActivationDate();
@@ -489,6 +491,14 @@ public class FixedDepositAccount extends SavingsAccount {
// this.savingsAccountTransactionSummaryWrapper, this.transactions);
}
public void updateClosedStatus(){
this.status = SavingsAccountStatusType.CLOSED.getValue();
}
public void updateOnAccountClosureStatus(DepositAccountOnClosureType onClosureType){
this.accountTermAndPreClosure.updateOnAccountClosureStatus(onClosureType);
}
public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final LocalDate interestPostingUpToDate = maturityDate();
final MathContext mc = MathContext.DECIMAL64;
@@ -756,6 +766,14 @@ public class FixedDepositAccount extends SavingsAccount {
return this.accountTermAndPreClosure.isTransferToSavingsOnClosure();
}
public Integer getOnAccountClosureId(){
return this.accountTermAndPreClosure.getOnAccountClosureType();
}
public Long getTransferToSavingsAccountId() {
return this.accountTermAndPreClosure.getTransferToSavingsAccountId();
}
public FixedDepositAccount reInvest(BigDecimal depositAmount) {
final DepositAccountTermAndPreClosure newAccountTermAndPreClosure = this.accountTermAndPreClosure.copy(depositAmount);
@@ -830,4 +848,16 @@ public class FixedDepositAccount extends SavingsAccount {
super.loadLazyCollections();
this.chart.getId() ;
}
public BigDecimal getDepositAmount() {
return this.accountTermAndPreClosure.depositAmount();
}
@Override
public BigDecimal getAccountBalance() {
return this.summary.getAccountBalance(this.currency).getAmount();
}
public boolean isMatured(){
return SavingsAccountStatusType.MATURED.getValue().equals(this.status);
}
}
@@ -1232,4 +1232,8 @@ public class RecurringDepositAccount extends SavingsAccount {
super.loadLazyCollections();
this.chart.getId() ;
}
public BigDecimal getDepositAmount() {
return this.accountTermAndPreClosure.depositAmount();
}
}
@@ -1433,7 +1433,6 @@ public class SavingsAccount extends AbstractPersistableCustom {
baseDataValidator.reset().parameter(withHoldTaxParamName).failWithCode("not.supported.for.this.account");
}
}
validateLockinDetails(baseDataValidator);
esnureOverdraftLimitsSetForOverdraftAccounts();
}
@@ -378,6 +378,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
final boolean feeChargesOnly = false;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly);
final Collection<EnumOptionData> maturityInstructionOptions = this.depositsDropdownReadPlatformService.maturityInstructionOptions();
Collection<StaffData> fieldOfficerOptions = null;
if (officeId != null) {
@@ -405,7 +407,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
}
// retrieve chart Slabs
final InterestRateChartData productChartData = this.productChartReadPlatformService.retrieveActiveChartWithTemplate(productId);
final InterestRateChartData productChartData = this.productChartReadPlatformService.retrieveActiveChartWithTemplate(productId);
final DepositAccountInterestRateChartData accountChart = DepositAccountInterestRateChartData.from(productChartData);
if (depositAccountType.isFixedDeposit()) {
@@ -414,7 +416,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, transactions, charges, chargeOptions, preClosurePenalInterestOnTypeOptions,
periodFrequencyTypeOptions, savingsAccountDatas);
periodFrequencyTypeOptions, savingsAccountDatas, maturityInstructionOptions);
template = FixedDepositAccountData.withInterestChart((FixedDepositAccountData) template, accountChart);
} else if (depositAccountType.isRecurringDeposit()) {
@@ -450,6 +452,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsAccountChargeData> charges = null;
final Collection<EnumOptionData> maturityInstructionOptions = null;
final boolean feeChargesOnly = true;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly);
@@ -462,7 +465,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, transactions, charges, chargeOptions, preClosurePenalInterestOnTypeOptions,
periodFrequencyTypeOptions, savingsAccountDatas);
periodFrequencyTypeOptions, savingsAccountDatas, maturityInstructionOptions);
} else if (depositAccountType.isRecurringDeposit()) {
template = RecurringDepositAccountData.withClientTemplate(clientId, clientName, groupId, groupName);
@@ -778,7 +781,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
sqlBuilder.append("datp.deposit_period as depositPeriod, ");
sqlBuilder.append("datp.deposit_period_frequency_enum as depositPeriodFrequencyTypeId, ");
sqlBuilder.append("datp.on_account_closure_enum as onAccountClosureId, ");
sqlBuilder.append("datp.transfer_interest_to_linked_account as transferInterestToSavings ");
sqlBuilder.append("datp.transfer_interest_to_linked_account as transferInterestToSavings, ");
sqlBuilder.append("datp.transfer_to_savings_account_id as transferToSavingsId ");
sqlBuilder.append(super.selectTablesSql());
@@ -826,10 +830,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
.depositAccountOnClosureType(onAccountClosureId);
final Boolean transferInterestToSavings = rs.getBoolean("transferInterestToSavings");
final Long transferToSavingsId = JdbcSupport.getLong(rs, "transferToSavingsId");
return FixedDepositAccountData.instance(depositAccountData, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings);
depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings, transferToSavingsId);
}
}
@@ -1266,7 +1273,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
return FixedDepositAccountData.instance(depositAccountData, preClosurePenalApplicable, preClosurePenalInterest,
preClosurePenalInterestOnType, minDepositTerm, maxDepositTerm, minDepositTermType, maxDepositTermType,
inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate, depositPeriod,
depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings);
depositPeriodFrequencyType, onAccountClosureType, transferInterestToSavings, null);
}
}
@@ -28,6 +28,7 @@ import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -44,6 +45,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
@@ -145,6 +147,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
private final HolidayRepositoryWrapper holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -164,7 +167,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final DepositAccountReadPlatformService depositAccountReadPlatformService,
final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService,
final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository) {
final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository,
final FromJsonHelper fromApiJsonHelper) {
this.context = context;
this.savingAccountRepositoryWrapper = savingAccountRepositoryWrapper;
@@ -188,6 +192,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.calendarInstanceRepository = calendarInstanceRepository;
this.configurationDomainService = configurationDomainService;
this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository;
this.fromApiJsonHelper = fromApiJsonHelper;
}
@Transactional
@@ -798,14 +803,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.depositAccountTransactionDataValidator.validateClosing(command, DepositAccountType.FIXED_DEPOSIT, isPreMatureClose);
final Map<String, Object> changes = new LinkedHashMap<>();
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final PaymentDetail
paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final FixedDepositAccount account = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId,
DepositAccountType.FIXED_DEPOSIT);
checkClientOrGroupActive(account);
this.depositAccountDomainService.handleFDAccountClosure(account, paymentDetail, user, command, DateUtils.getLocalDateOfTenant(),
changes);
this.depositAccountDomainService.handleFDAccountClosure(account, paymentDetail, user, command, DateUtils.getLocalDateOfTenant(), changes);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
@@ -1352,6 +1357,32 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
if (depositAccountType.isFixedDeposit()) {
((FixedDepositAccount) account).updateMaturityStatus(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
FixedDepositAccount fdAccount = ((FixedDepositAccount) account);
//handle maturity instructions
if(fdAccount.isMatured() && (fdAccount.isReinvestOnClosure() || fdAccount.isTransferToSavingsOnClosure())){
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd");
Map<String, Object> changes = new HashMap<>();
AppUser user = context.authenticatedUser();
Long toSavingsId = fdAccount.getTransferToSavingsAccountId();
this.depositAccountDomainService.handleFDAccountMaturityClosure(fdAccount, null, user,
fdAccount.maturityDate(), fmt, fdAccount.maturityDate(),
fdAccount.getOnAccountClosureId(), toSavingsId, "Apply maturity instructions", changes);
if(changes.get("reinvestedDepositId") != null) {
Long reinvestedDepositId = (Long) changes.get("reinvestedDepositId");
Money amountForDeposit = account.activateWithBalance();
final FixedDepositAccount reinvestAccount = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(reinvestedDepositId,
DepositAccountType.FIXED_DEPOSIT);
Money activationChargeAmount = getActivationCharge(reinvestAccount);
if(activationChargeAmount.isGreaterThanZero()){
payActivationCharge(reinvestAccount, user);
amountForDeposit = amountForDeposit.plus(activationChargeAmount);
}
this.depositAccountDomainService.handleFDDeposit(reinvestAccount, fmt, fdAccount.maturityDate(),
amountForDeposit.getAmount(), null);
}
}
} else if (depositAccountType.isRecurringDeposit()) {
((RecurringDepositAccount) account).updateMaturityStatus(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
@@ -27,6 +27,8 @@ public interface DepositsDropdownReadPlatformService {
Collection<EnumOptionData> retrievePreClosurePenalInterestOnTypeOptions();
Collection<EnumOptionData> maturityInstructionOptions();
/*
* Collection<EnumOptionData> retrieveDepositTermTypeOptions();
*
@@ -18,8 +18,10 @@
*/
package org.apache.fineract.portfolio.savings.service;
import java.util.Arrays;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.savings.DepositAccountOnClosureType;
import org.apache.fineract.portfolio.savings.PreClosurePenalInterestOnType;
import org.springframework.stereotype.Service;
@@ -48,4 +50,14 @@ public class DepositsDropdownReadPlatformServiceImpl implements DepositsDropdown
* .recurringDepositFrequencyType(SavingsPeriodFrequencyType.values()); }
*/
@Override
public Collection<EnumOptionData> maturityInstructionOptions() {
return Arrays.asList(
SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType.WITHDRAW_DEPOSIT),
SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS),
SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST),
SavingsEnumerations.depositAccountOnClosureType(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY));
}
}
@@ -795,10 +795,14 @@ public class SavingsEnumerations {
optionData = new EnumOptionData(DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getValue().longValue(),
DepositAccountOnClosureType.TRANSFER_TO_SAVINGS.getCode(), "Transfer to Savings");
break;
case REINVEST:
optionData = new EnumOptionData(DepositAccountOnClosureType.REINVEST.getValue().longValue(),
DepositAccountOnClosureType.REINVEST.getCode(), "Re-Invest");
case REINVEST_PRINCIPAL_AND_INTEREST:
optionData = new EnumOptionData(DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST.getValue().longValue(),
DepositAccountOnClosureType.REINVEST_PRINCIPAL_AND_INTEREST.getCode(), "Re-Invest Maturity Amount");
break;
case REINVEST_PRINCIPAL_ONLY:
optionData = new EnumOptionData(DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getValue().longValue(),
DepositAccountOnClosureType.REINVEST_PRINCIPAL_ONLY.getCode(), "Re-Invest Principal Only");
break;
}
return optionData;
}
@@ -835,5 +839,4 @@ public class SavingsEnumerations {
}
return optionData;
}
}
@@ -0,0 +1,21 @@
--
-- 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.
--
alter table m_deposit_account_term_and_preclosure add column transfer_to_savings_account_id bigint(20) DEFAULT NULL,
add CONSTRAINT FOREIGN KEY (`transfer_to_savings_account_id`) REFERENCES `m_savings_account` (`id`);