Java tutorial
/* * 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.application.servicefacade; import java.math.BigDecimal; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.mifos.accounts.acceptedpaymenttype.persistence.LegacyAcceptedPaymentTypeDao; import org.mifos.accounts.business.AccountActionDateEntity; import org.mifos.accounts.business.AccountActionEntity; import org.mifos.accounts.business.AccountCustomFieldEntity; import org.mifos.accounts.business.AccountNotesEntity; import org.mifos.accounts.business.AccountPaymentEntity; import org.mifos.accounts.business.AccountStateEntity; import org.mifos.accounts.business.AccountStateMachines; import org.mifos.accounts.business.AccountStatusChangeHistoryEntity; import org.mifos.accounts.business.AccountTrxnEntity; import org.mifos.accounts.business.service.AccountBusinessService; import org.mifos.accounts.exceptions.AccountException; import org.mifos.accounts.financial.business.FinancialTransactionBO; import org.mifos.accounts.productdefinition.business.InterestCalcTypeEntity; import org.mifos.accounts.productdefinition.business.SavingsOfferingBO; import org.mifos.accounts.productdefinition.persistence.SavingsProductDao; import org.mifos.accounts.productdefinition.util.helpers.InterestCalcType; import org.mifos.accounts.savings.business.SavingsAccountActivationDetail; import org.mifos.accounts.savings.business.SavingsAccountTypeInspector; import org.mifos.accounts.savings.business.SavingsBO; import org.mifos.accounts.savings.business.SavingsScheduleEntity; import org.mifos.accounts.savings.business.SavingsTrxnDetailEntity; import org.mifos.accounts.savings.interest.CalendarPeriod; import org.mifos.accounts.savings.interest.CalendarPeriodHelper; import org.mifos.accounts.savings.interest.EndOfDayDetail; import org.mifos.accounts.savings.interest.InterestCalculationPeriodCalculator; import org.mifos.accounts.savings.interest.InterestCalculationPeriodDetail; import org.mifos.accounts.savings.interest.InterestCalculationPeriodResult; import org.mifos.accounts.savings.interest.InterestCalculator; import org.mifos.accounts.savings.interest.InterestPostingPeriodResult; import org.mifos.accounts.savings.interest.NonCompoundingInterestCalculator; import org.mifos.accounts.savings.interest.SavingsInterestCalculatorFactory; import org.mifos.accounts.savings.interest.SavingsInterestDetail; import org.mifos.accounts.savings.interest.SavingsProductHistoricalInterestDetail; import org.mifos.accounts.savings.interest.schedule.InterestScheduledEvent; import org.mifos.accounts.savings.interest.schedule.SavingsInterestScheduledEventFactory; import org.mifos.accounts.savings.persistence.SavingsDao; import org.mifos.accounts.servicefacade.UserContextFactory; import org.mifos.accounts.util.helpers.AccountActionTypes; import org.mifos.accounts.util.helpers.AccountPaymentData; import org.mifos.accounts.util.helpers.AccountSearchResultsDto; import org.mifos.accounts.util.helpers.AccountState; import org.mifos.accounts.util.helpers.PaymentData; import org.mifos.accounts.util.helpers.SavingsPaymentData; import org.mifos.application.admin.servicefacade.InvalidDateException; import org.mifos.application.holiday.persistence.HolidayDao; import org.mifos.application.master.MessageLookup; import org.mifos.application.master.business.CustomFieldType; import org.mifos.application.master.business.LookUpValueEntity; import org.mifos.application.master.business.MifosCurrency; import org.mifos.application.master.business.PaymentTypeEntity; import org.mifos.application.util.helpers.EntityType; import org.mifos.application.util.helpers.TrxnTypes; import org.mifos.calendar.CalendarEvent; import org.mifos.config.AccountingRules; import org.mifos.config.ProcessFlowRules; import org.mifos.core.MifosRuntimeException; import org.mifos.customers.api.CustomerLevel; import org.mifos.customers.business.CustomerBO; import org.mifos.customers.persistence.CustomerDao; import org.mifos.customers.persistence.CustomerPersistence; import org.mifos.customers.personnel.business.PersonnelBO; import org.mifos.customers.personnel.persistence.PersonnelDao; import org.mifos.dto.domain.AccountStatusDto; import org.mifos.dto.domain.AccountUpdateStatus; import org.mifos.dto.domain.AuditLogDto; import org.mifos.dto.domain.CreateAccountNote; import org.mifos.dto.domain.CustomFieldDto; import org.mifos.dto.domain.CustomerDto; import org.mifos.dto.domain.CustomerSearchDto; import org.mifos.dto.domain.CustomerSearchResultDto; import org.mifos.dto.domain.DueOnDateDto; import org.mifos.dto.domain.NoteSearchDto; import org.mifos.dto.domain.OpeningBalanceSavingsAccount; import org.mifos.dto.domain.PrdOfferingDto; import org.mifos.dto.domain.SavingsAccountClosureDto; import org.mifos.dto.domain.SavingsAccountCreationDto; import org.mifos.dto.domain.SavingsAccountDetailDto; import org.mifos.dto.domain.SavingsAdjustmentDto; import org.mifos.dto.domain.SavingsDepositDto; import org.mifos.dto.domain.SavingsStatusChangeHistoryDto; import org.mifos.dto.domain.SavingsWithdrawalDto; import org.mifos.dto.screen.DepositWithdrawalReferenceDto; import org.mifos.dto.screen.ListElement; import org.mifos.dto.screen.NotesSearchResultsDto; import org.mifos.dto.screen.SavingsAccountDepositDueDto; import org.mifos.dto.screen.SavingsAdjustmentReferenceDto; import org.mifos.dto.screen.SavingsProductReferenceDto; import org.mifos.dto.screen.SavingsRecentActivityDto; import org.mifos.dto.screen.SavingsTransactionHistoryDto; import org.mifos.framework.components.audit.business.service.AuditBusinessService; import org.mifos.framework.components.audit.util.helpers.AuditLogView; import org.mifos.framework.exceptions.HibernateSearchException; import org.mifos.framework.exceptions.PageExpiredException; import org.mifos.framework.exceptions.PersistenceException; import org.mifos.framework.exceptions.ServiceException; import org.mifos.framework.exceptions.StatesInitializationException; import org.mifos.framework.hibernate.helper.HibernateTransactionHelper; import org.mifos.framework.hibernate.helper.HibernateTransactionHelperForStaticHibernateUtil; import org.mifos.framework.hibernate.helper.QueryResult; import org.mifos.framework.util.DateTimeService; import org.mifos.framework.util.helpers.Constants; import org.mifos.framework.util.helpers.DateUtils; import org.mifos.framework.util.helpers.Money; import org.mifos.framework.util.helpers.MoneyUtils; import org.mifos.framework.util.helpers.SessionUtils; import org.mifos.platform.questionnaire.service.QuestionGroupDetail; import org.mifos.platform.questionnaire.service.QuestionGroupDetails; import org.mifos.platform.questionnaire.service.QuestionnaireServiceFacade; import org.mifos.security.MifosUser; import org.mifos.security.util.UserContext; import org.mifos.service.BusinessRuleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; public class SavingsServiceFacadeWebTier implements SavingsServiceFacade { private static final Logger logger = LoggerFactory.getLogger(SavingsServiceFacadeWebTier.class); private final SavingsDao savingsDao; private final SavingsProductDao savingsProductDao; private final PersonnelDao personnelDao; private final CustomerDao customerDao; private final HolidayDao holidayDao; @Autowired private LegacyAcceptedPaymentTypeDao legacyAcceptedPaymentTypeDao; @Autowired private QuestionnaireServiceFacade questionnaireServiceFacade; private HibernateTransactionHelper transactionHelper = new HibernateTransactionHelperForStaticHibernateUtil(); private CalendarPeriodHelper interestCalculationIntervalHelper = new CalendarPeriodHelper(); private SavingsInterestScheduledEventFactory savingsInterestScheduledEventFactory = new SavingsInterestScheduledEventFactory(); @Autowired public SavingsServiceFacadeWebTier(SavingsDao savingsDao, SavingsProductDao savingsProductDao, PersonnelDao personnelDao, CustomerDao customerDao, HolidayDao holidayDao) { this.savingsDao = savingsDao; this.savingsProductDao = savingsProductDao; this.personnelDao = personnelDao; this.customerDao = customerDao; this.holidayDao = holidayDao; } @Override public void deposit(SavingsDepositDto savingsDeposit) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsDeposit.getSavingsId()); try { personnelDao.checkAccessPermission(userContext, savingsAccount.getOfficeId(), savingsAccount.getCustomer().getLoanOfficerId()); } catch (AccountException e) { throw new MifosRuntimeException(e.getMessage(), e); } savingsAccount.updateDetails(userContext); PersonnelBO createdBy = this.personnelDao.findPersonnelById(Short.valueOf((short) user.getUserId())); CustomerBO customer = this.customerDao.findCustomerById(savingsDeposit.getCustomerId().intValue()); Money totalAmount = new Money(savingsAccount.getCurrency(), BigDecimal.valueOf(savingsDeposit.getAmount())); PaymentData payment = PaymentData.createPaymentData(totalAmount, createdBy, savingsDeposit.getModeOfPayment().shortValue(), savingsDeposit.getDateOfDeposit().toDateMidnight().toDate()); if (savingsDeposit.getDateOfReceipt() != null) { payment.setReceiptDate(savingsDeposit.getDateOfReceipt().toDateMidnight().toDate()); } payment.setReceiptNum(savingsDeposit.getReceiptId()); payment.setCustomer(customer); for (AccountActionDateEntity installment : savingsAccount .getTotalInstallmentsDue(savingsDeposit.getCustomerId().intValue())) { AccountPaymentData accountPaymentData = new SavingsPaymentData(installment); payment.addAccountPaymentData(accountPaymentData); } try { this.transactionHelper.startTransaction(); this.transactionHelper.beginAuditLoggingFor(savingsAccount); savingsAccount.applyPayment(payment); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (AccountException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getKey(), e); } finally { this.transactionHelper.closeSession(); } } public void setTransactionHelper(HibernateTransactionHelper transactionHelper) { this.transactionHelper = transactionHelper; } @Override public void withdraw(SavingsWithdrawalDto savingsWithdrawal) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsWithdrawal.getSavingsId()); try { personnelDao.checkAccessPermission(userContext, savingsAccount.getOfficeId(), savingsAccount.getCustomer().getLoanOfficerId()); } catch (AccountException e) { throw new MifosRuntimeException(e.getMessage(), e); } savingsAccount.updateDetails(userContext); PersonnelBO createdBy = this.personnelDao.findPersonnelById(Short.valueOf((short) user.getUserId())); CustomerBO customer = this.customerDao.findCustomerById(savingsWithdrawal.getCustomerId().intValue()); Money totalAmount = new Money(savingsAccount.getCurrency(), BigDecimal.valueOf(savingsWithdrawal.getAmount())); PaymentData payment = PaymentData.createPaymentData(totalAmount, createdBy, savingsWithdrawal.getModeOfPayment().shortValue(), savingsWithdrawal.getDateOfWithdrawal().toDateMidnight().toDate()); if (savingsWithdrawal.getDateOfReceipt() != null) { payment.setReceiptDate(savingsWithdrawal.getDateOfReceipt().toDateMidnight().toDate()); } payment.setReceiptNum(savingsWithdrawal.getReceiptId()); payment.setCustomer(customer); List<EndOfDayDetail> allEndOfDayDetailsForAccount = savingsDao .retrieveAllEndOfDayDetailsFor(savingsAccount.getCurrency(), savingsWithdrawal.getSavingsId()); MifosCurrency currencyInUse = savingsAccount.getCurrency(); LocalDate dateOfWithdrawal = new LocalDate(payment.getTransactionDate()); Money balanceOnDateOfWithdrawal = calculateAccountBalanceOn(dateOfWithdrawal.plusDays(1), allEndOfDayDetailsForAccount, currencyInUse); if (payment.getTotalAmount().isGreaterThan(balanceOnDateOfWithdrawal)) { throw new BusinessRuleException("errors.insufficentbalance", new String[] { savingsAccount.getGlobalAccountNum() }); } try { this.transactionHelper.startTransaction(); this.transactionHelper.beginAuditLoggingFor(savingsAccount); savingsAccount.withdraw(payment, false); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (AccountException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getKey(), e); } finally { this.transactionHelper.closeSession(); } } @Override public void adjustTransaction(SavingsAdjustmentDto savingsAdjustment) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsAdjustment.getSavingsId()); try { personnelDao.checkAccessPermission(userContext, savingsAccount.getOfficeId(), savingsAccount.getCustomer().getLoanOfficerId()); } catch (AccountException e) { throw new MifosRuntimeException(e.getMessage(), e); } PersonnelBO updatedBy = this.personnelDao.findPersonnelById(userContext.getId()); savingsAccount.updateDetails(userContext); Money amountAdjustedTo = new Money(savingsAccount.getCurrency(), BigDecimal.valueOf(savingsAdjustment.getAdjustedAmount())); try { this.transactionHelper.startTransaction(); this.transactionHelper.beginAuditLoggingFor(savingsAccount); savingsAccount.adjustLastUserAction(amountAdjustedTo, savingsAdjustment.getNote(), updatedBy); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (BusinessRuleException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getMessageKey(), e); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e.getMessage(), e); } finally { this.transactionHelper.closeSession(); } } public void setInterestCalculationIntervalHelper(CalendarPeriodHelper interestCalculationIntervalHelper) { this.interestCalculationIntervalHelper = interestCalculationIntervalHelper; } public void setSavingsInterestScheduledEventFactory( SavingsInterestScheduledEventFactory savingsInterestScheduledEventFactory) { this.savingsInterestScheduledEventFactory = savingsInterestScheduledEventFactory; } /** * This method is responsible for posting interest for the last posting period only. * * It assumes that interest calculation and interest posting frequencies cannot change on savings product of savings * account. It assumes that the interest posting date is correct and valid with respect to the interest posting * frequency of the product/account. */ @Override public void postInterestForLastPostingPeriod(LocalDate dateBatchJobIsScheduled) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); PersonnelBO createdBy = this.personnelDao.findPersonnelById(userContext.getId()); List<Integer> accountIds = this.savingsDao .retrieveAllActiveAndInActiveSavingsAccountsPendingInterestPostingOn(dateBatchJobIsScheduled); for (Integer savingsId : accountIds) { SavingsBO savingsAccount = this.savingsDao.findById(Long.valueOf(savingsId)); savingsAccount.updateDetails(userContext); List<EndOfDayDetail> allEndOfDayDetailsForAccount = savingsDao .retrieveAllEndOfDayDetailsFor(savingsAccount.getCurrency(), Long.valueOf(savingsId)); LocalDate interestPostingDate = new LocalDate(savingsAccount.getNextIntPostDate()); InterestScheduledEvent postingSchedule = savingsInterestScheduledEventFactory .createScheduledEventFrom(savingsAccount.getInterestPostingMeeting()); LocalDate startOfPeriod = postingSchedule.findFirstDateOfPeriodForMatchingDate(interestPostingDate); CalendarPeriod lastInterestPostingPeriod = new CalendarPeriod(startOfPeriod, interestPostingDate); InterestPostingPeriodResult interestPostingPeriodResult = determinePostingPeriodResult( lastInterestPostingPeriod, savingsAccount, allEndOfDayDetailsForAccount); savingsAccount.postInterest(postingSchedule, interestPostingPeriodResult, createdBy); StringBuilder postingInfoMessage = new StringBuilder().append("account id: ") .append(savingsAccount.getAccountId()).append("posting interest: ") .append(interestPostingPeriodResult); logger.info(postingInfoMessage.toString()); try { this.transactionHelper.startTransaction(); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(savingsAccount.getAccountId().toString(), e); } finally { this.transactionHelper.closeSession(); } } } private Money calculateAccountBalanceOn(LocalDate date, List<EndOfDayDetail> allEndOfDayDetailsForAccount, MifosCurrency currency) { Money balance = Money.zero(currency); for (EndOfDayDetail endOfDayDetail : allEndOfDayDetailsForAccount) { if (endOfDayDetail.getDate().isBefore(date)) { balance = balance.add(endOfDayDetail.getResultantAmountForDay()); } } return balance; } // TODO - move into InterestPostingPeriodCalculator private InterestPostingPeriodResult doCalculateInterestForPostingPeriod(CalendarPeriod interestPostingPeriod, Money startingBalanceForPeriod, List<SavingsProductHistoricalInterestDetail> historicalInterestDetails, List<EndOfDayDetail> allEndOfDayDetailsForAccount, SavingsInterestDetail interestDetail, InterestScheduledEvent interestCalculationEvent) { InterestPostingPeriodResult postingPeriodResult = new InterestPostingPeriodResult(interestPostingPeriod); Money runningBalance = startingBalanceForPeriod; if (!allEndOfDayDetailsForAccount.isEmpty()) { List<CalendarPeriod> interestCalculationPeriods = new ArrayList<CalendarPeriod>(); // 1. determine all valid interest calculation periods that fall within this posting period and create a // interest calculation period calculator for each one (to handle possible different interest rates) LocalDate firstActivityDate = allEndOfDayDetailsForAccount.get(0).getDate(); if (interestPostingPeriod.contains(firstActivityDate)) { interestCalculationPeriods = this.interestCalculationIntervalHelper.determineAllPossiblePeriods( firstActivityDate, interestCalculationEvent, interestPostingPeriod.getEndDate()); } else { interestCalculationPeriods = this.interestCalculationIntervalHelper.determineAllPossiblePeriods( interestPostingPeriod.getStartDate(), interestCalculationEvent, interestPostingPeriod.getEndDate()); } for (CalendarPeriod calendarPeriod : interestCalculationPeriods) { NonCompoundingInterestCalculator interestCalculationPeriodCalculator = createInterestCalculationPeriodCalculator( interestDetail, interestCalculationEvent); SavingsProductHistoricalInterestDetail historicalInterestDetail = findMatchingHistoricalInterestDetail( historicalInterestDetails, calendarPeriod); if (historicalInterestDetail != null) { int accountingNumberOfInterestDaysInYear = AccountingRules.getNumberOfInterestDays(); SavingsInterestDetail historicalSavingsInterestDetail = new SavingsInterestDetail( interestDetail.getInterestCalcType(), historicalInterestDetail.getInterestRate(), accountingNumberOfInterestDaysInYear, historicalInterestDetail.getMinAmntForInt()); interestCalculationPeriodCalculator = createInterestCalculationPeriodCalculator( historicalSavingsInterestDetail, interestCalculationEvent); } // 2. populate InterestCalculationPeriodDetail with valid end of day details for calculation period InterestCalculationPeriodDetail interestCalculationPeriodDetail = InterestCalculationPeriodDetail .populatePeriodDetailBasedOnInterestCalculationInterval(calendarPeriod, allEndOfDayDetailsForAccount, runningBalance); // 3. calculate average principal, total principal and interest details for calculation period. InterestCalculationPeriodResult calculationPeriodResult = interestCalculationPeriodCalculator .calculateCalculationPeriodDetail(interestCalculationPeriodDetail); // 4. only sum the total principal as 'interest calculation periods are non-compounding' runningBalance = runningBalance.add(calculationPeriodResult.getTotalPrincipal()); postingPeriodResult.add(calculationPeriodResult); } } postingPeriodResult.setPeriodBalance(runningBalance); return postingPeriodResult; } private SavingsProductHistoricalInterestDetail findMatchingHistoricalInterestDetail( List<SavingsProductHistoricalInterestDetail> historicalInterestDetails, CalendarPeriod calendarPeriod) { SavingsProductHistoricalInterestDetail match = null; SavingsProductHistoricalInterestDetail closestMatch = null; for (SavingsProductHistoricalInterestDetail historicalInterestDetail : historicalInterestDetails) { if (calendarPeriod.contains(historicalInterestDetail.getStartDate())) { match = historicalInterestDetail; } if (calendarPeriod.getEndDate().isBefore(historicalInterestDetail.getStartDate())) { closestMatch = historicalInterestDetail; } } if (match == null) { match = closestMatch; } return match; } private NonCompoundingInterestCalculator createInterestCalculationPeriodCalculator( SavingsInterestDetail interestDetail, InterestScheduledEvent interestCalculationEvent) { InterestCalculator interestCalculator = SavingsInterestCalculatorFactory.create(interestDetail); NonCompoundingInterestCalculator interestCalculationPeriodCalculator = new InterestCalculationPeriodCalculator( interestCalculator, interestCalculationEvent, interestCalculationIntervalHelper); return interestCalculationPeriodCalculator; } private UserContext toUserContext(MifosUser user) { return new UserContextFactory().create(user); } @Override public SavingsAccountClosureDto retrieveClosingDetails(Long savingsId, LocalDate closureDate) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); InterestScheduledEvent postingSchedule = savingsInterestScheduledEventFactory .createScheduledEventFrom(savingsAccount.getInterestPostingMeeting()); LocalDate nextPostingDate = new LocalDate(savingsAccount.getNextIntPostDate()); LocalDate startOfPostingPeriod = postingSchedule.findFirstDateOfPeriodForMatchingDate(nextPostingDate); CalendarPeriod postingPeriodAtClosure; if (startOfPostingPeriod.isAfter(closureDate)) { postingPeriodAtClosure = new CalendarPeriod(closureDate, closureDate); } else { postingPeriodAtClosure = new CalendarPeriod(startOfPostingPeriod, closureDate); } List<EndOfDayDetail> allEndOfDayDetailsForAccount = savingsDao .retrieveAllEndOfDayDetailsFor(savingsAccount.getCurrency(), Long.valueOf(savingsId)); InterestPostingPeriodResult postingPeriodAtClosureResult = determinePostingPeriodResult( postingPeriodAtClosure, savingsAccount, allEndOfDayDetailsForAccount); Money endOfAccountBalance = postingPeriodAtClosureResult.getPeriodBalance(); Money interestAmountAtClosure = postingPeriodAtClosureResult.getPeriodInterest(); List<ListElement> depositPaymentTypes = retrieveDepositPaymentTypes(userContext); return new SavingsAccountClosureDto(new LocalDate(), endOfAccountBalance.toString(), interestAmountAtClosure.toString(), depositPaymentTypes); } private InterestPostingPeriodResult determinePostingPeriodResult(CalendarPeriod postingPeriod, SavingsBO savingsAccount, List<EndOfDayDetail> allEndOfDayDetailsForAccount) { List<SavingsProductHistoricalInterestDetail> historicalInterestDetails = savingsAccount .getHistoricalInterestDetailsForPeriod(postingPeriod); MifosCurrency currencyInUse = savingsAccount.getCurrency(); Money startingBalanceForPeriod = calculateAccountBalanceOn(postingPeriod.getStartDate(), allEndOfDayDetailsForAccount, currencyInUse); InterestCalcType interestCalcType = savingsAccount.getInterestCalcType(); int accountingNumberOfInterestDaysInYear = AccountingRules.getNumberOfInterestDays(); SavingsInterestDetail interestDetail = new SavingsInterestDetail(interestCalcType, savingsAccount.getInterestRate(), accountingNumberOfInterestDaysInYear, savingsAccount.getMinAmntForInt()); InterestScheduledEvent interestCalculationEvent = new SavingsInterestScheduledEventFactory() .createScheduledEventFrom(savingsAccount.getInterestCalculationMeeting()); return doCalculateInterestForPostingPeriod(postingPeriod, startingBalanceForPeriod, historicalInterestDetails, allEndOfDayDetailsForAccount, interestDetail, interestCalculationEvent); } @Override public void closeSavingsAccount(Long savingsId, String notes, SavingsWithdrawalDto closeAccountDto) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); savingsAccount.updateDetails(userContext); PersonnelBO createdBy = this.personnelDao.findPersonnelById(userContext.getId()); LocalDate closureDate = closeAccountDto.getDateOfWithdrawal(); // Assumption that all previous interest postings occured correctly InterestScheduledEvent postingSchedule = savingsInterestScheduledEventFactory .createScheduledEventFrom(savingsAccount.getInterestPostingMeeting()); LocalDate nextPostingDate = new LocalDate(savingsAccount.getNextIntPostDate()); LocalDate startOfPostingPeriod = postingSchedule.findFirstDateOfPeriodForMatchingDate(nextPostingDate); CalendarPeriod postingPeriodAtClosure; if (startOfPostingPeriod.isAfter(closureDate)) { postingPeriodAtClosure = new CalendarPeriod(closureDate, closureDate); } else { postingPeriodAtClosure = new CalendarPeriod(startOfPostingPeriod, closureDate); } List<EndOfDayDetail> allEndOfDayDetailsForAccount = savingsDao .retrieveAllEndOfDayDetailsFor(savingsAccount.getCurrency(), Long.valueOf(savingsId)); InterestPostingPeriodResult postingPeriodAtClosureResult = determinePostingPeriodResult( postingPeriodAtClosure, savingsAccount, allEndOfDayDetailsForAccount); savingsAccount.postInterest(postingSchedule, postingPeriodAtClosureResult, createdBy); AccountNotesEntity notesEntity = new AccountNotesEntity(new DateTimeService().getCurrentJavaSqlDate(), notes, createdBy, savingsAccount); try { CustomerBO customer = savingsAccount.getCustomer(); if (closeAccountDto.getCustomerId() != null) { List<CustomerBO> clientList = new CustomerPersistence().getActiveAndOnHoldChildren( savingsAccount.getCustomer().getSearchId(), savingsAccount.getCustomer().getOfficeId(), CustomerLevel.CLIENT); for (CustomerBO client : clientList) { if (closeAccountDto.getCustomerId().intValue() == client.getCustomerId().intValue()) { customer = client; break; } } } Money amount = new Money(savingsAccount.getCurrency(), closeAccountDto.getAmount().toString()); PaymentTypeEntity paymentType = new PaymentTypeEntity(closeAccountDto.getModeOfPayment().shortValue()); Date receiptDate = null; if (closeAccountDto.getDateOfReceipt() != null) { receiptDate = closeAccountDto.getDateOfReceipt().toDateMidnight().toDate(); } AccountPaymentEntity closeAccount = new AccountPaymentEntity(savingsAccount, amount, closeAccountDto.getReceiptId(), receiptDate, paymentType, closeAccountDto.getDateOfWithdrawal().toDateMidnight().toDate()); this.transactionHelper.startTransaction(); this.transactionHelper.beginAuditLoggingFor(savingsAccount); savingsAccount.closeAccount(closeAccount, notesEntity, customer, createdBy); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (BusinessRuleException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getMessageKey(), e); } catch (PersistenceException e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e); } finally { this.transactionHelper.closeSession(); } } @Override public DepositWithdrawalReferenceDto retrieveDepositWithdrawalReferenceData(Long savingsId, Integer customerId) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); try { SavingsBO savingsAccount = savingsDao.findById(savingsId); String depositDue = savingsAccount.getTotalPaymentDue(customerId).toString(); String withdrawalDue = "0"; List<ListElement> clients = new ArrayList<ListElement>(); if (savingsAccount.isGroupModelWithIndividualAccountability()) { List<CustomerBO> activeAndOnHoldClients = new CustomerPersistence().getActiveAndOnHoldChildren( savingsAccount.getCustomer().getSearchId(), savingsAccount.getCustomer().getOfficeId(), CustomerLevel.CLIENT); for (CustomerBO client : activeAndOnHoldClients) { clients.add(new ListElement(client.getCustomerId(), client.getDisplayName())); } } List<AccountActionEntity> trxnTypes = new ArrayList<AccountActionEntity>(); trxnTypes.add(new AccountBusinessService() .getAccountAction(AccountActionTypes.SAVINGS_DEPOSIT.getValue(), userContext.getLocaleId())); trxnTypes.add(new AccountBusinessService() .getAccountAction(AccountActionTypes.SAVINGS_WITHDRAWAL.getValue(), userContext.getLocaleId())); List<ListElement> transactionTypes = new ArrayList<ListElement>(); for (AccountActionEntity accountActionEntity : trxnTypes) { LookUpValueEntity lookupValue = accountActionEntity.getLookUpValue(); String messageText = lookupValue.getMessageText(); if (StringUtils.isBlank(messageText)) { messageText = ApplicationContextProvider.getBean(MessageLookup.class) .lookup(lookupValue.getPropertiesKey()); } transactionTypes.add(new ListElement(accountActionEntity.getId().intValue(), messageText)); } List<ListElement> depositPaymentTypes = retrieveDepositPaymentTypes(userContext); List<ListElement> withdrawalPaymentTypes = new ArrayList<ListElement>(); List<PaymentTypeEntity> withdrawalPaymentEntityTypes = legacyAcceptedPaymentTypeDao .getAcceptedPaymentTypesForATransaction(userContext.getLocaleId(), TrxnTypes.savings_withdrawal.getValue()); for (PaymentTypeEntity paymentTypeEntity : withdrawalPaymentEntityTypes) { LookUpValueEntity lookupValue = paymentTypeEntity.getLookUpValue(); String messageText = lookupValue.getMessageText(); if (StringUtils.isBlank(messageText)) { messageText = ApplicationContextProvider.getBean(MessageLookup.class) .lookup(lookupValue.getPropertiesKey()); } withdrawalPaymentTypes.add(new ListElement(paymentTypeEntity.getId().intValue(), messageText)); } boolean backDatedTransactionsAllowed = AccountingRules.isBackDatedTxnAllowed(); LocalDate defaultTransactionDate = new LocalDate(); return new DepositWithdrawalReferenceDto(transactionTypes, depositPaymentTypes, withdrawalPaymentTypes, clients, backDatedTransactionsAllowed, defaultTransactionDate, depositDue, withdrawalDue); } catch (ServiceException e) { throw new MifosRuntimeException(e); } catch (PersistenceException e) { throw new MifosRuntimeException(e); } } private List<ListElement> retrieveDepositPaymentTypes(UserContext userContext) { try { List<ListElement> depositPaymentTypes = new ArrayList<ListElement>(); List<PaymentTypeEntity> acceptedPaymentEntityTypes = legacyAcceptedPaymentTypeDao .getAcceptedPaymentTypesForATransaction(userContext.getLocaleId(), TrxnTypes.savings_deposit.getValue()); for (PaymentTypeEntity paymentTypeEntity : acceptedPaymentEntityTypes) { LookUpValueEntity lookupValue = paymentTypeEntity.getLookUpValue(); String messageText = lookupValue.getMessageText(); if (StringUtils.isBlank(messageText)) { messageText = ApplicationContextProvider.getBean(MessageLookup.class) .lookup(lookupValue.getPropertiesKey()); } depositPaymentTypes.add(new ListElement(paymentTypeEntity.getId().intValue(), messageText)); } return depositPaymentTypes; } catch (PersistenceException e) { throw new MifosRuntimeException(e); } } @Override public SavingsAdjustmentReferenceDto retrieveAdjustmentReferenceData(Long savingsId) { SavingsBO savings = savingsDao.findById(savingsId); AccountPaymentEntity lastPayment = savings.findMostRecentPaymentByPaymentDate(); String clientName = null; String amount = null; boolean depositOrWithdrawal = false; if (lastPayment != null) { amount = lastPayment.getAmount().toString(); depositOrWithdrawal = lastPayment.isSavingsDepositOrWithdrawal(); } if (!savings.getCustomer().isClient() && lastPayment != null) { CustomerBO customer = null; for (AccountTrxnEntity accountTrxn : lastPayment.getAccountTrxns()) { customer = accountTrxn.getCustomer(); break; } if (customer != null && customer.isClient()) { clientName = customer.getDisplayName(); } } return new SavingsAdjustmentReferenceDto(clientName, amount, depositOrWithdrawal); } @Override public List<PrdOfferingDto> retrieveApplicableSavingsProductsForCustomer(Integer customerId) { List<PrdOfferingDto> applicableSavingsProducts = new ArrayList<PrdOfferingDto>(); CustomerBO customer = this.customerDao.findCustomerById(customerId); applicableSavingsProducts = this.savingsProductDao .findSavingsProductByCustomerLevel(customer.getCustomerLevel()); return applicableSavingsProducts; } @Override public SavingsProductReferenceDto retrieveSavingsProductReferenceData(Integer productId) { SavingsOfferingBO savingsProduct = this.savingsProductDao.findById(productId); List<ListElement> interestCalcTypeOptions = new ArrayList<ListElement>(); List<InterestCalcTypeEntity> interestCalculationTypes = this.savingsProductDao .retrieveInterestCalculationTypes(); for (InterestCalcTypeEntity entity : interestCalculationTypes) { interestCalcTypeOptions.add(new ListElement(entity.getId().intValue(), entity.getName())); } boolean savingsPendingApprovalEnabled = ProcessFlowRules.isSavingsPendingApprovalStateEnabled(); return new SavingsProductReferenceDto(interestCalcTypeOptions, savingsProduct.toFullDto(), savingsPendingApprovalEnabled); } @Override public String createSavingsAccount(OpeningBalanceSavingsAccount openingBalanceSavingsAccount) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); LocalDate createdDate = new LocalDate(); Integer createdById = user.getUserId(); PersonnelBO createdBy = this.personnelDao.findPersonnelById(createdById.shortValue()); CustomerBO customer = this.customerDao .findCustomerBySystemId(openingBalanceSavingsAccount.getCustomerGlobalId()); SavingsOfferingBO savingsProduct = this.savingsProductDao .findBySystemId(openingBalanceSavingsAccount.getProductGlobalId()); AccountState savingsAccountState = AccountState.fromShort(openingBalanceSavingsAccount.getAccountState()); Money recommendedOrMandatory = new Money(savingsProduct.getCurrency(), openingBalanceSavingsAccount.getRecommendedOrMandatoryAmount()); Money openingBalance = new Money(savingsProduct.getCurrency(), openingBalanceSavingsAccount.getOpeningBalance()); LocalDate activationDate = openingBalanceSavingsAccount.getActivationDate(); CalendarEvent calendarEvents = this.holidayDao.findCalendarEventsForThisYearAndNext(customer.getOfficeId()); SavingsAccountActivationDetail activationDetails = SavingsBO.determineAccountActivationDetails(customer, savingsProduct, recommendedOrMandatory, savingsAccountState, calendarEvents, activationDate); SavingsBO savingsAccount = SavingsBO.createOpeningBalanceIndividualSavingsAccount(customer, savingsProduct, recommendedOrMandatory, savingsAccountState, createdDate, createdById, activationDetails, createdBy, openingBalance); try { this.transactionHelper.startTransaction(); this.savingsDao.save(savingsAccount); this.transactionHelper.flushSession(); savingsAccount.generateSystemId(createdBy.getOffice().getGlobalOfficeNum()); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); return savingsAccount.getGlobalAccountNum(); } catch (BusinessRuleException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getMessageKey(), e); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e); } finally { this.transactionHelper.closeSession(); } } @Override public Long createSavingsAccount(SavingsAccountCreationDto savingsAccountCreation, List<QuestionGroupDetail> questionGroups) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); LocalDate createdDate = new LocalDate(); Integer createdById = user.getUserId(); PersonnelBO createdBy = this.personnelDao.findPersonnelById(createdById.shortValue()); CustomerBO customer = this.customerDao.findCustomerById(savingsAccountCreation.getCustomerId()); SavingsOfferingBO savingsProduct = this.savingsProductDao.findById(savingsAccountCreation.getProductId()); Money recommendedOrMandatory = new Money(savingsProduct.getCurrency(), savingsAccountCreation.getRecommendedOrMandatoryAmount()); AccountState savingsAccountState = AccountState.fromShort(savingsAccountCreation.getAccountState()); CalendarEvent calendarEvents = this.holidayDao.findCalendarEventsForThisYearAndNext(customer.getOfficeId()); SavingsAccountTypeInspector savingsAccountWrapper = new SavingsAccountTypeInspector(customer, savingsProduct.getRecommendedAmntUnit()); try { SavingsBO savingsAccount = null; if (savingsAccountWrapper.isIndividualSavingsAccount()) { savingsAccount = SavingsBO.createIndividalSavingsAccount(customer, savingsProduct, recommendedOrMandatory, savingsAccountState, createdDate, createdById, calendarEvents, createdBy); } else if (savingsAccountWrapper.isJointSavingsAccountWithClientTracking()) { List<CustomerBO> activeAndOnHoldClients = new CustomerPersistence().getActiveAndOnHoldChildren( customer.getSearchId(), customer.getOfficeId(), CustomerLevel.CLIENT); savingsAccount = SavingsBO.createJointSavingsAccount(customer, savingsProduct, recommendedOrMandatory, savingsAccountState, createdDate, createdById, calendarEvents, createdBy, activeAndOnHoldClients); } this.transactionHelper.startTransaction(); this.savingsDao.save(savingsAccount); this.transactionHelper.flushSession(); savingsAccount.generateSystemId(createdBy.getOffice().getGlobalOfficeNum()); this.savingsDao.save(savingsAccount); this.transactionHelper.flushSession(); // save question groups if (!questionGroups.isEmpty()) { Integer eventSourceId = questionnaireServiceFacade.getEventSourceId("Create", "Savings"); QuestionGroupDetails questionGroupDetails = new QuestionGroupDetails( Integer.valueOf(user.getUserId()).shortValue(), savingsAccount.getAccountId(), eventSourceId, questionGroups); questionnaireServiceFacade.saveResponses(questionGroupDetails); } this.transactionHelper.commitTransaction(); return savingsAccount.getAccountId().longValue(); } catch (BusinessRuleException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getMessageKey(), e); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e); } finally { this.transactionHelper.closeSession(); } } @Override public Long createSavingsAccount(SavingsAccountCreationDto savingsAccountCreation) { return createSavingsAccount(savingsAccountCreation, new ArrayList<QuestionGroupDetail>()); } @Override public AccountStatusDto retrieveAccountStatuses(Long savingsId) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); try { List<ListElement> savingsStatesList = new ArrayList<ListElement>(); AccountStateMachines.getInstance().initializeSavingsStates(); List<AccountStateEntity> statusList = AccountStateMachines.getInstance() .getSavingsStatusList(savingsAccount.getAccountState()); for (AccountStateEntity accountState : statusList) { savingsStatesList.add(new ListElement(accountState.getId().intValue(), accountState.getName())); } return new AccountStatusDto(savingsStatesList); } catch (StatesInitializationException e) { throw new MifosRuntimeException(e); } } @Override public void updateSavingsAccountStatus(AccountUpdateStatus updateStatus) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); PersonnelBO loggedInUser = this.personnelDao.findPersonnelById(userContext.getId()); SavingsBO savingsAccount = this.savingsDao.findById(updateStatus.getSavingsId()); savingsAccount.updateDetails(userContext); try { this.transactionHelper.startTransaction(); this.transactionHelper.beginAuditLoggingFor(savingsAccount); AccountState newStatus = AccountState.fromShort(updateStatus.getNewStatusId()); // FIXME - keithw - refactor savings specific logic out of changeStatus and create savings statue machine // wrapper. savingsAccount.changeStatus(newStatus, updateStatus.getFlagId(), updateStatus.getComment(), loggedInUser); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (BusinessRuleException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getMessageKey(), e); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e); } finally { this.transactionHelper.closeSession(); } } @Override public SavingsAccountDepositDueDto retrieveDepositDueDetails(String globalAccountNum) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findBySystemId(globalAccountNum); try { personnelDao.checkAccessPermission(userContext, savingsAccount.getOfficeId(), savingsAccount.getCustomer().getLoanOfficerId()); } catch (AccountException e) { throw new MifosRuntimeException(e.getMessage(), e); } List<DueOnDateDto> previousDueDates = new ArrayList<DueOnDateDto>(); SavingsScheduleEntity nextInstallment = (SavingsScheduleEntity) savingsAccount .getDetailsOfNextInstallment(); Money totalDepositDue = Money.zero(savingsAccount.getCurrency()); LocalDate nextDueDate = new LocalDate(); if (nextInstallment != null) { nextDueDate = new LocalDate(nextInstallment.getActionDate()); totalDepositDue = nextInstallment.getTotalDepositDue(); } List<AccountActionDateEntity> scheduledDeposits = savingsAccount .getAccountActionDatesSortedByInstallmentId(); for (AccountActionDateEntity scheduledDeposit : scheduledDeposits) { if (!scheduledDeposit.isPaid() && scheduledDeposit.isBefore(nextDueDate)) { SavingsScheduleEntity savingsScheduledDeposit = (SavingsScheduleEntity) scheduledDeposit; previousDueDates.add(new DueOnDateDto(scheduledDeposit.getActionDate(), MoneyUtils.currencyRound(savingsScheduledDeposit.getTotalDepositDue()).toString())); } } DueOnDateDto nextDueDetail = new DueOnDateDto( new java.sql.Date(nextDueDate.toDateMidnight().toDate().getTime()), MoneyUtils.currencyRound(totalDepositDue).toString()); AccountStateEntity accountStateEntity = savingsAccount.getAccountState(); return new SavingsAccountDepositDueDto(nextDueDetail, previousDueDates, accountStateEntity.getId(), accountStateEntity.getName()); } @Override public List<SavingsRecentActivityDto> retrieveRecentSavingsActivities(Long savingsId) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); savingsAccount.updateDetails(userContext); return savingsAccount.getRecentAccountActivity(null); } @Override public List<SavingsTransactionHistoryDto> retrieveTransactionHistory(String globalAccountNum) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findBySystemId(globalAccountNum); CustomerBO customerBO = savingsAccount.getCustomer(); savingsAccount.updateDetails(userContext); try { personnelDao.checkAccessPermission(userContext, customerBO.getOfficeId(), customerBO.getLoanOfficerId()); } catch (AccountException e) { throw new MifosRuntimeException("Access denied!", e); } List<SavingsTransactionHistoryDto> savingsTransactionHistoryViewList = new ArrayList<SavingsTransactionHistoryDto>(); // Check for order-by clause in AccountBO.hbm.xml, // AccountPayment.hbm.xml and AccountTrxnEntity.hbm.xml for // accountPaymentSet , // accountTrxnSet and financialBoSet. They all should be set for their // primay key column desc in both. If stated is not there, the code // below will behave abnormally. List<AccountPaymentEntity> accountPaymentSet = savingsAccount.getAccountPayments(); for (AccountPaymentEntity accountPaymentEntity : accountPaymentSet) { Set<AccountTrxnEntity> accountTrxnEntitySet = accountPaymentEntity.getAccountTrxns(); for (AccountTrxnEntity accountTrxnEntity : accountTrxnEntitySet) { Set<FinancialTransactionBO> financialTransactionBOSet = accountTrxnEntity .getFinancialTransactions(); for (FinancialTransactionBO financialTransactionBO : financialTransactionBOSet) { SavingsTransactionHistoryDto savingsTransactionHistoryDto = new SavingsTransactionHistoryDto(); savingsTransactionHistoryDto.setTransactionDate(financialTransactionBO.getActionDate()); String preferredTransactionDate = DateUtils.getUserLocaleDate(userContext.getPreferredLocale(), financialTransactionBO.getActionDate().toString()); savingsTransactionHistoryDto.setUserPrefferedTransactionDate(preferredTransactionDate); savingsTransactionHistoryDto.setPaymentId(accountTrxnEntity.getAccountPayment().getPaymentId()); savingsTransactionHistoryDto.setAccountTrxnId(accountTrxnEntity.getAccountTrxnId()); savingsTransactionHistoryDto.setType(financialTransactionBO.getFinancialAction().getName()); savingsTransactionHistoryDto.setGlcode(financialTransactionBO.getGlcode().getGlcode()); if (financialTransactionBO.isDebitEntry()) { savingsTransactionHistoryDto .setDebit(String.valueOf(removeSign(financialTransactionBO.getPostedAmount()))); } else if (financialTransactionBO.isCreditEntry()) { savingsTransactionHistoryDto .setCredit(String.valueOf(removeSign(financialTransactionBO.getPostedAmount()))); } savingsTransactionHistoryDto.setBalance( String.valueOf(removeSign(((SavingsTrxnDetailEntity) accountTrxnEntity).getBalance()))); savingsTransactionHistoryDto.setClientName(accountTrxnEntity.getCustomer().getDisplayName()); savingsTransactionHistoryDto.setPostedDate(financialTransactionBO.getPostedDate()); String preferredDate = DateUtils.getUserLocaleDate(userContext.getPreferredLocale(), financialTransactionBO.getPostedDate().toString()); savingsTransactionHistoryDto.setUserPrefferedPostedDate(preferredDate); if (accountTrxnEntity.getPersonnel() != null) { savingsTransactionHistoryDto.setPostedBy(accountTrxnEntity.getPersonnel().getDisplayName()); } if (financialTransactionBO.getNotes() != null && !financialTransactionBO.getNotes().equals("")) { savingsTransactionHistoryDto.setNotes(financialTransactionBO.getNotes()); } savingsTransactionHistoryViewList.add(savingsTransactionHistoryDto); } } } return savingsTransactionHistoryViewList; } private String removeSign(Money amount) { if (amount.isLessThanZero()) { return amount.negate().toString(); } return amount.toString(); } @Override public List<SavingsStatusChangeHistoryDto> retrieveStatusChangeHistory(String globalAccountNum) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findBySystemId(globalAccountNum); savingsAccount.updateDetails(userContext); List<SavingsStatusChangeHistoryDto> dtoList = new ArrayList<SavingsStatusChangeHistoryDto>(); List<AccountStatusChangeHistoryEntity> statusChangeHistory = savingsAccount.getAccountStatusChangeHistory(); for (AccountStatusChangeHistoryEntity accountStatusChangeHistory : statusChangeHistory) { dtoList.add(accountStatusChangeHistory.toDto()); } return dtoList; } @Override public void updateSavingsAccountDetails(Long savingsId, String recommendedOrMandatoryAmount, List<CustomFieldDto> customFields) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); savingsAccount.updateDetails(userContext); Set<AccountCustomFieldEntity> accountCustomFields = savingsAccount.getAccountCustomFields(); for (CustomFieldDto view : customFields) { boolean fieldPresent = false; if (CustomFieldType.DATE.getValue().equals(view.getFieldType()) && org.apache.commons.lang.StringUtils.isNotBlank(view.getFieldValue())) { try { SimpleDateFormat format = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, userContext.getPreferredLocale()); String userfmt = DateUtils.convertToCurrentDateFormat(format.toPattern()); view.setFieldValue(DateUtils.convertUserToDbFmt(view.getFieldValue(), userfmt)); } catch (InvalidDateException e) { throw new BusinessRuleException(e.getMessage(), e); } } for (AccountCustomFieldEntity customFieldEntity : accountCustomFields) { if (customFieldEntity.getFieldId().equals(view.getFieldId())) { fieldPresent = true; customFieldEntity.setFieldValue(view.getFieldValue()); } } if (!fieldPresent) { accountCustomFields .add(new AccountCustomFieldEntity(savingsAccount, view.getFieldId(), view.getFieldValue())); } } Money amount = new Money(savingsAccount.getCurrency(), recommendedOrMandatoryAmount); try { this.transactionHelper.startTransaction(); this.transactionHelper.beginAuditLoggingFor(savingsAccount); savingsAccount.update(amount, accountCustomFields); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (BusinessRuleException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getMessageKey(), e); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e); } finally { this.transactionHelper.closeSession(); } } @Override public SavingsAccountDetailDto retrieveSavingsAccountDetails(Long savingsId) { MifosUser mifosUser = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = new UserContextFactory().create(mifosUser); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); try { personnelDao.checkAccessPermission(userContext, savingsAccount.getOfficeId(), savingsAccount.getCustomer().getLoanOfficerId()); } catch (AccountException e) { throw new MifosRuntimeException("Access denied!", e); } return savingsAccount.toDto(); } @Override public SavingsAccountDetailDto retrieveSavingsAccountDetails(String globalAccountNum) { MifosUser mifosUser = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = new UserContextFactory().create(mifosUser); SavingsBO savingsAccount = this.savingsDao.findBySystemId(globalAccountNum); savingsAccount.setUserContext(userContext); try { personnelDao.checkAccessPermission(userContext, savingsAccount.getOfficeId(), savingsAccount.getCustomer().getLoanOfficerId()); } catch (AccountException e) { throw new MifosRuntimeException("Access denied!", e); } return savingsAccount.toDto(); } @Override public void waiveNextDepositAmountDue(Long savingsId) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); savingsAccount.updateDetails(userContext); PersonnelBO loggedInUser = this.personnelDao.findPersonnelById(userContext.getId()); try { this.transactionHelper.startTransaction(); savingsAccount.waiveNextDepositAmountDue(loggedInUser); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (BusinessRuleException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getMessageKey(), e); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e); } finally { this.transactionHelper.closeSession(); } } @Override public void waiveDepositAmountOverDue(Long savingsId) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); savingsAccount.updateDetails(userContext); PersonnelBO loggedInUser = this.personnelDao.findPersonnelById(userContext.getId()); try { this.transactionHelper.startTransaction(); savingsAccount.waiveAmountOverDue(loggedInUser); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (BusinessRuleException e) { this.transactionHelper.rollbackTransaction(); throw new BusinessRuleException(e.getMessageKey(), e); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e); } finally { this.transactionHelper.closeSession(); } } @Override public List<AuditLogDto> retrieveSavingsAccountAuditLogs(Long savingsId) { List<AuditLogDto> auditLogDtos = new ArrayList<AuditLogDto>(); AuditBusinessService auditBusinessService = new AuditBusinessService(); try { List<AuditLogView> auditLogs = auditBusinessService.getAuditLogRecords(EntityType.SAVINGS.getValue(), savingsId.intValue()); for (AuditLogView auditLogView : auditLogs) { auditLogDtos.add(auditLogView.toDto()); } return auditLogDtos; } catch (ServiceException e) { throw new MifosRuntimeException(e); } } @Override public NotesSearchResultsDto retrievePagedNotesDto(NoteSearchDto noteSearch) { return this.savingsDao.searchNotes(noteSearch); } @Override public SavingsAccountDetailDto retrieveSavingsAccountNotes(Long savingsId) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); SavingsBO savingsAccount = this.savingsDao.findById(savingsId); savingsAccount.updateDetails(userContext); return savingsAccount.toDto(); } @Override public void addNote(CreateAccountNote accountNote) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); PersonnelBO createdBy = this.personnelDao.findPersonnelById(accountNote.getCreatedById().shortValue()); SavingsBO savingsAccount = this.savingsDao.findById(accountNote.getAccountId().longValue()); AccountNotesEntity accountNotes = new AccountNotesEntity( new java.sql.Date(accountNote.getCommentDate().toDateMidnight().toDate().getTime()), accountNote.getComment(), createdBy, savingsAccount); try { this.transactionHelper.startTransaction(); savingsAccount.updateDetails(userContext); savingsAccount.addAccountNotes(accountNotes); this.savingsDao.save(savingsAccount); this.transactionHelper.commitTransaction(); } catch (Exception e) { this.transactionHelper.rollbackTransaction(); throw new MifosRuntimeException(e); } finally { this.transactionHelper.closeSession(); } } @Override public List<CustomerSearchResultDto> retrieveCustomerThatQualifyForSavings( CustomerSearchDto customerSearchDto) { MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserContext userContext = toUserContext(user); try { List<CustomerSearchResultDto> pagedDetails = new ArrayList<CustomerSearchResultDto>(); QueryResult customerForSavings = new CustomerPersistence() .searchCustForSavings(customerSearchDto.getSearchTerm(), userContext.getId()); int position = this.resultsetOffset(customerSearchDto.getPage(), customerSearchDto.getPageSize()); List<AccountSearchResultsDto> pagedResults = customerForSavings.get(position, customerSearchDto.getPageSize()); int i = 1; for (AccountSearchResultsDto customerBO : pagedResults) { CustomerSearchResultDto customer = new CustomerSearchResultDto(); customer.setCustomerId(customerBO.getClientId()); customer.setBranchName(customerBO.getOfficeName()); customer.setGlobalId(customerBO.getGlobelNo()); customer.setSearchIndex(i); customer.setCenterName(StringUtils.defaultIfEmpty(customerBO.getCenterName(), "--")); customer.setGroupName(StringUtils.defaultIfEmpty(customerBO.getGroupName(), "--")); customer.setClientName(customerBO.getClientName()); pagedDetails.add(customer); i++; } return pagedDetails; } catch (PersistenceException e) { throw new MifosRuntimeException(e); } catch (HibernateSearchException e) { throw new MifosRuntimeException(e); } } /** * Calculate the correct offset in SQL "limit" clause. * * This method has protected visibility so that it can be tested. * * @param currentPage * Current page number. Page number starts at 1. * @param itemsPerPage * Number of entries per page. * @return */ protected int resultsetOffset(int currentPage, int itemsPerPage) { return (currentPage - 1) * itemsPerPage; } @Override public CustomerDto retreieveCustomerDetails(Integer customerId) { CustomerBO customer = this.customerDao.findCustomerById(customerId); return new CustomerDto(customerId, customer.getDisplayName(), customer.getGlobalCustNum(), customer.getStatus().getValue(), customer.getCustomerLevel().getId(), customer.getVersionNo(), customer.getOfficeId(), customer.getLoanOfficerId()); } @Override public void putSavingsBusinessKeyInSession(String globalAccountNum, HttpServletRequest request) { SavingsBO savingsBO = this.savingsDao.findBySystemId(globalAccountNum); try { SessionUtils.removeThenSetAttribute(Constants.BUSINESS_KEY, savingsBO, request); } catch (PageExpiredException e) { throw new MifosRuntimeException(e); } } }