/* * 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.customers.client.business; import static org.apache.commons.lang.math.NumberUtils.SHORT_ZERO; import static org.mifos.customers.client.business.LoanCounter.TRANSFORM_LOAN_COUNTER_TO_LOAN_CYCLE; import static org.mifos.framework.util.CollectionUtils.find; import static org.mifos.framework.util.CollectionUtils.select; import java.math.BigDecimal; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.mifos.accounts.business.AccountBO; import org.mifos.accounts.loan.business.LoanBO; import org.mifos.accounts.productdefinition.business.LoanOfferingBO; import org.mifos.accounts.productdefinition.business.PrdOfferingBO; import org.mifos.application.master.business.MifosCurrency; import org.mifos.application.util.helpers.YesNoFlag; import org.mifos.customers.business.CustomerPerformanceHistory; import org.mifos.framework.util.helpers.Money; import org.mifos.framework.util.helpers.Predicate; public class ClientPerformanceHistoryEntity extends CustomerPerformanceHistory { private final Integer id; private Set<LoanCounter> loanCounters; private Integer noOfActiveLoans; private Money lastLoanAmount; private Money delinquentPortfolio; private Money totalSavings; private ClientBO client; public void setClient(ClientBO client) { this.client = client; } public ClientPerformanceHistoryEntity(ClientBO client) { super(); this.id = null; this.client = client; this.loanCounters = new HashSet<LoanCounter>(); this.noOfActiveLoans = 0; this.lastLoanAmount = new Money(getCurrency()); this.delinquentPortfolio = new Money(getCurrency()); this.totalSavings = new Money(getCurrency()); } protected ClientPerformanceHistoryEntity() { this.id = null; this.client = null; this.loanCounters = new HashSet<LoanCounter>(); this.noOfActiveLoans = 0; this.lastLoanAmount = null; this.delinquentPortfolio = null; this.totalSavings = null; } public Integer getId() { return id; } @SuppressWarnings("unused") private Money getDelinquentPortfolio() { return delinquentPortfolio; } @SuppressWarnings("unused") private void setDelinquentPortfolio(Money delinquentPortfolio) { this.delinquentPortfolio = delinquentPortfolio; } public Money getLastLoanAmount() { return lastLoanAmount; } public void setLastLoanAmount(Money lastLoanAmount) { this.lastLoanAmount = lastLoanAmount; } public Set<LoanCounter> getLoanCounters() { return loanCounters; } public void setLoanCounters(Set<LoanCounter> loanCounters) { this.loanCounters = loanCounters; } private void addLoanCounter(LoanCounter loanCounter) { this.loanCounters.add(loanCounter); } public Integer getNoOfActiveLoans() { return noOfActiveLoans; } public void setNoOfActiveLoans(Integer noOfActiveLoans) { this.noOfActiveLoans = noOfActiveLoans; } @SuppressWarnings("unused") private Money getTotalSavings() { return totalSavings; } public Money getTotalSavingsAmount() { return getClient().getSavingsBalance(getCurrency()); } @SuppressWarnings("unused") private void setTotalSavings(Money totalSavings) { this.totalSavings = totalSavings; } public ClientBO getClient() { return client; } public void updateLoanCounter(final LoanOfferingBO loanOffering, YesNoFlag counterFlag) { if (loanCounters == null) { loanCounters = new HashSet<LoanCounter>(); } LoanCounter loanCounter = null; try { loanCounter = findLoanCounterForProduct(loanOffering); } catch (Exception e) { } if (loanCounter == null) { loanCounter = new LoanCounter(this, loanOffering, counterFlag); addLoanCounter(loanCounter); } else { loanCounter.updateLoanCounter(counterFlag); } } public LoanCounter findLoanCounterForProduct(final LoanOfferingBO loanOffering) throws Exception { return find(loanCounters, new Predicate<LoanCounter>() { @Override public boolean evaluate(LoanCounter loanCounter) { return loanOffering.isOfSameOffering(loanCounter.getLoanOffering()); } }); } public MifosCurrency getCurrency() { // TODO: performance history will only work if all accounts use the default (or same?) currency return Money.getDefaultCurrency(); } public Integer getLoanCycleNumber() { Integer loanCount = 0; for (LoanCounter loanCounter : getLoanCounters()) { if (loanCounter.getLoanOffering().isIncludeInLoanCounter()) { loanCount += loanCounter.getLoanCycleCounter(); } } if (client.getHistoricalData() != null) { if (client.getHistoricalData().getLoanCycleNumber() == null) { client.getHistoricalData().setLoanCycleNumber(0); } loanCount += client.getHistoricalData().getLoanCycleNumber(); } return loanCount; } public Money getDelinquentPortfolioAmount() { // we don't know what currency to use when we start totaling these // so don't initialize them with a currency Money amountOverDue = null; Money totalOutStandingAmount = null; for (AccountBO accountBO : client.getAccounts()) { if (accountBO.isLoanAccount() && ((LoanBO) accountBO).isAccountActive()) { Money totalPrincipalAmountInArrears = ((LoanBO) accountBO).getTotalPrincipalAmountInArrears(); if (amountOverDue == null) { amountOverDue = totalPrincipalAmountInArrears; } else { amountOverDue = amountOverDue.add(totalPrincipalAmountInArrears); } Money originalPrincipal = ((LoanBO) accountBO).getLoanSummary().getOriginalPrincipal(); if (totalOutStandingAmount == null) { totalOutStandingAmount = originalPrincipal; } else { totalOutStandingAmount = totalOutStandingAmount.add(originalPrincipal); } } } // FIXME: this seems like it should just be returning a BigDecimal rather than a Money object if (totalOutStandingAmount != null && !totalOutStandingAmount.getAmount().equals(BigDecimal.ZERO)) { return new Money(amountOverDue.getCurrency(), amountOverDue.divide(totalOutStandingAmount)); } return new Money(getCurrency()); } public Short getMaxLoanCycleForProduct(final PrdOfferingBO prdOffering) { { Set<LoanCounter> clientLoanCounters = getLoanCounters(); try { Collection<Short> loanCyclesForProduct = select(clientLoanCounters, new Predicate<LoanCounter>() { @Override public boolean evaluate(LoanCounter counter) { return counter.isOfSameProduct(prdOffering); } }, TRANSFORM_LOAN_COUNTER_TO_LOAN_CYCLE); return loanCyclesForProduct.isEmpty() ? SHORT_ZERO : Collections.max(loanCyclesForProduct); } catch (Exception e) { return SHORT_ZERO; } } } public void updateOnDisbursement(LoanOfferingBO loanOffering) { updateLoanCounter(loanOffering, YesNoFlag.YES); incrementNoOfActiveLoans(); } public void updateOnWriteOff(LoanOfferingBO loanOffering) { updateLoanCounter(loanOffering, YesNoFlag.NO); decrementNoOfActiveLoans(); } public void updateOnReversal(LoanOfferingBO loanOffering, Money lastLoanAmount) { updateCommonHistoryOnReversal(loanOffering); } public void updateCommonHistoryOnReversal(LoanOfferingBO loanOffering) { updateLoanCounter(loanOffering, YesNoFlag.NO); decrementNoOfActiveLoans(); } public void updateOnFullRepayment(Money totalAmount) { setLastLoanAmount(totalAmount); decrementNoOfActiveLoans(); } public void incrementNoOfActiveLoans(){ setNoOfActiveLoans(getNoOfActiveLoans() + 1); } public void decrementNoOfActiveLoans(){ setNoOfActiveLoans(getNoOfActiveLoans() - 1); } }