Dormant Savings Feature
This commit is contained in:
+4
-3
@@ -151,7 +151,7 @@ public class AccountingConstants {
|
||||
/*** Accounting placeholders for cash based accounting for savings products ***/
|
||||
public static enum CASH_ACCOUNTS_FOR_SAVINGS {
|
||||
SAVINGS_REFERENCE(1), SAVINGS_CONTROL(2), INTEREST_ON_SAVINGS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), TRANSFERS_SUSPENSE(
|
||||
10), OVERDRAFT_PORTFOLIO_CONTROL(11), INCOME_FROM_INTEREST(12), LOSSES_WRITTEN_OFF(13);
|
||||
10), OVERDRAFT_PORTFOLIO_CONTROL(11), INCOME_FROM_INTEREST(12), LOSSES_WRITTEN_OFF(13), ESCHEAT_LIABILITY(14);
|
||||
|
||||
private final Integer value;
|
||||
|
||||
@@ -192,7 +192,7 @@ public class AccountingConstants {
|
||||
"paymentTypeId"), FUND_SOURCE("fundSourceAccountId"), TRANSFERS_SUSPENSE("transfersInSuspenseAccountId"), FEE_INCOME_ACCOUNT_MAPPING(
|
||||
"feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING("penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID(
|
||||
"incomeAccountId"), OVERDRAFT_PORTFOLIO_CONTROL("overdraftPortfolioControlId"), INCOME_FROM_INTEREST("incomeFromInterestId"), LOSSES_WRITTEN_OFF(
|
||||
"writeOffAccountId");
|
||||
"writeOffAccountId"), ESCHEAT_LIABILITY("escheatLiabilityId");
|
||||
|
||||
private final String value;
|
||||
|
||||
@@ -215,7 +215,8 @@ public class AccountingConstants {
|
||||
"incomeFromPenaltyAccount"), INTEREST_ON_SAVINGS("interestOnSavingsAccount"), PAYMENT_TYPE("paymentType"), FUND_SOURCE(
|
||||
"fundSourceAccount"), TRANSFERS_SUSPENSE("transfersInSuspenseAccount"), PENALTY_INCOME_ACCOUNT_MAPPING(
|
||||
"penaltyToIncomeAccountMappings"), CHARGE_ID("charge"), INCOME_ACCOUNT_ID("incomeAccount"), OVERDRAFT_PORTFOLIO_CONTROL(
|
||||
"overdraftPortfolioControl"), INCOME_FROM_INTEREST("incomeFromInterest"), LOSSES_WRITTEN_OFF("writeOffAccount");
|
||||
"overdraftPortfolioControl"), INCOME_FROM_INTEREST("incomeFromInterest"), LOSSES_WRITTEN_OFF("writeOffAccount"),
|
||||
ESCHEAT_LIABILITY("escheatLiabilityAccount");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
||||
+5
@@ -143,6 +143,11 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isEscheat()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
|
||||
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL.getValue(), CASH_ACCOUNTS_FOR_SAVINGS.ESCHEAT_LIABILITY.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
/**
|
||||
* Handle Interest Applications and reversals of Interest
|
||||
* Applications
|
||||
|
||||
+9
@@ -20,6 +20,7 @@ package org.apache.fineract.accounting.producttoaccountmapping.serialization;
|
||||
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -206,6 +207,14 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer {
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
|
||||
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue())
|
||||
.value(incomeFromPenaltyId).notNull().integerGreaterThanZero();
|
||||
|
||||
final Boolean isDormancyTrackingActive = this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element);
|
||||
if(null != isDormancyTrackingActive && isDormancyTrackingActive){
|
||||
final Long escheatLiabilityId = this.fromApiJsonHelper.extractLongNamed(
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue(), element);
|
||||
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue())
|
||||
.value(escheatLiabilityId).notNull().integerGreaterThanZero();
|
||||
}
|
||||
|
||||
if (!accountType.equals(DepositAccountType.RECURRING_DEPOSIT) && !accountType.equals(DepositAccountType.FIXED_DEPOSIT)) {
|
||||
final Long overdraftAccount = this.fromApiJsonHelper.extractLongNamed(
|
||||
|
||||
+2
@@ -228,6 +228,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
||||
accountMappingDetails.put(SAVINGS_PRODUCT_ACCOUNTING_DATA_PARAMS.LOSSES_WRITTEN_OFF.getValue(), gLAccountData);
|
||||
} else if (glAccountForSavings.equals(CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_INTEREST)) {
|
||||
accountMappingDetails.put(SAVINGS_PRODUCT_ACCOUNTING_DATA_PARAMS.INCOME_FROM_INTEREST.getValue(), gLAccountData);
|
||||
} else if (glAccountForSavings.equals(CASH_ACCOUNTS_FOR_SAVINGS.ESCHEAT_LIABILITY)) {
|
||||
accountMappingDetails.put(SAVINGS_PRODUCT_ACCOUNTING_DATA_PARAMS.ESCHEAT_LIABILITY.getValue(), gLAccountData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.accounting.producttoaccountmapping.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
@@ -224,6 +225,13 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
||||
this.savingsProductToGLAccountMappingHelper.saveSavingsToLiabilityAccountMapping(element,
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), savingProductId,
|
||||
CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE.getValue());
|
||||
|
||||
final Boolean isDormancyTrackingActive = this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element);
|
||||
if(null != isDormancyTrackingActive && isDormancyTrackingActive){
|
||||
this.savingsProductToGLAccountMappingHelper.saveSavingsToLiabilityAccountMapping(element,
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue(), savingProductId,
|
||||
CASH_ACCOUNTS_FOR_SAVINGS.ESCHEAT_LIABILITY.getValue());
|
||||
}
|
||||
|
||||
// advanced accounting mappings
|
||||
this.savingsProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, savingProductId, null);
|
||||
|
||||
+3
@@ -222,6 +222,9 @@ public class SavingsProductToGLAccountMappingHelper extends ProductToGLAccountMa
|
||||
mergeSavingsToLiabilityAccountMappingChanges(element, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
|
||||
savingsProductId, CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE.getValue(),
|
||||
CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE.toString(), changes);
|
||||
mergeSavingsToLiabilityAccountMappingChanges(element, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue(),
|
||||
savingsProductId, CASH_ACCOUNTS_FOR_SAVINGS.ESCHEAT_LIABILITY.getValue(),
|
||||
CASH_ACCOUNTS_FOR_SAVINGS.ESCHEAT_LIABILITY.toString(), changes);
|
||||
break;
|
||||
case ACCRUAL_PERIODIC:
|
||||
break;
|
||||
|
||||
+2
-1
@@ -39,7 +39,8 @@ public enum JobName {
|
||||
RECALCULATE_INTEREST_FOR_LOAN("Recalculate Interest For Loans"), //
|
||||
GENERATE_RD_SCEHDULE("Generate Mandatory Savings Schedule"), //
|
||||
GENERATE_LOANLOSS_PROVISIONING("Generate Loan Loss Provisioning"), //
|
||||
POST_DIVIDENTS_FOR_SHARES("Post Dividends For Shares");
|
||||
POST_DIVIDENTS_FOR_SHARES("Post Dividends For Shares"),
|
||||
UPDATE_SAVINGS_DORMANT_ACCOUNTS("Update Savings Dormant Accounts");
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
+8
-1
@@ -24,6 +24,8 @@ import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimelineData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Immutable data object for savings accounts.
|
||||
@@ -43,13 +45,16 @@ public class SavingsAccountSummaryData {
|
||||
//differentiate Individual, JLG or Group account
|
||||
private final EnumOptionData accountType;
|
||||
private final SavingsAccountApplicationTimelineData timeline;
|
||||
private final SavingsAccountSubStatusEnumData subStatus;
|
||||
private final LocalDate lastActiveTransactionDate;
|
||||
|
||||
//differentiate deposit accounts Savings, FD and RD accounts
|
||||
private final EnumOptionData depositType;
|
||||
|
||||
public SavingsAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId,
|
||||
final String productName, final String shortProductName, final SavingsAccountStatusEnumData status, final CurrencyData currency,
|
||||
final BigDecimal accountBalance, final EnumOptionData accountType, final SavingsAccountApplicationTimelineData timeline, final EnumOptionData depositType) {
|
||||
final BigDecimal accountBalance, final EnumOptionData accountType, final SavingsAccountApplicationTimelineData timeline, final EnumOptionData depositType,
|
||||
final SavingsAccountSubStatusEnumData subStatus, final LocalDate lastActiveTransactionDate) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
this.externalId = externalId;
|
||||
@@ -62,5 +67,7 @@ public class SavingsAccountSummaryData {
|
||||
this.accountType = accountType;
|
||||
this.timeline = timeline;
|
||||
this.depositType = depositType;
|
||||
this.subStatus = subStatus;
|
||||
this.lastActiveTransactionDate = lastActiveTransactionDate;
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -39,6 +39,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimelineData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountApplicationTimelineData;
|
||||
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountStatusEnumData;
|
||||
@@ -258,6 +259,13 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
accountsSummary.append("avbu.username as activatedByUsername,");
|
||||
accountsSummary.append("avbu.firstname as activatedByFirstname, avbu.lastname as activatedByLastname,");
|
||||
|
||||
accountsSummary.append("sa.sub_status_enum as subStatusEnum, ");
|
||||
accountsSummary.append("(select IFNULL(max(sat.transaction_date),sa.activatedon_date) ");
|
||||
accountsSummary.append("from m_savings_account_transaction as sat ");
|
||||
accountsSummary.append("where sat.is_reversed = 0 ");
|
||||
accountsSummary.append("and sat.transaction_type_enum in (1,2) ");
|
||||
accountsSummary.append("and sat.savings_account_id = sa.id) as lastActiveTransactionDate, ");
|
||||
|
||||
accountsSummary.append("sa.closedon_date as closedOnDate,");
|
||||
accountsSummary.append("cbu.username as closedByUsername,");
|
||||
accountsSummary.append("cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,");
|
||||
@@ -340,6 +348,10 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
final String closedByUsername = rs.getString("closedByUsername");
|
||||
final String closedByFirstname = rs.getString("closedByFirstname");
|
||||
final String closedByLastname = rs.getString("closedByLastname");
|
||||
final Integer subStatusEnum = JdbcSupport.getInteger(rs, "subStatusEnum");
|
||||
final SavingsAccountSubStatusEnumData subStatus = SavingsEnumerations.subStatus(subStatusEnum);
|
||||
|
||||
final LocalDate lastActiveTransactionDate = JdbcSupport.getLocalDate(rs, "lastActiveTransactionDate");
|
||||
|
||||
final SavingsAccountApplicationTimelineData timeline = new SavingsAccountApplicationTimelineData(submittedOnDate,
|
||||
submittedByUsername, submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername,
|
||||
@@ -349,7 +361,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
closedByLastname);
|
||||
|
||||
return new SavingsAccountSummaryData(id, accountNo, externalId, productId, productName, shortProductName, status, currency, accountBalance,
|
||||
accountTypeData, timeline, depositTypeData);
|
||||
accountTypeData, timeline, depositTypeData, subStatus, lastActiveTransactionDate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -184,10 +184,10 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.calculation.type.for.savings");
|
||||
}
|
||||
|
||||
if (!ChargeTimeType.fromInt(getChargeTimeType()).isWithdrawalFee()
|
||||
if (!(ChargeTimeType.fromInt(getChargeTimeType()).isWithdrawalFee() || ChargeTimeType.fromInt(getChargeTimeType()).isSavingsNoActivityFee())
|
||||
&& ChargeCalculationType.fromInt(getChargeCalculation()).isPercentageOfAmount()) {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation)
|
||||
.failWithCodeNoParameterAddedToErrorCode("savings.charge.calculation.type.percentage.allowed.only.for.withdrawal");
|
||||
.failWithCodeNoParameterAddedToErrorCode("savings.charge.calculation.type.percentage.allowed.only.for.withdrawal.or.NoActivity");
|
||||
}
|
||||
|
||||
} else if (isLoanCharge()) {
|
||||
@@ -401,10 +401,10 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.calculation.type.for.savings");
|
||||
}
|
||||
|
||||
if (!ChargeTimeType.fromInt(getChargeTimeType()).isWithdrawalFee()
|
||||
if (!(ChargeTimeType.fromInt(getChargeTimeType()).isWithdrawalFee() || ChargeTimeType.fromInt(getChargeTimeType()).isSavingsNoActivityFee())
|
||||
&& ChargeCalculationType.fromInt(getChargeCalculation()).isPercentageOfAmount()) {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation)
|
||||
.failWithCodeNoParameterAddedToErrorCode("charge.calculation.type.percentage.allowed.only.for.withdrawal");
|
||||
.failWithCodeNoParameterAddedToErrorCode("charge.calculation.type.percentage.allowed.only.for.withdrawal.or.noactivity");
|
||||
}
|
||||
} else if (isClientCharge()) {
|
||||
if (!isAllowedClientChargeCalculationType()) {
|
||||
|
||||
+13
-3
@@ -35,7 +35,9 @@ public enum ChargeTimeType {
|
||||
// loan
|
||||
SHAREACCOUNT_ACTIVATION(13, "chargeTimeType.activation"), // only for loan
|
||||
SHARE_PURCHASE(14, "chargeTimeType.sharespurchase"),
|
||||
SHARE_REDEEM(15, "chargeTimeType.sharesredeem");
|
||||
SHARE_REDEEM(15, "chargeTimeType.sharesredeem"),
|
||||
|
||||
SAVINGS_NOACTIVITY_FEE(16,"chargeTimeType.savingsNoActivityFee");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -67,7 +69,8 @@ public enum ChargeTimeType {
|
||||
public static Object[] validSavingsValues() {
|
||||
return new Integer[] { ChargeTimeType.SPECIFIED_DUE_DATE.getValue(), ChargeTimeType.SAVINGS_ACTIVATION.getValue(),
|
||||
ChargeTimeType.SAVINGS_CLOSURE.getValue(), ChargeTimeType.WITHDRAWAL_FEE.getValue(), ChargeTimeType.ANNUAL_FEE.getValue(),
|
||||
ChargeTimeType.MONTHLY_FEE.getValue(), ChargeTimeType.OVERDRAFT_FEE.getValue(), ChargeTimeType.WEEKLY_FEE.getValue() };
|
||||
ChargeTimeType.MONTHLY_FEE.getValue(), ChargeTimeType.OVERDRAFT_FEE.getValue(), ChargeTimeType.WEEKLY_FEE.getValue(),
|
||||
ChargeTimeType.SAVINGS_NOACTIVITY_FEE.getValue()};
|
||||
}
|
||||
|
||||
public static Object[] validClientValues() {
|
||||
@@ -127,6 +130,9 @@ public enum ChargeTimeType {
|
||||
case 15:
|
||||
chargeTimeType = SHARE_REDEEM;
|
||||
break;
|
||||
case 16:
|
||||
chargeTimeType = SAVINGS_NOACTIVITY_FEE;
|
||||
break;
|
||||
default:
|
||||
chargeTimeType = INVALID;
|
||||
break;
|
||||
@@ -154,6 +160,10 @@ public enum ChargeTimeType {
|
||||
public boolean isWithdrawalFee() {
|
||||
return this.value.equals(ChargeTimeType.WITHDRAWAL_FEE.getValue());
|
||||
}
|
||||
|
||||
public boolean isSavingsNoActivityFee() {
|
||||
return this.value.equals(ChargeTimeType.SAVINGS_NOACTIVITY_FEE.getValue());
|
||||
}
|
||||
|
||||
public boolean isAnnualFee() {
|
||||
return this.value.equals(ChargeTimeType.ANNUAL_FEE.getValue());
|
||||
@@ -185,7 +195,7 @@ public enum ChargeTimeType {
|
||||
|
||||
public boolean isAllowedSavingsChargeTime() {
|
||||
return isOnSpecifiedDueDate() || isSavingsActivation() || isSavingsClosure() || isWithdrawalFee() || isAnnualFee()
|
||||
|| isMonthlyFee() || isWeeklyFee() || isOverdraftFee();
|
||||
|| isMonthlyFee() || isWeeklyFee() || isOverdraftFee() || isSavingsNoActivityFee();
|
||||
}
|
||||
|
||||
public boolean isOverdraftFee() {
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
// chargeTimeType(ChargeTimeType.SAVINGS_CLOSURE),
|
||||
chargeTimeType(ChargeTimeType.WITHDRAWAL_FEE), chargeTimeType(ChargeTimeType.ANNUAL_FEE),
|
||||
chargeTimeType(ChargeTimeType.MONTHLY_FEE), chargeTimeType(ChargeTimeType.WEEKLY_FEE),
|
||||
chargeTimeType(ChargeTimeType.OVERDRAFT_FEE));
|
||||
chargeTimeType(ChargeTimeType.OVERDRAFT_FEE), chargeTimeType(ChargeTimeType.SAVINGS_NOACTIVITY_FEE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
@@ -91,6 +91,10 @@ public class ChargeEnumerations {
|
||||
case SHARE_REDEEM:
|
||||
optionData = new EnumOptionData(ChargeTimeType.SHARE_REDEEM.getValue().longValue(), ChargeTimeType.SHARE_REDEEM.getCode(), "Share Redeem") ;
|
||||
break ;
|
||||
case SAVINGS_NOACTIVITY_FEE:
|
||||
optionData = new EnumOptionData(ChargeTimeType.SAVINGS_NOACTIVITY_FEE.getValue().longValue(), ChargeTimeType.SAVINGS_NOACTIVITY_FEE.getCode(),
|
||||
"Saving No Activity Fee");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid");
|
||||
break;
|
||||
|
||||
+10
-2
@@ -40,7 +40,8 @@ public enum SavingsAccountTransactionType {
|
||||
WITHDRAW_TRANSFER(14, "savingsAccountTransactionType.withdrawTransfer"), //
|
||||
REJECT_TRANSFER(15, "savingsAccountTransactionType.rejectTransfer"), WRITTEN_OFF(16, "savingsAccountTransactionType.writtenoff"), //
|
||||
OVERDRAFT_INTEREST(17, "savingsAccountTransactionType.overdraftInterest"), //
|
||||
WITHHOLD_TAX(18, "savingsAccountTransactionType.withholdTax");
|
||||
WITHHOLD_TAX(18, "savingsAccountTransactionType.withholdTax"),
|
||||
ESCHEAT(19, "savingsAccountTransactionType.escheat");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -109,6 +110,9 @@ public enum SavingsAccountTransactionType {
|
||||
case 18:
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.WITHHOLD_TAX;
|
||||
break;
|
||||
case 19:
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.ESCHEAT;
|
||||
break;
|
||||
}
|
||||
return savingsAccountTransactionType;
|
||||
}
|
||||
@@ -177,8 +181,12 @@ public enum SavingsAccountTransactionType {
|
||||
return this.value.equals(SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue());
|
||||
}
|
||||
|
||||
public boolean isEscheat() {
|
||||
return this.value.equals(SavingsAccountTransactionType.ESCHEAT.getValue());
|
||||
}
|
||||
|
||||
public boolean isDebit() {
|
||||
return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge() || isIncomeFromInterest() || isWithHoldTax();
|
||||
return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge() || isIncomeFromInterest() || isWithHoldTax() || isEscheat();
|
||||
}
|
||||
|
||||
public boolean isCredit() {
|
||||
|
||||
+13
-2
@@ -160,6 +160,13 @@ public class SavingsApiConstants {
|
||||
public static final String onHoldTransactionTypeParamName = "transactionType";
|
||||
public static final String onHoldTransactionDateParamName = "transactionDate";
|
||||
public static final String onHoldReversedParamName = "reversed";
|
||||
|
||||
// Savings Dormancy
|
||||
public static final String isDormancyTrackingActiveParamName = "isDormancyTrackingActive";
|
||||
public static final String daysToInactiveParamName = "daysToInactive";
|
||||
public static final String daysToDormancyParamName = "daysToDormancy";
|
||||
public static final String daysToEscheatParamName = "daysToEscheat";
|
||||
|
||||
|
||||
public static final Set<String> SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
|
||||
monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName,
|
||||
@@ -178,7 +185,10 @@ public class SavingsApiConstants {
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_INTEREST.getValue(), allowOverdraftParamName, overdraftLimitParamName,
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_INTEREST.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue(),
|
||||
isDormancyTrackingActiveParamName, daysToDormancyParamName, daysToInactiveParamName, daysToEscheatParamName,
|
||||
allowOverdraftParamName, overdraftLimitParamName,
|
||||
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, minRequiredBalanceParamName,
|
||||
enforceMinRequiredBalanceParamName, minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName));
|
||||
|
||||
@@ -196,7 +206,8 @@ public class SavingsApiConstants {
|
||||
"interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions",
|
||||
"interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions",
|
||||
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, withHoldTaxParamName,
|
||||
taxGroupIdParamName));
|
||||
taxGroupIdParamName, isDormancyTrackingActiveParamName, daysToInactiveParamName, daysToDormancyParamName,
|
||||
daysToInactiveParamName));
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, monthDayFormatParamName, staffIdParamName, accountNoParamName, externalIdParamName, clientIdParamName,
|
||||
|
||||
+119
-77
@@ -29,6 +29,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.staff.data.StaffData;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Immutable data object representing a savings account.
|
||||
@@ -48,6 +49,7 @@ public class SavingsAccountData {
|
||||
private final Long fieldOfficerId;
|
||||
private final String fieldOfficerName;
|
||||
private final SavingsAccountStatusEnumData status;
|
||||
private final SavingsAccountSubStatusEnumData subStatus;
|
||||
private final SavingsAccountApplicationTimelineData timeline;
|
||||
private final CurrencyData currency;
|
||||
private final BigDecimal nominalAnnualInterestRate;
|
||||
@@ -67,6 +69,11 @@ public class SavingsAccountData {
|
||||
private final BigDecimal onHoldFunds;
|
||||
private final boolean withHoldTax;
|
||||
private final TaxGroupData taxGroup;
|
||||
private final LocalDate lastActiveTransactionDate;
|
||||
private final boolean isDormancyTrackingActive;
|
||||
private final Integer daysToInactive;
|
||||
private final Integer daysToDormancy;
|
||||
private final Integer daysToEscheat;
|
||||
|
||||
// associations
|
||||
private final SavingsAccountSummaryData summary;
|
||||
@@ -96,16 +103,18 @@ public class SavingsAccountData {
|
||||
public static SavingsAccountData instance(final Long id, final String accountNo, final EnumOptionData depositType,
|
||||
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 interestRate, final EnumOptionData interestCompoundingPeriodType,
|
||||
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
|
||||
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers,
|
||||
final SavingsAccountSummaryData summary, final boolean allowOverdraft, final BigDecimal overdraftLimit,
|
||||
final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
|
||||
final boolean withHoldTax, final TaxGroupData taxGroup) {
|
||||
final SavingsAccountStatusEnumData status, SavingsAccountSubStatusEnumData subStatus, final SavingsAccountApplicationTimelineData timeline,
|
||||
final CurrencyData currency, final BigDecimal interestRate,
|
||||
final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType,
|
||||
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeForTransfers, final SavingsAccountSummaryData summary, final boolean allowOverdraft,
|
||||
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance,
|
||||
final boolean enforceMinRequiredBalance, final BigDecimal minBalanceForInterestCalculation,
|
||||
final BigDecimal onHoldFunds, final BigDecimal nominalAnnualInterestRateOverdraft,
|
||||
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
|
||||
final LocalDate lastActiveTransactionDate, final boolean isDormancyTrackingActive, final Integer daysToInactive,
|
||||
final Integer daysToDormancy, final Integer daysToEscheat) {
|
||||
|
||||
final Collection<SavingsProductData> productOptions = null;
|
||||
final Collection<StaffData> fieldOfficerOptions = null;
|
||||
@@ -121,14 +130,15 @@ public class SavingsAccountData {
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
|
||||
return new SavingsAccountData(id, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions,
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, interestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions,
|
||||
productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
|
||||
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
|
||||
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsAccountData lookup(final Long accountId, final String accountNo, final EnumOptionData depositType) {
|
||||
@@ -184,16 +194,23 @@ public class SavingsAccountData {
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
final SavingsAccountSubStatusEnumData subStatus = null;
|
||||
final LocalDate lastActiveTransactionDate = null;
|
||||
final boolean isDormancyTrackingActive = false;
|
||||
final Integer daysToInactive = null;
|
||||
final Integer daysToDormancy = null;
|
||||
final Integer daysToEscheat = null;
|
||||
|
||||
return new SavingsAccountData(accountId, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions,
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, nominalAnnualInterestRate,
|
||||
interestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions,
|
||||
productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
|
||||
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
|
||||
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo,
|
||||
@@ -247,16 +264,23 @@ public class SavingsAccountData {
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
final SavingsAccountSubStatusEnumData subStatus = null;
|
||||
final LocalDate lastActiveTransactionDate = null;
|
||||
final boolean isDormancyTrackingActive = false;
|
||||
final Integer daysToInactive = null;
|
||||
final Integer daysToDormancy = null;
|
||||
final Integer daysToEscheat = null;
|
||||
|
||||
return new SavingsAccountData(accountId, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions,
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, nominalAnnualInterestRate,
|
||||
interestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions,
|
||||
productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
|
||||
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
|
||||
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template,
|
||||
@@ -280,17 +304,19 @@ public class SavingsAccountData {
|
||||
|
||||
return new SavingsAccountData(account.id, account.accountNo, account.depositType, account.externalId, account.groupId,
|
||||
account.groupName, account.clientId, account.clientName, account.savingsProductId, account.savingsProductName,
|
||||
account.fieldOfficerId, account.fieldOfficerName, account.status, account.timeline, account.currency,
|
||||
account.nominalAnnualInterestRate, account.interestCompoundingPeriodType, account.interestPostingPeriodType,
|
||||
account.interestCalculationType, account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance,
|
||||
account.lockinPeriodFrequency, account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary,
|
||||
transactions, template.productOptions, template.fieldOfficerOptions, template.interestCompoundingPeriodTypeOptions,
|
||||
template.interestPostingPeriodTypeOptions, template.interestCalculationTypeOptions,
|
||||
template.interestCalculationDaysInYearTypeOptions, template.lockinPeriodFrequencyTypeOptions,
|
||||
template.withdrawalFeeTypeOptions, charges, template.chargeOptions, account.allowOverdraft, account.overdraftLimit,
|
||||
account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation,
|
||||
account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation,
|
||||
account.withHoldTax, account.taxGroup);
|
||||
account.fieldOfficerId, account.fieldOfficerName, account.status, account.subStatus, account.timeline,
|
||||
account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType,
|
||||
account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType,
|
||||
account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers,
|
||||
account.summary, transactions, template.productOptions, template.fieldOfficerOptions,
|
||||
template.interestCompoundingPeriodTypeOptions, template.interestPostingPeriodTypeOptions,
|
||||
template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions,
|
||||
template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions, account.allowOverdraft,
|
||||
account.overdraftLimit, account.minRequiredBalance, account.enforceMinRequiredBalance,
|
||||
account.minBalanceForInterestCalculation, account.onHoldFunds, account.nominalAnnualInterestRateOverdraft,
|
||||
account.minOverdraftForInterestCalculation, account.withHoldTax, account.taxGroup,
|
||||
account.lastActiveTransactionDate, account.isDormancyTrackingActive, account.daysToInactive,
|
||||
account.daysToDormancy, account.daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
|
||||
@@ -305,16 +331,17 @@ public class SavingsAccountData {
|
||||
|
||||
return new SavingsAccountData(account.id, account.accountNo, account.depositType, account.externalId, account.groupId,
|
||||
account.groupName, account.clientId, account.clientName, account.savingsProductId, account.savingsProductName,
|
||||
account.fieldOfficerId, account.fieldOfficerName, account.status, account.timeline, account.currency,
|
||||
account.nominalAnnualInterestRate, account.interestCompoundingPeriodType, account.interestPostingPeriodType,
|
||||
account.interestCalculationType, account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance,
|
||||
account.lockinPeriodFrequency, account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary,
|
||||
transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, account.allowOverdraft, account.overdraftLimit,
|
||||
account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation,
|
||||
account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation,
|
||||
account.withHoldTax, account.taxGroup);
|
||||
account.fieldOfficerId, account.fieldOfficerName, account.status, account.subStatus, account.timeline,
|
||||
account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType,
|
||||
account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType,
|
||||
account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers,
|
||||
account.summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, account.allowOverdraft,
|
||||
account.overdraftLimit, account.minRequiredBalance, account.enforceMinRequiredBalance,
|
||||
account.minBalanceForInterestCalculation, account.onHoldFunds, account.nominalAnnualInterestRateOverdraft,
|
||||
account.minOverdraftForInterestCalculation, account.withHoldTax, account.taxGroup, account.lastActiveTransactionDate,
|
||||
account.isDormancyTrackingActive, account.daysToInactive, account.daysToDormancy, account.daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
|
||||
@@ -370,38 +397,47 @@ public class SavingsAccountData {
|
||||
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final SavingsAccountSubStatusEnumData subStatus = null;
|
||||
final LocalDate lastActiveTransactionDate = null;
|
||||
final boolean isDormancyTrackingActive = false;
|
||||
final Integer daysToInactive = null;
|
||||
final Integer daysToDormancy = null;
|
||||
final Integer daysToEscheat = null;
|
||||
|
||||
return new SavingsAccountData(id, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions,
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, nominalAnnualInterestRate,
|
||||
interestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions,
|
||||
productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
|
||||
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
|
||||
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
private SavingsAccountData(final Long id, final String accountNo, final EnumOptionData depositType, 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 SavingsAccountSummaryData summary, final Collection<SavingsAccountTransactionData> transactions,
|
||||
final Collection<SavingsProductData> productOptions, final Collection<StaffData> fieldOfficerOptions,
|
||||
final SavingsAccountSubStatusEnumData subStatus, 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 SavingsAccountSummaryData summary,
|
||||
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsProductData> 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 boolean allowOverdraft,
|
||||
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
|
||||
final boolean withHoldTax, final TaxGroupData taxGroup) {
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions, final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions,
|
||||
final Collection<EnumOptionData> withdrawalFeeTypeOptions, final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance,
|
||||
final boolean enforceMinRequiredBalance, final BigDecimal minBalanceForInterestCalculation,
|
||||
final BigDecimal onHoldFunds, final BigDecimal nominalAnnualInterestRateOverdraft,
|
||||
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
|
||||
final LocalDate lastActiveTransactionDate, final boolean isDormancyTrackingActive, final Integer daysToInactive,
|
||||
final Integer daysToDormancy, final Integer daysToEscheat) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
this.depositType = depositType;
|
||||
@@ -415,6 +451,7 @@ public class SavingsAccountData {
|
||||
this.fieldOfficerId = fieldofficerId;
|
||||
this.fieldOfficerName = fieldofficerName;
|
||||
this.status = status;
|
||||
this.subStatus = subStatus;
|
||||
this.timeline = timeline;
|
||||
this.currency = currency;
|
||||
this.nominalAnnualInterestRate = nominalAnnualInterestRate;
|
||||
@@ -461,6 +498,11 @@ public class SavingsAccountData {
|
||||
this.onHoldFunds = onHoldFunds;
|
||||
this.withHoldTax = withHoldTax;
|
||||
this.taxGroup = taxGroup;
|
||||
this.lastActiveTransactionDate = lastActiveTransactionDate;
|
||||
this.isDormancyTrackingActive = isDormancyTrackingActive;
|
||||
this.daysToInactive = daysToInactive;
|
||||
this.daysToDormancy = daysToDormancy;
|
||||
this.daysToEscheat = daysToEscheat;
|
||||
}
|
||||
|
||||
private SavingsAccountChargeData getWithdrawalFee() {
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
/**
|
||||
* Immutable data object represent savings account sub-status enumerations.
|
||||
*/
|
||||
public class SavingsAccountSubStatusEnumData {
|
||||
|
||||
private final Long id;
|
||||
@SuppressWarnings("unused")
|
||||
private final String code;
|
||||
@SuppressWarnings("unused")
|
||||
private final String value;
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean none;
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean inactive;
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean dormant;
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean escheat;
|
||||
|
||||
public SavingsAccountSubStatusEnumData(final Long id, final String code, final String value, final boolean none,
|
||||
final boolean inactive, final boolean dormant, final boolean escheat) {
|
||||
this.id = id;
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.none = none;
|
||||
this.inactive = inactive;
|
||||
this.dormant = dormant;
|
||||
this.escheat = escheat;
|
||||
}
|
||||
|
||||
public Long id() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
+6
@@ -43,6 +43,7 @@ public class SavingsAccountTransactionEnumData {
|
||||
private final boolean writtenoff;
|
||||
private final boolean overdraftFee = true;
|
||||
private final boolean withholdTax;
|
||||
private final boolean escheat;
|
||||
|
||||
public SavingsAccountTransactionEnumData(final Long id, final String code, final String value) {
|
||||
this.id = id;
|
||||
@@ -62,6 +63,7 @@ public class SavingsAccountTransactionEnumData {
|
||||
this.writtenoff = Long.valueOf(SavingsAccountTransactionType.WRITTEN_OFF.getValue()).equals(this.id);
|
||||
this.overdraftInterest = Long.valueOf(SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue()).equals(this.id);
|
||||
this.withholdTax = Long.valueOf(SavingsAccountTransactionType.WITHHOLD_TAX.getValue()).equals(this.id);
|
||||
this.escheat = Long.valueOf(SavingsAccountTransactionType.ESCHEAT.getValue()).equals(this.id);
|
||||
// this.overdraftFee =
|
||||
// Long.valueOf(SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue()).equals(this.id);
|
||||
}
|
||||
@@ -133,5 +135,9 @@ public class SavingsAccountTransactionEnumData {
|
||||
public boolean isDividendPayout() {
|
||||
return this.dividendPayout;
|
||||
}
|
||||
|
||||
public boolean isEscheat() {
|
||||
return this.escheat;
|
||||
}
|
||||
|
||||
}
|
||||
+29
-8
@@ -86,6 +86,10 @@ public class SavingsProductData {
|
||||
private final Collection<ChargeData> chargeOptions;
|
||||
private final Collection<ChargeData> penaltyOptions;
|
||||
private final Collection<TaxGroupData> taxGroupOptions;
|
||||
private final Boolean isDormancyTrackingActive;
|
||||
private final Long daysToInactive;
|
||||
private final Long daysToDormancy;
|
||||
private final Long daysToEscheat;
|
||||
|
||||
public static SavingsProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
|
||||
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
|
||||
@@ -122,6 +126,10 @@ public class SavingsProductData {
|
||||
final BigDecimal minOverdraftForInterestCalculation = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
final Boolean isDormancyTrackingActive = false;
|
||||
final Long daysToInactive = null;
|
||||
final Long daysToDormancy = null;
|
||||
final Long daysToEscheat = null;
|
||||
|
||||
return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
@@ -132,7 +140,7 @@ public class SavingsProductData {
|
||||
accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroup, taxGroupOptions);
|
||||
taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsProductData withCharges(final SavingsProductData product, final Collection<ChargeData> charges) {
|
||||
@@ -148,7 +156,8 @@ public class SavingsProductData {
|
||||
product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings, product.allowOverdraft, product.overdraftLimit,
|
||||
product.minRequiredBalance, product.enforceMinRequiredBalance, product.minBalanceForInterestCalculation,
|
||||
product.nominalAnnualInterestRateOverdraft, product.minOverdraftForInterestCalculation, product.withHoldTax,
|
||||
product.taxGroup, product.taxGroupOptions);
|
||||
product.taxGroup, product.taxGroupOptions, product.isDormancyTrackingActive, product.daysToInactive,
|
||||
product.daysToDormancy, product.daysToEscheat);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,7 +190,8 @@ public class SavingsProductData {
|
||||
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, existingProduct.allowOverdraft,
|
||||
existingProduct.overdraftLimit, existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance,
|
||||
existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft,
|
||||
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions);
|
||||
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions,
|
||||
existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive, existingProduct.daysToDormancy, existingProduct.daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct,
|
||||
@@ -217,7 +227,8 @@ public class SavingsProductData {
|
||||
existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance,
|
||||
existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft,
|
||||
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
existingProduct.taxGroupOptions);
|
||||
existingProduct.taxGroupOptions, existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive,
|
||||
existingProduct.daysToDormancy, existingProduct.daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsProductData instance(final Long id, final String name, final String shortName, final String description,
|
||||
@@ -228,7 +239,8 @@ public class SavingsProductData {
|
||||
final EnumOptionData accountingType, final boolean allowOverdraft, final BigDecimal overdraftLimit,
|
||||
final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
|
||||
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup) {
|
||||
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
|
||||
final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) {
|
||||
|
||||
final Map<String, Object> accountingMappings = null;
|
||||
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
|
||||
@@ -259,7 +271,7 @@ public class SavingsProductData {
|
||||
accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroup, taxGroupOptions);
|
||||
taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
public static SavingsProductData lookup(final Long id, final String name) {
|
||||
@@ -305,6 +317,10 @@ public class SavingsProductData {
|
||||
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
|
||||
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
final Boolean isDormancyTrackingActive = null;
|
||||
final Long daysToInactive = null;
|
||||
final Long daysToDormancy = null;
|
||||
final Long daysToEscheat = null;
|
||||
|
||||
return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
@@ -315,7 +331,7 @@ public class SavingsProductData {
|
||||
accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroup, taxGroupOptions);
|
||||
taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
private SavingsProductData(final Long id, final String name, final String shortName, final String description,
|
||||
@@ -338,7 +354,8 @@ public class SavingsProductData {
|
||||
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
|
||||
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
|
||||
final Collection<TaxGroupData> taxGroupOptions) {
|
||||
final Collection<TaxGroupData> taxGroupOptions, final Boolean isDormancyTrackingActive, final Long daysToInactive,
|
||||
final Long daysToDormancy, final Long daysToEscheat) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -391,6 +408,10 @@ public class SavingsProductData {
|
||||
this.taxGroup = taxGroup;
|
||||
this.withHoldTax = withHoldTax;
|
||||
this.taxGroupOptions = taxGroupOptions;
|
||||
this.isDormancyTrackingActive = isDormancyTrackingActive;
|
||||
this.daysToInactive = daysToInactive;
|
||||
this.daysToDormancy = daysToDormancy;
|
||||
this.daysToEscheat = daysToEscheat;
|
||||
}
|
||||
|
||||
public boolean hasAccountingEnabled() {
|
||||
|
||||
+70
-1
@@ -44,6 +44,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNam
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
@@ -64,6 +68,7 @@ import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodTyp
|
||||
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.domain.SavingsProduct;
|
||||
import org.joda.time.MonthDay;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -232,6 +237,27 @@ public class SavingsProductDataValidator {
|
||||
baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).notNull().zeroOrPositiveAmount();
|
||||
}
|
||||
}
|
||||
|
||||
//dormancy
|
||||
final Boolean isDormancyActive = this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element);
|
||||
|
||||
if(null != isDormancyActive && isDormancyActive){
|
||||
final Long daysToInact = this.fromApiJsonHelper.extractLongNamed(daysToInactiveParamName, element);
|
||||
baseDataValidator.reset().parameter(daysToInactiveParamName).value(daysToInact).notNull().longGreaterThanZero();
|
||||
|
||||
final Long daysToDor = this.fromApiJsonHelper.extractLongNamed(daysToDormancyParamName, element);
|
||||
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).notNull().longGreaterThanZero();
|
||||
|
||||
final Long daysToEsc = this.fromApiJsonHelper.extractLongNamed(daysToEscheatParamName, element);
|
||||
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).notNull().longGreaterThanZero();
|
||||
|
||||
if(null != daysToInact
|
||||
&& null != daysToDor
|
||||
&& null != daysToEsc){
|
||||
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).longGreaterThanNumber(daysToInact);
|
||||
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).longGreaterThanNumber(daysToDor);
|
||||
}
|
||||
}
|
||||
|
||||
// accounting related data validation
|
||||
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
|
||||
@@ -283,6 +309,13 @@ public class SavingsProductDataValidator {
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
||||
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writtenoff)
|
||||
.notNull().integerGreaterThanZero();
|
||||
|
||||
if(null != isDormancyActive && isDormancyActive){
|
||||
final Long escheatLiabilityAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue(), element);
|
||||
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue())
|
||||
.value(escheatLiabilityAccountId).notNull().integerGreaterThanZero();
|
||||
}
|
||||
|
||||
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
|
||||
validateChargeToIncomeAccountMappings(baseDataValidator, element);
|
||||
@@ -302,7 +335,7 @@ public class SavingsProductDataValidator {
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateForUpdate(final String json) {
|
||||
public void validateForUpdate(final String json, final SavingsProduct product) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
@@ -453,6 +486,42 @@ public class SavingsProductDataValidator {
|
||||
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writtenoff)
|
||||
.ignoreIfNull().integerGreaterThanZero();
|
||||
|
||||
//dormancy
|
||||
final Boolean isDormancyActive = this.fromApiJsonHelper.parameterExists(isDormancyTrackingActiveParamName, element)?
|
||||
this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element):
|
||||
product.isDormancyTrackingActive();
|
||||
|
||||
if(null != isDormancyActive && isDormancyActive){
|
||||
final Long daysToInact = this.fromApiJsonHelper.parameterExists(daysToInactiveParamName, element)?
|
||||
this.fromApiJsonHelper.extractLongNamed(daysToInactiveParamName, element):
|
||||
product.getDaysToInactive();
|
||||
baseDataValidator.reset().parameter(daysToInactiveParamName).value(daysToInact).notNull().longGreaterThanZero();
|
||||
|
||||
final Long daysToDor = this.fromApiJsonHelper.parameterExists(daysToDormancyParamName, element)?
|
||||
this.fromApiJsonHelper.extractLongNamed(daysToDormancyParamName, element):
|
||||
product.getDaysToDormancy();
|
||||
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).notNull().longGreaterThanZero();
|
||||
|
||||
final Long daysToEsc = this.fromApiJsonHelper.parameterExists(daysToEscheatParamName, element)?
|
||||
this.fromApiJsonHelper.extractLongNamed(daysToEscheatParamName, element):
|
||||
product.getDaysToEscheat();
|
||||
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).notNull().longGreaterThanZero();
|
||||
|
||||
if(null != daysToInact
|
||||
&& null != daysToDor
|
||||
&& null != daysToEsc){
|
||||
baseDataValidator.reset().parameter(daysToDormancyParamName).value(daysToDor).longGreaterThanNumber(daysToInact);
|
||||
baseDataValidator.reset().parameter(daysToEscheatParamName).value(daysToEsc).longGreaterThanNumber(daysToDor);
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue(), element)){
|
||||
final Long escheatLiabilityAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue(), element);
|
||||
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.ESCHEAT_LIABILITY.getValue())
|
||||
.value(escheatLiabilityAccountId).notNull().integerGreaterThanZero();
|
||||
}
|
||||
}
|
||||
|
||||
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
|
||||
validateChargeToIncomeAccountMappings(baseDataValidator, element);
|
||||
validateOverdraftParams(baseDataValidator, element);
|
||||
|
||||
+46
@@ -114,6 +114,7 @@ import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
@@ -154,6 +155,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
@Column(name = "status_enum", nullable = false)
|
||||
protected Integer status;
|
||||
|
||||
@Column(name = "sub_status_enum", nullable = false)
|
||||
protected Integer sub_status = 0;
|
||||
|
||||
@Column(name = "account_type_enum", nullable = false)
|
||||
protected Integer accountType;
|
||||
|
||||
@@ -901,6 +905,11 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
this.transactions.add(transaction);
|
||||
|
||||
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||
|
||||
if(this.sub_status.equals(SavingsAccountSubStatusEnum.INACTIVE.getValue())
|
||||
|| this.sub_status.equals(SavingsAccountSubStatusEnum.DORMANT.getValue())){
|
||||
this.sub_status = SavingsAccountSubStatusEnum.NONE.getValue();
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
@@ -987,6 +996,10 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
// auto pay withdrawal fee
|
||||
payWithdrawalFee(transactionDTO.getTransactionAmount(), transactionDTO.getTransactionDate(), transactionDTO.getAppUser());
|
||||
}
|
||||
if(this.sub_status.equals(SavingsAccountSubStatusEnum.INACTIVE.getValue())
|
||||
|| this.sub_status.equals(SavingsAccountSubStatusEnum.DORMANT.getValue())){
|
||||
this.sub_status = SavingsAccountSubStatusEnum.NONE.getValue();
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@@ -2744,4 +2757,37 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
|
||||
return recalucateDailyBalance;
|
||||
}
|
||||
|
||||
public void setSubStatusInactive(AppUser appUser) {
|
||||
this.sub_status = SavingsAccountSubStatusEnum.INACTIVE.getValue();
|
||||
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
|
||||
for (SavingsAccountCharge charge : this.charges()) {
|
||||
if (charge.isSavingsNoActivity() && charge.isActive()) {
|
||||
charge.updateWithdralFeeAmount(this.getAccountBalance());
|
||||
this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, appUser);
|
||||
}
|
||||
}
|
||||
recalculateDailyBalances(Money.zero(this.currency), transactionDate);
|
||||
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||
}
|
||||
|
||||
|
||||
public void setSubStatusDormant() {
|
||||
this.sub_status = SavingsAccountSubStatusEnum.DORMANT.getValue();
|
||||
}
|
||||
|
||||
public void escheat(AppUser appUser) {
|
||||
this.status = SavingsAccountStatusType.CLOSED.getValue();
|
||||
this.sub_status = SavingsAccountSubStatusEnum.ESCHEAT.getValue();
|
||||
this.closedOnDate = DateUtils.getDateOfTenant();
|
||||
this.closedBy = appUser;
|
||||
|
||||
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
|
||||
if(this.getSummary().getAccountBalance(this.getCurrency()).isGreaterThanZero()){
|
||||
SavingsAccountTransaction transaction = SavingsAccountTransaction.escheat(this, transactionDate, appUser);
|
||||
this.transactions.add(transaction);
|
||||
}
|
||||
recalculateDailyBalances(Money.zero(this.currency), transactionDate);
|
||||
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||
}
|
||||
}
|
||||
+4
@@ -656,6 +656,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
public boolean isSavingsActivation() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isSavingsActivation();
|
||||
}
|
||||
|
||||
public boolean isSavingsNoActivity(){
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isSavingsNoActivityFee();
|
||||
}
|
||||
|
||||
public boolean isSavingsClosure() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isSavingsClosure();
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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.domain;
|
||||
|
||||
/**
|
||||
* Enum representation of {@link SavingsAccount} sub-status states.
|
||||
*/
|
||||
public enum SavingsAccountSubStatusEnum {
|
||||
|
||||
NONE(0, "SavingsAccountSubStatusEnum.none"), //
|
||||
INACTIVE(100, "SavingsAccountSubStatusEnum.inactive"), //
|
||||
DORMANT(200, "SavingsAccountSubStatusEnum.dormant"),
|
||||
ESCHEAT(300,"SavingsAccountSubStatusEnum.escheat");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
public static SavingsAccountSubStatusEnum fromInt(final Integer type) {
|
||||
|
||||
SavingsAccountSubStatusEnum enumeration = SavingsAccountSubStatusEnum.NONE;
|
||||
if(null != type){
|
||||
switch (type) {
|
||||
case 100:
|
||||
enumeration = SavingsAccountSubStatusEnum.INACTIVE;
|
||||
break;
|
||||
case 200:
|
||||
enumeration = SavingsAccountSubStatusEnum.DORMANT;
|
||||
break;
|
||||
case 300:
|
||||
enumeration = SavingsAccountSubStatusEnum.ESCHEAT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
|
||||
private SavingsAccountSubStatusEnum(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public boolean hasStateOf(final SavingsAccountSubStatusEnum state) {
|
||||
return this.value.equals(state.getValue());
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public boolean isSubStatusInactive() {
|
||||
return this.value.equals(SavingsAccountSubStatusEnum.INACTIVE.getValue());
|
||||
}
|
||||
|
||||
public boolean isSubStatusDormant() {
|
||||
return this.value.equals(SavingsAccountSubStatusEnum.DORMANT.getValue());
|
||||
}
|
||||
|
||||
public boolean isSubStatusNone() {
|
||||
return this.value.equals(SavingsAccountSubStatusEnum.NONE.getValue());
|
||||
}
|
||||
|
||||
public boolean isSubStatusEscheat() {
|
||||
return this.value.equals(SavingsAccountSubStatusEnum.ESCHEAT.getValue());
|
||||
}
|
||||
}
|
||||
+9
@@ -228,6 +228,15 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
return accountTransaction;
|
||||
}
|
||||
|
||||
public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date,
|
||||
final AppUser appUser) {
|
||||
final boolean isReversed = false;
|
||||
final PaymentDetail paymentDetail = null;
|
||||
return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail,
|
||||
SavingsAccountTransactionType.ESCHEAT.getValue(), date, new Date(), savingsAccount.getSummary()
|
||||
.getAccountBalance(), isReversed, appUser);
|
||||
}
|
||||
|
||||
public static void updateTaxDetails(final Map<TaxComponent, BigDecimal> taxDetails, final SavingsAccountTransaction accountTransaction) {
|
||||
if (taxDetails != null) {
|
||||
for (Map.Entry<TaxComponent, BigDecimal> mapEntry : taxDetails.entrySet()) {
|
||||
|
||||
+74
-4
@@ -46,6 +46,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNam
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
@@ -191,6 +195,18 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
@JoinColumn(name = "tax_group_id")
|
||||
private TaxGroup taxGroup;
|
||||
|
||||
@Column(name = "is_dormancy_tracking_active")
|
||||
private Boolean isDormancyTrackingActive;
|
||||
|
||||
@Column(name = "days_to_inactive")
|
||||
private Long daysToInactive;
|
||||
|
||||
@Column(name = "days_to_dormancy")
|
||||
private Long daysToDormancy;
|
||||
|
||||
@Column(name = "days_to_escheat")
|
||||
private Long daysToEscheat;
|
||||
|
||||
public static SavingsProduct createNew(final String name, final String shortName, final String description,
|
||||
final MonetaryCurrency currency, final BigDecimal interestRate,
|
||||
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
|
||||
@@ -201,13 +217,15 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minRequiredBalance, final BigDecimal minBalanceForInterestCalculation,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
|
||||
boolean withHoldTax, TaxGroup taxGroup) {
|
||||
boolean withHoldTax, TaxGroup taxGroup,
|
||||
final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) {
|
||||
|
||||
return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges,
|
||||
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
|
||||
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
protected SavingsProduct() {
|
||||
@@ -226,7 +244,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
this(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit,
|
||||
false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup);
|
||||
false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup, null, null, null, null);
|
||||
}
|
||||
|
||||
protected SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency,
|
||||
@@ -238,7 +256,8 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minRequiredBalance, BigDecimal minBalanceForInterestCalculation,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
|
||||
final boolean withHoldTax, final TaxGroup taxGroup) {
|
||||
final boolean withHoldTax, final TaxGroup taxGroup,
|
||||
final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) {
|
||||
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -283,6 +302,11 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
|
||||
this.withHoldTax = withHoldTax;
|
||||
this.taxGroup = taxGroup;
|
||||
|
||||
this.isDormancyTrackingActive = isDormancyTrackingActive;
|
||||
this.daysToInactive = daysToInactive;
|
||||
this.daysToDormancy = daysToDormancy;
|
||||
this.daysToEscheat = daysToEscheat;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,6 +565,36 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
} else {
|
||||
this.taxGroup = null;
|
||||
}
|
||||
|
||||
if(command.isChangeInBooleanParameterNamed(isDormancyTrackingActiveParamName, this.isDormancyTrackingActive)){
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isDormancyTrackingActiveParamName);
|
||||
actualChanges.put(isDormancyTrackingActiveParamName, newValue);
|
||||
this.isDormancyTrackingActive = newValue;
|
||||
}
|
||||
|
||||
if(command.isChangeInLongParameterNamed(daysToInactiveParamName, this.daysToInactive)){
|
||||
final Long newValue = command.longValueOfParameterNamed(daysToInactiveParamName);
|
||||
actualChanges.put(daysToInactiveParamName, newValue);
|
||||
this.daysToInactive = newValue;
|
||||
}
|
||||
|
||||
if(command.isChangeInLongParameterNamed(daysToDormancyParamName, this.daysToDormancy)){
|
||||
final Long newValue = command.longValueOfParameterNamed(daysToDormancyParamName);
|
||||
actualChanges.put(daysToDormancyParamName, newValue);
|
||||
this.daysToDormancy = newValue;
|
||||
}
|
||||
|
||||
if(command.isChangeInLongParameterNamed(daysToEscheatParamName, this.daysToEscheat)){
|
||||
final Long newValue = command.longValueOfParameterNamed(daysToEscheatParamName);
|
||||
actualChanges.put(daysToEscheatParamName, newValue);
|
||||
this.daysToEscheat = newValue;
|
||||
}
|
||||
|
||||
if(!this.isDormancyTrackingActive){
|
||||
this.daysToInactive = null;
|
||||
this.daysToDormancy = null;
|
||||
this.daysToEscheat = null;
|
||||
}
|
||||
|
||||
validateLockinDetails();
|
||||
esnureOverdraftLimitsSetForOverdraftAccounts();
|
||||
@@ -676,4 +730,20 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
return this.withHoldTax;
|
||||
}
|
||||
|
||||
public boolean isDormancyTrackingActive() {
|
||||
return null == this.isDormancyTrackingActive? false: this.isDormancyTrackingActive;
|
||||
}
|
||||
|
||||
public Long getDaysToInactive() {
|
||||
return this.daysToInactive;
|
||||
}
|
||||
|
||||
public Long getDaysToDormancy() {
|
||||
return this.daysToDormancy;
|
||||
}
|
||||
|
||||
public Long getDaysToEscheat() {
|
||||
return this.daysToEscheat;
|
||||
}
|
||||
|
||||
}
|
||||
+11
-1
@@ -44,6 +44,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraf
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToInactiveParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToDormancyParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.daysToEscheatParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
@@ -173,12 +177,18 @@ public class SavingsProductAssembler {
|
||||
|
||||
boolean withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
final TaxGroup taxGroup = assembleTaxGroup(command);
|
||||
|
||||
final Boolean isDormancyTrackingActive = command.booleanObjectValueOfParameterNamed(isDormancyTrackingActiveParamName);
|
||||
final Long daysToInactive = command.longValueOfParameterNamed(daysToInactiveParamName);
|
||||
final Long daysToDormancy = command.longValueOfParameterNamed(daysToDormancyParamName);
|
||||
final Long daysToEscheat = command.longValueOfParameterNamed(daysToEscheatParamName);
|
||||
|
||||
return SavingsProduct.createNew(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges,
|
||||
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
|
||||
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
|
||||
public Set<Charge> assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) {
|
||||
|
||||
+8
@@ -19,12 +19,14 @@
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public interface SavingsAccountReadPlatformService {
|
||||
|
||||
@@ -50,4 +52,10 @@ public interface SavingsAccountReadPlatformService {
|
||||
SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId, DepositAccountType depositAccountType);
|
||||
|
||||
Collection<SavingsAccountData> retrieveForLookup(Long clientId, Boolean overdraft);
|
||||
|
||||
List<Long> retrieveSavingsIdsPendingInactive(LocalDate tenantLocalDate);
|
||||
|
||||
List<Long> retrieveSavingsIdsPendingDormant(LocalDate tenantLocalDate);
|
||||
|
||||
List<Long> retrieveSavingsIdsPendingEscheat(LocalDate tenantLocalDate);
|
||||
}
|
||||
+144
-13
@@ -24,6 +24,8 @@ import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
@@ -38,6 +40,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.data.AccountTransferData;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.client.data.ClientData;
|
||||
@@ -56,15 +59,22 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimel
|
||||
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.SavingsAccountTransactionEnumData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
@@ -82,6 +92,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
private final StaffReadPlatformService staffReadPlatformService;
|
||||
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
|
||||
|
||||
// mappers
|
||||
private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper;
|
||||
@@ -217,7 +228,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sp.id as productId, sp.name as productName, ");
|
||||
sqlBuilder.append("s.id fieldOfficerId, s.display_name as fieldOfficerName, ");
|
||||
sqlBuilder.append("sa.status_enum as statusEnum, ");
|
||||
|
||||
sqlBuilder.append("sa.sub_status_enum as subStatusEnum, ");
|
||||
sqlBuilder.append("sa.submittedon_date as submittedOnDate,");
|
||||
sqlBuilder.append("sbu.username as submittedByUsername,");
|
||||
sqlBuilder.append("sbu.firstname as submittedByFirstname, sbu.lastname as submittedByLastname,");
|
||||
@@ -282,8 +293,16 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds, ");
|
||||
sqlBuilder.append("sa.withhold_tax as withHoldTax,");
|
||||
sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax,");
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
||||
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
|
||||
sqlBuilder.append("(select IFNULL(max(sat.transaction_date),sa.activatedon_date) ");
|
||||
sqlBuilder.append("from m_savings_account_transaction as sat ");
|
||||
sqlBuilder.append("where sat.is_reversed = 0 ");
|
||||
sqlBuilder.append("and sat.transaction_type_enum in (1,2) ");
|
||||
sqlBuilder.append("and sat.savings_account_id = sa.id) as lastActiveTransactionDate, ");
|
||||
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("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 ");
|
||||
@@ -328,6 +347,31 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum");
|
||||
final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusEnum);
|
||||
|
||||
final Integer subStatusEnum = JdbcSupport.getInteger(rs, "subStatusEnum");
|
||||
final SavingsAccountSubStatusEnumData subStatus = SavingsEnumerations.subStatus(subStatusEnum);
|
||||
|
||||
final LocalDate lastActiveTransactionDate = JdbcSupport.getLocalDate(rs, "lastActiveTransactionDate");
|
||||
final boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive");
|
||||
final Integer numDaysToInactive = JdbcSupport.getInteger(rs, "daysToInactive");
|
||||
final Integer numDaysToDormancy = JdbcSupport.getInteger(rs, "daysToDormancy");
|
||||
final Integer numDaysToEscheat = JdbcSupport.getInteger(rs, "daysToEscheat");
|
||||
Integer daysToInactive = null;
|
||||
Integer daysToDormancy = null;
|
||||
Integer daysToEscheat = null;
|
||||
|
||||
LocalDate localTenantDate = DateUtils.getLocalDateOfTenant();
|
||||
if(isDormancyTrackingActive && statusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())){
|
||||
if(subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()){
|
||||
daysToEscheat = Days.daysBetween(localTenantDate,lastActiveTransactionDate.plusDays(numDaysToEscheat)).getDays();
|
||||
}
|
||||
if(subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()){
|
||||
daysToDormancy = Days.daysBetween(localTenantDate,lastActiveTransactionDate.plusDays(numDaysToDormancy)).getDays();
|
||||
}
|
||||
if(subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()){
|
||||
daysToInactive = Days.daysBetween(localTenantDate,lastActiveTransactionDate.plusDays(numDaysToInactive)).getDays();
|
||||
}
|
||||
}
|
||||
|
||||
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
|
||||
final String submittedByUsername = rs.getString("submittedByUsername");
|
||||
final String submittedByFirstname = rs.getString("submittedByFirstname");
|
||||
@@ -469,11 +513,12 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
}
|
||||
|
||||
return SavingsAccountData.instance(id, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary,
|
||||
allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation,
|
||||
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData);
|
||||
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency,
|
||||
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
|
||||
summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -996,15 +1041,23 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
// final LocalDate annualFeeNextDueDate = null;
|
||||
final SavingsAccountSummaryData summary = null;
|
||||
final BigDecimal onHoldFunds = null;
|
||||
|
||||
final SavingsAccountSubStatusEnumData subStatus = null;
|
||||
final LocalDate lastActiveTransactionDate = null;
|
||||
final boolean isDormancyTrackingActive = false;
|
||||
final Integer daysToInactive = null;
|
||||
final Integer daysToDormancy = null;
|
||||
final Integer daysToEscheat = null;
|
||||
|
||||
final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault();
|
||||
final EnumOptionData depositType = null;
|
||||
return SavingsAccountData.instance(null, null, depositType, null, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualIterestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary,
|
||||
allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation,
|
||||
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData);
|
||||
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency,
|
||||
nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
|
||||
summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1025,6 +1078,84 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> retrieveSavingsIdsPendingInactive(
|
||||
LocalDate tenantLocalDate) {
|
||||
List<Long> ret = null;
|
||||
StringBuilder sql = new StringBuilder("select sa.id ");
|
||||
sql.append(" from m_savings_account as sa ");
|
||||
sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) ");
|
||||
sql.append(" where sa.status_enum = 300 ");
|
||||
sql.append(" and sa.sub_status_enum = 0 ");
|
||||
sql.append(" and DATEDIFF(?,(select IFNULL(max(sat.transaction_date),sa.activatedon_date) ");
|
||||
sql.append(" from m_savings_account_transaction as sat ");
|
||||
sql.append(" where sat.is_reversed = 0 ");
|
||||
sql.append(" and sat.transaction_type_enum in (1,2) ");
|
||||
sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_inactive ");
|
||||
|
||||
try {
|
||||
ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] {formatter.print(tenantLocalDate)});
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
// ignore empty result scenario
|
||||
} catch (DataAccessException e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> retrieveSavingsIdsPendingDormant(
|
||||
LocalDate tenantLocalDate) {
|
||||
List<Long> ret = null;
|
||||
StringBuilder sql = new StringBuilder("select sa.id ");
|
||||
sql.append(" from m_savings_account as sa ");
|
||||
sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) ");
|
||||
sql.append(" where sa.status_enum = 300 ");
|
||||
sql.append(" and sa.sub_status_enum = 100 ");
|
||||
sql.append(" and DATEDIFF(?,(select IFNULL(max(sat.transaction_date),sa.activatedon_date) ");
|
||||
sql.append(" from m_savings_account_transaction as sat ");
|
||||
sql.append(" where sat.is_reversed = 0 ");
|
||||
sql.append(" and sat.transaction_type_enum in (1,2) ");
|
||||
sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_dormancy ");
|
||||
|
||||
try {
|
||||
ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] {formatter.print(tenantLocalDate)});
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
// ignore empty result scenario
|
||||
} catch (DataAccessException e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> retrieveSavingsIdsPendingEscheat(
|
||||
LocalDate tenantLocalDate) {
|
||||
List<Long> ret = null;
|
||||
StringBuilder sql = new StringBuilder("select sa.id ");
|
||||
sql.append(" from m_savings_account as sa ");
|
||||
sql.append(" inner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) ");
|
||||
sql.append(" where sa.status_enum = 300 ");
|
||||
sql.append(" and sa.sub_status_enum = 200 ");
|
||||
sql.append(" and DATEDIFF(?,(select IFNULL(max(sat.transaction_date),sa.activatedon_date) ");
|
||||
sql.append(" from m_savings_account_transaction as sat ");
|
||||
sql.append(" where sat.is_reversed = 0 ");
|
||||
sql.append(" and sat.transaction_type_enum in (1,2) ");
|
||||
sql.append(" and sat.savings_account_id = sa.id)) >= sp.days_to_escheat ");
|
||||
|
||||
try {
|
||||
ret = this.jdbcTemplate.queryForList(sql.toString(), Long.class, new Object[] {formatter.print(tenantLocalDate)});
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
// ignore empty result scenario
|
||||
} catch (DataAccessException e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* private static final class SavingsAccountAnnualFeeMapper implements
|
||||
* RowMapper<SavingsAccountAnnualFeeData> {
|
||||
|
||||
+6
@@ -81,4 +81,10 @@ public interface SavingsAccountWritePlatformService {
|
||||
void postInterest(SavingsAccount account);
|
||||
|
||||
CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, JsonCommand command);
|
||||
|
||||
void setSubStatusInactive(Long savingsId);
|
||||
|
||||
void setSubStatusDormant(Long savingsId);
|
||||
|
||||
void escheat(Long savingsId);
|
||||
}
|
||||
+38
-2
@@ -78,6 +78,7 @@ import org.apache.fineract.portfolio.savings.exception.SavingsOfficerAssignmentE
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsOfficerUnassignmentException;
|
||||
import org.apache.fineract.portfolio.savings.exception.TransactionUpdateNotAllowedException;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
@@ -130,6 +131,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
|
||||
private final AppUserRepositoryWrapper appuserRepository;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -148,7 +150,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final ChargeRepositoryWrapper chargeRepository, final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository,
|
||||
final SavingsAccountDataValidator fromApiJsonDeserializer, final SavingsAccountRepositoryWrapper savingsRepository,
|
||||
final StaffRepositoryWrapper staffRepository, final ConfigurationDomainService configurationDomainService,
|
||||
final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository) {
|
||||
final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository,
|
||||
final AppUserRepositoryWrapper appuserRepository) {
|
||||
this.context = context;
|
||||
this.savingAccountRepository = savingAccountRepository;
|
||||
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
|
||||
@@ -171,6 +174,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
this.staffRepository = staffRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository;
|
||||
this.appuserRepository = appuserRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -1223,7 +1227,39 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.build();
|
||||
}
|
||||
|
||||
private AppUser getAppUserIfPresent() {
|
||||
@Override
|
||||
@Transactional
|
||||
public void setSubStatusInactive(Long savingsId){
|
||||
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
|
||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
account.setSubStatusInactive(appuserRepository.fetchSystemUser());
|
||||
this.savingAccountRepository.save(account);
|
||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void setSubStatusDormant(Long savingsId){
|
||||
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
|
||||
account.setSubStatusDormant();
|
||||
this.savingAccountRepository.save(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void escheat(Long savingsId){
|
||||
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
|
||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
account.escheat(appuserRepository.fetchSystemUser());
|
||||
this.savingAccountRepository.save(account);
|
||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
|
||||
private AppUser getAppUserIfPresent() {
|
||||
AppUser user = null;
|
||||
if (this.context != null) {
|
||||
user = this.context.getAuthenticatedUserIfPresent();
|
||||
|
||||
+39
@@ -36,8 +36,10 @@ import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsWithdrawalFeesType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum;
|
||||
|
||||
public class SavingsEnumerations {
|
||||
|
||||
@@ -187,6 +189,10 @@ public class SavingsEnumerations {
|
||||
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.DIVIDEND_PAYOUT.getValue().longValue(),
|
||||
SavingsAccountTransactionType.DIVIDEND_PAYOUT.getCode(), "Dividend Payout");
|
||||
break;
|
||||
case ESCHEAT:
|
||||
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.ESCHEAT.getValue().longValue(),
|
||||
SavingsAccountTransactionType.ESCHEAT.getCode(), "Escheat");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
@@ -278,6 +284,39 @@ public class SavingsEnumerations {
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static SavingsAccountSubStatusEnumData subStatus(final Integer subStatusEnum) {
|
||||
return subStatus(SavingsAccountSubStatusEnum.fromInt(subStatusEnum));
|
||||
}
|
||||
|
||||
public static SavingsAccountSubStatusEnumData subStatus(final SavingsAccountSubStatusEnum type) {
|
||||
|
||||
final boolean none = type.isSubStatusNone();
|
||||
final boolean inactive = type.isSubStatusInactive();
|
||||
final boolean dormant = type.isSubStatusDormant();
|
||||
final boolean escheat = type.isSubStatusEscheat();
|
||||
|
||||
SavingsAccountSubStatusEnumData optionData = new SavingsAccountSubStatusEnumData(SavingsAccountSubStatusEnum.NONE.getValue().longValue(),
|
||||
SavingsAccountSubStatusEnum.NONE.getCode(), "None", true, inactive, dormant, escheat);
|
||||
|
||||
switch (type) {
|
||||
case INACTIVE:
|
||||
optionData = new SavingsAccountSubStatusEnumData(SavingsAccountSubStatusEnum.INACTIVE.getValue().longValue(),
|
||||
SavingsAccountSubStatusEnum.INACTIVE.getCode(), "Inactive", none, inactive, dormant, escheat);
|
||||
break;
|
||||
case DORMANT:
|
||||
optionData = new SavingsAccountSubStatusEnumData(SavingsAccountSubStatusEnum.DORMANT.getValue().longValue(),
|
||||
SavingsAccountSubStatusEnum.DORMANT.getCode(), "Dormant", none, inactive, dormant, escheat);
|
||||
break;
|
||||
case ESCHEAT:
|
||||
optionData = new SavingsAccountSubStatusEnumData(SavingsAccountSubStatusEnum.ESCHEAT.getValue().longValue(),
|
||||
SavingsAccountSubStatusEnum.ESCHEAT.getCode(), "Escheat", none, inactive, dormant, escheat);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData interestPostingPeriodType(final Integer type) {
|
||||
return interestPostingPeriodType(SavingsPostingInterestPeriodType.fromInt(type));
|
||||
}
|
||||
|
||||
+12
-3
@@ -134,7 +134,11 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
|
||||
sqlBuilder.append("sp.accounting_type as accountingType, ");
|
||||
sqlBuilder.append("sp.withhold_tax as withHoldTax,");
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
|
||||
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("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 ");
|
||||
@@ -210,13 +214,18 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
|
||||
final Boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive");
|
||||
final Long daysToInactive = JdbcSupport.getLong(rs, "daysToInactive");
|
||||
final Long daysToDormancy = JdbcSupport.getLong(rs, "daysToDormancy");
|
||||
final Long daysToEscheat = JdbcSupport.getLong(rs, "daysToEscheat");
|
||||
|
||||
return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
|
||||
accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroupData);
|
||||
taxGroupData, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -145,11 +145,11 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
this.fromApiJsonDataValidator.validateForUpdate(command.json());
|
||||
|
||||
final SavingsProduct product = this.savingProductRepository.findOne(productId);
|
||||
if (product == null) { throw new SavingsProductNotFoundException(productId); }
|
||||
|
||||
this.fromApiJsonDataValidator.validateForUpdate(command.json(), product);
|
||||
|
||||
final Map<String, Object> changes = product.update(command);
|
||||
|
||||
if (changes.containsKey(chargesParamName)) {
|
||||
|
||||
+2
@@ -24,5 +24,7 @@ import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||
public interface SavingsSchedularService {
|
||||
|
||||
void postInterestForAccounts() throws JobExecutionException;
|
||||
|
||||
void updateSavingsDormancyStatus() throws JobExecutionException;
|
||||
|
||||
}
|
||||
|
||||
+36
-1
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
|
||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||
@@ -27,6 +28,7 @@ import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -36,14 +38,17 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
|
||||
private final SavingsAccountAssembler savingAccountAssembler;
|
||||
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
|
||||
private final SavingsAccountRepository savingAccountRepository;
|
||||
private final SavingsAccountReadPlatformService savingAccountReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsSchedularServiceImpl(final SavingsAccountAssembler savingAccountAssembler,
|
||||
final SavingsAccountWritePlatformService savingsAccountWritePlatformService,
|
||||
final SavingsAccountRepository savingAccountRepository) {
|
||||
final SavingsAccountRepository savingAccountRepository,
|
||||
final SavingsAccountReadPlatformService savingAccountReadPlatformService) {
|
||||
this.savingAccountAssembler = savingAccountAssembler;
|
||||
this.savingsAccountWritePlatformService = savingsAccountWritePlatformService;
|
||||
this.savingAccountRepository = savingAccountRepository;
|
||||
this.savingAccountReadPlatformService = savingAccountReadPlatformService;
|
||||
}
|
||||
|
||||
@CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS)
|
||||
@@ -68,4 +73,34 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
|
||||
|
||||
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
||||
}
|
||||
|
||||
@CronTarget(jobName = JobName.UPDATE_SAVINGS_DORMANT_ACCOUNTS)
|
||||
@Override
|
||||
public void updateSavingsDormancyStatus() throws JobExecutionException {
|
||||
final LocalDate tenantLocalDate = DateUtils.getLocalDateOfTenant();
|
||||
|
||||
final List<Long> savingsPendingInactive = this.savingAccountReadPlatformService
|
||||
.retrieveSavingsIdsPendingInactive(tenantLocalDate);
|
||||
if(null != savingsPendingInactive && savingsPendingInactive.size() > 0){
|
||||
for(Long savingsId : savingsPendingInactive){
|
||||
this.savingsAccountWritePlatformService.setSubStatusInactive(savingsId);
|
||||
}
|
||||
}
|
||||
|
||||
final List<Long> savingsPendingDormant = this.savingAccountReadPlatformService
|
||||
.retrieveSavingsIdsPendingDormant(tenantLocalDate);
|
||||
if(null != savingsPendingDormant && savingsPendingDormant.size() > 0){
|
||||
for(Long savingsId : savingsPendingDormant){
|
||||
this.savingsAccountWritePlatformService.setSubStatusDormant(savingsId);
|
||||
}
|
||||
}
|
||||
|
||||
final List<Long> savingsPendingEscheat = this.savingAccountReadPlatformService
|
||||
.retrieveSavingsIdsPendingEscheat(tenantLocalDate);
|
||||
if(null != savingsPendingEscheat && savingsPendingEscheat.size() > 0){
|
||||
for(Long savingsId : savingsPendingEscheat){
|
||||
this.savingsAccountWritePlatformService.escheat(savingsId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
ALTER TABLE `m_savings_product`
|
||||
ADD COLUMN `is_dormancy_tracking_active` SMALLINT(1) NULL,
|
||||
ADD COLUMN `days_to_inactive` INT(11) NULL,
|
||||
ADD COLUMN `days_to_dormancy` INT(11) NULL,
|
||||
ADD COLUMN `days_to_escheat` INT(11) NULL;
|
||||
|
||||
ALTER TABLE `m_savings_account`
|
||||
ADD COLUMN `sub_status_enum` SMALLINT(5) NOT NULL DEFAULT '0' AFTER `status_enum`;
|
||||
|
||||
INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`) VALUES ('Update Savings Dormant Accounts', 'Update Savings Dormant Accounts', '0 0 0 1/1 * ? *', now());
|
||||
|
||||
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_category`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Savings Accounts Dormancy Report', 'Table', 'Savings', 'select cl.display_name as \'Client Display Name\',\r\nsa.account_no as \'Account Number\',\r\ncl.mobile_no as \'Mobile Number\',\r\n@lastdate:=(select IFNULL(max(sat.transaction_date),sa.activatedon_date) \r\n from m_savings_account_transaction as sat \r\n where sat.is_reversed = 0 \r\n and sat.transaction_type_enum in (1,2) \r\n and sat.savings_account_id = sa.id) as \'Date of Last Activity\',\r\nDATEDIFF(now(), @lastdate) as \'Days Since Last Activity\'\r\nfrom m_savings_account as sa \r\ninner join m_savings_product as sp on (sa.product_id = sp.id and sp.is_dormancy_tracking_active = 1) \r\nleft join m_client as cl on sa.client_id = cl.id \r\nwhere sa.sub_status_enum = ${subStatus}\r\nand cl.office_id = ${officeId}', NULL, 1, 1);
|
||||
|
||||
INSERT INTO `stretchy_parameter` (`parameter_name`, `parameter_variable`, `parameter_label`, `parameter_displayType`, `parameter_FormatType`, `parameter_default`, `parameter_sql`) VALUES ('SavingsAccountSubStatus', 'subStatus', 'SavingsAccountDormancyStatus', 'select', 'number', '100', 'select * from\r\n(select 100 as id, "Inactive" as name union all\r\nselect 200 as id, "Dormant" as name union all \r\nselect 300 as id, "Escheat" as name) x\r\norder by x.`id`');
|
||||
|
||||
INSERT INTO stretchy_report_parameter (report_id, parameter_id) VALUES ((select sr.id From stretchy_report sr where sr.report_name='Savings Accounts Dormancy Report'),(select sp.id from stretchy_parameter sp where sp.parameter_name='SavingsAccountSubStatus'));
|
||||
|
||||
INSERT INTO stretchy_report_parameter (report_id, parameter_id) VALUES ((select sr.id From stretchy_report sr where sr.report_name='Savings Accounts Dormancy Report'),(select sp.id from stretchy_parameter sp where sp.parameter_name='OfficeIdSelectOne'));
|
||||
Reference in New Issue
Block a user