* Anreise-Wochentag nun per Saison statt Objekt definierbar
* Anreise-Wochentag wird für buchbare Tage im Kalender berücksichtigt
* Kalenderblätter bis zum Ende des spätesten bekannten Saisonende
* Datepicker erlaubt nur noch mögliche Reiseenddaten anzuklicken im Fewo-Buchungsformular
* Algogrithmus zur Bestimmung buchbarer Kalendereinträge komplett umgeschrieben, da bisher Reservierungen von nur einer Nacht nicht berücksichtigt werden konnten
* Upload mehrerer Bilder gleichzeitig nun möglich
* Beim Upload von Bildern werden diese gleich in JPEG umgewandelt, komprimiert und für den Slider zugeschnitten
* Behoben: CRM-Export funktioniert teilweise nicht
* Für CMS-Template "overview" können nun twig-Variablen per JSON in Spalte cms_settings gesetzt werden (=> Kulturreisensuche ausblendbar für Fewo-Übersichtsseite)
* 

Sonstiges:
* Falls CRM-Export nicht funktioniert, wird dies in der Buchungs-Mail für den Service deutlich gemacht

SQL:

ALTER TABLE fewo_lodging DROP only_weekday;
ALTER TABLE fewo_season ADD only_weekday INT DEFAULT NULL;


git-svn-id: http://78.47.251.156/svn/dev/sterntours-3@3359 f459cee4-fb09-11de-96c3-f9c5f16c3c76
This commit is contained in:
uli 2017-11-28 06:16:21 +00:00
parent 45977fd4de
commit 1a0388311e
26 changed files with 485 additions and 254 deletions

View file

@ -4,16 +4,20 @@
{% block body %}
<section class="clearfix">
<div class="content-copy">
<h1>Neues Bild für "{{ lodging.name }}" anlegen</h1>
<h1>Bild für "{{ lodging.name }}" anlegen / bearbeiten</h1>
<form class="st-booking-form" method="post" enctype="multipart/form-data">
{{ form_errors(form) }}
<div id="message"></div>
<div class="form-box">
{{ form_row(form.file, {label: 'Bild'}) }}
{% if is_new %}
{{ form_row(form.file, {label: 'Bild'}) }}
{% else %}
<div>
<img src="{{ asset('uploads/images/' ~ image_file_name) }}" alt="{{ image.description }}" style="width:456px;height:151px;" >
</div>
{% endif %}
{{ form_row(form.fileName, {label: 'Name'}) }}
{{ form_row(form.description, {label: 'Beschreibung'}) }}
@ -25,7 +29,8 @@
</button>
</div>
</div><!-- end form-box -->
{{ form_rest(form) }}
{{ form_widget(form._token) }}
</form>
<a href="{{ '/admin/fewo/lodgings/' ~ lodging.id }}"
class="btn btn-primary"

View file

@ -0,0 +1,35 @@
{% extends 'admin.html.twig' %}
{% form_theme form 'default/form/theme.html.twig' %}
{% block body %}
<section class="clearfix">
<div class="content-copy">
<h1>Mehrere Bilder für "{{ lodging.name }}" hochladen</h1>
<form class="st-booking-form" method="post" enctype="multipart/form-data">
{{ form_errors(form) }}
<div class="form-box">
{{ form(form) }}
<br/>
<div class="col-md-12 col-sm-12 col-xs-12">
<button type="submit" value="SEND" id="submit"
class="btn btn-primary btn-lg border-radius"
>
Speichern
</button>
</div>
</div>
</form>
<a href="{{ '/admin/fewo/lodgings/' ~ lodging.id }}"
class="btn btn-primary"
rel="nofollow"
>
Zurück
</a>
</div>
</section>
{% endblock body %}

View file

@ -24,7 +24,6 @@
{{ form_row(form.city, {'label': 'Ort'}) }}
{{ form_row(form.maximumPersons, {'label': 'Maximale Personenanzahl'}) }}
{{ form_row(form.deposit, {'label': 'Kaution'}) }}
{{ form_row(form.onlyWeekday, {'label': 'Exklusiver Wochentag'}) }}
{#
<div class="checkbox">
@ -52,13 +51,17 @@
<tbody>
{% for image in lodging.images %}
<tr>
<td><img src="{{ asset('uploads/images/' ~ image.file) }}" alt="{{ image.description }}" style="width:228px;height:128px;" ></td>
<td><img src="{{ asset('uploads/images/' ~ image.file) }}" alt="{{ image.description }}" style="width:228px;height:75px;" ></td>
<td>{{ image.fileName }}</td>
<td>{{ image.description }}</td>
<td>
<a href="{{ '/admin/fewo/lodgings/' ~ lodging.id ~ '/images/' ~ image.id }}"
class="btn btn-primary"
>
Bearbeiten
</a>
<a href="{{ '/admin/fewo/lodgings/' ~ lodging.id ~ '/images/' ~ image.id ~ '/delete' }}"
class="btn btn-primary"
rel="nofollow"
>
Löschen
</a>
@ -71,12 +74,12 @@
<h4>Keine Bilder vorhanden</h4>
{% endif %}
<a href="{{ '/admin/fewo/lodgings/' ~ lodging.id ~ '/images/new' }}"
class="btn btn-primary"
rel="nofollow"
>
<a href="{{ '/admin/fewo/lodgings/' ~ lodging.id ~ '/images/new' }}" class="btn btn-primary">
Hinzufügen
</a>
<a href="/admin/fewo/lodgings/{{ lodging.id }}/images/multi-upload" class="btn btn-primary">
Mehrere Bilder hochladen
</a>
<br><br>

View file

@ -1,9 +1,9 @@
<table class="table">
{% for monthIndex in 0..11 %}
{% for calendar_month in calendar %}
<thead>
<tr>
<th colspan="7">
{{ calendar[monthIndex]['monthName'] }} {{ calendar[monthIndex]['year'] }}
{{ calendar_month['monthName'] }} {{ calendar_month['year'] }}
</th>
</tr>
<tr>
@ -21,7 +21,7 @@
<tr>
{% for dayIndex in 0..6 %}
{# @var dayState \AppBundle\Util\CalendarDayState #}
{% set dayState = calendar[monthIndex]['data'][(weekIndex * 7) + dayIndex] %}
{% set dayState = calendar_month['data'][(weekIndex * 7) + dayIndex] %}
{% set is_past_date = dayState.date is not empty and date(dayState.date) < date('now') %}
@ -47,7 +47,7 @@
{{ dayState.day }}
</a>
{% elseif dayState.isBookable and not is_past_date %}
<a href="/admin/fewo/lodgings/{{ lodging.id }}/reservations/new/{{ dayState.day < 10 ? '0':'' }}{{dayState.day}}{{ calendar[monthIndex]['monthNumber'] < 10 ? '0':'' }}{{calendar[monthIndex]['monthNumber']}}{{calendar[monthIndex]['year']}}">
<a href="/admin/fewo/lodgings/{{ lodging.id }}/reservations/new/{{ dayState.day < 10 ? '0':'' }}{{dayState.day}}{{ calendar_month['monthNumber'] < 10 ? '0':'' }}{{calendar_month['monthNumber']}}{{calendar_month['year']}}">
{{ dayState.day }}
</a>
{% elseif dayState.day == 0 %}

View file

@ -24,7 +24,6 @@
{{ form_row(form.city, {label: 'Ort'}) }}
{{ form_row(form.maximumPersons, {label: 'Maximale Personenanzahl'}) }}
{{ form_row(form.deposit, {label: 'Kaution'}) }}
{{ form_row(form.onlyWeekday, {label: 'Exklusiver Wochentag'}) }}
{#
<div class="checkbox">

View file

@ -18,8 +18,7 @@
{{ form_row(form.toDate, {label: 'Bis'}) }}
{{ form_row(form.minimumStay, {label: 'Mindestbelegung in Tagen'}) }}
{{ form_row(form.description, {label: 'Beschreibung'}) }}
{{ form_row(form.onlyWeekday, {label: 'Exklusiver Wochentag'}) }}
<div class="col-md-12 col-sm-12 col-xs-12">
<button type="submit" value="SEND" id="submit"

View file

@ -18,7 +18,7 @@
{{ form_row(form.toDate, {label: 'Bis'}) }}
{{ form_row(form.minimumStay, {label: 'Mindestbelegung in Tagen'}) }}
{{ form_row(form.description, {label: 'Beschreibung'}) }}
{{ form_row(form.onlyWeekday, {label: 'Exklusiver Wochentag'}) }}
<div class="col-md-12 col-sm-12 col-xs-12">

View file

@ -1,11 +1,11 @@
<div class="row">
{% for monthIndex in 0..11 %}
{% for calendar_month in calendar %}
<div class="col-xs-12 col-sm-6">
<table class="table calendar-table">
<thead>
<tr>
<th colspan="7">
{{ calendar[monthIndex]['monthName'] }} {{ calendar[monthIndex]['year'] }}
{{ calendar_month['monthName'] }} {{ calendar_month['year'] }}
</th>
</tr>
<tr>
@ -23,7 +23,7 @@
<tr>
{% for dayIndex in 0..6 %}
{# @var dayState \AppBundle\Util\CalendarDayState #}
{% set dayState = calendar[monthIndex]['data'][(weekIndex * 7) + dayIndex] %}
{% set dayState = calendar_month['data'][(weekIndex * 7) + dayIndex] %}
{% set is_past_date = dayState.date is not empty and date(dayState.date) < date('now') %}
@ -43,7 +43,7 @@
<td class="{{ cell_class }}">
{% if dayState.isBookable and not is_past_date %}
<a href="{{ page.urlPath }}/buchen?pnr={{ dayState.price.id }}&fd={{ dayState.day < 10 ? '0':'' }}{{dayState.day}}{{ calendar[monthIndex]['monthNumber'] < 10 ? '0':'' }}{{calendar[monthIndex]['monthNumber']}}{{calendar[monthIndex]['year']}}"
<a href="{{ page.urlPath }}/buchen?pnr={{ dayState.price.id }}&fd={{ dayState.day < 10 ? '0':'' }}{{dayState.day}}{{ calendar_month['monthNumber'] < 10 ? '0':'' }}{{calendar_month['monthNumber']}}{{calendar_month['year']}}"
style="color: #80B176;"
rel="nofollow"
>

View file

@ -63,7 +63,7 @@
<tr>
<td>{{ form_label(form.toDate, 'Reiseende') }}</td>
<td>
{{ form_widget(form.toDate) }}
{{ form_widget(form.toDate, {attr: {'data-allowed-dates': allowed_to_dates}}) }}
{{ form_errors(form.toDate) }}
</td>
</tr>
@ -132,6 +132,10 @@
{{ form_field_pho(form.city, 'Ort') }}
</div>
<div class="form-group col-md-12 col-sm-12 col-xs-12">
{{ form_field_pho(form.nation, 'Land') }}
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
{{ form_field_pho(form.phone, 'Telefon tagsüber') }}
</div>

View file

@ -24,10 +24,13 @@ use AppBundle\Service\FileManager;
use AppBundle\Util;
use Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional\FileSystemCacheTest;
use Doctrine\ORM\EntityManager;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\HttpFoundation\File;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
@ -159,17 +162,17 @@ class AdminController extends Controller
$lodging = null;
$calendarUtil = $this->container->get('app.lodging_calendar_util');
$calendarService = $this->container->get('app.lodging_calendar_util');
$lodging = $fewoLodgingRepo->find($lodgingId);
$reservations = $calendarUtil->getReservations($lodging);
$reservations = $calendarService->getReservations($lodging);
$paddedCalendar = $calendarUtil->getCalendarWithPadding();
$calendar = $calendarUtil->getCalendar();
$paddedCalendar = $calendarService->getCalendarWithPadding($calendarService->getMinCalendarEntriesByLodging($lodging));
$calendar = $calendarService->getCalendar($lodging);
if (count($lodging->getPrices()->toArray()) != 0)
{
$calendar = $calendarUtil->getCalendarWithReservations($lodging);
$calendar = $calendarUtil->mergeWithPaddedCalendar($calendar, $paddedCalendar);
$calendar = $calendarService->createCalendarAndFillDayStates($lodging);
$calendar = $calendarService->mergeWithPaddedCalendar($calendar, $paddedCalendar);
} else {
$calendar = $paddedCalendar;
}
@ -230,16 +233,13 @@ class AdminController extends Controller
$lodging = $fewoLodgingRepo->find($lodgingId);
$calendarUtil = $this->container->get('app.lodging_calendar_util');
$calendarService = $this->container->get('app.lodging_calendar_util');
$reservations = $calendarUtil->getReservations($lodging);
$paddedCalendar = $calendarUtil->getCalendarWithPadding();
$calendar = $calendarUtil->getCalendar();
$paddedCalendar = $calendarService->getCalendarWithPadding($calendarService->getMinCalendarEntriesByLodging($lodging));
if (count($lodging->getPrices()->toArray()) != 0)
{
$calendar = $calendarUtil->getCalendarWithReservations($lodging);
$calendar = $calendarUtil->mergeWithPaddedCalendar($calendar, $paddedCalendar);
$calendar = $calendarService->createCalendarAndFillDayStates($lodging);
$calendar = $calendarService->mergeWithPaddedCalendar($calendar, $paddedCalendar);
} else {
$calendar = $paddedCalendar;
}
@ -771,51 +771,133 @@ class AdminController extends Controller
return $this->redirect('/admin/fewo/seasons');
}
/**
* @Route("/admin/fewo/lodgings/{lodgingId}/images/multi-upload", requirements={"lodgingId": "\d+"})
*/
public function adminFewoImageMultiUploadAction(Request $request, $lodgingId)
{
$em = $this->getEntityManager();
$lodging = $em->getRepository('AppBundle:FewoLodging')->find($lodgingId);
if (!$lodging)
{
throw new HttpException(404, 'Unbekannte lodging-ID: '. $lodgingId);
}
$form = $this->createForm(FileType::class, null, [
'multiple' => true
]);
if ($request->getMethod() == 'POST')
{
$form->handleRequest($request);
if ($form->isValid())
{
$uploader = $this->container->get('app.image_uploader');
foreach ($form->getData() as $uploadedFile)
{
$image = new FewoLodgingImage();
$image->setFile($uploadedFile);
$image->setLodging($lodging);
$image->setFileName('');
$image->setDescription('');
$em->persist($image);
$em->flush();
}
return $this->redirect('/admin/fewo/lodgings/'. $lodging->getId());
}
}
return $this->render('default/admin/fewoImageMultiUpload.html.twig', [
'form' => $form->createView(),
'lodging' => $lodging,
]);
}
/**
* @Route("/admin/fewo/lodgings/{lodgingId}/images/new", requirements={"lodgingId": "\d+"})
*/
public function adminFewoNewImageAction(Request $request, $lodgingId)
{
$lodging = $this->getEntityManager()->getRepository('AppBundle:FewoLodging')->find($lodgingId);
if (!$lodging)
{
throw new HttpException(404, 'Unbekannte lodging-ID: '. $lodgingId);
}
return $this->processImageForm($request, null, $lodging);
}
/**
* @Route("/admin/fewo/lodgings/{lodgingId}/images/{imageId}", requirements={"lodgingId": "\d+", "imageId": "\d+"})
*/
public function adminFewoEditImageAction(Request $request, $lodgingId, $imageId)
{
/** @var FewoLodgingImage $image */
$image = $this->getEntityManager()->getRepository('AppBundle:FewoLodgingImage')->find($imageId);
if (!$image || $image->getLodging()->getId() != $lodgingId)
{
throw new HttpException(404, 'Unbekannte FewoLodgingImage-ID oder lodging-ID passt nicht: '.
$imageId .' / '. $lodgingId);
}
return $this->processImageForm($request, $image);
}
/**
* @param Request $request
* @param FewoLodgingImage $image
* @param FewoLodging|null $lodging
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function processImageForm(Request $request, $image, $lodging = null)
{
$em = $this->getEntityManager();
$fewoLodgingRepo = $em->getRepository('AppBundle:FewoLodging');
$lodging = $fewoLodgingRepo->find($lodgingId);
if ($image == null)
{
$isNew = true;
}
else
{
$isNew = false;
$lodging = $image->getLodging();
$imageFileName = $image->getFile();
}
$image = null;
if ($request->getMethod() != 'POST')
if ($request->getMethod() != 'POST' && $image == null)
{
$image = new FewoLodgingImage();
}
$form = $this->createForm(FewoLodgingImageType::class, $image, [
]);
$form = $this->createForm(FewoLodgingImageType::class, $image);
if ($request->getMethod() == 'POST')
{
$form->handleRequest($request);
$image = $form->getData();
}
if ($request->getMethod() == 'POST' && $form->isValid())
{
if($lodging == null)
$image = $form->getData();
if ($isNew)
{
return $this->redirect('/admin/fewo/lodgings');
$image->setLodging($lodging);
}
else
{
$image->setFile($imageFileName);
}
$image->setLodging($lodging);
$em->persist($image);
$em->flush();
return $this->redirect('/admin/fewo/lodgings/'.$lodgingId);
return $this->redirect('/admin/fewo/lodgings/'. ($lodging->getId()));
}
return $this->render('default/admin/imagesNew.html.twig', [
return $this->render('default/admin/fewoImage.html.twig', [
'form' => $form->createView(),
'lodging' => $lodging,
'is_new' => $isNew,
'image_file_name' => $isNew ? null : $imageFileName,
'image' => $image,
]);
}

View file

@ -93,7 +93,14 @@ class BookingController extends Controller
$em->flush();
$crmBookingUrl = $this->get('app.booking_exporter')->process($bookingRequest, $travelDate, $bookingPriceInfo);
$crmBookingUrl = preg_replace('/\\/api/', '', $crmBookingUrl) .'/edit';
if (!$crmBookingUrl)
{
$crmBookingUrl = '[CRM-EXPORT FEHLGESCHLAGEN]';
}
else
{
$crmBookingUrl = preg_replace('/\\/api/', '', $crmBookingUrl).'/edit';
}
$this->get('mailer')->send(\Swift_Message::newInstance()
->setSubject('Ihr Buchungsauftrag bei STERN TOURS')

View file

@ -49,11 +49,16 @@ class CmsController extends Controller
public function overviewAction(Page $page)
{
return $this->render('default/pages/cms/overview.html.twig', [
$settings = $page->getCmsSettings();
if (!is_array($settings))
{
$settings = [];
}
return $this->render('default/pages/cms/overview.html.twig', array_merge($settings, [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'page' => $page,
// TODO
]);
]));
}
public function travelProgramOverviewAction(Page $page)
@ -126,17 +131,14 @@ class CmsController extends Controller
public function fewoLodgingAction(Page $page)
{
//$calendarUtil = new Util\LodgingCalendarUtil();
$calendarUtil = $this->container->get('app.lodging_calendar_util');
//$fewoLodgingRepo = $this->getEntityManager()->getRepository('AppBundle:FewoLodging');
$calendarService = $this->container->get('app.lodging_calendar_util');
$lodging = $page->getFewoLodging();
$paddedCalendar = $calendarUtil->getCalendarWithPadding();
$calendar = $calendarUtil->getCalendar();
$paddedCalendar = $calendarService->getCalendarWithPadding($calendarService->getMinCalendarEntriesByLodging($lodging));
if (count($lodging->getPrices()->toArray()) != 0)
{
$calendar = $calendarUtil->getCalendarWithReservations($lodging);
$calendar = $calendarUtil->mergeWithPaddedCalendar($calendar, $paddedCalendar);
$calendar = $calendarService->createCalendarAndFillDayStates($lodging);
$calendar = $calendarService->mergeWithPaddedCalendar($calendar, $paddedCalendar);
} else {
$calendar = $paddedCalendar;
}

View file

@ -43,9 +43,7 @@ class FewoBookingController extends Controller
*/
public function indexAction(Page $fewoTravelProgramPage, $action, Request $request)
{
//$calendarUtils = new Util\LodgingCalendarUtil();
$calendarUtil = $this->container->get('app.lodging_calendar_util');
$calendarService = $this->container->get('app.lodging_calendar_util');
$em = $this->getEntityManager();
//$fewoLodgingRepo = $em->getRepository('AppBundle:FewoLodging');
$fewoPriceRepo = $em->getRepository('AppBundle:FewoPrice');
@ -66,7 +64,7 @@ class FewoBookingController extends Controller
$season = $price->getSeason();
$minimumStay = $season->getMinimumStay();
$fromDate = $calendarUtil->convertDate($fromDate);
$fromDate = $calendarService->convertDate($fromDate);
$fromDateTime = new \DateTime($fromDate);
$toDate = '';
@ -82,16 +80,59 @@ class FewoBookingController extends Controller
$fewoBookingRequest->setLodging($lodging);
$fewoBookingRequest->setPrice($price);
$allowedToDateStrs = [];
if (!$lodging->getPrices()->isEmpty())
{
$nightsSinceFromDate = 0;
$calendar = $calendarService->createCalendarAndFillDayStates($lodging);
$areAllowedToDatesCollected = false;
foreach ($calendar as $calendarMonth)
{
if ($calendarMonth['year'] < $fromDateTime->format('Y') ||
$calendarMonth['monthNumber'] < $fromDateTime->format('n'))
{
continue;
}
/** @var Util\CalendarDayState[] $dayStates */
$dayStates = $calendarMonth['data'];
$i = 0;
if ($calendarMonth['year'] == $fromDateTime->format('Y') &&
$calendarMonth['monthNumber'] == $fromDateTime->format('n'))
{
$i = intval($fromDateTime->format('j')) - 1;
}
for (; $i < count($dayStates); ++$i)
{
if ($nightsSinceFromDate >= $season->getMinimumStay())
{
$dayState = $dayStates[$i];
if ($dayState->getIsInSeason() && (!$dayState->getIsReserved() ||
($dayState->getIsReservationBegin() && !$dayState->getIsReservationEnd())))
{
$allowedToDateStrs[] = $dayState->getDate()->format('Y-m-d');
}
else
{
$areAllowedToDatesCollected = true;
break;
}
}
++$nightsSinceFromDate;
}
if ($areAllowedToDatesCollected)
{
break;
}
}
if (!empty($allowedToDateStrs))
{
$toDate = $allowedToDateStrs[0];
}
}
if ($request->getMethod() != 'POST')
{
$dateAppendix = " + ".($minimumStay - 1)." day";
if($minimumStay > 1)
{
$dateAppendix= $dateAppendix."s";
}
$toDate = date('d.m.Y', strtotime($fromDate.$dateAppendix));
$fewoBookingRequest->setFromDate($fromDate);
$fewoBookingRequest->setToDate($toDate);
$fewoBookingRequest->setNumberDays($minimumStay);
@ -144,7 +185,14 @@ class FewoBookingController extends Controller
$em->flush();
$crmBookingUrl = $this->get('app.fewo_booking_exporter')->process($fewoBookingRequest, $lodging, $price);//, $travelDate, $bookingPriceInfo);
$crmBookingUrl = preg_replace('/\\/api/', '', $crmBookingUrl) . '/edit';
if (!$crmBookingUrl)
{
$crmBookingUrl = '[CRM-EXPORT FEHLGESCHLAGEN]';
}
else
{
$crmBookingUrl = preg_replace('/\\/api/', '', $crmBookingUrl) . '/edit';
}
$this->get('mailer')->send(\Swift_Message::newInstance()
->setSubject('Ihr FeWo-Buchungsauftrag bei STERN TOURS')
@ -208,9 +256,9 @@ class FewoBookingController extends Controller
'fewo_price' => $price,
'total_price' => $totalPrice,
'total_price_per_night' => $perDayTotalPrice,
'allowed_to_dates' => implode(';', $allowedToDateStrs),
'page' => $fewoTravelProgramPage,
'show_search_sidebar_widget' => false,
]);
}

View file

@ -125,13 +125,6 @@ class FewoLodging
//------------------------------------------------------------------------------------------------------------------
// Kalender
/**
* @var integer
*
* @ORM\Column(name="only_weekday", type="integer", nullable=true)
*/
private $onlyWeekday;
/**
* @var \AppBundle\Entity\FewoReservation
*
@ -411,30 +404,6 @@ class FewoLodging
return $this->deposit;
}
/**
* Set onlyWeekday
*
* @param integer $onlyWeekday
*
* @return FewoLodging
*/
public function setOnlyWeekday($onlyWeekday)
{
$this->onlyWeekday = $onlyWeekday;
return $this;
}
/**
* Get onlyWeekday
*
* @return integer
*/
public function getOnlyWeekday()
{
return $this->onlyWeekday;
}
/**
* Set calendarVisible
*

View file

@ -1,6 +1,7 @@
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Query\Expr\OrderBy;
/**
* FewoLodgingRepository
@ -10,8 +11,21 @@ namespace AppBundle\Entity;
*/
class FewoLodgingRepository extends \Doctrine\ORM\EntityRepository
{
public function findWithChoosableSeasons($lodgingId)
/**
* @param FewoLodging $lodging
*
* @return FewoSeason|null
*/
public function findLatestSeasonEndForLodging($lodging)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('s');
$qb->from('AppBundle:FewoSeason', 's');
$qb->innerJoin('s.prices', 'p');
$qb->where($qb->expr()->eq('p.lodging', $lodging->getId()));
$qb->addOrderBy('s.toDate', 'DESC');
$qb->setMaxResults(1);
return $qb->getQuery()->getOneOrNullResult();
}
}

View file

@ -65,6 +65,14 @@ class FewoSeason
* @ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* @var integer
*
* @ORM\Column(name="only_weekday", type="integer", nullable=true)
*/
private $onlyWeekday;
/**
* Constructor
*/
@ -242,4 +250,28 @@ class FewoSeason
return $this->name;
}
/**
* Set onlyWeekday
*
* @param integer $onlyWeekday
*
* @return FewoSeason
*/
public function setOnlyWeekday($onlyWeekday)
{
$this->onlyWeekday = $onlyWeekday;
return $this;
}
/**
* Get onlyWeekday
*
* @return integer
*/
public function getOnlyWeekday()
{
return $this->onlyWeekday;
}
}

View file

@ -1255,13 +1255,13 @@ class Page
/**
* Set cmsSettings
*
* @param string $cmsSettings
* @param mixed $cmsSettings
*
* @return Page
*/
public function setCmsSettings($cmsSettings)
{
$this->cmsSettings = $cmsSettings;
$this->cmsSettings = is_array($cmsSettings) ? json_encode($cmsSettings) : $cmsSettings;
return $this;
}
@ -1269,10 +1269,15 @@ class Page
/**
* Get cmsSettings
*
* @return string
* @return mixed
*/
public function getCmsSettings()
{
return $this->cmsSettings;
$ret = json_decode($this->cmsSettings, true);
if (empty($ret) || !is_array($ret))
{
return $this->cmsSettings;
}
return $ret;
}
}

View file

@ -32,6 +32,7 @@ class Traveler
/**
* @Assert\NotBlank()
* @Assert\Date()
*/
private $birthDate;

View file

@ -50,17 +50,17 @@ class FewoBookingSternToursCrmExporter extends SternToursCrmExporter
$resp = $this->httpPost('lead', ['lead' => [
'customerForm' => [
'salutation_id' => $fewoBookingRequest->getSalutation(),
'name' => $fewoBookingRequest->getLastName(),
'name' => $fewoBookingRequest->getLastName(), // required
'firstname' => $fewoBookingRequest->getFirstName(),
'street' => $fewoBookingRequest->getStreetAddress(),
'zip' => $fewoBookingRequest->getZipCode(),
'city' => $fewoBookingRequest->getCity(),
'country_id' => $fewoBookingRequest->getNation(),
'country_id' => $fewoBookingRequest->getNation(), // required
'phone' => $fewoBookingRequest->getPhone(),
'fax' => $fewoBookingRequest->getFax(),
'email' => $fewoBookingRequest->getEmail()
],
'request_date' => (new \DateTime())->format('Y-m-d'),
'request_date' => (new \DateTime())->format('Y-m-d'), // required
'sf_guard_user_id' => self::API_USER_ID,
'status_id' => 7, // 'gebucht'
'travelperiod_start' => $fewoBookingRequest->getFromDate()->format('Y-m-d'),
@ -73,6 +73,9 @@ class FewoBookingSternToursCrmExporter extends SternToursCrmExporter
'remarks' => $fewoBookingRequest->getNotes()
]]);
//Util::varDump($resp);
//die();
if ($resp['success'])
{
$ret = $this->httpGet($resp['location']);
@ -98,10 +101,10 @@ class FewoBookingSternToursCrmExporter extends SternToursCrmExporter
'branch_id' => 4,
'website_id' => self::WEBSITE_ID,
'title' => $lodging->getName(),
'start_date' => $fewoBookingRequest->getFromDate()->format('Y-m-d'),
'end_date' => $fewoBookingRequest->getToDate()->format('Y-m-d'),
'start_date' => $fewoBookingRequest->getFromDate()->format('Y-m-d'), // required
'end_date' => $fewoBookingRequest->getToDate()->format('Y-m-d'), // required
'pax' => $fewoBookingRequest->getTravelerCount(),
'travel_number' => $lodging->getName()." - ".$price->getSeason()->getName(),
'travel_number' => substr($lodging->getName()." - ".$price->getSeason()->getName(), 0, 30),
'price' => $fewoBookingRequest->getTotalPrice(),
'participant_salutation_id' => $fewoBookingRequest->getSalutation(),

View file

@ -105,6 +105,13 @@ class FewoBookingRequestType extends AbstractType
])
->add('firstName')
->add('lastName')
->add('nation', ChoiceType::class, [
'choices' => self::$NATION_CHOICES,
'constraints' => [
new NotNull(),
new Choice(['choices' => self::$NATION_CHOICES])
]
])
->add('streetAddress')
->add('zipCode')
->add('city')

View file

@ -16,7 +16,7 @@ class FewoLodgingImageType extends AbstractType
{
$builder
->add('file', FileType::class, [
'data_class' => null
])
->add('fileName')
->add('description')

View file

@ -21,18 +21,6 @@ use AppBundle\Entity\FewoSeason;
class FewoLodgingType extends AbstractType
{
public static $WEEKDAY_CHOICES = [
'keiner' => 0,
'Montag' => 1,
'Dienstag' => 2,
'Mittwoch' => 3,
'Donnerstag' => 4,
'Freitag' => 5,
'Samstag' => 6,
'Sonntag' => 7
];
/**
* {@inheritdoc}
@ -65,12 +53,6 @@ class FewoLodgingType extends AbstractType
->add('deposit', null, [
'required' => true,
])
->add('onlyWeekday', ChoiceType::class, [
'choices' => self::$WEEKDAY_CHOICES,
'constraints' => [
new Choice(['choices' => self::$WEEKDAY_CHOICES])
]
])
//->add('calendarVisible')
->add('type', EntityType::class, [
'placeholder' => '(Bitte wählen) *',

View file

@ -21,6 +21,17 @@ use Symfony\Component\Validator\Constraints\NotNull;
class FewoSeasonType extends AbstractType
{
public static $WEEKDAY_CHOICES = [
'beliebig' => NULL,
'Sonntag' => 0,
'Montag' => 1,
'Dienstag' => 2,
'Mittwoch' => 3,
'Donnerstag' => 4,
'Freitag' => 5,
'Samstag' => 6,
];
/**
* {@inheritdoc}
*/
@ -38,6 +49,12 @@ class FewoSeasonType extends AbstractType
])
->add('description')
->add('onlyWeekday', ChoiceType::class, [
'choices' => self::$WEEKDAY_CHOICES,
'constraints' => [
new Choice(['choices' => self::$WEEKDAY_CHOICES])
]
])
;
}

View file

@ -514,7 +514,24 @@ jQuery(document).ready(function($) {
dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'],
firstDay: 1
firstDay: 1,
beforeShowDay: function(date) {
var allowedDateStrListStr = $(this).attr('data-allowed-dates');
if (allowedDateStrListStr)
{
var allowedDatesStrList = allowedDateStrListStr.split(';');
for (var i = 0; i < allowedDatesStrList.length; ++i)
{
var allowedDate = new Date(allowedDatesStrList[i]);
if (allowedDate.getDate() === date.getDate() && allowedDate.getMonth() === date.getMonth() &&
allowedDate.getYear() === date.getYear())
{
return [true, '', ''];
}
}
return [false, '', '']
}
}
});
function initAutoEndDate(startTxt$, endTxt$)

View file

@ -14,11 +14,56 @@ class FileManager
$this->targetDir = $targetDir;
}
const MAX_DEST_WIDTH = 848;
const MAX_DEST_HEIGHT = 280;
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$baseFileName = md5(uniqid());
$fileName = $baseFileName.'.'.$file->guessExtension();
$file->move($this->getTargetDir(), $fileName);
$path = $this->getTargetDir() .'/'. $fileName;
$imgInfo = getimagesize($path);
if ($imgInfo !== false)
{
$srcWidth = $imgInfo[0];
$srcHeight = $imgInfo[1];
switch ($imgInfo[2]) {
case IMAGETYPE_GIF : $src = imagecreatefromgif($path); break;
case IMAGETYPE_JPEG : $src = imagecreatefromjpeg($path); break;
case IMAGETYPE_PNG : $src = imagecreatefrompng($path); break;
// default: // #TODO Warning
}
if (isset($src))
{
$tmp = imagecreatetruecolor(848, 280);
$destWidth = self::MAX_DEST_HEIGHT * $srcWidth / $srcHeight;
if ($destWidth < self::MAX_DEST_WIDTH)
{
$destWidth = self::MAX_DEST_WIDTH;
$destHeight = $destWidth * $srcHeight / $srcWidth;
$destX = 0;
$destY = ($destHeight - self::MAX_DEST_HEIGHT) / 2;
}
else
{
$destHeight = self::MAX_DEST_HEIGHT;
$destX = ($destWidth - self::MAX_DEST_WIDTH) / 2;
$destY = 0;
}
imagecopyresampled($tmp, $src, 0, 0, $destX, $destY,
$destWidth, $destHeight, $srcWidth, $srcHeight);
$newFileName = md5(uniqid()) .'.jpg';
if (imagejpeg($tmp, $this->getTargetDir() .'/'. $newFileName, 80))
{
$filesystem = new Filesystem();
$filesystem->remove($this->getTargetDir() .'/'. $fileName);
return $newFileName;
}
// else #TODO warn
}
}
return $fileName;
}

View file

@ -26,7 +26,7 @@ class LodgingCalendarService
return $month == 2 ? ($year % 4 ? 28 : ($year % 100 ? 29 : ($year % 400 ? 28 : 29))) : (($month - 1) % 7 % 2 ? 30 : 31);
}
public function getCalendarWithPadding()
public function getCalendarWithPadding($months)
{
$calendar = null;
$currentMonth = date('n');
@ -35,7 +35,7 @@ class LodgingCalendarService
setlocale(LC_TIME, "german");
for($m = $currentMonth; $m < ($currentMonth + 12); $m++)
for($m = $currentMonth; $m < ($currentMonth + $months); $m++)
{
if($m < 13)
{
@ -117,9 +117,9 @@ class LodgingCalendarService
}
public function getCalendar()
public function getCalendar($lodging)
{
$months = $this->getMinCalendarEntriesByLodging($lodging);
$calendar = null;
$currentMonth = date('n');
$currentYear = date('Y');
@ -127,7 +127,7 @@ class LodgingCalendarService
setlocale(LC_TIME, "german");
for($m = $currentMonth; $m < ($currentMonth + 12); $m++)
for($m = $currentMonth; $m < ($currentMonth + $months); $m++)
{
if($m < 13)
{
@ -180,6 +180,22 @@ class LodgingCalendarService
return $calendar;
}
/**
* Retrieve needed number of months to generate a calendar which includes all available seasons
*
* @param FewoLodging $lodging
* @return int
*/
public function getMinCalendarEntriesByLodging($lodging)
{
$latestSeason = $this->em->getRepository('AppBundle:FewoLodging')->findLatestSeasonEndForLodging($lodging);
$curMonthDate = new \DateTime();
$curMonthDate->setDate($curMonthDate->format('Y'), $curMonthDate->format('n'), 1);
$seasonMonthDate = new \DateTime($latestSeason->getToDate()->format('Y-m-d'));
$seasonMonthDate->setDate($seasonMonthDate->format('Y'), $seasonMonthDate->format('n'), 1);
return 2 + intval($seasonMonthDate->diff($curMonthDate)->format('%m'));
}
/**
* @param FewoLodging $lodging
@ -426,7 +442,7 @@ class LodgingCalendarService
private function mergeCalendars($calendar, $extensionCalendar, $withFromTo = false)
{
for($i = 0; $i < 12; $i++)
for($i = 0; $i < count($calendar); $i++)
{
/** @var CalendarDayState[] $calendarData */
$calendarData = $calendar[$i]['data'];
@ -584,10 +600,8 @@ class LodgingCalendarService
*/
private function filterReservableDays(FewoLodging $lodging, $calendar)
{
/** @var CalendarDayState[] $potentiallyReservableDays */
$potentiallyReservableDays = [];
$priceId = 0;
/** @var FewoPrice $lastReservablePrice */
$lastReservablePrice = null;
for ($currMonthIndex = 0; $currMonthIndex < count($calendar); $currMonthIndex++)
{
@ -599,140 +613,81 @@ class LodgingCalendarService
{
$currDay = $data[$currDayIndex];
if ($currDay->getPrice() !== $lastReservablePrice && !$currDay->getIsInSeason())
// We passed the season end? => All days in the stack are not reservable, as their minimum stay
// requirement would mean their reservation end is outside a season
if (!$currDay->getIsInSeason())
{
$actuallyReservableDays = $this->processPotentiallyReservableDays($potentiallyReservableDays,
$lastReservablePrice->getSeason()->getMinimumStay());
$this->processRest($actuallyReservableDays, $lastReservablePrice, $calendar);
$potentiallyReservableDays = [];
}
if ($currDay->getIsInSeason() && (!$currDay->getIsReserved() || $currDay->getIsReservationBegin() ||
$currDay->getIsReservationEnd()))
{
$currentDayDate = date("d.m.Y", strtotime($currMonth['year']."-".
$currMonth['monthNumber']."-".$currDay->getDay()));
$potentiallyReservableDays[] = $currentDayDate;
while (!empty($potentiallyReservableDays))
{
$potentiallyReservableDay = array_pop($potentiallyReservableDays);
$potentiallyReservableDay->setIsBookable(false);
}
$currDay->setIsBookable(false);
continue;
}
$lastReservablePrice = $currDay->getIsInSeason() ? $currDay->getPrice() : null;
}
}
if ($lastReservablePrice !== null)
{
$actuallyReservableDays = $this->processPotentiallyReservableDays($potentiallyReservableDays,
$lastReservablePrice->getSeason()->getMinimumStay());
$this->processRest($actuallyReservableDays, $lastReservablePrice, $calendar);
}
return $calendar;
}
$currSeason = $currDay->getPrice()->getSeason();
private function processPotentiallyReservableDays($potentiallyReservableDays, $minimumStay)
{
$coherentDays = [];
$actuallyReservableDays = [];
//$coherentDays[] = $potentiallyReservableDays[0];
for($i = 1; $i < count($potentiallyReservableDays); $i++)
{
if($this->isNextDay($potentiallyReservableDays[$i - 1], $potentiallyReservableDays[$i]))
{
if(count($coherentDays) == 0)
// Process the stack of reservable days and set reservable=true, if their minimum stay requirement could
// be fulfilled. Set reservable=false if there is no chance for the stack entry to be reservable.
// Remove potentially reservable days from the stack for which a final decision has been made.
if (!$currDay->getIsReserved() || $currDay->getIsReservationBegin())
{
$coherentDays[] = $potentiallyReservableDays[$i - 1];
foreach ($potentiallyReservableDays as $k => $potentiallyReservableDay)
{
$nights = intval($potentiallyReservableDay->getDate()->diff($currDay->getDate())->format('%a'));
if ($nights >= $currSeason->getMinimumStay())
{
$potentiallyReservableDay->setIsBookable(true);
unset($potentiallyReservableDays[$k]);
}
elseif ($currDay->getIsReservationBegin())
{
// Reservation begin => We are absolutely sure that this day is not reservable
$potentiallyReservableDay->setIsBookable(false);
unset($potentiallyReservableDays[$k]);
}
}
}
$coherentDays[] = $potentiallyReservableDays[$i];
}
else
{
if(count($coherentDays) < $minimumStay)
// Add the current calendar day to stack, as we need to analyse the following days in order to be able
// to decide if the day is reservable or not.
if ((!$currDay->getIsReserved() || $currDay->getIsReservationEnd()) &&
($currSeason->getOnlyWeekday() == null || $currSeason->getOnlyWeekday() == $currDay->getDate()->format('w')))
{
$coherentDays = [];
$coherentDays[] = $potentiallyReservableDays[$i];
$potentiallyReservableDays[] = $currDay;
}
else
{
$coherentDaysCount = count($coherentDays);
$offset = $coherentDaysCount - ($minimumStay - 1);
$coherentDays = array_splice($coherentDays, 0, $offset);
for($j = 0; $j < count($coherentDays); $j++)
{
$actuallyReservableDays[] = $coherentDays[$j];
}
$coherentDays = [];
$currDay->setIsBookable(false);
}
}
}
if(count($coherentDays) > 0)
// Remaining entries in the stack are definitely not reservable. Otherwise they would have been processed before.
foreach ($potentiallyReservableDays as $potentiallyReservableDay)
{
$coherentDaysCount = count($coherentDays);
$offset = $coherentDaysCount - ($minimumStay - 1);
$coherentDays = array_splice($coherentDays, 0, $offset);
for($j = 0; $j < count($coherentDays); $j++)
{
$actuallyReservableDays[] = $coherentDays[$j];
}
$potentiallyReservableDay->setIsBookable(false);
}
return $actuallyReservableDays;
return $calendar;
}
private function processRest($actuallyReservableDays, FewoPrice $price, &$cleanCalendar)
{
for($currMonthIndex = 0; $currMonthIndex < count($cleanCalendar); $currMonthIndex++)
{
$currMonth = $cleanCalendar[$currMonthIndex];
/** @var CalendarDayState[] $data */
$data = $currMonth['data'];
for($currDayIndex = 0; $currDayIndex < count($data); $currDayIndex++)
{
$currDay = $data[$currDayIndex];
$currentDate = date("d.m.Y", strtotime($currMonth['year']."-".$currMonth['monthNumber']."-".$currDay->getDay()));
if (in_array($currentDate, $actuallyReservableDays))
{
//$data[$currDayIndex]->setIsInSeason(true);
//$data[$currDayIndex]->setIsReserved(false); // todo ?
$data[$currDayIndex]->setIsBookable(true);
//$data[$currDayIndex]->setIsReservationBegin(false);
//$data[$currDayIndex]->setIsReservationEnd(false); // todo ?
//$data[$currDayIndex]->setPrice($price);
$currMonth['marked'] = 1;
}
}
$currMonth['data'] = $data;
$cleanCalendar[$currMonthIndex] = $currMonth;
}
}
/**
* @param FewoLodging $lodging
* @param $calendar
* @return mixed
*
* createCalendarAndFillDayStates
*/
public function getCalendarWithReservations(FewoLodging $lodging)
public function createCalendarAndFillDayStates(FewoLodging $lodging)
{
$today = new \DateTime();
$pricesCalendar = $this->getCalendar();
$reservationsCalendar = $this->getCalendar();
$pricesCalendar = $this->getCalendar($lodging);
$reservationsCalendar = $this->getCalendar($lodging);
$reservations = $lodging->getReservations();
$prices = $lodging->getPrices();
$lodgingId = $lodging->getId();
foreach ($prices as $price)
{
/** @var FewoSeason $season */