diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java index 42d1bf828..127592f4a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java @@ -82,10 +82,10 @@ public enum SavingsAccountTransactionType { case 15: savingsAccountTransactionType = SavingsAccountTransactionType.REJECT_TRANSFER; break; - case 18: + case 16: savingsAccountTransactionType = SavingsAccountTransactionType.WRITTEN_OFF; break; - case 19: + case 17: savingsAccountTransactionType = SavingsAccountTransactionType.OVERDRAFT_INTEREST; break; } @@ -149,7 +149,7 @@ public enum SavingsAccountTransactionType { } public boolean isDebit() { - return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge(); + return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge() || isIncomeFromInterest(); } public boolean isCredit() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index f2e368273..3090c7e44 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -97,6 +97,8 @@ public class SavingsApiConstants { public static final String bankNumberParamName = "bankNumber"; public static final String allowOverdraftParamName = "allowOverdraft"; public static final String overdraftLimitParamName = "overdraftLimit"; + public static final String nominalAnnualInterestRateOverdraftParamName = "nominalAnnualInterestRateOverdraft"; + public static final String minOverdraftForInterestCalculationParamName = "minOverdraftForInterestCalculation"; public static final String minRequiredBalanceParamName = "minRequiredBalance"; public static final String enforceMinRequiredBalanceParamName = "enforceMinRequiredBalance"; public static final String minBalanceForInterestCalculationParamName = "minBalanceForInterestCalculation"; @@ -162,6 +164,7 @@ public class SavingsApiConstants { 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, + nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName, minBalanceForInterestCalculationParamName)); /** @@ -176,7 +179,8 @@ public class SavingsApiConstants { lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, "currencyOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", - "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions")); + "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", + nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName)); public static final Set SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, dateFormatParamName, monthDayFormatParamName, staffIdParamName, accountNoParamName, externalIdParamName, clientIdParamName, @@ -186,7 +190,8 @@ public class SavingsApiConstants { lockinPeriodFrequencyTypeParamName, // withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, chargesParamName, allowOverdraftParamName, - overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName)); + overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName, + nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName)); /** * These parameters will match the class level parameters of @@ -201,7 +206,8 @@ public class SavingsApiConstants { withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, "summary", "transactions", "productOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", - "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", "withdrawalFee", "annualFee", onHoldFundsParamName)); + "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", "withdrawalFee", "annualFee", onHoldFundsParamName, + nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName)); public static final Set SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName, paymentTypeIdParamName, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java index 24e8ad231..87e55c741 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java @@ -74,6 +74,8 @@ public class SavingsAccountData { private final SavingsAccountChargeData withdrawalFee; @SuppressWarnings("unused") private final SavingsAccountChargeData annualFee; + private final BigDecimal nominalAnnualInterestRateOverdraft; + private final BigDecimal minOverdraftForInterestCalculation; 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, @@ -85,7 +87,8 @@ public class SavingsAccountData { 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 minBalanceForInterestCalculation, final BigDecimal onHoldFunds, + final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) { final Collection productOptions = null; final Collection fieldOfficerOptions = null; @@ -107,7 +110,8 @@ public class SavingsAccountData { fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, - enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds); + enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } public static SavingsAccountData lookup(final Long accountId, final String accountNo, final EnumOptionData depositType) { @@ -140,6 +144,8 @@ public class SavingsAccountData { // final LocalDate annualFeeNextDueDate = null; final boolean allowOverdraft = false; final BigDecimal overdraftLimit = null; + final BigDecimal nominalAnnualInterestRateOverdraft = null; + final BigDecimal minOverdraftForInterestCalculation = null; final BigDecimal minRequiredBalance = null; final boolean enforceMinRequiredBalance = false; final BigDecimal minBalanceForInterestCalculation = null; @@ -167,7 +173,8 @@ public class SavingsAccountData { fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, - enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds); + enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo, @@ -198,6 +205,8 @@ public class SavingsAccountData { // final LocalDate annualFeeNextDueDate = null; final boolean allowOverdraft = false; final BigDecimal overdraftLimit = null; + final BigDecimal nominalAnnualInterestRateOverdraft = null; + final BigDecimal minOverdraftForInterestCalculation = null; final BigDecimal minRequiredBalance = null; final boolean enforceMinRequiredBalance = false; final BigDecimal minBalanceForInterestCalculation = null; @@ -225,7 +234,8 @@ public class SavingsAccountData { fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, - enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds); + enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template, @@ -258,7 +268,7 @@ public class SavingsAccountData { template.interestCalculationDaysInYearTypeOptions, template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions, account.allowOverdraft, account.overdraftLimit, account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation, - account.onHoldFunds); + account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation); } public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, @@ -281,7 +291,7 @@ public class SavingsAccountData { interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, account.allowOverdraft, account.overdraftLimit, account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation, - account.onHoldFunds); + account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation); } public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, @@ -314,6 +324,8 @@ public class SavingsAccountData { // final LocalDate annualFeeNextDueDate = null; final boolean allowOverdraft = false; final BigDecimal overdraftLimit = null; + final BigDecimal nominalAnnualInterestRateOverdraft = null; + final BigDecimal minOverdraftForInterestCalculation = null; final BigDecimal minRequiredBalance = null; final boolean enforceMinRequiredBalance = false; final BigDecimal minBalanceForInterestCalculation = null; @@ -341,7 +353,8 @@ public class SavingsAccountData { fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, - enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds); + enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } private SavingsAccountData(final Long id, final String accountNo, final EnumOptionData depositType, final String externalId, @@ -361,7 +374,8 @@ public class SavingsAccountData { final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, final Collection charges, final Collection chargeOptions, final boolean allowOverdraft, final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, - final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds) { + final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds, + final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) { this.id = id; this.accountNo = accountNo; this.depositType = depositType; @@ -413,6 +427,8 @@ public class SavingsAccountData { this.annualFee = getAnnualFee(); this.allowOverdraft = allowOverdraft; this.overdraftLimit = overdraftLimit; + this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft; + this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation; this.minRequiredBalance = minRequiredBalance; this.enforceMinRequiredBalance = enforceMinRequiredBalance; this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountDataValidator.java index e65f1ea36..8acf397f2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountDataValidator.java @@ -25,6 +25,8 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeri import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; @@ -389,6 +391,16 @@ public class SavingsAccountDataValidator { baseDataValidator.reset().parameter(overdraftLimitParamName).value(overdraftLimit).ignoreIfNull().zeroOrPositiveAmount(); } + if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateOverdraftParamName, element)) { + final BigDecimal nominalAnnualInterestRateOverdraft = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateOverdraftParamName, element); + baseDataValidator.reset().parameter(nominalAnnualInterestRateOverdraftParamName).value(nominalAnnualInterestRateOverdraft).ignoreIfNull().zeroOrPositiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(minOverdraftForInterestCalculationParamName, element)) { + final BigDecimal minOverdraftForInterestCalculation = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minOverdraftForInterestCalculationParamName, element); + baseDataValidator.reset().parameter(minOverdraftForInterestCalculationParamName).value(minOverdraftForInterestCalculation).ignoreIfNull().zeroOrPositiveAmount(); + } + } public void validateForAssignSavingsOfficer(final String json) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountSummaryData.java index d2730da62..d4e403cfa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountSummaryData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountSummaryData.java @@ -25,11 +25,12 @@ public class SavingsAccountSummaryData { private final BigDecimal accountBalance; private final BigDecimal totalFeeCharge; private final BigDecimal totalPenaltyCharge; + private final BigDecimal totalOverdraftInterestDerived; public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals, final BigDecimal totalWithdrawalFees, final BigDecimal totalAnnualFees, final BigDecimal totalInterestEarned, final BigDecimal totalInterestPosted, final BigDecimal accountBalance, final BigDecimal totalFeeCharge, - final BigDecimal totalPenaltyCharge) { + final BigDecimal totalPenaltyCharge, final BigDecimal totalOverdraftInterestDerived) { this.currency = currency; this.totalDeposits = totalDeposits; this.totalWithdrawals = totalWithdrawals; @@ -40,5 +41,6 @@ public class SavingsAccountSummaryData { this.accountBalance = accountBalance; this.totalFeeCharge = totalFeeCharge; this.totalPenaltyCharge = totalPenaltyCharge; + this.totalOverdraftInterestDerived = totalOverdraftInterestDerived; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java index 112e2dba8..0afbf84b0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java @@ -67,6 +67,8 @@ public class SavingsProductData { private final Map> accountingMappingOptions; private final Collection chargeOptions; private final Collection penaltyOptions; + private final BigDecimal nominalAnnualInterestRateOverdraft; + private final BigDecimal minOverdraftForInterestCalculation; public static SavingsProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, @@ -99,6 +101,9 @@ public class SavingsProductData { final BigDecimal minRequiredBalance = null; final boolean enforceMinRequiredBalance = false; final BigDecimal minBalanceForInterestCalculation = null; + final BigDecimal nominalAnnualInterestRateOverdraft = null; + final BigDecimal minOverdraftForInterestCalculation = null; + return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, @@ -108,7 +113,7 @@ public class SavingsProductData { lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } public static SavingsProductData withCharges(final SavingsProductData product, final Collection charges) { @@ -122,7 +127,8 @@ public class SavingsProductData { product.lockinPeriodFrequencyTypeOptions, product.withdrawalFeeTypeOptions, product.paymentTypeOptions, product.accountingRuleOptions, product.accountingMappingOptions, charges, product.chargeOptions, product.penaltyOptions, product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings, product.allowOverdraft, product.overdraftLimit, - product.minRequiredBalance, product.enforceMinRequiredBalance, product.minBalanceForInterestCalculation); + product.minRequiredBalance, product.enforceMinRequiredBalance, product.minBalanceForInterestCalculation, + product.nominalAnnualInterestRateOverdraft, product.minOverdraftForInterestCalculation); } /** @@ -151,7 +157,8 @@ public class SavingsProductData { accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, existingProduct.allowOverdraft, existingProduct.overdraftLimit, existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance, - existingProduct.minBalanceForInterestCalculation); + existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft, + existingProduct.minOverdraftForInterestCalculation); } public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct, @@ -185,7 +192,8 @@ public class SavingsProductData { accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, existingProduct.allowOverdraft, existingProduct.overdraftLimit, existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance, - existingProduct.minBalanceForInterestCalculation); + existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft, + existingProduct.minOverdraftForInterestCalculation); } public static SavingsProductData instance(final Long id, final String name, final String shortName, final String description, @@ -194,7 +202,8 @@ public class SavingsProductData { final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, final EnumOptionData accountingType, final boolean allowOverdraft, final BigDecimal overdraftLimit, - final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal minBalanceForInterestCalculation, + final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) { final Map accountingMappings = null; final Collection paymentChannelToFundSourceMappings = null; @@ -223,7 +232,7 @@ public class SavingsProductData { lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } public static SavingsProductData lookup(final Long id, final String name) { @@ -245,6 +254,8 @@ public class SavingsProductData { final Collection paymentChannelToFundSourceMappings = null; final boolean allowOverdraft = false; final BigDecimal overdraftLimit = null; + final BigDecimal nominalAnnualInterestRateOverdraft = null; + final BigDecimal minOverdraftForInterestCalculation = null; final BigDecimal minRequiredBalance = null; final boolean enforceMinRequiredBalance = false; final BigDecimal minBalanceForInterestCalculation = null; @@ -273,7 +284,7 @@ public class SavingsProductData { lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } private SavingsProductData(final Long id, final String name, final String shortName, final String description, @@ -294,7 +305,8 @@ public class SavingsProductData { final Collection feeToIncomeAccountMappings, final Collection penaltyToIncomeAccountMappings, final boolean allowOverdraft, final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, - final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minBalanceForInterestCalculation, + final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) { this.id = id; this.name = name; this.shortName = shortName; @@ -342,6 +354,8 @@ public class SavingsProductData { this.minRequiredBalance = minRequiredBalance; this.enforceMinRequiredBalance = enforceMinRequiredBalance; this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; + this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft; + this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation; } public boolean hasAccountingEnabled() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java index 4f89d547f..193d13e5b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java @@ -25,6 +25,8 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.shortNameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; @@ -537,6 +539,16 @@ public class SavingsProductDataValidator { baseDataValidator.reset().parameter(overdraftLimitParamName).value(overdraftLimit).ignoreIfNull().zeroOrPositiveAmount(); } + if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateOverdraftParamName, element)) { + final BigDecimal nominalAnnualInterestRateOverdraft = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateOverdraftParamName, element); + baseDataValidator.reset().parameter(nominalAnnualInterestRateOverdraftParamName).value(nominalAnnualInterestRateOverdraft).ignoreIfNull().zeroOrPositiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(minOverdraftForInterestCalculationParamName, element)) { + final BigDecimal minOverdraftForInterestCalculation = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minOverdraftForInterestCalculationParamName, element); + baseDataValidator.reset().parameter(minOverdraftForInterestCalculationParamName).value(minOverdraftForInterestCalculation).ignoreIfNull().zeroOrPositiveAmount(); + } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 0fa7a0ab1..55ddfdb70 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -5,7 +5,54 @@ */ package org.mifosplatform.portfolio.savings.domain; -import com.google.gson.JsonArray; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.allowOverdraftParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Transient; +import javax.persistence.UniqueConstraint; +import javax.persistence.Version; + import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.hibernate.annotations.LazyCollection; @@ -51,50 +98,7 @@ import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.util.CollectionUtils; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.DiscriminatorColumn; -import javax.persistence.DiscriminatorType; -import javax.persistence.DiscriminatorValue; -import javax.persistence.Embedded; -import javax.persistence.Entity; -import javax.persistence.Inheritance; -import javax.persistence.InheritanceType; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OrderBy; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; -import java.math.BigDecimal; -import java.math.MathContext; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.allowOverdraftParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; +import com.google.gson.JsonArray; @Entity @Table(name = "m_savings_account", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "sa_account_no_UNIQUE"), @@ -245,6 +249,12 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "overdraft_limit", scale = 6, precision = 19, nullable = true) private BigDecimal overdraftLimit; + @Column(name = "nominal_annual_interest_rate_overdraft", scale = 6, precision = 19, nullable = true) + protected BigDecimal nominalAnnualInterestRateOverdraft; + + @Column(name = "min_overdraft_for_interest_calculation", scale = 6, precision = 19, nullable = true) + private BigDecimal minOverdraftForInterestCalculation; + @Column(name = "enforce_min_required_balance") private boolean enforceMinRequiredBalance; @@ -300,14 +310,15 @@ public class SavingsAccount extends AbstractPersistable { final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final boolean withdrawalFeeApplicableForTransfer, final Set savingsAccountCharges, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, - final BigDecimal minRequiredBalance) { + final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft, + final BigDecimal minOverdraftForInterestCalculation) { final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL; return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, enforceMinRequiredBalance, - minRequiredBalance); + minRequiredBalance, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer, @@ -322,7 +333,7 @@ public class SavingsAccount extends AbstractPersistable { this(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, - withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null); + withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null, null, null); } protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff savingsOfficer, @@ -334,7 +345,8 @@ public class SavingsAccount extends AbstractPersistable { final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final boolean withdrawalFeeApplicableForTransfer, final Set savingsAccountCharges, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, - final BigDecimal minRequiredBalance) { + final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft, + final BigDecimal minOverdraftForInterestCalculation) { this.client = client; this.group = group; this.product = product; @@ -371,6 +383,8 @@ public class SavingsAccount extends AbstractPersistable { this.summary = new SavingsAccountSummary(); this.allowOverdraft = allowOverdraft; this.overdraftLimit = overdraftLimit; + this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft; + this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation; esnureOverdraftLimitsSetForOverdraftAccounts(); this.enforceMinRequiredBalance = enforceMinRequiredBalance; @@ -443,16 +457,33 @@ public class SavingsAccount extends AbstractPersistable { final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); if (postingTransaction == null) { - final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), - interestPostingTransactionDate, interestEarnedToBePostedForPeriod); + SavingsAccountTransaction newPostingTransaction; + if(interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))){ + newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), + interestPostingTransactionDate, interestEarnedToBePostedForPeriod); + }else{ + newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), + interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated()); + } this.transactions.add(newPostingTransaction); recalucateDailyBalanceDetails = true; } else { - final boolean correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod); + boolean correctionRequired = false; + if(postingTransaction.isInterestPostingAndNotReversed()){ + correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod); + }else{ + correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod.negated()); + } if (correctionRequired) { postingTransaction.reverse(); - final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), - interestPostingTransactionDate, interestEarnedToBePostedForPeriod); + SavingsAccountTransaction newPostingTransaction; + if(interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))){ + newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), + interestPostingTransactionDate, interestEarnedToBePostedForPeriod); + }else{ + newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), + interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated()); + } this.transactions.add(newPostingTransaction); recalucateDailyBalanceDetails = true; } @@ -479,7 +510,7 @@ public class SavingsAccount extends AbstractPersistable { SavingsAccountTransaction postingTransation = null; for (final SavingsAccountTransaction transaction : this.transactions) { - if (transaction.isInterestPostingAndNotReversed() && transaction.occursOn(postingDate)) { + if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed()) && transaction.occursOn(postingDate)) { postingTransation = transaction; break; } @@ -575,15 +606,18 @@ public class SavingsAccount extends AbstractPersistable { final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, upToInterestCalculationDate); + final BigDecimal overdraftInterestRateAsFraction = getEffectiveOverdraftInterestRateAsFraction(mc); final Collection interestPostTransactions = this.savingsHelper.fetchPostInterestTransactionIds(getId()); final Money minBalanceForInterestCalculation = Money.of(getCurrency(), minBalanceForInterestCalculation()); + final Money minOverdraftForInterestCalculation = Money.of(getCurrency(), this.minOverdraftForInterestCalculation); for (final LocalDateInterval periodInterval : postingPeriodIntervals) { final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate, interestPostTransactions, - isInterestTransfer, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd); + isInterestTransfer, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd, + overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); periodStartingBalance = postingPeriod.closingBalance(); @@ -599,7 +633,11 @@ public class SavingsAccount extends AbstractPersistable { return allPostingPeriods; } - @SuppressWarnings("unused") + private BigDecimal getEffectiveOverdraftInterestRateAsFraction(MathContext mc) { + return this.nominalAnnualInterestRateOverdraft.divide(BigDecimal.valueOf(100l), mc); + } + + @SuppressWarnings("unused") protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate upToInterestCalculationDate) { return this.nominalAnnualInterestRate.divide(BigDecimal.valueOf(100l), mc); } @@ -610,7 +648,7 @@ public class SavingsAccount extends AbstractPersistable { final List orderedNonInterestPostingTransactions = new ArrayList<>(); for (final SavingsAccountTransaction transaction : listOfTransactionsSorted) { - if (!transaction.isInterestPostingAndNotReversed() && transaction.isNotReversed()) { + if (!(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed()) && transaction.isNotReversed()) { orderedNonInterestPostingTransactions.add(transaction); } } @@ -689,7 +727,9 @@ public class SavingsAccount extends AbstractPersistable { LocalDate endOfBalanceDate = interestPostingUpToDate; for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) { final SavingsAccountTransaction transaction = accountTransactionsSorted.get(i); - if (transaction.isNotReversed() && !transaction.isInterestPostingAndNotReversed()) { + if (transaction.isNotReversed() + && !(transaction.isInterestPostingAndNotReversed() + || transaction.isOverdraftInterestAndNotReversed())) { transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate); // this transactions transaction date is end of balance date for // previous transaction. @@ -848,7 +888,7 @@ public class SavingsAccount extends AbstractPersistable { boolean transactionBeforeLastInterestPosting = false; for (final SavingsAccountTransaction transaction : retreiveListOfTransactions()) { - if (transaction.isInterestPostingAndNotReversed() && transaction.isAfter(transactionDate)) { + if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed()) && transaction.isAfter(transactionDate)) { transactionBeforeLastInterestPosting = true; break; } @@ -1132,8 +1172,24 @@ public class SavingsAccount extends AbstractPersistable { this.overdraftLimit = newValue; } + if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(nominalAnnualInterestRateOverdraftParamName, this.nominalAnnualInterestRateOverdraft)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName); + actualChanges.put(nominalAnnualInterestRateOverdraftParamName, newValue); + actualChanges.put(localeParamName, localeAsInput); + this.nominalAnnualInterestRateOverdraft = newValue; + } + + if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minOverdraftForInterestCalculationParamName, this.minOverdraftForInterestCalculation)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName); + actualChanges.put(minOverdraftForInterestCalculationParamName, newValue); + actualChanges.put(localeParamName, localeAsInput); + this.minOverdraftForInterestCalculation = newValue; + } + if (!this.allowOverdraft) { this.overdraftLimit = null; + this.nominalAnnualInterestRateOverdraft = null; + this.minOverdraftForInterestCalculation = null; } if (command.isChangeInBooleanParameterNamed(enforceMinRequiredBalanceParamName, this.enforceMinRequiredBalance)) { @@ -1159,8 +1215,10 @@ public class SavingsAccount extends AbstractPersistable { **/ private void esnureOverdraftLimitsSetForOverdraftAccounts() { - if (this.allowOverdraft && this.overdraftLimit == null) { - this.overdraftLimit = BigDecimal.ZERO; + if (this.allowOverdraft) { + this.overdraftLimit = this.overdraftLimit == null? BigDecimal.ZERO : this.overdraftLimit; + this.nominalAnnualInterestRateOverdraft = this.nominalAnnualInterestRateOverdraft == null? BigDecimal.ZERO : this.nominalAnnualInterestRateOverdraft; + this.minOverdraftForInterestCalculation = this.minOverdraftForInterestCalculation == null? BigDecimal.ZERO : this.minOverdraftForInterestCalculation; } } @@ -1531,6 +1589,10 @@ public class SavingsAccount extends AbstractPersistable { return this.nominalAnnualInterestRate; } + public BigDecimal getNominalAnnualInterestRateOverdraft() { + return this.nominalAnnualInterestRateOverdraft; + } + public Map approveApplication(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) { final Map actualChanges = new LinkedHashMap<>(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index 59bb61336..3d550e2ac 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -23,6 +23,8 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; @@ -223,6 +225,20 @@ public class SavingsAccountAssembler { overdraftLimit = product.overdraftLimit(); } + BigDecimal nominalAnnualInterestRateOverdraft = BigDecimal.ZERO; + if (command.parameterExists(nominalAnnualInterestRateOverdraftParamName)) { + nominalAnnualInterestRateOverdraft = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName); + } else { + nominalAnnualInterestRateOverdraft = product.nominalAnnualInterestRateOverdraft(); + } + + BigDecimal minOverdraftForInterestCalculation = BigDecimal.ZERO; + if (command.parameterExists(minOverdraftForInterestCalculationParamName)) { + minOverdraftForInterestCalculation = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName); + } else { + minOverdraftForInterestCalculation = product.minOverdraftForInterestCalculation(); + } + boolean enforceMinRequiredBalance = false; if (command.parameterExists(enforceMinRequiredBalanceParamName)) { enforceMinRequiredBalance = command.booleanPrimitiveValueOfParameterNamed(enforceMinRequiredBalanceParamName); @@ -241,7 +257,8 @@ public class SavingsAccountAssembler { externalId, accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges, allowOverdraft, - overdraftLimit, enforceMinRequiredBalance, minRequiredBalance); + overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME); @@ -295,7 +312,8 @@ public class SavingsAccountAssembler { product.interestPostingPeriodType(), product.interestCalculationType(), product.interestCalculationDaysInYearType(), product.minRequiredOpeningBalance(), product.lockinPeriodFrequency(), product.lockinPeriodFrequencyType(), product.isWithdrawalFeeApplicableForTransfer(), charges, product.isAllowOverdraft(), product.overdraftLimit(), - product.isMinRequiredBalanceEnforced(), product.minRequiredBalance()); + product.isMinRequiredBalanceEnforced(), product.minRequiredBalance(), product.nominalAnnualInterestRateOverdraft(), + product.minOverdraftForInterestCalculation()); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java index 7445eccc5..02116818b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java @@ -57,6 +57,9 @@ public final class SavingsAccountSummary { @Transient private BigDecimal totalPenaltyChargesWaived = BigDecimal.ZERO; + @Column(name = "total_overdraft_interest_derived", scale = 6, precision = 19) + private BigDecimal totalOverdraftInterestDerived; + protected SavingsAccountSummary() { // } @@ -73,10 +76,11 @@ public final class SavingsAccountSummary { this.totalPenaltyCharge = wrapper.calculateTotalPenaltyCharge(currency, transactions); this.totalFeeChargesWaived = wrapper.calculateTotalFeesChargeWaived(currency, transactions); this.totalPenaltyChargesWaived = wrapper.calculateTotalPenaltyChargeWaived(currency, transactions); + this.totalOverdraftInterestDerived = wrapper.calculateTotalOverdraftInterest(currency, transactions); this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals) .minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).minus(this.totalFeeCharge).minus(this.totalPenaltyCharge) - .getAmount(); + .minus(totalOverdraftInterestDerived).getAmount(); } public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, final List allPostingPeriods) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index 0b0ba4b59..bc45a2b45 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -129,7 +129,14 @@ public final class SavingsAccountTransaction extends AbstractPersistable { amount, isReversed, null); } - public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date, + public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office, final LocalDate date, + final Money amount) { + final boolean isReversed = false; + return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date, + amount, isReversed, null); + } + + public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date, final Money amount, final AppUser appUser) { final boolean isReversed = false; return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount, @@ -505,10 +512,18 @@ public final class SavingsAccountTransaction extends AbstractPersistable { } public boolean isDebit() { - return isWithdrawal() || isWithdrawalFeeAndNotReversed() || isAnnualFeeAndNotReversed() || isPayCharge(); + return isWithdrawal() + || isWithdrawalFeeAndNotReversed() + || isAnnualFeeAndNotReversed() + || isPayCharge() + || isOverdraftInterestAndNotReversed(); } - public boolean isPayCharge() { + public boolean isOverdraftInterestAndNotReversed() { + return SavingsAccountTransactionType.fromInt(this.typeOf).isIncomeFromInterest() && isNotReversed(); + } + + public boolean isPayCharge() { return SavingsAccountTransactionType.fromInt(this.typeOf).isPayCharge(); } @@ -608,4 +623,5 @@ public final class SavingsAccountTransaction extends AbstractPersistable { public BigDecimal getAmount() { return this.amount; } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java index d8fd2babb..bc930d9a8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java @@ -109,4 +109,15 @@ public final class SavingsAccountTransactionSummaryWrapper { return total.getAmountDefaultedToNullIfZero(); } + public BigDecimal calculateTotalOverdraftInterest(MonetaryCurrency currency, + List transactions) { + Money total = Money.zero(currency); + for (final SavingsAccountTransaction transaction : transactions) { + if (transaction.isOverdraftInterestAndNotReversed()) { + total = total.plus(transaction.getAmount(currency)); + } + } + return total.getAmountDefaultedToNullIfZero(); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java index 5dac8b733..5c71f8faa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java @@ -27,6 +27,8 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.shortNameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; @@ -150,6 +152,12 @@ public class SavingsProduct extends AbstractPersistable { @Column(name = "overdraft_limit", scale = 6, precision = 19, nullable = true) private BigDecimal overdraftLimit; + @Column(name = "nominal_annual_interest_rate_overdraft", scale = 6, precision = 19, nullable = true) + private BigDecimal nominalAnnualInterestRateOverdraft; + + @Column(name = "min_overdraft_for_interest_calculation", scale = 6, precision = 19, nullable = true) + private BigDecimal minOverdraftForInterestCalculation; + @Column(name = "enforce_min_required_balance") private boolean enforceMinRequiredBalance; @@ -167,12 +175,14 @@ public class SavingsProduct extends AbstractPersistable { final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, - final BigDecimal minRequiredBalance, final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minRequiredBalance, final BigDecimal minBalanceForInterestCalculation, + final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) { return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, - allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation); + allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation, + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } protected SavingsProduct() { @@ -190,7 +200,7 @@ public class SavingsProduct extends AbstractPersistable { this(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, - false, null, minBalanceForInterestCalculation); + false, null, minBalanceForInterestCalculation, null, null); } protected SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency, @@ -200,7 +210,8 @@ public class SavingsProduct extends AbstractPersistable { final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, - final BigDecimal minRequiredBalance, BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minRequiredBalance, BigDecimal minBalanceForInterestCalculation, + final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) { this.name = name; this.shortName = shortName; @@ -235,6 +246,8 @@ public class SavingsProduct extends AbstractPersistable { validateLockinDetails(); this.allowOverdraft = allowOverdraft; this.overdraftLimit = overdraftLimit; + this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft; + this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation; esnureOverdraftLimitsSetForOverdraftAccounts(); @@ -249,8 +262,10 @@ public class SavingsProduct extends AbstractPersistable { **/ private void esnureOverdraftLimitsSetForOverdraftAccounts() { - if (this.allowOverdraft && this.overdraftLimit() == null) { - this.overdraftLimit = BigDecimal.ZERO; + if (this.allowOverdraft) { + this.overdraftLimit = this.overdraftLimit == null? BigDecimal.ZERO : this.overdraftLimit; + this.nominalAnnualInterestRateOverdraft = this.nominalAnnualInterestRateOverdraft == null? BigDecimal.ZERO : this.nominalAnnualInterestRateOverdraft; + this.minOverdraftForInterestCalculation = this.minOverdraftForInterestCalculation == null? BigDecimal.ZERO : this.minOverdraftForInterestCalculation; } } @@ -434,8 +449,25 @@ public class SavingsProduct extends AbstractPersistable { actualChanges.put(localeParamName, localeAsInput); this.overdraftLimit = newValue; } + + if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(nominalAnnualInterestRateOverdraftParamName, this.nominalAnnualInterestRateOverdraft)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName); + actualChanges.put(nominalAnnualInterestRateOverdraftParamName, newValue); + actualChanges.put(localeParamName, localeAsInput); + this.nominalAnnualInterestRateOverdraft = newValue; + } + + if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minOverdraftForInterestCalculationParamName, this.minOverdraftForInterestCalculation)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName); + actualChanges.put(minOverdraftForInterestCalculationParamName, newValue); + actualChanges.put(localeParamName, localeAsInput); + this.minOverdraftForInterestCalculation = newValue; + } + if (!this.allowOverdraft) { this.overdraftLimit = null; + this.nominalAnnualInterestRateOverdraft = null; + this.minOverdraftForInterestCalculation = null; } if (command.isChangeInBooleanParameterNamed(enforceMinRequiredBalanceParamName, this.enforceMinRequiredBalance)) { @@ -571,4 +603,12 @@ public class SavingsProduct extends AbstractPersistable { return this.shortName; } + public BigDecimal nominalAnnualInterestRateOverdraft() { + return this.nominalAnnualInterestRateOverdraft; + } + + public BigDecimal minOverdraftForInterestCalculation() { + return this.minOverdraftForInterestCalculation; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java index 564094800..754eb2c63 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java @@ -25,6 +25,8 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.shortNameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; @@ -128,6 +130,16 @@ public class SavingsProductAssembler { overdraftLimit = command.bigDecimalValueOfParameterNamed(overdraftLimitParamName); } + BigDecimal nominalAnnualInterestRateOverdraft = BigDecimal.ZERO; + if(command.parameterExists(nominalAnnualInterestRateOverdraftParamName)){ + nominalAnnualInterestRateOverdraft = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateOverdraftParamName); + } + + BigDecimal minOverdraftForInterestCalculation = BigDecimal.ZERO; + if(command.parameterExists(minOverdraftForInterestCalculationParamName)){ + minOverdraftForInterestCalculation = command.bigDecimalValueOfParameterNamed(minOverdraftForInterestCalculationParamName); + } + boolean enforceMinRequiredBalance = false; if (command.parameterExists(enforceMinRequiredBalanceParamName)) { enforceMinRequiredBalance = command.booleanPrimitiveValueOfParameterNamed(enforceMinRequiredBalanceParamName); @@ -143,7 +155,8 @@ public class SavingsProductAssembler { return SavingsProduct.createNew(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges, - allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation); + allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation, + nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } public Set assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/AnnualCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/AnnualCompoundingPeriod.java index 0340bf92a..5431042b1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/AnnualCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/AnnualCompoundingPeriod.java @@ -7,7 +7,6 @@ package org.mifosplatform.portfolio.savings.domain.interest; import java.math.BigDecimal; import java.math.MathContext; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; @@ -35,18 +34,19 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { @Override public BigDecimal calculateInterest(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestToCompound, - final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation, + final BigDecimal overdraftInterestRateAsFraction, final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; switch (interestCalculationType) { case DAILY_BALANCE: interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, interestRateAsFraction, - daysInYear, minBalanceForInterestCalculation); + daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case AVERAGE_DAILY_BALANCE: interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case INVALID: break; @@ -56,7 +56,8 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { } private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, - final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final long daysInYear, final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { BigDecimal cumulativeBalance = BigDecimal.ZERO; Integer numberOfDays = Integer.valueOf(0); @@ -74,12 +75,22 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64) .setScale(9, MoneyHelper.getRoundingMode()); - final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); - final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); - final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); - if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { - interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - MoneyHelper.getRoundingMode()); + if(averageDailyBalance.compareTo(BigDecimal.ZERO) >= 0){ + if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); + interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } + }else{ + if (averageDailyBalance.compareTo(minOverdraftForInterestCalculation.negate()) < 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = overdraftInterestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); + interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } } } @@ -88,7 +99,8 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { private BigDecimal calculateUsingDailyBalanceMethod(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, final long daysInYear, - final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; BigDecimal interestOnBalanceUnrounded = BigDecimal.ZERO; @@ -97,15 +109,15 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { switch (compoundingInterestPeriodType) { case DAILY: interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, interestRateAsFraction, - daysInYear, minBalanceForInterestCalculation); + daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case MONTHLY: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case QUATERLY: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; // case WEEKLY: // break; @@ -113,11 +125,11 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { // break; case BI_ANNUAL: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case ANNUAL: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; // case NO_COMPOUNDING_SIMPLE_INTEREST: // break; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/BiAnnualCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/BiAnnualCompoundingPeriod.java index 43ae86ee9..0a232bfef 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/BiAnnualCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/BiAnnualCompoundingPeriod.java @@ -7,7 +7,6 @@ package org.mifosplatform.portfolio.savings.domain.interest; import java.math.BigDecimal; import java.math.MathContext; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; @@ -35,18 +34,19 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { @Override public BigDecimal calculateInterest(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestToCompound, - final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation, + final BigDecimal overdraftInterestRateAsFraction, final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; switch (interestCalculationType) { case DAILY_BALANCE: interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, interestRateAsFraction, - daysInYear, minBalanceForInterestCalculation); + daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case AVERAGE_DAILY_BALANCE: interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case INVALID: break; @@ -56,7 +56,8 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { } private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, - final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final long daysInYear, final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { BigDecimal cumulativeBalance = BigDecimal.ZERO; Integer numberOfDays = Integer.valueOf(0); @@ -74,12 +75,22 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64) .setScale(9, MoneyHelper.getRoundingMode()); - final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); - final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); - final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); - if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { - interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - MoneyHelper.getRoundingMode()); + if(averageDailyBalance.compareTo(BigDecimal.ZERO) >= 0){ + if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); + interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } + }else{ + if (averageDailyBalance.compareTo(minOverdraftForInterestCalculation.negate()) < 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = overdraftInterestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); + interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } } } @@ -88,7 +99,8 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { private BigDecimal calculateUsingDailyBalanceMethod(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, final long daysInYear, - final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; BigDecimal interestOnBalanceUnrounded = BigDecimal.ZERO; @@ -97,15 +109,15 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { switch (compoundingInterestPeriodType) { case DAILY: interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, interestRateAsFraction, - daysInYear, minBalanceForInterestCalculation); + daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case MONTHLY: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case QUATERLY: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; // case WEEKLY: // break; @@ -113,11 +125,11 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { // break; case BI_ANNUAL: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case ANNUAL: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; // case NO_COMPOUNDING_SIMPLE_INTEREST: // break; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/CompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/CompoundingPeriod.java index bea70c4b5..60028fe39 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/CompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/CompoundingPeriod.java @@ -14,5 +14,6 @@ public interface CompoundingPeriod { BigDecimal calculateInterest(SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, SavingsInterestCalculationType interestCalculationType, BigDecimal interestFromPreviousPostingPeriod, - BigDecimal interestRateAsFraction, long daysInYear, BigDecimal minBalanceForInterestCalculation); + BigDecimal interestRateAsFraction, long daysInYear, BigDecimal minBalanceForInterestCalculation, + BigDecimal overdraftInterestRateAsFraction, BigDecimal minOverdraftForInterestCalculation); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/DailyCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/DailyCompoundingPeriod.java index a5a5acf62..128eaaeec 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/DailyCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/DailyCompoundingPeriod.java @@ -67,7 +67,8 @@ public class DailyCompoundingPeriod implements CompoundingPeriod { @SuppressWarnings("unused") final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, @SuppressWarnings("unused") final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestFromPreviousPostingPeriod, final BigDecimal interestRateAsFraction, final long daysInYear, - final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minBalanceForInterestCalculation, + final BigDecimal overdraftInterestRateAsFraction, final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; // for daily compounding - each interest calculated from previous daily @@ -75,7 +76,8 @@ public class DailyCompoundingPeriod implements CompoundingPeriod { BigDecimal interestToCompound = interestFromPreviousPostingPeriod; for (final EndOfDayBalance balance : this.endOfDayBalances) { final BigDecimal interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, - interestRateAsFraction, daysInYear, minBalanceForInterestCalculation); + interestRateAsFraction, daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, + minOverdraftForInterestCalculation); interestToCompound = interestToCompound.add(interestOnBalanceUnrounded, MathContext.DECIMAL64).setScale(9); interestEarned = interestEarned.add(interestOnBalanceUnrounded); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java index aa35ed6db..faaf92ae2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java @@ -7,7 +7,6 @@ package org.mifosplatform.portfolio.savings.domain.interest; import java.math.BigDecimal; import java.math.MathContext; -import java.math.RoundingMode; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; @@ -49,17 +48,27 @@ public class EndOfDayBalance { } public BigDecimal calculateInterestOnBalance(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, - final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final long daysInYear, final BigDecimal minBalanceForInterestCalculation, + final BigDecimal overdraftInterestRateAsFraction, final BigDecimal minOverdraftForInterestCalculation) { - final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); - final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); - final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(this.numberOfDays), MathContext.DECIMAL64); - - final BigDecimal realBalanceForInterestCalculation = this.endOfDayBalance.getAmount().add(interestToCompound); BigDecimal interest = BigDecimal.ZERO.setScale(9, MoneyHelper.getRoundingMode()); - if (realBalanceForInterestCalculation.compareTo(minBalanceForInterestCalculation) >= 0) { - interest = realBalanceForInterestCalculation.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - MoneyHelper.getRoundingMode()); + final BigDecimal realBalanceForInterestCalculation = this.endOfDayBalance.getAmount().add(interestToCompound); + if(realBalanceForInterestCalculation.compareTo(BigDecimal.ZERO) >= 0){ + if (realBalanceForInterestCalculation.compareTo(minBalanceForInterestCalculation) >= 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(this.numberOfDays), MathContext.DECIMAL64); + interest = realBalanceForInterestCalculation.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } + } else { + if (realBalanceForInterestCalculation.compareTo(minOverdraftForInterestCalculation.negate()) < 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = overdraftInterestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(this.numberOfDays), MathContext.DECIMAL64); + interest = realBalanceForInterestCalculation.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } } return interest; } @@ -72,22 +81,37 @@ public class EndOfDayBalance { * 1/365 n = number of periods rate is compounded */ public BigDecimal calculateInterestOnBalanceAndInterest(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, - final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final long daysInYear, final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); final BigDecimal presentValue = this.endOfDayBalance.getAmount().add(interestToCompound); - - final BigDecimal r = interestRateAsFraction.multiply(multiplicand); - - final BigDecimal interestRateForCompoundingPeriodPlusOne = BigDecimal.ONE.add(r); - - final double interestRateForCompoundingPeriodPowered = Math.pow(interestRateForCompoundingPeriodPlusOne.doubleValue(), - Integer.valueOf(this.numberOfDays).doubleValue()); BigDecimal futureValue = presentValue.setScale(9, MoneyHelper.getRoundingMode()); - if (presentValue.compareTo(minBalanceForInterestCalculation) >= 0) { - futureValue = presentValue.multiply(BigDecimal.valueOf(interestRateForCompoundingPeriodPowered), MathContext.DECIMAL64) - .setScale(9, MoneyHelper.getRoundingMode()); + + if(presentValue.compareTo(BigDecimal.ZERO) >= 0){ + if (presentValue.compareTo(minBalanceForInterestCalculation) >= 0) { + final BigDecimal r = interestRateAsFraction.multiply(multiplicand); + + final BigDecimal interestRateForCompoundingPeriodPlusOne = BigDecimal.ONE.add(r); + + final double interestRateForCompoundingPeriodPowered = Math.pow(interestRateForCompoundingPeriodPlusOne.doubleValue(), + Integer.valueOf(this.numberOfDays).doubleValue()); + futureValue = presentValue.multiply(BigDecimal.valueOf(interestRateForCompoundingPeriodPowered), MathContext.DECIMAL64) + .setScale(9, MoneyHelper.getRoundingMode()); + } + }else{ + if (presentValue.compareTo(minOverdraftForInterestCalculation.negate()) < 0) { + final BigDecimal r = overdraftInterestRateAsFraction.multiply(multiplicand); + + final BigDecimal interestRateForCompoundingPeriodPlusOne = BigDecimal.ONE.add(r); + + final double interestRateForCompoundingPeriodPowered = Math.pow(interestRateForCompoundingPeriodPlusOne.doubleValue(), + Integer.valueOf(this.numberOfDays).doubleValue()); + futureValue = presentValue.multiply(BigDecimal.valueOf(interestRateForCompoundingPeriodPowered), MathContext.DECIMAL64) + .setScale(9, MoneyHelper.getRoundingMode()); + } } + return futureValue.subtract(presentValue); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java index 519b4c127..61ac40dcf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java @@ -7,7 +7,6 @@ package org.mifosplatform.portfolio.savings.domain.interest; import java.math.BigDecimal; import java.math.MathContext; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; @@ -35,18 +34,19 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { @Override public BigDecimal calculateInterest(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestToCompound, - final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation, + final BigDecimal overdraftInterestRateAsFraction, final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; switch (interestCalculationType) { case DAILY_BALANCE: interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, interestRateAsFraction, - daysInYear, minBalanceForInterestCalculation); + daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case AVERAGE_DAILY_BALANCE: interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case INVALID: break; @@ -56,7 +56,8 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { } private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, - final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final long daysInYear, final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { BigDecimal cumulativeBalance = BigDecimal.ZERO; Integer numberOfDays = Integer.valueOf(0); @@ -74,12 +75,22 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64) .setScale(9, MoneyHelper.getRoundingMode()); - final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); - final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); - final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); - if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { - interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - MoneyHelper.getRoundingMode()); + if(averageDailyBalance.compareTo(BigDecimal.ZERO) >= 0){ + if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); + interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } + }else{ + if (averageDailyBalance.compareTo(minOverdraftForInterestCalculation.negate()) < 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = overdraftInterestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); + interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } } } @@ -88,7 +99,8 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { private BigDecimal calculateUsingDailyBalanceMethod(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, final long daysInYear, - final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; BigDecimal interestOnBalanceUnrounded = BigDecimal.ZERO; @@ -97,11 +109,11 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { switch (compoundingInterestPeriodType) { case DAILY: interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, interestRateAsFraction, - daysInYear, minBalanceForInterestCalculation); + daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case MONTHLY: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; // case QUATERLY: // break; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java index 1a22da750..501b5ce8b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/PostingPeriod.java @@ -44,6 +44,25 @@ public class PostingPeriod { // minimum balance for interest calculation private final Money minBalanceForInterestCalculation; + private BigDecimal overdraftInterestRateAsFraction; + private Money minOverdraftForInterestCalculation; + + public static PostingPeriod createFrom(final LocalDateInterval periodInterval, final Money periodStartingBalance, + final List orderedListOfTransactions, final MonetaryCurrency currency, + final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, + final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear, + final LocalDate upToInterestCalculationDate, Collection interestPostTransactions, boolean isInterestTransfer, + final Money minBalanceForInterestCalculation, final boolean isSavingsInterestPostingAtCurrentPeriodEnd) { + + final BigDecimal overdraftInterestRateAsFraction = BigDecimal.ZERO; + final Money minOverdraftForInterestCalculation = Money.zero(currency); + + return createFrom(periodInterval, periodStartingBalance, orderedListOfTransactions, currency, + interestCompoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYear, + upToInterestCalculationDate, interestPostTransactions, isInterestTransfer, + minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd, + overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); + } // isInterestTransfer boolean is to identify newly created transaction is // interest transfer @@ -52,7 +71,8 @@ public class PostingPeriod { final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear, final LocalDate upToInterestCalculationDate, Collection interestPostTransactions, boolean isInterestTransfer, - final Money minBalanceForInterestCalculation, final boolean isSavingsInterestPostingAtCurrentPeriodEnd) { + final Money minBalanceForInterestCalculation, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, + final BigDecimal overdraftInterestRateAsFraction, final Money minOverdraftForInterestCalculation) { final List accountEndOfDayBalances = new ArrayList<>(); boolean interestTransfered = false; @@ -113,14 +133,16 @@ public class PostingPeriod { return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods, interestTransfered, - minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd); + minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd, + overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); } private PostingPeriod(final LocalDateInterval periodInterval, final MonetaryCurrency currency, final Money openingBalance, final Money closingBalance, final SavingsCompoundingInterestPeriodType interestCompoundingType, final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear, final List compoundingPeriods, boolean interestTransfered, final Money minBalanceForInterestCalculation, - final boolean isSavingsInterestPostingAtCurrentPeriodEnd) { + final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final BigDecimal overdraftInterestRateAsFraction, + final Money minOverdraftForInterestCalculation) { this.periodInterval = periodInterval; this.currency = currency; this.openingBalance = openingBalance; @@ -137,6 +159,8 @@ public class PostingPeriod { this.dateOfPostingTransaction = periodInterval.endDate().plusDays(1); this.interestTransfered = interestTransfered; this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; + this.overdraftInterestRateAsFraction = overdraftInterestRateAsFraction; + this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation; } public Money interest() { @@ -165,7 +189,8 @@ public class PostingPeriod { final BigDecimal interestUnrounded = compoundingPeriod.calculateInterest(this.interestCompoundingType, this.interestCalculationType, interestCompounded, this.interestRateAsFraction, this.daysInYear, - this.minBalanceForInterestCalculation.getAmount()); + this.minBalanceForInterestCalculation.getAmount(), this.overdraftInterestRateAsFraction, + this.minOverdraftForInterestCalculation.getAmount()); interestCompounded = interestCompounded.add(interestUnrounded); interestEarned = interestEarned.add(interestUnrounded); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/QuarterlyCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/QuarterlyCompoundingPeriod.java index 7b8d50c7c..0931a3d85 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/QuarterlyCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/QuarterlyCompoundingPeriod.java @@ -7,7 +7,6 @@ package org.mifosplatform.portfolio.savings.domain.interest; import java.math.BigDecimal; import java.math.MathContext; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; @@ -35,18 +34,19 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { @Override public BigDecimal calculateInterest(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestToCompound, - final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation, + final BigDecimal overdraftInterestRateAsFraction, final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; switch (interestCalculationType) { case DAILY_BALANCE: interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, interestRateAsFraction, - daysInYear, minBalanceForInterestCalculation); + daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case AVERAGE_DAILY_BALANCE: interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case INVALID: break; @@ -56,7 +56,8 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { } private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, - final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + final long daysInYear, final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { BigDecimal cumulativeBalance = BigDecimal.ZERO; Integer numberOfDays = Integer.valueOf(0); @@ -74,12 +75,22 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64) .setScale(9, MoneyHelper.getRoundingMode()); - final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); - final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); - final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); - if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { - interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - MoneyHelper.getRoundingMode()); + if(averageDailyBalance.compareTo(BigDecimal.ZERO) >= 0){ + if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); + interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } + }else{ + if (averageDailyBalance.compareTo(minOverdraftForInterestCalculation.negate()) < 0) { + final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); + final BigDecimal dailyInterestRate = overdraftInterestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); + final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); + interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, + MoneyHelper.getRoundingMode()); + } } } @@ -88,7 +99,8 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { private BigDecimal calculateUsingDailyBalanceMethod(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType, final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, final long daysInYear, - final BigDecimal minBalanceForInterestCalculation) { + final BigDecimal minBalanceForInterestCalculation, final BigDecimal overdraftInterestRateAsFraction, + final BigDecimal minOverdraftForInterestCalculation) { BigDecimal interestEarned = BigDecimal.ZERO; BigDecimal interestOnBalanceUnrounded = BigDecimal.ZERO; @@ -97,15 +109,15 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { switch (compoundingInterestPeriodType) { case DAILY: interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, interestRateAsFraction, - daysInYear, minBalanceForInterestCalculation); + daysInYear, minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case MONTHLY: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case QUATERLY: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; // case WEEKLY: // break; @@ -113,11 +125,11 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { // break; case BI_ANNUAL: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; case ANNUAL: interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation); break; // case NO_COMPOUNDING_SIMPLE_INTEREST: // break; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java index 55398a60e..27f15f9d9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java @@ -709,10 +709,11 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance"); final BigDecimal totalFeeCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalFeeCharge"); final BigDecimal totalPenaltyCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge"); + final BigDecimal totalOverdraftInterestDerived = null; final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals, totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance, totalFeeCharge, - totalPenaltyCharge); + totalPenaltyCharge, totalOverdraftInterestDerived); return DepositAccountData.instance(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 4de7156c6..b6c12a047 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -237,6 +237,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sa.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); sqlBuilder.append("sa.allow_overdraft as allowOverdraft, "); sqlBuilder.append("sa.overdraft_limit as overdraftLimit, "); + sqlBuilder.append("sa.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, "); + sqlBuilder.append("sa.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, "); // sqlBuilder.append("sa.annual_fee_amount as annualFeeAmount,"); // sqlBuilder.append("sa.annual_fee_on_month as annualFeeOnMonth, "); // sqlBuilder.append("sa.annual_fee_on_day as annualFeeOnDay, "); @@ -247,6 +249,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sa.total_annual_fees_derived as totalAnnualFees, "); sqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, "); sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, "); + sqlBuilder.append("sa.total_overdraft_interest_derived as totalOverdraftInterestDerived, "); sqlBuilder.append("sa.account_balance_derived as accountBalance, "); sqlBuilder.append("sa.total_fees_charge_derived as totalFeeCharge, "); sqlBuilder.append("sa.total_penalty_charge_derived as totalPenaltyCharge, "); @@ -384,6 +387,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final boolean allowOverdraft = rs.getBoolean("allowOverdraft"); final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit"); + final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "nominalAnnualInterestRateOverdraft"); + final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minOverdraftForInterestCalculation"); final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance"); final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance"); @@ -413,20 +418,22 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance"); final BigDecimal totalFeeCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalFeeCharge"); final BigDecimal totalPenaltyCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge"); + final BigDecimal totalOverdraftInterestDerived = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverdraftInterestDerived"); + final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minBalanceForInterestCalculation"); final BigDecimal onHoldFunds = rs.getBigDecimal("onHoldFunds"); final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals, totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance, totalFeeCharge, - totalPenaltyCharge); + totalPenaltyCharge, totalOverdraftInterestDerived); 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); + onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } } @@ -820,6 +827,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation, "); sqlBuilder.append("sp.allow_overdraft as allowOverdraft, "); sqlBuilder.append("sp.overdraft_limit as overdraftLimit, "); + sqlBuilder.append("sp.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, "); + sqlBuilder.append("sp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, "); // sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,"); // sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth, "); // sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay "); @@ -890,6 +899,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final boolean allowOverdraft = rs.getBoolean("allowOverdraft"); final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit"); + final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "nominalAnnualInterestRateOverdraft"); + final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minOverdraftForInterestCalculation"); final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance"); final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance"); @@ -937,7 +948,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, - onHoldFunds); + onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index ad8520938..b51ec554d 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -371,7 +371,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - if (account.getNominalAnnualInterestRate().compareTo(BigDecimal.ZERO) == 1) { + if (account.getNominalAnnualInterestRate().compareTo(BigDecimal.ZERO) > 0 + || (account.allowOverdraft() && account.getNominalAnnualInterestRateOverdraft().compareTo(BigDecimal.ZERO) > 0)) { final Set existingTransactionIds = new HashSet<>(); final Set existingReversedTransactionIds = new HashSet<>(); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index cbcdb9a32..239c40558 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -114,6 +114,8 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); sqlBuilder.append("sp.allow_overdraft as allowOverdraft, "); sqlBuilder.append("sp.overdraft_limit as overdraftLimit, "); + sqlBuilder.append("sp.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, "); + sqlBuilder.append("sp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, "); sqlBuilder.append("sp.min_required_balance as minRequiredBalance, "); sqlBuilder.append("sp.enforce_min_required_balance as enforceMinRequiredBalance, "); sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation,"); @@ -178,6 +180,8 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers"); final boolean allowOverdraft = rs.getBoolean("allowOverdraft"); final BigDecimal overdraftLimit = rs.getBigDecimal("overdraftLimit"); + final BigDecimal nominalAnnualInterestRateOverdraft = rs.getBigDecimal("nominalAnnualInterestRateOverdraft"); + final BigDecimal minOverdraftForInterestCalculation = rs.getBigDecimal("minOverdraftForInterestCalculation"); final BigDecimal minRequiredBalance = rs.getBigDecimal("minRequiredBalance"); final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance"); @@ -187,7 +191,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, - minBalanceForInterestCalculation); + minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation); } } diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V288__overdraft_interest.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V288__overdraft_interest.sql new file mode 100644 index 000000000..5b6783311 --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V288__overdraft_interest.sql @@ -0,0 +1,8 @@ +ALTER TABLE `m_savings_account` +ADD COLUMN `nominal_annual_interest_rate_overdraft` DECIMAL(19,6) NULL DEFAULT NULL AFTER `overdraft_limit`, +ADD COLUMN `total_overdraft_interest_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `total_interest_posted_derived`, +ADD COLUMN `min_overdraft_for_interest_calculation` DECIMAL(19,6) NULL DEFAULT NULL AFTER `nominal_annual_interest_rate_overdraft`; + +ALTER TABLE `m_savings_product` +ADD COLUMN `nominal_annual_interest_rate_overdraft` DECIMAL(19,6) NULL DEFAULT NULL AFTER `overdraft_limit`, +ADD COLUMN `min_overdraft_for_interest_calculation` DECIMAL(19,6) NULL DEFAULT NULL AFTER `nominal_annual_interest_rate_overdraft`;