Add age range based prices to groups price config

This commit is contained in:
Björn Fromme
2021-08-09 17:34:58 +02:00
parent 3916dede50
commit fc42801c8a
7 changed files with 576 additions and 299 deletions
@@ -95,6 +95,9 @@ class AjaxGroupsPriceController extends ActionController
'dateFrom' => $groupsPriceInquiry->getDateFrom(), 'dateFrom' => $groupsPriceInquiry->getDateFrom(),
'dateTo' => $groupsPriceInquiry->getDateTo(), 'dateTo' => $groupsPriceInquiry->getDateTo(),
'pax' => $groupsPriceInquiry->getPax(), 'pax' => $groupsPriceInquiry->getPax(),
'paxGroup1' => $groupsPriceInquiry->getPaxGroup1(),
'paxGroup2' => $groupsPriceInquiry->getPaxGroup2(),
'paxGroup3' => $groupsPriceInquiry->getPaxGroup3(),
'options' => $selectedOptions, 'options' => $selectedOptions,
'board' => $groupsPriceInquiry->getBoard(), 'board' => $groupsPriceInquiry->getBoard(),
'summary' => $groupsPriceInquiry->getSummary(), 'summary' => $groupsPriceInquiry->getSummary(),
@@ -66,10 +66,24 @@ class GroupsPriceInquiry
/** /**
* @var string * @var string
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/ */
protected $pax; protected $pax;
/**
* @var string
*/
protected $paxGroup1;
/**
* @var string
*/
protected $paxGroup2;
/**
* @var string
*/
protected $paxGroup3;
/** /**
* @var array * @var array
*/ */
@@ -197,6 +211,54 @@ class GroupsPriceInquiry
$this->pax = $pax; $this->pax = $pax;
} }
/**
* @return string
*/
public function getPaxGroup1()
{
return $this->paxGroup1;
}
/**
* @param string $paxGroup1
*/
public function setPaxGroup1($paxGroup1)
{
$this->paxGroup1 = $paxGroup1;
}
/**
* @return string
*/
public function getPaxGroup2()
{
return $this->paxGroup2;
}
/**
* @param string $paxGroup2
*/
public function setPaxGroup2($paxGroup2)
{
$this->paxGroup2 = $paxGroup2;
}
/**
* @return string
*/
public function getPaxGroup3()
{
return $this->paxGroup3;
}
/**
* @param string $paxGroup3
*/
public function setPaxGroup3($paxGroup3)
{
$this->paxGroup3 = $paxGroup3;
}
/** /**
* @return array * @return array
*/ */
@@ -56,6 +56,26 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
*/ */
protected $priceAdditionalPerson; protected $priceAdditionalPerson;
/**
* @var float
*/
protected $priceAdditionalPersonGroup1;
/**
* @var float
*/
protected $priceAdditionalPersonGroup2;
/**
* @var float
*/
protected $priceAdditionalPersonGroup3;
/**
* @var bool
*/
protected $bonusCardIncluded = false;
/** /**
* @return \DateTime * @return \DateTime
*/ */
@@ -136,6 +156,70 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
$this->priceAdditionalPerson = $priceAdditionalPerson; $this->priceAdditionalPerson = $priceAdditionalPerson;
} }
/**
* @return float
*/
public function getPriceAdditionalPersonGroup1(): float
{
return $this->priceAdditionalPersonGroup1;
}
/**
* @param float $priceAdditionalPersonGroup1
*/
public function setPriceAdditionalPersonGroup1(float $priceAdditionalPersonGroup1): void
{
$this->priceAdditionalPersonGroup1 = $priceAdditionalPersonGroup1;
}
/**
* @return float
*/
public function getPriceAdditionalPersonGroup2(): float
{
return $this->priceAdditionalPersonGroup2;
}
/**
* @param float $priceAdditionalPersonGroup2
*/
public function setPriceAdditionalPersonGroup2(float $priceAdditionalPersonGroup2): void
{
$this->priceAdditionalPersonGroup2 = $priceAdditionalPersonGroup2;
}
/**
* @return float
*/
public function getPriceAdditionalPersonGroup3(): float
{
return $this->priceAdditionalPersonGroup3;
}
/**
* @param float $priceAdditionalPersonGroup3
*/
public function setPriceAdditionalPersonGroup3(float $priceAdditionalPersonGroup3): void
{
$this->priceAdditionalPersonGroup3 = $priceAdditionalPersonGroup3;
}
/**
* @return bool
*/
public function isBonusCardIncluded(): bool
{
return $this->bonusCardIncluded;
}
/**
* @param bool $bonusCardIncluded
*/
public function setBonusCardIncluded(bool $bonusCardIncluded): void
{
$this->bonusCardIncluded = $bonusCardIncluded;
}
/** /**
* @return array * @return array
*/ */
@@ -148,6 +232,12 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
'personsIncluded' => $this->getPersonsIncluded(), 'personsIncluded' => $this->getPersonsIncluded(),
'price' => $this->getPrice(), 'price' => $this->getPrice(),
'priceAdditionalPerson' => $this->getPriceAdditionalPerson(), 'priceAdditionalPerson' => $this->getPriceAdditionalPerson(),
'priceAdditionalPersonByGroup' => [
1 => $this->getPriceAdditionalPersonGroup1(),
2 => $this->getPriceAdditionalPersonGroup2(),
3 => $this->getPriceAdditionalPersonGroup3(),
],
'bonusCardIncluded' => $this->isBonusCardIncluded(),
]; ];
} }
} }
@@ -25,16 +25,20 @@ return [
], ],
'interface' => [ 'interface' => [
'showRecordFieldList' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, date_from, date_to, 'showRecordFieldList' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, date_from, date_to,
persons_included, price, price_additional_person', persons_included, bonus_card_included, price, price_additional_person,
price_additional_person_group_1, price_additional_person_group_2, price_additional_person_group_3',
], ],
'types' => [ 'types' => [
'1' => [ '1' => [
'showitem' => '--palette--;;dates, --palette--;;prices, persons_included', 'showitem' => '--palette--;;dates, --palette--;;included, --palette--;;prices, --palette--;;prices_groups',
], ],
], ],
'palettes' => [ 'palettes' => [
'dates' => ['showitem' => 'date_from, date_to'], 'dates' => ['showitem' => 'date_from, date_to'],
'included' => ['showitem' => 'bonus_card_included, persons_included'],
'prices' => ['showitem' => 'price, price_additional_person'], 'prices' => ['showitem' => 'price, price_additional_person'],
'prices_groups' => ['showitem' => 'price_additional_person_group_1, price_additional_person_group_2,
--linebreak--, price_additional_person_group_3'],
], ],
'columns' => [ 'columns' => [
@@ -50,6 +54,7 @@ return [
['LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages', -1], ['LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages', -1],
['LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.default_value', 0] ['LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.default_value', 0]
], ],
'default' => -1,
], ],
], ],
'l10n_parent' => [ 'l10n_parent' => [
@@ -166,11 +171,49 @@ return [
'price_additional_person' => [ 'price_additional_person' => [
'exclude' => false, 'exclude' => false,
'label' => 'Preis weitere Person', 'label' => 'Preis weitere Person',
'description' => 'ohne Jokercard, alle Altersgruppen',
'config' => [ 'config' => [
'type' => 'input', 'type' => 'input',
'size' => 8, 'size' => 8,
'eval' => 'double2,required', 'eval' => 'double2',
] ]
], ],
'price_additional_person_group_1' => [
'exclude' => false,
'label' => 'Preis weitere Person',
'description' => 'mit Jokercard, 4-6 Jahre',
'config' => [
'type' => 'input',
'size' => 8,
'eval' => 'double2',
]
],
'price_additional_person_group_2' => [
'exclude' => false,
'label' => 'Preis weitere Person',
'description' => 'mit Jokercard, 7-14 Jahre',
'config' => [
'type' => 'input',
'size' => 8,
'eval' => 'double2',
]
],
'price_additional_person_group_3' => [
'exclude' => false,
'label' => 'Preis weitere Person',
'description' => 'mit Jokercard, ab 15 Jahre',
'config' => [
'type' => 'input',
'size' => 8,
'eval' => 'double2',
]
],
'bonus_card_included' => [
'exclude' => 0,
'label' => 'Jokercard inklusive',
'config' => [
'type' => 'check',
],
],
], ],
]; ];
@@ -288,6 +288,10 @@ CREATE TABLE tx_epproducts_domain_model_groupspriceconfig
persons_included int(11) unsigned DEFAULT '0' NOT NULL, persons_included int(11) unsigned DEFAULT '0' NOT NULL,
price float(8) unsigned DEFAULT '0' NOT NULL, price float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person float(8) unsigned DEFAULT '0' NOT NULL, price_additional_person float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person_group_1 float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person_group_2 float(8) unsigned DEFAULT '0' NOT NULL,
price_additional_person_group_3 float(8) unsigned DEFAULT '0' NOT NULL,
bonus_card_included tinyint(4) unsigned DEFAULT '0' NOT NULL,
hotel int(11) unsigned DEFAULT '0', hotel int(11) unsigned DEFAULT '0',
tstamp int(11) unsigned DEFAULT '0' NOT NULL, tstamp int(11) unsigned DEFAULT '0' NOT NULL,
@@ -6,7 +6,23 @@
</div> </div>
<div class="form-group" v-show="!submitted"> <div class="form-group" v-show="!submitted">
<label>Personenzahl</label> <label>Personenzahl</label>
<input type="number" class="form-control" v-model="selectedPax" @change="onPaxUpdated()"/> <input type="number" class="form-control" v-model.number="selectedPax" @change="onPaxUpdated()"
v-show="nightsCount > 0 && !includesBonusCard"/>
<div class="flex space-x-4" v-show="nightsCount > 0 && includesBonusCard">
<div class="w-1/3 flex flex-col space-y-1">
<span>ab 15 Jahre</span>
<input type="number" class="form-control" v-model.number="selectedPaxGroup3" @change="onPaxUpdated()"/>
</div>
<div class="w-1/3 flex flex-col space-y-1">
<span>7-14 Jahre</span>
<input type="number" class="form-control" v-model.number="selectedPaxGroup2" @change="onPaxUpdated()"/>
</div>
<div class="w-1/3 flex flex-col space-y-1">
<span>4-6 Jahre</span>
<input type="number" class="form-control" v-model.number="selectedPaxGroup1" @change="onPaxUpdated()"/>
</div>
</div>
<p class="text-sm italic" v-show="nightsCount === 0">Bitte wählen Sie einen Zeitraum</p>
</div> </div>
<div class="form-group" v-show="options.length > 0 && !submitted"> <div class="form-group" v-show="options.length > 0 && !submitted">
<label>Optionale Zusatzleistungen</label> <label>Optionale Zusatzleistungen</label>
@@ -22,7 +38,9 @@
<label>Optionale Verpflegungsleistungen</label> <label>Optionale Verpflegungsleistungen</label>
<select class="form-control" v-model="selectedBoard" v-if="selectedPax >= 30"> <select class="form-control" v-model="selectedBoard" v-if="selectedPax >= 30">
<option :value="null">Keine Auswahl</option> <option :value="null">Keine Auswahl</option>
<option v-for="board of boards" :value="board" :key="board.uid">{{ board.title }} ({{ formatCurrency(board.price) }} pro Person und Nacht)</option> <option v-for="board of boards" :value="board" :key="board.uid">{{ board.title }}
({{ formatCurrency(board.price) }} pro Person und Nacht)
</option>
</select> </select>
<div v-show="selectedPax < 30"> <div v-show="selectedPax < 30">
<span class="label label-warning">Erst ab 30 Personen buchbar.</span> <span class="label label-warning">Erst ab 30 Personen buchbar.</span>
@@ -63,7 +81,8 @@
</tr> </tr>
<tr> <tr>
<th>Gesamtpreis</th> <th>Gesamtpreis</th>
<td><strong>{{ formatCurrency(priceTotal) }}</strong><br><small>zzgl. vor Ort zu entrichtender Ortstaxe</small></td> <td><strong>{{ formatCurrency(priceTotal) }}</strong><br><small>zzgl. vor Ort zu entrichtender
Ortstaxe</small></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -155,6 +174,9 @@
selectedTo: null, selectedTo: null,
selectedRange: [], selectedRange: [],
selectedPax: 30, selectedPax: 30,
selectedPaxGroup1: 0,
selectedPaxGroup2: 0,
selectedPaxGroup3: 30,
selectedOptions: [], selectedOptions: [],
selectedBoard: null, selectedBoard: null,
name: null, name: null,
@@ -225,7 +247,10 @@
'tx_epproducts_ajax[groupsPriceInquiry][remarks]': this.remarks, 'tx_epproducts_ajax[groupsPriceInquiry][remarks]': this.remarks,
'tx_epproducts_ajax[groupsPriceInquiry][dateFrom]': this.selectedFrom.format('DD.MM.YYYY'), 'tx_epproducts_ajax[groupsPriceInquiry][dateFrom]': this.selectedFrom.format('DD.MM.YYYY'),
'tx_epproducts_ajax[groupsPriceInquiry][dateTo]': this.selectedTo.format('DD.MM.YYYY'), 'tx_epproducts_ajax[groupsPriceInquiry][dateTo]': this.selectedTo.format('DD.MM.YYYY'),
'tx_epproducts_ajax[groupsPriceInquiry][pax]': this.selectedPax, 'tx_epproducts_ajax[groupsPriceInquiry][pax]': this.includesBonusCard ? '' : this.selectedPax,
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup1]': this.includesBonusCard ? this.selectedPaxGroup1 : '',
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup2]': this.includesBonusCard ? this.selectedPaxGroup2 : '',
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup3]': this.includesBonusCard ? this.selectedPaxGroup3 : '',
'tx_epproducts_ajax[groupsPriceInquiry][board]': this.selectedBoard ? this.selectedBoard.uid : null, 'tx_epproducts_ajax[groupsPriceInquiry][board]': this.selectedBoard ? this.selectedBoard.uid : null,
'tx_epproducts_ajax[groupsPriceInquiry][options]': options, 'tx_epproducts_ajax[groupsPriceInquiry][options]': options,
'tx_epproducts_ajax[groupsPriceInquiry][summary]': this.priceSummary 'tx_epproducts_ajax[groupsPriceInquiry][summary]': this.priceSummary
@@ -259,21 +284,57 @@
return this.selectedFrom.format('DD.MM.YYYY') + ' - ' + this.selectedTo.format('DD.MM.YYYY'); return this.selectedFrom.format('DD.MM.YYYY') + ' - ' + this.selectedTo.format('DD.MM.YYYY');
}, },
pricePax () { includesBonusCard() {
let base = 0; let included = false
let additional = 0;
for (let date of this.selectedRange) { for (let date of this.selectedRange) {
for (let config of this.configs) { for (let config of this.configs) {
if (date >= dayjs(config.dateFrom) && date < dayjs(config.dateTo)) { if (date >= dayjs(config.dateFrom) && date < dayjs(config.dateTo)) {
base += config.price; if (config.bonusCardIncluded) {
if (this.selectedPax > config.personsIncluded) { included = true
}
}
}
}
return included
},
pricePax() {
let base = 0
let additional = 0
for (let date of this.selectedRange) {
for (let config of this.configs) {
if (date >= dayjs(config.dateFrom) && date < dayjs(config.dateTo)) {
base += config.price
if (this.totalPax > config.personsIncluded) {
if (false === config.bonusCardIncluded) {
additional += (this.selectedPax - config.personsIncluded) * config.priceAdditionalPerson; additional += (this.selectedPax - config.personsIncluded) * config.priceAdditionalPerson;
} else {
// Distribute additional persons costs by group
let included = config.personsIncluded
if (this.selectedPaxGroup3 <= included) {
included -= this.selectedPaxGroup3
} else {
additional += (this.selectedPaxGroup3 - included) * config.priceAdditionalPersonByGroup[3]
included = 0
}
if (this.selectedPaxGroup2 <= included) {
included -= this.selectedPaxGroup2
} else {
additional += (this.selectedPaxGroup2 - included) * config.priceAdditionalPersonByGroup[2]
included = 0
}
if (this.selectedPaxGroup1 > included) {
additional += (this.selectedPaxGroup1 - included) * config.priceAdditionalPersonByGroup[1]
}
}
} }
} }
} }
} }
return {base, additional}; return {base, additional};
}, },
totalPax() {
return this.includesBonusCard ? this.selectedPaxGroup1 + this.selectedPaxGroup2 + this.selectedPaxGroup3 : this.selectedPax
},
priceOptions() { priceOptions() {
let price = 0; let price = 0;
for (let option of this.selectedOptions) { for (let option of this.selectedOptions) {
@@ -283,11 +344,11 @@
if (1 === option.type) { if (1 === option.type) {
price += option.price; price += option.price;
} else if (2 === option.type) { } else if (2 === option.type) {
price += option.price * this.selectedPax; price += option.price * this.totalPax;
} else if (3 === option.type) { } else if (3 === option.type) {
price += option.price * this.nightsCount; price += option.price * this.nightsCount;
} else if (4 === option.type) { } else if (4 === option.type) {
price += option.price * this.nightsCount * this.selectedPax; price += option.price * this.nightsCount * this.totalPax;
} }
} }
return price; return price;
@@ -295,13 +356,13 @@
priceBoard() { priceBoard() {
let price = 0; let price = 0;
if (this.selectedBoard) { if (this.selectedBoard) {
price = this.selectedBoard.price * this.nightsCount * this.selectedPax; price = this.selectedBoard.price * this.nightsCount * this.totalPax;
} }
return price; return price;
}, },
priceShortTerm() { priceShortTerm() {
let price = 0; let price = 0;
const totalPricePax = this.pricePax.base + this.pricePax.additional; const totalPricePax = (this.pricePax.base + this.pricePax.additional);
if (2 === this.nightsCount) { if (2 === this.nightsCount) {
price = totalPricePax * 0.2; price = totalPricePax * 0.2;
} }
@@ -356,9 +417,11 @@
.form-wrapper { .form-wrapper {
position: relative; position: relative;
} }
.form-control[readonly] { .form-control[readonly] {
background-color: white; background-color: white;
} }
.form-overlay { .form-overlay {
position: fixed; position: fixed;
top: 0; top: 0;
@@ -29,7 +29,19 @@
</tr> </tr>
<tr> <tr>
<th>Anzahl der Personen</th> <th>Anzahl der Personen</th>
<td>{pax}</td> <td>{pax -> v:or(alternative: '-')}</td>
</tr>
<tr>
<th>Anzahl der Personen (ab 15 Jahre)</th>
<td>{paxGroup3 -> v:or(alternative: '-')}</td>
</tr>
<tr>
<th>Anzahl der Personen (7-14 Jahre)</th>
<td>{paxGroup2 -> v:or(alternative: '-')}</td>
</tr>
<tr>
<th>Anzahl der Personen (4-6 Jahre)</th>
<td>{paxGroup1 -> v:or(alternative: '-')}</td>
</tr> </tr>
<tr> <tr>
<th>Verpflegung</th> <th>Verpflegung</th>