diff --git a/public/typo3conf/ext/ep_products/Classes/CsvFormatter/FacebookCatalogCsvFormatter.php b/public/typo3conf/ext/ep_products/Classes/CsvFormatter/FacebookCatalogCsvFormatter.php
index d6924b9c..2b86448f 100644
--- a/public/typo3conf/ext/ep_products/Classes/CsvFormatter/FacebookCatalogCsvFormatter.php
+++ b/public/typo3conf/ext/ep_products/Classes/CsvFormatter/FacebookCatalogCsvFormatter.php
@@ -56,7 +56,7 @@ class FacebookCatalogCsvFormatter
$product['images']['image'][0],
$product['link'],
$product['name'],
- $minPrice . ' EUR',
+ sprintf('%.2f EUR', $minPrice),
'brand-epreisen'
];
}
diff --git a/public/typo3conf/ext/ep_products/Classes/CsvFormatter/GoogleBusinessFeedCsvFormatter.php b/public/typo3conf/ext/ep_products/Classes/CsvFormatter/GoogleBusinessFeedCsvFormatter.php
index e39eb57c..9a8e17ca 100644
--- a/public/typo3conf/ext/ep_products/Classes/CsvFormatter/GoogleBusinessFeedCsvFormatter.php
+++ b/public/typo3conf/ext/ep_products/Classes/CsvFormatter/GoogleBusinessFeedCsvFormatter.php
@@ -48,7 +48,7 @@ class GoogleBusinessFeedCsvFormatter
$product['name'],
$product['link'],
$product['images']['image'][0],
- $minPrice . '.00 EUR',
+ sprintf('%.2f EUR', $minPrice),
];
}
}
diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Model/Date.php b/public/typo3conf/ext/ep_products/Classes/Domain/Model/Date.php
index 7df0541f..a6060a2d 100644
--- a/public/typo3conf/ext/ep_products/Classes/Domain/Model/Date.php
+++ b/public/typo3conf/ext/ep_products/Classes/Domain/Model/Date.php
@@ -120,9 +120,9 @@ class Date extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
protected $busAvailable = false;
/**
- * @var int
+ * @var float
*/
- protected $busPrice = 0;
+ protected $busPrice = 0.0;
/**
* @var string
@@ -145,19 +145,19 @@ class Date extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
protected $pickups = '';
/**
- * @var int
+ * @var float
*/
- protected $discount = 0;
+ protected $discount = 0.0;
/**
- * @var int
+ * @var float
*/
- protected $minPrice = 0;
+ protected $minPrice = 0.0;
/**
- * @var int
+ * @var float
*/
- protected $pseudoPrice = 0;
+ protected $pseudoPrice = 0.0;
/**
* @var int
@@ -438,7 +438,7 @@ class Date extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
}
/**
- * @return int
+ * @return float
*/
public function getBusPrice()
{
@@ -446,7 +446,7 @@ class Date extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
}
/**
- * @param int $busPrice
+ * @param float $busPrice
*/
public function setBusPrice($busPrice)
{
@@ -533,21 +533,21 @@ class Date extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
}
/**
- * @return int
+ * @return float
*/
public function getDiscount() {
return $this->discount;
}
/**
- * @param int $discount
+ * @param float $discount
*/
public function setDiscount($discount) {
$this->discount = $discount;
}
/**
- * @return int
+ * @return float
*/
public function getMinPrice()
{
@@ -555,7 +555,7 @@ class Date extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
}
/**
- * @param int $minPrice
+ * @param float $minPrice
*/
public function setMinPrice($minPrice)
{
@@ -563,7 +563,7 @@ class Date extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
}
/**
- * @return int
+ * @return float
*/
public function getPseudoPrice()
{
@@ -571,7 +571,7 @@ class Date extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
}
/**
- * @param int $pseudoPrice
+ * @param float $pseudoPrice
*/
public function setPseudoPrice($pseudoPrice)
{
diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Serialization/Date.php b/public/typo3conf/ext/ep_products/Classes/Domain/Serialization/Date.php
index d4f62f37..05270a2f 100644
--- a/public/typo3conf/ext/ep_products/Classes/Domain/Serialization/Date.php
+++ b/public/typo3conf/ext/ep_products/Classes/Domain/Serialization/Date.php
@@ -44,7 +44,7 @@ class Date
$date['@id'] = $array['dateBusProId'];
$date['from'] = $array['dateStart'];
$date['to'] = $array['dateEnd'];
- $date['minprice'] = $array['dateMinPrice'];
+ $date['minprice'] = (float) $array['dateMinPrice'];
$date['nights'] = $array['dateNights'];
$date['skipassincluded'] = $array['dateSkiPassIncluded'];
$date['busincluded'] = $array['dateBusIncluded'];
diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Serialization/Room.php b/public/typo3conf/ext/ep_products/Classes/Domain/Serialization/Room.php
index f9a9c90e..e6d60251 100644
--- a/public/typo3conf/ext/ep_products/Classes/Domain/Serialization/Room.php
+++ b/public/typo3conf/ext/ep_products/Classes/Domain/Serialization/Room.php
@@ -40,8 +40,8 @@ class Room
$room['@pax'] = $array['roomPax'];
$room['@code'] = $array['roomCode'];
$room['name'] = $array['roomName'];
- $room['price'] = $array['roomPrice'];
- $room['busprice'] = $array['busPrice'];
+ $room['price'] = (float) $array['roomPrice'];
+ $room['busprice'] = (float) $array['busPrice'];
$room['nights'] = $array['roomNights'];
$room['available'] = $array['roomAvailable'];
diff --git a/public/typo3conf/ext/ep_products/Classes/Service/DateImportService.php b/public/typo3conf/ext/ep_products/Classes/Service/DateImportService.php
index 12527da7..bd31806d 100644
--- a/public/typo3conf/ext/ep_products/Classes/Service/DateImportService.php
+++ b/public/typo3conf/ext/ep_products/Classes/Service/DateImportService.php
@@ -193,10 +193,17 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
return $dateCount;
}
+ /**
+ * Recreated on every run, so the temp tables always follow the schema of the
+ * live tables (a column type change would otherwise survive in them) and a
+ * crashed import cannot leave rows behind for the next one.
+ */
public function createTempTables(): void
{
- $this->db->executeStatement('CREATE TABLE IF NOT EXISTS tx_epproducts_domain_model_date_temp LIKE tx_epproducts_domain_model_date');
- $this->db->executeStatement('CREATE TABLE IF NOT EXISTS tx_epproducts_domain_model_room_temp LIKE tx_epproducts_domain_model_room');
+ $this->db->executeStatement('DROP TABLE IF EXISTS tx_epproducts_domain_model_date_temp');
+ $this->db->executeStatement('DROP TABLE IF EXISTS tx_epproducts_domain_model_room_temp');
+ $this->db->executeStatement('CREATE TABLE tx_epproducts_domain_model_date_temp LIKE tx_epproducts_domain_model_date');
+ $this->db->executeStatement('CREATE TABLE tx_epproducts_domain_model_room_temp LIKE tx_epproducts_domain_model_room');
}
public function clearTables(): void
@@ -425,7 +432,7 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
if ($discount) {
$servicesTransportationOptional[] = [
'label' => 'Rabatt bei Eigenanreise',
- 'price' => $discount,
+ 'price' => number_format($discount, 2, ',', '.'),
];
}
@@ -549,15 +556,15 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
*/
protected function parseRooms(array $row, $hotelUid, array &$date, array $services): void
{
- $minPriceOverall = 0;
- $minPriceAvailable = 0;
+ $minPriceOverall = 0.0;
+ $minPriceAvailable = 0.0;
$minPax = 0;
$availableTotal = 0;
$boardCode = '';
$count = 0;
if ($row['pseudoPrice'] !== null) {
- $date['pseudo_price'] = (int) $row['pseudoPrice'];
+ $date['pseudo_price'] = (float) $row['pseudoPrice'];
}
foreach ($row['rooms'] as $roomRow) {
@@ -567,7 +574,7 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
$pax = (int) $roomRow['maxPax'];
$available = (int) $roomRow['available'];
- $price = (int) $roomRow['price'];
+ $price = (float) $roomRow['price'];
// Skip rooms with zero price _and_ zero availability.
if (!$available && !$price) {
@@ -594,12 +601,12 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
$availableTotal += $available * $pax;
// Determine lowest price of available rooms
- if ($code !== static::CODE_BABYROOM && $available > 0 && ($minPriceAvailable === 0 || $price < $minPriceAvailable)) {
+ if ($code !== static::CODE_BABYROOM && $available > 0 && ($minPriceAvailable === 0.0 || $price < $minPriceAvailable)) {
$minPriceAvailable = $price;
}
// Determine lowest overall price
- if ($code !== static::CODE_BABYROOM && ($minPriceOverall === 0 || $price < $minPriceOverall)) {
+ if ($code !== static::CODE_BABYROOM && ($minPriceOverall === 0.0 || $price < $minPriceOverall)) {
$minPriceOverall = $price;
}
@@ -649,19 +656,20 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
/**
* @param array $services
- * @return int
+ * @return float
*/
- protected function determineDiscount(array $services): int
+ protected function determineDiscount(array $services): float
{
foreach ($services as $key => $section) {
foreach ($section as $service) {
- if ($service['price'] < 0) {
- return (int) $service['price'];
+ $price = $this->parseFormattedPrice($service['price']);
+ if ($price < 0) {
+ return $price;
}
}
}
- return 0;
+ return 0.0;
}
/**
@@ -788,12 +796,24 @@ class DateImportService implements SingletonInterface, LoggerAwareInterface
}
foreach ($services['BUS'] as $service) {
+ $price = $this->parseFormattedPrice($service['price']);
$date['bus_available'] = 1;
- $date['bus_price'] = (int) $service['price'];
- $date['bus_included'] = (int) ((int) $service['price'] === 0);
+ $date['bus_price'] = $price;
+ $date['bus_included'] = (int) ($price === 0.0);
}
}
+ /**
+ * Reads back a price parseService() formatted for display ("1.234,50").
+ *
+ * @param string $price
+ * @return float
+ */
+ protected function parseFormattedPrice(string $price): float
+ {
+ return (float) str_replace(['.', ','], ['', '.'], $price);
+ }
+
protected function getHotelMappings(): void
{
$sql = '
diff --git a/public/typo3conf/ext/ep_products/ext_tables.sql b/public/typo3conf/ext/ep_products/ext_tables.sql
index c521370a..675354db 100644
--- a/public/typo3conf/ext/ep_products/ext_tables.sql
+++ b/public/typo3conf/ext/ep_products/ext_tables.sql
@@ -117,15 +117,15 @@ CREATE TABLE tx_epproducts_domain_model_date
bus_included tinyint(1) unsigned DEFAULT '0' NOT NULL,
bus_available tinyint(1) unsigned DEFAULT '0' NOT NULL,
skipass_included tinyint(1) unsigned DEFAULT '0' NOT NULL,
- bus_price int(11) DEFAULT '0' NOT NULL,
+ bus_price decimal(10,2) DEFAULT '0.00' NOT NULL,
services_included text NOT NULL,
services_general text NOT NULL,
courses_included text NOT NULL,
pickups text NOT NULL,
guide text NOT NULL,
- discount int(11) DEFAULT '0' NOT NULL,
- min_price int(11) DEFAULT '0' NOT NULL,
- pseudo_price int(11) DEFAULT '0' NOT NULL,
+ discount decimal(10,2) DEFAULT '0.00' NOT NULL,
+ min_price decimal(10,2) DEFAULT '0.00' NOT NULL,
+ pseudo_price decimal(10,2) DEFAULT '0.00' NOT NULL,
available int(11) DEFAULT '0' NOT NULL,
min_pax int(11) DEFAULT '0' NOT NULL,
product int(11) unsigned DEFAULT '0',
@@ -456,7 +456,7 @@ CREATE TABLE tx_epproducts_domain_model_room
name varchar(255) DEFAULT '' NOT NULL,
code varchar(255) DEFAULT '' NOT NULL,
type int(11) DEFAULT '0' NOT NULL,
- price int(11) DEFAULT '0' NOT NULL,
+ price decimal(10,2) DEFAULT '0.00' NOT NULL,
services text NOT NULL,
nights int(11) DEFAULT '0' NOT NULL,
pax int(11) DEFAULT '0' NOT NULL,
diff --git a/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/PriceViewHelper.php b/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/PriceViewHelper.php
new file mode 100644
index 00000000..10b98bc1
--- /dev/null
+++ b/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/PriceViewHelper.php
@@ -0,0 +1,45 @@
+ ep:price()} €
+ */
+class PriceViewHelper extends AbstractViewHelper
+{
+ use CompileWithContentArgumentAndRenderStatic;
+
+ public function initializeArguments()
+ {
+ parent::initializeArguments();
+
+ $this->registerArgument('value', 'mixed', 'Amount in euros');
+ }
+
+ /**
+ * @param array $arguments
+ * @param \Closure $renderChildrenClosure
+ * @param RenderingContextInterface $renderingContext
+ * @return string
+ */
+ public static function renderStatic(
+ array $arguments,
+ \Closure $renderChildrenClosure,
+ RenderingContextInterface $renderingContext
+ ) {
+ $amount = round((float) $renderChildrenClosure(), 2);
+ $decimals = floor($amount) === $amount ? 0 : 2;
+
+ return number_format($amount, $decimals, ',', '');
+ }
+}
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/controllers/pricetable_controller.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/controllers/pricetable_controller.js
index 2840d710..a05952d7 100644
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/controllers/pricetable_controller.js
+++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/controllers/pricetable_controller.js
@@ -42,13 +42,13 @@ export default class extends Controller {
modeValueChanged() {
this.priceTargets.forEach(price => {
- let room = parseInt(price.dataset.room)
- let bus = parseInt(price.dataset.bus)
- let discount = parseInt(price.dataset.discount)
- price.innerText = `${this.calculatePrice(room, bus, discount)} €`
+ let room = parseFloat(price.dataset.room)
+ let bus = parseFloat(price.dataset.bus)
+ let discount = parseFloat(price.dataset.discount)
+ price.innerText = `${this.formatPrice(this.calculatePrice(room, bus, discount))} €`
})
this.busIconTargets.forEach(icon => {
- let busPrice = parseInt(icon.dataset.busPrice)
+ let busPrice = parseFloat(icon.dataset.busPrice)
let busIncluded = !!icon.dataset.busIncluded
icon.classList.toggle('hidden', this.isHideBusIcon(busPrice, busIncluded))
})
@@ -87,6 +87,16 @@ export default class extends Controller {
return minPrice
}
+ // Keep in sync with EP\EpTheme\ViewHelpers\PriceViewHelper: whole euros
+ // as before ("229"), German decimals where there are cents ("99,90").
+ formatPrice(amount) {
+ let cents = Math.round(amount * 100)
+ if (cents % 100 === 0) {
+ return String(cents / 100)
+ }
+ return (cents / 100).toFixed(2).replace('.', ',')
+ }
+
isHideBusIcon(busPrice, busIncluded) {
return (
busPrice && this.modeValue !== 'surcharge' ||
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Hotel/Teaser.html b/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Hotel/Teaser.html
index 77cb703c..c40a3ed1 100644
--- a/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Hotel/Teaser.html
+++ b/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Hotel/Teaser.html
@@ -39,7 +39,7 @@
{hotel.teaser -> f:format.crop(maxCharacters: settings.teaserTextMaxChars) -> f:format.html()}