/*
* 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 java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.mifos.accounts.business.AccountFeesActionDetailEntity;
import org.mifos.accounts.business.AccountFeesEntity;
import org.mifos.accounts.business.AccountFlagMapping;
import org.mifos.accounts.business.AccountPaymentEntity;
import org.mifos.accounts.business.AccountPenaltiesEntity;
import org.mifos.accounts.business.AccountTrxnEntity;
import org.mifos.accounts.business.FeesTrxnDetailEntity;
import org.mifos.accounts.business.PenaltiesTrxnDetailEntity;
import org.mifos.accounts.exceptions.AccountException;
import org.mifos.accounts.util.helpers.AccountActionTypes;
import org.mifos.accounts.util.helpers.AccountState;
import org.mifos.accounts.util.helpers.AccountStateFlag;
import org.mifos.customers.personnel.business.PersonnelBO;
import org.mifos.framework.persistence.LegacyGenericDao;
import org.mifos.framework.util.helpers.Money;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* LoanTrxnDetailEntity encapsulates a financial transaction
* such as disbursing a loan or making a payment on a loan.
* Possible types of transactions are described by AccountActionTypes.
*/
public class LoanTrxnDetailEntity extends AccountTrxnEntity {
private static final Logger logger = LoggerFactory.getLogger(LoanTrxnDetailEntity.class);
private final Money principalAmount;
private final Money interestAmount;
private final Money penaltyAmount;
private final Money miscFeeAmount;
private final Money miscPenaltyAmount;
private final Set<FeesTrxnDetailEntity> feesTrxnDetails;
private final Set<PenaltiesTrxnDetailEntity> penaltiesTrxnDetails;
private CalculatedInterestOnPayment calculatedInterestOnPayment;
public Set<FeesTrxnDetailEntity> getFeesTrxnDetails() {
return feesTrxnDetails;
}
public Set<PenaltiesTrxnDetailEntity> getPenaltiesTrxnDetails() {
return penaltiesTrxnDetails;
}
public Money getInterestAmount() {
return interestAmount;
}
public Money getPenaltyAmount() {
return penaltyAmount;
}
public Money getPrincipalAmount() {
return principalAmount;
}
public Money getMiscFeeAmount() {
return miscFeeAmount;
}
public void addFeesTrxnDetail(FeesTrxnDetailEntity feesTrxn) {
feesTrxnDetails.add(feesTrxn);
}
public void addPenaltiesTrxnDetail(PenaltiesTrxnDetailEntity penaltiesTrxn) {
penaltiesTrxnDetails.add(penaltiesTrxn);
}
public Money getMiscPenaltyAmount() {
return miscPenaltyAmount;
}
protected LoanTrxnDetailEntity() {
feesTrxnDetails = new HashSet<FeesTrxnDetailEntity>();
penaltiesTrxnDetails = new HashSet<PenaltiesTrxnDetailEntity>();
principalAmount = null;
interestAmount = null;
penaltyAmount = null;
miscFeeAmount = null;
miscPenaltyAmount = null;
}
public LoanTrxnDetailEntity(AccountPaymentEntity accountPayment, AccountActionTypes accountActionType,
Short installmentId, Date dueDate, PersonnelBO personnel, Date actionDate, Money amount, String comments,
AccountTrxnEntity relatedTrxn, Money principalAmount, Money interestAmount, Money penaltyAmount,
Money miscFeeAmount, Money miscPenaltyAmount, List<AccountFeesEntity> accountFees, List<AccountPenaltiesEntity> accountPenalties) {
super(accountPayment, accountActionType, installmentId, dueDate, personnel, null, actionDate, amount,
comments, relatedTrxn);
this.principalAmount = principalAmount;
this.interestAmount = interestAmount;
this.penaltyAmount = penaltyAmount;
this.miscFeeAmount = miscFeeAmount;
this.miscPenaltyAmount = miscPenaltyAmount;
feesTrxnDetails = new HashSet<FeesTrxnDetailEntity>();
if (accountFees != null && accountFees.size() > 0) {
for (AccountFeesEntity accountFeesEntity : accountFees) {
addFeesTrxnDetail(new FeesTrxnDetailEntity(this, accountFeesEntity, accountFeesEntity
.getAccountFeeAmount()));
}
}
penaltiesTrxnDetails = new HashSet<PenaltiesTrxnDetailEntity>();
if (accountPenalties != null && accountPenalties.size() > 0) {
for (AccountPenaltiesEntity accountPenaltiesEntity : accountPenalties) {
addPenaltiesTrxnDetail(new PenaltiesTrxnDetailEntity(this, accountPenaltiesEntity, accountPenaltiesEntity
.getAccountPenaltyAmount()));
}
}
}
public LoanTrxnDetailEntity(AccountPaymentEntity accountPaymentEntity, LoanScheduleEntity loanScheduleEntity,
PersonnelBO personnel, Date transactionDate, AccountActionTypes accountActionType,
String comments, LegacyGenericDao persistence) {
super(accountPaymentEntity, accountActionType, loanScheduleEntity.getInstallmentId(), loanScheduleEntity
.getActionDate(), personnel, null, transactionDate, loanScheduleEntity.getPaymentAllocation().getTotalPaid(), comments,
null);
PaymentAllocation paymentAllocation = loanScheduleEntity.getPaymentAllocation();
interestAmount = paymentAllocation.getTotalInterestPaid();
penaltyAmount = paymentAllocation.getPenaltyPaid();
principalAmount = paymentAllocation.getPrincipalPaid();
miscFeeAmount = paymentAllocation.getMiscFeePaid();
miscPenaltyAmount = paymentAllocation.getMiscPenaltyPaid();
feesTrxnDetails = new HashSet<FeesTrxnDetailEntity>();
for (AccountFeesActionDetailEntity accountFeesActionDetail : loanScheduleEntity.getAccountFeesActionDetails()) {
Integer feeId = accountFeesActionDetail.getAccountFeesActionDetailId();
if (feeId == null) { // special workaround for MIFOS-4517
feeId = accountFeesActionDetail.hashCode();
}
if(paymentAllocation.isFeeAllocated(feeId)) {
Money feePaid = paymentAllocation.getFeePaid(feeId);
addFeesTrxnDetail(new FeesTrxnDetailEntity(this, accountFeesActionDetail.getAccountFee(), feePaid));
}
}
penaltiesTrxnDetails = new HashSet<PenaltiesTrxnDetailEntity>();
for (LoanPenaltyScheduleEntity loanPenaltyScheduleEntity : loanScheduleEntity.getLoanPenaltyScheduleEntities()) {
Integer penaltyId = loanPenaltyScheduleEntity.getLoanPenaltyScheduleEntityId();
if (penaltyId == null) { // special workaround for MIFOS-4517
penaltyId = loanPenaltyScheduleEntity.hashCode();
}
if(paymentAllocation.isPenaltyAllocated(penaltyId)) {
Money penaltyPaid = paymentAllocation.getPenaltyPaid(penaltyId);
addPenaltiesTrxnDetail(new PenaltiesTrxnDetailEntity(this, loanPenaltyScheduleEntity.getAccountPenalty(), penaltyPaid));
}
}
}
@Override
protected AccountTrxnEntity generateReverseTrxn(PersonnelBO loggedInUser, String adjustmentComment)
throws AccountException {
logger.debug(
"Inside generate reverse transaction method of loan trxn detail");
String comment = null;
if (null == adjustmentComment) {
comment = getComments();
} else {
comment = adjustmentComment;
}
LoanTrxnDetailEntity reverseAccntTrxn;
reverseAccntTrxn = new LoanTrxnDetailEntity(getAccountPayment(),
getReverseTransctionActionType(), getInstallmentId(), getDueDate(), loggedInUser, getActionDate(), getAmount()
.negate(), comment, this, getPrincipalAmount().negate(), getInterestAmount().negate(),
getPenaltyAmount().negate(), getMiscFeeAmount().negate(), getMiscPenaltyAmount().negate(), null, null);
reverseAccntTrxn.setCalculatedInterestOnPayment(this.getCalculatedInterestOnPayment());
if (null != getFeesTrxnDetails() && getFeesTrxnDetails().size() > 0) {
logger.debug(
"Before generating reverse entries for fees");
for (FeesTrxnDetailEntity feeTrxnDetail : getFeesTrxnDetails()) {
reverseAccntTrxn.addFeesTrxnDetail(feeTrxnDetail.generateReverseTrxn(reverseAccntTrxn));
}
logger.debug("after generating reverse entries for fees");
}
if (null != getPenaltiesTrxnDetails() && getPenaltiesTrxnDetails().size() > 0) {
logger.debug("Before generating reverse entries for penalties");
for (PenaltiesTrxnDetailEntity penaltyTrxnDetail : getPenaltiesTrxnDetails()) {
reverseAccntTrxn.addPenaltiesTrxnDetail(penaltyTrxnDetail.generateReverseTrxn(reverseAccntTrxn));
}
logger.debug("after generating reverse entries for penalties");
}
return reverseAccntTrxn;
}
public Money getFeeAmount() {
Money feeAmnt = new Money(getAccount().getCurrency());
if (null != feesTrxnDetails && feesTrxnDetails.size() > 0) {
for (FeesTrxnDetailEntity feesTrxn : feesTrxnDetails) {
feeAmnt = feeAmnt.add(feesTrxn.getFeeAmount());
}
}
return feeAmnt;
}
public FeesTrxnDetailEntity getFeesTrxn(Integer accountFeeId) {
if (null != feesTrxnDetails && feesTrxnDetails.size() > 0) {
for (FeesTrxnDetailEntity feesTrxn : feesTrxnDetails) {
if (feesTrxn.getAccountFees().getAccountFeeId().equals(accountFeeId)) {
return feesTrxn;
}
}
}
return null;
}
public PenaltiesTrxnDetailEntity getPenaltiesTrxn(Integer accountPenaltyId) {
if (null != penaltiesTrxnDetails && penaltiesTrxnDetails.size() > 0) {
for (PenaltiesTrxnDetailEntity penaltiesTrxn : penaltiesTrxnDetails) {
if (penaltiesTrxn.getAccountPenalties().getLoanAccountPenaltyId().equals(accountPenaltyId)) {
return penaltiesTrxn;
}
}
}
return null;
}
private boolean isAccountCancelled() {
if (getAccount().getAccountState().getId().equals(AccountState.LOAN_CANCELLED.getValue())) {
Set<AccountFlagMapping> accountFlags = getAccount().getAccountFlags();
if (accountFlags != null && accountFlags.size() > 0) {
for (AccountFlagMapping accountFlagMapping : accountFlags) {
if (accountFlagMapping.getFlag().getId().equals(AccountStateFlag.LOAN_REVERSAL.getValue())) {
return true;
}
}
}
}
return false;
}
private AccountActionTypes getReverseTransctionActionType() {
if (getAccountActionEntity().getId().equals(AccountActionTypes.DISBURSAL.getValue())) {
return AccountActionTypes.LOAN_DISBURSAL_AMOUNT_REVERSAL;
} else if (isAccountCancelled()) {
return AccountActionTypes.LOAN_REVERSAL;
} else {
return AccountActionTypes.LOAN_ADJUSTMENT;
}
}
Money totalPenaltyPaid() {
return getPenaltyAmount().add(getMiscPenaltyAmount());
}
Money totalAndMiscFeesPaid() {
return getFeeAmount().add(getMiscFeeAmount());
}
public boolean isNotEmptyTransaction() {
return getInstallmentId() != null && !getInstallmentId().equals(Short.valueOf("0"));
}
void adjustFees(AccountFeesActionDetailEntity accntFeesAction) {
FeesTrxnDetailEntity feesTrxnDetailEntity = getFeesTrxn(accntFeesAction.getAccountFeeId());
if (feesTrxnDetailEntity != null) {
((LoanFeeScheduleEntity) accntFeesAction).adjustFees(feesTrxnDetailEntity);
}
}
void adjustPenalties(LoanPenaltyScheduleEntity loanPenaltyScheduleEntity) {
PenaltiesTrxnDetailEntity penaltiesTrxnDetailEntity = getPenaltiesTrxn(loanPenaltyScheduleEntity.getAccountPenaltyId());
if(penaltiesTrxnDetailEntity != null) {
loanPenaltyScheduleEntity.adjustPenalties(penaltiesTrxnDetailEntity);
}
}
public CalculatedInterestOnPayment getCalculatedInterestOnPayment() {
return calculatedInterestOnPayment;
}
public void setCalculatedInterestOnPayment(CalculatedInterestOnPayment calculatedInterestOnPayment) {
this.calculatedInterestOnPayment = calculatedInterestOnPayment;
}
public void computeAndSetCalculatedInterestOnPayment(Money originalInterest, Money extraInterestPaid, Money interestDueTillPaid) {
CalculatedInterestOnPayment calculatedInterestOnPayment = new CalculatedInterestOnPayment();
calculatedInterestOnPayment.setLoanTrxnDetailEntity(this);
calculatedInterestOnPayment.setOriginalInterest(originalInterest);
calculatedInterestOnPayment.setExtraInterestPaid(extraInterestPaid);
calculatedInterestOnPayment.setInterestDueTillPaid(interestDueTillPaid);
setCalculatedInterestOnPayment(calculatedInterestOnPayment);
}
}