package org.mifos.clientportfolio.newloan.domain;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.mifos.accounts.productdefinition.util.helpers.GraceType;
import org.mifos.accounts.util.helpers.InstallmentDate;
import org.mifos.application.holiday.business.Holiday;
import org.mifos.application.holiday.persistence.HolidayDao;
import org.mifos.config.FiscalCalendarRules;
import org.mifos.schedule.ScheduledDateGeneration;
import org.mifos.schedule.ScheduledEvent;
import org.mifos.schedule.internal.HolidayAndWorkingDaysAndMoratoriaScheduledDateGeneration;
public class IndependentOfCustomerMeetingScheduleLoanInstallmentGenerator implements LoanInstallmentGenerator {
private final ScheduledEvent scheduledEvent;
private final HolidayDao holidayDao;
private final LocalDate meetingStartDate;
public IndependentOfCustomerMeetingScheduleLoanInstallmentGenerator(ScheduledEvent scheduledEvent, HolidayDao holidayDao, LocalDate meetingStartDate) {
this.scheduledEvent = scheduledEvent;
this.holidayDao = holidayDao;
this.meetingStartDate = meetingStartDate;
}
@Override
public List<InstallmentDate> generate(LocalDate actualDisbursementDate, int numberOfInstallments, GraceType graceType, int graceDuration, Short officeId) {
List<InstallmentDate> installmentDates = new ArrayList<InstallmentDate>();
int installmentsToSkip = getInstallmentSkipToStartRepayment(graceType, graceDuration);
installmentDates = getInstallmentDates(this.scheduledEvent, actualDisbursementDate, numberOfInstallments, installmentsToSkip, officeId);
return installmentDates;
}
private int getInstallmentSkipToStartRepayment(GraceType graceType, int graceDuration) {
// if LoanScheduleIndependentofMeeting is on, then repayments start on
// the first meeting in the schedule (#0)
int firstRepaymentInstallment = 0;
if (graceType == GraceType.PRINCIPALONLYGRACE || graceType == GraceType.NONE) {
return firstRepaymentInstallment;
}
return graceDuration + firstRepaymentInstallment;
}
private final List<InstallmentDate> getInstallmentDates(final ScheduledEvent scheduledEvent, LocalDate meetingStartDate, final int noOfInstallments, final int installmentToSkip, Short officeId) {
List<InstallmentDate> dueInstallmentDates = new ArrayList<InstallmentDate>();
if (noOfInstallments > 0) {
List<Days> workingDays = new FiscalCalendarRules().getWorkingDaysAsJodaTimeDays();
List<Holiday> holidays = new ArrayList<Holiday>();
DateTime startFromMeetingDate = meetingStartDate.toDateMidnight().toDateTime();
holidays = holidayDao.findAllHolidaysFromDateAndNext(officeId, startFromMeetingDate.toLocalDate().toString());
final int occurrences = noOfInstallments + installmentToSkip;
ScheduledDateGeneration dateGeneration = new HolidayAndWorkingDaysAndMoratoriaScheduledDateGeneration(workingDays, holidays);
List<Date> dueDates = new ArrayList<Date>();
DateTime startFromDayAfterAssignedMeetingDateRatherThanSkippingInstallments = startFromMeetingDate;
// ensure loans that are created or disbursed on a meeting date start on next valid meeting date and not todays meeting
// ensure loans that are created or disbursed before a meeting date start on next valid meeting date
startFromDayAfterAssignedMeetingDateRatherThanSkippingInstallments = startFromMeetingDate.plusDays(1);
List<DateTime> installmentDates = dateGeneration.generateScheduledDates(occurrences, startFromDayAfterAssignedMeetingDateRatherThanSkippingInstallments, scheduledEvent, false);
for (DateTime installmentDate : installmentDates) {
dueDates.add(installmentDate.toDate());
}
dueInstallmentDates = createInstallmentDates(installmentToSkip, dueDates);
}
return dueInstallmentDates;
}
private List<InstallmentDate> createInstallmentDates(final int installmentToSkip, final List<Date> dueDates) {
List<InstallmentDate> installmentDates = new ArrayList<InstallmentDate>();
int installmentId = 1;
for (Date date : dueDates) {
installmentDates.add(new InstallmentDate((short) installmentId++, date));
}
removeInstallmentsNeedNotPay(installmentToSkip, installmentDates);
return installmentDates;
}
private void removeInstallmentsNeedNotPay(final int installmentSkipToStartRepayment, final List<InstallmentDate> installmentDates) {
int removeCounter = 0;
for (int i = 0; i < installmentSkipToStartRepayment; i++) {
installmentDates.remove(removeCounter);
}
// re-adjust the installment ids
if (installmentSkipToStartRepayment > 0) {
int count = installmentDates.size();
for (int i = 0; i < count; i++) {
InstallmentDate instDate = installmentDates.get(i);
instDate.setInstallmentId(new Short(Integer.toString(i + 1)));
}
}
}
}