use of alfio.controller.api.v2.model.TicketCategory in project alf.io by alfio-event.
the class BaseReservationFlowTest method testBasicFlow.
protected void testBasicFlow(Supplier<ReservationFlowContext> contextSupplier) throws Exception {
// as soon as the test starts, insert the extension in the database (prepare the environment)
try (var extensionInputStream = requireNonNull(getClass().getResourceAsStream("/extension.js"))) {
List<String> extensionStream = IOUtils.readLines(new InputStreamReader(extensionInputStream, StandardCharsets.UTF_8));
String concatenation = String.join("\n", extensionStream).replace("EVENTS", Arrays.stream(ExtensionEvent.values()).map(ee -> "'" + ee.name() + "'").collect(Collectors.joining(",")));
extensionService.createOrUpdate(null, null, new Extension("-", "syncName", concatenation.replace("placeHolder", "false"), true));
extensionService.createOrUpdate(null, null, new Extension("-", "asyncName", concatenation.replace("placeHolder", "true"), true));
}
List<BasicEventInfo> body = eventApiV2Controller.listEvents(SearchOptions.empty()).getBody();
assertNotNull(body);
assertTrue(body.isEmpty());
cleanupExtensionLog();
var context = contextSupplier.get();
ensureConfiguration(context);
// check if EVENT_CREATED was logged
List<ExtensionLog> extLogs = extensionLogRepository.getPage(null, null, null, 100, 0);
assertEventLogged(extLogs, EVENT_METADATA_UPDATE, 8);
assertEventLogged(extLogs, EVENT_CREATED, 8);
{
Principal p = Mockito.mock(Principal.class);
Mockito.when(p.getName()).thenReturn(context.userId);
assertTrue(usersApiController.getAllOrganizations(p).stream().anyMatch(o -> context.event.getOrganizationId() == o.getId()));
assertEquals(context.event.getOrganizationId(), usersApiController.getOrganization(context.event.getOrganizationId(), p).getId());
}
//
assertEquals(ContentLanguage.ALL_LANGUAGES.size(), translationsApiController.getSupportedLanguages().size());
assertEquals("or", translationsApiController.getPublicTranslations("en", true).get("common.or"));
assertEquals("o", translationsApiController.getPublicTranslations("it", true).get("common.or"));
assertEquals("oder", translationsApiController.getPublicTranslations("de", true).get("common.or"));
// check all public translations
ContentLanguage.ALL_LANGUAGES.forEach(cl -> assertFalse(translationsApiController.getPublicTranslations(cl.getLanguage(), true).isEmpty()));
var alfioInfo = infoApiController.getInfo(new MockHttpSession());
assertFalse(alfioInfo.isDemoModeEnabled());
assertTrue(alfioInfo.isDevModeEnabled());
assertFalse(alfioInfo.isProdModeEnabled());
assertTrue(alfioInfo.getAnalyticsConfiguration().isGoogleAnalyticsScrambledInfo());
assertNull(alfioInfo.getAnalyticsConfiguration().getGoogleAnalyticsKey());
assertNull(alfioInfo.getAnalyticsConfiguration().getClientId());
//
assertEquals("Switzerland", translationsApiController.getCountries("en").stream().filter(c -> "CH".equals(c.getIsoCode())).findFirst().get().getName());
assertEquals("Greece", translationsApiController.getCountries("en").stream().filter(c -> "GR".equals(c.getIsoCode())).findFirst().get().getName());
assertEquals("Suisse", translationsApiController.getCountries("fr").stream().filter(c -> "CH".equals(c.getIsoCode())).findFirst().get().getName());
assertEquals("Svizzera", translationsApiController.getCountries("it").stream().filter(c -> "CH".equals(c.getIsoCode())).findFirst().get().getName());
assertEquals("Schweiz", translationsApiController.getCountries("de").stream().filter(c -> "CH".equals(c.getIsoCode())).findFirst().get().getName());
// EL -> greece for vat
assertEquals("Greece", translationsApiController.getCountriesForVat("en").stream().filter(c -> "EL".equals(c.getIsoCode())).findFirst().get().getName());
//
assertEquals(27, translationsApiController.getEuCountriesForVat("en").size());
//
assertTrue(requireNonNull(eventApiV2Controller.listEvents(SearchOptions.empty()).getBody()).isEmpty());
//
List<EventStatistic> eventStatistic = eventStatisticsManager.getAllEventsWithStatistics(context.userId);
assertEquals(1, eventStatistic.size());
var statisticsFrom = ZonedDateTime.now(context.event.getZoneId()).minusYears(1);
var statisticsTo = ZonedDateTime.now(context.event.getZoneId()).plusDays(1);
assertEquals(0L, eventStatisticsManager.getTicketSoldStatistics(context.event.getId(), statisticsFrom, statisticsTo, "day").stream().mapToLong(TicketsByDateStatistic::getCount).sum());
EventWithAdditionalInfo eventWithAdditionalInfo = eventStatisticsManager.getEventWithAdditionalInfo(context.event.getShortName(), context.userId);
// <- 2 tickets are the bounded category
assertEquals(2, eventWithAdditionalInfo.getNotSoldTickets());
assertEquals(0, eventWithAdditionalInfo.getSoldTickets());
assertEquals(20, eventWithAdditionalInfo.getAvailableSeats());
//
// publish the event
eventManager.toggleActiveFlag(context.event.getId(), context.userId, true);
//
var resListEvents = eventApiV2Controller.listEvents(SearchOptions.empty());
var events = resListEvents.getBody();
assertEquals(HttpStatus.OK, resListEvents.getStatusCode());
assertNotNull(events);
assertEquals(1, events.size());
assertEquals(context.event.getShortName(), events.get(0).getShortName());
//
assertEquals(HttpStatus.NOT_FOUND, eventApiV2Controller.getEvent("NOT_EXISTS", new MockHttpSession()).getStatusCode());
//
var eventRes = eventApiV2Controller.getEvent(context.event.getShortName(), new MockHttpSession());
assertEquals(HttpStatus.OK, eventRes.getStatusCode());
var selectedEvent = eventRes.getBody();
assertNotNull(selectedEvent);
assertEquals("CHF", selectedEvent.getCurrency());
assertFalse(selectedEvent.isFree());
assertEquals(context.event.getSameDay(), selectedEvent.isSameDay());
assertTrue(selectedEvent.isVatIncluded());
assertEquals(context.event.getShortName(), selectedEvent.getShortName());
assertEquals(context.event.getDisplayName(), selectedEvent.getDisplayName());
assertEquals(context.event.getFileBlobId(), selectedEvent.getFileBlobId());
assertTrue(selectedEvent.getI18nOverride().isEmpty());
configurationRepository.insert("TRANSLATION_OVERRIDE", Json.toJson(Map.of("en", Map.of("show-context.event.tickets.left", "{0} left!"))), "");
configurationRepository.insertEventLevel(context.event.getOrganizationId(), context.event.getId(), "TRANSLATION_OVERRIDE", Json.toJson(Map.of("en", Map.of("common.vat", "context.event.vat"))), "");
eventRes = eventApiV2Controller.getEvent(context.event.getShortName(), new MockHttpSession());
selectedEvent = eventRes.getBody();
assertNotNull(selectedEvent);
assertFalse(selectedEvent.getI18nOverride().isEmpty());
assertEquals("context.event.vat", selectedEvent.getI18nOverride().get("en").get("common.vat"));
assertEquals("{{0}} left!", selectedEvent.getI18nOverride().get("en").get("show-context.event.tickets.left"));
checkCalendar(context.event.getShortName());
// it, en, de
assertEquals(3, selectedEvent.getContentLanguages().size());
assertEquals(selectedEvent.getContentLanguages().stream().map(Language::getLocale).collect(Collectors.toSet()), Set.of("it", "en", "de"));
// check if for each language we have the expected locale dependent entries
for (String lang : Arrays.asList("it", "en", "de")) {
assertNotNull(selectedEvent.getDescription().get(lang));
//
assertNotNull(selectedEvent.getFormattedBeginDate().get(lang));
assertNotNull(selectedEvent.getFormattedBeginTime().get(lang));
assertNotNull(selectedEvent.getFormattedEndDate().get(lang));
assertNotNull(selectedEvent.getFormattedEndTime().get(lang));
}
assertEquals("redirect:/api/v2/public/event/" + context.event.getShortName() + "/code/MY_CODE", indexController.redirectCode(context.event.getShortName(), "MY_CODE"));
// check open graph & co
{
var res = new MockHttpServletResponse();
indexController.replyToIndex(context.event.getShortName(), null, "not a social share", "en", new ServletWebRequest(new MockHttpServletRequest()), res, new MockHttpSession());
var htmlParser = new Parser();
var docWithoutOpenGraph = htmlParser.parse(new String(res.getContentAsByteArray(), StandardCharsets.UTF_8));
assertTrue(docWithoutOpenGraph.getAllNodesMatching(Selector.select().element("meta").attrValEq("name", "twitter:card").toMatcher()).isEmpty());
res = new MockHttpServletResponse();
indexController.replyToIndex(context.event.getShortName(), null, "Twitterbot/42", "en", new ServletWebRequest(new MockHttpServletRequest()), res, new MockHttpSession());
var docWithOpenGraph = htmlParser.parse(new String(res.getContentAsByteArray(), StandardCharsets.UTF_8));
assertFalse(docWithOpenGraph.getAllNodesMatching(Selector.select().element("meta").attrValEq("name", "twitter:card").toMatcher()).isEmpty());
var title = (Element) docWithOpenGraph.getAllNodesMatching(Selector.select().element("meta").attrValEq("property", "og:title").toMatcher(), true).get(0);
assertEquals("Get your tickets for " + context.event.getDisplayName(), title.getAttribute("content"));
}
//
// check ticket & all, we have 2 ticket categories, 1 hidden
assertEquals(HttpStatus.NOT_FOUND, eventApiV2Controller.getTicketCategories("NOT_EXISTING", null).getStatusCode());
{
var itemsRes = eventApiV2Controller.getTicketCategories(context.event.getShortName(), null);
assertEquals(HttpStatus.OK, itemsRes.getStatusCode());
var items = itemsRes.getBody();
assertNotNull(items);
assertEquals(1, items.getTicketCategories().size());
var visibleCat = items.getTicketCategories().get(0);
assertEquals("default", visibleCat.getName());
assertEquals("10.00", visibleCat.getFormattedFinalPrice());
assertFalse(visibleCat.isHasDiscount());
assertEquals(1, items.getAdditionalServices().size());
var additionalItem = items.getAdditionalServices().get(0);
assertEquals("40.00", additionalItem.getFormattedFinalPrice());
assertEquals("1.00", additionalItem.getVatPercentage());
// TODO: check: if there are missing lang, we should at least copy them (?)
assertEquals(1, additionalItem.getTitle().size());
assertEquals(1, additionalItem.getDescription().size());
assertEquals("additional title", additionalItem.getTitle().get("en"));
assertEquals("<p>additional desc</p>\n", additionalItem.getDescription().get("en"));
// check presence of reservation list
assertFalse(items.isWaitingList());
assertFalse(items.isPreSales());
//
// fix dates to enable reservation list
var tc = ticketCategoryRepository.getById(visibleCat.getId());
ticketCategoryRepository.fixDates(visibleCat.getId(), tc.getInception(context.event.getZoneId()).plusDays(2), tc.getExpiration(context.event.getZoneId()));
//
items = eventApiV2Controller.getTicketCategories(context.event.getShortName(), null).getBody();
assertNotNull(items);
assertTrue(items.isWaitingList());
assertTrue(items.isPreSales());
//
var subForm = new WaitingQueueSubscriptionForm();
subForm.setFirstName("first");
subForm.setLastName("last");
subForm.setPrivacyPolicyAccepted(true);
subForm.setTermAndConditionsAccepted(true);
subForm.setUserLanguage(Locale.ENGLISH);
var subRes = eventApiV2Controller.subscribeToWaitingList(context.event.getShortName(), subForm, new BeanPropertyBindingResult(subForm, "subForm"));
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, subRes.getStatusCode());
assertNotNull(subRes.getBody());
assertFalse(subRes.getBody().isSuccess());
assertEquals(1, subRes.getBody().getValidationErrors().size());
assertEquals("email", subRes.getBody().getValidationErrors().get(0).getFieldName());
assertEquals("error.email", subRes.getBody().getValidationErrors().get(0).getCode());
//
subForm.setEmail("email@email.com");
subRes = eventApiV2Controller.subscribeToWaitingList(context.event.getShortName(), subForm, new BeanPropertyBindingResult(subForm, "subForm"));
assertEquals(HttpStatus.OK, subRes.getStatusCode());
assertNotNull(subRes.getBody());
assertTrue(subRes.getBody().isSuccess());
assertEquals(0, subRes.getBody().getErrorCount());
assertTrue(subRes.getBody().getValue());
//
ticketCategoryRepository.fixDates(visibleCat.getId(), tc.getInception(context.event.getZoneId()).minusDays(2), tc.getExpiration(context.event.getZoneId()));
}
// dynamic promo codes can be applied only automatically
{
eventManager.addPromoCode("DYNAMIC_CODE", context.event.getId(), null, ZonedDateTime.now(clockProvider.getClock()).minusDays(2), context.event.getEnd().plusDays(2), 10, PromoCodeDiscount.DiscountType.PERCENTAGE, null, 3, "description", "test@test.ch", PromoCodeDiscount.CodeType.DYNAMIC, null);
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, eventApiV2Controller.validateCode(context.event.getShortName(), "DYNAMIC_CODE").getStatusCode());
// try to enter it anyway
var form = new ReservationForm();
var ticketReservation = new TicketReservationModification();
form.setPromoCode("DYNAMIC_CODE");
ticketReservation.setQuantity(1);
ticketReservation.setTicketCategoryId(eventApiV2Controller.getTicketCategories(context.event.getShortName(), null).getBody().getTicketCategories().get(0).getId());
form.setReservation(Collections.singletonList(ticketReservation));
var res = eventApiV2Controller.reserveTickets(context.event.getShortName(), "en", form, new BeanPropertyBindingResult(form, "reservation"), new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), null);
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, res.getStatusCode());
}
// hidden category check
{
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, eventApiV2Controller.validateCode(context.event.getShortName(), "NOT_EXISTING").getStatusCode());
var hiddenCodeRes = eventApiV2Controller.validateCode(context.event.getShortName(), HIDDEN_CODE);
assertEquals(HttpStatus.OK, hiddenCodeRes.getStatusCode());
var hiddenCode = hiddenCodeRes.getBody();
assertNotNull(hiddenCode);
assertEquals(EventCode.EventCodeType.ACCESS, hiddenCode.getValue().getType());
var itemsRes2 = eventApiV2Controller.getTicketCategories(context.event.getShortName(), HIDDEN_CODE);
var items2 = itemsRes2.getBody();
assertNotNull(items2);
assertEquals(2, items2.getTicketCategories().size());
var hiddenCat = items2.getTicketCategories().stream().filter(t -> t.isAccessRestricted()).findFirst().get();
assertEquals(hiddenCategoryId, hiddenCat.getId());
assertEquals("hidden", hiddenCat.getName());
assertEquals("1.00", hiddenCat.getFormattedFinalPrice());
assertFalse(hiddenCat.isHasDiscount());
assertTrue(hiddenCat.isAccessRestricted());
// do a reservation for a hidden category+cancel
var form = new ReservationForm();
var ticketReservation = new TicketReservationModification();
form.setPromoCode(HIDDEN_CODE);
ticketReservation.setQuantity(1);
ticketReservation.setTicketCategoryId(hiddenCat.getId());
form.setReservation(Collections.singletonList(ticketReservation));
var res = eventApiV2Controller.reserveTickets(context.event.getShortName(), "en", form, new BeanPropertyBindingResult(form, "reservation"), new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
assertEquals(HttpStatus.OK, res.getStatusCode());
assertNotNull(res.getBody());
var reservationInfo = reservationApiV2Controller.getReservationInfo(res.getBody().getValue(), context.getPublicUser());
assertEquals(HttpStatus.OK, reservationInfo.getStatusCode());
assertNotNull(reservationInfo.getBody());
assertEquals("1.00", reservationInfo.getBody().getOrderSummary().getTotalPrice());
assertEquals("hidden", reservationInfo.getBody().getOrderSummary().getSummary().get(0).getName());
var activePaymentMethods = reservationInfo.getBody().getActivePaymentMethods();
assertFalse(activePaymentMethods.isEmpty());
assertTrue(activePaymentMethods.containsKey(PaymentMethod.BANK_TRANSFER));
configurationRepository.insertTicketCategoryLevel(context.event.getOrganizationId(), context.event.getId(), hiddenCategoryId, ConfigurationKeys.PAYMENT_METHODS_BLACKLIST.name(), PaymentProxy.OFFLINE.name(), "");
reservationInfo = reservationApiV2Controller.getReservationInfo(res.getBody().getValue(), context.getPublicUser());
assertNotNull(reservationInfo.getBody());
activePaymentMethods = reservationInfo.getBody().getActivePaymentMethods();
assertTrue(activePaymentMethods.isEmpty());
configurationRepository.deleteCategoryLevelByKey(ConfigurationKeys.PAYMENT_METHODS_BLACKLIST.name(), context.event.getId(), hiddenCategoryId);
// clear the extension_log table so that we can check the very next additions
// cannot have just one row in the log, every event adds EXACTLY two logs
// log expected: RESERVATION_CANCELLED
cleanupExtensionLog();
reservationApiV2Controller.cancelPendingReservation(res.getBody().getValue());
extLogs = extensionLogRepository.getPage(null, null, null, 100, 0);
assertEventLogged(extLogs, RESERVATION_CANCELLED, 2);
assertEquals(0, jdbcTemplate.queryForObject("select count(*) from ticket where status = 'FREE' and final_price_cts > 0", Map.of(), Integer.class));
// this is run by a job, but given the fact that it's in another separate transaction, it cannot work in this test (WaitingQueueSubscriptionProcessor.handleWaitingTickets)
assertEquals(1, ticketReservationManager.revertTicketsToFreeIfAccessRestricted(context.event.getId()));
}
//
// check reservation auto creation with code: TODO: will need to check all the flows
{
// code not found
var notFoundRes = eventApiV2Controller.handleCode(context.event.getShortName(), "NOT_EXIST", new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
assertEquals("/event/" + context.event.getShortName(), notFoundRes.getHeaders().getLocation().getPath());
assertEquals("errors=error.STEP_1_CODE_NOT_FOUND", notFoundRes.getHeaders().getLocation().getQuery());
//
// promo code, we expect a redirect to event with the code in the query string
var redirectPromoCodeRes = eventApiV2Controller.handleCode(context.event.getShortName(), PROMO_CODE, new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
assertEquals("/event/" + context.event.getShortName(), redirectPromoCodeRes.getHeaders().getLocation().getPath());
assertEquals("code=MYPROMOCODE", redirectPromoCodeRes.getHeaders().getLocation().getQuery());
// code existing
assertEquals(2, specialPriceRepository.countFreeTokens(hiddenCategoryId).intValue());
var res = eventApiV2Controller.handleCode(context.event.getShortName(), URL_CODE_HIDDEN, new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
var location = requireNonNull(res.getHeaders().getLocation()).toString();
var reservationId = location.substring(("/event/" + context.event.getShortName() + "/reservation/").length(), location.length() - "/book".length());
var reservationInfo = reservationApiV2Controller.getReservationInfo(reservationId, context.getPublicUser());
assertEquals(HttpStatus.OK, reservationInfo.getStatusCode());
assertNotNull(reservationInfo.getBody());
assertEquals(reservationId, reservationInfo.getBody().getId());
assertEquals(1, specialPriceRepository.countFreeTokens(hiddenCategoryId).intValue());
reservationApiV2Controller.cancelPendingReservation(reservationId);
assertEquals(2, specialPriceRepository.countFreeTokens(hiddenCategoryId).intValue());
// this is run by a job, but given the fact that it's in another separate transaction, it cannot work in this test (WaitingQueueSubscriptionProcessor.handleWaitingTickets)
assertEquals(1, ticketReservationManager.revertTicketsToFreeIfAccessRestricted(context.event.getId()));
}
// check reservation auto creation with deletion from the admin side
{
assertEquals(2, specialPriceRepository.countFreeTokens(hiddenCategoryId).intValue());
var res = eventApiV2Controller.handleCode(context.event.getShortName(), URL_CODE_HIDDEN, new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
var location = requireNonNull(res.getHeaders().getLocation()).toString();
var reservationId = location.substring(("/event/" + context.event.getShortName() + "/reservation/").length(), location.length() - "/book".length());
var reservationInfo = reservationApiV2Controller.getReservationInfo(reservationId, context.getPublicUser());
assertEquals(HttpStatus.OK, reservationInfo.getStatusCode());
assertNotNull(reservationInfo.getBody());
assertEquals(reservationId, reservationInfo.getBody().getId());
assertEquals(1, reservationInfo.getBody().getActivePaymentMethods().size());
assertTrue(reservationInfo.getBody().getActivePaymentMethods().containsKey(PaymentMethod.BANK_TRANSFER));
assertEquals(1, specialPriceRepository.countFreeTokens(hiddenCategoryId).intValue());
adminReservationManager.removeReservation(PurchaseContextType.event, context.event.getShortName(), reservationId, false, false, false, context.userId);
assertEquals(2, specialPriceRepository.countFreeTokens(hiddenCategoryId).intValue());
// this is run by a job, but given the fact that it's in another separate transaction, it cannot work in this test (WaitingQueueSubscriptionProcessor.handleWaitingTickets)
assertEquals(1, ticketReservationManager.revertTicketsToFreeIfAccessRestricted(context.event.getId()));
}
// discount check
{
var discountCodeRes = eventApiV2Controller.validateCode(context.event.getShortName(), PROMO_CODE);
var discountCode = discountCodeRes.getBody();
assertNotNull(discountCode);
assertEquals(EventCode.EventCodeType.DISCOUNT, discountCode.getValue().getType());
var itemsRes3 = eventApiV2Controller.getTicketCategories(context.event.getShortName(), PROMO_CODE);
var items3 = itemsRes3.getBody();
assertNotNull(items3);
assertEquals(1, items3.getTicketCategories().size());
var visibleCat = items3.getTicketCategories().get(0);
assertEquals("default", visibleCat.getName());
assertEquals("10.00", visibleCat.getFormattedFinalPrice());
assertTrue(visibleCat.isHasDiscount());
assertEquals("9.00", visibleCat.getFormattedDiscountedPrice());
}
// validation error: select at least one
{
var form = new ReservationForm();
var res = eventApiV2Controller.reserveTickets(context.event.getShortName(), "en", form, new BeanPropertyBindingResult(form, "reservation"), new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, res.getStatusCode());
var resBody = res.getBody();
assertNotNull(resBody);
assertFalse(resBody.isSuccess());
assertEquals(1, resBody.getErrorCount());
}
// cancel a reservation
{
var form = new ReservationForm();
var ticketReservation = new TicketReservationModification();
ticketReservation.setQuantity(1);
ticketReservation.setTicketCategoryId(eventApiV2Controller.getTicketCategories(context.event.getShortName(), null).getBody().getTicketCategories().get(0).getId());
form.setReservation(Collections.singletonList(ticketReservation));
var res = eventApiV2Controller.reserveTickets(context.event.getShortName(), "en", form, new BeanPropertyBindingResult(form, "reservation"), new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
assertEquals(HttpStatus.OK, res.getStatusCode());
var resBody = res.getBody();
assertNotNull(resBody);
assertTrue(resBody.isSuccess());
assertEquals(0, resBody.getErrorCount());
var reservationId = resBody.getValue();
checkStatus(reservationId, HttpStatus.OK, false, TicketReservation.TicketReservationStatus.PENDING, context);
var cancelRes = reservationApiV2Controller.cancelPendingReservation(reservationId);
assertEquals(HttpStatus.OK, cancelRes.getStatusCode());
checkStatus(reservationId, HttpStatus.NOT_FOUND, null, null, context);
}
// check blacklist payment methods
{
var form = new ReservationForm();
var categories = eventApiV2Controller.getTicketCategories(context.event.getShortName(), HIDDEN_CODE).getBody().getTicketCategories();
var c1 = new TicketReservationModification();
c1.setQuantity(1);
int firstCategoryId = categories.get(0).getId();
c1.setTicketCategoryId(firstCategoryId);
var c2 = new TicketReservationModification();
c2.setQuantity(1);
c2.setTicketCategoryId(categories.get(1).getId());
form.setReservation(List.of(c1, c2));
form.setPromoCode(HIDDEN_CODE);
var res = eventApiV2Controller.reserveTickets(context.event.getShortName(), "en", form, new BeanPropertyBindingResult(form, "reservation"), new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
assertEquals(HttpStatus.OK, res.getStatusCode());
var resBody = res.getBody();
assertNotNull(resBody);
assertTrue(resBody.isSuccess());
assertEquals(0, resBody.getErrorCount());
var reservationId = resBody.getValue();
checkStatus(reservationId, HttpStatus.OK, false, TicketReservation.TicketReservationStatus.PENDING, context);
var cancelRes = reservationApiV2Controller.cancelPendingReservation(reservationId);
assertEquals(HttpStatus.OK, cancelRes.getStatusCode());
checkStatus(reservationId, HttpStatus.NOT_FOUND, null, null, context);
}
// buy 2 ticket, with additional service + field
{
var form = new ReservationForm();
var ticketReservation = new TicketReservationModification();
ticketReservation.setQuantity(2);
ticketReservation.setTicketCategoryId(eventApiV2Controller.getTicketCategories(context.event.getShortName(), null).getBody().getTicketCategories().get(0).getId());
form.setReservation(Collections.singletonList(ticketReservation));
var additionalService = new AdditionalServiceReservationModification();
additionalService.setAdditionalServiceId(additionalServiceId);
additionalService.setQuantity(1);
form.setAdditionalService(Collections.singletonList(additionalService));
var res = eventApiV2Controller.reserveTickets(context.event.getShortName(), "en", form, new BeanPropertyBindingResult(form, "reservation"), new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
assertEquals(HttpStatus.OK, res.getStatusCode());
var resBody = res.getBody();
assertNotNull(resBody);
assertTrue(resBody.isSuccess());
assertEquals(0, resBody.getErrorCount());
var reservationId = resBody.getValue();
checkStatus(reservationId, HttpStatus.OK, false, TicketReservation.TicketReservationStatus.PENDING, context);
var resInfoRes = reservationApiV2Controller.getReservationInfo(reservationId, context.getPublicUser());
assertEquals(HttpStatus.OK, resInfoRes.getStatusCode());
assertNotNull(resInfoRes.getBody());
var ticketsByCat = resInfoRes.getBody().getTicketsByCategory();
assertEquals(1, ticketsByCat.size());
assertEquals(2, ticketsByCat.get(0).getTickets().size());
var ticket1 = ticketsByCat.get(0).getTickets().get(0);
// 1
assertEquals(1, ticket1.getTicketFieldConfigurationBeforeStandard().size());
// 1 + 1 additional service related field (appear only on first ticket)
assertEquals(2, ticket1.getTicketFieldConfigurationAfterStandard().size());
var ticket2 = ticketsByCat.get(0).getTickets().get(1);
// 1
assertEquals(1, ticket2.getTicketFieldConfigurationBeforeStandard().size());
// 1
assertEquals(1, ticket2.getTicketFieldConfigurationAfterStandard().size());
var contactForm = new ContactAndTicketsForm();
contactForm.setAddCompanyBillingDetails(true);
contactForm.setSkipVatNr(false);
contactForm.setInvoiceRequested(true);
contactForm.setEmail("test@test.com");
contactForm.setBillingAddress("my billing address");
contactForm.setFirstName("full");
contactForm.setLastName("name");
var ticketForm1 = new UpdateTicketOwnerForm();
ticketForm1.setFirstName("ticketfull");
ticketForm1.setLastName("ticketname");
ticketForm1.setEmail("tickettest@test.com");
ticketForm1.setAdditional(new HashMap<>(Map.of("field1", Collections.singletonList("value"))));
var ticketForm2 = new UpdateTicketOwnerForm();
ticketForm2.setFirstName("ticketfull");
ticketForm2.setLastName("ticketname");
ticketForm2.setEmail("tickettest@test.com");
ticketForm2.setAdditional(Map.of("field1", Collections.singletonList("value")));
contactForm.setTickets(Map.of(ticket1.getUuid(), ticketForm1, ticket2.getUuid(), ticketForm2));
var failure = reservationApiV2Controller.validateToOverview(reservationId, "en", false, contactForm, new BeanPropertyBindingResult(contactForm, "paymentForm"), context.getPublicAuthentication());
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, failure.getStatusCode());
assertNotNull(failure.getBody());
// <- missing mandatory
assertEquals(1, failure.getBody().getValidationErrors().stream().filter(f -> f.getFieldName().equals("tickets[" + ticket1.getUuid() + "].additional[field3][0]")).count());
// check billing errors
// <- missing mandatory
assertEquals(1, failure.getBody().getValidationErrors().stream().filter(f -> f.getFieldName().equals("billingAddressLine1")).count());
// <- missing mandatory
assertEquals(1, failure.getBody().getValidationErrors().stream().filter(f -> f.getFieldName().equals("billingAddressZip")).count());
// <- missing mandatory
assertEquals(1, failure.getBody().getValidationErrors().stream().filter(f -> f.getFieldName().equals("billingAddressCity")).count());
// <- missing mandatory
assertEquals(1, failure.getBody().getValidationErrors().stream().filter(f -> f.getFieldName().equals("vatCountryCode")).count());
//
contactForm.setVatCountryCode("CH");
contactForm.setBillingAddressLine1("LINE 1");
contactForm.setBillingAddressCity("CITY");
contactForm.setBillingAddressZip("ZIP");
ticketForm1.getAdditional().put("field3", Collections.singletonList("missing value"));
var success = reservationApiV2Controller.validateToOverview(reservationId, "en", false, contactForm, new BeanPropertyBindingResult(contactForm, "paymentForm"), context.getPublicAuthentication());
assertEquals(HttpStatus.OK, success.getStatusCode());
reservationApiV2Controller.cancelPendingReservation(reservationId);
}
// buy one ticket, without discount
{
var form = new ReservationForm();
var ticketReservation = new TicketReservationModification();
ticketReservation.setQuantity(1);
ticketReservation.setTicketCategoryId(eventApiV2Controller.getTicketCategories(context.event.getShortName(), null).getBody().getTicketCategories().get(0).getId());
form.setReservation(Collections.singletonList(ticketReservation));
var res = eventApiV2Controller.reserveTickets(context.event.getShortName(), "en", form, new BeanPropertyBindingResult(form, "reservation"), new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()), context.getPublicUser());
assertEquals(HttpStatus.OK, res.getStatusCode());
var resBody = res.getBody();
assertNotNull(resBody);
assertTrue(resBody.isSuccess());
assertEquals(0, resBody.getErrorCount());
var reservationId = resBody.getValue();
checkStatus(reservationId, HttpStatus.OK, false, TicketReservation.TicketReservationStatus.PENDING, context);
var resInfoRes = reservationApiV2Controller.getReservationInfo(reservationId, context.getPublicUser());
assertEquals(HttpStatus.OK, resInfoRes.getStatusCode());
var reservation = resInfoRes.getBody();
assertNotNull(reservation);
assertEquals(reservationId, reservation.getId());
assertEquals(1, reservation.getTicketsByCategory().size());
assertEquals(1, reservation.getTicketsByCategory().get(0).getTickets().size());
var selectedTicket = reservation.getTicketsByCategory().get(0).getTickets().get(0);
assertEquals("field1", selectedTicket.getTicketFieldConfigurationBeforeStandard().get(0).getName());
assertTrue(selectedTicket.getTicketFieldConfigurationBeforeStandard().get(0).isRequired());
assertEquals("field2", selectedTicket.getTicketFieldConfigurationAfterStandard().get(0).getName());
assertFalse(selectedTicket.getTicketFieldConfigurationAfterStandard().get(0).isRequired());
var contactForm = new ContactAndTicketsForm();
var validationErrorsRes = reservationApiV2Controller.validateToOverview(reservationId, "en", false, contactForm, new BeanPropertyBindingResult(contactForm, "paymentForm"), context.getPublicAuthentication());
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, validationErrorsRes.getStatusCode());
assertNotNull(validationErrorsRes.getBody());
assertFalse(validationErrorsRes.getBody().isSuccess());
// first name, last name, email + MISSING_ATTENDEE DATA
assertEquals(4, validationErrorsRes.getBody().getErrorCount());
// move to overview status
contactForm = new ContactAndTicketsForm();
contactForm.setEmail("test@test.com");
contactForm.setBillingAddress("my billing address");
contactForm.setFirstName("full");
contactForm.setLastName("name");
var ticketForm = new UpdateTicketOwnerForm();
ticketForm.setFirstName("ticketfull");
ticketForm.setLastName("ticketname");
ticketForm.setEmail("tickettest@test.com");
contactForm.setTickets(Collections.singletonMap(reservation.getTicketsByCategory().get(0).getTickets().get(0).getUuid(), ticketForm));
var overviewResFailed = reservationApiV2Controller.validateToOverview(reservationId, "en", false, contactForm, new BeanPropertyBindingResult(contactForm, "paymentForm"), context.getPublicAuthentication());
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, overviewResFailed.getStatusCode());
checkStatus(reservationId, HttpStatus.OK, false, TicketReservation.TicketReservationStatus.PENDING, context);
// add mandatory additional field
ticketForm.setAdditional(Collections.singletonMap("field1", Collections.singletonList("value")));
var overviewRes = reservationApiV2Controller.validateToOverview(reservationId, "en", false, contactForm, new BeanPropertyBindingResult(contactForm, "paymentForm"), context.getPublicAuthentication());
assertEquals(HttpStatus.OK, overviewRes.getStatusCode());
checkStatus(reservationId, HttpStatus.OK, true, TicketReservation.TicketReservationStatus.PENDING, context);
//
reservationApiV2Controller.backToBooking(reservationId);
checkStatus(reservationId, HttpStatus.OK, false, TicketReservation.TicketReservationStatus.PENDING, context);
overviewRes = reservationApiV2Controller.validateToOverview(reservationId, "en", false, contactForm, new BeanPropertyBindingResult(contactForm, "paymentForm"), context.getPublicAuthentication());
assertNotNull(overviewRes.getBody());
assertTrue(overviewRes.getBody().getValue());
checkStatus(reservationId, HttpStatus.OK, true, TicketReservation.TicketReservationStatus.PENDING, context);
var owner = ticketReservationRepository.getReservationOwnerAndOrganizationId(reservationId);
if (context.publicUserId != null) {
assertTrue(owner.isPresent());
assertEquals(context.publicUserId, owner.get().getUserId());
// make sure that the profile has been persisted
var optionalProfile = userRepository.loadUserProfile(context.publicUserId);
assertTrue(optionalProfile.isPresent());
// access to the reservation must be denied for anonymous users
assertThrows(ReservationAccessDenied.class, () -> reservationApiV2Controller.getReservationInfo(reservationId, null));
} else {
assertTrue(owner.isEmpty());
}
var paymentForm = new PaymentForm();
var handleResError = reservationApiV2Controller.confirmOverview(reservationId, "en", paymentForm, new BeanPropertyBindingResult(paymentForm, "paymentForm"), new MockHttpServletRequest(), context.getPublicUser());
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, handleResError.getStatusCode());
paymentForm.setPrivacyPolicyAccepted(true);
paymentForm.setTermAndConditionsAccepted(true);
paymentForm.setPaymentProxy(PaymentProxy.OFFLINE);
paymentForm.setSelectedPaymentMethod(PaymentMethod.BANK_TRANSFER);
// bank transfer does not have a transaction, it's created on confirmOverview call
var tStatus = reservationApiV2Controller.getTransactionStatus(reservationId, "BANK_TRANSFER");
assertEquals(HttpStatus.NOT_FOUND, tStatus.getStatusCode());
//
var handleRes = reservationApiV2Controller.confirmOverview(reservationId, "en", paymentForm, new BeanPropertyBindingResult(paymentForm, "paymentForm"), new MockHttpServletRequest(), context.getPublicUser());
assertEquals(HttpStatus.OK, handleRes.getStatusCode());
checkStatus(reservationId, HttpStatus.OK, true, TicketReservation.TicketReservationStatus.OFFLINE_PAYMENT, context);
tStatus = reservationApiV2Controller.getTransactionStatus(reservationId, "BANK_TRANSFER");
assertEquals(HttpStatus.OK, tStatus.getStatusCode());
assertNotNull(tStatus.getBody());
assertFalse(tStatus.getBody().isSuccess());
reservation = reservationApiV2Controller.getReservationInfo(reservationId, context.getPublicUser()).getBody();
assertNotNull(reservation);
var orderSummary = reservation.getOrderSummary();
assertTrue(orderSummary.isNotYetPaid());
assertEquals("10.00", orderSummary.getTotalPrice());
assertEquals("0.10", orderSummary.getTotalVAT());
assertEquals("1.00", orderSummary.getVatPercentage());
// clear the extension_log table so that we can check the expectation
cleanupExtensionLog();
validatePayment(context.event.getShortName(), reservationId, context);
extLogs = extensionLogRepository.getPage(null, null, null, 100, 0);
boolean online = containsOnlineTickets(context, reservationId);
assertEventLogged(extLogs, RESERVATION_CONFIRMED, online ? 12 : 10);
assertEventLogged(extLogs, CONFIRMATION_MAIL_CUSTOM_TEXT, online ? 12 : 10);
assertEventLogged(extLogs, TICKET_ASSIGNED, online ? 12 : 10);
if (online) {
assertEventLogged(extLogs, CUSTOM_ONLINE_JOIN_URL, 12);
}
assertEventLogged(extLogs, TICKET_ASSIGNED_GENERATE_METADATA, online ? 12 : 10);
assertEventLogged(extLogs, TICKET_MAIL_CUSTOM_TEXT, online ? 12 : 10);
checkStatus(reservationId, HttpStatus.OK, true, TicketReservation.TicketReservationStatus.COMPLETE, context);
tStatus = reservationApiV2Controller.getTransactionStatus(reservationId, "BANK_TRANSFER");
assertEquals(HttpStatus.OK, tStatus.getStatusCode());
assertNotNull(tStatus.getBody());
assertTrue(tStatus.getBody().isSuccess());
reservation = reservationApiV2Controller.getReservationInfo(reservationId, context.getPublicUser()).getBody();
assertNotNull(reservation);
orderSummary = reservation.getOrderSummary();
assertFalse(orderSummary.isNotYetPaid());
var confRes = reservationApiV2Controller.reSendReservationConfirmationEmail(PurchaseContextType.event, context.event.getShortName(), reservationId, "en", context.getPublicUser());
assertEquals(HttpStatus.OK, confRes.getStatusCode());
assertNotNull(confRes.getBody());
assertTrue(confRes.getBody());
// trigger email processing
triggerEmailProcessingAndCheck(context, reservationId);
var ticket = reservation.getTicketsByCategory().stream().findFirst().orElseThrow().getTickets().get(0);
assertEquals("tickettest@test.com", ticket.getEmail());
assertEquals("ticketfull", ticket.getFirstName());
assertEquals("ticketname", ticket.getLastName());
var ticketNotFoundRes = ticketApiV2Controller.getTicketInfo(context.event.getShortName(), "DONT_EXISTS");
assertEquals(HttpStatus.NOT_FOUND, ticketNotFoundRes.getStatusCode());
var ticketFoundRes = ticketApiV2Controller.getTicketInfo(context.event.getShortName(), ticket.getUuid());
assertEquals(HttpStatus.OK, ticketFoundRes.getStatusCode());
var ticketFoundBody = ticketFoundRes.getBody();
assertNotNull(ticketFoundBody);
assertEquals("tickettest@test.com", ticketFoundBody.getEmail());
assertEquals("ticketfull ticketname", ticketFoundBody.getFullName());
assertEquals("full name", ticketFoundBody.getReservationFullName());
assertTrue(reservationId.startsWith(ticketFoundBody.getReservationId().toLowerCase(Locale.ENGLISH)));
var sendTicketByEmailRes = ticketApiV2Controller.sendTicketByEmail(context.event.getShortName(), ticket.getUuid());
assertEquals(HttpStatus.OK, sendTicketByEmailRes.getStatusCode());
assertNotNull(sendTicketByEmailRes.getBody());
assertTrue(sendTicketByEmailRes.getBody());
// trigger email processing
triggerEmailProcessingAndCheck(context, reservationId);
// update ticket
var updateTicketOwnerForm = new UpdateTicketOwnerForm();
updateTicketOwnerForm.setFirstName("Test");
updateTicketOwnerForm.setLastName("Testson");
updateTicketOwnerForm.setEmail("testmctest@test.com");
updateTicketOwnerForm.setAdditional(Collections.singletonMap("field1", Collections.singletonList("value")));
var updateTicketRes = ticketApiV2Controller.updateTicketInfo(context.event.getShortName(), ticket.getUuid(), updateTicketOwnerForm, new BeanPropertyBindingResult(updateTicketOwnerForm, "ticket"), context.getPublicAuthentication());
assertNotNull(updateTicketRes.getBody());
assertTrue(updateTicketRes.getBody().isSuccess());
// not found
assertEquals(HttpStatus.NOT_FOUND, ticketApiV2Controller.updateTicketInfo(context.event.getShortName(), ticket.getUuid() + "42", updateTicketOwnerForm, new BeanPropertyBindingResult(updateTicketOwnerForm, "ticket"), context.getPublicAuthentication()).getStatusCode());
ticketFoundRes = ticketApiV2Controller.getTicketInfo(context.event.getShortName(), ticket.getUuid());
ticketFoundBody = ticketFoundRes.getBody();
assertNotNull(ticketFoundBody);
assertEquals("testmctest@test.com", ticketFoundBody.getEmail());
assertEquals("Test Testson", ticketFoundBody.getFullName());
assertEquals("full name", ticketFoundBody.getReservationFullName());
reservation = reservationApiV2Controller.getReservationInfo(reservationId, context.getPublicUser()).getBody();
assertNotNull(reservation);
ticket = reservation.getTicketsByCategory().stream().findFirst().orElseThrow().getTickets().get(0);
assertEquals("testmctest@test.com", ticket.getEmail());
assertEquals("Test", ticket.getFirstName());
assertEquals("Testson", ticket.getLastName());
var ticketPdfMockResp = new MockHttpServletResponse();
ticketApiV2Controller.generateTicketPdf(context.event.getShortName(), ticket.getUuid(), ticketPdfMockResp);
assertEquals("application/pdf", ticketPdfMockResp.getContentType());
var ticketQRCodeResp = new MockHttpServletResponse();
ticketApiV2Controller.showQrCode(context.event.getShortName(), ticket.getUuid(), ticketQRCodeResp);
assertEquals("image/png", ticketQRCodeResp.getContentType());
var fullTicketInfo = ticketRepository.findByUUID(ticket.getUuid());
var qrCodeReader = new QRCodeReader();
var qrCodeRead = qrCodeReader.decode(new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(ImageIO.read(new ByteArrayInputStream(ticketQRCodeResp.getContentAsByteArray()))))), Map.of(DecodeHintType.PURE_BARCODE, Boolean.TRUE));
assertEquals(fullTicketInfo.ticketCode(context.event.getPrivateKey()), qrCodeRead.getText());
// can only be done for free tickets
var releaseTicketFailure = ticketApiV2Controller.releaseTicket(context.event.getShortName(), ticket.getUuid());
assertEquals(HttpStatus.BAD_REQUEST, releaseTicketFailure.getStatusCode());
assertEquals(HttpStatus.OK, ticketApiV2Controller.getTicketInfo(context.event.getShortName(), ticket.getUuid()).getStatusCode());
// no invoice, but receipt
assertEquals(HttpStatus.NOT_FOUND, reservationApiV2Controller.getInvoice(context.event.getShortName(), reservationId, new MockHttpServletResponse(), context.getPublicAuthentication()).getStatusCode());
assertEquals(HttpStatus.OK, reservationApiV2Controller.getReceipt(context.event.getShortName(), reservationId, new MockHttpServletResponse(), context.getPublicAuthentication()).getStatusCode());
//
{
// clear the extension_log table so that we can check the expectation
cleanupExtensionLog();
Principal principal = mock(Principal.class);
Mockito.when(principal.getName()).thenReturn(context.userId);
String ticketIdentifier = fullTicketInfo.getUuid();
String eventName = context.event.getShortName();
String ticketCode = fullTicketInfo.ticketCode(context.event.getPrivateKey());
TicketAndCheckInResult ticketAndCheckInResult = checkInApiController.findTicketWithUUID(context.event.getId(), ticketIdentifier, ticketCode);
assertEquals(CheckInStatus.OK_READY_TO_BE_CHECKED_IN, ticketAndCheckInResult.getResult().getStatus());
CheckInApiController.TicketCode tc = new CheckInApiController.TicketCode();
tc.setCode(ticketCode);
assertEquals(CheckInStatus.SUCCESS, checkInApiController.checkIn(context.event.getId(), ticketIdentifier, tc, new TestingAuthenticationToken("ciccio", "ciccio")).getResult().getStatus());
List<ScanAudit> audits = scanAuditRepository.findAllForEvent(context.event.getId());
assertFalse(audits.isEmpty());
assertTrue(audits.stream().anyMatch(sa -> sa.getTicketUuid().equals(ticketIdentifier)));
extLogs = extensionLogRepository.getPage(null, null, null, 100, 0);
assertEventLogged(extLogs, TICKET_CHECKED_IN, 2);
TicketAndCheckInResult ticketAndCheckInResultOk = checkInApiController.findTicketWithUUID(context.event.getId(), ticketIdentifier, ticketCode);
assertEquals(CheckInStatus.ALREADY_CHECK_IN, ticketAndCheckInResultOk.getResult().getStatus());
// check stats after check in one ticket
assertTrue(eventStatisticsManager.getTicketSoldStatistics(context.event.getId(), statisticsFrom, statisticsTo, "day").stream().mapToLong(TicketsByDateStatistic::getCount).sum() > 0);
EventWithAdditionalInfo eventWithAdditionalInfo3 = eventStatisticsManager.getEventWithAdditionalInfo(context.event.getShortName(), context.userId);
assertEquals(2, eventWithAdditionalInfo3.getNotSoldTickets());
assertEquals(0, eventWithAdditionalInfo3.getSoldTickets());
assertEquals(20, eventWithAdditionalInfo3.getAvailableSeats());
assertEquals(1, eventWithAdditionalInfo3.getCheckedInTickets());
// test revert check in
assertTrue(checkInApiController.revertCheckIn(context.event.getId(), ticketIdentifier, principal));
assertFalse(checkInApiController.revertCheckIn(context.event.getId(), ticketIdentifier, principal));
TicketAndCheckInResult ticketAndCheckInResult2 = checkInApiController.findTicketWithUUID(context.event.getId(), ticketIdentifier, ticketCode);
assertEquals(CheckInStatus.OK_READY_TO_BE_CHECKED_IN, ticketAndCheckInResult2.getResult().getStatus());
UsersApiController.UserWithPasswordAndQRCode sponsorUser = usersApiController.insertUser(new UserModification(null, context.event.getOrganizationId(), "SPONSOR", "sponsor", "first", "last", "email@email.com", User.Type.INTERNAL, null, null), "http://localhost:8080", principal);
Principal sponsorPrincipal = mock(Principal.class);
Mockito.when(sponsorPrincipal.getName()).thenReturn(sponsorUser.getUsername());
// check failures
assertEquals(CheckInStatus.EVENT_NOT_FOUND, attendeeApiController.scanBadge(new AttendeeApiController.SponsorScanRequest("not-existing-event", "not-existing-ticket", null, null), sponsorPrincipal).getBody().getResult().getStatus());
assertEquals(CheckInStatus.TICKET_NOT_FOUND, attendeeApiController.scanBadge(new AttendeeApiController.SponsorScanRequest(eventName, "not-existing-ticket", null, null), sponsorPrincipal).getBody().getResult().getStatus());
assertEquals(CheckInStatus.INVALID_TICKET_STATE, attendeeApiController.scanBadge(new AttendeeApiController.SponsorScanRequest(eventName, ticketIdentifier, null, null), sponsorPrincipal).getBody().getResult().getStatus());
//
// check stats after revert check in one ticket
assertTrue(eventStatisticsManager.getTicketSoldStatistics(context.event.getId(), statisticsFrom, statisticsTo, "day").stream().mapToLong(TicketsByDateStatistic::getCount).sum() > 0);
EventWithAdditionalInfo eventWithAdditionalInfo4 = eventStatisticsManager.getEventWithAdditionalInfo(context.event.getShortName(), context.userId);
assertEquals(2, eventWithAdditionalInfo4.getNotSoldTickets());
assertEquals(1, eventWithAdditionalInfo4.getSoldTickets());
assertEquals(20, eventWithAdditionalInfo4.getAvailableSeats());
assertEquals(0, eventWithAdditionalInfo4.getCheckedInTickets());
cleanupExtensionLog();
CheckInApiController.TicketCode tc2 = new CheckInApiController.TicketCode();
tc2.setCode(ticketCode);
TicketAndCheckInResult ticketAndcheckInResult = checkInApiController.checkIn(context.event.getId(), ticketIdentifier, tc2, new TestingAuthenticationToken("ciccio", "ciccio"));
assertEquals(CheckInStatus.SUCCESS, ticketAndcheckInResult.getResult().getStatus());
extLogs = extensionLogRepository.getPage(null, null, null, 100, 0);
assertEventLogged(extLogs, TICKET_CHECKED_IN, 2);
var offlineIdentifiers = checkInApiController.getOfflineIdentifiers(context.event.getShortName(), 0L, new MockHttpServletResponse(), principal);
if (context.checkInStationsEnabled) {
assertFalse(offlineIdentifiers.isEmpty(), "Alf.io-PI integration must be enabled by default");
// disable Alf.io-PI
configurationRepository.insert(ConfigurationKeys.ALFIO_PI_INTEGRATION_ENABLED.name(), "false", null);
offlineIdentifiers = checkInApiController.getOfflineIdentifiers(context.event.getShortName(), 0L, new MockHttpServletResponse(), principal);
assertTrue(offlineIdentifiers.isEmpty());
// re-enable Alf.io-PI
configurationRepository.insertEventLevel(context.event.getOrganizationId(), context.event.getId(), ConfigurationKeys.OFFLINE_CHECKIN_ENABLED.name(), "true", null);
configurationRepository.update(ConfigurationKeys.ALFIO_PI_INTEGRATION_ENABLED.name(), "true");
offlineIdentifiers = checkInApiController.getOfflineIdentifiers(context.event.getShortName(), 0L, new MockHttpServletResponse(), principal);
assertFalse(offlineIdentifiers.isEmpty());
// download encrypted ticket data
TicketWithCategory ticketwc = testEncryptedCheckInPayload(principal, ticketAndcheckInResult, offlineIdentifiers, false, context);
// insert a poll and download again encrypted data. This time we expect a pin to be present because we haven't specified a tag
var rowCountAndKey = pollRepository.insert(Map.of("en", "test poll"), null, List.of(), 0, context.event.getId(), context.event.getOrganizationId());
testEncryptedCheckInPayload(principal, ticketAndcheckInResult, offlineIdentifiers, true, context);
// we define a tag for the poll, this time we won't have a pin in the result
pollRepository.update(Map.of("en", "test poll"), null, List.of("blabla"), 0, rowCountAndKey.getKey(), context.event.getId());
testEncryptedCheckInPayload(principal, ticketAndcheckInResult, offlineIdentifiers, false, context);
// now we add a matching tag to the ticket. As a result, the pin must be included in the result
ticketRepository.updateTicketTags(List.of(ticketwc.getId()), List.of("blabla"));
testEncryptedCheckInPayload(principal, ticketAndcheckInResult, offlineIdentifiers, true, context);
//
// check register sponsor scan success flow
assertTrue(attendeeApiController.getScannedBadges(context.event.getShortName(), EventUtil.JSON_DATETIME_FORMATTER.format(LocalDateTime.of(1970, 1, 1, 0, 0)), sponsorPrincipal).getBody().isEmpty());
assertEquals(CheckInStatus.SUCCESS, attendeeApiController.scanBadge(new AttendeeApiController.SponsorScanRequest(eventName, ticketwc.getUuid(), null, null), sponsorPrincipal).getBody().getResult().getStatus());
assertEquals(1, attendeeApiController.getScannedBadges(context.event.getShortName(), EventUtil.JSON_DATETIME_FORMATTER.format(LocalDateTime.of(1970, 1, 1, 0, 0)), sponsorPrincipal).getBody().size());
// check export
MockHttpServletResponse response = new MockHttpServletResponse();
eventApiController.downloadSponsorScanExport(context.event.getShortName(), "csv", response, principal);
response.getContentAsString();
CSVReader csvReader = new CSVReader(new StringReader(response.getContentAsString()));
List<String[]> csvSponsorScan = csvReader.readAll();
assertEquals(2, csvSponsorScan.size());
assertEquals("sponsor", csvSponsorScan.get(1)[0]);
assertEquals("Test Testson", csvSponsorScan.get(1)[3]);
assertEquals("testmctest@test.com", csvSponsorScan.get(1)[4]);
assertEquals("", csvSponsorScan.get(1)[8]);
assertEquals(SponsorScan.LeadStatus.WARM.name(), csvSponsorScan.get(1)[9]);
//
// check update notes
assertEquals(CheckInStatus.SUCCESS, attendeeApiController.scanBadge(new AttendeeApiController.SponsorScanRequest(eventName, ticket.getUuid(), "this is a very good lead!", "HOT"), sponsorPrincipal).getBody().getResult().getStatus());
assertEquals(1, attendeeApiController.getScannedBadges(context.event.getShortName(), EventUtil.JSON_DATETIME_FORMATTER.format(LocalDateTime.of(1970, 1, 1, 0, 0)), sponsorPrincipal).getBody().size());
response = new MockHttpServletResponse();
eventApiController.downloadSponsorScanExport(context.event.getShortName(), "csv", response, principal);
response.getContentAsString();
csvReader = new CSVReader(new StringReader(response.getContentAsString()));
csvSponsorScan = csvReader.readAll();
assertEquals(2, csvSponsorScan.size());
assertEquals("sponsor", csvSponsorScan.get(1)[0]);
assertEquals("Test Testson", csvSponsorScan.get(1)[3]);
assertEquals("testmctest@test.com", csvSponsorScan.get(1)[4]);
assertEquals("this is a very good lead!", csvSponsorScan.get(1)[8]);
assertEquals(SponsorScan.LeadStatus.HOT.name(), csvSponsorScan.get(1)[9]);
// #742 - test multiple check-ins
// since on the badge we don't have the full ticket info, we will pass in "null" as scanned code
CheckInApiController.TicketCode badgeScan = new CheckInApiController.TicketCode();
badgeScan.setCode(null);
ticketAndcheckInResult = checkInApiController.checkIn(context.event.getId(), ticketIdentifier, badgeScan, new TestingAuthenticationToken("ciccio", "ciccio"));
// ONCE_PER_DAY is disabled by default, therefore we get an error
assertEquals(CheckInStatus.EMPTY_TICKET_CODE, ticketAndcheckInResult.getResult().getStatus());
// enable ONCE_PER_DAYFalse
TicketCategory category = ticketCategoryRepository.getById(ticketwc.getCategoryId());
ticketCategoryRepository.update(category.getId(), category.getName(), category.getInception(context.event.getZoneId()), category.getExpiration(context.event.getZoneId()), category.getMaxTickets(), category.isAccessRestricted(), MonetaryUtil.unitToCents(category.getPrice(), category.getCurrencyCode()), category.getCode(), category.getValidCheckInFrom(), category.getValidCheckInTo(), category.getTicketValidityStart(), category.getTicketValidityEnd(), TicketCategory.TicketCheckInStrategy.ONCE_PER_DAY, category.getTicketAccessType());
ticketAndcheckInResult = checkInApiController.checkIn(context.event.getId(), ticketIdentifier, badgeScan, new TestingAuthenticationToken("ciccio", "ciccio"));
// the event start date is in one week, so we expect an error here
assertEquals(CheckInStatus.INVALID_TICKET_CATEGORY_CHECK_IN_DATE, ticketAndcheckInResult.getResult().getStatus());
eventRepository.updateHeader(context.event.getId(), context.event.getDisplayName(), context.event.getWebsiteUrl(), context.event.getExternalUrl(), context.event.getTermsAndConditionsUrl(), context.event.getPrivacyPolicyUrl(), context.event.getImageUrl(), context.event.getFileBlobId(), context.event.getLocation(), context.event.getLatitude(), context.event.getLongitude(), context.event.now(clockProvider).minusSeconds(1), context.event.getEnd(), context.event.getTimeZone(), context.event.getOrganizationId(), context.event.getLocales(), context.event.getFormat());
ticketAndcheckInResult = checkInApiController.checkIn(context.event.getId(), ticketIdentifier, badgeScan, new TestingAuthenticationToken("ciccio", "ciccio"));
// we have already scanned the ticket today, so we expect to receive a warning
assertEquals(CheckInStatus.BADGE_SCAN_ALREADY_DONE, ticketAndcheckInResult.getResult().getStatus());
assertEquals(1, (int) auditingRepository.countAuditsOfTypeForReservation(reservationId, Audit.EventType.BADGE_SCAN));
// move the scans to yesterday
// we expect 3 rows because:
// 1 check-in
// 1 revert
// 1 badge scan
assertEquals(3, jdbcTemplate.update("update auditing set event_time = event_time - interval '1 day' where reservation_id = :reservationId and event_type in ('BADGE_SCAN', 'CHECK_IN')", Map.of("reservationId", reservationId)));
ticketAndcheckInResult = checkInApiController.checkIn(context.event.getId(), ticketIdentifier, badgeScan, new TestingAuthenticationToken("ciccio", "ciccio"));
// we now expect to receive a successful message
assertEquals(CheckInStatus.BADGE_SCAN_SUCCESS, ticketAndcheckInResult.getResult().getStatus());
assertEquals(2, (int) auditingRepository.countAuditsOfTypeForReservation(reservationId, Audit.EventType.BADGE_SCAN));
} else {
assertTrue(offlineIdentifiers.isEmpty(), "Alf.io-PI integration must be disabled");
}
}
performAdditionalTests(context);
eventManager.deleteEvent(context.event.getId(), context.userId);
}
}
use of alfio.controller.api.v2.model.TicketCategory in project alf.io by alfio-event.
the class EventApiV2Controller method getTicketCategories.
@GetMapping("event/{eventName}/ticket-categories")
public ResponseEntity<ItemsByCategory> getTicketCategories(@PathVariable("eventName") String eventName, @RequestParam(value = "code", required = false) String code) {
//
return eventRepository.findOptionalByShortName(eventName).filter(e -> e.getStatus() != Event.Status.DISABLED).map(event -> {
var configurations = configurationManager.getFor(List.of(DISPLAY_TICKETS_LEFT_INDICATOR, MAX_AMOUNT_OF_TICKETS_BY_RESERVATION, DISPLAY_EXPIRED_CATEGORIES), event.getConfigurationLevel());
var ticketCategoryLevelConfiguration = configurationManager.getAllCategoriesAndValueWith(event, MAX_AMOUNT_OF_TICKETS_BY_RESERVATION);
var messageSource = messageSourceManager.getMessageSourceFor(event);
var appliedPromoCode = promoCodeRequestManager.checkCode(event, code);
Optional<SpecialPrice> specialCode = appliedPromoCode.getValue().getLeft();
Optional<PromoCodeDiscount> promoCodeDiscount = appliedPromoCode.getValue().getRight();
final ZonedDateTime now = event.now(clockProvider);
// hide access restricted ticket categories
var ticketCategories = ticketCategoryRepository.findAllTicketCategories(event.getId());
List<SaleableTicketCategory> saleableTicketCategories = ticketCategories.stream().filter((c) -> !c.isAccessRestricted() || shouldDisplayRestrictedCategory(specialCode, c, promoCodeDiscount)).map((category) -> {
int maxTickets = getMaxAmountOfTicketsPerReservation(configurations, ticketCategoryLevelConfiguration, category.getId());
PromoCodeDiscount filteredPromoCode = promoCodeDiscount.filter(promoCode -> shouldApplyDiscount(promoCode, category)).orElse(null);
if (specialCode.isPresent()) {
maxTickets = Math.min(1, maxTickets);
} else if (filteredPromoCode != null && filteredPromoCode.getMaxUsage() != null) {
maxTickets = filteredPromoCode.getMaxUsage() - promoCodeRepository.countConfirmedPromoCode(filteredPromoCode.getId(), categoriesOrNull(filteredPromoCode), null, categoriesOrNull(filteredPromoCode) != null ? "X" : null);
}
return new SaleableTicketCategory(category, now, event, ticketReservationManager.countAvailableTickets(event, category), maxTickets, filteredPromoCode);
}).collect(Collectors.toList());
var valid = saleableTicketCategories.stream().filter(tc -> !tc.getExpired()).collect(Collectors.toList());
//
var ticketCategoryIds = valid.stream().map(SaleableTicketCategory::getId).collect(Collectors.toList());
var ticketCategoryDescriptions = ticketCategoryDescriptionRepository.descriptionsByTicketCategory(ticketCategoryIds);
boolean displayTicketsLeft = configurations.get(DISPLAY_TICKETS_LEFT_INDICATOR).getValueAsBooleanOrDefault();
var categoriesByExpiredFlag = saleableTicketCategories.stream().map(stc -> {
var description = Formatters.applyCommonMark(ticketCategoryDescriptions.getOrDefault(stc.getId(), Collections.emptyMap()));
var expiration = Formatters.getFormattedDate(event, stc.getZonedExpiration(), "common.ticket-category.date-format", messageSource);
var inception = Formatters.getFormattedDate(event, stc.getZonedInception(), "common.ticket-category.date-format", messageSource);
return new TicketCategory(stc, description, inception, expiration, displayTicketsLeft && !stc.isAccessRestricted());
}).sorted(Comparator.comparingInt(TicketCategory::getOrdinal)).collect(partitioningBy(TicketCategory::isExpired));
var promoCode = Optional.of(appliedPromoCode).filter(ValidatedResponse::isSuccess).map(ValidatedResponse::getValue).flatMap(Pair::getRight);
//
var saleableAdditionalServices = additionalServiceRepository.loadAllForEvent(event.getId()).stream().map(as -> new SaleableAdditionalService(event, as, promoCode.orElse(null))).filter(SaleableAdditionalService::isNotExpired).collect(Collectors.toList());
// will be used for fetching descriptions and titles for all the languages
var saleableAdditionalServicesIds = saleableAdditionalServices.stream().map(SaleableAdditionalService::getId).collect(Collectors.toList());
var additionalServiceTexts = additionalServiceTextRepository.getDescriptionsByAdditionalServiceIds(saleableAdditionalServicesIds);
var additionalServicesRes = saleableAdditionalServices.stream().map(as -> {
var expiration = Formatters.getFormattedDate(event, as.getZonedExpiration(), "common.ticket-category.date-format", messageSource);
var inception = Formatters.getFormattedDate(event, as.getZonedInception(), "common.ticket-category.date-format", messageSource);
var title = additionalServiceTexts.getOrDefault(as.getId(), Collections.emptyMap()).getOrDefault(AdditionalServiceText.TextType.TITLE, Collections.emptyMap());
var description = Formatters.applyCommonMark(additionalServiceTexts.getOrDefault(as.getId(), Collections.emptyMap()).getOrDefault(AdditionalServiceText.TextType.DESCRIPTION, Collections.emptyMap()));
return new AdditionalService(as.getId(), as.getType(), as.getSupplementPolicy(), as.isFixPrice(), as.getAvailableQuantity(), as.getMaxQtyPerOrder(), as.getFree(), as.getFormattedFinalPrice(), as.getSupportsDiscount(), as.getDiscountedPrice(), as.getVatApplies(), as.getVatIncluded(), as.getVatPercentage().toString(), as.isExpired(), as.getSaleInFuture(), inception, expiration, title, description);
}).collect(Collectors.toList());
//
// waiting queue parameters
boolean displayWaitingQueueForm = EventUtil.displayWaitingQueueForm(event, saleableTicketCategories, configurationManager, eventStatisticsManager.noSeatsAvailable());
boolean preSales = EventUtil.isPreSales(event, saleableTicketCategories);
Predicate<SaleableTicketCategory> waitingQueueTargetCategory = tc -> !tc.getExpired() && !tc.isBounded();
List<SaleableTicketCategory> unboundedCategories = saleableTicketCategories.stream().filter(waitingQueueTargetCategory).collect(Collectors.toList());
var tcForWaitingList = unboundedCategories.stream().map(stc -> new ItemsByCategory.TicketCategoryForWaitingList(stc.getId(), stc.getName())).collect(toList());
//
var activeCategories = categoriesByExpiredFlag.get(false);
var expiredCategories = configurations.get(DISPLAY_EXPIRED_CATEGORIES).getValueAsBooleanOrDefault() ? categoriesByExpiredFlag.get(true) : List.<TicketCategory>of();
return new ResponseEntity<>(new ItemsByCategory(activeCategories, expiredCategories, additionalServicesRes, displayWaitingQueueForm, preSales, tcForWaitingList), getCorsHeaders(), HttpStatus.OK);
}).orElseGet(() -> ResponseEntity.notFound().headers(getCorsHeaders()).build());
}
use of alfio.controller.api.v2.model.TicketCategory in project alf.io by alfio-event.
the class TicketApiV2Controller method getTicketInfo.
@GetMapping("/api/v2/public/event/{eventName}/ticket/{ticketIdentifier}")
public ResponseEntity<TicketInfo> getTicketInfo(@PathVariable("eventName") String eventName, @PathVariable("ticketIdentifier") String ticketIdentifier) {
// TODO: cleanup, we load useless data here!
var oData = ticketReservationManager.fetchCompleteAndAssigned(eventName, ticketIdentifier);
if (oData.isEmpty()) {
return ResponseEntity.notFound().build();
}
var data = oData.get();
TicketReservation ticketReservation = data.getMiddle();
Ticket ticket = data.getRight();
Event event = data.getLeft();
TicketCategory ticketCategory = ticketCategoryRepository.getByIdAndActive(ticket.getCategoryId(), event.getId());
boolean deskPaymentRequired = Optional.ofNullable(ticketReservation.getPaymentMethod()).orElse(PaymentProxy.STRIPE).isDeskPaymentRequired();
//
var validityStart = Optional.ofNullable(ticketCategory.getTicketValidityStart(event.getZoneId())).orElse(event.getBegin());
var validityEnd = Optional.ofNullable(ticketCategory.getTicketValidityEnd(event.getZoneId())).orElse(event.getEnd());
var sameDay = validityStart.truncatedTo(ChronoUnit.DAYS).equals(validityEnd.truncatedTo(ChronoUnit.DAYS));
var messageSource = messageSourceManager.getMessageSourceFor(event);
var formattedDates = Formatters.getFormattedDates(event, messageSource, event.getContentLanguages());
return ResponseEntity.ok(new TicketInfo(ticket.getFullName(), ticket.getEmail(), ticket.getUuid(), ticketCategory.getName(), ticketReservation.getFullName(), ticketReservationManager.getShortReservationID(event, ticketReservation), deskPaymentRequired, event.getTimeZone(), DatesWithTimeZoneOffset.fromEvent(event), sameDay, formattedDates.beginDate, formattedDates.beginTime, formattedDates.endDate, formattedDates.endTime));
}
Aggregations