/*
* 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.util.helper;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import org.joda.time.DateTime;
import org.junit.Test;
import org.mifos.accounts.business.AccountBO;
import org.mifos.accounts.business.AccountFeesEntity;
import org.mifos.accounts.fees.business.AmountFeeBO;
import org.mifos.accounts.fees.business.FeeBO;
import org.mifos.accounts.fees.util.helpers.FeeCategory;
import org.mifos.accounts.fees.util.helpers.FeeFrequencyType;
import org.mifos.accounts.fees.util.helpers.FeeStatus;
import org.mifos.accounts.financial.business.GLCodeEntity;
import org.mifos.accounts.util.helpers.FeeInstallment;
import org.mifos.application.master.business.MifosCurrency;
import org.mifos.application.meeting.business.MeetingBO;
import org.mifos.application.meeting.util.helpers.MeetingType;
import org.mifos.application.meeting.util.helpers.RecurrenceType;
import org.mifos.domain.builders.MeetingBuilder;
import org.mifos.domain.builders.ScheduledEventBuilder;
import org.mifos.framework.TestUtils;
import org.mifos.framework.util.helpers.Money;
import org.mifos.schedule.ScheduledEvent;
public class FeeInstallmentTest {
/*************************************
* These tests verify that fees are correctly merged for a single fee on an account.
****************************************/
@Test
public void createMergedFeeInstallmentsForOneFeeBothAccountAndFeeAreScheduledEveryWeekStartwithSecondInstallmentShouldGetOneFeeInstallmentPerAccountEvent() {
ScheduledEvent masterEvent = new ScheduledEventBuilder().every(1).weeks().build();
FeeBO feeBO = createWeeklyFeeBO(1);
AccountFeesEntity accountFeesEntity = createAccountFeesEntity(feeBO, 10.0);
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallmentsForOneFeeStartingWith(masterEvent, accountFeesEntity, 2, 2);
assertThat(feeInstallments.size(), is(2));
assertFeeInstallment(feeInstallments.get(0), 2, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(1), 3, 10.0, feeBO);
}
@Test
public void createMergedFeeInstallmentsForOneFeeBothAccountAndFeeAreScheduledEveryWeekShouldGetOneFeeInstallmentPerAccountEvent() {
ScheduledEvent masterEvent = new ScheduledEventBuilder().every(1).weeks().build();
FeeBO feeBO = createWeeklyFeeBO(1);
AccountFeesEntity accountFeesEntity = createAccountFeesEntity(feeBO, 10.0);
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallmentsForOneFee(masterEvent,
accountFeesEntity, 3);
assertThat(feeInstallments.size(), is(3));
assertFeeInstallment(feeInstallments.get(0), 1, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(1), 2, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(2), 3, 10.0, feeBO);
}
@Test
public void createMergedFeeInstallmentsForOneFeeAccountScheduledEveryWeekFeeScheduledEveryOtherWeekShouldGetOneFeeInstallmentPerEveryOtherAccountEvent() {
ScheduledEvent accountEvent = new ScheduledEventBuilder().every(1).weeks().build();
FeeBO feeBO = createWeeklyFeeBO(2);
AccountFeesEntity accountFeesEntity = createAccountFeesEntity(feeBO, 10.0);
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallmentsForOneFee(accountEvent,
accountFeesEntity, 4);
assertThat(feeInstallments.size(), is(2));
assertFeeInstallment(feeInstallments.get(0), 1, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(1), 3, 10.0, feeBO);
}
@Test
public void createMergedFeeInstallmentsForOneFeeAccountScheduledEveryOtherWeekFeeScheduledEveryWeekt() {
ScheduledEvent masterEvent = new ScheduledEventBuilder().every(2).weeks().build();
FeeBO feeBO = createWeeklyFeeBO(1);
AccountFeesEntity accountFeesEntity = createAccountFeesEntity(feeBO, 10.0);
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallmentsForOneFee(masterEvent,
accountFeesEntity, 4);
assertThat(feeInstallments.size(), is(4));
assertFeeInstallment(feeInstallments.get(0), 1, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(1), 2, 20.0, feeBO);
assertFeeInstallment(feeInstallments.get(2), 3, 20.0, feeBO);
assertFeeInstallment(feeInstallments.get(3), 4, 20.0, feeBO);
}
@Test
public void createMergedFeeInstallmentsForOneFeeAccountScheduledEverySecondWeekFeeScheduledEveryThirdWeek() {
ScheduledEvent masterEvent = new ScheduledEventBuilder().every(2).weeks().build();
FeeBO feeBO = createWeeklyFeeBO(3);
AccountFeesEntity accountFeesEntity = createAccountFeesEntity(feeBO, 10.0);
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallmentsForOneFee(masterEvent,
accountFeesEntity, 7);
assertThat(feeInstallments.size(), is(5));
assertFeeInstallment(feeInstallments.get(0), 1, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(1), 3, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(2), 4, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(3), 6, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(4), 7, 10.0, feeBO);
}
@Test
public void createMergedFeeInstallmentsForOneFeeAccountScheduledEveryThirdWeekFeeScheduledEverySecondWeek() {
ScheduledEvent accountScheduledEvent = new ScheduledEventBuilder().every(3).weeks().build();
FeeBO feeBO = createWeeklyFeeBO(2);
AccountFeesEntity accountFeesEntity = createAccountFeesEntity(feeBO, 10.0);
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallmentsForOneFee(accountScheduledEvent,
accountFeesEntity, 4);
assertThat(feeInstallments.size(), is(4));
assertFeeInstallment(feeInstallments.get(0), 1, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(1), 2, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(2), 3, 20.0, feeBO);
assertFeeInstallment(feeInstallments.get(3), 4, 10.0, feeBO);
}
@Test
public void createMergedFeeInstallmentsForOneFeeAccountScheduledEveryThirdMonthFeeScheduledEverySecondMonth()
throws Exception {
ScheduledEvent accountScheduledEvent = new ScheduledEventBuilder().every(3).months().onDayOfMonth(3).build();
FeeBO feeBO = createMonthlyOnDayFeeBO(2);
AccountFeesEntity accountFeesEntity = createAccountFeesEntity(feeBO, 10.0);
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallmentsForOneFee(accountScheduledEvent,
accountFeesEntity, 4);
assertThat(feeInstallments.size(), is(4));
assertFeeInstallment(feeInstallments.get(0), 1, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(1), 2, 10.0, feeBO);
assertFeeInstallment(feeInstallments.get(2), 3, 20.0, feeBO);
assertFeeInstallment(feeInstallments.get(3), 4, 10.0, feeBO);
}
/**********************************
* Tests verify merging fees when two or more fees are attached to the account
*
* TODO: KRP: Remove the assumption of order that fee installments appear in the created list.
**********************************/
@Test
public void createMergedFeeInstallmentsForTwoFeesBothAccountAndFeesAreScheduledEveryWeekShouldGetOneFeeInstallmentPerAccountEventPerFee() {
ScheduledEvent masterEvent = new ScheduledEventBuilder().every(1).weeks().build();
FeeBO feeBO1 = createWeeklyFeeBO(1);
FeeBO feeBO2 = createWeeklyFeeBO(1);
AccountFeesEntity accountFeesEntity1 = createAccountFeesEntity(feeBO1, 10.0);
AccountFeesEntity accountFeesEntity2 = createAccountFeesEntity(feeBO2, 13.0);
List<AccountFeesEntity> accountFees = Arrays.asList(new AccountFeesEntity[]{accountFeesEntity1,accountFeesEntity2});
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallments(
masterEvent,
accountFees, 3);
assertThat(feeInstallments.size(), is(6));
assertFeeInstallment(feeInstallments.get(0), 1, 10.0, feeBO1);
assertFeeInstallment(feeInstallments.get(1), 2, 10.0, feeBO1);
assertFeeInstallment(feeInstallments.get(2), 3, 10.0, feeBO1);
assertFeeInstallment(feeInstallments.get(3), 1, 13.0, feeBO2);
assertFeeInstallment(feeInstallments.get(4), 2, 13.0, feeBO2);
assertFeeInstallment(feeInstallments.get(5), 3, 13.0, feeBO2);
}
/**
* Expected results:
* | week | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
* | account event | | 1 | | 2 | | 3 | | 4 | | 5 | | 6 |
* | fee1 event | x | x | x | x | x | x | x | x | x | x | x | x |
* | fee2 event | | | y | | | y | | | y | | | y |
*/
@Test
public void createMergedFeeInstallmentsForTwoFeesAccountScheduledBiWeeklyFee1ScheduledWeeklyFee2ScheduledEveryThreeWeeks() {
ScheduledEvent masterEvent = new ScheduledEventBuilder().every(2).weeks().build();
FeeBO feeBO1 = createWeeklyFeeBO(1);
FeeBO feeBO2 = createWeeklyFeeBO(3);
AccountFeesEntity accountFeesEntity1 = createAccountFeesEntity(feeBO1, 10.0);
AccountFeesEntity accountFeesEntity2 = createAccountFeesEntity(feeBO2, 13.0);
List<AccountFeesEntity> accountFees = Arrays.asList(new AccountFeesEntity[]{accountFeesEntity1,accountFeesEntity2});
List<FeeInstallment> feeInstallments = FeeInstallment
.createMergedFeeInstallments(
masterEvent,
accountFees, 6);
assertThat(feeInstallments.size(), is(10));
assertFeeInstallment(feeInstallments.get(0), 1, 10.0, feeBO1);
assertFeeInstallment(feeInstallments.get(1), 2, 20.0, feeBO1);
assertFeeInstallment(feeInstallments.get(2), 3, 20.0, feeBO1);
assertFeeInstallment(feeInstallments.get(3), 4, 20.0, feeBO1);
assertFeeInstallment(feeInstallments.get(4), 5, 20.0, feeBO1);
assertFeeInstallment(feeInstallments.get(5), 6, 20.0, feeBO1);
assertFeeInstallment(feeInstallments.get(6), 1, 13.0, feeBO2);
assertFeeInstallment(feeInstallments.get(7), 3, 13.0, feeBO2);
assertFeeInstallment(feeInstallments.get(8), 4, 13.0, feeBO2);
assertFeeInstallment(feeInstallments.get(9), 6, 13.0, feeBO2);
}
private AccountBO createAccountBOForTesting() {
return new AccountBO() {
@Override
public MifosCurrency getCurrency() {
return TestUtils.RUPEE;
}
};
}
private void assertFeeInstallment (FeeInstallment feeInstallment, int installmentId,
double expectedMergedFeeAmount, FeeBO expectedFeeBO) {
assertThat(feeInstallment.getAccountFee(), is(new Money(TestUtils.RUPEE, new BigDecimal(expectedMergedFeeAmount))));
assertThat(feeInstallment.getInstallmentId(), is((short) (installmentId)));
assertThat(feeInstallment.getAccountFee().getAmountDoubleValue(), is(expectedMergedFeeAmount));
assertThat(feeInstallment.getAccountFeesEntity().getFees(), is(expectedFeeBO));
}
private FeeBO createWeeklyFeeBO (int every) {
MeetingBO feeMeeting = new MeetingBuilder().every(every).weekly().build();
FeeBO feeBO = new AmountFeeBO(new Money(TestUtils.RUPEE, "10.0"), "Fee", FeeCategory.ALLCUSTOMERS,
FeeFrequencyType.PERIODIC, new GLCodeEntity(Short.valueOf("1"), "10000"),
feeMeeting, null, new DateTime().minusDays(14).toDate(), TestUtils.makeUserWithLocales().getId());
return feeBO;
}
private FeeBO createMonthlyOnDayFeeBO (int every) throws Exception {
MeetingBO feeMeeting = new MeetingBO(RecurrenceType.MONTHLY, (short) every, new DateTime().toDate(), MeetingType.PERIODIC_FEE);
FeeBO feeBO = new AmountFeeBO(new Money(TestUtils.RUPEE, "10.0"), "Fee", FeeCategory.ALLCUSTOMERS,
FeeFrequencyType.PERIODIC, new GLCodeEntity(Short.valueOf("1"), "10000"),
feeMeeting, null, new DateTime().minusDays(14).toDate(), TestUtils.makeUserWithLocales().getId());
return feeBO;
}
private AccountFeesEntity createAccountFeesEntity (FeeBO feeBO, double feeAmount) {
AccountFeesEntity accountFeesEntity
= new AccountFeesEntity(createAccountBOForTesting(), feeBO, feeAmount,
FeeStatus.ACTIVE.getValue(), new DateTime().toDate(), null);
return accountFeesEntity;
}
}