<?php
namespace App\Service;
use App\Controller\Page\CheckoutController;
use App\Entity\Airport;
use App\Entity\Booking;
use App\Entity\BookingPerson;
use App\Entity\Game;
use App\Entity\Mail;
use App\Entity\MainBooker;
use App\Entity\MainBookerHome;
use App\Entity\StadiumCategory;
use App\Entity\User;
use App\Entity\VRExtraCosts;
use App\Generator\Invoice;
use App\Model\Basket as BasketModel;
use App\Model\Pyton\Accommodation\AccommodationWrapper;
use App\Model\Pyton\AccommodationReceipt;
use App\Model\Pyton\Basket as PytonBasket;
use App\Model\Pyton\Flight\Leg;
use App\Model\Pyton\Flight\Trip;
use App\Model\Pyton\FlightReceipt;
use App\Sendgrid\MailerInterface;
use App\Util\StringToEntityUtil;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use JMS\Serializer\SerializerInterface;
use Money\Currency;
use Money\Money;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
final class Basket
{
public const SESSION_BASKET_NAME = 'pyton_basket_%s';
public function __construct(
private readonly SessionInterface $session,
private readonly Pyton $pyton,
private readonly EntityManagerInterface $entityManager,
private readonly TokenGeneratorInterface $tokenGenerator,
private readonly MailerInterface $mailer,
private readonly Invoice $invoiceGenerator,
private readonly SerializerInterface $serializer,
private readonly ParameterBagInterface $parameterBag,
private readonly StringToEntityUtil $stringToEntityUtil,
) {
}
/**
* @return string
*/
public function getSessionId()
{
if (!$this->session->getId()) {
$this->session->start();
}
return $this->session->getId();
}
/**
* Returns the basket from the session, otherwise return empty BasketModel.
*/
public function getBasket(Game $match, bool $createPytonBasket = false): ?BasketModel
{
/** @var BasketModel $basket */
$basket = $this->session->get(sprintf(self::SESSION_BASKET_NAME, $match->getId()), new BasketModel());
$basket->setPrice($this->calculatePrice($basket, $match));
if ($createPytonBasket) {
if (!$basket->getPytonBasket()) {
$basket->setPytonBasket(new PytonBasket());
}
if (!$basket->getPytonBasket()->getBasketId()) {
try {
$response = json_decode($this->pyton->createBasket(), true);
if (!empty($response['BasketId'])) {
$basket->getPytonBasket()->setBasketId($response['BasketId']);
}
} catch (Exception $exception) {
}
}
}
return $basket;
}
/**
* @return $this
*/
public function setBasket(Game $match, BasketModel $basket): self
{
// TODO:: https://symfony.com/doc/current/components/http_foundation/session_configuration.html#session-metadata
$this->session->set(sprintf(self::SESSION_BASKET_NAME, $match->getId()), $basket);
return $this;
}
/**
* @throws UnknownCurrencyException
*
* @return Money
*/
private function calculatePrice(BasketModel $basket, Game $match)
{
$adults = $basket->getAdults() ?? 1;
$children = $basket->getChildren() ?? 0;
$price = 0;
$price += ($match->getMatchTicketPrice()->getAmount() * ($adults + $children));
if ($accommodation = $this->getSelectedAccommodation($basket)) {
$price += ($accommodation->getPriceDetails()->getPrice()->getValue() * ($adults + $children));
}
if ($basketCategory = $basket->getStadiumCategory()) {
foreach ($match->getCategories() as $matchCategory) {
if ($matchCategory->getCategory()->getId() === $basketCategory) {
$price += ($matchCategory->getPrice()->getAmount() * ($adults + $children));
}
}
}
if ($flight = $this->getSelectedTrip($basket)) {
$roundedFlightValue = round($flight->getPrice()->getValue() / 100) * 100;
$price += ($roundedFlightValue * ($adults + $children));
}
// if ($discountTotal = $basket->getDiscountTotal()) {
// $price -= (int) $basket->getDiscountTotal()->getAmount();
// }
return new Money($price, new Currency('EUR'));
}
public function getSelectedAccommodation(BasketModel $basket): ?AccommodationWrapper
{
if (!$basket->getAccommodation() || !$basket->getAccomodationList()) {
return null;
}
/** @var AccommodationWrapper $accommodation */
foreach ($basket->getAccomodationList() as $accommodation) {
if ($basket->getAccommodation() === $accommodation->getAccommodation()->getId()) {
// At this point the accommodation still has (cron)cached price-details;
// therefor we need to update it with the actual chosen travel-data
$accommodation->getPriceDetails()
->setDepartureDate($basket->getDepartDate())
->setDurationInDays($basket->getReturnDate()->diff($basket->getDepartDate())->days + 1);
return $accommodation;
}
}
return null;
}
/**
* @return AccommodationWrapper|null
*/
public function getSelectedTrip(BasketModel $basket): ?Trip
{
if (!$basket->getFlight() || !$basket->getFlightList()) {
return null;
}
foreach ($basket->getFlightList()->getFlights()->getTrips() as $trip) {
/** @var Trip $trip */
if ($basket->getFlight() === $trip->getId()) {
return $trip;
}
}
return null;
}
/**
* @throws Exception
*
* @return AccommodationReceipt|null
*/
public function getSelectedAccommodationRooms(BasketModel $basket)
{
$selectedRooms = [];
if (!$basket->getPytonBasket()) {
return $selectedRooms;
}
$accomodationReceiptArray = json_decode($basket->getPytonBasket()->getAccommodationReceipt(), true);
// dd($accomodationReceiptArray);
if (isset($accomodationReceiptArray['Receipt']['Accommodation'])) {
foreach ($accomodationReceiptArray['Receipt']['Accommodation']['Units'] as $val => $unit) {
if ($unit['Allotment']['Chosen'] > 0) {
$selectedRooms[] = $unit;
}
}
} else {
return 'nrf';
}
return $selectedRooms;
}
/**
* @throws Exception
*/
public function getSelectedAccommodationForPytonReceipt(BasketModel $basket): ?AccommodationReceipt
{
$accommodationReceipt = new AccommodationReceipt();
$accommodation = $this->getSelectedAccommodation($basket);
if (null === $accommodation) {
return $accommodationReceipt;
}
$accommodationReceipt->addAccommodation($accommodation);
for ($i = 1; $i <= $basket->getAdults(); ++$i) {
$accommodationReceipt->addTraveller(new DateTime('-30 years'));
}
for ($i = 1; $i <= $basket->getChildren(); ++$i) {
$accommodationReceipt->addTraveller(new DateTime('-24 years')); //Fault in Pyton, should be 16
}
return $accommodationReceipt;
}
/**
* @throws Exception
*/
public function getSelectedFlightForPytonReceipt(BasketModel $basket): ?FlightReceipt
{
if (!$basket->getFlight() || !$basket->getFlightList()) {
return null;
}
$flightReceipt = new FlightReceipt();
/** @var Trip $trip */
foreach ($basket->getFlightList()->getFlights()->getTrips() as $trip) {
if ($trip->getId() === $basket->getFlight()) {
$flightReceipt->addTrip($trip);
}
}
for ($i = 1; $i <= $basket->getAdults(); ++$i) {
$flightReceipt->addTraveller(new DateTime('-30 years'));
}
for ($i = 1; $i <= $basket->getChildren(); ++$i) {
$flightReceipt->addTraveller(new DateTime('-24 years')); //Fault in Pyton, should be 16
}
if (empty($flightReceipt->getTrips())) {
return null;
}
/** @var Trip $trip */
foreach ($flightReceipt->getTrips() as $trip) {
foreach ($trip->getInbound()->getLegIds() as $legId) {
/** @var Leg $leg */
foreach ($basket->getFlightList()->getFlights()->getLegs() as $leg) {
if ($leg->getId() === $legId) {
$flightReceipt->addLeg($leg);
if (!$flightReceipt->getFirstDeparture()) {
$flightReceipt->setFirstDeparture($leg->getArrive());
}
}
}
}
foreach ($trip->getOutbound()->getLegIds() as $legId) {
/** @var Leg $leg */
foreach ($basket->getFlightList()->getFlights()->getLegs() as $leg) {
if ($leg->getId() === $legId) {
$flightReceipt->addLeg($leg);
if (!$flightReceipt->getFinalDestination()) {
$flightReceipt->setFinalDestination($leg->getArrive());
}
}
}
}
}
return $flightReceipt;
}
/**
* @throws UnknownCurrencyException
*
* @return int|null
*/
public function getTicketCount(Game $match)
{
$basket = $this->getBasket($match);
return $basket->getAdults() + $basket->getChildren();
}
/**
* @throws Exception
*
* @return Booking
*/
public function saveBasket(Game $match, User $user, float $paidAmount = 0)
{
$basket = $this->getBasket($match);
$booking = new Booking();
$booking->setUser($user);
$booking->setMatch($match);
$booking->setPiggyParticipate((bool) $this->session->get(CheckoutController::PIGGY_PARTICIPATE_KEY));
$booking->setPiggyReferral($this->session->get(CheckoutController::PIGGY_REFERRAL_KEY));
if ($basket->getStadiumCategory() && $stadiumCategory = $this->entityManager->getRepository(
StadiumCategory::class
)->find($basket->getStadiumCategory())) {
$booking->setStadiumCategory($stadiumCategory);
}
$booking
->setPrice($basket->getPrice())
->setDepartDate($basket->getDepartDate())
->setReturnDate($basket->getReturnDate())
->setAccommodation($basket->getAccommodation())
->setFlight($basket->getFlight())
->setAdults($basket->getAdults())
->setChildren($basket->getChildren())
->setRooms($basket->getRooms())
// ->setTravelInsurance($basket->isTravelInsurance())
// ->setCancellationBasicInsurance($basket->isCancellationBasicInsurance())
// ->setCancellationAllInsurance($basket->isCancellationAllInsurance())
->setTotalInsuranceCosts($basket->getFullPrice()->subtract($basket->getPrice()));
/* @var VRExtraCosts $extraCost */
// basket.extraCosts is filled in ExtracostsService.calculateExtraCosts
foreach ($basket->getExtraCosts() as $bookingExtraCost) {
$VRExtraCostRepository = $this->entityManager->getRepository(VRExtraCosts::class);
$unmanagedVRExtraCost = $bookingExtraCost->getExtraCost();
$bookingExtraCost->setExtraCost(
$VRExtraCostRepository->find(
$unmanagedVRExtraCost->getId()
)
);
$booking->addExtraCost($bookingExtraCost);
}
$booking->setTotalInsuranceCosts($basket->getCalculatedExtraPrices());
// chosenAccommodation opslaan
$chosenAccommodation = $this->serializer->toArray(
$this->getSelectedAccommodationRooms($basket)
); // $this->basketService->getSelectedAccommodationRooms($basket);
$booking->setChosenAccommodation($chosenAccommodation);
if ($basket->getDiscountTotal() && $basket->getDiscountReceipt()) {
$booking->setDiscountTotal($basket->getDiscountTotal());
$booking->setDiscountReceipt($basket->getDiscountReceipt());
}
if ($basket->getAirport() && $airport = $this->entityManager
->getRepository(Airport::class)->find($basket->getAirport())) {
$booking->setAirport($airport);
}
if ($basket->getFlight()) {
$flightData = [];
foreach ($basket->getFlightListNormalized() as $flight) {
if ($flight['tripId'] === $basket->getFlight()) {
$flightData = $flight;
break;
}
}
$booking->setFlightInbound(
$this->serializer->toArray($flightData['inbound'])
);
$booking->setFlightOutbound(
$this->serializer->toArray($flightData['outbound'])
);
}
$basketMainBooker = $basket->getTravelersData()['mainBooker'];
if (!$mainBooker = $this->entityManager->getRepository(MainBooker::class)->findOneBy(['user' => $user->getId()]
)) {
$mainBooker = new MainBooker();
$mainBooker
->setCompany($basketMainBooker['company'])
->setAddress($basketMainBooker['address'])
->setGender($basketMainBooker['gender'])
->setHouseNumber($basketMainBooker['houseNumber'])
->setHouseNumberAttribute($basketMainBooker['houseNumberAttribute'])
->setFirstNames($basketMainBooker['firstNames'])
->setLastName($basketMainBooker['lastName'])
->setZipcode($basketMainBooker['zipcode'])
->setCity($basketMainBooker['city'])
->setCountry($basketMainBooker['country'])
->setDob($basketMainBooker['dob'])
->setMobile($basketMainBooker['mobile'])
->setEmail($basketMainBooker['email'])
->setUser($user);
}
$booking->setMainBooker($mainBooker);
foreach ($basket->getTravelersData()['persons'] as $personData) {
$person = new BookingPerson();
$person
->setBooking($booking)
->setGender($personData['gender'])
->setFirstNames($personData['firstNames'])
->setLastName($personData['lastName'])
->setDob($personData['dob'])
->setCountry($personData['country']);
$booking->addPerson($person);
}
$mainBookerHomeData = $basket->getTravelersData()['mainBookerHome'];
$mainBookerHome = new mainBookerHome();
$mainBookerHome
->setGender($mainBookerHomeData['gender'])
->setFirstName($mainBookerHomeData['firstName'])
->setLastName($mainBookerHomeData['lastName'])
->setMobile($mainBookerHomeData['mobile']);
$booking->setMainBookerHome($mainBookerHome);
$booking->setPaid(new Money($paidAmount * 100, new Currency('EUR')));
$this->entityManager->persist($booking);
$this->entityManager->flush();
$this->invoiceGenerator->generateInvoiceForNewBooking($booking, $paidAmount * 100);
return $booking;
}
/**
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*/
public function createUser(Game $match): User
{
$basket = $this->getBasket($match);
$userRepository = $this->entityManager->getRepository(User::class);
$email = $basket->getTravelersData()['mainBooker']['email'];
$username = mb_substr($email, 0, mb_strpos($email, '@'));
if ($user = $userRepository->findOneBy(['email' => $email])) {
return $user;
}
$user = new User();
$user
->setEnabled(true)
->setEmail($email)
->setRoles(['ROLE_SITE_USER']);
while (!empty($userRepository->findOneBy(['username' => $username]))) {
$username .= rand(0, 100);
}
$user->setUsername($username);
// generate password
$password = mb_substr($this->tokenGenerator->generateToken(), 0, 8);
$user->setPlainPassword($password);
$this->entityManager->persist($user);
$this->entityManager->flush();
$mail = $this->getMail('site_newuser_mail');
$this->mailer->mail(
$mail,
'noreply@voetbalretour.nl',
[$basket->getTravelersData()['mainBooker']['email']],
[
'name' => $basket->getTravelersData()['mainBooker']['firstNames'],
'password' => $password,
'email' => $basket->getTravelersData()['mainBooker']['email'],
],
[]
);
// $message = (new Swift_Message())
// ->setFrom('info@voetbalretour.nl')
// ->setTo($basket->getTravelersData()['mainBooker']['email'])
// ->setSubject('Account aangemaakt')
// ->setBody($this->twig->render('mail/account_created.html.twig', [
// 'password' => $password
// ]), 'text/html')
// ;
//
// $this->mailer->send($message);
return $user;
}
private function getMail(string $mailId): ?Mail
{
$mail = $this->parameterBag->get($mailId);
return $this->stringToEntityUtil->stringToEntity(
$mail
);
}
public function test()
{
// $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => 'randy@getnoticed.nl']);
//
// $mainbooker = $this->entityManager->getRepository(MainBooker::class)->findOneBy(['user' => $user->getId()]);
// dd($mainbooker);
}
}