/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.savings.domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.interest.CompoundInterestHelper;
import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod;
public final class SavingsHelper {
AccountTransfersReadPlatformService accountTransfersReadPlatformService = null;
public SavingsHelper(AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
}
private final CompoundInterestHelper compoundInterestHelper = new CompoundInterestHelper();
public List<LocalDateInterval> determineInterestPostingPeriods(final LocalDate startInterestCalculationLocalDate,
final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType,
final Integer financialYearBeginningMonth) {
final List<LocalDateInterval> postingPeriods = new ArrayList<>();
LocalDate periodStartDate = startInterestCalculationLocalDate;
LocalDate periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType,
interestPostingUpToDate, financialYearBeginningMonth);
periodEndDate = interestPostingLocalDate.minusDays(1);
postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
periodEndDate = interestPostingLocalDate;
periodStartDate = interestPostingLocalDate;
}
return postingPeriods;
}
private LocalDate determineInterestPostingPeriodEndDateFrom(final LocalDate periodStartDate,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final LocalDate interestPostingUpToDate,
Integer financialYearBeginningMonth) {
LocalDate periodEndDate = interestPostingUpToDate;
final Integer monthOfYear = periodStartDate.getMonthOfYear();
financialYearBeginningMonth--;
if (financialYearBeginningMonth == 0) financialYearBeginningMonth = 12;
final ArrayList<LocalDate> quarterlyDates = new ArrayList<>();
quarterlyDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).dayOfMonth().withMaximumValue());
quarterlyDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).plusMonths(3).withYear(periodStartDate.getYear())
.dayOfMonth().withMaximumValue());
quarterlyDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).plusMonths(6).withYear(periodStartDate.getYear())
.dayOfMonth().withMaximumValue());
quarterlyDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).plusMonths(9).withYear(periodStartDate.getYear())
.dayOfMonth().withMaximumValue());
Collections.sort(quarterlyDates);
final ArrayList<LocalDate> biannualDates = new ArrayList<>();
biannualDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).dayOfMonth().withMaximumValue());
biannualDates.add(periodStartDate.withMonthOfYear(financialYearBeginningMonth).plusMonths(6).withYear(periodStartDate.getYear())
.dayOfMonth().withMaximumValue());
Collections.sort(biannualDates);
boolean isEndDateSet = false;
switch (interestPostingPeriodType) {
case INVALID:
break;
case MONTHLY:
// produce period end date on last day of current month
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
break;
case QUATERLY:
for (LocalDate quarterlyDate : quarterlyDates) {
if (quarterlyDate.isAfter(periodStartDate)) {
periodEndDate = quarterlyDate;
isEndDateSet = true;
break;
}
}
if (!isEndDateSet) periodEndDate = quarterlyDates.get(0).plusYears(1).dayOfMonth().withMaximumValue();
break;
case BIANNUAL:
for (LocalDate biannualDate : biannualDates) {
if (biannualDate.isAfter(periodStartDate)) {
periodEndDate = biannualDate;
isEndDateSet = true;
break;
}
}
if (!isEndDateSet) periodEndDate = biannualDates.get(0).plusYears(1).dayOfMonth().withMaximumValue();
break;
case ANNUAL:
if (financialYearBeginningMonth < monthOfYear) {
periodEndDate = periodStartDate.withMonthOfYear(financialYearBeginningMonth);
periodEndDate = periodEndDate.plusYears(1);
} else {
periodEndDate = periodStartDate.withMonthOfYear(financialYearBeginningMonth);
}
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
}
// interest posting always occurs on next day after the period end date.
periodEndDate = periodEndDate.plusDays(1);
return periodEndDate;
}
public Money calculateInterestForAllPostingPeriods(final MonetaryCurrency currency, final List<PostingPeriod> allPeriods,
LocalDate accountLockedUntil, Boolean immediateWithdrawalOfInterest) {
return this.compoundInterestHelper.calculateInterestForAllPostingPeriods(currency, allPeriods, accountLockedUntil,
immediateWithdrawalOfInterest);
}
public Collection<Long> fetchPostInterestTransactionIds(Long accountId) {
return this.accountTransfersReadPlatformService.fetchPostInterestTransactionIds(accountId);
}
}