de.appsolve.padelcampus.admin.controller.bookings.AdminBookingsReservationsController.java Source code

Java tutorial

Introduction

Here is the source code for de.appsolve.padelcampus.admin.controller.bookings.AdminBookingsReservationsController.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package de.appsolve.padelcampus.admin.controller.bookings;

import de.appsolve.padelcampus.admin.controller.AdminBaseController;
import de.appsolve.padelcampus.constants.*;
import de.appsolve.padelcampus.constants.Currency;
import de.appsolve.padelcampus.data.DateRange;
import de.appsolve.padelcampus.data.OfferDurationPrice;
import de.appsolve.padelcampus.data.ReservationRequest;
import de.appsolve.padelcampus.data.TimeSlot;
import de.appsolve.padelcampus.db.dao.BookingDAOI;
import de.appsolve.padelcampus.db.dao.CalendarConfigDAOI;
import de.appsolve.padelcampus.db.dao.MasterDataDAOI;
import de.appsolve.padelcampus.db.dao.OfferDAOI;
import de.appsolve.padelcampus.db.dao.generic.BaseEntityDAOI;
import de.appsolve.padelcampus.db.model.*;
import de.appsolve.padelcampus.exceptions.CalendarConfigException;
import de.appsolve.padelcampus.spring.LocalDateEditor;
import de.appsolve.padelcampus.spring.OfferOptionCollectionEditor;
import de.appsolve.padelcampus.utils.BookingMonitorUtil;
import de.appsolve.padelcampus.utils.BookingUtil;
import de.appsolve.padelcampus.utils.SessionUtil;
import jersey.repackaged.com.google.common.collect.Sets;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.joda.time.Minutes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.*;

import static de.appsolve.padelcampus.utils.FormatUtils.DATE_HUMAN_READABLE;
import static de.appsolve.padelcampus.utils.FormatUtils.DATE_HUMAN_READABLE_PATTERN;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

/**
 * @author dominik
 */
@Controller()
@RequestMapping("/admin/bookings/reservations")
public class AdminBookingsReservationsController extends AdminBaseController<ReservationRequest> {

    private static final Logger LOG = Logger.getLogger(AdminBookingsReservationsController.class);

    @Autowired
    BookingDAOI bookingDAO;

    @Autowired
    OfferDAOI offerDAO;

    @Autowired
    BookingUtil bookingUtil;

    @Autowired
    BookingMonitorUtil bookingMonitorUtil;

    @Autowired
    CalendarConfigDAOI calendarConfigDAO;

    @Autowired
    SessionUtil sessionUtil;

    @Autowired
    MasterDataDAOI masterDataDAO;

    @Autowired
    OfferOptionCollectionEditor offerOptionCollectionEditor;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Set.class, "offerOptions", offerOptionCollectionEditor);
        binder.registerCustomEditor(LocalDate.class, new LocalDateEditor(DATE_HUMAN_READABLE_PATTERN, false));
        binder.registerCustomEditor(SortedSet.class, "offers", new CustomCollectionEditor(SortedSet.class) {
            @Override
            protected Object convertElement(Object element) {
                if (element instanceof Offer) {
                    return element;
                }
                Long id = Long.parseLong((String) element);
                return offerDAO.findById(id);
            }
        });
    }

    @Override
    public ModelAndView showIndex(HttpServletRequest request, Pageable pageable, String search) {
        String startDateStr = request.getParameter("startDate");
        String endDateStr = request.getParameter("endDate");
        LocalDate startDate = sessionUtil.getBookingListStartDate(request);
        if (startDate == null) {
            startDate = new LocalDate();
        }
        if (!StringUtils.isEmpty(startDateStr)) {
            try {
                startDate = LocalDate.parse(startDateStr, DATE_HUMAN_READABLE);
            } catch (IllegalArgumentException e) {
                //ignore
            }
        }
        sessionUtil.setBookingListStartDate(request, startDate);
        LocalDate endDate = new LocalDate(startDate);
        if (!StringUtils.isEmpty(endDateStr)) {
            try {
                endDate = LocalDate.parse(endDateStr, DATE_HUMAN_READABLE);
            } catch (IllegalArgumentException e) {
                //ignore
            }
        }
        if (endDate.isBefore(startDate)) {
            endDate = new LocalDate(startDate);
        }

        DateRange dateRange = new DateRange();
        dateRange.setStartDate(startDate);
        dateRange.setEndDate(endDate);
        return getIndexView(dateRange);
    }

    @Override
    public ModelAndView showAddView(HttpServletRequest httpRequest) {
        ReservationRequest request = new ReservationRequest();
        request.setPublicBooking(Boolean.TRUE);
        request.setStartTimeHour(Constants.BOOKING_DEFAULT_VALID_FROM_HOUR);
        request.setStartTimeMinute(Constants.BOOKING_DEFAULT_VALID_FROM_MINUTE);
        LocalTime endTime = request.getStartTime().plusMinutes(Constants.BOOKING_DEFAULT_DURATION);
        request.setEndTimeHour(endTime.getHourOfDay());
        request.setEndTimeMinute(endTime.getMinuteOfHour());
        String date = httpRequest.getParameter("date");
        if (!StringUtils.isEmpty(date)) {
            LocalDate localDate = DATE_HUMAN_READABLE.parseLocalDate(date);
            request.setStartDate(localDate);
            request.setEndDate(localDate);
        }
        return getAddView(request);
    }

    @Override
    public ModelAndView postEditView(@ModelAttribute("Model") ReservationRequest reservationRequest,
            HttpServletRequest request, BindingResult bindingResult) {
        ModelAndView addView = getAddView(reservationRequest);
        validator.validate(reservationRequest, bindingResult);
        if (bindingResult.hasErrors()) {
            return addView;
        }
        try {
            Player player = sessionUtil.getUser(request);

            //calculate reservation bookings, taking into account holidays
            LocalDate date = reservationRequest.getStartDate();
            LocalDate endDate = reservationRequest.getEndDate();

            List<Booking> bookings = new ArrayList<>();
            List<Booking> failedBookings = new ArrayList<>();
            LocalDateTime blockingTime = new LocalDateTime();

            while (date.compareTo(endDate) <= 0) {
                Set<CalendarWeekDay> calendarWeekDays = reservationRequest.getCalendarWeekDays();
                for (CalendarWeekDay calendarWeekDay : calendarWeekDays) {
                    if (calendarWeekDay.ordinal() + 1 == date.getDayOfWeek()) {
                        try {
                            List<CalendarConfig> calendarConfigs = calendarConfigDAO.findFor(date); //throws CalendarConfigException
                            Iterator<CalendarConfig> iterator = calendarConfigs.iterator();
                            while (iterator.hasNext()) {
                                CalendarConfig calendarConfig = iterator.next();
                                if (!bookingUtil.isHoliday(date, calendarConfig)) {
                                    for (Offer offer : reservationRequest.getOffers()) {

                                        Booking booking = new Booking();
                                        booking.setAmount(BigDecimal.ZERO);
                                        booking.setBlockingTime(blockingTime);
                                        booking.setBookingDate(date);
                                        booking.setBookingTime(reservationRequest.getStartTime());
                                        booking.setBookingType(BookingType.reservation);
                                        booking.setComment(reservationRequest.getComment());
                                        booking.setConfirmed(Boolean.TRUE);
                                        booking.setCurrency(Currency.EUR);
                                        booking.setDuration(getDuration(reservationRequest, calendarConfig));
                                        booking.setPaymentConfirmed(reservationRequest.getPaymentConfirmed());
                                        booking.setPaymentMethod(PaymentMethod.Reservation);
                                        booking.setPlayer(player);
                                        booking.setUUID(BookingUtil.generateUUID());
                                        booking.setOffer(offer);
                                        booking.setPublicBooking(reservationRequest.getPublicBooking());

                                        //we call this inside the loop to prevent overbooking
                                        List<Booking> confirmedBookings = bookingDAO
                                                .findBlockedBookingsForDate(date);

                                        OfferDurationPrice offerDurationPrice = bookingUtil.getOfferDurationPrice(
                                                calendarConfigs, confirmedBookings, booking.getBookingDate(),
                                                booking.getBookingTime(), offer);
                                        if (offerDurationPrice == null) {
                                            failedBookings.add(booking);
                                            continue;
                                        } else {
                                            BigDecimal price = offerDurationPrice.getDurationPriceMap()
                                                    .get(booking.getDuration().intValue());
                                            booking.setAmount(price);
                                        }

                                        TimeSlot timeSlot = new TimeSlot();
                                        timeSlot.setDate(date);
                                        timeSlot.setStartTime(reservationRequest.getStartTime());
                                        timeSlot.setEndTime(reservationRequest.getEndTime());
                                        timeSlot.setConfig(calendarConfig);
                                        Long bookingSlotsLeft = bookingUtil.getBookingSlotsLeft(timeSlot, offer,
                                                confirmedBookings);

                                        if (bookingSlotsLeft < 1) {
                                            failedBookings.add(booking);
                                            continue;
                                        }

                                        //we save the booking directly to prevent overbookings
                                        booking = bookingDAO.saveOrUpdate(booking);
                                        bookings.add(booking);
                                    }
                                }
                                break;
                            }
                            break;
                        } catch (CalendarConfigException e) {
                            LOG.warn(
                                    "Caught calendar config exception during add reservation request. This may be normal (for holidays)",
                                    e);
                            Booking failedBooking = new Booking();
                            failedBooking.setPlayer(player);
                            failedBooking.setBookingDate(date);
                            failedBooking.setBookingTime(reservationRequest.getStartTime());
                            failedBookings.add(failedBooking);
                        }
                    }
                }
                date = date.plusDays(1);
            }

            if (!failedBookings.isEmpty()) {
                throw new Exception(msg.get("UnableToReserveAllDesiredTimes", new Object[] {
                        StringUtils.join(bookings, "<br/>"), StringUtils.join(failedBookings, "<br/>") }));
            }

            if (bookings.isEmpty()) {
                throw new Exception(msg.get("NoCourtReservationsForSelectedDateTime"));
            }

            return new ModelAndView("redirect:/admin/bookings/reservations");
        } catch (Exception e) {
            LOG.error(e, e);
            bindingResult.addError(new ObjectError("comment", e.getMessage()));
            return addView;
        }
    }

    @RequestMapping(method = GET, value = "booking/{bookingId}")
    public ModelAndView getEditBooking(@PathVariable("bookingId") Long bookingId) {
        Booking booking = bookingDAO.findByIdWithOfferOptions(bookingId);
        if (booking == null) {
            return getNotFoundView();
        }
        ReservationRequest request = getReservationRequestFromBooking(booking);
        return getEditView(request);
    }

    @RequestMapping(method = POST, value = "booking/{bookingId}")
    public ModelAndView postEditBooking(@PathVariable("bookingId") Long bookingId,
            @Valid @ModelAttribute("Model") ReservationRequest model, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return getEditView(model);
        }
        try {
            LocalDate today = new LocalDate();
            LocalTime now = new LocalTime();
            if (model.getStartDate().compareTo(today) < 0
                    || (model.getStartDate().equals(today) && model.getStartTime().compareTo(now) < 0)) {
                throw new Exception(msg.get("RequestedTimeIsInThePast"));
            }

            /* only one offer possible */
            Offer offer = model.getOffers().iterator().next();

            /* make sure to not modify booking before we know it can be changed b/c auf auto transaction commit */
            Booking booking = bookingDAO.findById(bookingId);

            List<CalendarConfig> calendarConfigs = calendarConfigDAO.findFor(model.getStartDate()); //throws CalendarConfigException
            Iterator<CalendarConfig> iterator = calendarConfigs.iterator();
            while (iterator.hasNext()) {
                CalendarConfig calendarConfig = iterator.next();
                if (!bookingUtil.isHoliday(model.getStartDate(), calendarConfig)) {
                    List<Booking> confirmedBookings = bookingDAO.findBlockedBookingsForDate(model.getStartDate());
                    //remove the current booking as we want to update it
                    confirmedBookings.remove(booking);

                    OfferDurationPrice offerDurationPrice = bookingUtil.getOfferDurationPrice(calendarConfigs,
                            confirmedBookings, model.getStartDate(), model.getStartTime(), offer);
                    if (offerDurationPrice != null) {
                        TimeSlot timeSlot = new TimeSlot();
                        timeSlot.setDate(model.getStartDate());
                        timeSlot.setStartTime(model.getStartTime());
                        timeSlot.setEndTime(model.getEndTime());
                        timeSlot.setConfig(calendarConfig);
                        Long bookingSlotsLeft = bookingUtil.getBookingSlotsLeft(timeSlot, offer, confirmedBookings);

                        if (bookingSlotsLeft >= 1) {
                            BigDecimal price = offerDurationPrice.getDurationPriceMap()
                                    .get(booking.getDuration().intValue());
                            booking.setAmount(price);
                            booking.setBlockingTime(new LocalDateTime());
                            booking.setBookingDate(model.getStartDate());
                            booking.setBookingTime(model.getStartTime());
                            booking.setComment(model.getComment());
                            booking.setDuration(getDuration(model, calendarConfig));
                            booking.setOffer(offer);
                            booking.setOfferOptions(model.getOfferOptions());
                            booking.setPaymentConfirmed(model.getPaymentConfirmed());
                            booking.setPaymentMethod(PaymentMethod.Reservation);
                            booking.setPublicBooking(model.getPublicBooking());
                            bookingDAO.saveOrUpdate(booking);
                            return redirectToIndex();
                        }
                    }
                }
            }
            throw new Exception(msg.get("NoCourtReservationsForSelectedDateTime"));
        } catch (Exception e) {
            LOG.error(e);
            bindingResult.addError(new ObjectError("id", e.getMessage()));
            return getEditView(model);
        }
    }

    @RequestMapping(method = GET, value = "/{bookingId}/deleteall")
    public ModelAndView getBookingDeleteAll(@PathVariable("bookingId") Long bookingId) {
        Booking booking = bookingDAO.findById(bookingId);
        if (booking == null) {
            return getNotFoundView();
        }
        List<Booking> commentBookings = bookingDAO.findByBlockingTimeAndComment(booking.getBlockingTime(),
                booking.getComment());
        return getBookingDeleteAllView(booking, commentBookings);
    }

    @RequestMapping(method = POST, value = "/{bookingId}/deleteall")
    public ModelAndView postBookingDeleteAll(HttpServletRequest request,
            @PathVariable("bookingId") Long bookingId) {
        Booking booking = bookingDAO.findById(bookingId);
        if (booking == null) {
            return getNotFoundView();
        }
        List<Booking> commentBookings = bookingDAO.findByBlockingTimeAndComment(booking.getBlockingTime(),
                booking.getComment());
        for (Booking commentBooking : commentBookings) {
            bookingDAO.deleteById(commentBooking.getId());
            bookingMonitorUtil.notifyUsers(request, commentBooking);
        }
        return redirectToIndex(request);
    }

    @RequestMapping(value = "/{id}/delete", method = POST)
    public ModelAndView postDelete(HttpServletRequest request, @PathVariable("id") Long id) {
        @SuppressWarnings("unchecked")
        Booking model = (Booking) getDAO().findById(id);
        try {
            if (model == null) {
                return getNotFoundView();
            }
            getDAO().deleteById(id);
            bookingMonitorUtil.notifyUsers(request, model);
        } catch (DataIntegrityViolationException e) {
            LOG.warn("Attempt to delete " + model + " failed due to " + e);
            ModelAndView deleteView = new ModelAndView("include/delete", "Model", model);
            deleteView.addObject("error", msg.get("CannotDeleteDueToRefrence", new Object[] { model.toString() }));
            return deleteView;
        }
        return redirectToIndex(request);
    }

    @RequestMapping("print/{start}/{end}")
    public ModelAndView getPrintInvoices(@PathVariable("start") String start, @PathVariable("end") String end) {
        MasterData masterData = masterDataDAO.findFirst();
        if (masterData == null) {
            return new ModelAndView("invoices/masterdata_missing");
        }
        LocalDate startDate = new LocalDate(start);
        LocalDate endDate = new LocalDate(end);
        DateRange dateRange = new DateRange();
        dateRange.setStartDate(startDate);
        dateRange.setEndDate(endDate);
        List<Booking> bookings = bookingDAO.findActiveBookingsBetween(dateRange.getStartDate(),
                dateRange.getEndDate());
        ModelAndView mav = new ModelAndView("admin/bookings/reservations/printinvoices");
        mav.addObject("MasterData", masterData);
        mav.addObject("Bookings", bookings);
        return mav;
    }

    private ModelAndView getIndexView(DateRange dateRange) {
        List<Booking> bookings = bookingDAO.findActiveBookingsBetween(dateRange.getStartDate(),
                dateRange.getEndDate());

        BigDecimal total = new BigDecimal(0);
        for (Booking booking : bookings) {
            if (booking.getAmount() != null) {
                total = total.add(booking.getAmount());
            }
        }
        ModelAndView listView = new ModelAndView("admin/bookings/reservations/index");
        listView.addObject("Total", total);
        listView.addObject("Bookings", bookings);
        listView.addObject("DateRange", dateRange);
        return listView;
    }

    @Override
    protected ModelAndView getEditView(ReservationRequest request) {
        ModelAndView mav = new ModelAndView("admin/bookings/reservations/edit");
        mav.addObject("Model", request);
        mav.addObject("Offers", offerDAO.findAllFetchWithOfferOptions());
        return mav;
    }

    private ModelAndView redirectToIndex() {
        return new ModelAndView("redirect:/admin/bookings/reservations");
    }

    private ModelAndView getAddView(ReservationRequest reservationRequest) {
        ModelAndView mav = new ModelAndView("admin/bookings/reservations/add");
        mav.addObject("Model", reservationRequest);
        mav.addObject("WeekDays", CalendarWeekDay.values());
        mav.addObject("Offers", offerDAO.findAll());
        return mav;
    }

    @Override
    public BaseEntityDAOI getDAO() {
        return bookingDAO;
    }

    @Override
    public String getModuleName() {
        return "admin/bookings/reservations";
    }

    private ModelAndView getBookingDeleteAllView(Booking booking, List<Booking> bookingsToDelete) {
        ModelAndView mav = new ModelAndView("admin/bookings/reservations/deleteall");
        mav.addObject("Model", booking);
        mav.addObject("BookingsToDelete", bookingsToDelete);
        return mav;
    }

    private ReservationRequest getReservationRequestFromBooking(Booking booking) {
        ReservationRequest request = new ReservationRequest();
        request.setCalendarWeekDays(
                Sets.newHashSet(CalendarWeekDay.values()[booking.getBookingDate().dayOfWeek().get() - 1]));
        request.setComment(booking.getComment());
        request.setStartDate(booking.getBookingDate());
        request.setStartTimeHour(booking.getBookingTime().getHourOfDay());
        request.setStartTimeMinute(booking.getBookingTime().getMinuteOfHour());
        request.setEndDate(booking.getBookingDate());
        LocalTime endTime = booking.getBookingTime().plusMinutes(booking.getDuration().intValue());
        request.setEndTimeHour(endTime.getHourOfDay());
        request.setEndTimeMinute(endTime.getMinuteOfHour());
        request.setId(booking.getId());
        request.setPublicBooking(booking.getPublicBooking());
        request.setPaymentConfirmed(booking.getPaymentConfirmed());
        request.setOffers(Sets.newHashSet(booking.getOffer()));
        request.setOfferOptions(booking.getOfferOptions());
        return request;
    }

    private Long getDuration(ReservationRequest reservationRequest, CalendarConfig calendarConfig)
            throws Exception {
        int minutes = Minutes.minutesBetween(reservationRequest.getStartTime(), reservationRequest.getEndTime())
                .getMinutes();
        if (minutes < calendarConfig.getMinDuration()) {
            throw new Exception(msg.get("MinDurationIs", new Object[] { calendarConfig.getMinDuration() }));
        }
        return Long.valueOf(minutes);
    }

    @Override
    public ModelAndView getNotFoundView() {
        return new ModelAndView("admin/bookings/reservations/notfound");
    }
}