/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Cyclos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package nl.strohalm.cyclos.controls.loans;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import nl.strohalm.cyclos.annotations.Inject;
import nl.strohalm.cyclos.controls.ActionContext;
import nl.strohalm.cyclos.controls.BaseFormAction;
import nl.strohalm.cyclos.entities.accounts.AccountType;
import nl.strohalm.cyclos.entities.accounts.loans.Loan;
import nl.strohalm.cyclos.entities.accounts.loans.LoanGroup;
import nl.strohalm.cyclos.entities.accounts.loans.LoanPayment;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.entities.customization.fields.CustomField;
import nl.strohalm.cyclos.entities.customization.fields.CustomFieldValue;
import nl.strohalm.cyclos.entities.customization.fields.PaymentCustomField;
import nl.strohalm.cyclos.entities.customization.fields.PaymentCustomFieldValue;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.entities.settings.LocalSettings;
import nl.strohalm.cyclos.services.customization.PaymentCustomFieldService;
import nl.strohalm.cyclos.services.loangroups.LoanGroupService;
import nl.strohalm.cyclos.services.transactions.DoPaymentDTO;
import nl.strohalm.cyclos.services.transactions.GrantLoanDTO;
import nl.strohalm.cyclos.services.transactions.GrantLoanWithInterestDTO;
import nl.strohalm.cyclos.services.transactions.GrantMultiPaymentLoanDTO;
import nl.strohalm.cyclos.services.transactions.LoanService;
import nl.strohalm.cyclos.services.transactions.PaymentService;
import nl.strohalm.cyclos.services.transactions.ProjectionDTO;
import nl.strohalm.cyclos.services.transactions.exceptions.AuthorizedPaymentInPastException;
import nl.strohalm.cyclos.services.transactions.exceptions.CreditsException;
import nl.strohalm.cyclos.services.transfertypes.TransactionFeePreviewDTO;
import nl.strohalm.cyclos.services.transfertypes.TransactionFeeService;
import nl.strohalm.cyclos.services.transfertypes.TransferTypeService;
import nl.strohalm.cyclos.utils.ActionHelper;
import nl.strohalm.cyclos.utils.CustomFieldHelper;
import nl.strohalm.cyclos.utils.CustomFieldHelper.Entry;
import nl.strohalm.cyclos.utils.RelationshipHelper;
import nl.strohalm.cyclos.utils.conversion.CoercionHelper;
import nl.strohalm.cyclos.utils.validation.RequiredError;
import nl.strohalm.cyclos.utils.validation.ValidationException;
import org.apache.commons.lang.StringUtils;
import org.apache.struts.action.ActionForward;
/**
* Action used to confirm a loan
* @author luis
*/
public class ConfirmLoanAction extends BaseFormAction {
private LoanService loanService;
private LoanGroupService loanGroupService;
private TransactionFeeService transactionFeeService;
private PaymentCustomFieldService paymentCustomFieldService;
private PaymentService paymentService;
private TransferTypeService transferTypeService;
private CustomFieldHelper customFieldHelper;
@Inject
public void setCustomFieldHelper(final CustomFieldHelper customFieldHelper) {
this.customFieldHelper = customFieldHelper;
}
@Inject
public void setLoanGroupService(final LoanGroupService loanGroupService) {
this.loanGroupService = loanGroupService;
}
@Inject
public void setLoanService(final LoanService loanService) {
this.loanService = loanService;
}
@Inject
public void setPaymentCustomFieldService(final PaymentCustomFieldService paymentCustomFieldService) {
this.paymentCustomFieldService = paymentCustomFieldService;
}
@Inject
public void setPaymentService(final PaymentService paymentService) {
this.paymentService = paymentService;
}
@Inject
public void setTransactionFeeService(final TransactionFeeService transactionFeeService) {
this.transactionFeeService = transactionFeeService;
}
@Inject
public void setTransferTypeService(final TransferTypeService transferTypeService) {
this.transferTypeService = transferTypeService;
}
@Override
protected ActionForward handleSubmit(final ActionContext context) throws Exception {
final ConfirmLoanForm form = context.getForm();
final GrantLoanDTO dto = validateLoan(context);
// Check for the transaction password, if needed
if (context.isTransactionPasswordEnabled()) {
context.checkTransactionPassword(form.getTransactionPassword());
}
// Check which method we need to use to grant the loan
Loan loan;
try {
loan = loanService.grant(dto);
} catch (final CreditsException e) {
return context.sendError(actionHelper.resolveErrorKey(e), actionHelper.resolveParameters(e));
} catch (final AuthorizedPaymentInPastException e) {
return context.sendError("payment.error.authorizedInPast");
}
final boolean pending = loan.getTransfer().getProcessDate() == null;
context.sendMessage(pending ? "loan.awaitingAuthorization" : "loan.granted");
if (form.getMemberId() > 0) {
return ActionHelper.redirectWithParam(context.getRequest(), context.findForward("member"), "memberId", form.getMemberId());
} else {
return ActionHelper.redirectWithParam(context.getRequest(), context.findForward("loanGroup"), "loanGroupId", form.getLoanGroupId());
}
}
@Override
protected void prepareForm(final ActionContext context) throws Exception {
final GrantLoanDTO loan = validateLoan(context);
// Check for transaction password
final HttpServletRequest request = context.getRequest();
final boolean requestTransactionPassword = context.isTransactionPasswordEnabled();
if (requestTransactionPassword) {
context.validateTransactionPassword();
}
request.setAttribute("requestTransactionPassword", requestTransactionPassword);
// Fetch related data
final Member member = elementService.load(loan.getMember().getId(), Element.Relationships.USER);
final TransferType transferType = transferTypeService.load(loan.getTransferType().getId(), RelationshipHelper.nested(TransferType.Relationships.FROM, AccountType.Relationships.CURRENCY), TransferType.Relationships.TO);
final LoanGroup loanGroup = loan.getLoanGroup() == null ? null : loanGroupService.load(loan.getLoanGroup().getId(), LoanGroup.Relationships.MEMBERS);
final BigDecimal amount = loan.getAmount();
loan.setLoanGroup(loanGroup);
loan.setMember(member);
loan.setTransferType(transferType);
request.setAttribute("unitsPattern", transferType.getFrom().getCurrency().getPattern());
if (loanGroup != null) {
// Ensure the responsible is the first member shown and the list will be sorted
final List<Member> membersInGroup = new ArrayList<Member>(loanGroup.getMembers());
final LocalSettings localSettings = settingsService.getLocalSettings();
Collections.sort(membersInGroup, localSettings.getMemberComparator());
membersInGroup.remove(member);
membersInGroup.add(0, member);
request.setAttribute("membersInGroup", membersInGroup);
}
// Get the loan payments
List<LoanPayment> payments = null;
switch (loan.getLoanType()) {
case MULTI_PAYMENT:
payments = ((GrantMultiPaymentLoanDTO) loan).getPayments();
break;
case WITH_INTEREST:
final GrantLoanWithInterestDTO dto = (GrantLoanWithInterestDTO) loan;
final ProjectionDTO projection = new ProjectionDTO();
projection.setAmount(dto.getAmount());
projection.setDate(dto.getDate());
projection.setFirstExpirationDate(dto.getFirstRepaymentDate());
projection.setTransferType(dto.getTransferType());
projection.setPaymentCount(dto.getPaymentCount());
payments = loanService.calculatePaymentProjection(projection);
break;
}
request.setAttribute("payments", payments);
// Return the custom field values
final Collection<PaymentCustomFieldValue> customValues = loan.getCustomValues();
if (customValues != null) {
final List<PaymentCustomField> customFields = paymentCustomFieldService.list(transferType, false);
final Collection<Entry> entries = customFieldHelper.buildEntries(customFields, customValues);
// Load the value for enumerated and member values, since this collection was built from direct databinding with ids only
for (final Entry entry : entries) {
final CustomField field = entry.getField();
final CustomFieldValue fieldValue = entry.getValue();
if (field.getType() == CustomField.Type.ENUMERATED) {
final Long possibleValueId = CoercionHelper.coerce(Long.class, fieldValue.getValue());
if (possibleValueId != null) {
fieldValue.setPossibleValue(paymentCustomFieldService.loadPossibleValue(possibleValueId));
}
} else if (field.getType() == CustomField.Type.MEMBER) {
final Long memberId = CoercionHelper.coerce(Long.class, fieldValue.getValue());
if (memberId != null) {
final Element element = elementService.load(memberId);
if (element instanceof Member) {
fieldValue.setMemberValue((Member) element);
}
}
}
}
request.setAttribute("customFields", entries);
}
// Store the transaction fees
final TransactionFeePreviewDTO preview = transactionFeeService.preview(context.getAccountOwner(), member, transferType, amount);
request.setAttribute("finalAmount", preview.getFinalAmount());
request.setAttribute("fees", preview.getFees());
// Check if would require authorization
final DoPaymentDTO payment = new DoPaymentDTO();
payment.setTransferType(loan.getTransferType());
payment.setAmount(loan.getAmount());
payment.setTo(member);
request.setAttribute("wouldRequireAuthorization", paymentService.wouldRequireAuthorization(payment));
}
@Override
protected void validateForm(final ActionContext context) {
if (context.isTransactionPasswordEnabled()) {
final ConfirmLoanForm form = context.getForm();
if (StringUtils.isEmpty(form.getTransactionPassword())) {
throw new ValidationException("_transactionPassword", "login.transactionPassword", new RequiredError());
}
}
}
private GrantLoanDTO validateLoan(final ActionContext context) {
final GrantLoanDTO payment = (GrantLoanDTO) context.getSession().getAttribute("loan");
if (payment == null) {
throw new ValidationException();
}
return payment;
}
}