* Behoben: Falsche CRM-URL in Buchungs-Service-E-Mail

* Kontaktformular

git-svn-id: http://78.47.251.156/svn/dev/sterntours-3@3300 f459cee4-fb09-11de-96c3-f9c5f16c3c76
This commit is contained in:
uli 2017-02-21 16:54:37 +00:00
parent ff9c159297
commit 353b758bcd
12 changed files with 741 additions and 34 deletions

View file

@ -0,0 +1,27 @@
{# @var contact_request \AppBundle\Entity\ContactRequest
#}Neue Kontaktanfrage:
CRM: {{ crm_url }}
Anrede: {{ contact_request.salutation == 1 ? 'Herr' : 'Frau' }}
Vorname: {{ contact_request.firstName|default('-') }}
Nachname: {{ contact_request.lastName|default('-') }}
Adresse: {{ contact_request.streetAddress|default('-') }}
PLZ: {{ contact_request.zipCode|default('-') }}
Ort: {{ contact_request.city|default('-') }}
Telefonnummer: {{ contact_request.phone|default('-') }}
Fax: {{ contact_request.mobilePhone|default('-') }}
E-Mail: {{ contact_request.email|default('-') }}
Abflugort 1: {{ contact_request.departure0|default('-') }}
Abflugort 2: {{ contact_request.departure1|default('-') }}
Abflugort 3: {{ contact_request.departure2|default('-') }}
Reisebeginn: {{ contact_request.start is not empty ? contact_request.start|date }}
Reiseende: {{ contact_request.end is not empty ? contact_request.end|date }}
Dauer: {{ contact_request.duration|default('-') }} Tage
Anzahl d. Reisenden: {{ contact_request.travelerCount|default('-') }}
Kommentar:
{{ contact_request.notes }}

View file

@ -4,4 +4,8 @@
{% block body %}
<h1>Vielen Dank für Ihren Buchungsauftrag!</h1>
<p>
<a href="/">Zurück zur Startseite</a>
</p>
{% endblock %}

View file

@ -0,0 +1,73 @@
{% extends get_base_template() %}
{% form_theme contact_form 'default/form/theme.html.twig' %}
{% block body %}
<section class="clearfix">
<h1>Individuelle Reiseplanung</h1>
<p>
Sie haben noch nicht die richtige Reise gefunden? Oder planen Sie einen ganz besonderen Urlaub, den Sie im
Katalog nicht finden? Dann teilen Sie uns doch alle relevanten Informationen für Ihre Wunsch-Reise mit. Wir
werden uns dann umgehend bei Ihnen melden. Ihre Daten (Name, Telefonnummer etc.) werden ausschließlich zur
Bearbeitung Ihrer Anfrage verwendet und nicht an Dritte weitergegeben.
</p>
<div id="contact_form" class="contact_form">
<form id="contactform" action="/kontakt" name="contactform" method="post">
<div class="form-box">
<div class="col-md-12 col-sm-12 col-xs-12">
<h2>Reisedaten</h2>
</div>
<div class="col-md-12 col-sm-12 col-xs-12">
<h3>Abflughafen</h3>
</div>
<div class="col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.departure0, 'Abflughafen 1. Wahl') }}</div>
<div class="col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.departure1, 'Abflughafen 2. Wahl') }}</div>
<div class="col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.departure2, 'Abflughafen 3. Wahl') }}</div>
<div class="col-md-12 col-sm-12 col-xs-12">
<h3>Reisetermin</h3>
</div>
<div class="form-group col-md-6 col-sm-6 col-xs-12">{{ form_field_pho(contact_form.start, 'Anreise') }}</div>
<div class="form-group col-md-6 col-sm-6 col-xs-12">{{ form_field_pho(contact_form.end, 'Abreise') }}</div>
<div class="col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.duration, 'Reisedauer in Tagen') }}</div>
</div><!-- end form-box -->
<div class="form-box">
<div class="col-md-12 col-sm-12 col-xs-12">
<h2>Persönliche Daten</h2>
</div>
<div class="form-group col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.salutation) }}</div>
<div class="col-md-6 col-sm-6 col-xs-12">{{ form_field_pho(contact_form.firstName, 'Vorname') }}</div>
<div class="col-md-6 col-sm-6 col-xs-12">{{ form_field_pho(contact_form.lastName, 'Nachname') }}</div>
<div class="col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.streetAddress, 'Straße, Hausnummer') }}</div>
<div class="col-md-6 col-sm-6 col-xs-12">{{ form_field_pho(contact_form.zipCode, 'Postleitzahl') }}</div>
<div class="col-md-6 col-sm-6 col-xs-12">{{ form_field_pho(contact_form.city, 'Ort') }}</div>
<div class="form-group col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.nation) }}</div>
<div class="col-md-6 col-sm-6 col-xs-12">{{ form_field_pho(contact_form.phone, 'Telefon tagsüber') }}</div>
<div class="col-md-6 col-sm-6 col-xs-12">{{ form_field_pho(contact_form.mobilePhone, 'Telefon mobil') }}</div>
<div class="col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.email, 'E-Mail-Adresse') }}</div>
<div class="form-group col-md-12 col-sm-12 col-xs-12">{{ form_field_pho(contact_form.travelerCount) }}</div>
<div class="col-md-12 col-sm-12 col-xs-12">
{{ form_field_pho(contact_form.notes, 'Bitte Reiseland und Programmwunsch angeben', {attr: {rows: 8}}) }}
<button type="submit" value="SEND" id="submit" class="aligncenter btn btn-primary btn-lg border-radius">Anfrage absenden</button>
</div>
</div><!-- end form-box -->
{{ form_rest(contact_form) }}
</form>
</div><!-- end contact-form -->
</section>
{% endblock body %}

View file

@ -0,0 +1,11 @@
{% extends 'base.html.twig' %}
{% block canonical_tag %}{% endblock %}
{% block body %}
<h1>Vielen Dank, wir haben Ihre Anfrage erhalten!</h1>
<p>
<a href="/">Zurück zur Startseite</a>
</p>
{% endblock %}

View file

@ -32,7 +32,12 @@ services:
- { name: form.type, alias: datalist }
app.booking_exporter:
class: AppBundle\Export\SternToursCrmBookingExporter
class: AppBundle\Export\BookingSternToursCrmExporter
arguments:
- '@monolog.logger'
app.contact_exporter:
class: AppBundle\Export\ContactSternToursCrmExporter
arguments:
- '@monolog.logger'

View file

@ -94,6 +94,7 @@ class BookingController extends Controller
$em->flush();
$crmBookingUrl = $this->get('app.booking_exporter')->process($bookingRequest, $travelDate, $bookingPriceInfo);
$crmBookingUrl = preg_replace('/\\/api/', '', $crmBookingUrl) .'/edit';
$this->get('mailer')->send(\Swift_Message::newInstance()
->setSubject('Ihr Buchungsauftrag bei STERN TOURS')
@ -118,7 +119,7 @@ class BookingController extends Controller
->setBody(
$this->renderView('default/email/bookingServiceEmail.txt.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'crm_url' => $crmBookingUrl .'/edit',
'crm_url' => $crmBookingUrl,
'travel_program_url' => Util::getBaseUrl() . $travelProgramPage->getUrlPath(),
'booking_request' => $bookingRequest,
'booking_price_info' => $bookingPriceInfo,

View file

@ -3,8 +3,10 @@
namespace AppBundle\Controller;
use AppBundle\Entity\BreadcrumbEntry;
use AppBundle\Entity\ContactRequest;
use AppBundle\Entity\Page;
use AppBundle\Entity\TravelProgram;
use AppBundle\Form\ContactRequestType;
use AppBundle\Form\SearchRequestType;
use AppBundle\Form\TtSearchRequestType;
use AppBundle\Util;
@ -224,6 +226,62 @@ class DefaultController extends Controller
]);
}
/**
* @Route("/kontakt")
*/
public function contactAction(Request $request)
{
$form = $this->createForm(ContactRequestType::class);
$breadcrumbEntries = [new BreadcrumbEntry('Kontaktformular')];
if ($request->getMethod() == 'POST')
{
$form->handleRequest($request);
if ($form->isValid())
{
/** @var ContactRequest $contactRequest */
$contactRequest = $form->getData();
$crmLeadUrl = $this->get('app.contact_exporter')->process($contactRequest);
if ($crmLeadUrl)
{
$crmLeadUrl = preg_replace('/\\/api\\/lead/', '/leads', $crmLeadUrl) .'/edit';
}
else
{
$crmLeadUrl = '[Übertragung zum CRM fehlgeschlagung]';
}
$this->get('mailer')->send(\Swift_Message::newInstance()
->setSubject('Kontaktformular (stern-tours.de)')
->setFrom('stern@stern-tours.de', 'STERN TOURS')
->setTo('ulrich.hecht@hecht-software.de')
->setBody(
$this->renderView('default/email/contactServiceEmail.txt.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'crm_url' => $crmLeadUrl,
'contact_request' => $contactRequest,
]),
'text/plain', 'utf-8'
)
);
// #TODO This will lead to multiple submissions. Redirect instead!
return $this->render('default/pages/contactConfirmation.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'breadcrumb_entries' => $breadcrumbEntries,
]);
}
}
return $this->render('default/pages/contact.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'breadcrumb_entries' => $breadcrumbEntries,
'contact_form' => $form->createView(),
]);
}
public function headerAction()
{
$qb = $this->getEntityManager()->createQueryBuilder();

View file

@ -0,0 +1,361 @@
<?php
/**
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
* @date 02/21/2017
*/
namespace AppBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
class ContactRequest
{
const MR = 1;
const MRS = 2;
/** @var int|null $salutation */
private $salutation;
/** @var string|null $firstName */
private $firstName;
/** @var string|null $lastName */
private $lastName;
/** @var string|null $streetAddress */
private $streetAddress;
/** @var string|null $zipCode */
private $zipCode;
/** @var string|null $city */
private $city;
/** @var int|null $nation */
private $nation;
/** @var string|null $phone */
private $phone;
/** @var string|null $mobilePhone */
private $mobilePhone;
/** @var string|null $email */
private $email;
/**
* @var string|null $travelerCount */
private $travelerCount;
/** @var string|null $notes */
private $notes;
/** @var string|null $departure0 */
private $departure0;
/** @var string|null $departure1 */
private $departure1;
/** @var string|null $departure2 */
private $departure2;
/** @var \DateTime|null $start */
private $start;
/** @var \DateTime|null $end */
private $end;
/**
* @var int|null $duration
* @Assert\Type(type="integer", message="Bitte geben Sie eine gültige Ganzzahl für die Dauer in Tagen ein")
*/
private $duration;
/**
* @return int|null
*/
public function getSalutation()
{
return $this->salutation;
}
/**
* @param int|null $salutation
*/
public function setSalutation($salutation)
{
$this->salutation = $salutation;
}
/**
* @return null|string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* @param null|string $firstName
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
}
/**
* @return null|string
*/
public function getLastName()
{
return $this->lastName;
}
/**
* @param null|string $lastName
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
}
/**
* @return null|string
*/
public function getStreetAddress()
{
return $this->streetAddress;
}
/**
* @param null|string $streetAddress
*/
public function setStreetAddress($streetAddress)
{
$this->streetAddress = $streetAddress;
}
/**
* @return null|string
*/
public function getZipCode()
{
return $this->zipCode;
}
/**
* @param null|string $zipCode
*/
public function setZipCode($zipCode)
{
$this->zipCode = $zipCode;
}
/**
* @return null|string
*/
public function getCity()
{
return $this->city;
}
/**
* @param null|string $city
*/
public function setCity($city)
{
$this->city = $city;
}
/**
* @return int|null
*/
public function getNation()
{
return $this->nation;
}
/**
* @param int|null $nation
*/
public function setNation($nation)
{
$this->nation = $nation;
}
/**
* @return null|string
*/
public function getPhone()
{
return $this->phone;
}
/**
* @param null|string $phone
*/
public function setPhone($phone)
{
$this->phone = $phone;
}
/**
* @return null|string
*/
public function getMobilePhone()
{
return $this->mobilePhone;
}
/**
* @param null|string $mobilePhone
*/
public function setMobilePhone($mobilePhone)
{
$this->mobilePhone = $mobilePhone;
}
/**
* @return null|string
*/
public function getEmail()
{
return $this->email;
}
/**
* @param null|string $email
*/
public function setEmail($email)
{
$this->email = $email;
}
/**
* @return null|string
*/
public function getTravelerCount()
{
return $this->travelerCount;
}
/**
* @param null|string $travelerCount
*/
public function setTravelerCount($travelerCount)
{
$this->travelerCount = $travelerCount;
}
/**
* @return null|string
*/
public function getNotes()
{
return $this->notes;
}
/**
* @param null|string $notes
*/
public function setNotes($notes)
{
$this->notes = $notes;
}
/**
* @return null|string
*/
public function getDeparture0()
{
return $this->departure0;
}
/**
* @param null|string $departure0
*/
public function setDeparture0($departure0)
{
$this->departure0 = $departure0;
}
/**
* @return null|string
*/
public function getDeparture1()
{
return $this->departure1;
}
/**
* @param null|string $departure1
*/
public function setDeparture1($departure1)
{
$this->departure1 = $departure1;
}
/**
* @return null|string
*/
public function getDeparture2()
{
return $this->departure2;
}
/**
* @param null|string $departure2
*/
public function setDeparture2($departure2)
{
$this->departure2 = $departure2;
}
/**
* @return \DateTime|null
*/
public function getStart()
{
return $this->start;
}
/**
* @param \DateTime|null $start
*/
public function setStart($start)
{
$this->start = $start;
}
/**
* @return \DateTime|null
*/
public function getEnd()
{
return $this->end;
}
/**
* @param \DateTime|null $end
*/
public function setEnd($end)
{
$this->end = $end;
}
/**
* @return int|null
*/
public function getDuration()
{
return $this->duration;
}
/**
* @param int|null $duration
*/
public function setDuration($duration)
{
$this->duration = $duration;
}
}

View file

@ -13,18 +13,12 @@ use AppBundle\Entity\Traveler;
use AppBundle\Util;
use Monolog\Logger;
class SternToursCrmBookingExporter
class BookingSternToursCrmExporter extends SternToursCrmExporter
{
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;
parent::__construct($logger);
}
public function process(BookingRequest $bookingRequest, TravelDate $travelDate, $bookingPriceInfo)
@ -322,30 +316,6 @@ class SternToursCrmBookingExporter
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)
{

View file

@ -0,0 +1,61 @@
<?php
/**
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
* @date 02/21/2017
*/
namespace AppBundle\Export;
use AppBundle\Entity\ContactRequest;
use Monolog\Logger;
class ContactSternToursCrmExporter extends SternToursCrmExporter
{
public function __construct(Logger $logger)
{
parent::__construct($logger);
}
public function process(ContactRequest $contactRequest)
{
$data = ['lead' => [
'customerForm' => [
'salutation_id' => $contactRequest->getSalutation(),
'name' => $contactRequest->getLastName(),
'firstname' => $contactRequest->getFirstName(),
'street' => $contactRequest->getStreetAddress(),
'zip' => $contactRequest->getZipCode(),
'city' => $contactRequest->getCity(),
'country_id' => $contactRequest->getNation(),
'phone' => $contactRequest->getPhone(),
'phonemobile' => $contactRequest->getMobilePhone(),
'email' => $contactRequest->getEmail()
],
'request_date' => (new \DateTime())->format('Y-m-d'),
'sf_guard_user_id' => self::API_USER_ID,
'status_id' => 10, // 'Angebot erstellen'
'travelperiod_start' => $contactRequest->getStart(),
'travelperiod_end' => $contactRequest->getEnd(),
//'travelcategory_id'
'is_closed' => 0,
'website_id' => self::WEBSITE_ID,
'initialcontacttype_id' => 1,
'travelperiod_length' => $contactRequest->getDuration(),
'remarks' => $contactRequest->getNotes(),
'pax' => $contactRequest->getTravelerCount(),
]];
$resp = $this->httpPost('lead', $data);
if (!$resp['success'])
{
$this->logger->error(get_class($this). ': Failed submitting contact request to CRM');
$this->logger->error('*** Submitted data: '. json_encode($data));
$this->logger->error('*** Server response: '. $resp['content']);
}
return $resp['location'] ?? null;
}
}

View file

@ -0,0 +1,51 @@
<?php
/**
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
* @date 02/21/2017
*/
namespace AppBundle\Export;
use AppBundle\Util;
use Monolog\Logger;
abstract class SternToursCrmExporter
{
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'
protected $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
protected final function httpGet($url)
{
$resp = Util::httpGet($url, ['X-ApiKey: '. self::API_KEY]);
$ret = json_decode($resp['content'], true);
if ($ret === null)
{
$this->logger->warn(get_class($this) .': Invalid server response: '. $resp['content']);
$this->logger->warn('*** Date: '. (new \DateTime())->format('d.m.Y'));
}
return $ret;
}
protected final 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)
];
}
}

View file

@ -0,0 +1,85 @@
<?php
/**
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
* @date 02/21/2017
*/
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
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 ContactRequestType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\ContactRequest',
]);
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$travelerCountChoices = [];
for ($i = 1; $i <= 20; ++$i)
{
$travelerCountChoices[$i] = $i;
}
$builder
->add('salutation', ChoiceType::class, [
'placeholder' => 'Anrede (Bitte wählen) *',
'choices' => BookingRequestType::$SALUTATION_CHOICES,
'constraints' => [
new NotNull(),
new Choice(['choices' => BookingRequestType::$SALUTATION_CHOICES])
]
])
->add('firstName', TextType::class, ['required' => true])
->add('lastName', TextType::class, ['required' => true])
->add('streetAddress')
->add('zipCode')
->add('city')
->add('nation', ChoiceType::class, [
'placeholder' => 'Land (Bitte wählen)',
'choices' => BookingRequestType::$NATION_CHOICES,
'constraints' => [
new NotNull(),
new Choice(['choices' => BookingRequestType::$NATION_CHOICES])
],
'data' => BookingRequestType::$NATION_CHOICES['Deutschland'],
])
->add('phone', TextType::class, ['required' => true])
->add('mobilePhone')
->add('email', EmailType::class, ['required' => true])
->add('travelerCount', ChoiceType::class, [
'placeholder' => 'Anzahl der Reisenden (Bitte wählen)',
'choices' => $travelerCountChoices,
'constraints' => [
new Choice(['choices' => $travelerCountChoices])]
])
->add('notes', TextareaType::class, ['required' => false])
->add('departure0')
->add('departure1')
->add('departure2')
->add('start', StDateType::class, ['required' => false])
->add('end', StDateType::class, ['required' => false])
->add('duration')
;
}
}