* Fertigstellung Buchungsformular (#1321) (SternTours-CRM-API-Anbindung, Mailversand, Validierung, Dynamische Preisberechnung, Persistierung von Buchungsinformationen)
* Fehler bei der Preisberechnung behoben * Farbschema geändert (Kevin Adametz) git-svn-id: http://78.47.251.156/svn/dev/sterntours-3@3289 f459cee4-fb09-11de-96c3-f9c5f16c3c76
This commit is contained in:
parent
dde3b91724
commit
3a28866cd2
36 changed files with 2200 additions and 268 deletions
|
|
@ -21,7 +21,6 @@
|
|||
{% stylesheets
|
||||
'bundles/app/css/bootstrap-3.3.7.css'
|
||||
'bundles/app/css/custom.css'
|
||||
'bundles/app/css/booking.css'
|
||||
filter='cssrewrite'
|
||||
%}
|
||||
<link rel="stylesheet" href="{{ asset_url }}"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<table class="st-booking-table">
|
||||
<tbody>
|
||||
{% for summary_entry in summary %}
|
||||
<tr>
|
||||
<td class="st-position-price-col">
|
||||
{{ summary_entry.value|number_format(2) }} €
|
||||
</td>
|
||||
<td class="st-position-name-col">
|
||||
{{ summary_entry.label|raw }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr class="st-total-tr">
|
||||
<td class="st-position-price-col">
|
||||
<span class="st-total-price">= {{ total_price|number_format(2) }} €</span>
|
||||
</td>
|
||||
<td class="st-position-name-col">Gesamtpreis der Reise</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{# @var booking_request \AppBundle\Entity\BookingRequest #}
|
||||
Sehr geehrte{{ booking_request.salutation == 1 ? 'r Herr' : ' Frau' }} {{ booking_request.lastName }},
|
||||
|
||||
vielen Dank für Ihren Buchungsauftrag. Dieser wird schnellstmöglich bearbeitet und stellt noch keine{#
|
||||
#} Buchungsbestätigung dar. Bitte prüfen Sie noch einmal Ihre Angaben und kontaktieren Sie uns bitte, wenn ein Fehler{#
|
||||
#} enthalten ist.
|
||||
|
||||
{% include 'default/email/components/bookingSummary.txt.twig' %}
|
||||
|
||||
{% include 'default/email/components/signature.txt.twig' %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
FOLGENDE REISE WURDE GEBUCHT:
|
||||
|
||||
URL: {{ travel_program_url }}
|
||||
|
||||
CRM: {{ crm_url }}
|
||||
|
||||
{% include 'default/email/components/bookingSummary.txt.twig' %}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
{# @var booking_request \AppBundle\Entity\BookingRequest #}
|
||||
=====================================================================================
|
||||
Reisedaten:
|
||||
=====================================================================================
|
||||
Reiseprogramm: {{ travel_date.travelProgram.title }} ({{ travel_date.name }})
|
||||
Kategorie: Standard
|
||||
Reisezeitraum: {{ travel_date.start|date }} - {{ travel_date.end|date }}
|
||||
Abfahrts-/Abflugort: {{ booking_request.departure.name }} {{ booking_request.departure.extraCharge|number_format(2) }} € p.P.
|
||||
|
||||
{% for room in booking_price_info['rooms'] %}
|
||||
1x {{ room['name'] }} [Personen: {{ room.adults }} x {{ room['price']|number_format(2) }} €]
|
||||
{% endfor %}
|
||||
{{ booking_request.departure.extraCharge < 0 ? 'Aufschlag' : 'Abzug' }} Abfahrts-/Abflugort {{ booking_request.departure.name }}{#
|
||||
#} {{ booking_request.travelerCount }} x {{ booking_request.departure.extraCharge|number_format(2) }} €: {{
|
||||
(booking_request.travelerCount * booking_request.departure.extraCharge)|number_format(2) }} €
|
||||
{% for insuranceInfo in booking_price_info['insurances'] %}
|
||||
{{ insuranceInfo['count'] }}x RV {{ insuranceInfo['insurance'].name }} ({{ insuranceInfo['insurancePrice'].code -}}
|
||||
) {{ insuranceInfo['insurancePriceValue']|number_format(2) }} €: {{ (insuranceInfo['count'] *
|
||||
insuranceInfo['insurancePriceValue'])|number_format(2) }} €
|
||||
{% endfor %}
|
||||
{% for option in booking_request.travelOptions %}
|
||||
{{ booking_request.travelerCount }}x zugebuchte Leistung (Erwachsener): {{ option.name }} {{ option.price|number_format(2) -}}
|
||||
€: {{ (booking_request.travelerCount * option.price|number_format(2)) }} €
|
||||
{% endfor %}
|
||||
{% for classOption in booking_price_info['classOptions'] %}
|
||||
{{ classOption['count'] }}x {{ classOption['name'] }} {{ classOption['price']|number_format(2) }} €: {{
|
||||
(classOption['count'] * classOption['price'])|number_format(2) }} €
|
||||
{% endfor %}
|
||||
|
||||
Gesamtpreis: {{ booking_price_info['total']|number_format(2) }} €
|
||||
|
||||
=====================================================================================
|
||||
Reiseleistungen:
|
||||
=====================================================================================
|
||||
Eingeschlossene Leistungen:
|
||||
{% for travel_program_service in travel_date.travelProgram.included|split('\n') %}
|
||||
[x] {{ travel_program_service|raw }}
|
||||
{% endfor %}
|
||||
|
||||
Nicht eingeschlossene, zubuchbare Leistungen:
|
||||
{% for travel_program_service in travel_date.travelProgram.excluded|split('\n') %}
|
||||
[o] {{ travel_program_service|raw }}
|
||||
{% endfor %}
|
||||
|
||||
=====================================================================================
|
||||
Reiseanmelder{% if booking_request.salutation == 2 %}in{% endif %}
|
||||
|
||||
=====================================================================================
|
||||
Vorname: {{ booking_request.firstName }}
|
||||
Nachname: {{ booking_request.lastName }}
|
||||
Adresse: {{ booking_request.streetAddress }}
|
||||
PLZ: {{ booking_request.zipCode }}
|
||||
Ort: {{ booking_request.city }}
|
||||
Telefonnummer: {{ booking_request.phone }}
|
||||
Fax: {{ booking_request.fax ?? 'keine Angabe' }}
|
||||
|
||||
=====================================================================================
|
||||
Reiseteilnehmer:
|
||||
#) Geschlecht, Vorname, Nachname, Geburtsdatum
|
||||
=====================================================================================
|
||||
{% for traveler in booking_request.travelers|slice(0, booking_request.travelerCount) %}
|
||||
{{ loop.index }}) {{ traveler.sex == 1 ? 'männlich' : 'weiblich' }}, {{ traveler.firstName }}, {{ traveler.lastName -}}
|
||||
, {{ traveler.birthDate|date }}
|
||||
{% endfor %}
|
||||
|
||||
=====================================================================================
|
||||
Mitteilungen / Sonstiges:
|
||||
=====================================================================================
|
||||
{{ booking_request.notes ?? '-' }}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
Mit freundlichen Grüßen
|
||||
Ihr Team von STERN TOURS
|
||||
|
||||
--
|
||||
|
||||
STERN TOURS Travelservice GmbH
|
||||
Uhlandstr. 137
|
||||
10717 Berlin
|
||||
|
||||
Geschäftsführer: Thomas Stern
|
||||
E-Mail: stern@stern-tours.de
|
||||
Tel.: 030 / 700 94 100
|
||||
Fax: 030 / 700 94 1044
|
||||
|
||||
Registergericht: Amtsgericht Charlottenburg
|
||||
Registernummer: HRB 67111
|
||||
Steuernummer: 27/016/10728
|
||||
UST-Ident.-Nr.: DE192609253
|
||||
Finanzamt: Wilmersdorf
|
||||
19
trunk/app/Resources/views/default/form/helpers.html.twig
Normal file
19
trunk/app/Resources/views/default/form/helpers.html.twig
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{%- block form_field -%}
|
||||
{{- form_label(form, label, opt ?? {}) -}}
|
||||
{{- form_widget(form, opt ?? {}) -}}
|
||||
{{- form_errors(form) -}}
|
||||
{%- endblock form_field -%}
|
||||
|
||||
{%- block form_field_pho -%}
|
||||
{%- set opt = opt|merge({
|
||||
label_attr: (opt.label_attr ?? {})|merge({class: (opt.label_attr.class|default('') ~ ' sr-only')|trim}),
|
||||
attr: (opt.attr ?? {})|merge({placeholder: opt.attr.placeholder|default(
|
||||
(form.vars.translation_domain is same as(false) ? label : label|trans({}, form.vars.translation_domain)) ~
|
||||
((opt.required ?? form.vars.required) ? ' *' : '')
|
||||
)})
|
||||
}) -%}
|
||||
{#{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' sr-only')|trim}) -%}#}
|
||||
{{- form_label(form, label, opt) -}}
|
||||
{{- form_widget(form, opt) -}}
|
||||
{{- form_errors(form, opt) -}}
|
||||
{%- endblock form_field_pho -%}
|
||||
32
trunk/app/Resources/views/default/form/theme.html.twig
Normal file
32
trunk/app/Resources/views/default/form/theme.html.twig
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{% use 'form_div_layout.html.twig' with
|
||||
choice_widget_collapsed as base_choice_widget_collapsed,
|
||||
checkbox_widget as base_checkbox_widget,
|
||||
radio_widget as base_radio_widget
|
||||
%}
|
||||
{% use 'bootstrap_3_layout.html.twig' %}
|
||||
|
||||
{% block choice_widget_collapsed -%}
|
||||
{% set attr = attr|merge({
|
||||
class: (attr.class|default('') ~ ' selectpicker')|trim,
|
||||
'data-style': attr['data-style']|default('btn-white'),
|
||||
'data-dropout': attr['data-dropout']|default('false')
|
||||
}) %}
|
||||
<div class="dropdown">
|
||||
{{- block('base_choice_widget_collapsed') -}}
|
||||
</div>
|
||||
{%- endblock %}
|
||||
|
||||
{% block checkbox_widget -%}
|
||||
{{- block('base_checkbox_widget') -}}
|
||||
{%- endblock checkbox_widget %}
|
||||
|
||||
{% block radio_widget -%}
|
||||
{{- block('base_radio_widget') -}}
|
||||
{%- endblock radio_widget %}
|
||||
|
||||
{% block form_label -%}
|
||||
{%- if required -%}
|
||||
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' st-required')|trim}) -%}
|
||||
{%- endif -%}
|
||||
{{- parent() -}}
|
||||
{%- endblock form_label %}
|
||||
|
|
@ -1,4 +1,19 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
{% form_theme form 'default/form/theme.html.twig' %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
{% stylesheets 'bundles/app/css/booking.css' filter='cssrewrite' %}
|
||||
<link rel="stylesheet" href="{{ asset_url }}"/>
|
||||
{% endstylesheets %}
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
{% javascripts '@AppBundle/Resources/public/js/booking.js' %}
|
||||
<script src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ include('default/components/breadcrumb.html.twig') }}
|
||||
|
|
@ -21,7 +36,10 @@
|
|||
|
||||
<div id="booking_form" class="booking_form">
|
||||
|
||||
<form id="contactform" class="" action="#" name="contactform" method="post">
|
||||
<form class="st-booking-form" method="post">
|
||||
|
||||
{{ form_errors(form) }}
|
||||
|
||||
<div id="message"></div>
|
||||
|
||||
<div class="form-box">
|
||||
|
|
@ -46,30 +64,27 @@
|
|||
<tr>
|
||||
<td>{{ form_label(form.departure, 'Abflugort') }}</td>
|
||||
<td>
|
||||
<div class="dropdown">
|
||||
{{ form_widget(form.departure, {'attr': {
|
||||
'class': 'selectpicker',
|
||||
'data-style': 'btn-white'
|
||||
}}) }}
|
||||
</div>
|
||||
{{ form_widget(form.departure) }}
|
||||
{{ form_errors(form.departure) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ form_label(form.travelerCount, 'Reiseteilnehmer') }}</td>
|
||||
<td><div class="dropdown">
|
||||
Erwachsene<br>
|
||||
{{ form_widget(form.travelerCount) }}
|
||||
</div>
|
||||
<td>
|
||||
Erwachsene<br>
|
||||
{{ form_widget(form.travelerCount) }}
|
||||
{{ form_errors(form.travelerCount) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Reiseversicherung</td>
|
||||
<td>
|
||||
<div class="radio">
|
||||
<input id="radio1" type="radio" name="radio">
|
||||
<label for="radio1">
|
||||
keine Reiseversicherung
|
||||
</label>
|
||||
<input id="st-no-insurance-opt" type="radio" value=""
|
||||
name="{{ form.insurance.vars.full_name }}"
|
||||
{% if form.insurance.vars.value == '' %}checked{% endif %}
|
||||
>
|
||||
<label for="st-no-insurance-opt">keine Reiseversicherung</label>
|
||||
</div>
|
||||
|
||||
{% for insuranceForm in form.insurance %}
|
||||
|
|
@ -78,6 +93,8 @@
|
|||
'insurance': form.insurance.vars.choices[insuranceForm.vars.value].data
|
||||
} %}
|
||||
{% endfor %}
|
||||
|
||||
{{ form_errors(form.insurance) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
@ -100,7 +117,7 @@
|
|||
{% for price in travel_date.prices %}
|
||||
{# @var price \AppBundle\Entity\TravelPeriodPrice #}
|
||||
<li>
|
||||
p.P. {{ price.priceComfort|number_format(2) }} €
|
||||
p.P. {{ price.effectiveComfortPrice|number_format(2) }} €
|
||||
{{ price_type_by_id[price.priceType.id].name }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
@ -128,23 +145,12 @@
|
|||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<h3>Ihr gewähltes Angebot</h3>
|
||||
<table class="st-booking-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-position-price-col">-700,00 €</td>
|
||||
<td class="st-position-name-col">
|
||||
Abzug für Abfahrts-/Abflugort "Eigenanreise" (2 x -350,00 €):
|
||||
<strong>-700,00 €</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="st-total-tr">
|
||||
<td class="st-position-price-col">
|
||||
<span class="st-total-price">= 3.921,68 €</span>
|
||||
</td>
|
||||
<td class="st-position-name-col">Gesamtpreis der Reise</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="st-booking-summary">
|
||||
{% include 'default/components/booking/summary.html.twig' with {
|
||||
'summary': summary,
|
||||
'total_price': total_price
|
||||
} %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -197,59 +203,44 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="dropdown">
|
||||
<select name="salutation" id="salutation" class="selectpicker" data-style="btn-white" data-dropup-auto="false">
|
||||
<option value="" selected="selected">Anrede (Bitte wählen)</option>
|
||||
<option value="1">Herr</option>
|
||||
<option value="2">Frau</option>
|
||||
</select>
|
||||
</div>
|
||||
{{ form_field_pho(form.salutation, 'Anrede', {'label_attr': {class: 'sr-only'}}) }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||
<input type="text" name="firstname" id="firstname" class="form-control" placeholder="Vorname *">
|
||||
{#<input type="text" name="firstname" id="firstname" class="form-control" placeholder="Vorname *">#}
|
||||
{{ form_field_pho(form.firstName, 'Vorname') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||
<input type="text" name="lastname" id="lastname" class="form-control" placeholder="Nachname *">
|
||||
{{ form_field_pho(form.lastName, 'Nachname') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 col-sm-12 col-xs-12">
|
||||
<input type="text" name="street" id="street" class="form-control" placeholder="Straße, Hausnummer">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||
<input type="text" name="plz" id="plz" class="form-control" placeholder="PLZ">
|
||||
{{ form_field_pho(form.streetAddress, 'Straße, Hausnummer') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||
<input type="text" name="ort" id="ort" class="form-control" placeholder="Ort">
|
||||
{{ form_field_pho(form.zipCode, 'PLZ') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||
{{ form_field_pho(form.city, 'Ort') }}
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="dropdown">
|
||||
<select name="country" class="selectpicker" data-style="btn-white" data-dropup-auto="false">
|
||||
<option value="" selected="selected">Land (Bitte wählen)</option>
|
||||
<option value="27">Deutschland</option>
|
||||
<option value="34">Österreich</option>
|
||||
<option value="181">Schweiz</option>
|
||||
<option value="196">Niederlande</option>
|
||||
<option value="197">Sonstiges</option>
|
||||
</select>
|
||||
</div>
|
||||
{{ form_field_pho(form.nation, 'Land') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||
<input type="text" name="firstname" id="firstname" class="form-control" placeholder="Telefon tagsüber *">
|
||||
{{ form_field_pho(form.phone, 'Telefon tagsüber') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||
<input type="text" name="lastname" id="lastname" class="form-control" placeholder="Fax (optional)">
|
||||
{{ form_field_pho(form.fax, 'Fax (optional)') }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 col-sm-12 col-xs-12">
|
||||
<input type="text" name="email" id="email" class="form-control" placeholder="E-Mail-Adresse *">
|
||||
{{ form_field_pho(form.email, 'E-Mail-Adresse') }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -274,53 +265,36 @@
|
|||
<th>Geburtsdatum (TT.MM.JJJJ)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td data-title="Nr.">
|
||||
<button class="btn btn-primary btn-sm border-radius">1</button>
|
||||
</td>
|
||||
<td data-title="Geschlecht">
|
||||
<div class="dropdown">
|
||||
<select name="salutation" id="salutation1" class="selectpicker" data-style="btn-white" data-dropup-auto="false">
|
||||
<option value="" selected="selected">Anrede (Bitte wählen)</option>
|
||||
<option value="1">Herr</option>
|
||||
<option value="2">Frau</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="Vorname">
|
||||
<input type="text" name="firstname" id="firstname1" class="form-control" placeholder="Vorname">
|
||||
</td>
|
||||
<td data-title="Nachname">
|
||||
<input type="text" name="firstname" id="firstname1" class="form-control" placeholder="Nachname">
|
||||
</td>
|
||||
<td data-title="Geburtsdatum">
|
||||
<input type="text" name="firstname" id="firstname1" class="form-control" placeholder="Geburtsdatum">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-title="Nr.">
|
||||
<button class="btn btn-primary btn-sm border-radius">2</button>
|
||||
</td>
|
||||
<td data-title="Geschlecht">
|
||||
<div class="dropdown">
|
||||
<select name="salutation" id="salutation2" class="selectpicker" data-style="btn-white" data-dropup-auto="false">
|
||||
<option value="" selected="selected">Anrede (Bitte wählen)</option>
|
||||
<option value="2">Herr</option>
|
||||
<option value="2">Frau</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="Vorname">
|
||||
<input type="text" name="firstname" id="firstname2" class="form-control" placeholder="Vorname">
|
||||
</td>
|
||||
<td data-title="Nachname">
|
||||
<input type="text" name="firstname" id="firstname2" class="form-control" placeholder="Nachname">
|
||||
</td>
|
||||
<td data-title="Geburtsdatum">
|
||||
<input type="text" name="firstname" id="firstname2" class="form-control" placeholder="Geburtsdatum">
|
||||
</td>
|
||||
</tr>
|
||||
<tbody class="st-travelers">
|
||||
|
||||
{% for traveler_form in form.travelers %}
|
||||
<tr class="st-traveler st-traveler-{{ loop.index }}"
|
||||
data-st-traveler-index="{{ loop.index }}"
|
||||
style="display: none;"
|
||||
>
|
||||
<td>
|
||||
<button class="btn btn-primary btn-sm border-radius st-traveller-index"
|
||||
type="button"
|
||||
>
|
||||
{{ loop.index ?? '' }}
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
{{ form_field_pho(traveler_form.sex, 'Geschlecht', {
|
||||
required: false
|
||||
}) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ form_field_pho(traveler_form.firstName, 'Vorname') }}
|
||||
</td>
|
||||
<td>
|
||||
{{ form_field_pho(traveler_form.lastName, 'Nachname') }}
|
||||
</td>
|
||||
<td>
|
||||
{{ form_field_pho(traveler_form.birthDate, 'Geburtsdatum') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -335,7 +309,10 @@
|
|||
<h5>Mitteilungen / Sonstiges (optional)</h5>
|
||||
</div>
|
||||
<div class="col-md-12 col-sm-12 col-xs-12">
|
||||
<textarea class="form-control" name="comments" id="comments" rows="6" placeholder=""></textarea>
|
||||
{{ form_field(form.notes, 'Mitteilungen / Sonstiges (optional)', {
|
||||
'label_attr': {'class': 'sr-only'},
|
||||
'attr': {'rows': '6'}
|
||||
}) }}
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- end form-box -->
|
||||
|
|
@ -345,7 +322,9 @@
|
|||
<div class="col-md-12 col-sm-12 col-xs-12">
|
||||
<h5>Zahlung</h5>
|
||||
|
||||
<p>Die gewünschte Zahlungsart (Rechnung, Überweisung, Sofortüberweisung, Kreditkarten, Barzahlung) stimmen wir mit Ihnen im Anschluss an Ihre Buchung ab.</p>
|
||||
<p>Die gewünschte Zahlungsart (Rechnung, Überweisung, Sofortüberweisung, Kreditkarten,
|
||||
Barzahlung) stimmen wir mit Ihnen im Anschluss an Ihre Buchung ab.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
@ -357,18 +336,28 @@
|
|||
<div class="col-md-12 col-sm-12 col-xs-12">
|
||||
<h5>Allgemeine Geschäftsbedingungen</h5>
|
||||
<div class="checkbox">
|
||||
<input id="checkbox4" type="checkbox">
|
||||
<label for="checkbox4">
|
||||
Ich habe alle Daten und Angaben auf Richtigkeit überprüft. Ich habe die <a href="#">Allgemeinen Geschäftsbedingungen des Reiseveranstalters</a> SKR sowie die <a href="#">Allgemeinen Geschäftsbedingungen des Reisevermittlers</a> gelesen und akzeptiert. Zugleich erkenne ich diese für alle Reiseteilnehmer an.
|
||||
{{ form_widget(form.acceptTerms) }}
|
||||
<label for="{{ form.acceptTerms.vars.id }}">
|
||||
Ich habe alle Daten und Angaben auf Richtigkeit überprüft. Ich habe die
|
||||
<a href="#">Allgemeinen Geschäftsbedingungen des Reiseveranstalters</a> SKR
|
||||
sowie die <a href="#">Allgemeinen Geschäftsbedingungen des Reisevermittlers</a>
|
||||
gelesen und akzeptiert. Zugleich erkenne ich diese für alle Reiseteilnehmer an.
|
||||
</label>
|
||||
{{ form_errors(form.acceptTerms) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12 col-sm-12 col-xs-12">
|
||||
<button type="submit" value="SEND" id="submit" class="aligncenter btn btn-primary btn-lg border-radius">kostenpflichtig<br class="visible-xs"> buchen</button>
|
||||
<button type="submit" value="SEND" id="submit"
|
||||
class="aligncenter btn btn-primary btn-lg border-radius"
|
||||
>
|
||||
kostenpflichtig<br class="visible-xs"> buchen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- end form-box -->
|
||||
|
||||
|
||||
{{ form_rest(form) }}
|
||||
</form>
|
||||
|
||||
</div><!-- end contact-form -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Vielen Dank für Ihren Buchungsauftrag!</h1>
|
||||
{% endblock %}
|
||||
|
|
@ -5,12 +5,12 @@ imports:
|
|||
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||
# http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
|
||||
parameters:
|
||||
locale: en
|
||||
#parameters:
|
||||
# locale: en
|
||||
|
||||
framework:
|
||||
#esi: ~
|
||||
#translator: { fallbacks: ["%locale%"] }
|
||||
translator: { fallbacks: ["%locale%", "en"] }
|
||||
secret: "%secret%"
|
||||
router:
|
||||
resource: "%kernel.root_dir%/config/routing.yml"
|
||||
|
|
@ -31,8 +31,8 @@ framework:
|
|||
fragments: ~
|
||||
http_method_override: true
|
||||
assets: ~
|
||||
profiler:
|
||||
collect: false
|
||||
#profiler:
|
||||
# collect: false
|
||||
|
||||
# Twig Configuration
|
||||
twig:
|
||||
|
|
@ -88,7 +88,7 @@ assetic:
|
|||
cssrewrite: ~
|
||||
|
||||
stof_doctrine_extensions:
|
||||
default_locale: en_US
|
||||
default_locale: de_DE
|
||||
orm:
|
||||
default:
|
||||
tree: true
|
||||
|
|
@ -21,6 +21,9 @@ monolog:
|
|||
console:
|
||||
type: console
|
||||
channels: [!event, !doctrine]
|
||||
browser_console:
|
||||
type: browser_console
|
||||
level: debug
|
||||
# uncomment to get logging in your browser
|
||||
# you may have to allow bigger header sizes in your Web server configuration
|
||||
#firephp:
|
||||
|
|
|
|||
|
|
@ -19,3 +19,4 @@ parameters:
|
|||
secret: ThisTokenIsNotSoSecretChangeIt
|
||||
|
||||
st_cache_driver: array
|
||||
locale: de_DE
|
||||
|
|
|
|||
|
|
@ -14,4 +14,22 @@ services:
|
|||
- "@doctrine.orm.entity_manager"
|
||||
- "@controller_resolver"
|
||||
tags:
|
||||
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
|
||||
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
|
||||
|
||||
app.twig_extension:
|
||||
class: AppBundle\Twig\AppExtension
|
||||
#public: false
|
||||
arguments:
|
||||
- '@twig'
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
|
||||
app.booking_exporter:
|
||||
class: AppBundle\Export\SternToursCrmBookingExporter
|
||||
arguments:
|
||||
- '@monolog.logger'
|
||||
|
||||
app.booking_request_validator:
|
||||
class: AppBundle\Validator\BookingRequestValidator
|
||||
tags:
|
||||
- {name: validator.constraint_validator }
|
||||
|
|
@ -10,14 +10,32 @@ namespace AppBundle\Controller;
|
|||
use AppBundle\Entity\BookingRequest;
|
||||
use AppBundle\Entity\BreadcrumbEntry;
|
||||
use AppBundle\Entity\Page;
|
||||
use AppBundle\Entity\TravelDate;
|
||||
use AppBundle\Entity\Traveler;
|
||||
use AppBundle\Entity\TravelPeriodPrice;
|
||||
use AppBundle\Entity\TravelPeriodPriceType;
|
||||
use AppBundle\Form\BookingRequestType;
|
||||
use AppBundle\Util;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class BookingController extends Controller
|
||||
{
|
||||
public function indexAction(Page $travelProgramPage, Request $request)
|
||||
/** @var TravelPeriodPriceType[] $priceTypeById */
|
||||
private $priceTypeById;
|
||||
|
||||
/**
|
||||
* The routing for this action is entirely controlled by KernelControllerListener!
|
||||
*
|
||||
* @param Page $travelProgramPage
|
||||
* @param Request $request
|
||||
* @param $action
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function indexAction(Page $travelProgramPage, $action, Request $request)
|
||||
{
|
||||
$travelProgram = $travelProgramPage->getTravelProgram();
|
||||
if (!$request->query->has('nr'))
|
||||
|
|
@ -26,6 +44,8 @@ class BookingController extends Controller
|
|||
}
|
||||
$this->getDoctrine()->getRepository('AppBundle:TravelPeriod')->getTrueTravelPeriods($travelProgram);
|
||||
|
||||
$this->priceTypeById = $this->getDoctrine()->getRepository('AppBundle:TravelPeriodPriceType')->findAllIndexedById();
|
||||
|
||||
// #TODO Consider changing key of travel dates
|
||||
foreach ($travelProgram->getTravelDates() as $curTravelDate)
|
||||
{
|
||||
|
|
@ -40,31 +60,355 @@ class BookingController extends Controller
|
|||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$form = $this->createForm(BookingRequestType::class, null, [
|
||||
/** @var BookingRequest $bookingRequest */
|
||||
$bookingRequest = new BookingRequest();
|
||||
if ($request->getMethod() != 'POST')
|
||||
{
|
||||
$bookingRequest->setTravelerCount(2);
|
||||
$bookingRequest->setDeparture($travelDate->getDepartures()[0]);
|
||||
}
|
||||
$form = $this->createForm(BookingRequestType::class, $bookingRequest, [
|
||||
'travel_date' => $travelDate,
|
||||
'travel_program' => $travelProgram
|
||||
]);
|
||||
//$form->submit([]);
|
||||
if ($request->getMethod() == 'POST')
|
||||
{
|
||||
$form->handleRequest($request);
|
||||
$bookingRequest = $form->getData();
|
||||
}
|
||||
$htmlSummary = [];
|
||||
$bookingPriceInfo = [];
|
||||
$totalPrice = $this->calculatePrice($travelDate, $bookingRequest, $htmlSummary, $bookingPriceInfo);
|
||||
|
||||
$priceTypeById = $this->getDoctrine()->getRepository('AppBundle:TravelPeriodPriceType')->findAllIndexedById();
|
||||
if ($action == 'buchen')
|
||||
{
|
||||
$breadcrumbEntries = Util::createBreadcrumb($travelProgramPage);
|
||||
$breadcrumbEntries[] = new BreadcrumbEntry('Buchen', $travelProgramPage->getUrlPath() .'/buchen');
|
||||
|
||||
$breadcrumbEntries = Util::createBreadcrumb($travelProgramPage);
|
||||
$breadcrumbEntries[] = new BreadcrumbEntry('Buchen', $travelProgramPage->getUrlPath() .'/buchen');
|
||||
if ($request->getMethod() == 'POST' && $form->isValid())
|
||||
{
|
||||
$booking = $this->getDoctrine()->getRepository('AppBundle:TravelBooking')->createFromBookingRequest(
|
||||
$bookingRequest, $travelDate, $bookingPriceInfo);
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->persist($booking);
|
||||
$em->flush();
|
||||
|
||||
return $this->render('default/pages/booking.html.twig', [
|
||||
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
|
||||
'page' => $travelProgramPage,
|
||||
'travel_program' => $travelProgram,
|
||||
'travel_date' => $travelDate,
|
||||
'form' => $form->createView(),
|
||||
'price_type_by_id' => $priceTypeById,
|
||||
'breadcrumb_entries' => $breadcrumbEntries,
|
||||
]);
|
||||
$crmBookingUrl = $this->get('app.booking_exporter')->process($bookingRequest, $travelDate, $bookingPriceInfo);
|
||||
|
||||
$this->get('mailer')->send(\Swift_Message::newInstance()
|
||||
->setSubject('Ihr Buchungsauftrag bei STERN TOURS')
|
||||
->setFrom('stern@stern-tours.de', 'STERN TOURS')
|
||||
->setTo($bookingRequest->getEmail())
|
||||
->setBody(
|
||||
$this->renderView('default/email/bookingConfirmationEmail.txt.twig', [
|
||||
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
|
||||
'booking_request' => $bookingRequest,
|
||||
'booking_price_info' => $bookingPriceInfo,
|
||||
'travel_date' => $travelDate,
|
||||
'breadcrumb_entries' => $breadcrumbEntries,
|
||||
]),
|
||||
'text/plain', 'utf-8'
|
||||
)
|
||||
);
|
||||
|
||||
$this->get('mailer')->send(\Swift_Message::newInstance()
|
||||
->setSubject('BUCHUNG: '. $travelProgram->getTitle() .'('. $travelDate->getName() .')')
|
||||
->setFrom('stern@stern-tours.de', 'STERN TOURS')
|
||||
->setTo('sternt@stern-tours.de')
|
||||
->setBody(
|
||||
$this->renderView('default/email/bookingServiceEmail.txt.twig', [
|
||||
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
|
||||
'crm_url' => $crmBookingUrl .'/edit',
|
||||
'travel_program_url' => 'http' . (($_SERVER['SERVER_PORT'] == 443) ? 's://' : '://') .
|
||||
$_SERVER['HTTP_HOST'] . $travelProgramPage->getUrlPath(),
|
||||
'booking_request' => $bookingRequest,
|
||||
'booking_price_info' => $bookingPriceInfo,
|
||||
'travel_date' => $travelDate,
|
||||
'breadcrumb_entries' => $breadcrumbEntries,
|
||||
]),
|
||||
'text/plain', 'utf-8'
|
||||
)
|
||||
);
|
||||
|
||||
// #TODO This will lead to multiple bookings due to multiple form submission. Redirect instead!
|
||||
return $this->render('default/pages/bookingConfirmation.html.twig', [
|
||||
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
|
||||
'page' => $travelProgramPage,
|
||||
'breadcrumb_entries' => $breadcrumbEntries,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('default/pages/booking.html.twig', [
|
||||
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
|
||||
'page' => $travelProgramPage,
|
||||
'travel_program' => $travelProgram,
|
||||
'travel_date' => $travelDate,
|
||||
'form' => $form->createView(),
|
||||
'price_type_by_id' => $this->priceTypeById,
|
||||
'breadcrumb_entries' => $breadcrumbEntries,
|
||||
'summary' => $htmlSummary,
|
||||
'total_price' => $totalPrice
|
||||
]);
|
||||
}
|
||||
elseif ($action == 'berechne-gesamtpreis')
|
||||
{
|
||||
return $this->render('default/components/booking/summary.html.twig', [
|
||||
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
|
||||
'summary' => $htmlSummary,
|
||||
'total_price' => $totalPrice
|
||||
]);
|
||||
}
|
||||
throw new \Exception('Unknow BookingController action: '. $action);
|
||||
}
|
||||
|
||||
public function calculatePrice()
|
||||
public function calculatePrice(TravelDate $travelDate, BookingRequest $bookingRequest, &$outHtmlSummary = null,
|
||||
&$outPriceInfo = null)
|
||||
{
|
||||
$ret = 0;
|
||||
$insuranceAssessmentBasis = 0;
|
||||
$travelerCount = $bookingRequest->getTravelerCount();
|
||||
if (isset($outHtmlSummary))
|
||||
{
|
||||
$insuranceHtmlSummary = [];
|
||||
}
|
||||
if (isset($outPriceInfo))
|
||||
{
|
||||
$outPriceInfo['rooms'] = [];
|
||||
$outPriceInfo['insurances'] = [];
|
||||
$outPriceInfo['options'] = [];
|
||||
$outPriceInfo['classOptions'] = [];
|
||||
}
|
||||
|
||||
$departure = Util\DepartureUtil::limitIndividualArrivalPrice($bookingRequest->getDeparture(),
|
||||
$travelDate->getFlightPrice());
|
||||
if (isset($outPriceInfo))
|
||||
{
|
||||
$outPriceInfo['departure'] = $departure;
|
||||
}
|
||||
|
||||
if ($departure->getExtraCharge() != 0)
|
||||
{
|
||||
$insuranceAssessmentBasis += $departure->getExtraCharge();
|
||||
$a = $travelerCount * $departure->getExtraCharge();
|
||||
$ret += $a;
|
||||
if (isset($outHtmlSummary))
|
||||
{
|
||||
$outHtmlSummary[] = [
|
||||
'value' => $a,
|
||||
'label' => $travelerCount .'x '. ($departure->getExtraCharge() > 0 ? 'Aufschlag' : 'Abzug') .
|
||||
' für Abfahrts-/Abflugort "'. $departure->getName() .'" <strong>'.
|
||||
Util::formatPrice($departure->getExtraCharge()) .'</strong>'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($bookingRequest->getTravelOptions() as $travelOption)
|
||||
{
|
||||
$insuranceAssessmentBasis += $travelOption->getPrice();
|
||||
$a = $travelerCount * $travelOption->getPrice();
|
||||
$ret += $a;
|
||||
if (isset($outHtmlSummary))
|
||||
{
|
||||
$outHtmlSummary[] = [
|
||||
'value' => $a,
|
||||
'label' => $travelerCount .'x zugebuchte Leistung: '. $travelOption->getName() .' <strong>'.
|
||||
Util::formatPrice($travelOption->getPrice()) .'</strong>'
|
||||
];
|
||||
}
|
||||
if (isset($outPriceInfo))
|
||||
{
|
||||
$outPriceInfo['options'][] = $travelOption;
|
||||
}
|
||||
}
|
||||
|
||||
$persons = [
|
||||
'total' => $travelerCount,
|
||||
'adults' => $travelerCount,
|
||||
'children' => 0
|
||||
];
|
||||
$possibleRooms = $this->searchRooms($travelDate->getPrices(), $persons);
|
||||
|
||||
if (empty($possibleRooms))
|
||||
{
|
||||
if ($travelerCount % 2 == 0)
|
||||
{
|
||||
$possibleRooms = $this->splitIntoTwoGroups($travelDate->getPrices(), $persons, 'equal');
|
||||
}
|
||||
elseif ($travelerCount >= 3)
|
||||
{
|
||||
$possibleRooms = $this->splitIntoTwoGroups($travelDate->getPrices(), $persons, 'move_without_children');
|
||||
}
|
||||
}
|
||||
|
||||
if ($bookingRequest->getComfort())
|
||||
{
|
||||
foreach ($possibleRooms as $room)
|
||||
{
|
||||
$insuranceAssessmentBasis += $room['price']->getEffectiveComfortPrice();
|
||||
$a = $room['persons']['total'] * $room['price']->getEffectiveComfortPrice();
|
||||
$ret += $a;
|
||||
if (isset($outHtmlSummary))
|
||||
{
|
||||
$outHtmlSummary[] = [
|
||||
'value' => $a,
|
||||
'label' => $room['persons']['total'] .'x zugebuchte Leistung: Komfort-Kategorie <strong>'.
|
||||
Util::formatPrice($room['price']->getEffectiveComfortPrice()) .'</strong>'
|
||||
];
|
||||
}
|
||||
if (isset($outPriceInfo))
|
||||
{
|
||||
$outPriceInfo['classOptions'][] = [
|
||||
'count' => $room['persons']['total'],
|
||||
'name' => 'zugebuchte Leistung: Komfort (4 Sterne)',
|
||||
'price' => $room['price']->getEffectiveComfortPrice()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$insuranceTotal = 0;
|
||||
|
||||
foreach ($possibleRooms as $room)
|
||||
{
|
||||
$adultCount = $room['persons']['adults'];
|
||||
$singleFullPrice = $room['price']->getEffectivePrice();
|
||||
$roomPrice = $singleFullPrice * $adultCount + $singleFullPrice * $room['persons']['children'];
|
||||
$singleDiscountPrice = $room['price']->getEffectiveDiscountPrice();
|
||||
$discount = ($singleDiscountPrice === null) ? 0
|
||||
: ($adultCount * ($singleDiscountPrice - $singleFullPrice));
|
||||
$ret += $roomPrice + $discount;
|
||||
|
||||
if (isset($outPriceInfo))
|
||||
{
|
||||
$outPriceInfo['rooms'][] = [
|
||||
'name' => $room['priceType']->getName(),
|
||||
'adults' => $room['persons']['adults'],
|
||||
'children' => $room['persons']['children'],
|
||||
'price' => $singleDiscountPrice ?? $singleFullPrice,
|
||||
'price_children' => $room['price']->getEffectiveChildPrice(),
|
||||
'price_total' => $roomPrice + $discount,
|
||||
];
|
||||
}
|
||||
|
||||
if (isset($outHtmlSummary))
|
||||
{
|
||||
$label = '1x '. $room['priceType']->getName() .' [Personen: '. $adultCount .' x <strong>'.
|
||||
Util::formatPrice($singleFullPrice) .'</strong>';
|
||||
if ($room['persons']['children'] != 0)
|
||||
{
|
||||
$label .= ', Kinder: '. $room['persons']['children'] .' x <strong>'.
|
||||
Util::formatPrice($room['price']->getEffectiveChildPrice()) .'</strong>';
|
||||
}
|
||||
$label .= ']';
|
||||
$outHtmlSummary[] = [
|
||||
'value' => $roomPrice,
|
||||
'label' => $label
|
||||
];
|
||||
if ($singleDiscountPrice !== null)
|
||||
{
|
||||
$outHtmlSummary[] = [
|
||||
'value' => $discount,
|
||||
'label' => $adultCount .'x '.
|
||||
Util::formatPrice($singleFullPrice - $singleDiscountPrice) .' Rabatt'
|
||||
];
|
||||
}
|
||||
if ($bookingRequest->getInsurance() && $adultCount > 0)
|
||||
{
|
||||
$curAssessmentBasis = $insuranceAssessmentBasis + ($singleDiscountPrice ?? $singleFullPrice);
|
||||
$insurancePrice = $this->getDoctrine()->getRepository('AppBundle:TravelInsurancePrice')
|
||||
->findOneByInsuranceIdAndAssessmentBasis($bookingRequest->getInsurance()->getId(),
|
||||
$curAssessmentBasis);
|
||||
$insurancePriceValue = $insurancePrice->getPrice() > 0 ? $insurancePrice->getPrice()
|
||||
: round($insurancePrice->getPercent() * $curAssessmentBasis / 100, 2);
|
||||
$a = $adultCount * $insurancePriceValue;
|
||||
$insuranceTotal += $a;
|
||||
$ret += $a;
|
||||
if (isset($insuranceHtmlSummary))
|
||||
{
|
||||
$insuranceHtmlSummary[] = [
|
||||
'value' => $a,
|
||||
'label' => $adultCount .'x RV '. $bookingRequest->getInsurance()->getName() .' ('.
|
||||
$insurancePrice->getCode() .') <strong>'. Util::formatPrice($insurancePriceValue) .
|
||||
'</strong>'
|
||||
];
|
||||
}
|
||||
if (isset($outPriceInfo))
|
||||
{
|
||||
$outPriceInfo['insurances'][] = [
|
||||
'insurance' => $bookingRequest->getInsurance(),
|
||||
'insurancePriceValue' => $insurancePriceValue,
|
||||
'insurancePrice' => $insurancePrice,
|
||||
'count' => $adultCount,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($insuranceHtmlSummary))
|
||||
{
|
||||
$outHtmlSummary = array_merge($outHtmlSummary, $insuranceHtmlSummary);
|
||||
}
|
||||
if (isset($outPriceInfo))
|
||||
{
|
||||
$outPriceInfo['total'] = $ret;
|
||||
$outPriceInfo['totalWithoutInsurance'] = $ret - $insuranceTotal;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TravelPeriodPrice[] $prices
|
||||
* @param $persons
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function searchRooms($prices, $persons)
|
||||
{
|
||||
$ret = [];
|
||||
foreach ($prices as $price)
|
||||
{
|
||||
$priceType = $this->priceTypeById[$price->getPriceTypeId()];
|
||||
if ($priceType->getMax() == $persons['total'] &&
|
||||
$priceType->getMaxAdults() >= $persons['adults'] &&
|
||||
$priceType->getMinAdults() <= $persons['adults'] &&
|
||||
$priceType->getMaxChildren() >= $persons['children'])
|
||||
{
|
||||
$ret[] = [
|
||||
'priceType' => $priceType,
|
||||
'persons' => $persons,
|
||||
'price' => $price
|
||||
];
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function splitIntoTwoGroups($prices, $persons, $mode)
|
||||
{
|
||||
$group1 = [];
|
||||
$group2 = [];
|
||||
if($mode == 'equal')
|
||||
{
|
||||
$group1['adults'] = $group2['adults'] = $persons['adults'] / 2;
|
||||
$group1['children'] = $group2['children'] = $persons['children'] / 2;
|
||||
$group1['total'] = $group2['total'] = $group1['adults'] + $group2['children'];
|
||||
}
|
||||
elseif($mode = 'move_without_children')
|
||||
{
|
||||
$group1['adults'] = $persons['adults'] - 1;
|
||||
$group1['children'] = 0;
|
||||
$group1['total'] = $group1['adults'] + $group1['children'];
|
||||
|
||||
$group2['adults'] = 1;
|
||||
$group2['children'] = 0;
|
||||
$group2['total'] = $group2['adults'] + $group2['children'];
|
||||
}
|
||||
$possibleRoomsGroup1 = $this->searchRooms($prices, $group1);
|
||||
$possibleRoomsGroup2 = $this->searchRooms($prices, $group2);
|
||||
|
||||
return array_merge($possibleRoomsGroup1, $possibleRoomsGroup2);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,20 @@
|
|||
namespace AppBundle\Entity;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use AppBundle\Validator\Constraints as AppBundleAssert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
/**
|
||||
* Class BookingRequest
|
||||
* @package AppBundle\Entity
|
||||
* @AppBundleAssert\BookingRequest
|
||||
*/
|
||||
class BookingRequest
|
||||
{
|
||||
// Used in SternToursCrmBookingExports, expected to be equivalent to sex (as defined in Traveler)
|
||||
const MR = 1;
|
||||
const MRS = 2;
|
||||
|
||||
/**
|
||||
* @var TravelDeparturePoint $departure
|
||||
*/
|
||||
|
|
@ -23,14 +33,78 @@ class BookingRequest
|
|||
*/
|
||||
private $insurance;
|
||||
|
||||
private $comfort;
|
||||
private $comfort = false;
|
||||
|
||||
private $travelOptions;
|
||||
private $travelOptions = [];
|
||||
|
||||
private $salutation;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $firstName;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $lastName;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $streetAddress;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $zipCode;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $city;
|
||||
|
||||
private $nation;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $phone;
|
||||
|
||||
private $fax;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $email;
|
||||
|
||||
/*
|
||||
* @ Assert\Valid()
|
||||
*/
|
||||
private $travelers = [];
|
||||
|
||||
private $notes;
|
||||
|
||||
/**
|
||||
* @Assert\IsTrue()
|
||||
*/
|
||||
private $acceptTerms = false;
|
||||
|
||||
/**
|
||||
* BookingRequest constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
for ($i = 0; $i < 4; ++$i)
|
||||
{
|
||||
$this->travelers[] = new Traveler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TravelDeparturePoint
|
||||
*/
|
||||
public function getDeparture(): TravelDeparturePoint
|
||||
public function getDeparture()
|
||||
{
|
||||
return $this->departure;
|
||||
}
|
||||
|
|
@ -62,7 +136,7 @@ class BookingRequest
|
|||
/**
|
||||
* @return TravelInsurance
|
||||
*/
|
||||
public function getInsurance(): TravelInsurance
|
||||
public function getInsurance()
|
||||
{
|
||||
return $this->insurance;
|
||||
}
|
||||
|
|
@ -92,7 +166,7 @@ class BookingRequest
|
|||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @return TravelOption[]
|
||||
*/
|
||||
public function getTravelOptions()
|
||||
{
|
||||
|
|
@ -107,6 +181,221 @@ class BookingRequest
|
|||
$this->travelOptions = $travelOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSalutation()
|
||||
{
|
||||
return $this->salutation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $salutation
|
||||
*/
|
||||
public function setSalutation($salutation)
|
||||
{
|
||||
$this->salutation = $salutation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFirstName()
|
||||
{
|
||||
return $this->firstName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $firstName
|
||||
*/
|
||||
public function setFirstName($firstName)
|
||||
{
|
||||
$this->firstName = $firstName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLastName()
|
||||
{
|
||||
return $this->lastName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $lastName
|
||||
*/
|
||||
public function setLastName($lastName)
|
||||
{
|
||||
$this->lastName = $lastName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getStreetAddress()
|
||||
{
|
||||
return $this->streetAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $streetAddress
|
||||
*/
|
||||
public function setStreetAddress($streetAddress)
|
||||
{
|
||||
$this->streetAddress = $streetAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getZipCode()
|
||||
{
|
||||
return $this->zipCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $zipCode
|
||||
*/
|
||||
public function setZipCode($zipCode)
|
||||
{
|
||||
$this->zipCode = $zipCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCity()
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $city
|
||||
*/
|
||||
public function setCity($city)
|
||||
{
|
||||
$this->city = $city;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getNation()
|
||||
{
|
||||
return $this->nation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $nation
|
||||
*/
|
||||
public function setNation($nation)
|
||||
{
|
||||
$this->nation = $nation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPhone()
|
||||
{
|
||||
return $this->phone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $phone
|
||||
*/
|
||||
public function setPhone($phone)
|
||||
{
|
||||
$this->phone = $phone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFax()
|
||||
{
|
||||
return $this->fax;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fax
|
||||
*/
|
||||
public function setFax($fax)
|
||||
{
|
||||
$this->fax = $fax;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*/
|
||||
public function setEmail($email)
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Traveler[]
|
||||
*/
|
||||
public function getTravelers()
|
||||
{
|
||||
return $this->travelers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Traveler[] $travelers
|
||||
*/
|
||||
public function setTravelers($travelers)
|
||||
{
|
||||
$this->travelers = $travelers;
|
||||
}
|
||||
|
||||
/*
|
||||
public function addTraveler(Traveler $traveler)
|
||||
{
|
||||
$this->travelers[] = $traveler;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNotes()
|
||||
{
|
||||
return $this->notes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $notes
|
||||
*/
|
||||
public function setNotes($notes)
|
||||
{
|
||||
$this->notes = $notes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isAcceptTerms()
|
||||
{
|
||||
return $this->acceptTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $acceptTerms
|
||||
*/
|
||||
public function setAcceptTerms($acceptTerms)
|
||||
{
|
||||
$this->acceptTerms = $acceptTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Assert\Callback
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,4 +10,50 @@ namespace AppBundle\Entity;
|
|||
*/
|
||||
class FlightPeriodRepository extends \Doctrine\ORM\EntityRepository
|
||||
{
|
||||
public function getIndexedFlightPeriodsForTimePeriod($startDate, $endDate, $arrivalPointIds = null)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
$qb
|
||||
->from('AppBundle:FlightPeriod', 'fp')
|
||||
->addSelect('fp')
|
||||
->leftJoin('fp.departures', 'fp_dep')
|
||||
->addSelect('fp_dep')
|
||||
;
|
||||
|
||||
if ($startDate !== null)
|
||||
{
|
||||
$qb->where('fp.startDate >= :startDate');
|
||||
$qb->setParameter('startDate', $startDate);
|
||||
}
|
||||
if ($endDate !== null)
|
||||
{
|
||||
$qb->andWhere('fp.endDate <= :endDate');
|
||||
$qb->setParameter('endDate', $endDate);
|
||||
}
|
||||
if (!empty($arrivalPointIds))
|
||||
{
|
||||
if (is_array($arrivalPointIds))
|
||||
{
|
||||
$qb->andWhere($qb->expr()->in('IDENTITY(fp.travelArrivalPoint)', $arrivalPointIds));
|
||||
}
|
||||
else
|
||||
{
|
||||
$qb->andWhere($qb->expr()->eq('IDENTITY(fp.travelArrivalPoint)', $arrivalPointIds));
|
||||
}
|
||||
}
|
||||
|
||||
$unindexedFlightPeriods = $qb->getQuery()->getResult();
|
||||
$ret = [];
|
||||
|
||||
// Index by CONCAT(start date, end date, arrival point id):
|
||||
/** @var FlightPeriod $flightPeriod */
|
||||
foreach ($unindexedFlightPeriods as $flightPeriod)
|
||||
{
|
||||
$ret[$flightPeriod->getStartDate()->format('Y-m-d') .
|
||||
$flightPeriod->getEndDate()->format('Y-m-d') .
|
||||
$flightPeriod->getTravelArrivalPoint()->getId()] = $flightPeriod;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use Doctrine\ORM\Mapping as ORM;
|
|||
* TravelBooking
|
||||
*
|
||||
* @ORM\Table(name="travel_booking", indexes={@ORM\Index(name="FK_travel_booking_travel_period", columns={"period_id"}), @ORM\Index(name="FK_travel_booking_travel_program", columns={"program_id"})})
|
||||
* @ORM\Entity
|
||||
* @ORM\Entity(repositoryClass="AppBundle\Entity\TravelBookingRepository")
|
||||
*/
|
||||
class TravelBooking
|
||||
{
|
||||
|
|
@ -678,7 +678,7 @@ class TravelBooking
|
|||
*/
|
||||
public function setSelectedDeparture($selectedDeparture)
|
||||
{
|
||||
$this->selectedDeparture = $selectedDeparture;
|
||||
$this->selectedDeparture = is_array($selectedDeparture) ? json_encode($selectedDeparture) : $selectedDeparture;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -690,7 +690,12 @@ class TravelBooking
|
|||
*/
|
||||
public function getSelectedDeparture()
|
||||
{
|
||||
return $this->selectedDeparture;
|
||||
$ret = json_decode($this->selectedDeparture, true);
|
||||
if (empty($ret) || !is_array($ret))
|
||||
{
|
||||
return $this->selectedDeparture;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -846,7 +851,7 @@ class TravelBooking
|
|||
*/
|
||||
public function setRooms($rooms)
|
||||
{
|
||||
$this->rooms = $rooms;
|
||||
$this->options = is_array($rooms) ? json_encode($rooms) : $rooms;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -858,19 +863,43 @@ class TravelBooking
|
|||
*/
|
||||
public function getRooms()
|
||||
{
|
||||
return $this->rooms;
|
||||
$ret = json_decode($this->rooms, true);
|
||||
if (empty($ret) || !is_array($ret))
|
||||
{
|
||||
return $this->rooms;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set participants
|
||||
*
|
||||
* @param string $participants
|
||||
* @param Traveler[] $travelers
|
||||
*
|
||||
* @return TravelBooking
|
||||
*/
|
||||
public function setParticipants($participants)
|
||||
public function setParticipants($travelers)
|
||||
{
|
||||
$this->participants = $participants;
|
||||
if (!is_array($travelers))
|
||||
{
|
||||
$this->participants = $travelers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
$participants = [];
|
||||
|
||||
for ($i = 0; $i < count($travelers); ++$i)
|
||||
{
|
||||
$traveler = $travelers[$i];
|
||||
$participants[''. ($i+1)] = [
|
||||
'gender' => $traveler->getSex(),
|
||||
'first_name' => $traveler->getFirstName(),
|
||||
'last_name' => $traveler->getLastName(),
|
||||
'birthday' => $traveler->getBirthDate()->format('d.m.Y')
|
||||
];
|
||||
}
|
||||
|
||||
$this->participants = json_encode($participants);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -882,7 +911,22 @@ class TravelBooking
|
|||
*/
|
||||
public function getParticipants()
|
||||
{
|
||||
return $this->participants;
|
||||
$participants = json_decode($this->participants, true);
|
||||
if (empty($participants) || !is_array($participants))
|
||||
{
|
||||
return $this->participants;
|
||||
}
|
||||
$ret = [];
|
||||
foreach ($participants as $participant)
|
||||
{
|
||||
$traveler = new Traveler();
|
||||
$traveler->setSex(intval($participant['gender']));
|
||||
$traveler->setFirstName($participant['first_name']);
|
||||
$traveler->setLastName($participant['last_name']);
|
||||
$traveler->setBirthDate(\DateTime::createFromFormat('d.m.Y', $participant['birthday']));
|
||||
$ret[] = $traveler;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -990,7 +1034,7 @@ class TravelBooking
|
|||
*/
|
||||
public function setInsurances($insurances)
|
||||
{
|
||||
$this->insurances = $insurances;
|
||||
$this->insurances = is_array($insurances) ? json_encode($insurances) : $insurances;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -1002,7 +1046,12 @@ class TravelBooking
|
|||
*/
|
||||
public function getInsurances()
|
||||
{
|
||||
return $this->insurances;
|
||||
$ret = json_decode($this->insurances, true);
|
||||
if (empty($ret) || !is_array($ret))
|
||||
{
|
||||
return $this->insurances;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1014,7 +1063,7 @@ class TravelBooking
|
|||
*/
|
||||
public function setOptions($options)
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->options = is_array($options) ? json_encode($options) : $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -1026,7 +1075,12 @@ class TravelBooking
|
|||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
$ret = json_decode($this->options, true);
|
||||
if (empty($ret) || !is_array($ret))
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1038,7 +1092,7 @@ class TravelBooking
|
|||
*/
|
||||
public function setClassOptions($classOptions)
|
||||
{
|
||||
$this->classOptions = $classOptions;
|
||||
$this->classOptions = is_array($classOptions) ? json_encode($classOptions) : $classOptions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -1050,7 +1104,12 @@ class TravelBooking
|
|||
*/
|
||||
public function getClassOptions()
|
||||
{
|
||||
return $this->classOptions;
|
||||
$ret = json_decode($this->classOptions, true);
|
||||
if (empty($ret) || !is_array($ret))
|
||||
{
|
||||
return $this->classOptions;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1062,7 +1121,7 @@ class TravelBooking
|
|||
*/
|
||||
public function setExtraCategory($extraCategory)
|
||||
{
|
||||
$this->extraCategory = $extraCategory;
|
||||
$this->extraCategory = is_array($extraCategory) ? json_encode($extraCategory) : $extraCategory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -1074,7 +1133,12 @@ class TravelBooking
|
|||
*/
|
||||
public function getExtraCategory()
|
||||
{
|
||||
return $this->extraCategory;
|
||||
$ret = json_decode($this->extraCategory, true);
|
||||
if (empty($ret) || !is_array($ret))
|
||||
{
|
||||
return $this->extraCategory;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
88
trunk/src/AppBundle/Entity/TravelBookingRepository.php
Normal file
88
trunk/src/AppBundle/Entity/TravelBookingRepository.php
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace AppBundle\Entity;
|
||||
|
||||
/**
|
||||
* TravelBookingRepository
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
*/
|
||||
class TravelBookingRepository extends \Doctrine\ORM\EntityRepository
|
||||
{
|
||||
public function createFromBookingRequest(BookingRequest $bookingRequest, TravelDate $travelDate, $bookingPriceInfo)
|
||||
{
|
||||
$tp = $travelDate->getTravelProgram();
|
||||
$ret = new TravelBooking();
|
||||
$ret->setIp($_SERVER['REMOTE_ADDR']);
|
||||
$ret->setCreated(new \DateTime());
|
||||
$ret->setProgramName($tp->getTitle() . ' ('. $travelDate->getName() .')');
|
||||
//$ret->setClass()
|
||||
$ret->setSalutation($bookingRequest->getSalutation());
|
||||
$ret->setFirstName($bookingRequest->getFirstName());
|
||||
$ret->setLastName($bookingRequest->getLastName());
|
||||
$ret->setStreet($bookingRequest->getStreetAddress());
|
||||
//$ret->setHouseNr()
|
||||
$ret->setZipcode($bookingRequest->getZipCode());
|
||||
$ret->setCity($bookingRequest->getCity());
|
||||
$ret->setCountry($bookingRequest->getNation());
|
||||
$ret->setMail($bookingRequest->getEmail());
|
||||
$ret->setPhone($bookingRequest->getPhone());
|
||||
$ret->setFax($bookingRequest->getFax());
|
||||
$ret->setSelectedDeparture([
|
||||
'name' => $bookingRequest->getDeparture()->getName(),
|
||||
'extra_charge' => $bookingRequest->getDeparture()->getExtraCharge(),
|
||||
'extra_charge_total' => $bookingRequest->getTravelerCount()
|
||||
]);
|
||||
$ret->setSelectedStartDate($travelDate->getStart());
|
||||
$ret->setSelectedEndDate($travelDate->getEnd());
|
||||
$ret->setSelectedAdults($bookingRequest->getTravelerCount());
|
||||
$ret->setSelectedChild1(0);
|
||||
$ret->setSelectedChild2(0);
|
||||
$ret->setSelectedChild3(0);
|
||||
$insurance = $bookingRequest->getInsurance();
|
||||
$ret->setInsuranceName($insurance ? $insurance->getName() : '0'); // #TODO Adapted from v2
|
||||
if (empty($bookingPriceInfo['insurances']))
|
||||
{
|
||||
$ret->setInsurances(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
$insurances = [];
|
||||
foreach ($bookingPriceInfo['insurances'] as $insuranceInfo)
|
||||
{
|
||||
$insurances[] = [
|
||||
'count' => $insuranceInfo['count'],
|
||||
'price' => $insuranceInfo['insurancePriceValue'],
|
||||
'code' => $insuranceInfo['insurancePrice']->getCode()
|
||||
];
|
||||
}
|
||||
$ret->setInsurances($insurances);
|
||||
}
|
||||
$ret->setParticipants(array_slice($bookingRequest->getTravelers(), 0, $bookingRequest->getTravelerCount()));
|
||||
$ret->setParticipantsTotal($bookingRequest->getTravelerCount());
|
||||
$ret->setRooms($bookingPriceInfo['rooms']);
|
||||
$ret->setPriceTotal($bookingPriceInfo['total']);
|
||||
$ret->setComments($bookingRequest->getNotes());
|
||||
if (empty($bookingPriceInfo['options']))
|
||||
{
|
||||
$ret->setOptions(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
$options = [];
|
||||
foreach ($bookingPriceInfo['options'] as $option)
|
||||
{
|
||||
$options[] = [
|
||||
'name' => $option->getName(),
|
||||
'price' => $option->getPrice()
|
||||
];
|
||||
}
|
||||
$ret->setOptions($options);
|
||||
}
|
||||
$ret->setClassOptions(false);
|
||||
$ret->setExtraCategory(empty($bookingPriceInfo['classOptions']) ? false : $bookingPriceInfo['classOptions']);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -89,12 +89,12 @@ final class TravelDate
|
|||
}
|
||||
$this->start = $start;
|
||||
$this->index = $index;
|
||||
$this->flightPeriod = $flightPeriod;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->start = $travelPeriod->getStartDate();
|
||||
}
|
||||
$this->flightPeriod = $flightPeriod;
|
||||
$this->travelProgram = $travelPeriod->getProgram();
|
||||
$this->key = $key;
|
||||
$this->travelPeriod = $travelPeriod;
|
||||
|
|
@ -187,6 +187,24 @@ final class TravelDate
|
|||
return $this->departures;
|
||||
}
|
||||
|
||||
public function getFlightPrice()
|
||||
{
|
||||
if ($this->travelProgram->getIsMediated())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
$flightPrice = null;
|
||||
if ($this->flightPeriod !== null)
|
||||
{
|
||||
$flightPrice = $this->flightPeriod->getPrice();
|
||||
}
|
||||
if ($flightPrice === null)
|
||||
{
|
||||
$flightPrice = $this->travelProgram->getDefaultFlightPrice();
|
||||
}
|
||||
return $flightPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TravelPeriodPrice[]|\Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
|
|
@ -195,31 +213,21 @@ final class TravelDate
|
|||
if (!$this->calculatedEffectivePrices)
|
||||
{
|
||||
$this->calculatedEffectivePrices = true;
|
||||
$flightPrice = $this->getFlightPrice();
|
||||
if ($this->travelProgram->getIsMediated())
|
||||
{
|
||||
$profitMargin = 1;
|
||||
$flightPrice = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$profitMargin = $this->travelProgram->getProfitMargin() / 100 + 1;
|
||||
$flightPrice = null;
|
||||
if ($this->flightPeriod !== null)
|
||||
{
|
||||
$flightPrice = $this->flightPeriod->getPrice();
|
||||
}
|
||||
if ($flightPrice === null)
|
||||
{
|
||||
$flightPrice = $this->travelProgram->getDefaultFlightPrice();
|
||||
}
|
||||
}
|
||||
$currencyFactor = $this->travelProgram->getNettoPricesInEuro() ? 1 : $this->currencyFactor;
|
||||
foreach ($this->travelPeriod->getPrices() as $price)
|
||||
{
|
||||
$price->setEffectivePrice(round(($flightPrice + $price->getPrice() * $currencyFactor) * $profitMargin));
|
||||
$price->setEffectiveDiscountPrice(
|
||||
round(($flightPrice + $price->getDiscountPrice() * $currencyFactor) * $profitMargin));
|
||||
//$price->setEffectiveDiscountPrice($pr)
|
||||
$price->setEffectiveComfortPrice(round($price->getPriceComfort() * $currencyFactor * $profitMargin));
|
||||
$price->setEffectiveChildPrice(round($price->getPriceChildren() * $currencyFactor * $profitMargin));
|
||||
}
|
||||
}
|
||||
return $this->travelPeriod->getPrices();
|
||||
|
|
@ -255,6 +263,14 @@ final class TravelDate
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TravelProgram
|
||||
*/
|
||||
public function getTravelProgram(): TravelProgram
|
||||
{
|
||||
return $this->travelProgram;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TravelPeriod
|
||||
* @internal
|
||||
|
|
|
|||
|
|
@ -139,11 +139,11 @@ class TravelDeparturePoint
|
|||
/**
|
||||
* Get extraCharge
|
||||
*
|
||||
* @return string
|
||||
* @return float
|
||||
*/
|
||||
public function getExtraCharge()
|
||||
{
|
||||
return $this->extraCharge;
|
||||
return ($this->extraCharge === null || $this->extraCharge === '') ? null : floatval($this->extraCharge);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use Doctrine\ORM\Mapping as ORM;
|
|||
* TravelInsurancePrice
|
||||
*
|
||||
* @ORM\Table(name="travel_insurance_price", indexes={@ORM\Index(name="FK_travel_insurance_price_travel_insurance", columns={"insurance_id"})})
|
||||
* @ORM\Entity
|
||||
* @ORM\Entity(repositoryClass="AppBundle\Entity\TravelInsurancePriceRepository")
|
||||
*/
|
||||
class TravelInsurancePrice
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace AppBundle\Entity;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
||||
/**
|
||||
* TravelInsurancePriceRepository
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
*/
|
||||
class TravelInsurancePriceRepository extends \Doctrine\ORM\EntityRepository
|
||||
{
|
||||
/**
|
||||
* @param $insuranceId
|
||||
* @param $assessmentBasis
|
||||
*
|
||||
* @return TravelInsurancePrice|null
|
||||
*/
|
||||
public function findOneByInsuranceIdAndAssessmentBasis($insuranceId, $assessmentBasis)
|
||||
{
|
||||
$qb = $this->createQueryBuilder('i');
|
||||
return $qb
|
||||
->where($qb->expr()->eq('IDENTITY(i.insurance)', $insuranceId))
|
||||
//->where('IDENTITY(i.insurance) = '. $insuranceId)
|
||||
->andWhere($qb->expr()->gte('i.border', $assessmentBasis))
|
||||
->orderBy('i.border')
|
||||
->setMaxResults(1)
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +67,8 @@ class TravelPeriodPrice
|
|||
private $priceTypeId;
|
||||
|
||||
private $effectivePrice = null;
|
||||
private $effectiveDiscountPrice = null;
|
||||
private $effectiveChildPrice = null;
|
||||
private $effectiveComfortPrice = null;
|
||||
|
||||
/**
|
||||
* Set priceType
|
||||
|
|
@ -199,29 +200,28 @@ class TravelPeriodPrice
|
|||
return $this->period;
|
||||
}
|
||||
|
||||
public function getDiscountPrice()
|
||||
/**
|
||||
* Set priceTypeId
|
||||
*
|
||||
* @param integer $priceTypeId
|
||||
*
|
||||
* @return TravelPeriodPrice
|
||||
*/
|
||||
public function setPriceTypeId($priceTypeId)
|
||||
{
|
||||
if ($this->getPeriod() == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// #TODO FIX! Discount calculation differs for period and program
|
||||
$price = $this->price; // #TODO Is the discount calculated for the effective price or for the original price?
|
||||
$newPrice = $price;
|
||||
foreach ($this->getPeriod()->getDiscounts() as $discount)
|
||||
{
|
||||
$newPrice -= $discount->getPercent()
|
||||
? round($newPrice * $discount->getValue() / 100, 2) // FIXME
|
||||
: $discount->getValue();
|
||||
}
|
||||
$program = $this->getPeriod()->getProgram();
|
||||
if ($program != null && $program->getDiscount() != null)
|
||||
{
|
||||
$newPrice -= $program->getDiscountIsPercentValue()
|
||||
? round($price * $program->getDiscount() / 100, 2) // FIXME
|
||||
: $program->getDiscount();
|
||||
}
|
||||
return $price == $newPrice ? null : $newPrice;
|
||||
$this->priceTypeId = $priceTypeId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get priceTypeId
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getPriceTypeId()
|
||||
{
|
||||
return $this->priceTypeId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -245,48 +245,92 @@ class TravelPeriodPrice
|
|||
$this->effectivePrice = $effectivePrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Probably getEffectiveDiscountPrice() is the method you are actually looking for.
|
||||
* @return float|null
|
||||
*/
|
||||
public function getDiscountPrice()
|
||||
{
|
||||
return $this->calculateDiscountPrice($this->price);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getEffectiveDiscountPrice()
|
||||
{
|
||||
if ($this->effectiveDiscountPrice === null)
|
||||
if ($this->effectivePrice === null)
|
||||
{
|
||||
throw new \Exception('Effective discount price must be set from outside before reading it.');
|
||||
throw new \Exception('Effective price must be set from outside before reading effective discount price.');
|
||||
}
|
||||
return $this->effectiveDiscountPrice;
|
||||
return $this->calculateDiscountPrice($this->effectivePrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $effectiveDiscountPrice
|
||||
* @return float
|
||||
* @throws \Exception
|
||||
*
|
||||
* @todo The child price will not be set yet. This is just a preparation for later
|
||||
*/
|
||||
public function setEffectiveDiscountPrice($effectiveDiscountPrice)
|
||||
public function getEffectiveChildPrice()
|
||||
{
|
||||
$this->effectiveDiscountPrice = $effectiveDiscountPrice;
|
||||
if ($this->effectiveChildPrice === null)
|
||||
{
|
||||
throw new \Exception('Effective child price must be set from outside before reading it.');
|
||||
}
|
||||
return $this->effectiveChildPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set priceTypeId
|
||||
*
|
||||
* @param integer $priceTypeId
|
||||
*
|
||||
* @return TravelPeriodPrice
|
||||
* @param null $effectiveChildPrice
|
||||
*/
|
||||
public function setPriceTypeId($priceTypeId)
|
||||
public function setEffectiveChildPrice($effectiveChildPrice)
|
||||
{
|
||||
$this->priceTypeId = $priceTypeId;
|
||||
|
||||
return $this;
|
||||
$this->effectiveChildPrice = $effectiveChildPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get priceTypeId
|
||||
*
|
||||
* @return integer
|
||||
* @return float|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getPriceTypeId()
|
||||
public function getEffectiveComfortPrice()
|
||||
{
|
||||
return $this->priceTypeId;
|
||||
if ($this->effectiveComfortPrice === null)
|
||||
{
|
||||
throw new \Exception('Effective comfort price must be set from outside before reading it.');
|
||||
}
|
||||
return $this->effectiveComfortPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float|null $effectiveComfortPrice
|
||||
*/
|
||||
public function setEffectiveComfortPrice($effectiveComfortPrice)
|
||||
{
|
||||
$this->effectiveComfortPrice = $effectiveComfortPrice;
|
||||
}
|
||||
|
||||
private function calculateDiscountPrice($price)
|
||||
{
|
||||
if ($this->getPeriod() == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
$newPrice = $price;
|
||||
foreach ($this->getPeriod()->getDiscounts() as $discount)
|
||||
{
|
||||
$newPrice -= $discount->getPercent()
|
||||
? round($newPrice * $discount->getValue() / 100, 2) // #TODO FIXME
|
||||
: $discount->getValue();
|
||||
}
|
||||
$program = $this->getPeriod()->getProgram();
|
||||
if ($program != null && $program->getDiscount() != null)
|
||||
{
|
||||
$newPrice -= $program->getDiscountIsPercentValue()
|
||||
? round($price * $program->getDiscount() / 100, 2) // #TODO FIXME
|
||||
: $program->getDiscount();
|
||||
}
|
||||
return $price == $newPrice ? null : $newPrice;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,32 +129,9 @@ class TravelPeriodRepository extends \Doctrine\ORM\EntityRepository
|
|||
$usedArrivalPointIds = array_keys($isUsedArrivalPointById);
|
||||
|
||||
// Find flight periods and related departures
|
||||
$flightPeriods = [];
|
||||
if (!empty($isUsedArrivalPointById))
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
$unindexedFlightPeriods = $qb
|
||||
->from('AppBundle:FlightPeriod', 'fp')
|
||||
->addSelect('fp')
|
||||
->leftJoin('fp.departures', 'fp_dep')
|
||||
->addSelect('fp_dep')
|
||||
->where('fp.startDate >= :startDate')
|
||||
->andWhere('fp.endDate <= :endDate')
|
||||
->andWhere($qb->expr()->in('IDENTITY(fp.travelArrivalPoint)', $usedArrivalPointIds))
|
||||
->setParameter('startDate', $startDate)
|
||||
->setParameter('endDate', $endDate)
|
||||
->getQuery()->getResult();
|
||||
|
||||
// Index by CONCAT(start date, end date, arrival point id):
|
||||
/** @var FlightPeriod $flightPeriod */
|
||||
foreach ($unindexedFlightPeriods as $flightPeriod)
|
||||
{
|
||||
$flightPeriods[$flightPeriod->getStartDate()->format('Y-m-d') .
|
||||
$flightPeriod->getEndDate()->format('Y-m-d') .
|
||||
$flightPeriod->getTravelArrivalPoint()->getId()] = $flightPeriod;
|
||||
}
|
||||
|
||||
}
|
||||
$flightPeriods = empty($isUsedArrivalPointById) ? []
|
||||
: $this->getEntityManager()->getRepository('AppBundle:FlightPeriod')
|
||||
->getIndexedFlightPeriodsForTimePeriod($startDate, $endDate, $usedArrivalPointIds);
|
||||
|
||||
// Find default departures and classify by-program or by-arrival-point
|
||||
// We could've simply left joined them to get an equal result. But we're reducing the number of rows returned
|
||||
|
|
@ -253,8 +230,9 @@ class TravelPeriodRepository extends \Doctrine\ORM\EntityRepository
|
|||
// Only mediated travel programs define departures in travelPeriods
|
||||
$qb->leftJoin('p.departures', 'p_dep')->addSelect('p_dep');
|
||||
}
|
||||
else
|
||||
elseif (!($flags & self::TD_QUERY_VIRTUAL))
|
||||
{
|
||||
// Retrieving all flight periods by join is only possible, if virtual entries are excluded
|
||||
$qb->leftJoin('AppBundle:FlightPeriod', 'fp', Expr\Join::WITH, 'IDENTITY(fp.travelArrivalPoint) = '.
|
||||
':travelArrivalPointId AND d.startDate = fp.startDate AND d.endDate = fp.endDate');
|
||||
$qb->setParameter('travelArrivalPointId', $program->getTravelArrivalPoint()->getId());
|
||||
|
|
@ -299,11 +277,19 @@ class TravelPeriodRepository extends \Doctrine\ORM\EntityRepository
|
|||
->addOrderBy('p.name', 'ASC')
|
||||
;
|
||||
|
||||
// #TODO Try to optimize this later
|
||||
$entities = $qb->getQuery()->execute();
|
||||
$flightPeriodByKey = [];
|
||||
|
||||
$flightPeriodByKey = null;
|
||||
if (!$program->getIsMediated())
|
||||
{
|
||||
if ($flags & self::TD_QUERY_VIRTUAL)
|
||||
{
|
||||
// If virtual entries are included, we have to fetch all flight periods, because we don't know
|
||||
// the actual dates yet
|
||||
$flightPeriodByKey = $this->getEntityManager()->getRepository('AppBundle:FlightPeriod')
|
||||
->getIndexedFlightPeriodsForTimePeriod($startDate, null, $program->getTravelArrivalPoint()->getId());
|
||||
}
|
||||
|
||||
foreach ($entities as $key => $entity)
|
||||
{
|
||||
if ($entity == null)
|
||||
|
|
@ -334,6 +320,8 @@ class TravelPeriodRepository extends \Doctrine\ORM\EntityRepository
|
|||
}
|
||||
}
|
||||
}
|
||||
//$x = array_keys($flightPeriodByKey);
|
||||
//var_dump($x); die();
|
||||
|
||||
$this->addTravelDatesToProgram($program, $entities, $flightPeriodByKey, $startDate, null);
|
||||
return $program->getTravelDates();
|
||||
|
|
@ -415,6 +403,15 @@ class TravelPeriodRepository extends \Doctrine\ORM\EntityRepository
|
|||
{
|
||||
$flightPeriodKey = $travelDateKey .
|
||||
$travelProgram->getTravelArrivalPoint()->getId();
|
||||
// #DEBUG
|
||||
/*
|
||||
if ($travelPeriodDate->getId() . $travelPeriod->getName() . $i == '18888D8')
|
||||
{
|
||||
$x = array_keys($flightPeriods);
|
||||
var_dump($x);
|
||||
die (isset($flightPeriods[$flightPeriodKey]) ? 'ok' : 'nok');
|
||||
}
|
||||
*/
|
||||
$flightPeriod = $flightPeriods[$flightPeriodKey] ?? null;
|
||||
}
|
||||
$travelProgram->addTravelDateFromSeasonTravelPeriod(
|
||||
|
|
@ -443,8 +440,13 @@ class TravelPeriodRepository extends \Doctrine\ORM\EntityRepository
|
|||
$flightPeriod = null;
|
||||
if (!$travelProgram->getIsMediated())
|
||||
{
|
||||
//$x = array_keys($flightPeriods);
|
||||
//var_dump($x);
|
||||
//die($travelDateKey . $travelProgram->getTravelArrivalPoint()->getId());
|
||||
$flightPeriod = $flightPeriods[$travelDateKey . $travelProgram->getTravelArrivalPoint()->getId()]
|
||||
?? null;
|
||||
//if ($travelPeriod->getName() == 'ISRA-HOE18888D8');
|
||||
//die($travelPeriod->getName() .';'. $flightPeriod->getPrice());
|
||||
}
|
||||
|
||||
// #TODO There is an error in the old backend which causes duplicates
|
||||
|
|
|
|||
104
trunk/src/AppBundle/Entity/Traveler.php
Normal file
104
trunk/src/AppBundle/Entity/Traveler.php
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
|
||||
* @date 02/10/2017
|
||||
*/
|
||||
|
||||
namespace AppBundle\Entity;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class Traveler
|
||||
{
|
||||
// Used in SternToursCrmBookingExports, expected to be equivalent to salutation (as defined in BookingRequest)
|
||||
const MALE = 1;
|
||||
const FEMALE = 2;
|
||||
|
||||
/**
|
||||
* @Assert\NotNull
|
||||
* @ Assert\Choice(choices={1,2})
|
||||
*/
|
||||
private $sex;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $firstName;
|
||||
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $lastName;
|
||||
|
||||
/**
|
||||
* @var \DateTime $birthDate
|
||||
* @Assert\NotBlank()
|
||||
*/
|
||||
private $birthDate;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSex()
|
||||
{
|
||||
return $this->sex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $sex
|
||||
*/
|
||||
public function setSex($sex)
|
||||
{
|
||||
$this->sex = $sex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFirstName()
|
||||
{
|
||||
return $this->firstName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $firstName
|
||||
*/
|
||||
public function setFirstName($firstName)
|
||||
{
|
||||
$this->firstName = $firstName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLastName()
|
||||
{
|
||||
return $this->lastName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $lastName
|
||||
*/
|
||||
public function setLastName($lastName)
|
||||
{
|
||||
$this->lastName = $lastName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getBirthDate()
|
||||
{
|
||||
return $this->birthDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $birthDate
|
||||
*/
|
||||
public function setBirthDate($birthDate)
|
||||
{
|
||||
$this->birthDate = $birthDate;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
365
trunk/src/AppBundle/Export/SternToursCrmBookingExporter.php
Normal file
365
trunk/src/AppBundle/Export/SternToursCrmBookingExporter.php
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
|
||||
* @date 02/13/2017
|
||||
*/
|
||||
|
||||
namespace AppBundle\Export;
|
||||
|
||||
|
||||
use AppBundle\Entity\BookingRequest;
|
||||
use AppBundle\Entity\TravelDate;
|
||||
use AppBundle\Entity\Traveler;
|
||||
use AppBundle\Util;
|
||||
use Monolog\Logger;
|
||||
|
||||
class SternToursCrmBookingExporter
|
||||
{
|
||||
const API_URL = 'http://www.cms.stern-tours.net/api';
|
||||
const API_KEY = 'f6077389c9ce710e554763a5de02c8ec';
|
||||
const API_USER_ID = 15; // 'apiuser'
|
||||
const WEBSITE_ID = 1; // 'sterntours.de'
|
||||
|
||||
private $logger;
|
||||
|
||||
public function __construct(Logger $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function process(BookingRequest $bookingRequest, TravelDate $travelDate, $bookingPriceInfo)
|
||||
{
|
||||
$tp = $travelDate->getTravelProgram();
|
||||
$startDateStr = $travelDate->getStart()->format('Y-m-d');
|
||||
$lead = $this->createLead($bookingRequest, $travelDate);
|
||||
if ($lead === null)
|
||||
{
|
||||
$this->warn('Failed creating lead in CRM', $bookingRequest, $travelDate, Logger::ERROR);
|
||||
return false;
|
||||
}
|
||||
$bookingUrl = $this->createBooking($bookingRequest, $travelDate, $bookingPriceInfo, $lead['customer_id'],
|
||||
$lead['id']);
|
||||
if ($bookingUrl === false)
|
||||
{
|
||||
$this->warn('Failed creating booking in CRM', $bookingRequest, $travelDate, Logger::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($i = 1; $i < $bookingRequest->getTravelerCount(); ++$i)
|
||||
{
|
||||
if (!$this->createTraveler($bookingUrl, $bookingRequest->getTravelers()[$i]))
|
||||
{
|
||||
$this->warn('Failed creating traveler with index '. $i .' in CRM.', $bookingRequest, $travelDate);
|
||||
}
|
||||
}
|
||||
|
||||
if ($tp->getIsMediated())
|
||||
{
|
||||
$serviceItemDefaults = [
|
||||
'travel_company_id' => $tp->getOrganizer()->getCmsId(),
|
||||
'travel_date' => $startDateStr,
|
||||
'commission' => 0,
|
||||
];
|
||||
|
||||
foreach ($bookingPriceInfo['rooms'] as $room)
|
||||
{
|
||||
$this->createServiceItem($bookingUrl, $serviceItemDefaults + [
|
||||
'service_price' => $room['price_total'],
|
||||
'name' => $room['name'],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->createServiceItem($bookingUrl, $serviceItemDefaults + [
|
||||
'service_price' => $bookingRequest->getTravelerCount() * $bookingPriceInfo['departure']->getExtraCharge(),
|
||||
'name' => $bookingRequest->getTravelerCount() .' x '. $bookingPriceInfo['departure']->getName()
|
||||
]);
|
||||
|
||||
foreach ($bookingRequest->getTravelOptions() as $option)
|
||||
{
|
||||
$this->createServiceItem($bookingUrl, $serviceItemDefaults + [
|
||||
'service_price' => $option->getPrice() * $bookingRequest->getTravelerCount(),
|
||||
'name' => $bookingRequest->getTravelerCount() .' x '. $option->getName()
|
||||
]);
|
||||
}
|
||||
|
||||
// Actually: extra_category
|
||||
foreach ($bookingPriceInfo['classOptions'] as $classOption)
|
||||
{
|
||||
$this->createServiceItem($bookingUrl, $serviceItemDefaults + [
|
||||
'service_price' => $classOption['count'] * $classOption['price'],
|
||||
'name' => $classOption['count'] .' x '. $classOption['name']
|
||||
]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$viewPosition = 100;
|
||||
$viewPositionPrice = 50;
|
||||
$endDateStr = $travelDate->getEnd()->format('Y-m-d');
|
||||
|
||||
$arrangementDefaults = [
|
||||
'state' => (new \DateTime())->format('Y-m-d'),
|
||||
'in_pdf' => 1
|
||||
];
|
||||
|
||||
$this->createArrangement($bookingUrl, $arrangementDefaults + [
|
||||
'type_id' => 4, // Flug
|
||||
'type_s' => 'Flug',
|
||||
'begin' => $startDateStr,
|
||||
'view_position' => --$viewPosition,
|
||||
'data_s' => ['Hinflug' => 'von '. $bookingPriceInfo['departure']->getName()],
|
||||
]);
|
||||
|
||||
$this->createArrangement($bookingUrl, $arrangementDefaults + [
|
||||
'type_id' => 26, // Preisinformation
|
||||
'type_s' => 'Preisinformation',
|
||||
'view_position' => --$viewPositionPrice,
|
||||
'data_s' => [
|
||||
'Name' => 'Abfahrts-/Abflugort '. $bookingPriceInfo['departure']->getName(),
|
||||
'Preis' => $bookingPriceInfo['departure']->getExtraCharge(),
|
||||
'Teilnehmer' => $bookingRequest->getTravelerCount(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->createArrangement($bookingUrl, $arrangementDefaults + [
|
||||
'type_id' => 24, // Rundreise
|
||||
'type_s' => 'Rundreise', // Rundreise
|
||||
'begin' => $startDateStr,
|
||||
'end' => $endDateStr,
|
||||
'view_position' => --$viewPosition,
|
||||
'data_s' => ['Name' => $tp->getTitle() .' ('. $travelDate->getName() .')'],
|
||||
]);
|
||||
|
||||
$roomStrs = [];
|
||||
foreach ($bookingPriceInfo['rooms'] as $room)
|
||||
{
|
||||
$roomStrs[] = '1x '. $room['name'];
|
||||
|
||||
$this->createArrangement($bookingUrl, $arrangementDefaults + [
|
||||
'type_id' => 26, // Preisinformation
|
||||
'type_s' => 'Preisinformation',
|
||||
'view_position' => --$viewPositionPrice,
|
||||
'data_s' => [
|
||||
'Name' => 'pro Person im \''. $room['name'] .'\'',
|
||||
'Preis' => $room['price'],
|
||||
'Teilnehmer' => $room['adults'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
$this->createArrangement($bookingUrl, $arrangementDefaults + [
|
||||
'type_id' => 5, // Hotel
|
||||
'type_s' => 'Hotel',
|
||||
'begin' => $startDateStr,
|
||||
'end' => $endDateStr,
|
||||
'view_position' => --$viewPosition,
|
||||
'data_s' => ['Zimmer' => implode(', ', $roomStrs)],
|
||||
]);
|
||||
|
||||
// Actually: extra_category
|
||||
foreach ($bookingPriceInfo['classOptions'] as $classOption)
|
||||
{
|
||||
$this->createArrangement($bookingUrl, $arrangementDefaults + [
|
||||
'type_id' => 26, // Preisinformation
|
||||
'type_s' => 'Preisinformation',
|
||||
'view_position' => --$viewPositionPrice,
|
||||
'data_s' => [
|
||||
'Name' => $classOption['name'],
|
||||
'Preis' => $classOption['price'],
|
||||
'Teilnehmer' => $classOption['count'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->createArrangement($bookingUrl, $arrangementDefaults + [
|
||||
'type_id' => 4, // Flug
|
||||
'type_s' => 'Flug',
|
||||
'begin' => $endDateStr,
|
||||
'view_position' => --$viewPosition,
|
||||
'data_s' => ['Rückflug' => $bookingRequest->getDeparture()->getName()],
|
||||
]);
|
||||
|
||||
foreach ($bookingRequest->getTravelOptions() as $option)
|
||||
{
|
||||
$this->createArrangement($bookingUrl, $arrangementDefaults + [
|
||||
'type_id' => 26, // Preisinformation
|
||||
'type_s' => 'Preisinformation',
|
||||
'view_position' => --$viewPositionPrice,
|
||||
'data_s' => [
|
||||
'Name' => $option->getName(),
|
||||
'Preis' => $option->getPrice(),
|
||||
'Teilnehmer' => $bookingRequest->getTravelerCount(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($bookingPriceInfo['insurances'] as $insuranceInfo)
|
||||
{
|
||||
$this->createServiceItem($bookingUrl, [
|
||||
'travel_company_id' => 30,
|
||||
'service_price' => $insuranceInfo['count'] * $insuranceInfo['insurancePriceValue'],
|
||||
'name' => $insuranceInfo['count'] . 'x ' . $insuranceInfo['insurance']->getName() . ' ('.
|
||||
$insuranceInfo['insurancePrice']->getCode() . ')',
|
||||
'commission' => round(($insuranceInfo['count'] * $insuranceInfo['insurancePriceValue']) * 20 / 100, 2),
|
||||
'travel_date' => $startDateStr,
|
||||
]);
|
||||
}
|
||||
|
||||
return $bookingUrl;
|
||||
}
|
||||
|
||||
private function createLead(BookingRequest $bookingRequest, TravelDate $travelDate)
|
||||
{
|
||||
$resp = $this->httpPost('lead', ['lead' => [
|
||||
'customerForm' => [
|
||||
'salutation_id' => $bookingRequest->getSalutation(),
|
||||
'name' => $bookingRequest->getLastName(),
|
||||
'firstname' => $bookingRequest->getFirstName(),
|
||||
'street' => $bookingRequest->getStreetAddress(),
|
||||
'zip' => $bookingRequest->getZipCode(),
|
||||
'city' => $bookingRequest->getCity(),
|
||||
'country_id' => $bookingRequest->getNation(),
|
||||
'phone' => $bookingRequest->getPhone(),
|
||||
'fax' => $bookingRequest->getFax(),
|
||||
'email' => $bookingRequest->getEmail()
|
||||
],
|
||||
'request_date' => (new \DateTime())->format('Y-m-d'),
|
||||
'sf_guard_user_id' => self::API_USER_ID,
|
||||
'status_id' => 7, // 'gebucht'
|
||||
'travelperiod_start' => $travelDate->getStart()->format('Y-m-d'),
|
||||
'travelperiod_end' => $travelDate->getEnd()->format('Y-m-d'),
|
||||
//'travelcategory_id'
|
||||
'is_closed' => 1,
|
||||
'website_id' => self::WEBSITE_ID,
|
||||
'initialcontacttype_id' => 14,
|
||||
// 'travelperiod_length
|
||||
'remarks' => $bookingRequest->getNotes()
|
||||
]]);
|
||||
|
||||
if ($resp['success'])
|
||||
{
|
||||
$ret = $this->httpGet($resp['location']);
|
||||
if ($ret == null)
|
||||
{
|
||||
$this->warn('Failed retrieving newly created lead object', $bookingRequest, $travelDate);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function createBooking(BookingRequest $bookingRequest, TravelDate $travelDate, $bookingPriceInfo,
|
||||
$customerId, $leadId)
|
||||
{
|
||||
$tp = $travelDate->getTravelProgram();
|
||||
$resp = $this->httpPost('booking', ['booking' => [
|
||||
'booking_date' => (new \DateTime())->format('Y-m-d'),
|
||||
'customer_id' => $customerId,
|
||||
'lead_id' => $leadId,
|
||||
'travel_country_id' => $tp->getTravelCountry(),
|
||||
'travel_category_id' => $tp->getTravelCategory(),
|
||||
'travelagenda_id' => $tp->getTravelAgenda(),
|
||||
'sf_guard_user_id' => self::API_USER_ID,
|
||||
'branch_id' => 4,
|
||||
'website_id' => self::WEBSITE_ID,
|
||||
'title' => $tp->getTitle(),
|
||||
'start_date' => $travelDate->getStart()->format('Y-m-d'),
|
||||
'end_date' => $travelDate->getEnd()->format('Y-m-d'),
|
||||
'pax' => $bookingRequest->getTravelerCount(),
|
||||
'travel_number' => $travelDate->getName(),
|
||||
'price' => $bookingPriceInfo['totalWithoutInsurance'],
|
||||
|
||||
'participant_salutation_id' => $bookingRequest->getTravelers()[0]->getSex(),
|
||||
'participant_name' => $bookingRequest->getTravelers()[0]->getLastName(),
|
||||
'participant_firstname' => $bookingRequest->getTravelers()[0]->getFirstName(),
|
||||
'participant_birthdate' => $bookingRequest->getTravelers()[0]->getBirthDate()->format('Y-m-d'),
|
||||
]]);
|
||||
|
||||
if (!$resp['success'])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return $resp['location'];
|
||||
}
|
||||
|
||||
private function createTraveler($bookingUrl, Traveler $traveler)
|
||||
{
|
||||
$resp = $this->httpPost($bookingUrl .'/participant.json', ['participant' => [
|
||||
'participant_salutation_id' => $traveler->getSex(),
|
||||
'participant_name' => $traveler->getLastName(),
|
||||
'participant_firstname' => $traveler->getFirstName(),
|
||||
'participant_birthdate' => $traveler->getBirthDate()->format('Y-m-d'),
|
||||
]], true);
|
||||
return $resp['success'];
|
||||
}
|
||||
|
||||
private function createServiceItem($bookingUrl, $serviceItemData)
|
||||
{
|
||||
$resp = $this->httpPost($bookingUrl .'/serviceitem.json', ['booking_service_item' => $serviceItemData], true);
|
||||
if (!$resp['success'])
|
||||
{
|
||||
$this->warn('Failed creating service item '. $serviceItemData['name'] .' for booking '. $bookingUrl);
|
||||
}
|
||||
return $resp['success'];
|
||||
}
|
||||
|
||||
private function createArrangement($bookingUrl, $arrangementData)
|
||||
{
|
||||
if (isset($arrangementData['data_s']) && is_array($arrangementData['data_s']))
|
||||
{
|
||||
$tmp = [];
|
||||
foreach ($arrangementData['data_s'] as $k => $v)
|
||||
{
|
||||
$tmp[] .= $k .': '. $v;
|
||||
}
|
||||
$arrangementData['data_s'] = implode("\n", $tmp);
|
||||
}
|
||||
$resp = $this->httpPost($bookingUrl .'/arrangement.json', ['arrangement' => $arrangementData], true);
|
||||
if (!$resp['success'])
|
||||
{
|
||||
$this->warn('Failed creating arrangement item '. $arrangementData['type_s'] .' for booking '. $bookingUrl);
|
||||
}
|
||||
return $resp['success'];
|
||||
}
|
||||
|
||||
private function httpGet($url)
|
||||
{
|
||||
$resp = Util::httpGet($url, ['X-ApiKey: '. self::API_KEY]);
|
||||
$ret = json_decode($resp['content'], true);
|
||||
if ($ret === null)
|
||||
{
|
||||
$this->warn('Invalid server response: '. $resp['content']);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function httpPost($context, $postData = [], $isContextFullUrl = false)
|
||||
{
|
||||
$url = $isContextFullUrl ? $context : self::API_URL.'/'. $context .'.json';
|
||||
$resp = Util::httpPost($url, $postData, ['X-ApiKey: '. self::API_KEY], true);
|
||||
return [
|
||||
'content' => json_decode($resp['content']),
|
||||
'location' => isset($resp['response_headers']['location'])
|
||||
? $resp['response_headers']['location']
|
||||
: null,
|
||||
'success' => $resp['success'] && ($resp['status_code'] == 201)
|
||||
];
|
||||
}
|
||||
|
||||
private function warn($msg, BookingRequest $bookingRequest = null, TravelDate $travelDate = null,
|
||||
$level = Logger::WARNING)
|
||||
{
|
||||
$this->logger->log($level, 'SternToursCrmBookingExporter: '. $msg);
|
||||
$this->logger->log($level, '*** Date: '. (new \DateTime())->format('d.m.Y'));
|
||||
if ($travelDate !== null)
|
||||
{
|
||||
$this->logger->log($level, '*** Travel date: '. $travelDate->getName() .'('. $travelDate->getStart()->format('d.m.Y') .
|
||||
' - '. $travelDate->getEnd()->format('d.m.Y') .')');
|
||||
//$this->logger->warn('*** Travel program ID: '. $travelDate->)
|
||||
}
|
||||
if ($bookingRequest !== null)
|
||||
{
|
||||
$this->logger->log($level, '*** User name: '. $bookingRequest->getFirstName() .' '. $bookingRequest->getLastName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,9 @@
|
|||
namespace AppBundle\Form;
|
||||
|
||||
|
||||
use AppBundle\Entity\BookingRequest;
|
||||
use AppBundle\Entity\TravelDate;
|
||||
use AppBundle\Entity\Traveler;
|
||||
use AppBundle\Entity\TravelProgram;
|
||||
use AppBundle\Util;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
|
@ -15,7 +17,12 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
|||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Choice;
|
||||
use Symfony\Component\Validator\Constraints\NotNull;
|
||||
|
|
@ -26,7 +33,20 @@ class BookingRequestType extends AbstractType
|
|||
'1 Person' => 1,
|
||||
'2 Personen' => 2,
|
||||
'3 Personen' => 3,
|
||||
'4 Personen' => 4
|
||||
'4 Personen' => 4,
|
||||
];
|
||||
|
||||
public static $NATION_CHOICES = [
|
||||
'Deutschland' => 27,
|
||||
'Österreich' => 34,
|
||||
'Schweiz' => 181,
|
||||
'Niederlande' => 196,
|
||||
'Sonstiges' => 197,
|
||||
];
|
||||
|
||||
public static $SALUTATION_CHOICES = [
|
||||
'Herr' => BookingRequest::MR,
|
||||
'Frau' => BookingRequest::MRS
|
||||
];
|
||||
|
||||
/*
|
||||
|
|
@ -47,7 +67,8 @@ class BookingRequestType extends AbstractType
|
|||
{
|
||||
$resolver->setDefaults([
|
||||
'travel_date' => null,
|
||||
'travel_program' => null
|
||||
'travel_program' => null,
|
||||
'data_class' => 'AppBundle\Entity\BookingRequest',
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('travel_date', ['AppBundle\Entity\TravelDate']);
|
||||
|
|
@ -65,14 +86,46 @@ class BookingRequestType extends AbstractType
|
|||
/* @var TravelProgram $travelProgram */
|
||||
$travelProgram = $options['travel_program'];
|
||||
|
||||
$builder
|
||||
->add('salutation', ChoiceType::class, [
|
||||
'placeholder' => 'Anrede (Bitte wählen) *',
|
||||
'choices' => self::$SALUTATION_CHOICES,
|
||||
'constraints' => [
|
||||
new NotNull(),
|
||||
new Choice(['choices' => self::$SALUTATION_CHOICES])
|
||||
]
|
||||
])
|
||||
->add('firstName')
|
||||
->add('lastName')
|
||||
->add('streetAddress')
|
||||
->add('zipCode')
|
||||
->add('city')
|
||||
->add('nation', ChoiceType::class, [
|
||||
'choices' => self::$NATION_CHOICES,
|
||||
'constraints' => [
|
||||
new NotNull(),
|
||||
new Choice(['choices' => self::$NATION_CHOICES])
|
||||
]
|
||||
])
|
||||
->add('phone')
|
||||
->add('fax')
|
||||
->add('email')
|
||||
->add('travelers', CollectionType::class, [
|
||||
'entry_type' => TravelerType::class,
|
||||
'by_reference' => false,
|
||||
])
|
||||
->add('notes', TextareaType::class, ['required' => false])
|
||||
->add('acceptTerms', CheckboxType::class, ['required' => true])
|
||||
;
|
||||
|
||||
$builder->add('departure', EntityType::class, [
|
||||
'placeholder' => 'Abflugort (Bitte wählen) *',
|
||||
'class' => 'AppBundle\Entity\TravelDeparturePoint',
|
||||
'choices' => $travelDate->getDepartures(),
|
||||
'constraints' => [
|
||||
new NotNull(),
|
||||
new Choice([
|
||||
'choices' => $travelDate->getDepartures(),
|
||||
'multiple' => true
|
||||
'choices' => $travelDate->getDepartures()
|
||||
]
|
||||
)]
|
||||
]);
|
||||
|
|
@ -84,7 +137,6 @@ class BookingRequestType extends AbstractType
|
|||
)]
|
||||
]);
|
||||
|
||||
|
||||
$insuranceChoices = [];
|
||||
if ($travelProgram->getInsurance1())
|
||||
{
|
||||
|
|
|
|||
60
trunk/src/AppBundle/Form/TravelerType.php
Normal file
60
trunk/src/AppBundle/Form/TravelerType.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
|
||||
* @date 02/10/2017
|
||||
*/
|
||||
|
||||
namespace AppBundle\Form;
|
||||
|
||||
use AppBundle\Entity\Traveler;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Choice;
|
||||
use Symfony\Component\Validator\Constraints\NotNull;
|
||||
|
||||
class TravelerType extends AbstractType
|
||||
{
|
||||
public static $SEX_CHOICES = [
|
||||
'männlich' => Traveler::MALE,
|
||||
'weiblich' => Traveler::FEMALE
|
||||
];
|
||||
|
||||
/**
|
||||
* @param OptionsResolver $resolver
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults(array(
|
||||
'data_class' => 'AppBundle\Entity\Traveler'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
* @param array $options
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('sex', ChoiceType::class, [
|
||||
'placeholder' => 'Geschlecht (Bitte wählen) *',
|
||||
'choices' => self::$SEX_CHOICES,
|
||||
'constraints' => [
|
||||
new Choice(['choices' => self::$SEX_CHOICES])
|
||||
],
|
||||
'required' => true,
|
||||
])
|
||||
->add('firstName')
|
||||
->add('lastName')
|
||||
->add('birthDate', DateType::class, [
|
||||
'widget' => 'single_text',
|
||||
'format' => 'dd.MM.yyyy',
|
||||
'required' => true,
|
||||
])
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -71,9 +71,11 @@ class KernelControllerListener
|
|||
$request->attributes->set('_controller', 'AppBundle:Default:'. $handler);
|
||||
}
|
||||
}
|
||||
elseif (isset($parentNode) && $parentNode->getTravelProgram() != null && $pathArray[$i] == 'buchen')
|
||||
elseif (isset($parentNode) && $parentNode->getTravelProgram() != null && (
|
||||
$pathArray[$i] == 'buchen' || $pathArray[$i] == 'berechne-gesamtpreis'))
|
||||
{
|
||||
$request->attributes->set('travelProgramPage', $parentNode);
|
||||
$request->attributes->set('action', $pathArray[$i]);
|
||||
$request->attributes->set('_controller', 'AppBundle:Booking:index');
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -13,4 +13,9 @@
|
|||
.st-booking-table .st-total-price {
|
||||
border-bottom: 1px solid;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* #TODO Move */
|
||||
.st-required:after {
|
||||
content: " *";
|
||||
}
|
||||
|
|
@ -733,7 +733,7 @@ a,
|
|||
text-transform: uppercase;
|
||||
display: block;
|
||||
position: relative;
|
||||
background-color: #ffc926;
|
||||
background-color: #777777;
|
||||
color: #fff;
|
||||
padding: 8px 58px 8px 20px;
|
||||
font-size: 14px;
|
||||
|
|
@ -790,7 +790,7 @@ a,
|
|||
z-index: 10;
|
||||
left: 0;
|
||||
top: 140px;
|
||||
background-color: #648859;
|
||||
background-color: #1a457c;
|
||||
color: #fff;
|
||||
padding: 4px 6px 4px 12px;
|
||||
font-weight: bold;
|
||||
|
|
@ -835,7 +835,7 @@ a,
|
|||
text-transform: uppercase;
|
||||
display: block;
|
||||
position: relative;
|
||||
background-color: #ffc926;
|
||||
background-color: #777777;
|
||||
color: #fff;
|
||||
padding: 4px 12px 3px 12px;
|
||||
font-size: 14px;
|
||||
|
|
@ -852,18 +852,18 @@ a,
|
|||
}
|
||||
.item-switch > a.item-button-prev:hover {
|
||||
color: #fff;
|
||||
background-color: #777777;
|
||||
background-color: #ffc926;
|
||||
}
|
||||
.item-switch > a.item-button-next:hover {
|
||||
color: #fff;
|
||||
background-color: #777777;
|
||||
background-color: #ffc926;
|
||||
}
|
||||
.travel-wrapper .item > a.item-button:hover {
|
||||
color: #fff;
|
||||
background-color: #777777;
|
||||
background-color: #ffc926;
|
||||
}
|
||||
.travel-wrapper .item > a.item-button:hover:after {
|
||||
background-color: #5e5e5e;
|
||||
background-color: #f2b600;
|
||||
}
|
||||
.travel-wrapper .item > a.item-button:after {
|
||||
transition: 0.5s ease;
|
||||
|
|
@ -873,7 +873,7 @@ a,
|
|||
bottom: 0;
|
||||
width: 46px;
|
||||
right: 0px;
|
||||
background-color: #f2b600;
|
||||
background-color: #5e5e5e;
|
||||
content: '';
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
|
|
@ -883,7 +883,7 @@ a,
|
|||
font-size: 26px;
|
||||
}
|
||||
.item-switch > a.item-button-prev:hover:before {
|
||||
background-color: #5e5e5e;
|
||||
background-color: #f2b600;
|
||||
}
|
||||
.item-switch > a.item-button-prev:before {
|
||||
transition: 0.5s ease;
|
||||
|
|
@ -893,7 +893,7 @@ a,
|
|||
bottom: 0;
|
||||
width: 30px;
|
||||
left: 0px;
|
||||
background-color: #f2b600;
|
||||
background-color: #5e5e5e;
|
||||
content: '\f104';
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
|
|
@ -903,7 +903,7 @@ a,
|
|||
font-size: 26px;
|
||||
}
|
||||
.item-switch > a.item-button-next:hover:after {
|
||||
background-color: #5e5e5e;
|
||||
background-color: #f2b600;
|
||||
}
|
||||
.item-switch > a.item-button-next:after {
|
||||
transition: 0.5s ease;
|
||||
|
|
@ -913,7 +913,7 @@ a,
|
|||
bottom: 0;
|
||||
width: 30px;
|
||||
right: 0px;
|
||||
background-color: #f2b600;
|
||||
background-color: #5e5e5e;
|
||||
content: '';
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
|
|
@ -3296,6 +3296,11 @@ h5:hover a,
|
|||
.c2 li span,
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #777777;
|
||||
border-color: #777777;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:active:hover {
|
||||
background-color: #ffc926;
|
||||
border-color: #ffc926;
|
||||
}
|
||||
|
|
|
|||
1
trunk/src/AppBundle/Resources/public/css/custom.css.map
Normal file
1
trunk/src/AppBundle/Resources/public/css/custom.css.map
Normal file
File diff suppressed because one or more lines are too long
45
trunk/src/AppBundle/Resources/public/js/booking.js
Normal file
45
trunk/src/AppBundle/Resources/public/js/booking.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
$(document).ready(function() {
|
||||
|
||||
var frm$ = $('.st-booking-form');
|
||||
var summary$ = $('.st-booking-summary');
|
||||
var travelerCountDd$ = $('#booking_request_travelerCount');
|
||||
var travelers$ = $('.st-traveler');
|
||||
var travelerFields$ = travelers$.find('input,select');
|
||||
|
||||
frm$.find('input, select').change(function() {
|
||||
var tmp = location.href.split('?');
|
||||
var tmp2 = tmp[0].split('/');
|
||||
tmp2.pop();
|
||||
var url = tmp2.join('/') + '/berechne-gesamtpreis';
|
||||
if (tmp[1])
|
||||
{
|
||||
url += '?'+ tmp[1];
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'post',
|
||||
data: frm$.serialize()
|
||||
|
||||
}).then(function(r) {
|
||||
summary$.html(r);
|
||||
|
||||
}, function() {
|
||||
summary$.html('Aufgrund eines Fehlers konnte kein Angebot ermittelt werden.');
|
||||
})
|
||||
});
|
||||
|
||||
function updateTravelers()
|
||||
{
|
||||
var travelerCount = parseInt(travelerCountDd$.val());
|
||||
travelers$.hide();
|
||||
travelerFields$.prop('required', false);
|
||||
for (var i = 1; i <= travelerCount; ++i)
|
||||
{
|
||||
$('.st-traveler-'+ i).show().find('input,select').prop('required', true);
|
||||
}
|
||||
}
|
||||
travelerCountDd$.change(updateTravelers);
|
||||
updateTravelers();
|
||||
|
||||
});
|
||||
72
trunk/src/AppBundle/Twig/AppExtension.php
Normal file
72
trunk/src/AppBundle/Twig/AppExtension.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
|
||||
* @date 02/10/2017
|
||||
*/
|
||||
|
||||
namespace AppBundle\Twig;
|
||||
|
||||
|
||||
class AppExtension extends \Twig_Extension
|
||||
{
|
||||
protected $environment;
|
||||
private $template;
|
||||
|
||||
public function __construct(\Twig_Environment $env)
|
||||
{
|
||||
$this->environment = $env;
|
||||
}
|
||||
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
'form_field' => new \Twig_SimpleFunction('form_field', array($this, 'formField'), array(
|
||||
'is_safe' => array('html')
|
||||
)),
|
||||
'form_field_pho' => new \Twig_SimpleFunction('form_field_pho', array($this, 'formFieldPho'), array(
|
||||
'is_safe' => array('html')
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
public function formField($form, $label = null, $opt = null)
|
||||
{
|
||||
$this->template = $this->environment->loadTemplate( '::default/form/helpers.html.twig' );
|
||||
return $this->template->renderBlock('form_field', array(
|
||||
'form' => $form,
|
||||
'label' => $label,
|
||||
'opt' => $opt
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form field with placeholder only
|
||||
*
|
||||
* @param $form
|
||||
* @param null $label
|
||||
* @param null $opt
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function formFieldPho($form, $label = null, $opt = [])
|
||||
{
|
||||
$this->template = $this->environment->loadTemplate( '::default/form/helpers.html.twig' );
|
||||
|
||||
return $this->template->renderBlock('form_field_pho', array(
|
||||
'form' => $form,
|
||||
'label' => $label,
|
||||
'opt' => $opt
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'app_extension';
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,108 @@ class Util
|
|||
$prop->setValue($entity, $collection);
|
||||
}
|
||||
|
||||
public static function formatPrice($value)
|
||||
{
|
||||
return number_format($value, 2, ',', '.') .' €';
|
||||
}
|
||||
|
||||
static function httpRequest($url, $method = "GET", $data = "", $headers = array(), $withRespHeaders = false,
|
||||
$cookieJar = null)
|
||||
{
|
||||
global $kernel;
|
||||
|
||||
$ch = curl_init();
|
||||
$headerList = array();
|
||||
//self::buildHttpQueryForCurl($headers, $headerList); //?
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_HEADER, $withRespHeaders);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
if (isset($cookieJar))
|
||||
{
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieJar);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieJar);
|
||||
}
|
||||
if (strtoupper($method) == 'POST')
|
||||
{
|
||||
$dataStr = is_array($data) ? http_build_query($data) : $data;
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $dataStr);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
|
||||
$ret = [
|
||||
'info' => curl_getinfo($ch),
|
||||
'success' => true,
|
||||
'status_code' => curl_getinfo($ch, CURLINFO_HTTP_CODE)
|
||||
];
|
||||
|
||||
if ($withRespHeaders)
|
||||
{
|
||||
$respArray = explode("\r\n\r\n", $response);
|
||||
$ret['response_headers'] = [];
|
||||
// count() - 2 due to HTTP status 100 (continue) with multiple responses
|
||||
$headers = is_array($respArray) && isset($respArray[count($respArray) - 2])
|
||||
? explode("\r\n", $respArray[count($respArray) - 2])
|
||||
: [];
|
||||
|
||||
// HTTP/1.1 200 OK
|
||||
array_shift($headers);
|
||||
|
||||
foreach ($headers as $header)
|
||||
{
|
||||
$headerKV = explode(': ', $header);
|
||||
$key = strtolower($headerKV[0]);
|
||||
if (isset($ret['response_headers'][$key]))
|
||||
{
|
||||
if (is_array($ret['response_headers'][$key]))
|
||||
{
|
||||
$ret['response_headers'][$key][] = $headerKV[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret['response_headers'][$key] = array($ret['response_headers'][$key], $headerKV[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret['response_headers'][$key] = $headerKV[1];
|
||||
}
|
||||
}
|
||||
$ret['content'] = $respArray[count($respArray) - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret['content'] = $response;
|
||||
}
|
||||
|
||||
if (curl_errno($ch) > 0)
|
||||
{
|
||||
$ret['info']['curl_errno'] = curl_errno($ch);
|
||||
$ret['success'] = false;
|
||||
}
|
||||
|
||||
if (isset($kernel))
|
||||
{
|
||||
$logger = $kernel->getContainer()->get('logger');
|
||||
$logger->warn('HTTP request to \''. $url .'\' with server response code '. $ret['status_code']);
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function httpPost($url, $postData = '', $headers = array(), $withRespHeaders = true, $cookieJarPath = null)
|
||||
{
|
||||
return self::httpRequest($url, 'POST', $postData, $headers, $withRespHeaders, $cookieJarPath);
|
||||
}
|
||||
|
||||
static function httpGet($url, $headers = array(), $withRespHeaders = false, $cookieJarPath = null)
|
||||
{
|
||||
return self::httpRequest($url, 'GET', '', $headers, $withRespHeaders, $cookieJarPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints formatted back-trace. CLI-output is colorized as well.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue