/* 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.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import nl.strohalm.cyclos.access.AdminMemberPermission; 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.SystemAccountType; import nl.strohalm.cyclos.entities.accounts.loans.Loan; import nl.strohalm.cyclos.entities.accounts.loans.LoanGroup; import nl.strohalm.cyclos.entities.accounts.loans.LoanGroupQuery; import nl.strohalm.cyclos.entities.accounts.loans.LoanParameters; import nl.strohalm.cyclos.entities.accounts.loans.LoanPayment; import nl.strohalm.cyclos.entities.accounts.transactions.TransferType; import nl.strohalm.cyclos.entities.accounts.transactions.TransferTypeQuery; import nl.strohalm.cyclos.entities.customization.fields.PaymentCustomField; import nl.strohalm.cyclos.entities.customization.fields.PaymentCustomFieldValue; import nl.strohalm.cyclos.entities.groups.AdminGroup; import nl.strohalm.cyclos.entities.groups.Group; 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.exceptions.PermissionDeniedException; import nl.strohalm.cyclos.services.loangroups.LoanGroupService; 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.GrantSinglePaymentLoanDTO; import nl.strohalm.cyclos.services.transactions.LoanService; import nl.strohalm.cyclos.services.transactions.TransactionContext; import nl.strohalm.cyclos.services.transfertypes.TransferTypeService; import nl.strohalm.cyclos.utils.ActionHelper; import nl.strohalm.cyclos.utils.RelationshipHelper; import nl.strohalm.cyclos.utils.binding.BeanBinder; import nl.strohalm.cyclos.utils.binding.BeanCollectionBinder; import nl.strohalm.cyclos.utils.binding.DataBinder; import nl.strohalm.cyclos.utils.binding.PropertyBinder; import nl.strohalm.cyclos.utils.conversion.CoercionHelper; import nl.strohalm.cyclos.utils.conversion.HtmlConverter; import nl.strohalm.cyclos.utils.conversion.ReferenceConverter; import nl.strohalm.cyclos.utils.validation.InvalidError; import nl.strohalm.cyclos.utils.validation.ValidationException; import org.apache.commons.collections.CollectionUtils; import org.apache.struts.action.ActionForward; /** * Action used to grant a loan to a given member or loan group * @author luis */ public class GrantLoanAction extends BaseFormAction { private LoanGroupService loanGroupService; private LoanService loanService; private TransferTypeService transferTypeService; @Inject public void setLoanGroupService(final LoanGroupService loanGroupService) { this.loanGroupService = loanGroupService; } @Inject public void setLoanService(final LoanService loanService) { this.loanService = loanService; } @Inject public void setTransferTypeService(final TransferTypeService transferTypeService) { this.transferTypeService = transferTypeService; } @Override protected ActionForward handleDisplay(final ActionContext context) throws Exception { final HttpServletRequest request = context.getRequest(); final GrantLoanForm form = context.getForm(); Member member = null; LoanGroup loanGroup = null; if (form.getMemberId() > 0L) { final Element element = elementService.load(form.getMemberId(), Element.Relationships.USER); if (element instanceof Member) { member = (Member) element; } } else if (form.getLoanGroupId() > 0L) { loanGroup = loanGroupService.load(form.getLoanGroupId(), LoanGroup.Relationships.MEMBERS); } if (member == null && loanGroup == null) { throw new ValidationException(); } request.setAttribute("member", member); request.setAttribute("loanGroup", loanGroup); if (member != null) { form.setLoan("member", member.getId()); AdminGroup adminGroup = context.getGroup(); adminGroup = groupService.load(adminGroup.getId(), AdminGroup.Relationships.VIEW_INFORMATION_OF, Group.Relationships.TRANSFER_TYPES); // Get the possible transfer types List<TransferType> transferTypes; final ArrayList<SystemAccountType> systemAccounts = new ArrayList<SystemAccountType>(adminGroup.getViewInformationOf()); if (CollectionUtils.isEmpty(systemAccounts)) { transferTypes = Collections.emptyList(); } else { final TransferTypeQuery ttQuery = new TransferTypeQuery(); ttQuery.setContext(TransactionContext.LOAN); ttQuery.setToOwner(member); ttQuery.setUsePriority(true); ttQuery.setFromAccountTypes(systemAccounts); transferTypes = transferTypeService.search(ttQuery); // Remove transfer types without permission final Collection<TransferType> transferTypesWithPermission = adminGroup.getTransferTypes(); for (final Iterator<TransferType> iter = transferTypes.iterator(); iter.hasNext();) { final TransferType transferType = iter.next(); if (!transferTypesWithPermission.contains(transferType)) { iter.remove(); } } } if (transferTypes.isEmpty()) { return context.sendError("loan.error.noTransferType"); } request.setAttribute("transferTypes", transferTypes); if (permissionService.hasPermission(AdminMemberPermission.LOAN_GROUPS_VIEW)) { // Get the loan groups of this member final LoanGroupQuery lgQuery = new LoanGroupQuery(); lgQuery.setMember(member); final List<LoanGroup> loanGroups = loanGroupService.search(lgQuery); request.setAttribute("loanGroups", loanGroups); } } else { form.setLoan("loanGroup", loanGroup.getId()); } return context.getInputForward(); } @Override protected ActionForward handleSubmit(final ActionContext context) throws Exception { final GrantLoanDTO dto = resolveDTO(context); final GrantLoanForm form = context.getForm(); context.getSession().setAttribute("loan", dto); final Map<String, Object> params = new HashMap<String, Object>(); params.put("memberId", form.getMemberId()); params.put("loanGroupId", form.getLoanGroupId()); return ActionHelper.redirectWithParams(context.getRequest(), context.getSuccessForward(), params); } @Override protected void validateForm(final ActionContext context) { final GrantLoanDTO dto = resolveDTO(context); loanService.validate(dto); } @SuppressWarnings({ "unchecked", "rawtypes" }) private DataBinder<GrantLoanDTO> getDataBinder(final Loan.Type type) { final LocalSettings settings = settingsService.getLocalSettings(); final BeanBinder<PaymentCustomFieldValue> customValueBinder = BeanBinder.instance(PaymentCustomFieldValue.class); customValueBinder.registerBinder("field", PropertyBinder.instance(PaymentCustomField.class, "field", ReferenceConverter.instance(PaymentCustomField.class))); customValueBinder.registerBinder("value", PropertyBinder.instance(String.class, "value", HtmlConverter.instance())); final BeanBinder binder = new BeanBinder(); binder.registerBinder("member", PropertyBinder.instance(Member.class, "member", ReferenceConverter.instance(Member.class))); binder.registerBinder("loanGroup", PropertyBinder.instance(LoanGroup.class, "loanGroup", ReferenceConverter.instance(LoanGroup.class))); binder.registerBinder("amount", PropertyBinder.instance(BigDecimal.class, "amount", settings.getNumberConverter())); binder.registerBinder("date", PropertyBinder.instance(Calendar.class, "date", settings.getRawDateConverter())); binder.registerBinder("description", PropertyBinder.instance(String.class, "description")); binder.registerBinder("transferType", PropertyBinder.instance(TransferType.class, "transferType", ReferenceConverter.instance(TransferType.class))); binder.registerBinder("customValues", BeanCollectionBinder.instance(customValueBinder, "customValues")); switch (type) { case SINGLE_PAYMENT: binder.setType(GrantSinglePaymentLoanDTO.class); binder.registerBinder("repaymentDate", PropertyBinder.instance(Calendar.class, "repaymentDate", settings.getRawDateConverter())); break; case MULTI_PAYMENT: binder.setType(GrantMultiPaymentLoanDTO.class); final BeanBinder<LoanPayment> paymentBinder = BeanBinder.instance(LoanPayment.class); paymentBinder.registerBinder("expirationDate", PropertyBinder.instance(Calendar.class, "expirationDate", settings.getRawDateConverter())); paymentBinder.registerBinder("amount", PropertyBinder.instance(BigDecimal.class, "amount", settings.getNumberConverter())); binder.registerBinder("payments", BeanCollectionBinder.instance(paymentBinder, "payments")); break; case WITH_INTEREST: binder.setType(GrantLoanWithInterestDTO.class); binder.registerBinder("firstRepaymentDate", PropertyBinder.instance(Calendar.class, "firstRepaymentDate", settings.getRawDateConverter())); binder.registerBinder("paymentCount", PropertyBinder.instance(Integer.TYPE, "paymentCount")); break; default: throw new IllegalArgumentException("Invalid loan type: " + type); } return binder; } /** * Resolve the GrantLoanDTO */ private GrantLoanDTO resolveDTO(final ActionContext context) { final GrantLoanForm form = context.getForm(); final long transferTypeId = CoercionHelper.coerce(Long.TYPE, form.getLoan("transferType")); if (transferTypeId <= 0L) { throw new ValidationException(); } final TransferType transferType = transferTypeService.load(transferTypeId, RelationshipHelper.nested(TransferType.Relationships.TO, AccountType.Relationships.CURRENCY)); final LoanParameters loanParameters = transferType.getLoan(); if (loanParameters == null || loanParameters.getType() == null) { throw new ValidationException("transferType", "transfer.type", new InvalidError()); } final GrantLoanDTO dto = getDataBinder(loanParameters.getType()).readFromString(form.getLoan()); dto.setTransferType(transferType); if (dto.getLoanGroup() != null && !permissionService.hasPermission(AdminMemberPermission.LOAN_GROUPS_VIEW)) { throw new PermissionDeniedException(); } return dto; } }