fix: stop truncating imported prices to whole euros
This commit is contained in:
1 parent
b510299032
commit
27832d6548
16 files changed
+135
-60
No files matched your search
+1
-1
@@ -56,7 +56,7 @@ class FacebookCatalogCsvFormatter
|
||||
$product['images']['image'][0],
|
||||
$product['link'],
|
||||
$product['name'],
|
||||
$minPrice . ' EUR',
|
||||
sprintf('%.2f EUR', $minPrice),
|
||||
'brand-epreisen'
|
||||
];
|
||||
}
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ class GoogleBusinessFeedCsvFormatter
|
||||
$product['name'],
|
||||
$product['link'],
|
||||
$product['images']['image'][0],
|
||||
$minPrice . '.00 EUR',
|
||||
sprintf('%.2f EUR', $minPrice),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -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'];
|
||||
|
||||
|
||||
@@ -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 = '
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace EP\EpTheme\ViewHelpers;
|
||||
|
||||
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
|
||||
|
||||
/**
|
||||
* Formats a euro amount the way the website always has for whole euros ("229",
|
||||
* "1234"), and with German decimals where there are cents ("99,90"). Prices are
|
||||
* stored with cents, and the database hands them over as strings like "229.00".
|
||||
*
|
||||
* Keep in sync with formatPrice() in pricetable_controller.js.
|
||||
*
|
||||
* Usage: {row.minPrice -> 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, ',', '');
|
||||
}
|
||||
}
|
||||
+15
-5
@@ -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' ||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
{hotel.teaser -> f:format.crop(maxCharacters: settings.teaserTextMaxChars) -> f:format.html()}
|
||||
<f:if condition="{hotel.available} != -1">
|
||||
<span class="teaserbox__price">
|
||||
<strong>{f:if(condition: '{hotel.available} > 0', then: 'ab {hotel.minPrice} €', else: 'aktuell nicht buchbar')}</strong>
|
||||
<strong><f:if condition="{hotel.available} > 0"><f:then>ab {hotel.minPrice -> ep:price()} €</f:then><f:else>aktuell nicht buchbar</f:else></f:if></strong>
|
||||
</span>
|
||||
</f:if>
|
||||
</div>
|
||||
|
||||
@@ -53,10 +53,10 @@
|
||||
<td class="px-2 py-1 border border-zinc-200">
|
||||
<f:if condition="{row.available}">
|
||||
<f:then>
|
||||
{row.minPrice} €
|
||||
{row.minPrice -> ep:price()} €
|
||||
</f:then>
|
||||
<f:else>
|
||||
<s>{row.minPrice} €</s>
|
||||
<s>{row.minPrice -> ep:price()} €</s>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
</ul>
|
||||
<div class="bg-ep-primary-dark text-white font-bold px-4 py-1 peer-hover:bg-badge-gradient peer-hover:text-white transition-colors">
|
||||
<div class="lg:inline-block">
|
||||
ab <span class="product-teaser__price mx-1">{minPrice} €</span>
|
||||
ab <span class="product-teaser__price mx-1">{minPrice -> ep:price()} €</span>
|
||||
</div>
|
||||
<div class="product-teaser__label-price">
|
||||
p. P.{f:if(condition: '{season} == "w" && {skiPassIncluded}', then: ' / inkl. Skipass')}
|
||||
|
||||
@@ -129,9 +129,9 @@
|
||||
</td>
|
||||
</f:if>
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2 text-center">
|
||||
<f:if condition="{row.pseudoPrice}">
|
||||
<f:if condition="{row.pseudoPrice} > 0">
|
||||
<span class="block text-sm line-through">
|
||||
{row.pseudoPrice} €
|
||||
{row.pseudoPrice -> ep:price()} €
|
||||
</span>
|
||||
</f:if>
|
||||
<span class="font-bold normal-case text-sm md:text-base whitespace-nowrap"
|
||||
@@ -139,7 +139,7 @@
|
||||
data-room="{row.minPrice}"
|
||||
data-bus="{row.busPrice}"
|
||||
data-discount="{row.discount}">
|
||||
{row.minPrice}
|
||||
{row.minPrice -> ep:price()}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-2 py-1 md:p-2 text-center">
|
||||
|
||||
+2
-2
@@ -61,14 +61,14 @@
|
||||
<f:link.typolink
|
||||
parameter="{row.hotelDetailPageUid}"
|
||||
class="whitespace-nowrap {ep:themeClasses(classes: '{sbw: \'text-sbw-primary\', snz: \'text-snz-primary\', uch: \'text-uch-primary\'}', key: settings.themekey)}">
|
||||
{row.rooms.{type}.price}
|
||||
{row.rooms.{type}.price -> ep:price()}
|
||||
{f:if(condition: '{row.rooms.{type}.undersubscription}', then: '*')}
|
||||
</f:link.typolink>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:if condition="{row.rooms.{type}.available}">
|
||||
<f:else>
|
||||
<span class="line-through text-zinc-500">{row.rooms.{type}.price}</span>
|
||||
<span class="line-through text-zinc-500">{row.rooms.{type}.price -> ep:price()}</span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:else>
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
data-room="{row.minPrice}"
|
||||
data-bus="{row.busPrice}"
|
||||
data-discount="{row.discount}">
|
||||
{row.minPrice}
|
||||
{row.minPrice -> ep:price()}
|
||||
</span>
|
||||
</a>
|
||||
</f:then>
|
||||
@@ -279,7 +279,7 @@
|
||||
data-room="{row.minPrice}"
|
||||
data-bus="{row.busPrice}"
|
||||
data-discount="{row.discount}">
|
||||
{row.minPrice} €
|
||||
{row.minPrice -> ep:price()} €
|
||||
</span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Abpreis</th>
|
||||
<td>{date.minprice} €</td>
|
||||
<td>{date.minprice -> ep:price()} €</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Bus inkl.</th>
|
||||
@@ -154,7 +154,7 @@
|
||||
<tr>
|
||||
<td>{room.name}</td>
|
||||
<td>{v:variable.get(name: 'room.@pax')}</td>
|
||||
<td>{room.price} €</td>
|
||||
<td>{room.price -> ep:price()} €</td>
|
||||
<td>{room.available}</td>
|
||||
</tr>
|
||||
</f:for>
|
||||
|
||||
Reference in new issue
Block a user