Implement daily bookable dates
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-simplexml": "*",
|
||||
"typo3/cms": "^8.7",
|
||||
"typo3/cms-backend": "^8.7",
|
||||
"typo3/cms-belog": "^8.7",
|
||||
|
||||
Generated
+61
-2
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "bf966b3d9286731a2aa6030d0010afed",
|
||||
"content-hash": "0336ff7055dae975d70aea8153e7b798",
|
||||
"packages": [
|
||||
{
|
||||
"name": "christophlehmann/imageoptimizer",
|
||||
@@ -1265,6 +1265,63 @@
|
||||
],
|
||||
"time": "2018-02-06T08:27:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/period",
|
||||
"version": "3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/period.git",
|
||||
"reference": "280b54d737b4d9d804d19f865a6496b82d1cee2a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/period/zipball/280b54d737b4d9d804d19f865a6496b82d1cee2a",
|
||||
"reference": "280b54d737b4d9d804d19f865a6496b82d1cee2a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^1.10",
|
||||
"phpunit/phpunit": "^4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Period\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ignace Nyamagana Butera",
|
||||
"email": "[email protected]",
|
||||
"homepage": "https://github.com/nyamsprod/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A time range immutable value object",
|
||||
"homepage": "http://period.thephpleague.com",
|
||||
"keywords": [
|
||||
"date",
|
||||
"dateinterval",
|
||||
"dateperiod",
|
||||
"datetime",
|
||||
"interval",
|
||||
"range",
|
||||
"time"
|
||||
],
|
||||
"time": "2017-11-17T11:28:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "linkorb/jsmin-php",
|
||||
"version": "1.0.0",
|
||||
@@ -3686,7 +3743,9 @@
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^7.1",
|
||||
"ext-json": "*"
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-simplexml": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
|
||||
+9
-9
@@ -27,19 +27,19 @@ namespace EP\EpProducts\Command;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Service\DatesImportService;
|
||||
use EP\EpProducts\Service\DateImportService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\CommandController;
|
||||
use TYPO3\CMS\Extbase\Service\CacheService;
|
||||
|
||||
class ProductCommandController extends CommandController
|
||||
class DateCommandController extends CommandController
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \EP\EpProducts\Service\DatesImportService
|
||||
* @var \EP\EpProducts\Service\DateImportService
|
||||
*/
|
||||
protected $datesImportService;
|
||||
protected $dateImportService;
|
||||
|
||||
/**
|
||||
* @var CacheService
|
||||
@@ -52,18 +52,18 @@ class ProductCommandController extends CommandController
|
||||
protected $configurationManager;
|
||||
|
||||
/**
|
||||
* @param DatesImportService $datesImportService
|
||||
* @param DateImportService $importService
|
||||
* @param CacheService $cacheService
|
||||
* @param ConfigurationManagerInterface $configurationManager
|
||||
*/
|
||||
public function __construct
|
||||
(
|
||||
DatesImportService $datesImportService,
|
||||
DateImportService $importService,
|
||||
CacheService $cacheService,
|
||||
ConfigurationManagerInterface $configurationManager
|
||||
)
|
||||
{
|
||||
$this->datesImportService = $datesImportService;
|
||||
$this->dateImportService = $importService;
|
||||
$this->cacheService = $cacheService;
|
||||
$this->configurationManager = $configurationManager;
|
||||
}
|
||||
@@ -71,10 +71,10 @@ class ProductCommandController extends CommandController
|
||||
public function importCommand()
|
||||
{
|
||||
$path = GeneralUtility::getFileAbsFileName('fileadmin/xmlexport');
|
||||
if ($this->datesImportService->checkActiveUpload($path)) {
|
||||
if ($this->dateImportService->checkActiveUpload($path)) {
|
||||
$this->outputLine('Import cancelled due to active file upload.');
|
||||
} else {
|
||||
$count = $this->datesImportService->import($path);
|
||||
$count = $this->dateImportService->import($path);
|
||||
$this->outputLine('%d rows imported.', [ $count ]);
|
||||
$settings = $this->getSettings();
|
||||
$resellerPageUid = $settings['resellerExportPageUid'];
|
||||
@@ -58,7 +58,7 @@ class AjaxCalendarController extends ActionController
|
||||
*/
|
||||
public function rangeAction(Hotel $hotel, $year = null, $month = null, $months = 6)
|
||||
{
|
||||
list ($begin, $end) = DateUtility::getDateRange($year, $month, $months);
|
||||
[ $begin, $end ] = DateUtility::getDateRange($year, $month, $months);
|
||||
|
||||
$factory = new Calendar();
|
||||
$factory->getEventManager()->addProvider('contingent', $this->contingentRepository);
|
||||
@@ -88,4 +88,5 @@ class AjaxCalendarController extends ActionController
|
||||
'events' => $events,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace EP\EpProducts\Controller;
|
||||
|
||||
/***************************************************************
|
||||
*
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2016 Björn Fromme <[email protected]>, dreipunktnull
|
||||
*
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\Hotel;
|
||||
use EP\EpProducts\Domain\Model\Product;
|
||||
use EP\EpProducts\Domain\Repository\ContingentRepository;
|
||||
use EP\EpProducts\Service\ContingentDataService;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class AjaxContingentController extends ActionController
|
||||
{
|
||||
/**
|
||||
* @var \EP\EpProducts\Domain\Repository\ContingentRepository
|
||||
*/
|
||||
protected $contingentRepository;
|
||||
/**
|
||||
* @var ContingentDataService
|
||||
*/
|
||||
protected $contingentDataService;
|
||||
|
||||
/**
|
||||
* @param ContingentRepository $contingentRepository
|
||||
* @param ContingentDataService $contingentDataService
|
||||
*/
|
||||
public function __construct
|
||||
(
|
||||
ContingentRepository $contingentRepository,
|
||||
ContingentDataService $contingentDataService
|
||||
)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->contingentRepository = $contingentRepository;
|
||||
$this->contingentDataService = $contingentDataService;
|
||||
}
|
||||
|
||||
public function initializeAction()
|
||||
{
|
||||
if ($this->request->hasArgument('dateFrom')) {
|
||||
$dateFrom = new \DateTime($this->request->getArgument('dateFrom'));
|
||||
$this->request->setArgument('dateFrom', $dateFrom);
|
||||
}
|
||||
if ($this->request->hasArgument('dateTo')) {
|
||||
$dateTo = new \DateTime($this->request->getArgument('dateTo'));
|
||||
$this->request->setArgument('dateTo', $dateTo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Hotel $hotel
|
||||
* @param Product $product
|
||||
* @return string
|
||||
*/
|
||||
public function listAction(Hotel $hotel, Product $product)
|
||||
{
|
||||
$enabled = $this->contingentRepository->getAvailableContingents($hotel, $product);
|
||||
|
||||
return json_encode($enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Hotel $hotel
|
||||
* @param Product $product
|
||||
* @param \DateTime $dateFrom
|
||||
* @param \DateTime $dateTo
|
||||
* @return string
|
||||
*/
|
||||
public function roomsAction(Hotel $hotel, Product $product, \DateTime $dateFrom, \DateTime $dateTo)
|
||||
{
|
||||
$rooms = $this->contingentDataService->getAvailableRooms($dateFrom, $dateTo, $hotel, $product);
|
||||
|
||||
return \json_encode($rooms);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,6 +35,8 @@ use EP\EpProducts\Service\DateService;
|
||||
use EP\EpProducts\Service\FilterService;
|
||||
use EP\EpProducts\Service\ResellerDataService;
|
||||
use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
|
||||
use EP\EpProducts\Utility\DateUtility;
|
||||
use League\Period\Period;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
@@ -90,7 +90,13 @@ class AjaxTableController extends ActionController
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function pricetableAction(Product $product, Hotel $hotel, FilterSettings $filterSettings = null, Date $date = null)
|
||||
public function pricetableAction
|
||||
(
|
||||
Product $product,
|
||||
Hotel $hotel,
|
||||
FilterSettings $filterSettings = null,
|
||||
Date $date = null
|
||||
)
|
||||
{
|
||||
if ($filterSettings === null) {
|
||||
$forcedConceptUid = (int) $this->settings['forcedConceptUid'];
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace EP\EpProducts\Controller;
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Service\ContingentImportService;
|
||||
use EP\EpProducts\Service\DatesImportService;
|
||||
use EP\EpProducts\Service\DateImportService;
|
||||
use EP\EpProducts\Service\LogService;
|
||||
use EP\EpProducts\Service\SnowreportImportService;
|
||||
use TYPO3\CMS\Backend\View\BackendTemplateView;
|
||||
@@ -49,14 +49,9 @@ class BackendController extends ActionController
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var \EP\EpProducts\Service\DatesImportService
|
||||
* @var \EP\EpProducts\Service\DateImportService
|
||||
*/
|
||||
protected $datesImportService;
|
||||
|
||||
/**
|
||||
* @var \EP\EpProducts\Service\ContingentImportService
|
||||
*/
|
||||
protected $contingentImportService;
|
||||
protected $dateImportService;
|
||||
|
||||
/**
|
||||
* @var \EP\EpProducts\Service\SnowreportImportService
|
||||
@@ -69,23 +64,29 @@ class BackendController extends ActionController
|
||||
protected $logService;
|
||||
|
||||
/**
|
||||
* @param DatesImportService $datesImportService
|
||||
* @var ContingentImportService
|
||||
*/
|
||||
protected $contingentImportService;
|
||||
|
||||
/**
|
||||
* @param DateImportService $dateImportService
|
||||
* @param ContingentImportService $contingentImportService
|
||||
* @param SnowreportImportService $snowreportImportService
|
||||
* @param SnowreportImportService $snowreportService
|
||||
* @param LogService $logService
|
||||
*/
|
||||
public function __construct
|
||||
(
|
||||
DatesImportService $datesImportService,
|
||||
DateImportService $dateImportService,
|
||||
ContingentImportService $contingentImportService,
|
||||
SnowreportImportService $snowreportImportService,
|
||||
SnowreportImportService $snowreportService,
|
||||
LogService $logService
|
||||
)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->datesImportService = $datesImportService;
|
||||
|
||||
$this->dateImportService = $dateImportService;
|
||||
$this->contingentImportService = $contingentImportService;
|
||||
$this->snowreportImportService = $snowreportImportService;
|
||||
$this->snowreportImportService = $snowreportService;
|
||||
$this->logService = $logService;
|
||||
}
|
||||
|
||||
@@ -95,14 +96,14 @@ class BackendController extends ActionController
|
||||
public function importProductsAction()
|
||||
{
|
||||
$path = GeneralUtility::getFileAbsFileName('fileadmin/xmlexport');
|
||||
if ($this->datesImportService->checkActiveUpload($path)) {
|
||||
if ($this->dateImportService->checkActiveUpload($path)) {
|
||||
$this->addFlashMessage(
|
||||
'Datenexport aus BusProNet aktiv. Import nicht möglich.',
|
||||
'Import fehlgeschlagen',
|
||||
AbstractMessage::WARNING
|
||||
);
|
||||
} else {
|
||||
$dateCount = $this->datesImportService->import($path);
|
||||
$dateCount = $this->dateImportService->import($path);
|
||||
$this->addFlashMessage($dateCount . ' Termine importiert', 'Import abgechlossen', AbstractMessage::OK);
|
||||
$resellerPageUid = $this->settings['resellerExportPageUid'];
|
||||
$this->cacheService->clearPageCache($resellerPageUid);
|
||||
@@ -153,7 +154,7 @@ class BackendController extends ActionController
|
||||
header('Expires: 0');
|
||||
header('Pragma: public');
|
||||
|
||||
$fh = @fopen( 'php://output', 'w' );
|
||||
$fh = @fopen( 'php://output', 'wb' );
|
||||
|
||||
foreach ($csv as $data) {
|
||||
fputcsv($fh, $data, ';');
|
||||
|
||||
@@ -135,7 +135,7 @@ class ProductController extends ActionController
|
||||
$hotel = $this->hotelRepository->findByIdentifier($hotelUid);
|
||||
}
|
||||
if ($hotel === null) {
|
||||
// Forward to hotel list action in case no single hotel can be determined
|
||||
// Forward to hotel list action in case multiple hotels are assigned to this product
|
||||
if ($product->getHotels()->count() > 1) {
|
||||
$arguments = [
|
||||
'product' => $product,
|
||||
@@ -149,11 +149,11 @@ class ProductController extends ActionController
|
||||
$hotel = reset($product->getHotels()->toArray());
|
||||
}
|
||||
}
|
||||
list($productDateRangeFrom, $productDateRangeTo) = $this->filterService->getProductDateRange($product, $filterSettings);
|
||||
[ $productDateRangeFrom, $productDateRangeTo ] = $this->filterService->getProductDateRange($product, $filterSettings);
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
|
||||
$isProductWithExcludedConcept = ($product->getConcept() !== null) && in_array($product->getConcept()->getUid(), $excludedConceptUids);
|
||||
$isProductWithExcludedConcept = ($product->getConcept() !== null) && \in_array($product->getConcept()->getUid(), $excludedConceptUids, false);
|
||||
$noInfoConceptUids = GeneralUtility::trimExplode(',', $this->settings['noInfoConceptUids']);
|
||||
$isProductWithNoInfoConcept = ($product->getConcept() !== null) && in_array($product->getConcept()->getUid(), $noInfoConceptUids);
|
||||
$isProductWithNoInfoConcept = ($product->getConcept() !== null) && \in_array($product->getConcept()->getUid(), $noInfoConceptUids, false);
|
||||
$this->view->assignMultiple([
|
||||
'filterSettings' => $filterSettings,
|
||||
'hotel' => $hotel,
|
||||
@@ -195,7 +195,7 @@ class ProductController extends ActionController
|
||||
)
|
||||
{
|
||||
$hotels = $this->hotelDataService->getList($product);
|
||||
list ($productDateRangeFrom, $productDateRangeTo) = $this->filterService->getProductDateRange($product, $filterSettings);
|
||||
[ $productDateRangeFrom, $productDateRangeTo ] = $this->filterService->getProductDateRange($product, $filterSettings);
|
||||
$this->view->assignMultiple([
|
||||
'filterSettings' => $filterSettings,
|
||||
'hotels' => $hotels,
|
||||
|
||||
@@ -400,7 +400,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param \DateTime $dateFrom
|
||||
*/
|
||||
public function setDateFrom(\DateTime $dateFrom = null)
|
||||
public function setDateFrom(\DateTimeInterface $dateFrom = null)
|
||||
{
|
||||
$this->dateFrom = $dateFrom;
|
||||
}
|
||||
@@ -416,7 +416,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param \DateTime $dateTo
|
||||
*/
|
||||
public function setDateTo(\DateTime $dateTo = null)
|
||||
public function setDateTo(\DateTimeInterface $dateTo = null)
|
||||
{
|
||||
if ($this->dateFrom !== null && $dateTo !== null && $dateTo < $this->dateFrom) {
|
||||
$dateTo = $this->dateFrom;
|
||||
|
||||
@@ -284,7 +284,7 @@ class Product extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity implements
|
||||
*/
|
||||
public function getBookable()
|
||||
{
|
||||
return $this->bookable;
|
||||
return (bool) $this->bookable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,7 +300,7 @@ class Product extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity implements
|
||||
*/
|
||||
public function isDaytrip()
|
||||
{
|
||||
return $this->daytrip;
|
||||
return (bool) $this->daytrip;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +308,7 @@ class Product extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity implements
|
||||
*/
|
||||
public function setDaytrip($daytrip)
|
||||
{
|
||||
$this->daytrip = $daytrip;
|
||||
$this->daytrip = (bool) $daytrip;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace EP\EpProducts\Domain\Repository;
|
||||
use CalendR\Event\Provider\ProviderInterface;
|
||||
use EP\EpProducts\Domain\Model\Contingent;
|
||||
use EP\EpProducts\Domain\Model\Hotel;
|
||||
use EP\EpProducts\Domain\Model\Product;
|
||||
use EP\EpProducts\Utility\DbUtility;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
@@ -90,4 +91,67 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Hotel $hotel
|
||||
* @param Product $product
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableContingents(Hotel $hotel, Product $product)
|
||||
{
|
||||
$qb = DbUtility::getDbConnection()->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->select('c.date date', 'SUM(c.available) total')
|
||||
->from('tx_epproducts_domain_model_contingent', 'c')
|
||||
->innerJoin('c', 'tx_epproducts_domain_model_daytrip', 'd', 'c.date = d.date AND c.hotel_code = d.hotel_code')
|
||||
->where('c.hotel = :hotelUid')
|
||||
->andWhere('d.product = :productUid')
|
||||
->andWhere('c.available > 0')
|
||||
->setParameters([
|
||||
'hotelUid' => $hotel->getUid(),
|
||||
'productUid' => $product->getUid(),
|
||||
])
|
||||
->groupBy('c.date')
|
||||
->orderBy('c.date')
|
||||
;
|
||||
|
||||
return $qb->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $dateFrom
|
||||
* @param \DateTime $dateTo
|
||||
* @param Hotel $hotel
|
||||
* @param Product $product
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableRooms(\DateTime $dateFrom, \DateTime $dateTo, Hotel $hotel, Product $product)
|
||||
{
|
||||
$qb = DbUtility::getDbConnection()->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->select('c.room_code code', 'c.room_label label', 'c.available', 'c.min_price price',
|
||||
'c.hotel_bus_pro_id hotelBusProId', 'd.bus_pro_id busProId')
|
||||
->from('tx_epproducts_domain_model_contingent', 'c')
|
||||
->innerJoin('c', 'tx_epproducts_domain_model_daytrip', 'd', 'c.date = d.date AND c.hotel_code = d.hotel_code')
|
||||
->where('c.hotel = :hotelUid')
|
||||
->andWhere('d.product = :productUid')
|
||||
->andWhere('c.date >= :dateFrom')
|
||||
->andWhere('c.date <= :dateTo')
|
||||
->andWhere('c.min_price > 0')
|
||||
->setParameters([
|
||||
'hotelUid' => $hotel->getUid(),
|
||||
'productUid' => $product->getUid(),
|
||||
'dateFrom' => $dateFrom->format('Y-m-d'),
|
||||
'dateTo' => $dateTo->format('Y-m-d'),
|
||||
])
|
||||
->groupBy('c.room_code')
|
||||
->orderBy('c.pax')
|
||||
->addOrderBy('c.min_price')
|
||||
;
|
||||
|
||||
return $qb->execute()->fetchAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace EP\EpProducts\Service;
|
||||
|
||||
/***************************************************************
|
||||
*
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2018 Björn Fromme <[email protected]>, dreipunktnull
|
||||
*
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\Hotel;
|
||||
use EP\EpProducts\Domain\Model\Product;
|
||||
use EP\EpProducts\Domain\Repository\ContingentRepository;
|
||||
use EP\EpProducts\Utility\BookingUrlUtility;
|
||||
|
||||
class ContingentDataService
|
||||
{
|
||||
/**
|
||||
* @var ContingentRepository
|
||||
*/
|
||||
protected $contingentRepository;
|
||||
|
||||
/**
|
||||
* @param ContingentRepository $contingentRepository
|
||||
*/
|
||||
public function __construct(ContingentRepository $contingentRepository)
|
||||
{
|
||||
$this->contingentRepository = $contingentRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $dateFrom
|
||||
* @param \DateTime $dateTo
|
||||
* @param Hotel $hotel
|
||||
* @param Product $product
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableRooms(\DateTime $dateFrom, \DateTime $dateTo, Hotel $hotel, Product $product)
|
||||
{
|
||||
$data = $this->contingentRepository->getAvailableRooms($dateFrom, $dateTo, $hotel, $product);
|
||||
|
||||
$rooms = [];
|
||||
|
||||
foreach ($data as $row)
|
||||
{
|
||||
$row['bookingUrl'] = BookingUrlUtility::generateUrl([
|
||||
'bookingId' => $row['busProId'],
|
||||
'hotelId' => $row['hotelBusProId'],
|
||||
'dateEnd' => $dateTo->format('Y-m-d'),
|
||||
]);
|
||||
|
||||
$rooms[] = $row;
|
||||
}
|
||||
|
||||
return $rooms;
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ class ContingentImportService implements SingletonInterface
|
||||
protected function parseContingent(\SimpleXMLElement $xmlContingent)
|
||||
{
|
||||
$hotelCode = (string) $xmlContingent['code'];
|
||||
$sql = 'SELECT uid, groupsch_id FROM tx_epproducts_domain_model_hotel WHERE deleted = 0 AND hidden = 0 AND code = :code';
|
||||
$sql = 'SELECT uid, code, groupsch_id FROM tx_epproducts_domain_model_hotel WHERE deleted = 0 AND hidden = 0 AND code = :code';
|
||||
$hotelData = $this->db->fetchAssoc($sql, ['code' => $hotelCode]);
|
||||
|
||||
// Skip import in case hotel can't be found
|
||||
@@ -162,6 +162,7 @@ class ContingentImportService implements SingletonInterface
|
||||
$this->logger->warning('hotel has no contingents', ['code' => $hotelCode]);
|
||||
return 0;
|
||||
} else {
|
||||
$hotelData['busProId'] = (string) $xmlContingent['idbuspro'];
|
||||
$roomTypes = $this->parseRoomTypes($xmlContingent->unterbringungen);
|
||||
return $this->parseRooms($xmlContingent->kapazitaeten, $hotelData, $roomTypes);
|
||||
}
|
||||
@@ -179,6 +180,7 @@ class ContingentImportService implements SingletonInterface
|
||||
$types[(string) $xmlType['idbuspro']] = [
|
||||
'pax' => (int) $xmlType['pax_max'],
|
||||
'code' => (string) $xmlType['code'],
|
||||
'label' => (string) $xmlType['zimmerbezeichnung'],
|
||||
'ctrl' => (string) $xmlType['code'] === self::ROOMCODE_STATUS,
|
||||
];
|
||||
}
|
||||
@@ -231,12 +233,16 @@ class ContingentImportService implements SingletonInterface
|
||||
$status = Contingent::STATUS_ONREQUEST;
|
||||
}
|
||||
$minPrice = (int) $xmlRoom['abpreis'];
|
||||
$roomLabel = $roomType['label'];
|
||||
$this->db->insert(
|
||||
'tx_epproducts_domain_model_contingent_temp',
|
||||
[
|
||||
'pid' => $this->pid,
|
||||
'hotel' => $hotelData['uid'],
|
||||
'hotel_code' => $hotelData['code'],
|
||||
'hotel_bus_pro_id' => $hotelData['busProId'],
|
||||
'room_code' => $roomCode,
|
||||
'room_label' => $roomLabel,
|
||||
'groupsch_id' => $hotelData['groupsch_id'],
|
||||
'pax' => $pax,
|
||||
'available' => $availablePax,
|
||||
|
||||
+53
-16
@@ -36,7 +36,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Service\CacheService;
|
||||
|
||||
class DatesImportService implements SingletonInterface
|
||||
class DateImportService implements SingletonInterface
|
||||
{
|
||||
const CODE_PSEUDOPRICE = 'PDGS';
|
||||
const CODE_BABYROOM = 'Baby';
|
||||
@@ -174,24 +174,28 @@ class DatesImportService implements SingletonInterface
|
||||
public function createTempTables()
|
||||
{
|
||||
$this->db->exec('CREATE TABLE IF NOT EXISTS tx_epproducts_domain_model_date_temp LIKE tx_epproducts_domain_model_date');
|
||||
$this->db->exec('CREATE TABLE IF NOT EXISTS tx_epproducts_domain_model_daytrip_temp LIKE tx_epproducts_domain_model_daytrip');
|
||||
$this->db->exec('CREATE TABLE IF NOT EXISTS tx_epproducts_domain_model_room_temp LIKE tx_epproducts_domain_model_room');
|
||||
}
|
||||
|
||||
public function clearTables()
|
||||
{
|
||||
$this->db->exec('TRUNCATE TABLE tx_epproducts_domain_model_date');
|
||||
$this->db->exec('TRUNCATE TABLE tx_epproducts_domain_model_daytrip');
|
||||
$this->db->exec('TRUNCATE TABLE tx_epproducts_domain_model_room');
|
||||
}
|
||||
|
||||
public function copyTables()
|
||||
{
|
||||
$this->db->exec('INSERT INTO tx_epproducts_domain_model_date SELECT * FROM tx_epproducts_domain_model_date_temp');
|
||||
$this->db->exec('INSERT INTO tx_epproducts_domain_model_daytrip SELECT * FROM tx_epproducts_domain_model_daytrip_temp');
|
||||
$this->db->exec('INSERT INTO tx_epproducts_domain_model_room SELECT * FROM tx_epproducts_domain_model_room_temp');
|
||||
}
|
||||
|
||||
public function clearTempTables()
|
||||
{
|
||||
$this->db->exec('TRUNCATE TABLE tx_epproducts_domain_model_date_temp');
|
||||
$this->db->exec('TRUNCATE TABLE tx_epproducts_domain_model_daytrip_temp');
|
||||
$this->db->exec('TRUNCATE TABLE tx_epproducts_domain_model_room_temp');
|
||||
}
|
||||
|
||||
@@ -206,7 +210,7 @@ class DatesImportService implements SingletonInterface
|
||||
$dateCount = 0;
|
||||
|
||||
$sql = '
|
||||
SELECT p.uid uid, p.name product_name, p.bus_pro_id bus_pro_id, p.searchable product_searchable,
|
||||
SELECT p.uid uid, p.daytrip daytrip, p.name product_name, p.bus_pro_id bus_pro_id, p.searchable product_searchable,
|
||||
p.detail_page product_detail_page, p.keywords product_keywords, p.teaser product_teaser,
|
||||
p.feature product_feature, p.subline product_subline, p.country country, p.region region, p.city city,
|
||||
cnt.name country_name, cnt.code country_code, cnt.keywords country_keywords, r.name region_name,
|
||||
@@ -224,24 +228,31 @@ class DatesImportService implements SingletonInterface
|
||||
|
||||
// Skip import in case product can't be found
|
||||
if (count($products) === 0) {
|
||||
$this->logger->warning('product not found', ['code' => $code]);
|
||||
$this->logger->warning('product not found or daytrip', ['code' => $code]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach ($products as $product)
|
||||
{
|
||||
if (empty($product['bus_pro_id'])) {
|
||||
$this->db->update(
|
||||
'tx_epproducts_domain_model_product',
|
||||
['bus_pro_id' => $busProId],
|
||||
['uid' => $product['uid']]
|
||||
);
|
||||
}
|
||||
$product['product_fact'] = $this->getTopFactForRecord('tx_epproducts_product_fact_mm', $product['uid']);
|
||||
$product['region_fact'] = $this->getTopFactForRecord('tx_epproducts_region_fact_mm', $product['region']);
|
||||
foreach ($xmlProduct->termin as $xmlDate)
|
||||
{
|
||||
$dateCount += $this->parseDates($xmlDate, $product);
|
||||
// Skip import in case product is daytrip
|
||||
if ($product['daytrip']) {
|
||||
foreach ($xmlProduct->termin as $xmlDate)
|
||||
{
|
||||
$this->parseDaytripDates($xmlDate, $product);
|
||||
}
|
||||
} else {
|
||||
if (empty($product['bus_pro_id'])) {
|
||||
$this->db->update(
|
||||
'tx_epproducts_domain_model_product',
|
||||
['bus_pro_id' => $busProId],
|
||||
['uid' => $product['uid']]
|
||||
);
|
||||
}
|
||||
$product['product_fact'] = $this->getTopFactForRecord('tx_epproducts_product_fact_mm', $product['uid']);
|
||||
$product['region_fact'] = $this->getTopFactForRecord('tx_epproducts_region_fact_mm', $product['region']);
|
||||
foreach ($xmlProduct->termin as $xmlDate)
|
||||
{
|
||||
$dateCount += $this->parseDates($xmlDate, $product);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,6 +292,32 @@ class DatesImportService implements SingletonInterface
|
||||
return $dateCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SimpleXMLElement $xmlDate
|
||||
* @param array $product
|
||||
*/
|
||||
protected function parseDaytripDates(\SimpleXMLElement $xmlDate, array $product)
|
||||
{
|
||||
foreach ($xmlDate->hotel as $xmlHotel)
|
||||
{
|
||||
$hotelCode = (string) $xmlHotel->attributes()['code'];
|
||||
$hotel = $this->hotelMappings[$hotelCode];
|
||||
$dateStart = \DateTime::createFromFormat('d.m.Y', (string) $xmlDate->attributes()['termin'])->format('Y-m-d');
|
||||
$this->db->insert(
|
||||
'tx_epproducts_domain_model_daytrip_temp',
|
||||
[
|
||||
'product' => $product['uid'],
|
||||
'date' => $dateStart,
|
||||
'bus_pro_id' => (int) $xmlDate->attributes()['idbuspro'],
|
||||
'code' => (string) $xmlDate->attributes()['code'],
|
||||
'hotel' => $hotel['uid'],
|
||||
'hotel_bus_pro_id' => (int) $xmlHotel->attributes()['idbuspro'],
|
||||
'hotel_code' => $hotelCode,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $product
|
||||
* @param \SimpleXMLElement $xmlDate
|
||||
@@ -65,7 +65,7 @@ class FilterService implements SingletonInterface
|
||||
*/
|
||||
public function getDefaultFilterSettings($forcedConceptUid = null, $respectBookableDaterange = true)
|
||||
{
|
||||
list($dateFrom, $dateTo) = $this->constrainDateRange($this->getDateRange());
|
||||
[ $dateFrom, $dateTo ] = $this->constrainDateRange($this->getDateRange());
|
||||
if (!$respectBookableDaterange) {
|
||||
$dateFrom = new \DateTime('now');
|
||||
}
|
||||
@@ -160,12 +160,12 @@ class FilterService implements SingletonInterface
|
||||
}
|
||||
|
||||
// Hotel types
|
||||
if (!in_array($row['hotelType'], $hotelTypes)) {
|
||||
if (!\in_array($row['hotelType'], $hotelTypes, false)) {
|
||||
$hotelTypes[] = $row['hotelType'];
|
||||
}
|
||||
|
||||
// Room types
|
||||
if (!in_array($row['roomType'], $roomTypes)) {
|
||||
if (!\in_array($row['roomType'], $roomTypes, false)) {
|
||||
$roomTypes[] = $row['roomType'];
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ class FilterService implements SingletonInterface
|
||||
'uid' => $row['boardUid'],
|
||||
'label' => $row['boardType'],
|
||||
];
|
||||
if (!in_array($boardType, $boardTypes)) {
|
||||
if (!\in_array($boardType, $boardTypes, false)) {
|
||||
$boardTypes[] = $boardType;
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ class FilterService implements SingletonInterface
|
||||
*/
|
||||
public function constrainDateRange(array $dateRange, FilterSettings $filterSettings = null)
|
||||
{
|
||||
list ($dateFrom, $dateTo) = $dateRange;
|
||||
[ $dateFrom, $dateTo ] = $dateRange;
|
||||
if ($filterSettings !== null) {
|
||||
$filterSettingsDateFrom = $filterSettings->getDateFrom();
|
||||
if ($filterSettingsDateFrom !== null && $filterSettingsDateFrom > $dateFrom) {
|
||||
@@ -279,7 +279,7 @@ class FilterService implements SingletonInterface
|
||||
*/
|
||||
public function sanitizeDateRange(FilterSettings $filterSettings)
|
||||
{
|
||||
list($validDateFrom, $validDateTo) = $this->getDateRange();
|
||||
[ $validDateFrom, $validDateTo ] = $this->getDateRange();
|
||||
$dateFrom = $filterSettings->getDateFrom();
|
||||
$dateTo = $filterSettings->getDateTo();
|
||||
if ($dateFrom !== null && $dateTo !== null && $dateFrom >= $dateTo) {
|
||||
@@ -305,7 +305,7 @@ class FilterService implements SingletonInterface
|
||||
{
|
||||
if ($conceptUid) {
|
||||
$conceptUids = $filterSettings->getConceptUids();
|
||||
if (!in_array($conceptUid, $conceptUids)) {
|
||||
if (!\in_array($conceptUid, $conceptUids, false)) {
|
||||
$conceptUids[] = (int) $conceptUid;
|
||||
$filterSettings->setConceptUids($conceptUids);
|
||||
}
|
||||
|
||||
+4
-4
@@ -27,20 +27,20 @@ namespace EP\EpProducts\Task;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Service\DatesImportService;
|
||||
use EP\EpProducts\Service\DateImportService;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||
use TYPO3\CMS\Scheduler\Task\AbstractTask;
|
||||
|
||||
class ImportProductsTask extends AbstractTask
|
||||
class ImportDatesTask extends AbstractTask
|
||||
{
|
||||
public function execute()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
|
||||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||
/** @var \EP\EpProducts\Service\DatesImportService $importService */
|
||||
$importService = $objectManager->get(DatesImportService::class);
|
||||
/** @var \EP\EpProducts\Service\DateImportService $importService */
|
||||
$importService = $objectManager->get(DateImportService::class);
|
||||
$path = GeneralUtility::getFileAbsFileName('fileadmin/xmlexport');
|
||||
if ($importService->checkActiveUpload($path)) {
|
||||
$message = GeneralUtility::makeInstance(FlashMessage::class, 'Import aborted due to active file upload', '', FlashMessage::WARNING);
|
||||
@@ -38,9 +38,9 @@ class ImportSnowreportTask extends AbstractTask
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
|
||||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||
$imporService = $objectManager->get(SnowreportImportService::class);
|
||||
$imporService->downloadReportsXml();
|
||||
$imporService->importSnowReportData();
|
||||
$snowreportService = $objectManager->get(SnowreportImportService::class);
|
||||
$snowreportService->downloadReportsXml();
|
||||
$snowreportService->importSnowReportData();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -24,10 +24,10 @@ return [
|
||||
'iconfile' => 'EXT:ep_theme/Resources/Public/images/icon_ce.svg',
|
||||
],
|
||||
'interface' => [
|
||||
'showRecordFieldList' => 'date,pax,available,status,min_price,hotel,room_code,groupsch_id',
|
||||
'showRecordFieldList' => 'date,pax,available,status,min_price,hotel,room_code,room_label,groupsch_id',
|
||||
],
|
||||
'types' => [
|
||||
'1' => ['showitem' => 'date, pax, available, status, min_price, hotel, room_code, groupsch_id'],
|
||||
'1' => ['showitem' => 'date, pax, available, status, min_price, hotel, room_code, room_label, groupsch_id'],
|
||||
],
|
||||
'palettes' => [],
|
||||
'columns' => [
|
||||
@@ -203,5 +203,14 @@ return [
|
||||
'eval' => 'trim'
|
||||
],
|
||||
],
|
||||
'room_label' => [
|
||||
'exclude' => 0,
|
||||
'label' => 'Zimmerbezeichnung',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
'eval' => 'trim'
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -118,6 +118,10 @@ tx_epproducts_ajax_json {
|
||||
AjaxCalendar {
|
||||
1 = range
|
||||
}
|
||||
AjaxContingent {
|
||||
1 = list
|
||||
2 = rooms
|
||||
}
|
||||
AjaxWatchlist {
|
||||
1 = list
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ if (!defined('TYPO3_MODE')) {
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][$_EXTKEY] =
|
||||
\EP\EpProducts\Hook\Tcemain::class;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers']['ep_products_import'] =
|
||||
\EP\EpProducts\Command\ProductCommandController::class;
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers']['ep_products_dates'] =
|
||||
\EP\EpProducts\Command\DateCommandController::class;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers']['ep_products_snow_report'] =
|
||||
\EP\EpProducts\Command\SnowreportCommandController::class;
|
||||
@@ -15,9 +15,9 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers']['ep_p
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers']['ep_products_contingent'] =
|
||||
\EP\EpProducts\Command\ContingentCommandController::class;
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\Task\ImportProductsTask::class] = [
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\Task\ImportDatesTask::class] = [
|
||||
'extension' => $_EXTKEY,
|
||||
'title' => 'EP Produkt Import',
|
||||
'title' => 'EP Reisedaten Import',
|
||||
'description' => 'Importiert Reisedaten aus busProNet',
|
||||
];
|
||||
|
||||
@@ -327,9 +327,9 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\T
|
||||
'AjaxDate' => 'dates',
|
||||
'AjaxFilterpanel' => 'autocomplete',
|
||||
'AjaxSearchbar' => 'options,reset',
|
||||
'AjaxContent' => 'country,region,city,hotel,product,journey,webcam',
|
||||
'AjaxContingent' => 'list,rooms',
|
||||
'AjaxTable' => 'pricetable,pricetableHtml,eventPricetable',
|
||||
'AjaxCalendar' => 'range',
|
||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||
'AjaxWatchlist' => 'list',
|
||||
],
|
||||
[
|
||||
@@ -339,7 +339,8 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\T
|
||||
'AjaxSearchbar' => 'options,reset',
|
||||
'AjaxContent' => 'webcam',
|
||||
'AjaxTable' => 'pricetable,pricetableHtml,eventPricetable',
|
||||
'AjaxCalendar' => 'range',
|
||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||
'AjaxContingent' => 'list,rooms',
|
||||
'AjaxWatchlist' => 'list',
|
||||
]
|
||||
);
|
||||
|
||||
@@ -947,7 +947,10 @@ CREATE TABLE tx_epproducts_domain_model_contingent (
|
||||
available int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
date date DEFAULT '0000-00-00',
|
||||
hotel int(11) unsigned DEFAULT '0',
|
||||
hotel_code varchar(64) DEFAULT '' NOT NULL,
|
||||
hotel_bus_pro_id varchar(64) DEFAULT '' NOT NULL,
|
||||
room_code varchar(64) DEFAULT '' NOT NULL,
|
||||
room_label varchar(64) DEFAULT '' NOT NULL,
|
||||
status tinyint(4) unsigned DEFAULT '0' NOT NULL,
|
||||
min_price int(11) DEFAULT '0' NOT NULL,
|
||||
groupsch_id varchar(8) DEFAULT '' NOT NULL,
|
||||
@@ -982,6 +985,51 @@ CREATE TABLE tx_epproducts_domain_model_contingent (
|
||||
|
||||
);
|
||||
|
||||
#
|
||||
# Table structure for table 'tx_epproducts_domain_model_daytrip'
|
||||
#
|
||||
CREATE TABLE tx_epproducts_domain_model_daytrip (
|
||||
|
||||
uid int(11) NOT NULL auto_increment,
|
||||
pid int(11) DEFAULT '0' NOT NULL,
|
||||
|
||||
product int(11) unsigned DEFAULT '0',
|
||||
date date DEFAULT '0000-00-00',
|
||||
bus_pro_id varchar(64) DEFAULT '' NOT NULL,
|
||||
code varchar(64) DEFAULT '' NOT NULL,
|
||||
hotel int(11) unsigned DEFAULT '0',
|
||||
hotel_bus_pro_id varchar(64) DEFAULT '' NOT NULL,
|
||||
hotel_code varchar(64) DEFAULT '' NOT NULL,
|
||||
|
||||
tstamp int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
crdate int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
|
||||
hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
|
||||
starttime int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
endtime int(11) unsigned DEFAULT '0' NOT NULL,
|
||||
|
||||
t3ver_oid int(11) DEFAULT '0' NOT NULL,
|
||||
t3ver_id int(11) DEFAULT '0' NOT NULL,
|
||||
t3ver_wsid int(11) DEFAULT '0' NOT NULL,
|
||||
t3ver_label varchar(255) DEFAULT '' NOT NULL,
|
||||
t3ver_state tinyint(4) DEFAULT '0' NOT NULL,
|
||||
t3ver_stage int(11) DEFAULT '0' NOT NULL,
|
||||
t3ver_count int(11) DEFAULT '0' NOT NULL,
|
||||
t3ver_tstamp int(11) DEFAULT '0' NOT NULL,
|
||||
t3ver_move_id int(11) DEFAULT '0' NOT NULL,
|
||||
|
||||
sys_language_uid int(11) DEFAULT '0' NOT NULL,
|
||||
l10n_parent int(11) DEFAULT '0' NOT NULL,
|
||||
l10n_diffsource mediumblob,
|
||||
|
||||
PRIMARY KEY (uid),
|
||||
KEY parent (pid),
|
||||
KEY t3ver_oid (t3ver_oid,t3ver_wsid),
|
||||
KEY language (l10n_parent,sys_language_uid),
|
||||
|
||||
);
|
||||
|
||||
#
|
||||
# Table structure for table 'tx_epproducts_domain_model_badge'
|
||||
#
|
||||
|
||||
@@ -120,4 +120,5 @@ config {
|
||||
compressBody = 1
|
||||
typolinkCheckRootline = 1
|
||||
typolinkEnableLinksAcrossDomains = 1
|
||||
sendCacheHeaders = 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="date-select-wrapper">
|
||||
<div class="ep-headbar">{{ headerLabel }}</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="date-select">
|
||||
<div class="date-select__picker">
|
||||
<div class="daterange__panel">
|
||||
<input ref="field" type="hidden">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<table class="table table-striped pricetable" v-show="availableRooms.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zimmerart</th>
|
||||
<th>Preis</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="entry of availableRooms">
|
||||
<td>{{entry.label}}</td>
|
||||
<td>ab {{entry.price * selectedNumberOfDays}} €</td>
|
||||
<td>
|
||||
<a :href="entry.bookingUrl" target="_blank"
|
||||
class="button button--small button--action">Buchen</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-show="loading" class="loader"><p>Loading...</p></div>
|
||||
<div v-show="message">{{message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import $ from 'jquery'
|
||||
import Vue from 'vue'
|
||||
import flatpickr from 'flatpickr'
|
||||
import { German } from 'flatpickr/dist/l10n/de'
|
||||
import dayjs from 'dayjs'
|
||||
import 'dayjs/locale/de'
|
||||
import DatesTable from './DatesTable.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DatesTable
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
picker: null,
|
||||
displayedFrom: dayjs().startOf('month'),
|
||||
displayedTo: dayjs().endOf('month'),
|
||||
selectedFrom: null,
|
||||
selectedTo: null,
|
||||
availableDates: [],
|
||||
availableRooms: []
|
||||
}
|
||||
},
|
||||
props: {
|
||||
hotelName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hotelUid: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
productUid: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
minDate: {
|
||||
type: Date,
|
||||
default () {
|
||||
return new Date()
|
||||
}
|
||||
},
|
||||
maxDate: {
|
||||
type: Date,
|
||||
default () {
|
||||
return new Date(dayjs().add(1, 'year'))
|
||||
}
|
||||
},
|
||||
contingentEndpointUri: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
roomsEndpointUri: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
argumentPrefix: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
headerLabel () {
|
||||
let label = 'Buchungsoptionen';
|
||||
if (this.hotelName) {
|
||||
label += ' ' + this.hotelName;
|
||||
}
|
||||
if (this.selectedFrom && this.selectedTo) {
|
||||
label += ' ' + this.selectedFrom.format('DD.MM.YY') + ' - ' + this.selectedTo.format('DD.MM.YY');
|
||||
label += ' (' + this.selectedNumberOfDays + ' Nächte)';
|
||||
}
|
||||
return label;
|
||||
},
|
||||
selectedNumberOfDays () {
|
||||
if (this.selectedFrom === null || this.selectedTo === null) {
|
||||
return 0;
|
||||
}
|
||||
return this.selectedTo.diff(this.selectedFrom, 'days');
|
||||
},
|
||||
message () {
|
||||
if (this.loading) {
|
||||
return '';
|
||||
}
|
||||
if (this.selectedFrom === null || this.selectedTo === null) {
|
||||
return 'Bitte einen Zeitraum auswählen.';
|
||||
}
|
||||
if (this.selectedTo.diff(this.selectedFrom, 'days') < 2) {
|
||||
return 'Bitte mindestens zwei Nächte auswählen';
|
||||
}
|
||||
if (this.availableRooms.length === 0) {
|
||||
return 'Im gewählten Zeitraum sind leider keine Zimmer verfügbar.';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDateSelected (date) {
|
||||
this.selectedDate = dayjs(date);
|
||||
},
|
||||
fetchEnabledDates () {
|
||||
let query = {};
|
||||
query[this.argumentPrefix + '[hotel]'] = this.hotelUid;
|
||||
query[this.argumentPrefix + '[product]'] = this.productUid;
|
||||
this.loading = true;
|
||||
$.post(this.contingentEndpointUri, query, (json) => {
|
||||
this.loading = false;
|
||||
this.availableDates = json;
|
||||
const enabledDates = [];
|
||||
for (let entry of this.availableDates) {
|
||||
enabledDates.push(entry.date);
|
||||
}
|
||||
this.picker.set('enable', enabledDates);
|
||||
}, 'json');
|
||||
},
|
||||
fetchAvailableRooms () {
|
||||
let query = {};
|
||||
query[this.argumentPrefix + '[hotel]'] = this.hotelUid;
|
||||
query[this.argumentPrefix + '[product]'] = this.productUid;
|
||||
query[this.argumentPrefix + '[dateFrom]'] = this.selectedFrom.format('YYYY-MM-DD');
|
||||
query[this.argumentPrefix + '[dateTo]'] = this.selectedTo.format('YYYY-MM-DD');
|
||||
this.loading = true;
|
||||
$.post(this.roomsEndpointUri, query, (json) => {
|
||||
this.loading = false;
|
||||
this.availableRooms = json;
|
||||
}, 'json');
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.picker = $(this.$refs.field).flatpickr({
|
||||
mode: 'range',
|
||||
dateFormat: 'Y-m-d',
|
||||
locale: German,
|
||||
inline: true,
|
||||
minDate: new Date(this.minDate),
|
||||
maxDate: new Date(this.maxDate),
|
||||
onChange: (selectedDates) => {
|
||||
this.availableRooms = [];
|
||||
if (selectedDates.length === 2) {
|
||||
this.selectedFrom = dayjs(selectedDates[0]);
|
||||
this.selectedTo = dayjs(selectedDates[1]);
|
||||
if (this.selectedTo.diff(this.selectedFrom, 'days') > 1) {
|
||||
this.fetchAvailableRooms();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Vue.nextTick(() => {
|
||||
this.fetchEnabledDates();
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="dates-table-wrapper">
|
||||
<div v-if="bookable" class="ep-headbar">{{ headerLabel }}</div>
|
||||
<div v-if="!bookable" class="ep-headbar">Termine & Leistungen</div>
|
||||
<template v-if="showHeader">
|
||||
<div v-if="bookable" class="ep-headbar">{{ headerLabel }}</div>
|
||||
<div v-if="!bookable" class="ep-headbar">Termine & Leistungen</div>
|
||||
</template>
|
||||
<div class="dates-table__pricetoggle" v-if="hasSurcharge || hasDiscount">
|
||||
<a href="#" v-if="hasSurcharge" v-on:click.prevent="toggleBus">
|
||||
<template v-if="addBus">
|
||||
@@ -127,6 +129,10 @@
|
||||
altLabel: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="price-table-wrapper">
|
||||
<div class="row ep-headbar">
|
||||
<div class="row ep-headbar" v-if="showHeader">
|
||||
<div class="col-xs-6">Buchungsoptionen</div>
|
||||
<div class="col-xs-6 text-right">{{dateLabel}}</div>
|
||||
</div>
|
||||
@@ -130,6 +130,10 @@
|
||||
singleDate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showHeader: {
|
||||
typoe: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
||||
+66
-3
@@ -6,6 +6,7 @@
|
||||
import PriceTable from './PriceTable.vue'
|
||||
import ServicesIncluded from './ServicesListIncluded.vue'
|
||||
import ServicesOptional from './ServicesListOptional.vue'
|
||||
import DateSelect from './DateSelect.vue'
|
||||
import DaterangeSelect from './DaterangeSelect.vue'
|
||||
import AjaxContent from './AjaxContent.vue'
|
||||
import Calendar from './Calendar.vue'
|
||||
@@ -21,6 +22,7 @@
|
||||
PriceTable,
|
||||
ServicesIncluded,
|
||||
ServicesOptional,
|
||||
DateSelect,
|
||||
DaterangeSelect,
|
||||
AjaxContent,
|
||||
Calendar,
|
||||
@@ -47,7 +49,42 @@
|
||||
singleDate: false
|
||||
}
|
||||
},
|
||||
props: [ 'filterSettings', 'argumentPrefix', 'uris', 'hotelFirst', 'fbPixelData', 'watchlist' ],
|
||||
props: {
|
||||
filterSettings: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
argumentPrefix: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
uris: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
hotelFirst: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
fbPixelData: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
watchlist: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
daytrip: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dateLabel () {
|
||||
return this.dateStart + ' - ' + this.dateEnd;
|
||||
@@ -79,10 +116,36 @@
|
||||
this.loading = false;
|
||||
EventBus.$emit('tableLoaded');
|
||||
}, 'json');
|
||||
|
||||
},
|
||||
loadSelectableDates () {
|
||||
this.loading = true;
|
||||
this.datesShow = true;
|
||||
this.priceTableShow = false;
|
||||
this.detailShow = false;
|
||||
let query = {};
|
||||
query[this.argumentPrefix + '[product]'] = this.filterSettings;
|
||||
$.post(this.uris['dateslist'], query, (json) => {
|
||||
if (json.dates.length === 1) {
|
||||
this.singleDate = true;
|
||||
let dateRow = json.dates[0];
|
||||
this.loadPriceTableView(dateRow.dateUid);
|
||||
} else {
|
||||
this.singleDate = false;
|
||||
this.datesRows = json.dates;
|
||||
this.bookable = json.bookable;
|
||||
this.altLabel = json.altLabel;
|
||||
this.altProductLink = json.altProductLink;
|
||||
this.loading = false;
|
||||
}
|
||||
}, 'json');
|
||||
},
|
||||
loadDatesView (scroll = true) {
|
||||
this.loadDates();
|
||||
if (this.daytrip) {
|
||||
this.loading = false;
|
||||
this.datesShow = true;
|
||||
} else {
|
||||
this.loadDates();
|
||||
}
|
||||
this.activeSection = 'dates';
|
||||
if (scroll) {
|
||||
this.scrollToMain();
|
||||
|
||||
@@ -13,13 +13,16 @@
|
||||
<product-detail
|
||||
:filter-settings='<v:format.json.encode>{filterSettings}</v:format.json.encode>'
|
||||
:uris='{
|
||||
dates: "<f:format.raw>{ep:uri.ajax(action: 'dates', controller: 'AjaxDate', pageUid: settings.defaultAjaxUid, arguments: '{product: product, hotel: hotel}', format: 'json')}</f:format.raw>",
|
||||
pricetable: "<f:format.raw>{ep:uri.ajax(action: 'pricetable', controller: 'AjaxTable', pageUid: settings.defaultAjaxUid, arguments: '{product: product, hotel: hotel}', format: 'json')}</f:format.raw>"
|
||||
dates: "<f:format.raw>{ep:uri.ajax(action: 'dates', controller: 'AjaxDate', arguments: '{product: product, hotel: hotel}', format: 'json', noCacheHash: 1)}</f:format.raw>",
|
||||
contingents: "<f:format.raw>{ep:uri.ajax(action: 'list', controller: 'AjaxContingent', format: 'json', noCacheHash: 1)}</f:format.raw>",
|
||||
rooms: "<f:format.raw>{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', format: 'json', noCacheHash: 1)}</f:format.raw>",
|
||||
pricetable: "<f:format.raw>{ep:uri.ajax(action: 'pricetable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'json', noCacheHash: 1)}</f:format.raw>"
|
||||
}'
|
||||
argument-prefix='<ep:argumentPrefix pluginName="Ajax" />'
|
||||
:hotel-first="{f:if(condition: settings.hotelOnTop, then: 'true', else: 'false')}"
|
||||
:fb-pixel-data="{ productUid: <f:format.raw>{product.uid}</f:format.raw>, nameInternal: '<f:format.raw>{product.nameInternal}</f:format.raw>' }"
|
||||
:watchlist="watchlist"
|
||||
:daytrip="{product.daytrip -> v:variable.convert(type: 'int')}"
|
||||
inline-template>
|
||||
<div class="product-detail-wrapper">
|
||||
<div class="row">
|
||||
@@ -128,23 +131,37 @@
|
||||
</div>
|
||||
</f:if>
|
||||
</f:if>
|
||||
<template v-if="available">
|
||||
<dates-table :dates-rows="datesRows"
|
||||
:alt-product-link="altProductLink"
|
||||
:alt-label="altLabel"
|
||||
hotel-name="{hotel.name}"
|
||||
:bookable="bookable"
|
||||
></dates-table>
|
||||
</template>
|
||||
<f:if condition="{isProductWithNoInfoConcept}">
|
||||
<f:if condition="{product.daytrip}">
|
||||
<f:then>
|
||||
<date-select
|
||||
hotel-name="{hotel.name}"
|
||||
:hotel-uid="{hotel.uid}"
|
||||
:product-uid="{product.uid}"
|
||||
:contingent-endpoint-uri="uris.contingents"
|
||||
:rooms-endpoint-uri="uris.rooms"
|
||||
:argument-prefix="argumentPrefix"
|
||||
></date-select>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<template v-else>
|
||||
<div class="alert alert-info">
|
||||
Die von dir gewählte Reise ist aktuell nicht buchbar, aber wir helfen dir gerne dabei,
|
||||
eine andere Reise zu finden. Ruf uns an unter <a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}" title="Service Telefon anrufen" rel="nofollow">{settings.phoneNumber}</a>
|
||||
oder schreibe an <f:link.email email="[email protected]"/>.
|
||||
</div>
|
||||
<template v-if="available">
|
||||
<dates-table :dates-rows="datesRows"
|
||||
:alt-product-link="altProductLink"
|
||||
:alt-label="altLabel"
|
||||
hotel-name="{hotel.name}"
|
||||
:bookable="bookable"
|
||||
></dates-table>
|
||||
</template>
|
||||
<f:if condition="{isProductWithNoInfoConcept}">
|
||||
<f:else>
|
||||
<template v-else>
|
||||
<div class="alert alert-info">
|
||||
Die von dir gewählte Reise ist aktuell nicht buchbar, aber wir helfen dir gerne dabei,
|
||||
eine andere Reise zu finden. Ruf uns an unter <a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}" title="Service Telefon anrufen" rel="nofollow">{settings.phoneNumber}</a>
|
||||
oder schreibe an <f:link.email email="[email protected]"/>.
|
||||
</div>
|
||||
</template>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:else>
|
||||
</f:if>
|
||||
<f:render partial="Product/Details" arguments="{_all}"/>
|
||||
@@ -152,7 +169,8 @@
|
||||
<!-- /Dates -->
|
||||
<!-- Prices -->
|
||||
<div v-cloak v-show="!loading && priceTableShow">
|
||||
<price-table :price-table-rows="priceTableRows" :services-included="servicesIncluded" :date-label="dateLabel" :single-date="singleDate"></price-table>
|
||||
<price-table :price-table-rows="priceTableRows" :services-included="servicesIncluded"
|
||||
:date-label="dateLabel" :single-date="singleDate"></price-table>
|
||||
<f:render partial="Product/Details" arguments="{_all}"/>
|
||||
</div>
|
||||
<!-- /Prices -->
|
||||
|
||||
@@ -81,6 +81,7 @@ $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl'] = [
|
||||
'date' => 'AjaxDate',
|
||||
'table' => 'AjaxTable',
|
||||
'calendar' => 'AjaxCalendar',
|
||||
'contingent' => 'AjaxContingent',
|
||||
'watchlist' => 'AjaxWatchlist',
|
||||
]
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user