* @date 12/08/2016 */ namespace AppBundle\Controller; use AppBundle\Entity\BookingRequest; use AppBundle\Entity\BreadcrumbEntry; use AppBundle\Entity\Page; use AppBundle\Entity\TravelDate; use AppBundle\Entity\Traveler; use AppBundle\Entity\TravelPeriodPrice; use AppBundle\Entity\TravelPeriodPriceType; use AppBundle\Form\BookingRequestType; use AppBundle\Util; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; class BookingController extends Controller { /** @var TravelPeriodPriceType[] $priceTypeById */ private $priceTypeById; /** * The routing for this action is entirely controlled by KernelControllerListener! * * @param Page $travelProgramPage * @param Request $request * @param $action * * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response * @throws \Exception */ public function indexAction(Page $travelProgramPage, $action, Request $request) { $travelProgram = $travelProgramPage->getTravelProgram(); if (!$request->query->has('nr')) { return $this->redirect($travelProgramPage->getUrlPath()); } $this->getDoctrine()->getRepository('AppBundle:TravelPeriod')->getTrueTravelPeriods($travelProgram); $this->priceTypeById = $this->getDoctrine()->getRepository('AppBundle:TravelPeriodPriceType')->findAllIndexedById(); // #TODO Consider changing key of travel dates foreach ($travelProgram->getTravelDates() as $curTravelDate) { if ($curTravelDate->getName() == $request->query->get('nr')) { $travelDate = $curTravelDate; break; } } if (!isset($travelDate)) { throw $this->createNotFoundException(); } /** @var BookingRequest $bookingRequest */ $bookingRequest = new BookingRequest(); if ($request->getMethod() != 'POST') { $bookingRequest->setTravelerCount(2); $bookingRequest->setDeparture($travelDate->getDepartures()[0]); } $form = $this->createForm(BookingRequestType::class, $bookingRequest, [ 'travel_date' => $travelDate, 'travel_program' => $travelProgram ]); if ($request->getMethod() == 'POST') { $form->handleRequest($request); $bookingRequest = $form->getData(); } $htmlSummary = []; $bookingPriceInfo = []; $totalPrice = $this->calculatePrice($travelDate, $bookingRequest, $htmlSummary, $bookingPriceInfo); if ($action == '/buchen') { $breadcrumbEntries = Util::createBreadcrumb($travelProgramPage); $breadcrumbEntries[] = new BreadcrumbEntry('Buchen'); if ($request->getMethod() == 'POST' && $form->isValid()) { $booking = $this->getDoctrine()->getRepository('AppBundle:TravelBooking')->createFromBookingRequest( $bookingRequest, $travelDate, $bookingPriceInfo); $em = $this->getDoctrine()->getManager(); $em->persist($booking); $em->flush(); $crmBookingUrl = $this->get('app.booking_exporter')->process($bookingRequest, $travelDate, $bookingPriceInfo); $this->get('mailer')->send(\Swift_Message::newInstance() ->setSubject('Ihr Buchungsauftrag bei STERN TOURS') ->setFrom('stern@stern-tours.de', 'STERN TOURS') ->setTo($bookingRequest->getEmail()) ->setBody( $this->renderView('default/email/bookingConfirmationEmail.txt.twig', [ 'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR, 'booking_request' => $bookingRequest, 'booking_price_info' => $bookingPriceInfo, 'travel_date' => $travelDate, 'breadcrumb_entries' => $breadcrumbEntries, ]), 'text/plain', 'utf-8' ) ); $this->get('mailer')->send(\Swift_Message::newInstance() ->setSubject('BUCHUNG: '. $travelProgram->getTitle() .'('. $travelDate->getName() .')') ->setFrom('stern@stern-tours.de', 'STERN TOURS') ->setTo('stern@stern-tours.de') ->setBody( $this->renderView('default/email/bookingServiceEmail.txt.twig', [ 'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR, 'crm_url' => $crmBookingUrl .'/edit', 'travel_program_url' => Util::getBaseUrl() . $travelProgramPage->getUrlPath(), 'booking_request' => $bookingRequest, 'booking_price_info' => $bookingPriceInfo, 'travel_date' => $travelDate, 'breadcrumb_entries' => $breadcrumbEntries, ]), 'text/plain', 'utf-8' ) ); // #TODO This will lead to multiple bookings due to multiple form submission. Redirect instead! return $this->render('default/pages/bookingConfirmation.html.twig', [ 'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR, 'page' => $travelProgramPage, 'breadcrumb_entries' => $breadcrumbEntries, ]); } return $this->render('default/pages/booking.html.twig', [ 'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR, 'page' => $travelProgramPage, 'travel_program' => $travelProgram, 'travel_date' => $travelDate, 'form' => $form->createView(), 'price_type_by_id' => $this->priceTypeById, 'breadcrumb_entries' => $breadcrumbEntries, 'summary' => $htmlSummary, 'total_price' => $totalPrice, 'mediator_terms_filename' => $travelProgram->getIsMediated() ? $this->getDoctrine()->getRepository('AppBundle:TravelOrganizer')->find(1)->getFileName() : null ]); } elseif ($action == '/berechne-gesamtpreis') { return $this->render('default/components/booking/summary.html.twig', [ 'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR, 'summary' => $htmlSummary, 'total_price' => $totalPrice ]); } throw new \Exception('Unknow BookingController action: '. $action); } public function calculatePrice(TravelDate $travelDate, BookingRequest $bookingRequest, &$outHtmlSummary = null, &$outPriceInfo = null) { $ret = 0; $insuranceAssessmentBasis = 0; $travelerCount = $bookingRequest->getTravelerCount(); if (isset($outHtmlSummary)) { $insuranceHtmlSummary = []; } if (isset($outPriceInfo)) { $outPriceInfo['rooms'] = []; $outPriceInfo['insurances'] = []; $outPriceInfo['options'] = []; $outPriceInfo['classOptions'] = []; } $departure = Util\DepartureUtil::limitIndividualArrivalPrice($bookingRequest->getDeparture(), $travelDate->getFlightPrice()); if (isset($outPriceInfo)) { $outPriceInfo['departure'] = $departure; } if ($departure->getExtraCharge() != 0) { $insuranceAssessmentBasis += $departure->getExtraCharge(); $a = $travelerCount * $departure->getExtraCharge(); $ret += $a; if (isset($outHtmlSummary)) { $outHtmlSummary[] = [ 'value' => $a, 'label' => $travelerCount .'x '. ($departure->getExtraCharge() > 0 ? 'Aufschlag' : 'Abzug') . ' für Abfahrts-/Abflugort "'. $departure->getName() .'" '. Util::formatPrice($departure->getExtraCharge()) .'' ]; } } foreach ($bookingRequest->getTravelOptions() as $travelOption) { $insuranceAssessmentBasis += $travelOption->getPrice(); $a = $travelerCount * $travelOption->getPrice(); $ret += $a; if (isset($outHtmlSummary)) { $outHtmlSummary[] = [ 'value' => $a, 'label' => $travelerCount .'x zugebuchte Leistung: '. $travelOption->getName() .' '. Util::formatPrice($travelOption->getPrice()) .'' ]; } if (isset($outPriceInfo)) { $outPriceInfo['options'][] = $travelOption; } } $persons = [ 'total' => $travelerCount, 'adults' => $travelerCount, 'children' => 0 ]; $possibleRooms = $this->searchRooms($travelDate->getPrices(), $persons); if (empty($possibleRooms)) { if ($travelerCount % 2 == 0) { $possibleRooms = $this->splitIntoTwoGroups($travelDate->getPrices(), $persons, 'equal'); } elseif ($travelerCount >= 3) { $possibleRooms = $this->splitIntoTwoGroups($travelDate->getPrices(), $persons, 'move_without_children'); } } if ($bookingRequest->getComfort()) { foreach ($possibleRooms as $room) { $insuranceAssessmentBasis += $room['price']->getEffectiveComfortPrice(); $a = $room['persons']['total'] * $room['price']->getEffectiveComfortPrice(); $ret += $a; if (isset($outHtmlSummary)) { $outHtmlSummary[] = [ 'value' => $a, 'label' => $room['persons']['total'] .'x zugebuchte Leistung: Komfort-Kategorie '. Util::formatPrice($room['price']->getEffectiveComfortPrice()) .'' ]; } if (isset($outPriceInfo)) { $outPriceInfo['classOptions'][] = [ 'count' => $room['persons']['total'], 'name' => 'zugebuchte Leistung: Komfort (4 Sterne)', 'price' => $room['price']->getEffectiveComfortPrice() ]; } } } $insuranceTotal = 0; foreach ($possibleRooms as $room) { $adultCount = $room['persons']['adults']; $singleFullPrice = $room['price']->getEffectivePrice(); $roomPrice = $singleFullPrice * $adultCount + $singleFullPrice * $room['persons']['children']; $singleDiscountPrice = $room['price']->getEffectiveDiscountPrice(); $discount = ($singleDiscountPrice === null) ? 0 : ($adultCount * ($singleDiscountPrice - $singleFullPrice)); $ret += $roomPrice + $discount; if (isset($outPriceInfo)) { $outPriceInfo['rooms'][] = [ 'name' => $room['priceType']->getName(), 'adults' => $room['persons']['adults'], 'children' => $room['persons']['children'], 'price' => $singleDiscountPrice ?? $singleFullPrice, 'price_children' => $room['price']->getEffectiveChildPrice(), 'price_total' => $roomPrice + $discount, ]; } if (isset($outHtmlSummary)) { $label = '1x '. $room['priceType']->getName() .' [Personen: '. $adultCount .' x '. Util::formatPrice($singleFullPrice) .''; if ($room['persons']['children'] != 0) { $label .= ', Kinder: '. $room['persons']['children'] .' x '. Util::formatPrice($room['price']->getEffectiveChildPrice()) .''; } $label .= ']'; $outHtmlSummary[] = [ 'value' => $roomPrice, 'label' => $label ]; if ($singleDiscountPrice !== null) { $outHtmlSummary[] = [ 'value' => $discount, 'label' => $adultCount .'x '. Util::formatPrice($singleFullPrice - $singleDiscountPrice) .' Rabatt' ]; } if ($bookingRequest->getInsurance() && $adultCount > 0) { $curAssessmentBasis = $insuranceAssessmentBasis + ($singleDiscountPrice ?? $singleFullPrice); $insurancePrice = $this->getDoctrine()->getRepository('AppBundle:TravelInsurancePrice') ->findOneByInsuranceIdAndAssessmentBasis($bookingRequest->getInsurance()->getId(), $curAssessmentBasis); $insurancePriceValue = $insurancePrice->getPrice() > 0 ? $insurancePrice->getPrice() : round($insurancePrice->getPercent() * $curAssessmentBasis / 100, 2); $a = $adultCount * $insurancePriceValue; $insuranceTotal += $a; $ret += $a; if (isset($insuranceHtmlSummary)) { $insuranceHtmlSummary[] = [ 'value' => $a, 'label' => $adultCount .'x RV '. $bookingRequest->getInsurance()->getName() .' ('. $insurancePrice->getCode() .') '. Util::formatPrice($insurancePriceValue) . '' ]; } if (isset($outPriceInfo)) { $outPriceInfo['insurances'][] = [ 'insurance' => $bookingRequest->getInsurance(), 'insurancePriceValue' => $insurancePriceValue, 'insurancePrice' => $insurancePrice, 'count' => $adultCount, ]; } } } } if (isset($insuranceHtmlSummary)) { $outHtmlSummary = array_merge($outHtmlSummary, $insuranceHtmlSummary); } if (isset($outPriceInfo)) { $outPriceInfo['total'] = $ret; $outPriceInfo['totalWithoutInsurance'] = $ret - $insuranceTotal; } return $ret; } /** * @param TravelPeriodPrice[] $prices * @param $persons * * @return array */ private function searchRooms($prices, $persons) { $ret = []; foreach ($prices as $price) { $priceType = $this->priceTypeById[$price->getPriceTypeId()]; if ($priceType->getMax() == $persons['total'] && $priceType->getMaxAdults() >= $persons['adults'] && $priceType->getMinAdults() <= $persons['adults'] && $priceType->getMaxChildren() >= $persons['children']) { $ret[] = [ 'priceType' => $priceType, 'persons' => $persons, 'price' => $price ]; } } return $ret; } private function splitIntoTwoGroups($prices, $persons, $mode) { $group1 = []; $group2 = []; if($mode == 'equal') { $group1['adults'] = $group2['adults'] = $persons['adults'] / 2; $group1['children'] = $group2['children'] = $persons['children'] / 2; $group1['total'] = $group2['total'] = $group1['adults'] + $group2['children']; } elseif($mode = 'move_without_children') { $group1['adults'] = $persons['adults'] - 1; $group1['children'] = 0; $group1['total'] = $group1['adults'] + $group1['children']; $group2['adults'] = 1; $group2['children'] = 0; $group2['total'] = $group2['adults'] + $group2['children']; } $possibleRoomsGroup1 = $this->searchRooms($prices, $group1); $possibleRoomsGroup2 = $this->searchRooms($prices, $group2); return array_merge($possibleRoomsGroup1, $possibleRoomsGroup2); } }