/*
* 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.platform.rest.controller;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.mifos.accounts.api.AccountService;
import org.mifos.accounts.productdefinition.business.SavingsOfferingBO;
import org.mifos.accounts.productdefinition.persistence.SavingsProductDao;
import org.mifos.accounts.savings.business.SavingsBO;
import org.mifos.accounts.savings.persistence.SavingsDao;
import org.mifos.accounts.savings.persistence.SavingsPersistence;
import org.mifos.accounts.util.helpers.AccountState;
import org.mifos.application.servicefacade.SavingsServiceFacade;
import org.mifos.application.util.helpers.TrxnTypes;
import org.mifos.customers.business.CustomerBO;
import org.mifos.customers.persistence.CustomerDao;
import org.mifos.customers.personnel.persistence.PersonnelDao;
import org.mifos.dto.domain.CustomerDto;
import org.mifos.dto.domain.PaymentTypeDto;
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.SavingsWithdrawalDto;
import org.mifos.dto.screen.SavingsAccountDepositDueDto;
import org.mifos.framework.util.helpers.Constants;
import org.mifos.framework.util.helpers.Money;
import org.mifos.framework.util.helpers.SessionUtils;
import org.mifos.platform.rest.controller.RESTAPIHelper.ErrorMessage;
import org.mifos.platform.rest.controller.validation.ParamValidationException;
import org.mifos.security.MifosUser;
import org.mifos.security.util.UserContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class SavingsAccountRESTController {
@Autowired
private AccountService accountService;
@Autowired
private SavingsServiceFacade savingsServiceFacade;
@Autowired
private SavingsDao savingsDao;
@Autowired
private PersonnelDao personnelDao;
@Autowired
private CustomerDao customerDao;
@Autowired
private SavingsProductDao savingsProductDao;
@RequestMapping(value = "account/savings/num-{globalAccountNum}/deposit", method = RequestMethod.POST)
public @ResponseBody
Map<String, String> deposit(@PathVariable String globalAccountNum,
@RequestParam BigDecimal amount,
@RequestParam String trxnDate,
@RequestParam(required=false) Short receiptId,
@RequestParam(required=false) String receiptDate,
@RequestParam Short paymentTypeId) throws Exception {
return doSavingsTrxn(globalAccountNum, amount, trxnDate, receiptId, receiptDate, paymentTypeId, TrxnTypes.savings_deposit);
}
@RequestMapping(value = "account/savings/num-{globalAccountNum}/withdraw", method = RequestMethod.POST)
public @ResponseBody
Map<String, String> withdraw(@PathVariable String globalAccountNum,
@RequestParam BigDecimal amount,
@RequestParam String trxnDate,
@RequestParam(required=false) Short receiptId,
@RequestParam(required=false) String receiptDate,
@RequestParam Short paymentTypeId) throws Exception {
return doSavingsTrxn(globalAccountNum, amount, trxnDate, receiptId, receiptDate, paymentTypeId, TrxnTypes.savings_withdrawal);
}
@RequestMapping(value = "account/savings/num-{globalAccountNum}/adjustment", method = RequestMethod.POST)
public @ResponseBody
Map<String, String> applyAdjustment(@PathVariable String globalAccountNum,
@RequestParam BigDecimal amount,
@RequestParam String note,
@RequestParam(required=false) Integer paymentId) throws Exception {
validateAmount(amount);
validateNote(note);
SavingsBO savingsBO = savingsDao.findBySystemId(globalAccountNum);
new SavingsPersistence().initialize(savingsBO);
Integer accountId = savingsBO.getAccountId();
Long savingsId = Long.valueOf(accountId.toString());
SavingsAdjustmentDto savingsAdjustment = new SavingsAdjustmentDto(savingsId, amount.doubleValue(), note,
(paymentId == null) ? savingsBO.getLastPmnt().getPaymentId() : paymentId, new LocalDate(savingsBO.getLastPmnt().getPaymentDate()));
Money balanceBeforePayment = savingsBO.getSavingsBalance();
this.savingsServiceFacade.adjustTransaction(savingsAdjustment);
MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
DateTime today = new DateTime();
CustomerBO client = savingsBO.getCustomer();
Map<String, String> map = new HashMap<String, String>();
map.put("status", "success");
map.put("clientName", client.getDisplayName());
map.put("clientNumber", client.getGlobalCustNum());
map.put("savingsDisplayName", savingsBO.getSavingsOffering().getPrdOfferingName());
map.put("adjustmentDate", today.toLocalDate().toString());
map.put("adjustmentTime", today.toLocalTime().toString());
map.put("adjustmentAmount", savingsBO.getLastPmnt().getAmount().toString());
map.put("adjustmentMadeBy", personnelDao.findPersonnelById((short) user.getUserId()).getDisplayName());
map.put("balanceBeforeAdjustment", balanceBeforePayment.toString());
map.put("balanceAfterAdjustment", savingsBO.getSavingsBalance().toString());
map.put("note", note);
return map;
}
@RequestMapping(value = "/account/savings/num-{globalAccountNum}", method = RequestMethod.GET)
public @ResponseBody
SavingsAccountDetailDto getSavingsByNumber(@PathVariable String globalAccountNum, HttpServletRequest request) throws Exception {
SavingsBO savings = this.savingsDao.findBySystemId(globalAccountNum);
savings.setUserContext((UserContext) SessionUtils.getAttribute(Constants.USER_CONTEXT_KEY, request.getSession()));
return savingsServiceFacade.retrieveSavingsAccountDetails(savings.getAccountId().longValue());
}
@RequestMapping(value = "/account/savings/num-{globalAccountNum}/due", method = RequestMethod.GET)
public @ResponseBody
SavingsAccountDepositDueDto getSavingsDepositDueDetailsByNumber(@PathVariable String globalAccountNum) throws Exception {
return savingsServiceFacade.retrieveDepositDueDetails(globalAccountNum);
}
private Map<String, String> doSavingsTrxn(String globalAccountNum, BigDecimal amount,
String trxnDate, Short receiptId, String receiptDate, Short paymentTypeId, TrxnTypes trxnType) throws Exception {
validateAmount(amount);
String format = "dd-MM-yyyy";
DateTime trnxDate = validateDateString(trxnDate, format);
validateSavingsDate(trnxDate);
DateTime receiptDateTime = null;
if (receiptDate != null && !receiptDate.isEmpty()){
receiptDateTime = validateDateString(receiptDate, format);
validateSavingsDate(receiptDateTime);
} else {
receiptDateTime = new DateTime(trnxDate);
}
SavingsBO savingsBO = savingsDao.findBySystemId(globalAccountNum);
validateAccountState(savingsBO);
MifosUser user = (MifosUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Integer accountId = savingsBO.getAccountId();
DateTime today = new DateTime();
String receiptIdString;
if ( receiptId == null ){
receiptIdString = "";
} else {
receiptIdString = receiptId.toString();
}
CustomerBO client = savingsBO.getCustomer();
CustomerDto customer = new CustomerDto();
customer.setCustomerId(client.getCustomerId());
Money balanceBeforePayment = savingsBO.getSavingsBalance();
if (trxnType.equals(TrxnTypes.savings_deposit)) {
validateSavingsPaymentTypeId(paymentTypeId, accountService.getSavingsPaymentTypes());
SavingsDepositDto savingsDeposit = new SavingsDepositDto(accountId.longValue(), savingsBO.getCustomer().getCustomerId().longValue(),
trnxDate.toLocalDate(), amount.doubleValue(), paymentTypeId.intValue(), receiptIdString, receiptDateTime.toLocalDate(),
Locale.UK);
this.savingsServiceFacade.deposit(savingsDeposit);
}
else {
validateSavingsPaymentTypeId(paymentTypeId, accountService.getSavingsWithdrawalTypes());
SavingsWithdrawalDto savingsWithdrawal = new SavingsWithdrawalDto(accountId.longValue(), savingsBO.getCustomer().getCustomerId().longValue(),
trnxDate.toLocalDate(), amount.doubleValue(), paymentTypeId.intValue(), receiptIdString, receiptDateTime.toLocalDate(),
Locale.UK);
this.savingsServiceFacade.withdraw(savingsWithdrawal);
}
Map<String, String> map = new HashMap<String, String>();
map.put("status", "success");
map.put("clientName", client.getDisplayName());
map.put("clientNumber", client.getGlobalCustNum());
map.put("savingsDisplayName", savingsBO.getSavingsOffering().getPrdOfferingName());
map.put("paymentDate", today.toLocalDate().toString());
map.put("paymentTime", today.toLocalTime().toString());
map.put("paymentAmount", savingsBO.getLastPmnt().getAmount().toString());
map.put("paymentMadeBy", personnelDao.findPersonnelById((short) user.getUserId()).getDisplayName());
map.put("balanceBeforePayment", balanceBeforePayment.toString());
map.put("balanceAfterPayment", savingsBO.getSavingsBalance().toString());
return map;
}
@RequestMapping(value = "/account/savings/create", method=RequestMethod.POST)
public @ResponseBody
Map<String, String> createSavingsAccount(@RequestParam String globalCustomerNum,
@RequestParam(required=false) String amount,
@RequestParam Integer productId) throws Throwable {
CustomerBO customer = validateGlobalCustNum(globalCustomerNum);
SavingsOfferingBO product = validateProductId(productId);
String recommendedAmount = null != amount ? amount : product.getRecommendedAmount().toString();
Integer customerId = customer.getCustomerId();
AccountState accountState = AccountState.SAVINGS_PENDING_APPROVAL;
SavingsAccountCreationDto savingsAccountCreation = new SavingsAccountCreationDto(
productId, customerId, accountState.getValue(), recommendedAmount);
Long savings = savingsServiceFacade.createSavingsAccount(savingsAccountCreation);
SavingsAccountDetailDto details = savingsServiceFacade.retrieveSavingsAccountDetails(savings);
Map<String,String> map = new HashMap<String, String>();
map.put("customerName", customer.getDisplayName());
map.put("productName", product.getPrdOfferingName());
map.put("interesRate", details.getProductDetails().getInterestRate().toString());
map.put("interestRatePeriod", details.getProductDetails().getInterestCalculationFrequency().toString());
map.put("recommendedAmount", recommendedAmount);
return map;
}
private void validateAmount(BigDecimal amount) throws ParamValidationException {
if (amount != null && amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new ParamValidationException(ErrorMessage.NON_NEGATIVE_AMOUNT);
}
}
private void validateNote(String note) throws ParamValidationException {
if (note == null || note.isEmpty()){
throw new ParamValidationException(ErrorMessage.INVALID_NOTE);
}
}
private SavingsOfferingBO validateProductId(Integer productId) throws ParamValidationException {
SavingsOfferingBO product = savingsProductDao.findById(productId);
if (null == product) {
throw new ParamValidationException(ErrorMessage.INVALID_PRODUCT_ID);
}
else {
return product;
}
}
private CustomerBO validateGlobalCustNum(String globalCustomerNum) throws ParamValidationException {
CustomerBO client = customerDao.findCustomerBySystemId(globalCustomerNum);
if (null == client) {
throw new ParamValidationException(ErrorMessage.INVALID_GLOABAL_CUSTOMER_NUM);
}
else {
return client;
}
}
public void validateAccountState(SavingsBO savingsBO) throws ParamValidationException {
if (!savingsBO.getState().isActiveSavingsAccountState() ){
throw new ParamValidationException(ErrorMessage.NOT_ACTIVE_ACCOUNT);
}
}
public void validateSavingsPaymentTypeId(Short paymentTypeId, List<PaymentTypeDto> savingsPaymentTypes) throws ParamValidationException{
boolean valid = false;
for ( PaymentTypeDto paymentType : savingsPaymentTypes){
if ( paymentType.getValue().equals(paymentTypeId) ){
valid = true;
}
}
if ( !valid) {
throw new ParamValidationException(ErrorMessage.INVALID_PAYMENT_TYPE_ID);
}
}
public DateTime validateDateString(String dateString, String format) throws ParamValidationException {
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
try {
return new DateTime(dateFormat.parse(dateString));
} catch (ParseException e){
throw new ParamValidationException(ErrorMessage.INVALID_DATE_STRING + "in format " + format);
}
}
public void validateSavingsDate(DateTime date) throws ParamValidationException {
DateTime today = new DateTime();
if (date.isAfter(today)){
throw new ParamValidationException(ErrorMessage.FUTURE_DATE);
}
}
}