Skip to content

Commit

Permalink
#869 support dynamic discounts
Browse files Browse the repository at this point in the history
  • Loading branch information
cbellone committed Feb 13, 2020
1 parent 5635501 commit fb6d334
Show file tree
Hide file tree
Showing 27 changed files with 435 additions and 107 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ junitVersion=5.1.0
systemProp.jdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2"

# https://jitpack.io/#alfio-event/alf.io-public-frontend -> go to commit tab, set the version
alfioPublicFrontendVersion=ea2f0ea9f2
alfioPublicFrontendVersion=fd13a9f328
29 changes: 29 additions & 0 deletions src/main/java/alfio/controller/api/v2/model/DynamicDiscount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* This file is part of alf.io.
*
* alf.io 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 3 of the License, or
* (at your option) any later version.
*
* alf.io 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 alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.controller.api.v2.model;

import alfio.model.PromoCodeDiscount;
import lombok.Data;

import java.util.Map;

@Data
public class DynamicDiscount {
private final String discount;
private final PromoCodeDiscount.DiscountType discountType;
private final Map<String, String> formattedMessage;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import alfio.model.BillingDetails;
import alfio.model.OrderSummary;
import alfio.model.SummaryRow.SummaryType;
import alfio.model.TicketReservation.TicketReservationStatus;
import alfio.model.transaction.PaymentMethod;
import alfio.model.transaction.PaymentProxy;
Expand Down Expand Up @@ -189,7 +190,7 @@ public static class ReservationInfoOrderSummary {
public ReservationInfoOrderSummary(OrderSummary orderSummary) {
this.summary = orderSummary.getSummary()
.stream()
.map(s -> new ReservationInfoOrderSummaryRow(s.getName(), s.getAmount(), s.getPrice(), s.getSubTotal()))
.map(s -> new ReservationInfoOrderSummaryRow(s.getName(), s.getAmount(), s.getPrice(), s.getSubTotal(), s.getType()))
.collect(Collectors.toList());
this.totalPrice = orderSummary.getTotalPrice();
this.free = orderSummary.getFree();
Expand All @@ -209,5 +210,6 @@ public static class ReservationInfoOrderSummaryRow {
private final int amount;
private final String price;
private final String subTotal;
private final SummaryType type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import alfio.manager.system.ConfigurationLevel;
import alfio.manager.system.ConfigurationManager;
import alfio.model.*;
import alfio.model.modification.TicketReservationModification;
import alfio.model.modification.support.LocationDescriptor;
import alfio.model.result.ValidationResult;
import alfio.model.system.ConfigurationKeys;
Expand Down Expand Up @@ -64,8 +65,7 @@

import static alfio.model.PromoCodeDiscount.categoriesOrNull;
import static alfio.model.system.ConfigurationKeys.*;
import static java.util.stream.Collectors.partitioningBy;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.*;


@RestController
Expand All @@ -92,6 +92,7 @@ public class EventApiV2Controller {
private final EventStatisticsManager eventStatisticsManager;
private final RecaptchaService recaptchaService;
private final PromoCodeRequestManager promoCodeRequestManager;
private final ExtensionManager extensionManager;


@GetMapping("events")
Expand Down Expand Up @@ -496,6 +497,32 @@ public ResponseEntity<ValidatedResponse<EventCode>> validateCode(@PathVariable("
}
}

@PostMapping("event/{eventName}/check-discount")
public ResponseEntity<DynamicDiscount> checkDiscount(@PathVariable("eventName") String eventName, @RequestBody ReservationForm reservation) {
return eventRepository.findOptionalByShortName(eventName)
.flatMap(event -> {
Map<Integer, Long> quantityByCategory = reservation.getReservation().stream()
.filter(trm -> trm.getAmount() > 0)
.collect(groupingBy(TicketReservationModification::getTicketCategoryId, summingLong(TicketReservationModification::getAmount)));
if(quantityByCategory.isEmpty() || ticketCategoryRepository.countPaidCategoriesInReservation(quantityByCategory.keySet()) == 0) {
return Optional.empty();
}
return extensionManager.handleDynamicDiscount(event, quantityByCategory, null)
.filter(d -> d.getDiscountType() != PromoCodeDiscount.DiscountType.NONE)
.map(d -> {
String formattedDiscount;
if(d.getDiscountType() == PromoCodeDiscount.DiscountType.PERCENTAGE) {
formattedDiscount = String.valueOf(d.getDiscountAmount());
} else {
formattedDiscount = MonetaryUtil.formatCents(d.getDiscountAmount(), event.getCurrency());
}
return new DynamicDiscount(formattedDiscount, d.getDiscountType(), formatDynamicCodeMessage(event, d));
});
})
.filter(d -> d.getDiscountType() != PromoCodeDiscount.DiscountType.NONE)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.noContent().build());
}

@GetMapping("event/{eventName}/code/{code}")
public ResponseEntity<Void> handleCode(@PathVariable("eventName") String eventName, @PathVariable("code") String code, ServletWebRequest request) {
Expand Down Expand Up @@ -556,5 +583,30 @@ private boolean isCaptchaInvalid(String recaptchaResponse, HttpServletRequest re
&& !recaptchaService.checkRecaptcha(recaptchaResponse, request);
}

private Map<String, String> formatDynamicCodeMessage(Event event, PromoCodeDiscount promoCodeDiscount) {
Validate.isTrue(promoCodeDiscount != null && promoCodeDiscount.getDiscountType() != PromoCodeDiscount.DiscountType.NONE);
var messageSource = messageSourceManager.getMessageSourceForEvent(event);
Map<String, String> res = new HashMap<>();
String code;
String amount;
switch(promoCodeDiscount.getDiscountType()) {
case PERCENTAGE:
code = "reservation.dynamic.discount.confirmation.percentage.message";
amount = String.valueOf(promoCodeDiscount.getDiscountAmount());
break;
case FIXED_AMOUNT:
amount = MonetaryUtil.formatCents(promoCodeDiscount.getDiscountAmount(), event.getCurrency());
code = "reservation.dynamic.discount.confirmation.fix-per-ticket.message";
break;
default:
throw new IllegalStateException("Unexpected discount code type");
}

for (ContentLanguage cl : event.getContentLanguages()) {
res.put(cl.getLocale().getLanguage(), messageSource.getMessage(code, new Object[]{amount}, cl.getLocale()));
}
return res;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public ResponseEntity<ValidatedResponse<ReservationPaymentResult>> confirmOvervi
}


final TotalPrice reservationCost = ticketReservationManager.totalReservationCostWithVAT(reservationId);
final TotalPrice reservationCost = ticketReservationManager.totalReservationCostWithVAT(reservationId).getLeft();

paymentForm.validate(bindingResult, event, reservationCost);
if (bindingResult.hasErrors()) {
Expand Down Expand Up @@ -332,7 +332,7 @@ public ResponseEntity<ValidatedResponse<Boolean>> validateToOverview(@PathVariab
var event = er.getLeft();
var reservation = er.getRight();
var locale = LocaleUtil.forLanguageTag(lang, event);
final TotalPrice reservationCost = ticketReservationManager.totalReservationCostWithVAT(reservation.withVatStatus(event.getVatStatus()));
final TotalPrice reservationCost = ticketReservationManager.totalReservationCostWithVAT(reservation.withVatStatus(event.getVatStatus())).getLeft();
boolean forceAssignment = configurationManager.getFor(FORCE_TICKET_OWNER_ASSIGNMENT_AT_RESERVATION, ConfigurationLevel.event(event)).getValueAsBooleanOrDefault(false);

if(forceAssignment || ticketReservationManager.containsCategoriesLinkedToGroups(reservationId, event.getId())) {
Expand Down Expand Up @@ -444,7 +444,7 @@ private void checkAndApplyVATRules(String eventName, String reservationId, Conta
var reservation = ticketReservationManager.findById(reservationId).orElseThrow();
var currencyCode = reservation.getCurrencyCode();
PriceContainer.VatStatus vatStatus = determineVatStatus(event.getVatStatus(), vatValidation.isVatExempt());
var updatedPrice = ticketReservationManager.totalReservationCostWithVAT(reservation.withVatStatus(vatStatus));// update VatStatus to the new value for calculating the new price
var updatedPrice = ticketReservationManager.totalReservationCostWithVAT(reservation.withVatStatus(vatStatus)).getLeft();// update VatStatus to the new value for calculating the new price
var calculator = new ReservationPriceCalculator(reservation.withVatStatus(vatStatus), updatedPrice, ticketReservationManager.findTicketsInReservation(reservationId), event);
ticketReservationRepository.updateBillingData(vatStatus, reservation.getSrcPriceCts(),
unitToCents(calculator.getFinalPrice(), currencyCode), unitToCents(calculator.getVAT(), currencyCode), unitToCents(calculator.getAppliedDiscount(), currencyCode),
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/alfio/manager/AdminReservationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ private Result<Boolean> performUpdate(String reservationId, Event event, TicketR
if(newVatStatus != ObjectUtils.firstNonNull(r.getVatStatus(), event.getVatStatus())) {
auditingRepository.insert(reservationId, userRepository.getByUsername(username).getId(), event.getId(), Audit.EventType.FORCE_VAT_APPLICATION, new Date(), Audit.EntityType.RESERVATION, reservationId, singletonList(singletonMap("vatStatus", newVatStatus)));
ticketReservationRepository.addReservationInvoiceOrReceiptModel(reservationId, null);
var newPrice = ticketReservationManager.totalReservationCostWithVAT(r.withVatStatus(newVatStatus));
var newPrice = ticketReservationManager.totalReservationCostWithVAT(r.withVatStatus(newVatStatus)).getLeft();
ticketReservationRepository.resetVat(reservationId, r.isInvoiceRequested(), newVatStatus, r.getSrcPriceCts(), newPrice.getPriceWithVAT(),
newPrice.getVAT(), Math.abs(newPrice.getDiscount()), r.getCurrencyCode());
}
Expand Down Expand Up @@ -587,7 +587,7 @@ public void removeTickets(String eventName, String reservationId, List<Integer>
additionalServiceItemRepository.updateItemsStatusWithReservationUUID(reservation.getId(), AdditionalServiceItem.AdditionalServiceItemStatus.CANCELLED);
} else {
// recalculate totals
var totalPrice = ticketReservationManager.totalReservationCostWithVAT(reservationId);
var totalPrice = ticketReservationManager.totalReservationCostWithVAT(reservationId).getLeft();
var currencyCode = totalPrice.getCurrencyCode();
var updatedTickets = ticketRepository.findTicketsInReservation(reservationId);
var calculator = new ReservationPriceCalculator(reservation, totalPrice, updatedTickets, e);
Expand Down
Loading

0 comments on commit fb6d334

Please sign in to comment.