@Override
  protected QueryParameters prepareForm(final ActionContext context) {
    final SearchScheduledPaymentsForm form = context.getForm();
    final HttpServletRequest request = context.getRequest();

    final ScheduledPaymentQuery query = getDataBinder().readFromString(form.getQuery());
    query.fetch(
        ScheduledPayment.Relationships.TRANSFERS,
        RelationshipHelper.nested(
            ScheduledPayment.Relationships.FROM, MemberAccount.Relationships.MEMBER),
        RelationshipHelper.nested(
            ScheduledPayment.Relationships.TO, MemberAccount.Relationships.MEMBER));

    // Account owner
    AccountOwner owner = null;
    if (form.getMemberId() > 0) {
      owner = (Member) elementService.load(form.getMemberId());
      request.setAttribute("memberId", form.getMemberId());
    } else {
      // An admin or member or an operator searching his own scheduled payments
      owner = context.getAccountOwner();
    }
    query.setOwner(owner);

    List<? extends AccountType> accountTypes;
    if (context.isAdmin() && owner instanceof SystemAccountOwner) {
      final SystemAccountTypeQuery satq = new SystemAccountTypeQuery();
      accountTypes = accountTypeService.search(satq);
    } else {
      final MemberAccountTypeQuery matq = new MemberAccountTypeQuery();
      matq.setOwner((Member) owner);
      accountTypes = accountTypeService.search(matq);
    }
    request.setAttribute("accountTypes", accountTypes);

    if (query.getMember() != null) {
      final Member member = getFetchService().fetch(query.getMember(), Element.Relationships.USER);
      query.setMember(member);
    }
    if (query.getStatusList() == null) {
      query.setStatusGroup(ScheduledPaymentQuery.StatusGroup.OPEN);
      form.setQuery("statusGroup", ScheduledPaymentQuery.StatusGroup.OPEN);
    }

    RequestHelper.storeEnum(request, ScheduledPaymentQuery.SearchType.class, "searchTypes");
    RequestHelper.storeEnum(request, ScheduledPaymentQuery.StatusGroup.class, "statusGroups");
    request.setAttribute("accountOwner", owner);
    return query;
  }
  @Override
  protected ActionForward handleValidation(final ActionContext context) {
    try {
      final Invoice invoice = resolveInvoice(context);
      invoiceService.validate(invoice);

      // Retrive and fetch the destination account type
      AccountType accountType = invoice.getDestinationAccountType();
      if (accountType == null) {
        final TransferType tt =
            transferTypeService.load(
                invoice.getTransferType().getId(),
                RelationshipHelper.nested(
                    TransferType.Relationships.TO, AccountType.Relationships.CURRENCY));
        accountType = tt.getTo();
      } else {
        accountType = accountTypeService.load(accountType.getId());
      }

      // If the validation passed, resolve the confirmation message
      final LocalSettings localSettings = settingsService.getLocalSettings();
      final UnitsConverter unitsConverter =
          localSettings.getUnitsConverter(accountType.getCurrency().getPattern());

      final AccountOwner toOwner = invoice.getTo();
      final boolean toSystem = toOwner instanceof SystemAccountOwner;

      // Retrieve the message arguments
      String to;
      if (toSystem) {
        to = localSettings.getApplicationUsername();
      } else {
        final Member member = elementService.load(((Member) toOwner).getId());
        to = member.getName();
      }
      final String amount = unitsConverter.toString(invoice.getAmount());

      final String confirmationKey = "invoice.sendConfirmationMessage";

      final Map<String, Object> fields = new HashMap<String, Object>();
      fields.put("confirmationMessage", context.message(confirmationKey, to, amount));

      responseHelper.writeStatus(context.getResponse(), ResponseHelper.Status.SUCCESS, fields);

    } catch (final ValidationException e) {
      responseHelper.writeValidationErrors(context.getResponse(), e);
    }
    return null;
  }
 @Override
 @SuppressWarnings("unchecked")
 protected void renderContent(final ActionContext context) throws Exception {
   final SearchAccountTypesAjaxForm form = context.getForm();
   final MemberAccountTypeQuery query = getQueryBinder().readFromString(form);
   final List<MemberAccountType> accountTypes =
       (List<MemberAccountType>) accountTypeService.search(query);
   DataBinder<?> collectionBinder;
   if (form.isScheduling()) {
     final AccountTypesWithScheduling transformer =
         new AccountTypesWithScheduling(query.getCanPay(), query.getOwner());
     CollectionUtils.transform(accountTypes, transformer);
     collectionBinder = getAccountTypeBinderWithScheduling();
   } else {
     collectionBinder = getAccountTypeBinder();
   }
   final String json = collectionBinder.readAsString(accountTypes);
   responseHelper.writeJSON(context.getResponse(), json);
 }
  @Override
  protected ActionForward handleDisplay(final ActionContext context) throws Exception {
    final HttpServletRequest request = context.getRequest();
    final SendInvoiceForm form = context.getForm();
    final boolean toSystem = form.isToSystem();
    final boolean selectMember = form.isSelectMember();

    AccountOwner to;
    final Member fromMember =
        (form.getFrom() == null)
            ? null
            : (Member) elementService.load(Long.valueOf(form.getFrom()));
    final Element loggedElement = context.getElement();
    if (toSystem) {
      // System invoice
      to = SystemAccountOwner.instance();
    } else {
      if (!selectMember) {
        // Retrieve the member to send invoice for
        Member member = null;
        final Long memberId = IdConverter.instance().valueOf(form.getTo());
        if (memberId != null && memberId != loggedElement.getId()) {
          final Element element = elementService.load(memberId, Element.Relationships.USER);
          if (element instanceof Member) {
            member = (Member) element;
          }
        }
        if (member == null) {
          throw new ValidationException();
        }
        request.setAttribute("member", member);
        to = member;
      } else {
        // The member will be selected later
        to = null;
      }
    }

    // If we know who will receive the invoice, get the transfer types or dest account types
    if (to != null) {
      if (context.isAdmin() && fromMember == null) {
        // Only admins may select the transfer type
        final TransferTypeQuery query = new TransferTypeQuery();
        query.setChannel(Channel.WEB);
        query.setContext(TransactionContext.PAYMENT);
        query.setFromOwner(to);
        query.setToOwner(context.getAccountOwner());
        query.setUsePriority(true);
        request.setAttribute("transferTypes", transferTypeService.search(query));
      } else {
        // Members may select the destination account type
        final MemberAccountTypeQuery query = new MemberAccountTypeQuery();
        query.setOwner(fromMember == null ? (Member) loggedElement.getAccountOwner() : fromMember);
        query.setCanPay(to);
        final List<? extends AccountType> accountTypes = accountTypeService.search(query);
        if (accountTypes.isEmpty()) {
          return context.sendError("invoice.error.noAccountType");
        }
        request.setAttribute("accountTypes", accountTypes);
      }
    }

    // Resolve the possible currencies
    final MemberGroup group = getMemberGroup(context);
    final List<Currency> currencies;
    if (group != null) {
      currencies = currencyService.listByMemberGroup(group);
      final MemberAccountType defaultAccountType =
          accountTypeService.getDefault(group, AccountType.Relationships.CURRENCY);
      // Preselect the default currency
      if (defaultAccountType != null) {
        form.setCurrency(CoercionHelper.coerce(String.class, defaultAccountType.getCurrency()));
      }
    } else {
      currencies = currencyService.listAll();
    }
    request.setAttribute("currencies", currencies);

    if (currencies.isEmpty()) {
      // No currencies means no possible payment!!!
      throw new ValidationException("payment.error.noTransferType");
    } else if (currencies.size() == 1) {
      // Special case: There is a single currency. The JSP will use this object
      request.setAttribute("singleCurrency", currencies.get(0));
    }

    request.setAttribute("toSystem", toSystem);
    request.setAttribute("toMember", !toSystem);
    request.setAttribute("selectMember", selectMember);
    request.setAttribute("from", fromMember);

    final boolean useTransferType = context.isAdmin() && fromMember == null;
    request.setAttribute("useTransferType", useTransferType);

    // Check whether scheduled payments may be performed
    boolean allowsScheduling = false;
    boolean allowsMultipleScheduling = false;
    if (context.isAdmin() && fromMember == null) {
      allowsScheduling = true;
      allowsMultipleScheduling = true;
    } else {
      MemberGroup memberGroup;
      if (fromMember == null) {
        memberGroup = ((Member) context.getAccountOwner()).getMemberGroup();
      } else {
        memberGroup = fromMember.getMemberGroup();
      }
      final MemberGroupSettings memberSettings = memberGroup.getMemberSettings();
      allowsScheduling = memberSettings.isAllowsScheduledPayments();
      allowsMultipleScheduling = memberSettings.isAllowsMultipleScheduledPayments();
    }
    if (allowsScheduling) {
      request.setAttribute("allowsScheduling", allowsScheduling);
      request.setAttribute("allowsMultipleScheduling", allowsMultipleScheduling);
      final Collection<SchedulingType> schedulingTypes =
          EnumSet.of(SchedulingType.IMMEDIATELY, SchedulingType.SINGLE_FUTURE);
      if (allowsMultipleScheduling) {
        schedulingTypes.add(SchedulingType.MULTIPLE_FUTURE);
      }
      request.setAttribute("schedulingTypes", schedulingTypes);
      request.setAttribute(
          "schedulingFields",
          Arrays.asList(TimePeriod.Field.MONTHS, TimePeriod.Field.WEEKS, TimePeriod.Field.DAYS));
    }

    return context.getInputForward();
  }