@RequestMapping(value = "/events/check", method = POST)
 public ValidationResult validateEvent(
     @RequestBody EventModification eventModification, Errors errors) {
   ValidationResult base =
       validateEventHeader(Optional.<Event>empty(), eventModification, errors)
           .or(validateEventPrices(Optional.<Event>empty(), eventModification, errors))
           .or(
               eventModification
                   .getAdditionalServices()
                   .stream()
                   .map(as -> validateAdditionalService(as, eventModification, errors))
                   .reduce(ValidationResult::or)
                   .orElse(ValidationResult.success()));
   AtomicInteger counter = new AtomicInteger();
   return base.or(
           eventModification
               .getTicketCategories()
               .stream()
               .map(
                   c ->
                       validateCategory(
                           c, errors, "ticketCategories[" + counter.getAndIncrement() + "]."))
               .reduce(ValidationResult::or)
               .orElse(ValidationResult.success()))
       .or(validateAdditionalTicketFields(eventModification.getTicketFields(), errors));
 }
  @RequestMapping(value = "/events/{eventName}/pending-payments/bulk-confirmation", method = POST)
  public List<Triple<Boolean, String, String>> bulkConfirmation(
      @PathVariable("eventName") String eventName,
      Principal principal,
      @RequestBody UploadBase64FileModification file)
      throws IOException {

    try (InputStreamReader isr = new InputStreamReader(file.getInputStream());
        CSVReader reader = new CSVReader(isr)) {
      Event event = loadEvent(eventName, principal);
      return reader
          .readAll()
          .stream()
          .map(
              line -> {
                String reservationID = null;
                try {
                  Validate.isTrue(line.length >= 2);
                  reservationID = line[0];
                  ticketReservationManager.validateAndConfirmOfflinePayment(
                      reservationID, event, new BigDecimal(line[1]));
                  return Triple.of(Boolean.TRUE, reservationID, "");
                } catch (Exception e) {
                  return Triple.of(
                      Boolean.FALSE, Optional.ofNullable(reservationID).orElse(""), e.getMessage());
                }
              })
          .collect(Collectors.toList());
    }
  }
 @RequestMapping(
     value = "/events/{eventName}/pending-payments/{reservationId}/confirm",
     method = POST)
 public String confirmPayment(
     @PathVariable("eventName") String eventName,
     @PathVariable("reservationId") String reservationId,
     Principal principal,
     Model model,
     HttpServletRequest request) {
   ticketReservationManager.confirmOfflinePayment(loadEvent(eventName, principal), reservationId);
   ticketReservationManager
       .findById(reservationId)
       .filter(TicketReservation::isDirectAssignmentRequested)
       .ifPresent(
           reservation ->
               ticketHelper.directTicketAssignment(
                   eventName,
                   reservationId,
                   reservation.getEmail(),
                   reservation.getFullName(),
                   reservation.getFirstName(),
                   reservation.getLastName(),
                   reservation.getUserLanguage(),
                   Optional.empty(),
                   request,
                   model));
   return OK;
 }
 @RequestMapping(value = "/events/{id}/prices/update", method = POST)
 public ValidationResult updatePrices(
     @PathVariable("id") int id,
     @RequestBody EventModification eventModification,
     Errors errors,
     Principal principal) {
   Event event = eventManager.getSingleEventById(id, principal.getName());
   return validateEventPrices(Optional.of(event), eventModification, errors)
       .ifSuccess(
           () -> eventManager.updateEventPrices(event, eventModification, principal.getName()));
 }
  private ValidationResult validateAdditionalTicketFields(
      List<EventModification.AdditionalField> ticketFields, Errors errors) {
    // meh
    AtomicInteger cnt = new AtomicInteger();
    return Optional.ofNullable(ticketFields)
        .orElseGet(Collections::emptyList)
        .stream()
        .map(
            field -> {
              String prefix = "ticketFields[" + cnt.getAndIncrement() + "]";
              if (StringUtils.isBlank(field.getName())) {
                errors.rejectValue(prefix + ".name", "error.required");
              }
              // TODO: check label value is present for all the locales
              // TODO: for select check option value+label

              return Validator.evaluateValidationResult(errors);
            })
        .reduce(ValidationResult::or)
        .orElseGet(ValidationResult::success);
  }
 private Event loadEvent(String eventName, Principal principal) {
   Optional<Event> singleEvent =
       optionally(() -> eventManager.getSingleEvent(eventName, principal.getName()));
   Validate.isTrue(singleEvent.isPresent(), "event not found");
   return singleEvent.get();
 }
  @RequestMapping("/events/{eventName}/export.csv")
  public void downloadAllTicketsCSV(
      @PathVariable("eventName") String eventName,
      HttpServletRequest request,
      HttpServletResponse response,
      Principal principal)
      throws IOException {
    List<String> fields =
        Arrays.asList(
            Optional.ofNullable(request.getParameterValues("fields")).orElse(new String[] {}));
    Event event = loadEvent(eventName, principal);
    Map<Integer, TicketCategory> categoriesMap =
        eventManager
            .loadTicketCategories(event)
            .stream()
            .collect(Collectors.toMap(TicketCategory::getId, Function.identity()));
    ZoneId eventZoneId = event.getZoneId();

    Predicate<String> contains = FIXED_FIELDS::contains;

    response.setContentType("text/csv;charset=UTF-8");
    response.setHeader("Content-Disposition", "attachment; filename=" + eventName + "-export.csv");

    try (ServletOutputStream out = response.getOutputStream();
        CSVWriter writer = new CSVWriter(new OutputStreamWriter(out))) {

      for (int marker :
          BOM_MARKERS) { // UGLY-MODE_ON: specify that the file is written in UTF-8 with BOM, thanks
                         // to alexr http://stackoverflow.com/a/4192897
        out.write(marker);
      }

      writer.writeNext(fields.toArray(new String[fields.size()]));

      eventManager
          .findAllConfirmedTickets(eventName, principal.getName())
          .stream()
          .map(
              t -> {
                List<String> line = new ArrayList<>();
                if (fields.contains("ID")) {
                  line.add(t.getUuid());
                }
                if (fields.contains("creation")) {
                  line.add(t.getCreation().withZoneSameInstant(eventZoneId).toString());
                }
                if (fields.contains("category")) {
                  line.add(categoriesMap.get(t.getCategoryId()).getName());
                }
                if (fields.contains("event")) {
                  line.add(eventName);
                }
                if (fields.contains("status")) {
                  line.add(t.getStatus().toString());
                }
                if (fields.contains("originalPrice")) {
                  line.add(MonetaryUtil.centsToUnit(t.getSrcPriceCts()).toString());
                }
                if (fields.contains("paidPrice")) {
                  line.add(MonetaryUtil.centsToUnit(t.getFinalPriceCts()).toString());
                }
                if (fields.contains("discount")) {
                  line.add(MonetaryUtil.centsToUnit(t.getDiscountCts()).toString());
                }
                if (fields.contains("vat")) {
                  line.add(MonetaryUtil.centsToUnit(t.getVatCts()).toString());
                }
                if (fields.contains("reservationID")) {
                  line.add(t.getTicketsReservationId());
                }
                if (fields.contains("Full Name")) {
                  line.add(t.getFullName());
                }
                if (fields.contains("First Name")) {
                  line.add(t.getFirstName());
                }
                if (fields.contains("Last Name")) {
                  line.add(t.getLastName());
                }
                if (fields.contains("E-Mail")) {
                  line.add(t.getEmail());
                }
                if (fields.contains("locked")) {
                  line.add(String.valueOf(t.getLockedAssignment()));
                }
                if (fields.contains("Language")) {
                  line.add(String.valueOf(t.getUserLanguage()));
                }

                // obviously not optimized
                Map<String, String> additionalValues =
                    ticketFieldRepository.findAllValuesForTicketId(t.getId());

                fields
                    .stream()
                    .filter(contains.negate())
                    .forEachOrdered(
                        field -> {
                          line.add(additionalValues.getOrDefault(field, "").replaceAll("\"", ""));
                        });

                return line.toArray(new String[line.size()]);
              })
          .forEachOrdered(writer::writeNext);
      writer.flush();
      out.flush();
    }
  }