/*
* 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.servicefacade;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifos.accounts.acceptedpaymenttype.persistence.LegacyAcceptedPaymentTypeDao;
import org.mifos.accounts.api.AccountService;
import org.mifos.accounts.business.AccountBO;
import org.mifos.accounts.business.AccountFeesEntity;
import org.mifos.accounts.business.AccountPaymentEntity;
import org.mifos.accounts.business.AccountPenaltiesEntity;
import org.mifos.accounts.business.service.AccountBusinessService;
import org.mifos.accounts.exceptions.AccountException;
import org.mifos.accounts.fees.business.FeeBO;
import org.mifos.accounts.fees.business.RateFeeBO;
import org.mifos.accounts.fees.persistence.FeeDao;
import org.mifos.accounts.fees.util.helpers.RateAmountFlag;
import org.mifos.accounts.loan.business.LoanBO;
import org.mifos.accounts.loan.business.ScheduleCalculatorAdaptor;
import org.mifos.accounts.loan.persistance.LoanDao;
import org.mifos.accounts.loan.util.helpers.LoanExceptionConstants;
import org.mifos.accounts.penalties.business.PenaltyBO;
import org.mifos.accounts.penalties.business.RatePenaltyBO;
import org.mifos.accounts.penalties.persistence.PenaltyDao;
import org.mifos.accounts.persistence.LegacyAccountDao;
import org.mifos.accounts.util.helpers.AccountConstants;
import org.mifos.accounts.util.helpers.AccountExceptionConstants;
import org.mifos.accounts.util.helpers.AccountState;
import org.mifos.accounts.util.helpers.AccountTypes;
import org.mifos.accounts.util.helpers.PaymentData;
import org.mifos.application.admin.servicefacade.MonthClosingServiceFacade;
import org.mifos.application.master.business.PaymentTypeEntity;
import org.mifos.application.servicefacade.ClientServiceFacade;
import org.mifos.application.servicefacade.ListItem;
import org.mifos.application.servicefacade.SavingsServiceFacade;
import org.mifos.application.util.helpers.TrxnTypes;
import org.mifos.core.MifosRuntimeException;
import org.mifos.customers.api.CustomerLevel;
import org.mifos.customers.exceptions.CustomerException;
import org.mifos.customers.personnel.business.PersonnelBO;
import org.mifos.customers.personnel.persistence.LegacyPersonnelDao;
import org.mifos.customers.personnel.persistence.PersonnelDao;
import org.mifos.dto.domain.AccountPaymentParametersDto;
import org.mifos.dto.domain.AdjustedPaymentDto;
import org.mifos.dto.domain.ApplicableCharge;
import org.mifos.dto.domain.CustomerDto;
import org.mifos.dto.domain.PaymentDto;
import org.mifos.dto.domain.SavingsAdjustmentDto;
import org.mifos.dto.domain.SavingsDetailDto;
import org.mifos.dto.domain.UserReferenceDto;
import org.mifos.dto.screen.AccountTypeCustomerLevelDto;
import org.mifos.framework.exceptions.ApplicationException;
import org.mifos.framework.exceptions.PersistenceException;
import org.mifos.framework.exceptions.ServiceException;
import org.mifos.framework.hibernate.helper.HibernateTransactionHelper;
import org.mifos.framework.hibernate.helper.StaticHibernateUtil;
import org.mifos.framework.util.helpers.Constants;
import org.mifos.framework.util.helpers.Money;
import org.mifos.security.MifosUser;
import org.mifos.security.util.ActivityMapper;
import org.mifos.security.util.SecurityConstants;
import org.mifos.security.util.UserContext;
import org.mifos.service.BusinessRuleException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Concrete implementation of service to manipulate accounts from the presentation layer.
*
*/
public class WebTierAccountServiceFacade implements AccountServiceFacade {
private AccountService accountService;
private HibernateTransactionHelper transactionHelper;
private AccountBusinessService accountBusinessService;
private ScheduleCalculatorAdaptor scheduleCalculatorAdaptor;
private LegacyAcceptedPaymentTypeDao acceptedPaymentTypePersistence;
private LegacyPersonnelDao personnelPersistence;
private LegacyAccountDao legacyAccountDao;
private MonthClosingServiceFacade monthClosingServiceFacade;
private ClientServiceFacade clientServiceFacade;
private SavingsServiceFacade savingsServiceFacade;
@Autowired
private PersonnelDao personnelDao;
@Autowired
private LoanDao loanDao;
@Autowired
private FeeDao feeDao;
@Autowired
private PenaltyDao penaltyDao;
@Autowired
public WebTierAccountServiceFacade(AccountService accountService, HibernateTransactionHelper transactionHelper,
AccountBusinessService accountBusinessService,
ScheduleCalculatorAdaptor scheduleCalculatorAdaptor,
LegacyAcceptedPaymentTypeDao acceptedPaymentTypePersistence,
LegacyPersonnelDao personnelPersistence, LegacyAccountDao legacyAccountDao,
MonthClosingServiceFacade monthClosingServiceFacade,
ClientServiceFacade clientServiceFacade, SavingsServiceFacade savingsServiceFacade) {
this.accountService = accountService;
this.transactionHelper = transactionHelper;
this.accountBusinessService = accountBusinessService;
this.scheduleCalculatorAdaptor = scheduleCalculatorAdaptor;
this.acceptedPaymentTypePersistence = acceptedPaymentTypePersistence;
this.personnelPersistence = personnelPersistence;
this.legacyAccountDao = legacyAccountDao;
this.monthClosingServiceFacade = monthClosingServiceFacade;
this.clientServiceFacade = clientServiceFacade;
this.savingsServiceFacade = savingsServiceFacade;
}
@Override
public AccountPaymentDto getAccountPaymentInformation(Integer accountId, String paymentType, Short localeId, UserReferenceDto userReferenceDto, Date paymentDate) {
try {
AccountBO account = accountBusinessService.getAccount(accountId);
CustomerDto customer = account.getCustomer().toCustomerDto();
List<SavingsDetailDto> savingsInUse = clientServiceFacade.retrieveSavingsInUseForClient(customer.getCustomerId());
List<SavingsDetailDto> accountsForTransfer = new ArrayList<SavingsDetailDto>();
if (savingsInUse != null) {
for (SavingsDetailDto savingsAccount : savingsInUse) {
if (savingsAccount.getAccountStateId().equals(AccountState.SAVINGS_ACTIVE.getValue())) {
accountsForTransfer.add(savingsAccount);
}
}
}
if (isLoanPayment(paymentType)){
scheduleCalculatorAdaptor.computeExtraInterest((LoanBO) account, paymentDate);
}
UserReferenceDto accountUser = userReferenceDto;
if (account.getPersonnel() != null) {
accountUser = new UserReferenceDto(account.getPersonnel().getPersonnelId());
}
List<ListItem<Short>> paymentTypeList = constructPaymentTypeList(paymentType, localeId);
AccountTypeDto accountType = AccountTypeDto.getAccountType(account.getAccountType().getAccountTypeId());
Money totalPaymentDueMoney = account.getTotalPaymentDue();
String totalPaymentDue;
if (account instanceof LoanBO && totalPaymentDueMoney.isZero()) {
totalPaymentDue = ((LoanBO)account).getTotalRepayableAmount().toString();
}
else {
totalPaymentDue = totalPaymentDueMoney.toString();
}
clearSessionAndRollback();
return new AccountPaymentDto(accountType, account.getVersionNo(), paymentTypeList, totalPaymentDue,
accountUser, getLastPaymentDate(account), accountsForTransfer, customer);
} catch (ServiceException e) {
throw new MifosRuntimeException(e);
}
}
public Date retrieveLatPaymentDate(String globalAccountNum) {
try {
AccountBO account = accountBusinessService.findBySystemId(globalAccountNum);
return getLastPaymentDate(account);
} catch (ServiceException e) {
throw new MifosRuntimeException(e);
}
}
private Date getLastPaymentDate(AccountBO account) {
Date lastPaymentDate = new Date(0);
AccountPaymentEntity lastPayment = account.findMostRecentNonzeroPaymentByPaymentDate();
if(lastPayment != null){
lastPaymentDate = lastPayment.getPaymentDate();
}
return lastPaymentDate;
}
// Exposed for testing
void clearSessionAndRollback() {
StaticHibernateUtil.getSessionTL().clear();
transactionHelper.rollbackTransaction();
}
private boolean isLoanPayment(String paymentType) {
return paymentType.equals(Constants.LOAN);
}
@Override
public List<ListItem<Short>> constructPaymentTypeListForLoanRepayment(Short localeId) {
try {
List<PaymentTypeEntity> paymentTypeList = acceptedPaymentTypePersistence.getAcceptedPaymentTypesForATransaction(
localeId, TrxnTypes.loan_repayment.getValue());
List<ListItem<Short>> listItems = new ArrayList<ListItem<Short>>();
for (PaymentTypeEntity paymentTypeEntity : paymentTypeList) {
listItems.add(new ListItem<Short>(paymentTypeEntity.getId(), paymentTypeEntity.getName()));
}
return listItems;
} catch (PersistenceException e) {
throw new MifosRuntimeException(e);
}
}
private List<ListItem<Short>> constructPaymentTypeList(String paymentType, Short localeId) {
try {
List<PaymentTypeEntity> paymentTypeList = null;
if (paymentType != null && !Constants.EMPTY_STRING.equals(paymentType.trim())) {
if (isLoanPayment(paymentType)) {
paymentTypeList = acceptedPaymentTypePersistence.getAcceptedPaymentTypesForATransaction(
localeId, TrxnTypes.loan_repayment.getValue());
} else {
paymentTypeList = acceptedPaymentTypePersistence.getAcceptedPaymentTypesForATransaction(
localeId, TrxnTypes.fee.getValue());
}
}
List<ListItem<Short>> listItems = new ArrayList<ListItem<Short>>();
for (PaymentTypeEntity paymentTypeEntity : paymentTypeList) {
listItems.add(new ListItem<Short>(paymentTypeEntity.getId(), paymentTypeEntity.getName()));
}
return listItems;
} catch (PersistenceException e) {
throw new MifosRuntimeException(e);
}
}
@Override
public boolean isPaymentPermitted(Integer accountId) {
MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserContext userContext = toUserContext(user);
try {
AccountBO account = new AccountBusinessService().getAccount(accountId);
CustomerLevel customerLevel = null;
if (account.getType().equals(AccountTypes.CUSTOMER_ACCOUNT)) {
customerLevel = account.getCustomer().getLevel();
}
Short personnelId = userContext.getId();
if (account.getPersonnel() != null) {
personnelId = account.getPersonnel().getPersonnelId();
}
return ActivityMapper.getInstance().isPaymentPermittedForAccounts(account.getType(), customerLevel,
userContext, account.getOffice().getOfficeId(), personnelId);
} catch (ServiceException e) {
throw new MifosRuntimeException(e);
}
}
@Override
public List<ApplicableCharge> getApplicableFees(Integer accountId) {
try {
MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserContext userContext = toUserContext(user);
AccountBO account = this.accountBusinessService.getAccount(accountId);
try {
personnelDao.checkAccessPermission(userContext, account.getOfficeId(), account.getCustomer().getLoanOfficerId());
} catch (AccountException e) {
throw new MifosRuntimeException(e.getMessage(), e);
}
return new AccountBusinessService().getAppllicableFees(accountId, userContext);
} catch (ServiceException e) {
throw new MifosRuntimeException(e);
}
}
private UserContext toUserContext(MifosUser user) {
return new UserContextFactory().create(user);
}
@Override
public void applyCharge(Integer accountId, Short chargeId, Double chargeAmount, boolean isPenaltyType) {
MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserContext userContext = toUserContext(user);
try {
AccountBO account = new AccountBusinessService().getAccount(accountId);
if (account instanceof LoanBO && !account.isGroupLoanAccount()) {
List<LoanBO> individualLoans = this.loanDao.findIndividualLoans(account.getAccountId());
if (individualLoans != null && individualLoans.size() > 0) {
for (LoanBO individual : individualLoans) {
individual.updateDetails(userContext);
if (isPenaltyType && !chargeId.equals(Short.valueOf(AccountConstants.MISC_PENALTY))) {
PenaltyBO penalty = this.penaltyDao.findPenaltyById(chargeId.intValue());
individual.addAccountPenalty(new AccountPenaltiesEntity(individual, penalty, chargeAmount));
} else {
FeeBO fee = this.feeDao.findById(chargeId);
if (fee instanceof RateFeeBO) {
individual.applyCharge(chargeId, chargeAmount);
} else {
Double radio = individual.getLoanAmount().getAmount().doubleValue()
/ ((LoanBO) account).getLoanAmount().getAmount().doubleValue();
individual.applyCharge(chargeId, chargeAmount * radio);
}
}
}
}
}
account.updateDetails(userContext);
CustomerLevel customerLevel = null;
if (account.isCustomerAccount()) {
customerLevel = account.getCustomer().getLevel();
}
if (account.getPersonnel() != null) {
checkPermissionForApplyCharges(account.getType(), customerLevel, userContext,
account.getOffice().getOfficeId(), account.getPersonnel().getPersonnelId());
} else {
checkPermissionForApplyCharges(account.getType(), customerLevel, userContext,
account.getOffice().getOfficeId(), userContext.getId());
}
this.transactionHelper.startTransaction();
if(isPenaltyType && account instanceof LoanBO) {
PenaltyBO penalty = this.penaltyDao.findPenaltyById(chargeId.intValue());
((LoanBO) account).addAccountPenalty(new AccountPenaltiesEntity(account, penalty, chargeAmount));
} else {
account.applyCharge(chargeId, chargeAmount);
}
this.transactionHelper.commitTransaction();
} catch (ServiceException e) {
this.transactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} catch (ApplicationException e) {
this.transactionHelper.rollbackTransaction();
throw new BusinessRuleException(e.getKey(), e);
}
}
private void checkPermissionForApplyCharges(AccountTypes accountTypes, CustomerLevel customerLevel,
UserContext userContext, Short recordOfficeId, Short recordLoanOfficerId) throws ApplicationException {
if (!isPermissionAllowed(accountTypes, customerLevel, userContext, recordOfficeId, recordLoanOfficerId)) {
throw new CustomerException(SecurityConstants.KEY_ACTIVITY_NOT_ALLOWED);
}
}
private boolean isPermissionAllowed(AccountTypes accountTypes, CustomerLevel customerLevel,
UserContext userContext, Short recordOfficeId, Short recordLoanOfficerId) {
return ActivityMapper.getInstance().isApplyChargesPermittedForAccounts(accountTypes, customerLevel,
userContext, recordOfficeId, recordLoanOfficerId);
}
@Override
public AccountTypeCustomerLevelDto getAccountTypeCustomerLevelDto(Integer accountId) {
try {
AccountBO account = new AccountBusinessService().getAccount(accountId);
return new AccountTypeCustomerLevelDto(account.getType().getValue(), account.getCustomer().getCustomerLevel().getId());
} catch (ServiceException e) {
throw new MifosRuntimeException(e);
}
}
@Override
public void makePayment(AccountPaymentParametersDto accountPaymentParametersDto) {
this.accountService.makePayment(accountPaymentParametersDto);
}
@Override
public void applyAdjustment(String globalAccountNum, String adjustmentNote, Short loggedInUser) {
try {
AccountBO account = accountBusinessService.findBySystemId(globalAccountNum);
AccountPaymentEntity lastPayment = account.getLastPmntToBeAdjusted();
applyHistoricalAdjustment(globalAccountNum, (lastPayment == null) ? null : lastPayment.getPaymentId(),
adjustmentNote, loggedInUser, null);
} catch (ServiceException e) {
throw new MifosRuntimeException(e);
}
}
private void checkPermissionForAdjustment(AccountBO accountBO) throws ServiceException {
AccountPaymentEntity lastPmntToBeAdjusted = accountBO.getLastPmntToBeAdjusted();
if (lastPmntToBeAdjusted == null) return;
UserContext userContext = accountBO.getUserContext();
Date lastPaymentDate = lastPmntToBeAdjusted.getPaymentDate();
PersonnelBO personnel = accountBO.getPersonnel();
Short personnelId = personnel != null? personnel.getPersonnelId() : userContext.getId();
Short officeId = accountBO.getOfficeId();
accountBusinessService.checkPermissionForAdjustment(AccountTypes.LOAN_ACCOUNT, null, userContext, officeId, personnelId);
accountBusinessService.checkPermissionForAdjustmentOnBackDatedPayments(lastPaymentDate, userContext, officeId, personnelId);
}
UserContext getUserContext() {
MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return new UserContextFactory().create(user);
}
/**
* adjustment for member account payment has to be handled along with parent account payments and other members payments.
* This method prepares data and run applyHistoricalAdjustment so adjustment will be made for parent payment context.
*/
@Override
@PreAuthorize("isFullyAuthenticated()")
public void applyMemberAccountHistoricalAdjustment(String memberGlobalAccountNum, Integer memberPaymentId, String adjustmentNote,
Short personnelId, AdjustedPaymentDto adjustedPaymentDto) {
try {
LoanBO memberAccount = (LoanBO) accountBusinessService.findBySystemId(memberGlobalAccountNum);
LoanBO parentAccount = memberAccount.getParentAccount();
if (parentAccount == null){
throw new AccountException(LoanExceptionConstants.NO_PARENT_ACCOUNT_EXCEPTION);
}
AccountPaymentEntity parentPaymentEntity = memberAccount.findParentPaymentByMemberPaymentId(memberPaymentId);
List<AdjustedPaymentDto> membersAdjustedPaymentDtoList = new ArrayList<AdjustedPaymentDto>();
for (AccountPaymentEntity memberPayment : parentPaymentEntity.getMemberPayments()){
if (memberPayment.getAccount().getAccountId().equals(memberAccount.getAccountId())){
membersAdjustedPaymentDtoList.add(new AdjustedPaymentDto(adjustedPaymentDto.getAmount(), adjustedPaymentDto.getPaymentDate(),
adjustedPaymentDto.getPaymentType(), memberAccount.getAccountId()));
} else {
membersAdjustedPaymentDtoList.add(new AdjustedPaymentDto(memberPayment.getAmount().getAmount().toString(), adjustedPaymentDto.getPaymentDate(),
adjustedPaymentDto.getPaymentType(), memberPayment.getAccount().getAccountId()));
}
}
BigDecimal parentAmountSubstraction = memberAccount.findPaymentById(memberPaymentId).getAmount().getAmount().subtract((new BigDecimal(adjustedPaymentDto.getAmount())));
String newParentAmount = parentPaymentEntity.getAmount().getAmount().subtract(parentAmountSubstraction).toString();
AdjustedPaymentDto parentAdjustedPaymentDto = new AdjustedPaymentDto(newParentAmount, adjustedPaymentDto.getPaymentDate(),
adjustedPaymentDto.getPaymentType(), parentAccount.getAccountId(), membersAdjustedPaymentDtoList);
this.applyHistoricalAdjustment(parentAccount.getGlobalAccountNum(), parentPaymentEntity.getPaymentId(), adjustmentNote, personnelId, parentAdjustedPaymentDto);
} catch (ServiceException e) {
throw new MifosRuntimeException(e);
} catch (AccountException e) {
throw new MifosRuntimeException(e);
}
}
@Override
public void applyHistoricalAdjustment(String globalAccountNum, Integer paymentId, String adjustmentNote,
Short personnelId, AdjustedPaymentDto adjustedPaymentDto) {
try {
AccountBO accountBO = accountBusinessService.findBySystemId(globalAccountNum);
accountBO.setUserContext(getUserContext());
checkPermissionForAdjustment(accountBO);
PersonnelBO personnelBO = personnelPersistence.findPersonnelById(personnelId);
AccountPaymentEntity accountPaymentEntity = accountBO.findPaymentById(paymentId);
if (accountPaymentEntity == null) {
throw new AccountException(AccountExceptionConstants.CANNOTADJUST);
}
monthClosingServiceFacade.validateTransactionDate(accountPaymentEntity.getPaymentDate());
PaymentDto otherTransferPayment = accountPaymentEntity.getOtherTransferPaymentDto();
transactionHelper.flushAndClearSession(); //flush to avoid proxy casting problems
transactionHelper.startTransaction();
Integer newSavingsPaymentId = null;
if (otherTransferPayment != null) {
SavingsAdjustmentDto savingsAdjustment = new SavingsAdjustmentDto(otherTransferPayment.getAccountId().longValue(),
(adjustedPaymentDto == null) ? 0 : Double.valueOf(adjustedPaymentDto.getAmount()), adjustmentNote, otherTransferPayment.getPaymentId(),
(adjustedPaymentDto == null) ? otherTransferPayment.getPaymentDate() : new LocalDate(adjustedPaymentDto.getPaymentDate()));
PaymentDto newSavingsPayment = this.savingsServiceFacade.adjustTransaction(savingsAdjustment, true);
newSavingsPaymentId = (newSavingsPayment == null) ? null : newSavingsPayment.getPaymentId();
}
//reload after flush & clear
accountBO = accountBusinessService.findBySystemId(globalAccountNum);
accountBO.setUserContext(getUserContext());
AccountPaymentEntity adjustedPayment = null;
Integer adjustedId;
Stack<PaymentData> paymentsToBeReapplied = new Stack<PaymentData>();
Map<Integer, Stack<PaymentData>> memberPaymentsToBeReappliedMap = new HashMap<Integer, Stack<PaymentData>>();
if (accountBO.isGroupLoanAccount()){
for (LoanBO memberAccount : ((LoanBO)accountBO).getMemberAccounts()){
Stack<PaymentData> memberPaymentsToBeReapplied = new Stack<PaymentData>();
memberPaymentsToBeReappliedMap.put(memberAccount.getAccountId(), memberPaymentsToBeReapplied);
}
}
do {
adjustedPayment = accountBO.getLastPmntToBeAdjusted();
if (adjustedPayment == null) {
break;
}
adjustedId = adjustedPayment.getPaymentId();
if (!accountPaymentEntity.getPaymentId().equals(adjustedId)) {
PersonnelBO paymentCreator = (adjustedPayment.getCreatedByUser() == null) ? personnelBO : adjustedPayment.getCreatedByUser();
PaymentData paymentData = accountBO.createPaymentData(adjustedPayment.getAmount(), adjustedPayment.getPaymentDate(),
adjustedPayment.getReceiptNumber(), adjustedPayment.getReceiptDate(), adjustedPayment.getPaymentType().getId(),
paymentCreator);
paymentData.setOtherTransferPayment(adjustedPayment.getOtherTransferPayment());
paymentsToBeReapplied.push(paymentData);
// handling new Group Loan Members payments
for (AccountPaymentEntity memberAdjustedPayment : adjustedPayment.getMemberPayments()){
PaymentData memberPaymentData = memberAdjustedPayment.getAccount().createPaymentData(memberAdjustedPayment.getAmount(), adjustedPayment.getPaymentDate(),
adjustedPayment.getReceiptNumber(), adjustedPayment.getReceiptDate(), adjustedPayment.getPaymentType().getId(),
paymentCreator);
memberPaymentsToBeReappliedMap.get(memberAdjustedPayment.getAccount().getAccountId()).push(memberPaymentData);
}
}
transactionHelper.flushAndClearSession();
//reload after flush & clear
accountBO = accountBusinessService.findBySystemId(globalAccountNum);
accountBO.setUserContext(getUserContext());
accountBO.adjustLastPayment(adjustmentNote, personnelBO);
legacyAccountDao.createOrUpdate(accountBO);
//adjust New Group Loan member payments
if (accountBO.isGroupLoanAccount()){
for (LoanBO memberAccount : ((LoanBO)accountBO).getMemberAccounts()){
AccountPaymentEntity memberPayment = memberAccount.getLastPmntToBeAdjusted();
if (memberPayment.getParentPaymentId() == null) {
continue;
}
memberAccount.setUserContext(getUserContext());
memberAccount.adjustLastPayment(adjustmentNote, personnelBO);
legacyAccountDao.createOrUpdate(memberAccount);
}
}
transactionHelper.flushSession();
} while (!accountPaymentEntity.getPaymentId().equals(adjustedId));
if (adjustedPaymentDto != null) {
//reapply adjusted payment
PersonnelBO paymentCreator = (accountPaymentEntity.getCreatedByUser() == null) ? personnelBO : accountPaymentEntity.getCreatedByUser();
Money amount = new Money(accountBO.getCurrency(), adjustedPaymentDto.getAmount());
PaymentData paymentData = accountBO.createPaymentData(amount, adjustedPaymentDto.getPaymentDate(),
accountPaymentEntity.getReceiptNumber(), accountPaymentEntity.getReceiptDate(), adjustedPaymentDto.getPaymentType(),
paymentCreator);
paymentData.setAdjustment(true);
//new adjusted savings payment must be tied to this payment
if (newSavingsPaymentId != null) {
AccountPaymentEntity newSvngPayment = legacyAccountDao.findPaymentById(newSavingsPaymentId);
paymentData.setOtherTransferPayment(newSvngPayment);
}
accountBO.applyPayment(paymentData);
legacyAccountDao.createOrUpdate(accountBO);
transactionHelper.flushSession();
// handling new Group Loan Members payments
if (accountBO.isGroupLoanAccount()){
for (AdjustedPaymentDto adjustedMemberPayment : adjustedPaymentDto.getMemberPayments()){
AccountBO memberAccount = ((LoanBO)accountBO).findMemberById(adjustedMemberPayment.getAccountId());
BigDecimal adjustedMemberPaymentAmount = BigDecimal.ZERO;
if (!StringUtils.isBlank(adjustedMemberPayment.getAmount())) {
adjustedMemberPaymentAmount = new BigDecimal(adjustedMemberPayment.getAmount());
}
Money memberAmount = new Money(memberAccount.getCurrency(), adjustedMemberPaymentAmount.toString());
PaymentData memberPaymentData = memberAccount.createPaymentData(memberAmount, adjustedPaymentDto.getPaymentDate(),
accountPaymentEntity.getReceiptNumber(), accountPaymentEntity.getReceiptDate(), adjustedPaymentDto.getPaymentType(),
paymentCreator);
memberPaymentData.setParentPayment(accountBO.getLastPmnt());
memberAccount.applyPayment(memberPaymentData);
legacyAccountDao.createOrUpdate(memberAccount);
}
}
}
while (!paymentsToBeReapplied.isEmpty()) {
PaymentData paymentData = paymentsToBeReapplied.pop();
//avoid lazy loading exception
if (paymentData.getOtherTransferPayment() != null) {
legacyAccountDao.updatePayment(paymentData.getOtherTransferPayment());
}
accountBO.applyPayment(paymentData);
legacyAccountDao.createOrUpdate(accountBO);
transactionHelper.flushSession();
if (accountBO.isGroupLoanAccount()){
for (LoanBO memberAccount : ((LoanBO)accountBO).getMemberAccounts()){
PaymentData memberPaymentData = memberPaymentsToBeReappliedMap.get(memberAccount.getAccountId()).pop();
memberPaymentData.setParentPayment(accountBO.getLastPmnt());
memberAccount.applyPayment(memberPaymentData);
legacyAccountDao.createOrUpdate(memberAccount);
}
}
}
transactionHelper.commitTransaction();
} catch (ServiceException e) {
transactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} catch (AccountException e) {
transactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} catch (PersistenceException e) {
transactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} catch (RuntimeException e) {
transactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} finally {
transactionHelper.closeSession();
}
}
@Override
public void makePaymentFromSavingsAcc(AccountPaymentParametersDto accountPaymentParametersDto,
String savingsAccGlobalNumber) {
this.accountService.makePaymentFromSavings(accountPaymentParametersDto, savingsAccGlobalNumber);
}
@Override
/**
* do not use this method
* used and implemented in Audi Plugin
* @return null
*/
public Integer getAccountTrxnById(Integer id) {
return null;
}
@Override
public void applyGroupCharge(Map<Integer, String> idsAndValues, Short chargeId, boolean isPenaltyType) {
MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserContext userContext = toUserContext(user);
TreeMap<Integer, String> idsAndValueAsTreeMap = new TreeMap<Integer, String>(idsAndValues);
try {
AccountBO parentAccount = ((LoanBO)legacyAccountDao.getAccount(new AccountBusinessService().getAccount(idsAndValueAsTreeMap.firstKey()).getAccountId())).getParentAccount();
BigDecimal parentAmount = ((LoanBO) parentAccount).getLoanAmount().getAmount();
BigDecimal membersAmount = BigDecimal.ZERO;
for (Map.Entry<Integer, String> entry: idsAndValues.entrySet()) {
LoanBO individual = loanDao.findById(entry.getKey());
Double chargeAmount = Double.valueOf(entry.getValue());
if (chargeAmount.equals(0.0)) {
continue;
}
membersAmount = membersAmount.add(individual.getLoanAmount().getAmount());
individual.updateDetails(userContext);
if (isPenaltyType && !chargeId.equals(Short.valueOf(AccountConstants.MISC_PENALTY))) {
PenaltyBO penalty = this.penaltyDao.findPenaltyById(chargeId.intValue());
individual.addAccountPenalty(new AccountPenaltiesEntity(individual, penalty, chargeAmount));
} else {
individual.applyCharge(chargeId, chargeAmount);
}
}
boolean isRateCharge = false;
if (!chargeId.equals(Short.valueOf(AccountConstants.MISC_FEES))
&& !chargeId.equals(Short.valueOf(AccountConstants.MISC_PENALTY))) {
if (isPenaltyType) {
PenaltyBO penalty = this.penaltyDao.findPenaltyById(chargeId.intValue());
if (penalty instanceof RatePenaltyBO) {
isRateCharge = true;
}
} else {
FeeBO fee = feeDao.findById(chargeId);
if (fee.getFeeType().equals(RateAmountFlag.RATE)) {
isRateCharge = true;
}
}
}
Double chargeAmount = null;
if (!isRateCharge) {
chargeAmount = sumCharge(idsAndValues);
} else {
chargeAmount = Double.valueOf(idsAndValueAsTreeMap.firstEntry().getValue());
BigDecimal chargeAmountBig = new BigDecimal(chargeAmount);
membersAmount = membersAmount.multiply(chargeAmountBig);
int scale = Money.getInternalPrecision();
chargeAmountBig = membersAmount.divide(parentAmount, scale, RoundingMode.HALF_EVEN);
chargeAmount = chargeAmountBig.doubleValue();
}
parentAccount.updateDetails(userContext);
CustomerLevel customerLevel = null;
if (parentAccount.isCustomerAccount()) {
customerLevel = parentAccount.getCustomer().getLevel();
}
if (parentAccount.getPersonnel() != null) {
checkPermissionForApplyCharges(parentAccount.getType(), customerLevel, userContext,
parentAccount.getOffice().getOfficeId(), parentAccount.getPersonnel().getPersonnelId());
} else {
checkPermissionForApplyCharges(parentAccount.getType(), customerLevel, userContext,
parentAccount.getOffice().getOfficeId(), userContext.getId());
}
this.transactionHelper.startTransaction();
if(isPenaltyType && parentAccount instanceof LoanBO) {
PenaltyBO penalty = this.penaltyDao.findPenaltyById(chargeId.intValue());
((LoanBO) parentAccount).addAccountPenalty(new AccountPenaltiesEntity(parentAccount, penalty, chargeAmount));
} else {
parentAccount.applyCharge(chargeId, chargeAmount);
}
this.transactionHelper.commitTransaction();
} catch (ServiceException e) {
this.transactionHelper.rollbackTransaction();
throw new MifosRuntimeException(e);
} catch (ApplicationException e) {
this.transactionHelper.rollbackTransaction();
throw new BusinessRuleException(e.getKey(), e);
}
}
private Double sumCharge(Map<Integer, String> idsAndValues) {
Double sum = 0.0;
for (Map.Entry<Integer, String> entry: idsAndValues.entrySet()) {
sum += Double.valueOf(entry.getValue());
}
return sum;
}
@Override
public List<SavingsDetailDto> getActiveSavingsAccountsForClientByLoanId(Integer loanAccountId) {
try {
AccountBO account = accountBusinessService.getAccount(loanAccountId);
CustomerDto customer = account.getCustomer().toCustomerDto();
List<SavingsDetailDto> savingsInUse = clientServiceFacade.retrieveSavingsInUseForClient(customer
.getCustomerId());
List<SavingsDetailDto> accountsForTransfer = new ArrayList<SavingsDetailDto>();
if (savingsInUse != null) {
for (SavingsDetailDto savingsAccount : savingsInUse) {
if (savingsAccount.getAccountStateId().equals(AccountState.SAVINGS_ACTIVE.getValue())) {
accountsForTransfer.add(savingsAccount);
}
}
}
return accountsForTransfer;
} catch (ServiceException e) {
throw new MifosRuntimeException(e);
}
}
}