/* * 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; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.mifos.framework.TestUtils.getDate; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Set; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mifos.accounts.business.AccountActionDateEntity; import org.mifos.accounts.business.AccountActionEntity; import org.mifos.accounts.business.AccountPaymentEntity; import org.mifos.accounts.business.AccountPaymentEntityBuilder; import org.mifos.accounts.business.AccountTrxnEntity; import org.mifos.accounts.exceptions.AccountException; import org.mifos.accounts.loan.persistance.LegacyLoanDao; 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.persistence.LegacyAccountDao; import org.mifos.accounts.productdefinition.business.LoanOfferingBO; import org.mifos.accounts.util.helpers.AccountActionTypes; import org.mifos.accounts.util.helpers.PaymentStatus; import org.mifos.application.master.business.MifosCurrency; import org.mifos.customers.business.CustomerBO; import org.mifos.customers.personnel.business.PersonnelBO; import org.mifos.domain.builders.LoanAccountBuilder; import org.mifos.framework.exceptions.PersistenceException; import org.mifos.framework.util.helpers.Money; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.test.annotation.ExpectedException; @RunWith(MockitoJUnitRunner.class) public class LoanBOTest { @Mock private LoanScheduleEntity loanScheduleEntity; @Mock private LoanOfferingBO loanOfferingBO; private MifosCurrency rupee = new MifosCurrency(Short.valueOf("1"), "Rupee", BigDecimal.valueOf(1), "INR"); private Locale locale = new Locale("en", "GB"); private RepaymentScheduleInstallmentBuilder installmentBuilder = new RepaymentScheduleInstallmentBuilder(locale); @Test public void testWaiverAmount() { LoanBO loanBO = new LoanBO() { @Override public AccountActionDateEntity getDetailsOfNextInstallment() { return loanScheduleEntity; } }; Mockito.when(loanScheduleEntity.getInterestDue()).thenReturn(new Money(rupee, "42")); Assert.assertEquals(loanBO.waiverAmount(), new Money(rupee, "42")); Mockito.verify(loanScheduleEntity, Mockito.times(1)).getInterestDue(); } @Test public void testWaiverAmountWhenNextInstallmentIsNotPresent() { LoanBO loanBO = new LoanBO() { @Override public AccountActionDateEntity getDetailsOfNextInstallment() { return null; } @Override public LoanOfferingBO getLoanOffering() { return loanOfferingBO; } }; Mockito.when(loanOfferingBO.getCurrency()).thenReturn(rupee); Assert.assertEquals(loanBO.waiverAmount(), new Money(rupee, "0")); Mockito.verify(loanOfferingBO, Mockito.times(1)).getCurrency(); } @Test public void testWaiverAmountWhenNextInstallmentIsPaid() { LoanBO loanBO = new LoanBO() { @Override public AccountActionDateEntity getDetailsOfNextInstallment() { return loanScheduleEntity; } @Override public LoanOfferingBO getLoanOffering() { return loanOfferingBO; } }; Mockito.when(loanOfferingBO.getCurrency()).thenReturn(rupee); Mockito.when(loanScheduleEntity.isPaid()).thenReturn(true); Assert.assertEquals(loanBO.waiverAmount(), new Money(rupee, "0")); Mockito.verify(loanOfferingBO, Mockito.times(1)).getCurrency(); Mockito.verify(loanScheduleEntity, Mockito.times(1)).isPaid(); } /** * does't work when changing applicatonConfiguration.custom.properties file. * Need to pull out static references to AccountingRules in used classes. */ @Ignore @Test public void testCopyInstallmentSchedule() { Money.setDefaultCurrency(rupee); LoanBO loanBO = new LoanAccountBuilder().build(); loanBO.addAccountActionDate(getLoanScheduleEntity(rupee, getDate(23, 10, 2010), "100", "10", "1", Money.zero(rupee))); loanBO.addAccountActionDate(getLoanScheduleEntity(rupee, getDate(23, 11, 2010), "100", "10", "2", Money.zero(rupee))); loanBO.addAccountActionDate(getLoanScheduleEntity(rupee, getDate(23, 12, 2010), "100", "10", "3", Money.zero(rupee))); List<RepaymentScheduleInstallment> installments = new ArrayList<RepaymentScheduleInstallment>(); installments.add(getRepaymentScheduleInstallment("24-Oct-2010", 1, "123", "12")); installments.add(getRepaymentScheduleInstallment("24-Nov-2010", 2, "231", "23")); installments.add(getRepaymentScheduleInstallment("24-Dec-2010", 3, "312", "31")); loanBO.updateInstallmentSchedule(installments); Set<LoanScheduleEntity> loanScheduleEntities = loanBO.getLoanScheduleEntities(); LoanScheduleEntity[] loanScheduleEntitiesArr = loanScheduleEntities.toArray(new LoanScheduleEntity[loanScheduleEntities.size()]); assertLoanScheduleEntity(loanScheduleEntitiesArr[0], "123.0", "12.0", "2010-10-24"); assertLoanScheduleEntity(loanScheduleEntitiesArr[1], "231.0", "23.0", "2010-11-24"); assertLoanScheduleEntity(loanScheduleEntitiesArr[2], "312.0", "31.0", "2010-12-24"); } /** * does't work when changing applicatonConfiguration.custom.properties file. * Need to pull out static references to AccountingRules in used classes. */ @Ignore @Test public void testLoanSummaryShouldBeUpdateOnInstallmentScheduleUpdate() { Money.setDefaultCurrency(rupee); LoanBO loanBO = new LoanAccountBuilder().build(); loanBO.addAccountActionDate(getLoanScheduleEntity(rupee, getDate(23, 10, 2010), "100", "10", "1", Money.zero(rupee))); loanBO.addAccountActionDate(getLoanScheduleEntity(rupee, getDate(23, 11, 2010), "100", "10", "2", Money.zero(rupee))); loanBO.addAccountActionDate(getLoanScheduleEntity(rupee, getDate(23, 12, 2010), "100", "10", "3", Money.zero(rupee))); List<RepaymentScheduleInstallment> installments = new ArrayList<RepaymentScheduleInstallment>(); installments.add(getRepaymentScheduleInstallment("24-Oct-2010", 1, "123", "12")); installments.add(getRepaymentScheduleInstallment("24-Nov-2010", 2, "231", "23")); installments.add(getRepaymentScheduleInstallment("24-Dec-2010", 3, "312", "31")); loanBO.updateInstallmentSchedule(installments); Set<LoanScheduleEntity> loanScheduleEntities = loanBO.getLoanScheduleEntities(); LoanScheduleEntity[] loanScheduleEntitiesArr = loanScheduleEntities.toArray(new LoanScheduleEntity[loanScheduleEntities.size()]); assertLoanScheduleEntity(loanScheduleEntitiesArr[0], "123.0", "12.0", "2010-10-24"); assertLoanScheduleEntity(loanScheduleEntitiesArr[1], "231.0", "23.0", "2010-11-24"); assertLoanScheduleEntity(loanScheduleEntitiesArr[2], "312.0", "31.0", "2010-12-24"); LoanSummaryEntity loanSummary = loanBO.getLoanSummary(); assertEquals("666.0",loanSummary.getOriginalPrincipal().toString()); assertEquals("66.0",loanSummary.getOriginalInterest().toString()); } private void assertLoanScheduleEntity(LoanScheduleEntity loanScheduleEntity, String pricipal, String interest, String dueDate) { Assert.assertEquals(loanScheduleEntity.getPrincipal().toString(), pricipal); Assert.assertEquals(loanScheduleEntity.getInterest().toString(), interest); Assert.assertEquals(loanScheduleEntity.getActionDate().toString(), dueDate); } @Test @ExpectedException(value = AccountException.class) public void testInvalidConnectionForSave() throws PersistenceException { final LegacyAccountDao legacyAccountDao = mock(LegacyAccountDao.class); LoanBO loanBO = new LoanBO() { @Override public LegacyAccountDao getlegacyAccountDao() { return legacyAccountDao; } }; try { when(legacyAccountDao.createOrUpdate(loanBO)).thenThrow(new PersistenceException("some exception")); loanBO.update(); junit.framework.Assert.fail("should fail because of invalid session"); } catch (AccountException e) { } } @Test public void repayInstallmentsShouldPopulateCalculatedInterestsForDIPBLoans() throws PersistenceException { final LegacyLoanDao legacyLoanDao = mock(LegacyLoanDao.class); final CustomerBO customerBO = mock(CustomerBO.class); final LoanSummaryEntity loanSummaryEntity = mock(LoanSummaryEntity.class); LoanBO loanBO = new LoanBO(){ @Override public boolean isDecliningBalanceInterestRecalculation() { return true; } @Override public LegacyLoanDao getlegacyLoanDao() { return legacyLoanDao; } @Override public CustomerBO getCustomer() { return customerBO; } @Override public LoanSummaryEntity getLoanSummary() { return loanSummaryEntity; } }; AccountActionTypes accountActionTypes = AccountActionTypes.LOAN_REPAYMENT; AccountActionEntity accountActionEntity = mock(AccountActionEntity.class); AccountPaymentEntity accountPaymentEntity = new AccountPaymentEntityBuilder().with(loanBO).build(); PersonnelBO user = new PersonnelBO(); Money extraInterestDue = new Money(rupee, "0.98"); Money interest = new Money(rupee, "10"); Money interestDue = new Money(rupee, "2.07"); when(legacyLoanDao.getPersistentObject(AccountActionEntity.class, accountActionTypes.getValue())).thenReturn(accountActionEntity); when(loanScheduleEntity.getPrincipalDue()).thenReturn(new Money(rupee,"1000")); when(loanScheduleEntity.getTotalFeeDueWithMiscFeeDue()).thenReturn(new Money(rupee,"10")); when(loanScheduleEntity.getPenaltyDue()).thenReturn(new Money(rupee,"10")); when(loanScheduleEntity.getPenalty()).thenReturn(new Money(rupee,"100")); when(loanScheduleEntity.getExtraInterestDue()).thenReturn(extraInterestDue); when(loanScheduleEntity.getExtraInterestPaid()).thenReturn(extraInterestDue); when(loanScheduleEntity.getInterest()).thenReturn(interest); when(loanScheduleEntity.getInterestDue()).thenReturn(interestDue); loanBO.repayInstallment(loanScheduleEntity,accountPaymentEntity,accountActionTypes,user,"", interestDue); Set<AccountTrxnEntity> accountTrxns = accountPaymentEntity.getAccountTrxns(); AccountTrxnEntity accountTrxnEntity = accountTrxns.toArray(new AccountTrxnEntity[accountTrxns.size()])[0]; LoanTrxnDetailEntity loanTrxnDetailEntity = (LoanTrxnDetailEntity) accountTrxnEntity; assertThat(loanTrxnDetailEntity.getInterestAmount().getAmount().doubleValue(), is(3.05)); CalculatedInterestOnPayment calculatedInterestOnPayment = loanTrxnDetailEntity.getCalculatedInterestOnPayment(); assertNotNull(calculatedInterestOnPayment); assertThat(calculatedInterestOnPayment.getExtraInterestPaid(),is(extraInterestDue)); assertThat(calculatedInterestOnPayment.getInterestDueTillPaid(),is(interestDue)); assertThat(calculatedInterestOnPayment.getOriginalInterest(),is(interest)); Mockito.verify(loanScheduleEntity).makeEarlyRepaymentEntries(LoanConstants.PAY_FEES_PENALTY_INTEREST, interestDue, accountPaymentEntity.getPaymentDate()); } @Test public void repayInstallmentsShouldPopulateCalculatedInterestsForDIPBLoansWithWaiverInterest() throws PersistenceException { final LegacyLoanDao legacyLoanDao = mock(LegacyLoanDao.class); final CustomerBO customerBO = mock(CustomerBO.class); final LoanSummaryEntity loanSummaryEntity = mock(LoanSummaryEntity.class); LoanBO loanBO = new LoanBO(){ @Override public boolean isDecliningBalanceInterestRecalculation() { return true; } @Override public LegacyLoanDao getlegacyLoanDao() { return legacyLoanDao; } @Override public CustomerBO getCustomer() { return customerBO; } @Override public LoanSummaryEntity getLoanSummary() { return loanSummaryEntity; } @Override public MifosCurrency getCurrency() { return rupee; } }; AccountActionTypes accountActionTypes = AccountActionTypes.LOAN_REPAYMENT; AccountActionEntity accountActionEntity = mock(AccountActionEntity.class); AccountPaymentEntity accountPaymentEntity = new AccountPaymentEntityBuilder().with(loanBO).build(); PersonnelBO user = new PersonnelBO(); Money extraInterestDue = new Money(rupee, "0.98"); Money interest = new Money(rupee, "10"); Money interestDue = new Money(rupee, "0"); when(legacyLoanDao.getPersistentObject(AccountActionEntity.class, accountActionTypes.getValue())).thenReturn(accountActionEntity); when(loanScheduleEntity.getPrincipalDue()).thenReturn(new Money(rupee,"1000")); when(loanScheduleEntity.getTotalFeeDueWithMiscFeeDue()).thenReturn(new Money(rupee,"10")); when(loanScheduleEntity.getPenaltyDue()).thenReturn(new Money(rupee,"10")); when(loanScheduleEntity.getPenalty()).thenReturn(new Money(rupee,"100")); when(loanScheduleEntity.getExtraInterestDue()).thenReturn(extraInterestDue); when(loanScheduleEntity.getExtraInterestPaid()).thenReturn(extraInterestDue); when(loanScheduleEntity.getInterest()).thenReturn(interest); loanBO.repayInstallmentWithInterestWaiver(loanScheduleEntity,accountPaymentEntity,"",accountActionTypes,user); Set<AccountTrxnEntity> accountTrxns = accountPaymentEntity.getAccountTrxns(); AccountTrxnEntity accountTrxnEntity = accountTrxns.toArray(new AccountTrxnEntity[accountTrxns.size()])[0]; LoanTrxnDetailEntity loanTrxnDetailEntity = (LoanTrxnDetailEntity) accountTrxnEntity; assertThat(loanTrxnDetailEntity.getInterestAmount().getAmount().doubleValue(), is(0.98)); CalculatedInterestOnPayment calculatedInterestOnPayment = loanTrxnDetailEntity.getCalculatedInterestOnPayment(); assertNotNull(calculatedInterestOnPayment); assertThat(calculatedInterestOnPayment.getExtraInterestPaid(),is(extraInterestDue)); assertThat(calculatedInterestOnPayment.getInterestDueTillPaid(),is(interestDue)); assertThat(calculatedInterestOnPayment.getOriginalInterest(),is(interest)); Mockito.verify(loanScheduleEntity).makeEarlyRepaymentEntries(LoanConstants.PAY_FEES_PENALTY, interestDue, accountPaymentEntity.getPaymentDate()); } private LoanScheduleEntity getLoanScheduleEntity(MifosCurrency currency, Date date, String principal, String interest, String installmentId, Money extraInterest) { LoanBO loanBO = mock(LoanBO.class); when(loanBO.getCurrency()).thenReturn(currency); LoanScheduleEntity loanScheduleEntity = new LoanScheduleEntity(loanBO, mock(CustomerBO.class), Short .valueOf(installmentId), new java.sql.Date(date.getTime()), PaymentStatus.UNPAID, new Money(currency, principal), new Money(currency, interest)); loanScheduleEntity.setExtraInterest(extraInterest); return loanScheduleEntity; } private RepaymentScheduleInstallment getRepaymentScheduleInstallment(String dueDate, int installment, String principal, String interest) { return installmentBuilder.reset(locale).withInstallment(installment).withDueDateValue(dueDate). withPrincipal(new Money(rupee, principal)).withInterest(new Money(rupee, interest)). build(); } }