Merge pull request #1612 from pramodn02/MIFOSX-2390

MIFOSX-2390 : fixed prepay loan with installment charges & junit fixes
This commit is contained in:
Adi Narayana Raju
2016-01-20 16:27:24 +05:30
5 changed files with 129 additions and 78 deletions
@@ -546,18 +546,24 @@ public class ClientSavingsIntegrationTest {
cal.set(Calendar.YEAR, (Integer) dates.get(0)); cal.set(Calendar.YEAR, (Integer) dates.get(0));
cal.set(Calendar.MONTH, (Integer) dates.get(1) - 1); cal.set(Calendar.MONTH, (Integer) dates.get(1) - 1);
cal.set(Calendar.DAY_OF_MONTH, (Integer) dates.get(2)); cal.set(Calendar.DAY_OF_MONTH, (Integer) dates.get(2));
int n = 0;
Calendar current = Calendar.getInstance();
while (cal.compareTo(current) < 0) {
n++;
cal.set(Calendar.YEAR, (Integer) dates.get(0) + n);
}
cal.set(Calendar.YEAR, (Integer) dates.get(0));
cal.set(Calendar.MONTH, (Integer) dates.get(1) - 1);
cal.set(Calendar.DAY_OF_MONTH, (Integer) dates.get(2));
this.savingsAccountHelper.payCharge((Integer) savingsChargeForPay.get("id"), savingsId, for (int i = 1; i <= n; i++) {
((Float) savingsChargeForPay.get("amount")).toString(), sdf.format(cal.getTime())); this.savingsAccountHelper.payCharge((Integer) savingsChargeForPay.get("id"), savingsId,
HashMap paidCharge = this.savingsAccountHelper.getSavingsCharge(savingsId, (Integer) savingsChargeForPay.get("id")); ((Float) savingsChargeForPay.get("amount")).toString(), sdf.format(cal.getTime()));
assertEquals(savingsChargeForPay.get("amount"), paidCharge.get("amountPaid")); HashMap paidCharge = this.savingsAccountHelper.getSavingsCharge(savingsId, (Integer) savingsChargeForPay.get("id"));
Float expectedValue = (Float) savingsChargeForPay.get("amount") * i;
// temporary fix, this test case needs to be re-factored (will fail assertEquals(expectedValue, paidCharge.get("amountPaid"));
// again on Jan 15 2016) cal.set(Calendar.YEAR, (Integer) dates.get(0) + i);
cal.set(Calendar.YEAR, (Integer) dates.get(0) + 1); }
this.savingsAccountHelper.payCharge((Integer) savingsChargeForPay.get("id"), savingsId,
((Float) savingsChargeForPay.get("amount")).toString(), sdf.format(cal.getTime()));
paidCharge = this.savingsAccountHelper.getSavingsCharge(savingsId, (Integer) savingsChargeForPay.get("id"));
Integer inactivatedChargeId = (Integer) this.savingsAccountHelper.inactivateCharge(annualSavingsChargeId, savingsId, Integer inactivatedChargeId = (Integer) this.savingsAccountHelper.inactivateCharge(annualSavingsChargeId, savingsId,
CommonConstants.RESPONSE_RESOURCE_ID); CommonConstants.RESPONSE_RESOURCE_ID);
@@ -618,7 +624,7 @@ public class ClientSavingsIntegrationTest {
this.savingsAccountHelper.payCharge((Integer) savingsChargeForPay.get("id"), savingsId, this.savingsAccountHelper.payCharge((Integer) savingsChargeForPay.get("id"), savingsId,
((Float) savingsChargeForPay.get("amount")).toString(), sdf.format(cal.getTime())); ((Float) savingsChargeForPay.get("amount")).toString(), sdf.format(cal.getTime()));
paidCharge = this.savingsAccountHelper.getSavingsCharge(savingsId, (Integer) savingsChargeForPay.get("id")); HashMap paidCharge = this.savingsAccountHelper.getSavingsCharge(savingsId, (Integer) savingsChargeForPay.get("id"));
assertEquals(savingsChargeForPay.get("amount"), paidCharge.get("amountPaid")); assertEquals(savingsChargeForPay.get("amount"), paidCharge.get("amountPaid"));
List nextDueDates = (List) paidCharge.get("dueDate"); List nextDueDates = (List) paidCharge.get("dueDate");
LocalDate nextDueDate = new LocalDate((Integer) nextDueDates.get(0), (Integer) nextDueDates.get(1), (Integer) nextDueDates.get(2)); LocalDate nextDueDate = new LocalDate((Integer) nextDueDates.get(0), (Integer) nextDueDates.get(1), (Integer) nextDueDates.get(2));
@@ -516,6 +516,10 @@ public class GroupSavingsIntegrationTest {
LocalDate expectedNextDueDate = new LocalDate((Integer) dates.get(0), (Integer) dates.get(1), (Integer) dates.get(2)) LocalDate expectedNextDueDate = new LocalDate((Integer) dates.get(0), (Integer) dates.get(1), (Integer) dates.get(2))
.plusWeeks((Integer) paidCharge.get("feeInterval")); .plusWeeks((Integer) paidCharge.get("feeInterval"));
assertEquals(expectedNextDueDate, nextDueDate); assertEquals(expectedNextDueDate, nextDueDate);
cal = Calendar.getInstance();
this.savingsAccountHelper.closeSavingsAccountAndGetBackRequiredField(savingsId, "true", null, sdf.format(cal.getTime()));
} }
public static Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, public static Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
@@ -302,6 +302,7 @@ public class Loan extends AbstractPersistable<Long> {
// see // see
// http://stackoverflow.com/questions/4334970/hibernate-cannot-simultaneously-fetch-multiple-bags // http://stackoverflow.com/questions/4334970/hibernate-cannot-simultaneously-fetch-multiple-bags
@LazyCollection(LazyCollectionOption.FALSE) @LazyCollection(LazyCollectionOption.FALSE)
@OrderBy(value = "installmentNumber")
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = new ArrayList<>(); private final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = new ArrayList<>();
@@ -5627,6 +5628,15 @@ public class Loan extends AbstractPersistable<Long> {
} }
public int fetchNumberOfInstallmensAfterExceptions() { public int fetchNumberOfInstallmensAfterExceptions() {
if(this.repaymentScheduleInstallments.size() > 0){
int numberOfInstallments = 0;
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
if(!installment.isRecalculatedInterestComponent()){
numberOfInstallments++;
}
}
return numberOfInstallments;
}
return this.repaymentScheduleDetail().getNumberOfRepayments() + adjustNumberOfRepayments(); return this.repaymentScheduleDetail().getNumberOfRepayments() + adjustNumberOfRepayments();
} }
@@ -304,7 +304,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
installment = handleRecalculationForTransactions(mc, loanApplicationTerms, holidayDetailDTO, currency, scheduleParams, installment = handleRecalculationForTransactions(mc, loanApplicationTerms, holidayDetailDTO, currency, scheduleParams,
loanRepaymentScheduleTransactionProcessor, totalInterestChargedForFullLoanTerm, lastRestDate, scheduledDueDate, loanRepaymentScheduleTransactionProcessor, totalInterestChargedForFullLoanTerm, lastRestDate, scheduledDueDate,
periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams, periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams,
lastTotalOutstandingInterestPaymentDueToGrace, installment); lastTotalOutstandingInterestPaymentDueToGrace, installment, loanCharges);
periods.add(installment); periods.add(installment);
// Updates principal paid map with efective date for reducing // Updates principal paid map with efective date for reducing
@@ -424,7 +424,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
final Money totalInterestChargedForFullLoanTerm, final LocalDate lastRestDate, final LocalDate scheduledDueDate, final Money totalInterestChargedForFullLoanTerm, final LocalDate lastRestDate, final LocalDate scheduledDueDate,
final LocalDate periodStartDateApplicableForInterest, final Collection<RecalculationDetail> applicableTransactions, final LocalDate periodStartDateApplicableForInterest, final Collection<RecalculationDetail> applicableTransactions,
final ScheduleCurrentPeriodParams currentPeriodParams, final Money lastTotalOutstandingInterestPaymentDueToGrace, final ScheduleCurrentPeriodParams currentPeriodParams, final Money lastTotalOutstandingInterestPaymentDueToGrace,
final LoanScheduleModelPeriod installment) { final LoanScheduleModelPeriod installment, Set<LoanCharge> loanCharges) {
LoanScheduleModelPeriod modifiedInstallment = installment; LoanScheduleModelPeriod modifiedInstallment = installment;
if (scheduleParams.applyInterestRecalculation() && loanRepaymentScheduleTransactionProcessor != null) { if (scheduleParams.applyInterestRecalculation() && loanRepaymentScheduleTransactionProcessor != null) {
Money principalProcessed = Money.zero(currency); Money principalProcessed = Money.zero(currency);
@@ -446,7 +446,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
modifiedInstallment = handlePrepaymentOfLoan(mc, loanApplicationTerms, holidayDetailDTO, scheduleParams, modifiedInstallment = handlePrepaymentOfLoan(mc, loanApplicationTerms, holidayDetailDTO, scheduleParams,
totalInterestChargedForFullLoanTerm, scheduledDueDate, periodStartDateApplicableForInterest, totalInterestChargedForFullLoanTerm, scheduledDueDate, periodStartDateApplicableForInterest,
currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(), currentPeriodParams, currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(), currentPeriodParams,
lastTotalOutstandingInterestPaymentDueToGrace, transactionDate, modifiedInstallment); lastTotalOutstandingInterestPaymentDueToGrace, transactionDate, modifiedInstallment, loanCharges);
Money addToPrinciapal = Money.zero(currency); Money addToPrinciapal = Money.zero(currency);
if (scheduleParams.getOutstandingBalance().isLessThanZero()) { if (scheduleParams.getOutstandingBalance().isLessThanZero()) {
@@ -486,7 +486,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
final Money totalInterestChargedForFullLoanTerm, final LocalDate scheduledDueDate, final Money totalInterestChargedForFullLoanTerm, final LocalDate scheduledDueDate,
LocalDate periodStartDateApplicableForInterest, final double interestCalculationGraceOnRepaymentPeriodFraction, LocalDate periodStartDateApplicableForInterest, final double interestCalculationGraceOnRepaymentPeriodFraction,
final ScheduleCurrentPeriodParams currentPeriodParams, final Money lastTotalOutstandingInterestPaymentDueToGrace, final ScheduleCurrentPeriodParams currentPeriodParams, final Money lastTotalOutstandingInterestPaymentDueToGrace,
final LocalDate transactionDate, final LoanScheduleModelPeriod installment) { final LocalDate transactionDate, final LoanScheduleModelPeriod installment, Set<LoanCharge> loanCharges) {
LoanScheduleModelPeriod modifiedInstallment = installment; LoanScheduleModelPeriod modifiedInstallment = installment;
if (!scheduleParams.getOutstandingBalance().isGreaterThan(currentPeriodParams.getInterestForThisPeriod()) if (!scheduleParams.getOutstandingBalance().isGreaterThan(currentPeriodParams.getInterestForThisPeriod())
&& !scheduledDueDate.equals(transactionDate)) { && !scheduledDueDate.equals(transactionDate)) {
@@ -510,20 +510,31 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
lastTotalOutstandingInterestPaymentDueToGrace, scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms, lastTotalOutstandingInterestPaymentDueToGrace, scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms,
scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams), scheduleParams.getCompoundingMap(), scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams), scheduleParams.getCompoundingMap(),
periodStartDateApplicableForInterest, calculateTill, interestRates); periodStartDateApplicableForInterest, calculateTill, interestRates);
Money diff = currentPeriodParams.getInterestForThisPeriod().minus(interestTillDate.interest()); // applies charges for the period
final ScheduleCurrentPeriodParams tempPeriod = new ScheduleCurrentPeriodParams(
totalInterestChargedForFullLoanTerm.getCurrency(), interestCalculationGraceOnRepaymentPeriodFraction);
tempPeriod.setInterestForThisPeriod(interestTillDate.interest());
applyChargesForCurrentPeriod(loanCharges, totalInterestChargedForFullLoanTerm.getCurrency(), scheduleParams, calculateTill,
tempPeriod);
Money interestDiff = currentPeriodParams.getInterestForThisPeriod().minus(tempPeriod.getInterestForThisPeriod());
Money chargeDiff = currentPeriodParams.getFeeChargesForInstallment().minus(tempPeriod.getFeeChargesForInstallment());
Money penaltyDiff = currentPeriodParams.getPenaltyChargesForInstallment().minus(tempPeriod.getPenaltyChargesForInstallment());
Money diff = interestDiff.plus(chargeDiff).plus(penaltyDiff);
if (!scheduleParams.getOutstandingBalance().minus(diff).isGreaterThanZero()) { if (!scheduleParams.getOutstandingBalance().minus(diff).isGreaterThanZero()) {
scheduleParams.reduceOutstandingBalance(diff); scheduleParams.reduceOutstandingBalance(diff);
currentPeriodParams.minusInterestForThisPeriod(diff); currentPeriodParams.minusInterestForThisPeriod(interestDiff);
currentPeriodParams.minusFeeChargesForInstallment(chargeDiff);
currentPeriodParams.minusPenaltyChargesForInstallment(penaltyDiff);
currentPeriodParams.plusPrincipalForThisPeriod(diff); currentPeriodParams.plusPrincipalForThisPeriod(diff);
final Money totalDue = currentPeriodParams.getPrincipalForThisPeriod().plus(currentPeriodParams.getInterestForThisPeriod());
// create and replaces repayment period // create and replaces repayment period
// from parts // from parts
modifiedInstallment = LoanScheduleModelRepaymentPeriod.repayment(scheduleParams.getInstalmentNumber(), modifiedInstallment = LoanScheduleModelRepaymentPeriod.repayment(scheduleParams.getInstalmentNumber(),
scheduleParams.getPeriodStartDate(), transactionDate, currentPeriodParams.getPrincipalForThisPeriod(), scheduleParams.getPeriodStartDate(), transactionDate, currentPeriodParams.getPrincipalForThisPeriod(),
scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(), scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(),
currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(), totalDue, currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(),
false); currentPeriodParams.fetchTotalAmountForPeriod(), false);
scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(interestTillDate.interestPaymentDueToGrace()); scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(interestTillDate.interestPaymentDueToGrace());
} }
@@ -602,7 +613,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
&& scheduleParams.getLoanRepaymentScheduleTransactionProcessor() && scheduleParams.getLoanRepaymentScheduleTransactionProcessor()
.isInterestFirstRepaymentScheduleTransactionProcessor()) { .isInterestFirstRepaymentScheduleTransactionProcessor()) {
List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail); List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);
if (!transactionDate.isEqual(scheduleParams.getPeriodStartDate()) || scheduleParams.getInstalmentNumber() == 1) { if (!transactionDate.isEqual(scheduleParams.getPeriodStartDate()) || scheduleParams.getInstalmentNumber() == 1) {
int periodDays = Days.daysBetween(scheduleParams.getPeriodStartDate(), transactionDate).getDays(); int periodDays = Days.daysBetween(scheduleParams.getPeriodStartDate(), transactionDate).getDays();
// calculates period start date for interest // calculates period start date for interest
@@ -2603,6 +2614,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
this.feeChargesForInstallment = feeChargesForInstallment; this.feeChargesForInstallment = feeChargesForInstallment;
} }
public void minusFeeChargesForInstallment(Money feeChargesForInstallment) {
this.feeChargesForInstallment = this.feeChargesForInstallment.minus(feeChargesForInstallment);
}
public Money getPenaltyChargesForInstallment() { public Money getPenaltyChargesForInstallment() {
return this.penaltyChargesForInstallment; return this.penaltyChargesForInstallment;
} }
@@ -2611,6 +2626,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
this.penaltyChargesForInstallment = penaltyChargesForInstallment; this.penaltyChargesForInstallment = penaltyChargesForInstallment;
} }
public void minusPenaltyChargesForInstallment(Money penaltyChargesForInstallment) {
this.penaltyChargesForInstallment = this.penaltyChargesForInstallment.minus(penaltyChargesForInstallment);
}
public Money fetchTotalAmountForPeriod() { public Money fetchTotalAmountForPeriod() {
return this.principalForThisPeriod.plus(interestForThisPeriod).plus(feeChargesForInstallment) return this.principalForThisPeriod.plus(interestForThisPeriod).plus(feeChargesForInstallment)
.plus(penaltyChargesForInstallment); .plus(penaltyChargesForInstallment);
@@ -310,7 +310,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges, final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation) { final BigDecimal minOverdraftForInterestCalculation) {
final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL; final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
@@ -345,7 +345,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges, final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation) { final BigDecimal minOverdraftForInterestCalculation) {
this.client = client; this.client = client;
this.group = group; this.group = group;
@@ -457,33 +457,33 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate); final SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate);
if (postingTransaction == null) { if (postingTransaction == null) {
SavingsAccountTransaction newPostingTransaction; SavingsAccountTransaction newPostingTransaction;
if(interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))){ if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) {
newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), interestPostingTransactionDate,
interestPostingTransactionDate, interestEarnedToBePostedForPeriod); interestEarnedToBePostedForPeriod);
}else{ } else {
newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), interestPostingTransactionDate,
interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated()); interestEarnedToBePostedForPeriod.negated());
} }
this.transactions.add(newPostingTransaction); this.transactions.add(newPostingTransaction);
recalucateDailyBalanceDetails = true; recalucateDailyBalanceDetails = true;
} else { } else {
boolean correctionRequired = false; boolean correctionRequired = false;
if(postingTransaction.isInterestPostingAndNotReversed()){ if (postingTransaction.isInterestPostingAndNotReversed()) {
correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod); correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod);
}else{ } else {
correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod.negated()); correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod.negated());
} }
if (correctionRequired) { if (correctionRequired) {
postingTransaction.reverse(); postingTransaction.reverse();
SavingsAccountTransaction newPostingTransaction; SavingsAccountTransaction newPostingTransaction;
if(interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))){ if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) {
newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(), newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod); interestPostingTransactionDate, interestEarnedToBePostedForPeriod);
}else{ } else {
newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(), newPostingTransaction = SavingsAccountTransaction.overdraftInterest(this, office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated()); interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated());
} }
this.transactions.add(newPostingTransaction); this.transactions.add(newPostingTransaction);
recalucateDailyBalanceDetails = true; recalucateDailyBalanceDetails = true;
} }
@@ -510,7 +510,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
SavingsAccountTransaction postingTransation = null; SavingsAccountTransaction postingTransation = null;
for (final SavingsAccountTransaction transaction : this.transactions) { for (final SavingsAccountTransaction transaction : this.transactions) {
if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed()) && transaction.occursOn(postingDate)) { if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
&& transaction.occursOn(postingDate)) {
postingTransation = transaction; postingTransation = transaction;
break; break;
} }
@@ -634,10 +635,10 @@ public class SavingsAccount extends AbstractPersistable<Long> {
} }
private BigDecimal getEffectiveOverdraftInterestRateAsFraction(MathContext mc) { private BigDecimal getEffectiveOverdraftInterestRateAsFraction(MathContext mc) {
return this.nominalAnnualInterestRateOverdraft.divide(BigDecimal.valueOf(100l), mc); return this.nominalAnnualInterestRateOverdraft.divide(BigDecimal.valueOf(100l), mc);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate upToInterestCalculationDate) { protected BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate upToInterestCalculationDate) {
return this.nominalAnnualInterestRate.divide(BigDecimal.valueOf(100l), mc); return this.nominalAnnualInterestRate.divide(BigDecimal.valueOf(100l), mc);
} }
@@ -648,7 +649,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final List<SavingsAccountTransaction> orderedNonInterestPostingTransactions = new ArrayList<>(); final List<SavingsAccountTransaction> orderedNonInterestPostingTransactions = new ArrayList<>();
for (final SavingsAccountTransaction transaction : listOfTransactionsSorted) { for (final SavingsAccountTransaction transaction : listOfTransactionsSorted) {
if (!(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed()) && transaction.isNotReversed()) { if (!(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
&& transaction.isNotReversed()) {
orderedNonInterestPostingTransactions.add(transaction); orderedNonInterestPostingTransactions.add(transaction);
} }
} }
@@ -727,9 +729,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
LocalDate endOfBalanceDate = interestPostingUpToDate; LocalDate endOfBalanceDate = interestPostingUpToDate;
for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) { for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) {
final SavingsAccountTransaction transaction = accountTransactionsSorted.get(i); final SavingsAccountTransaction transaction = accountTransactionsSorted.get(i);
if (transaction.isNotReversed() if (transaction.isNotReversed()
&& !(transaction.isInterestPostingAndNotReversed() && !(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())) {
|| transaction.isOverdraftInterestAndNotReversed())) {
transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate); transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate);
// this transactions transaction date is end of balance date for // this transactions transaction date is end of balance date for
// previous transaction. // previous transaction.
@@ -888,7 +889,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
boolean transactionBeforeLastInterestPosting = false; boolean transactionBeforeLastInterestPosting = false;
for (final SavingsAccountTransaction transaction : retreiveListOfTransactions()) { for (final SavingsAccountTransaction transaction : retreiveListOfTransactions()) {
if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed()) && transaction.isAfter(transactionDate)) { if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
&& transaction.isAfter(transactionDate)) {
transactionBeforeLastInterestPosting = true; transactionBeforeLastInterestPosting = true;
break; break;
} }
@@ -897,7 +899,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return transactionBeforeLastInterestPosting; return transactionBeforeLastInterestPosting;
} }
public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount, final boolean isException,final List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions) { public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount, final boolean isException,
final List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions) {
final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions(); final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
Money runningBalance = Money.zero(this.currency); Money runningBalance = Money.zero(this.currency);
Money minRequiredBalance = minRequiredBalanceDerived(getCurrency()); Money minRequiredBalance = minRequiredBalanceDerived(getCurrency());
@@ -911,27 +914,28 @@ public class SavingsAccount extends AbstractPersistable<Long> {
continue; continue;
} }
final BigDecimal withdrawalFee = null; final BigDecimal withdrawalFee = null;
/* Loop through the onHold funds and see if we need to deduct or add to minimum required balance /*
and the point in time the transaction was made: */ * Loop through the onHold funds and see if we need to deduct or add
if(depositAccountOnHoldTransactions !=null){ * to minimum required balance and the point in time the transaction
for(final DepositAccountOnHoldTransaction onHoldTransaction: depositAccountOnHoldTransactions) { * was made:
*/
if (depositAccountOnHoldTransactions != null) {
for (final DepositAccountOnHoldTransaction onHoldTransaction : depositAccountOnHoldTransactions) {
// Compare the balance of the on hold: // Compare the balance of the on hold:
if((onHoldTransaction.getTransactionDate().isBefore(transaction.transactionLocalDate()) || onHoldTransaction.getTransactionDate().isEqual(transaction.transactionLocalDate())) if ((onHoldTransaction.getTransactionDate().isBefore(transaction.transactionLocalDate()) || onHoldTransaction
.getTransactionDate().isEqual(transaction.transactionLocalDate()))
&& (lastSavingsDate == null || onHoldTransaction.getTransactionDate().isAfter(lastSavingsDate))) { && (lastSavingsDate == null || onHoldTransaction.getTransactionDate().isAfter(lastSavingsDate))) {
if(onHoldTransaction.getTransactionType().isHold()) { if (onHoldTransaction.getTransactionType().isHold()) {
minRequiredBalance = minRequiredBalance.plus(onHoldTransaction.getAmountMoney(this.currency)); minRequiredBalance = minRequiredBalance.plus(onHoldTransaction.getAmountMoney(this.currency));
}else{ } else {
minRequiredBalance = minRequiredBalance.minus(onHoldTransaction.getAmountMoney(this.currency)); minRequiredBalance = minRequiredBalance.minus(onHoldTransaction.getAmountMoney(this.currency));
} }
} }
} }
} }
// deal with potential minRequiredBalance and // deal with potential minRequiredBalance and
// enforceMinRequiredBalance // enforceMinRequiredBalance
if (!isException && transaction.canProcessBalanceCheck()) { if (!isException && transaction.canProcessBalanceCheck()) {
@@ -943,7 +947,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
} }
} }
public void validateAccountBalanceDoesNotBecomeNegative(final String transactionAction,final List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions) { public void validateAccountBalanceDoesNotBecomeNegative(final String transactionAction,
final List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions) {
final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions(); final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
Money runningBalance = Money.zero(this.currency); Money runningBalance = Money.zero(this.currency);
@@ -956,16 +961,20 @@ public class SavingsAccount extends AbstractPersistable<Long> {
runningBalance = runningBalance.minus(transaction.getAmount(this.currency)); runningBalance = runningBalance.minus(transaction.getAmount(this.currency));
} }
/* Loop through the onHold funds and see if we need to deduct or add to minimum required balance /*
and the point in time the transaction was made: */ * Loop through the onHold funds and see if we need to deduct or add
if(depositAccountOnHoldTransactions !=null){ * to minimum required balance and the point in time the transaction
for(final DepositAccountOnHoldTransaction onHoldTransaction: depositAccountOnHoldTransactions) { * was made:
*/
if (depositAccountOnHoldTransactions != null) {
for (final DepositAccountOnHoldTransaction onHoldTransaction : depositAccountOnHoldTransactions) {
// Compare the balance of the on hold: // Compare the balance of the on hold:
if((onHoldTransaction.getTransactionDate().isBefore(transaction.transactionLocalDate()) || onHoldTransaction.getTransactionDate().isEqual(transaction.transactionLocalDate())) if ((onHoldTransaction.getTransactionDate().isBefore(transaction.transactionLocalDate()) || onHoldTransaction
.getTransactionDate().isEqual(transaction.transactionLocalDate()))
&& (lastSavingsDate == null || onHoldTransaction.getTransactionDate().isAfter(lastSavingsDate))) { && (lastSavingsDate == null || onHoldTransaction.getTransactionDate().isAfter(lastSavingsDate))) {
if(onHoldTransaction.getTransactionType().isHold()) { if (onHoldTransaction.getTransactionType().isHold()) {
minRequiredBalance = minRequiredBalance.plus(onHoldTransaction.getAmountMoney(this.currency)); minRequiredBalance = minRequiredBalance.plus(onHoldTransaction.getAmountMoney(this.currency));
}else{ } else {
minRequiredBalance = minRequiredBalance.minus(onHoldTransaction.getAmountMoney(this.currency)); minRequiredBalance = minRequiredBalance.minus(onHoldTransaction.getAmountMoney(this.currency));
} }
} }
@@ -1172,15 +1181,19 @@ public class SavingsAccount extends AbstractPersistable<Long> {
this.overdraftLimit = newValue; this.overdraftLimit = newValue;
} }
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(nominalAnnualInterestRateOverdraftParamName, this.nominalAnnualInterestRateOverdraft)) { if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(nominalAnnualInterestRateOverdraftParamName,
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName); this.nominalAnnualInterestRateOverdraft)) {
final BigDecimal newValue = command
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName);
actualChanges.put(nominalAnnualInterestRateOverdraftParamName, newValue); actualChanges.put(nominalAnnualInterestRateOverdraftParamName, newValue);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.nominalAnnualInterestRateOverdraft = newValue; this.nominalAnnualInterestRateOverdraft = newValue;
} }
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minOverdraftForInterestCalculationParamName, this.minOverdraftForInterestCalculation)) { if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minOverdraftForInterestCalculationParamName,
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName); this.minOverdraftForInterestCalculation)) {
final BigDecimal newValue = command
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName);
actualChanges.put(minOverdraftForInterestCalculationParamName, newValue); actualChanges.put(minOverdraftForInterestCalculationParamName, newValue);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.minOverdraftForInterestCalculation = newValue; this.minOverdraftForInterestCalculation = newValue;
@@ -1208,21 +1221,20 @@ public class SavingsAccount extends AbstractPersistable<Long> {
validateLockinDetails(baseDataValidator); validateLockinDetails(baseDataValidator);
esnureOverdraftLimitsSetForOverdraftAccounts(); esnureOverdraftLimitsSetForOverdraftAccounts();
} }
/** /**
* If overdrafts are allowed and the overdraft limit is not set, set the * If overdrafts are allowed and the overdraft limit is not set, set the
* same to Zero * same to Zero
**/ **/
private void esnureOverdraftLimitsSetForOverdraftAccounts() { private void esnureOverdraftLimitsSetForOverdraftAccounts() {
if (this.allowOverdraft) { this.overdraftLimit = this.overdraftLimit == null ? BigDecimal.ZERO : this.overdraftLimit;
this.overdraftLimit = this.overdraftLimit == null? BigDecimal.ZERO : this.overdraftLimit; this.nominalAnnualInterestRateOverdraft = this.nominalAnnualInterestRateOverdraft == null ? BigDecimal.ZERO
this.nominalAnnualInterestRateOverdraft = this.nominalAnnualInterestRateOverdraft == null? BigDecimal.ZERO : this.nominalAnnualInterestRateOverdraft; : this.nominalAnnualInterestRateOverdraft;
this.minOverdraftForInterestCalculation = this.minOverdraftForInterestCalculation == null? BigDecimal.ZERO : this.minOverdraftForInterestCalculation; this.minOverdraftForInterestCalculation = this.minOverdraftForInterestCalculation == null ? BigDecimal.ZERO
} : this.minOverdraftForInterestCalculation;
} }
private void validateLockinDetails(final DataValidatorBuilder baseDataValidator) { private void validateLockinDetails(final DataValidatorBuilder baseDataValidator) {
/* /*