/*
* Copyright (c) 2005-2011 Grameen Foundation USA
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* See also http://www.apache.org/licenses/LICENSE-2.0.html for an
* explanation of the license and how it is applied.
*/
package org.mifos.accounts.loan.business.service;
import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mifos.accounts.business.AccountActionDateEntity;
import org.mifos.accounts.business.AccountBO;
import org.mifos.accounts.business.AccountPaymentEntity;
import org.mifos.accounts.loan.business.LoanBO;
import org.mifos.accounts.loan.business.LoanScheduleEntity;
import org.mifos.accounts.loan.business.OriginalLoanScheduleEntity;
import org.mifos.accounts.loan.business.ScheduleCalculatorAdaptor;
import org.mifos.accounts.loan.business.ScheduleMapper;
import org.mifos.accounts.loan.business.matchers.OriginalLoanScheduleEntitiesMatcher;
import org.mifos.accounts.loan.persistance.LegacyLoanDao;
import org.mifos.accounts.loan.schedule.calculation.ScheduleCalculator;
import org.mifos.accounts.loan.struts.actionforms.LoanAccountActionForm;
import org.mifos.accounts.loan.util.helpers.LoanConstants;
import org.mifos.accounts.loan.util.helpers.RepaymentScheduleInstallment;
import org.mifos.accounts.loan.util.helpers.RepaymentScheduleInstallmentBuilder;
import org.mifos.accounts.productdefinition.util.helpers.InterestType;
import org.mifos.accounts.util.helpers.PaymentData;
import org.mifos.accounts.util.helpers.PaymentStatus;
import org.mifos.application.holiday.business.service.HolidayService;
import org.mifos.application.master.business.InterestTypesEntity;
import org.mifos.application.master.business.MifosCurrency;
import org.mifos.config.business.service.ConfigurationBusinessService;
import org.mifos.config.persistence.ConfigurationPersistence;
import org.mifos.customers.business.CustomerBO;
import org.mifos.customers.personnel.business.PersonnelBO;
import org.mifos.framework.TestUtils;
import org.mifos.framework.exceptions.PersistenceException;
import org.mifos.framework.util.helpers.DateUtils;
import org.mifos.framework.util.helpers.Money;
import org.mifos.platform.validations.ErrorEntry;
import org.mifos.platform.validations.Errors;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class LoanBusinessServiceTest {
private Locale locale;
private RepaymentScheduleInstallmentBuilder installmentBuilder;
private MifosCurrency rupee;
@Mock
private LoanBusinessService loanBusinessService;
@Mock
private LoanBO loanBO;
@Mock
private HolidayService holidayService;
@Mock
private AccountPaymentEntity accountPaymentEntity;
@Mock
ScheduleCalculatorAdaptor scheduleCalculatorAdaptor;
@Mock
private PaymentData paymentData;
@Mock
private PersonnelBO personnel;
@Mock
private LegacyLoanDao legacyLoanDao;
private Short officeId;
@Mock
private ConfigurationBusinessService configService;
@Before
public void setupAndInjectDependencies() {
loanBusinessService = new LoanBusinessService(legacyLoanDao, configService, null, holidayService, scheduleCalculatorAdaptor);
locale = new Locale("en", "GB");
installmentBuilder = new RepaymentScheduleInstallmentBuilder(locale);
rupee = new MifosCurrency(Short.valueOf("1"), "Rupee", BigDecimal.valueOf(1), "INR");
officeId = Short.valueOf("1");
}
@Test
public void shouldApplyPaymentForInterestRecalculationLoanInterestType() {
when(loanBO.isDecliningBalanceInterestRecalculation()).thenReturn(true);
Date transactionDate = new Date();
Money totalAmount = new Money(rupee, 10.0);
when(paymentData.getTransactionDate()).thenReturn(transactionDate);
when(paymentData.getTotalAmount()).thenReturn(totalAmount);
when(paymentData.getPersonnel()).thenReturn(personnel);
loanBusinessService.applyPayment(paymentData, loanBO, accountPaymentEntity);
verify(scheduleCalculatorAdaptor, times(1)).applyPayment(loanBO, totalAmount,
transactionDate, personnel, accountPaymentEntity, false);
verify(loanBO, times(1)).isDecliningBalanceInterestRecalculation();
verify(paymentData).getTransactionDate();
verify(paymentData).getTotalAmount();
verify(paymentData).getPersonnel();
}
@Test
public void shouldApplyPaymentForNonInterestRecalculationLoanInterestType() {
when(loanBO.isDecliningBalanceInterestRecalculation()).thenReturn(false);
List<AccountActionDateEntity> installments = new ArrayList<AccountActionDateEntity>();
LoanScheduleEntity installment1 = mock(LoanScheduleEntity.class);
LoanScheduleEntity installment2 = mock(LoanScheduleEntity.class);
when(installment1.applyPayment(Mockito.<AccountPaymentEntity>any(), Mockito.<Money>any(), Mockito.eq(personnel), Mockito.<Date>any())).thenReturn(new Money(rupee, 100d));
when(installment2.applyPayment(Mockito.<AccountPaymentEntity>any(), Mockito.<Money>any(), Mockito.eq(personnel), Mockito.<Date>any())).thenReturn(new Money(rupee, 0d));
installments.add(installment1);
installments.add(installment2);
when(loanBO.getAccountActionDatesSortedByInstallmentId()).thenReturn(installments);
Date transactionDate = new Date();
Money totalAmount = new Money(rupee, 10.0);
when(paymentData.getTransactionDate()).thenReturn(transactionDate);
when(paymentData.getTotalAmount()).thenReturn(totalAmount);
when(paymentData.getPersonnel()).thenReturn(personnel);
loanBusinessService.applyPayment(paymentData, loanBO, accountPaymentEntity);
verify(scheduleCalculatorAdaptor, times(0)).applyPayment(loanBO, totalAmount,
transactionDate, personnel, accountPaymentEntity, false);
verify(loanBO, times(1)).getAccountActionDatesSortedByInstallmentId();
verify(loanBO, times(1)).isDecliningBalanceInterestRecalculation();
verify(paymentData).getTransactionDate();
verify(paymentData).getTotalAmount();
verify(paymentData).getPersonnel();
verify(installment1).applyPayment(Matchers.<AccountPaymentEntity>any(), Matchers.<Money>any(), Matchers.eq(personnel), Matchers.<Date>any());
verify(installment2).applyPayment(Matchers.<AccountPaymentEntity>any(), Matchers.<Money>any(), Matchers.eq(personnel), Matchers.<Date>any());
}
@Test
public void shouldComputeVariableInstallmentScheduleForVariableInstallmentsIfVariableInstallmentsIsEnabled() throws Exception {
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("01-Sep-2010", 1, "94.4", "4.6", "1", "75", "0", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("08-Sep-2010", 2, "94.8", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("15-Sep-2010", 3, "95.3", "3.7", "1", "100", "0", "0");
LoanAccountActionForm loanAccountActionForm = mock(LoanAccountActionForm.class);
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3);
when(loanAccountActionForm.getLoanAmount()).thenReturn("1000");
when(loanAccountActionForm.getLoanAmountValue()).thenReturn(new Money(rupee, "1000"));
when(loanAccountActionForm.getInterestRate()).thenReturn("24");
when(loanAccountActionForm.isVariableInstallmentsAllowed()).thenReturn(true);
when(legacyLoanDao.findBySystemId(anyString())).thenReturn(loanBO);
when(loanBO.toRepaymentScheduleDto(locale)).thenReturn(installments);
loanBusinessService.
applyDailyInterestRatesWhereApplicable(new LoanScheduleGenerationDto(TestUtils.getDate(22, 8, 2010),
loanBO, loanAccountActionForm.isVariableInstallmentsAllowed(), loanAccountActionForm.getLoanAmountValue(),
loanAccountActionForm.getInterestDoubleValue()), locale);
verify(loanBO).updateInstallmentSchedule(installments);
verify(loanBO).toRepaymentScheduleDto(locale);
}
@Test
public void shouldComputeVariableInstallmentScheduleForVariableInstallmentsIfVariableInstallmentsIsEnabledAndInstallmentsArePassed() {
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("01-Sep-2010", 1, "94.4", "4.6", "1", "75", "0", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("08-Sep-2010", 2, "94.8", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("15-Sep-2010", 3, "95.3", "3.7", "1", "100", "0", "0");
LoanAccountActionForm loanAccountActionForm = mock(LoanAccountActionForm.class);
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3);
when(loanAccountActionForm.getLoanAmount()).thenReturn("1000");
when(loanAccountActionForm.getLoanAmountValue()).thenReturn(new Money(rupee, "1000"));
when(loanAccountActionForm.getInterestRate()).thenReturn("24");
when(loanAccountActionForm.isVariableInstallmentsAllowed()).thenReturn(true);
loanBusinessService.
applyDailyInterestRatesWhereApplicable(new LoanScheduleGenerationDto(TestUtils.getDate(22, 8, 2010),
loanBO, loanAccountActionForm.isVariableInstallmentsAllowed(), loanAccountActionForm.getLoanAmountValue(),
loanAccountActionForm.getInterestDoubleValue()), installments);
verify(loanBO).updateInstallmentSchedule(installments);
}
@Test
public void shouldComputeVariableInstallmentScheduleForVariableInstallmentsIfInterestTypeIsDecliningPrincipalBalance() throws Exception {
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("01-Sep-2010", 1, "94.4", "4.6", "1", "75", "0", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("08-Sep-2010", 2, "94.8", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("15-Sep-2010", 3, "95.3", "3.7", "1", "100", "0", "0");
LoanAccountActionForm loanAccountActionForm = mock(LoanAccountActionForm.class);
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3);
when(loanAccountActionForm.getLoanAmount()).thenReturn("1000");
when(loanAccountActionForm.getLoanAmountValue()).thenReturn(new Money(rupee, "1000"));
when(loanAccountActionForm.getInterestRate()).thenReturn("24");
when(loanAccountActionForm.isVariableInstallmentsAllowed()).thenReturn(false);
when(legacyLoanDao.findBySystemId(anyString())).thenReturn(loanBO);
when(loanBO.toRepaymentScheduleDto(locale)).thenReturn(installments);
when(loanBO.isDecliningBalanceInterestRecalculation()).thenReturn(true);
loanBusinessService.applyDailyInterestRatesWhereApplicable(new LoanScheduleGenerationDto(TestUtils.getDate(22, 8, 2010),
loanBO, loanAccountActionForm.isVariableInstallmentsAllowed(), loanAccountActionForm.getLoanAmountValue(),
loanAccountActionForm.getInterestDoubleValue()), locale);
verify(loanBO).updateInstallmentSchedule(installments);
verify(loanBO).toRepaymentScheduleDto(locale);
}
@SuppressWarnings("unchecked")
@Test
public void shouldNotComputeVariableInstallmentSchedule() throws Exception {
LoanAccountActionForm loanAccountActionForm = mock(LoanAccountActionForm.class);
when(loanAccountActionForm.isVariableInstallmentsAllowed()).thenReturn(false);
InterestTypesEntity interestTypesEntity = new InterestTypesEntity(InterestType.DECLINING);
when(loanBO.getInterestType()).thenReturn(interestTypesEntity);
when(legacyLoanDao.findBySystemId(anyString())).thenReturn(loanBO);
loanBusinessService.applyDailyInterestRatesWhereApplicable(new LoanScheduleGenerationDto(TestUtils.getDate(22, 8, 2010),
loanBO, loanAccountActionForm.isVariableInstallmentsAllowed(), loanAccountActionForm.getLoanAmountValue(),
loanAccountActionForm.getInterestDoubleValue()), locale);
verify(loanBO, never()).updateInstallmentSchedule(any(List.class));
}
@Test
public void shouldGenerateMonthlyInstallmentScheduleFromRepaymentScheduleUsingDailyInterest() {
MifosCurrency rupee = new MifosCurrency(Short.valueOf("1"), "Rupee", BigDecimal.valueOf(1), "INR");
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("25-Sep-2010", 1, "78.6", "20.4", "1", "100", "0", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("25-Oct-2010", 2, "182.8", "16.2", "1", "200", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("25-Nov-2010", 3, "186.0", "13.0", "1", "200", "0", "0");
RepaymentScheduleInstallment installment4 =
getRepaymentScheduleInstallment("25-Dec-2010", 4, "452.6", "8.9", "1", "462.5", "0", "0");
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3, installment4);
Date disbursementDate = TestUtils.getDate(25, 8, 2010);
final Money loanAmount = new Money(rupee, "1000");
loanBusinessService.applyDailyInterestRates(new LoanScheduleGenerationDto(disbursementDate, loanAmount, 24d, installments), false);
assertInstallment(installment1, "78.6", "20.4");
assertInstallment(installment2, "180.8", "18.2");
assertInstallment(installment3, "183.9", "15.1");
assertInstallment(installment4, "556.7", "11.0");
}
//Test for the bug fix
@Test
public void shouldNotModifyPrincipalEvenIfMiscChargeIsPresent() {
MifosCurrency rupee = new MifosCurrency(Short.valueOf("1"), "Rupee", BigDecimal.valueOf(1), "INR");
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("25-Sep-2010", 1, "78.6", "20.4", "1", "200", "100", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("25-Oct-2010", 2, "182.8", "16.2", "1", "200", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("25-Nov-2010", 3, "186.0", "13.0", "1", "200", "0", "0");
RepaymentScheduleInstallment installment4 =
getRepaymentScheduleInstallment("25-Dec-2010", 4, "452.6", "8.9", "1", "462.5", "0", "0");
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3, installment4);
Date disbursementDate = TestUtils.getDate(25, 8, 2010);
final Money loanAmount = new Money(rupee, "1000");
loanBusinessService.applyDailyInterestRates(new LoanScheduleGenerationDto(disbursementDate, loanAmount, 24d, installments), false);
assertInstallment(installment1, "78.6", "20.4");
assertInstallment(installment2, "180.8", "18.2");
assertInstallment(installment3, "183.9", "15.1");
assertInstallment(installment4, "556.7", "11.0");
}
@Test
public void shouldGenerateWeeklyInstallmentScheduleFromRepaymentScheduleUsingDailyInterest() {
MifosCurrency rupee = new MifosCurrency(Short.valueOf("1"), "Rupee", BigDecimal.valueOf(1), "INR");
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("01-Sep-2010", 1, "194.4", "4.6", "1", "100", "0", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("08-Sep-2010", 2, "195.3", "3.7", "1", "200", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("15-Sep-2010", 3, "196.2", "2.8", "1", "200", "0", "0");
RepaymentScheduleInstallment installment4 =
getRepaymentScheduleInstallment("22-Sep-2010", 4, "414.1", "1.9", "1", "417.0", "0", "0");
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3, installment4);
Date disbursementDate = TestUtils.getDate(25, 8, 2010);
final Money loanAmount = new Money(rupee, "1000");
loanBusinessService.applyDailyInterestRates(new LoanScheduleGenerationDto(disbursementDate, loanAmount, 24d, installments), false);
assertInstallment(installment1, "94.4", "4.6");
assertInstallment(installment2, "194.8", "4.2");
assertInstallment(installment3, "195.7", "3.3");
assertInstallment(installment4, "515.0", "2.4");
}
@Test
public void shouldGenerateInstallmentScheduleFromRepaymentScheduleUsingDailyInterest() {
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("01-Sep-2010", 1, "94.4", "4.6", "1", "75", "0", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("08-Sep-2010", 2, "94.8", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("15-Sep-2010", 3, "95.3", "3.7", "1", "100", "0", "0");
RepaymentScheduleInstallment installment4 =
getRepaymentScheduleInstallment("15-Oct-2010", 4, "84.9", "14.1", "1", "100", "0", "0");
RepaymentScheduleInstallment installment5 =
getRepaymentScheduleInstallment("25-Oct-2010", 5, "94.9", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment6 =
getRepaymentScheduleInstallment("01-Nov-2010", 6, "96.5", "2.5", "1", "100", "0", "0");
RepaymentScheduleInstallment installment7 =
getRepaymentScheduleInstallment("18-Nov-2010", 7, "439.2", "4.9", "1", "445.1", "0", "0");
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3,
installment4, installment5, installment6, installment7);
Date disbursementDate = TestUtils.getDate(25, 8, 2010);
final Money loanAmount = new Money(rupee, "1000");
loanBusinessService.applyDailyInterestRates(new LoanScheduleGenerationDto(disbursementDate, loanAmount, 24d, installments), false);
assertInstallment(installment1, "69.4", "4.6");
assertInstallment(installment2, "94.7", "4.3");
assertInstallment(installment3, "95.2", "3.8");
assertInstallment(installment4, "84.4", "14.6");
assertInstallment(installment5, "94.7", "4.3");
assertInstallment(installment6, "96.4", "2.6");
assertInstallment(installment7, "465.2", "5.2");
}
@Test
public void shouldMaintainInstallmentGapsPostDisbursal() {
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("01-Sep-2010", 1, "94.4", "4.6", "1", "75", "0", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("08-Sep-2010", 2, "94.8", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("15-Sep-2010", 3, "95.3", "3.7", "1", "100", "0", "0");
RepaymentScheduleInstallment installment4 =
getRepaymentScheduleInstallment("15-Oct-2010", 4, "84.9", "14.1", "1", "100", "0", "0");
RepaymentScheduleInstallment installment5 =
getRepaymentScheduleInstallment("25-Oct-2010", 5, "94.9", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment6 =
getRepaymentScheduleInstallment("01-Nov-2010", 6, "96.5", "2.5", "1", "100", "0", "0");
RepaymentScheduleInstallment installment7 =
getRepaymentScheduleInstallment("18-Nov-2010", 7, "439.2", "4.9", "1", "445.1", "0", "0");
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3,
installment4, installment5, installment6, installment7);
Date initialDisbursementDate = TestUtils.getDate(25, 8, 2010);
Date disbursementDate = TestUtils.getDate(30, 8, 2010);
when(holidayService.isFutureRepaymentHoliday(Matchers.<Calendar>any(), eq(officeId))).thenReturn(true);
when(holidayService.getNextWorkingDay(Matchers.<Date>any(), eq(officeId))).thenReturn(TestUtils.getDate(6, 9, 2010),
TestUtils.getDate(13, 9, 2010), TestUtils.getDate(20, 9, 2010), TestUtils.getDate(20, 10, 2010), TestUtils.getDate(30, 10, 2010),
TestUtils.getDate(6, 11, 2010), TestUtils.getDate(23, 11, 2010));
loanBusinessService.adjustInstallmentGapsPostDisbursal(installments, initialDisbursementDate, disbursementDate, officeId);
assertInstallmentDueDate(installment1, "06-Sep-2010");
assertInstallmentDueDate(installment2, "13-Sep-2010");
assertInstallmentDueDate(installment3, "20-Sep-2010");
assertInstallmentDueDate(installment4, "20-Oct-2010");
assertInstallmentDueDate(installment5, "30-Oct-2010");
assertInstallmentDueDate(installment6, "06-Nov-2010");
assertInstallmentDueDate(installment7, "23-Nov-2010");
verify(holidayService, times(7)).isFutureRepaymentHoliday(Matchers.<Calendar>any(), eq(officeId));
verify(holidayService, times(7)).getNextWorkingDay(Matchers.<Date>any(), eq(officeId));
}
@Test
public void shouldMaintainInstallmentGapsPostDisbursalWithNewDisbursalDateBeforeOldDisbursalDate() {
RepaymentScheduleInstallment installment1 =
getRepaymentScheduleInstallment("01-Sep-2010", 1, "94.4", "4.6", "1", "75", "0", "0");
RepaymentScheduleInstallment installment2 =
getRepaymentScheduleInstallment("08-Sep-2010", 2, "94.8", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment3 =
getRepaymentScheduleInstallment("15-Sep-2010", 3, "95.3", "3.7", "1", "100", "0", "0");
RepaymentScheduleInstallment installment4 =
getRepaymentScheduleInstallment("15-Oct-2010", 4, "84.9", "14.1", "1", "100", "0", "0");
RepaymentScheduleInstallment installment5 =
getRepaymentScheduleInstallment("25-Oct-2010", 5, "94.9", "4.2", "1", "100", "0", "0");
RepaymentScheduleInstallment installment6 =
getRepaymentScheduleInstallment("01-Nov-2010", 6, "96.5", "2.5", "1", "100", "0", "0");
RepaymentScheduleInstallment installment7 =
getRepaymentScheduleInstallment("18-Nov-2010", 7, "439.2", "4.9", "1", "445.1", "0", "0");
List<RepaymentScheduleInstallment> installments = asList(installment1, installment2, installment3,
installment4, installment5, installment6, installment7);
Date initialDisbursementDate = TestUtils.getDate(25, 8, 2010);
Date disbursementDate = TestUtils.getDate(20, 8, 2010);
when(holidayService.isFutureRepaymentHoliday(Matchers.<Calendar>any(), eq(officeId))).thenReturn(true);
when(holidayService.getNextWorkingDay(Matchers.<Date>any(), eq(officeId))).thenReturn(TestUtils.getDate(27, 8, 2010),
TestUtils.getDate(3, 9, 2010), TestUtils.getDate(10, 9, 2010), TestUtils.getDate(11, 10, 2010), TestUtils.getDate(21, 10, 2010),
TestUtils.getDate(28, 10, 2010), TestUtils.getDate(15, 11, 2010));
loanBusinessService.adjustInstallmentGapsPostDisbursal(installments, initialDisbursementDate, disbursementDate, officeId);
assertInstallmentDueDate(installment1, "27-Aug-2010");
assertInstallmentDueDate(installment2, "03-Sep-2010");
assertInstallmentDueDate(installment3, "10-Sep-2010");
assertInstallmentDueDate(installment4, "11-Oct-2010");
assertInstallmentDueDate(installment5, "21-Oct-2010");
assertInstallmentDueDate(installment6, "28-Oct-2010");
assertInstallmentDueDate(installment7, "15-Nov-2010");
verify(holidayService, times(7)).isFutureRepaymentHoliday(Matchers.<Calendar>any(), eq(officeId));
verify(holidayService, times(7)).getNextWorkingDay(Matchers.<Date>any(), eq(officeId));
}
@Test
public void persistOriginalSchedule() throws PersistenceException {
Set<LoanScheduleEntity> installments = new LinkedHashSet<LoanScheduleEntity>();
MifosCurrency mifosCurrency = new MifosCurrency(Short.valueOf("1"), "Rupee", BigDecimal.valueOf(1), "INR");
Money money = new Money(mifosCurrency,"123");
AccountBO accountBO = mock(AccountBO.class);
CustomerBO customerBO = mock(CustomerBO.class);
when(accountBO.getCurrency()).thenReturn(mifosCurrency);
LoanScheduleEntity loanScheduleEntity = new LoanScheduleEntity(accountBO, customerBO, new Short("1"),
new java.sql.Date(new Date().getTime()), PaymentStatus.UNPAID, money,money);
installments.add(loanScheduleEntity);
when(loanBO.getLoanScheduleEntities()).thenReturn(installments);
loanBusinessService.persistOriginalSchedule(loanBO);
ArrayList<OriginalLoanScheduleEntity> expected = new ArrayList<OriginalLoanScheduleEntity>();
expected.add(new OriginalLoanScheduleEntity(loanScheduleEntity));
verify(legacyLoanDao).saveOriginalSchedule(Mockito.argThat(
new OriginalLoanScheduleEntitiesMatcher(expected)
));
}
@Test
public void originalLoanScheduleShouldPersistMiscFee() throws PersistenceException {
Set<LoanScheduleEntity> installments = new LinkedHashSet<LoanScheduleEntity>();
MifosCurrency mifosCurrency = new MifosCurrency(Short.valueOf("1"), "Rupee", BigDecimal.valueOf(1), "INR");
Money money = new Money(mifosCurrency,"123");
AccountBO accountBO = mock(AccountBO.class);
CustomerBO customerBO = mock(CustomerBO.class);
when(accountBO.getCurrency()).thenReturn(mifosCurrency);
LoanScheduleEntity loanScheduleEntity = new LoanScheduleEntity(accountBO, customerBO, new Short("1"),
new java.sql.Date(new Date().getTime()), PaymentStatus.UNPAID, money,money);
loanScheduleEntity.setMiscFee(money);
installments.add(loanScheduleEntity);
when(loanBO.getLoanScheduleEntities()).thenReturn(installments);
loanBusinessService.persistOriginalSchedule(loanBO);
ArrayList<OriginalLoanScheduleEntity> expected = new ArrayList<OriginalLoanScheduleEntity>();
OriginalLoanScheduleEntity originalLoanScheduleEntity = new OriginalLoanScheduleEntity(loanScheduleEntity);
assertEquals(originalLoanScheduleEntity.getMiscFee(),loanScheduleEntity.getMiscFee());
expected.add(originalLoanScheduleEntity);
verify(legacyLoanDao).saveOriginalSchedule(Mockito.argThat(
new OriginalLoanScheduleEntitiesMatcher(expected)
));
}
@Test
public void shouldRetrieveOriginalLoanSchedule() throws PersistenceException {
Integer accountId = 1;
ArrayList<OriginalLoanScheduleEntity> expected = new ArrayList<OriginalLoanScheduleEntity>();
when(legacyLoanDao.getOriginalLoanScheduleEntity(accountId)).thenReturn(expected);
List<OriginalLoanScheduleEntity> loanScheduleEntities = loanBusinessService.retrieveOriginalLoanSchedule(accountId);
Assert.assertNotNull(loanScheduleEntities);
verify(legacyLoanDao).getOriginalLoanScheduleEntity(accountId);
Assert.assertEquals(expected,loanScheduleEntities);
}
@Test
public void shouldComputeExtraInterestAsOfDateLaterThanLastPaymentDate() {
Date asOfDate = TestUtils.getDate(10, 9, 2010);
Date lastPaymentDate = TestUtils.getDate(1, 9, 2010);
when(accountPaymentEntity.getPaymentDate()).thenReturn(lastPaymentDate);
when(loanBO.findMostRecentNonzeroPaymentByPaymentDate()).thenReturn(accountPaymentEntity);
when(loanBO.isDecliningBalanceInterestRecalculation()).thenReturn(true);
Errors errors = loanBusinessService.computeExtraInterest(loanBO, asOfDate);
assertThat(errors, is(not(nullValue())));
assertThat(errors.hasErrors(), is(false));
verify(scheduleCalculatorAdaptor).computeExtraInterest(loanBO, asOfDate);
}
@Test
public void shouldComputeExtraInterestAsOfDateLaterThanLastPaymentDateAndNoLastPayment() {
Date asOfDate = TestUtils.getDate(10, 9, 2010);
when(loanBO.findMostRecentNonzeroPaymentByPaymentDate()).thenReturn(null);
when(loanBO.isDecliningBalanceInterestRecalculation()).thenReturn(true);
Errors errors = loanBusinessService.computeExtraInterest(loanBO, asOfDate);
assertThat(errors, is(not(nullValue())));
assertThat(errors.hasErrors(), is(false));
verify(scheduleCalculatorAdaptor).computeExtraInterest(loanBO, asOfDate);
}
@Test
public void shouldComputeExtraInterestAsOfDateEarlierThanLastPaymentDate() {
Date asOfDate = TestUtils.getDate(1, 9, 2010);
Date lastPaymentDate = TestUtils.getDate(10, 9, 2010);
when(accountPaymentEntity.getPaymentDate()).thenReturn(lastPaymentDate);
when(loanBO.findMostRecentNonzeroPaymentByPaymentDate()).thenReturn(accountPaymentEntity);
when(loanBO.isDecliningBalanceInterestRecalculation()).thenReturn(true);
Errors errors = loanBusinessService.computeExtraInterest(loanBO, asOfDate);
assertThat(errors, is(not(nullValue())));
assertThat(errors.hasErrors(), is(true));
List<ErrorEntry> errorEntries = errors.getErrorEntries();
assertThat(errorEntries, is(not(nullValue())));
assertThat(errorEntries.size(), is(1));
assertThat(errorEntries.get(0).getErrorCode(), is(LoanConstants.CANNOT_VIEW_REPAYMENT_SCHEDULE));
verify(scheduleCalculatorAdaptor, never()).computeExtraInterest(loanBO, asOfDate);
}
private void assertInstallmentDueDate(RepaymentScheduleInstallment installment, String expectedDueDate) {
String actualDueDate = DateUtils.getDBtoUserFormatString(installment.getDueDateValue(), locale);
assertThat(actualDueDate, is(expectedDueDate));
}
private void assertInstallment(RepaymentScheduleInstallment installment, String principal, String interest) {
assertThat(installment.getPrincipal().toString(Short.valueOf("1")), is(principal));
assertThat(installment.getInterest().toString(Short.valueOf("1")), is(interest));
}
private RepaymentScheduleInstallment getRepaymentScheduleInstallment(String dueDate, int installment,
String principal, String interest,
String fees, String total, String miscFee, String miscPenality) {
return installmentBuilder.reset(locale).withInstallment(installment).withDueDateValue(dueDate).
withPrincipal(new Money(rupee, principal)).withInterest(new Money(rupee, interest)).
withFees(new Money(rupee, fees)).withMiscFee(new Money(rupee, miscFee)).withMiscPenality(new Money(rupee, miscPenality)).withTotalValue(total).build();
}
}