diff --git a/.ddev/config.yaml b/.ddev/config.yaml index 97e5975a..c57385ea 100644 --- a/.ddev/config.yaml +++ b/.ddev/config.yaml @@ -6,7 +6,7 @@ php_version: "7.3" webserver_type: apache-cgi router_http_port: "80" router_https_port: "443" -xdebug_enabled: false +xdebug_enabled: true additional_hostnames: - ep-events - ski-boarderweek diff --git a/package-lock.json b/package-lock.json index 126c6305..c4470cd5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -261,8 +261,7 @@ "@babel/helper-plugin-utils": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" }, "@babel/helper-regex": { "version": "7.8.3", @@ -433,7 +432,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz", "integrity": "sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.3", "@babel/plugin-syntax-object-rest-spread": "^7.8.0" @@ -509,7 +507,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -1757,38 +1754,6 @@ "object.assign": "^4.1.0" } }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", - "dev": true - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", - "dev": true - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", - "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.26.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -2762,12 +2727,6 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", - "dev": true - }, "core-js-compat": { "version": "3.6.4", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", @@ -8422,12 +8381,6 @@ "regenerate": "^1.4.0" } }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, "regenerator-transform": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", diff --git a/package.json b/package.json index b2adccad..a989fa94 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "epevents", "description": "EP Reisen TYPO3 website package", - "version": "3.0.0", + "version": "4.0.0", "author": "Björn Fromme, fromme@dreipunktnull.com", "license": "WTFPL", "repository": "https://gitlab.com/dreipunktnull/ep_theme.git", "scripts": { - "dev": "./node_modules/.bin/encore dev --watch --watch-poll 500", + "dev": "./node_modules/.bin/encore dev --watch", "build": "./node_modules/.bin/encore production" }, "dependencies": { @@ -33,10 +33,10 @@ "vuex": "^3.1.0" }, "devDependencies": { + "@babel/plugin-proposal-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@symfony/webpack-encore": "^0.28.2", "autoprefixer": "^8.6.5", - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-object-rest-spread": "^6.26.0", "node-sass": "^4.11.0", "postcss-loader": "^3.0.0", "sass-loader": "^7.3.1", @@ -45,6 +45,6 @@ }, "browserslist": [ "last 2 versions", - "ios >= 8" + "ios >= 9" ] } diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Model/Contingent.php b/public/typo3conf/ext/ep_products/Classes/Domain/Model/Contingent.php index cccb8668..a6a681a5 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Model/Contingent.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Model/Contingent.php @@ -342,4 +342,12 @@ class Contingent extends AbstractEntity implements EventInterface { return $this->getBegin() >= $period->getBegin() && $this->getEnd() < $period->getEnd(); } + + /** + * @return int + */ + public function getOccupancy() + { + return $this->getPax() - $this->getAvailable(); + } } diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/ContingentRepository.php b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/ContingentRepository.php index df6eccab..95322d4d 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/ContingentRepository.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/ContingentRepository.php @@ -125,6 +125,7 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa $qb ->select('c.date as date', 'c.min_nights as minNights') ->addSelectLiteral('SUM(c.available) as total') + ->addSelectLiteral('SUM(c.pax) as capacity') ->from('tx_epproducts_domain_model_contingent', 'c') ->innerJoin('c', 'tx_epproducts_domain_model_date', 'd', 'c.date = d.date_start AND c.hotel_code = d.hotel_code') ->where('c.hotel = :hotelUid') diff --git a/public/typo3conf/ext/ep_products/Classes/Service/ContingentDataService.php b/public/typo3conf/ext/ep_products/Classes/Service/ContingentDataService.php index b4bb26cb..ecb713a0 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/ContingentDataService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/ContingentDataService.php @@ -65,6 +65,7 @@ class ContingentDataService 'date' => $row['date'], 'minNights' => $row['minNights'], 'total' => $row['total'], + 'capacity' => $row['capacity'], ]; } diff --git a/public/typo3conf/ext/ep_products/Classes/Service/ContingentImportService.php b/public/typo3conf/ext/ep_products/Classes/Service/ContingentImportService.php index 6e22a50d..679d8697 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/ContingentImportService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/ContingentImportService.php @@ -167,7 +167,7 @@ class ContingentImportService implements SingletonInterface protected function parseContingent(\SimpleXMLElement $xmlContingent) { $hotelCode = (string) $xmlContingent['code']; - $sql = 'SELECT h.uid uid, h.groupsch_id groupsch_id + $sql = 'SELECT h.uid uid FROM tx_epproducts_domain_model_hotel h LEFT JOIN tx_epproducts_domain_model_matchcode m ON (h.uid = m.entity AND m.entity_type = "hotel") WHERE h.deleted = 0 AND h.hidden = 0 AND h.code = :code OR m.code = :code @@ -241,12 +241,12 @@ class ContingentImportService implements SingletonInterface $status = Contingent::STATUS_BLOCKED; } } else { - $pax = $roomType['pax'] * (int) $xmlRoom['kontingent']; + $capacity = (int) $xmlRoom['kontingent'] * $roomType['pax']; $availableRooms = (int) $xmlRoom['frei'] > 0 ? (int) $xmlRoom['frei'] : 0; $availablePax = $availableRooms * $roomType['pax']; } if ($availablePax === 0 && $roomType['ctrl'] === false) { - continue; + //continue; } $minPrice = (int) $xmlRoom['abpreis']; $minNights = (int) $xmlRoom['abpreis_naechte']; @@ -262,8 +262,7 @@ class ContingentImportService implements SingletonInterface 'hotel_bus_pro_id' => $hotelData['busProId'], 'room_code' => $roomCode, 'room_label' => $roomLabel, - 'groupsch_id' => $hotelData['groupsch_id'], - 'pax' => $pax, + 'pax' => $capacity, 'available' => $availablePax, 'status' => $status, 'date' => $date->format('Y-m-d'), diff --git a/public/typo3conf/ext/ep_products/Classes/Service/IcalService.php b/public/typo3conf/ext/ep_products/Classes/Service/IcalService.php index 1ab2507a..144fb232 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/IcalService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/IcalService.php @@ -5,7 +5,7 @@ namespace EP\EpProducts\Service; * * Copyright notice * - * (c) 2019 Björn Fromme , dreipunktnull + * (c) 2020 Björn Fromme , dreipunktnull * * All rights reserved * @@ -36,13 +36,16 @@ use TYPO3\CMS\Core\SingletonInterface; class IcalService implements SingletonInterface { - const THRESHOLD = 75; - /** * @var ContingentRepository */ protected $contingentRepository; + /** + * @var Vcalendar + */ + protected $vcalendar; + /** * @param ContingentRepository $contingentRepository */ @@ -69,35 +72,13 @@ class IcalService implements SingletonInterface $events = []; } - $vcalendar = $this->createCalendarInstance(); + $this->createCalendarInstance(); - $dateStart = $dateEnd = null; - - foreach ($events as $event) { - /** @var Contingent $event */ - $paxTotal = $event->getPax(); - $paxAvaiable = $event->getAvailable(); - - $percentageBooked = 100; - - if ($paxTotal > 0 && Contingent::STATUS_BLOCKED !== $event->getStatus()) { - $percentageBooked = 100 - round($paxAvaiable / $paxTotal * 100); - } - - if ($percentageBooked >= self::THRESHOLD) { - if (null !== $dateStart) { - continue; - } - $dateStart = $event->getBegin(); - } elseif (null !== $dateStart) { - $dateEnd = $event->getEnd(); - $this->addCalendarItem($vcalendar, $dateStart, $dateEnd); - $dateStart = $dateEnd = null; - } - } + $this->generateOccupancyItems($events); + $this->generateBlockedItems($events); try { - $feed = $vcalendar->vtimezonePopulate()->createCalendar(); + $feed = $this->vcalendar->vtimezonePopulate()->createCalendar(); } catch (\Exception $e) { $feed = ''; @@ -107,23 +88,69 @@ class IcalService implements SingletonInterface } /** - * @param Vcalendar $vcalendar - * @param \DateTime $dateStart - * @param \DateTime $dateEnd + * @param array $events */ - protected function addCalendarItem(Vcalendar $vcalendar, \DateTime $dateStart, \DateTime $dateEnd): void + protected function generateOccupancyItems(array $events): void + { + $occupancy = null; + + foreach ($events as $event) { + /** @var Contingent $event */ + if (null === $occupancy) { + $item = [ + 'from' => $event->getBegin(), + 'occupancy' => $event->getOccupancy(), + ]; + } elseif ($occupancy !== $event->getOccupancy()) { + $item['to'] = $event->getEnd(); + $this->addOccupancyItem($item); + $item = [ + 'from' => $event->getBegin(), + 'occupancy' => $event->getOccupancy(), + ]; + } + + $occupancy = $event->getOccupancy(); + } + + if (!isset($item['to'])) { + $item['to'] = $event->getEnd(); + $this->addOccupancyItem($item); + } + } + + /** + * @param array $events + */ + protected function generateBlockedItems(array $events): void + { + $item = null; + + foreach ($events as $event) { + /** @var Contingent $event */ + if (null === $item && Contingent::STATUS_BLOCKED === $event->getStatus()) { + $item = [ + 'from' => $event->getBegin(), + ]; + } elseif (true === is_array($item) && Contingent::STATUS_OK === $event->getStatus()) { + $item['to'] = $event->getEnd(); + $this->addBlockedItem($item); + $item = null; + } + } + } + + /** + * @param array $item + */ + protected function addBlockedItem(array $item): void { try { - $vcalendar + $this->vcalendar ->newVevent() - ->setDtstart($dateStart) - ->setDtend($dateEnd) + ->setDtstart($item['from']) + ->setDtend($item['to']) ->setSummary('Unavailable') - ->setDescription(sprintf( - 'Unavailable from %s to %s', - $dateStart->format('d M Y'), - $dateEnd->format('d.M Y') - )) ; } catch (\Exception $e) { @@ -131,16 +158,30 @@ class IcalService implements SingletonInterface } /** - * @return Vcalendar + * @param array $item */ - protected function createCalendarInstance(): Vcalendar + protected function addOccupancyItem( array $item): void { - $vcalendar = Vcalendar::factory([ Vcalendar::UNIQUE_ID => 'www.ep-reisen.de' ]); - $vcalendar->setXprop( + $summary = sprintf('Part-occupied:%d', $item['occupancy']); + + try { + $this->vcalendar + ->newVevent() + ->setDtstart($item['from']) + ->setDtend($item['to']) + ->setSummary($summary) + ; + } + catch (\Exception $e) { + } + } + + protected function createCalendarInstance(): void + { + $this->vcalendar = Vcalendar::factory([ Vcalendar::UNIQUE_ID => 'www.ep-reisen.de' ]); + $this->vcalendar->setXprop( Vcalendar::X_WR_TIMEZONE, 'Europe/Berlin' ); - - return $vcalendar; } -} \ No newline at end of file +} diff --git a/public/typo3conf/ext/ep_products/Configuration/TCA/tx_epproducts_domain_model_hotel.php b/public/typo3conf/ext/ep_products/Configuration/TCA/tx_epproducts_domain_model_hotel.php index 48bd6326..2cfa8d6c 100644 --- a/public/typo3conf/ext/ep_products/Configuration/TCA/tx_epproducts_domain_model_hotel.php +++ b/public/typo3conf/ext/ep_products/Configuration/TCA/tx_epproducts_domain_model_hotel.php @@ -42,7 +42,7 @@ return [ ], 'palettes' => [ 'top' => ['showitem' => 'hidden,earlybird,new,low_contingent,show_as_teaser, - --linebreak--,code,groupsch_id,detail_page,--linebreak--,external_link,path_segment'], + --linebreak--,code,detail_page,--linebreak--,external_link,path_segment'], 'geolocation' => ['showitem' => 'address, longitude, latitude'], 'destinations' => ['showitem' => 'country,region,city'], 'name' => ['showitem' => 'name,short_name,--linebreak--,header_title,header_subtitle,--linebreak--,headline'], @@ -446,15 +446,6 @@ return [ 'eval' => 'trim' ], ], - 'groupsch_id' => [ - 'exclude' => 0, - 'label' => 'groups.ch ID', - 'config' => [ - 'type' => 'input', - 'size' => 8, - 'eval' => 'trim,int' - ], - ], 'external_link' => [ 'exclude' => 0, 'label' => 'Externer Link (Google MyBusiness)', diff --git a/public/typo3conf/ext/ep_products/ext_tables.sql b/public/typo3conf/ext/ep_products/ext_tables.sql index 3b927f41..807e71db 100644 --- a/public/typo3conf/ext/ep_products/ext_tables.sql +++ b/public/typo3conf/ext/ep_products/ext_tables.sql @@ -222,7 +222,6 @@ CREATE TABLE tx_epproducts_domain_model_hotel images int(11) unsigned NOT NULL default '0', reseller_images int(11) unsigned NOT NULL default '0', code varchar(255) DEFAULT '' NOT NULL, - groupsch_id varchar(8) DEFAULT '' NOT NULL, external_link varchar(255) DEFAULT '' NOT NULL, path_segment varchar(255) DEFAULT '' NOT NULL, type varchar(255) DEFAULT '' NOT NULL, diff --git a/webpack.config.js b/webpack.config.js index e8b687f8..835266ad 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -23,7 +23,10 @@ Encore images: 'images/[name].[ext]' }) .configureBabel(function (babelConfig) { - babelConfig.plugins = [ 'transform-object-rest-spread', 'syntax-dynamic-import' ] + babelConfig.plugins = [ '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-syntax-dynamic-import' ] + }) + .configureWatchOptions(function (options) { + options.poll = 250; }); const EpThemeConfig = Encore.getWebpackConfig(); @@ -53,9 +56,11 @@ Encore images: 'images/[name].[ext]' }) .configureBabel(function (babelConfig) { - babelConfig.plugins = [ 'transform-object-rest-spread', 'syntax-dynamic-import' ] + babelConfig.plugins = [ '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-syntax-dynamic-import' ] }) -; + .configureWatchOptions(function (options) { + options.poll = 250; + }); const EpEventsConfig = Encore.getWebpackConfig(); EpEventsConfig.name = 'EpEventsConfig';