<?php
namespace App\Service;
use App\Entity\VoyageInterface;
use App\Exception\WebServiceException;
use App\Service\Payment\PaymentEventInterface;
use App\Service\Payment\HiPay\HiPayManager;
use App\Service\Process\ProcessInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
use Symfony\Component\HttpClient\Exception\TimeoutException;
use Symfony\Component\HttpClient\Exception\TransportException;
use App\Exception\LMDVException;
use App\Entity\GIRResaWeb\Etape3;
use App\Service\Process\GIRDirectAgenceProcess;
use App\Service\Process\GIRDirectMailProcess;
use App\Service\Process\GIRDistribProcess;
use App\Service\Process\GIRResaWebProcess;
use App\Service\Process\GRPFactIndivAgenceProcess;
use App\Service\Process\GRPFactIndivMailProcess;
use App\Service\Process\GRPFactIndivWebProcess;
/**
* Service class that handles all webservice operations againts SalesForce.
* It implements a cache (per session) that allows to call "getInfo()/getQA" multiple
* times to re-read partial JSON trees multiple times without the need to store the whole response
* in the $_SESSION and without making multiples network requests.
*
* @author jra
*
*/
class SalesForceConnector implements SalesForceConnectorInterface {
protected const ERROR_TYPE_SAPEIG = 'SAPEIG';
protected const ERROR_TYPE_NETWORK = 'NETWORK';
protected const ERROR_TYPE_SALESFORCE = 'SALESFORCE';
protected const EXPIRETIME_SF = 60; /* 1 minute shared between sessions */
protected const EXPIRETIME_APEX = 600; /* 10 minutes not shared between session */
protected const CACHE_BETA_EXPIRATION = 0;
protected const HTTPCLIENT_TIMEOUT = 60; // APEX WS give timeout at 30 secs normally
public const CACHE_TAG_GETINFO = 'getInfo';
public const CACHE_TAG_GETQA = 'getQA';
public const CACHE_TAG_CREATEBOOKING = 'createBooking';
public const CACHE_TAG_GETPDF = 'getPDF';
public const CACHE_TAG_GETCODEAGENT = 'getCodeAgent';
public const CACHE_TAG_GETSALESFORCEDATA = 'getSalesForceData';
public const CACHE_TAG_GETSALESFORCEPARTICIPANTS = 'getSalesForceParticipants';
public const CACHE_TAG_GETSALESFORCERESPONSABLE = 'getSalesForceResponsable';
public const CACHE_TAG_GETSALESFORCETITLEVOYAGE = 'getSalesForceTitleVoyage';
/**
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* @var \Symfony\Contracts\HttpClient\HttpClientInterface
*/
protected $client;
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var \Symfony\Contracts\Cache\TagAwareCacheInterface
*/
protected $lmdvCache;
protected $config;
/**
* @var \Symfony\Contracts\Translation\TranslatorInterface
*/
protected $translator;
/**
* @var \App\Service\SalesForceAuthenticatorInterface
*/
protected $sf_auth;
/**
* @var \App\Service\PrintLMDVInterface
*/
protected $print;
/**
* Constructor with dependency injection.
*
* @param RequestStack $request_stack
* @param HttpClientInterface $client
* @param LoggerInterface $logger
* @param array $config
*/
public function __construct(RequestStack $request_stack, HttpClientInterface $client, LoggerInterface $logger, array $config, SalesForceAuthenticatorInterface $sf_auth, TagAwareCacheInterface $lmdvCache, TranslatorInterface $translator, PrintLMDVInterface $print)
{
$this->request = $request_stack->getCurrentRequest();
$this->client = $client;
$this->logger = $logger;
$this->sf_auth = $sf_auth;
// Cache helper
$this->lmdvCache = $lmdvCache;
$this->config = $config;
$this->translator = $translator;
$this->print = $print;
}
/**
* Gets the data needed to call getInfo() from SF with an API call.
*
* @param string $idOppSF
* This opportunity can be FILLE or PARENT.
* @param bool $noValidation
* if TRUE, the WS result will be returned as is, without verifying any data. By default we check the returned values.
* @throws WebServiceException
* @return array
*/
public function getSalesForceData($idOppSF, $noValidation = FALSE) {
if($_ENV['WEBSERVICE_FAKE_DATA']) {
// This is an example of an opp. PARENT
$result = [
'idOppSFParent' => '',
'businessCodeParent' => '',
'businessCode' => '000001',
'agentCode' => 'SAP',
'productCode' => 'CDPE1',
'departureDate' => '2020-11-20',
'endDate' => '2020-12-04',
'departureCity' => 'PAR',
'adults' => 2,
'children' => 0,
'stageName' => 'Accord client'
];
// With this parameter we simulate an opp FILLE
if($this->request->query->get('opportunity_type') == 'fille') {
$result['idOppSFParent'] = "0063O000004uB7QQAU";
$result['businessCodeParent'] = '300382';
}
return $result;
}
$cacheKey = [
'method' => 'getSalesForceData',
'idOppSF' => $idOppSF,
'noValidation' => $noValidation,
];
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($idOppSF) {
$item->expiresAfter(self::EXPIRETIME_SF);
$item->tag(self::CACHE_TAG_GETSALESFORCEDATA);
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/data/v48.0/sobjects/Opportunity/' . $idOppSF;
$fields = [];
$fields[] = 'Opportunit_Parent__c'; // id opp PARENT
$fields[] = 'Opportunit_Parent__r.Account.Id_Sapeig__c'; // businessCode PARENT
$fields[] = 'Opportunit_Parent__r.ID_Current_Watabi__c'; // PDF reference field PARENT
$fields[] = 'ID_Current_Watabi__c'; // PDF reference field FILLE
$fields[] = 'Account.ID_Sapeig__c'; // businessCode FILLE
$fields[] = 'Owner.Code_Vendeur__c'; // agentCode
$fields[] = 'Product__r.ProductCode'; // productCode
$fields[] = 'Departure_date__c'; // departureDate
$fields[] = 'Arrival_date__c'; // endDate
$fields[] = 'Lead_departure_city__c'; // departureCity
$fields[] = 'Number_of_adults_lead__c'; // adults
$fields[] = 'Number_of_kids_lead__c'; // children
$fields[] = 'Opportunit_Parent__r.StageName'; // statut de l'opp PARENT
$fields[] = 'StageName'; // statut de l'opp FILLE
$fields[] = 'RecordType.DeveloperName';
$fields[] = 'Product_code_lead__c';
$url .= '?fields=' . implode(',', $fields);
try {
$response = $this->client->request('GET', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['statusCode'] = $response->getStatusCode();
$content['response'] = $response->toArray(FALSE);
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getSalesForceData', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => '']);
throw new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getSalesForceData', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = '';
return $content;
}, self::CACHE_BETA_EXPIRATION);
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
if($cachedContent['statusCode'] != 200) {
$this->logger->error("LMDV SF Api Error $cached getSalesForceData", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => print_r($cachedContent['response'], TRUE)]), LMDVException::ERROR_SALESFORCE);
}
if(! isset($cachedContent['response']['Id']) || $cachedContent['response']['Id'] === FALSE) {
$this->logger->error("LMDV SF Api Error $cached getSalesForceData", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "There s no Id key"]), LMDVException::ERROR_SALESFORCE);
}
$this->logger->info("LMDV SF Api Success $cached", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
///////////////////////////////////////////////////////////////////
// Here we build the return array with the needed information
///////////////////////////////////////////////////////////////////
$r = $cachedContent['response'];
$result = [
'idOppSFParent' => $r['Opportunit_Parent__c'],
'businessCodeParent' => isset($r['Opportunit_Parent__r']) ? $r['Opportunit_Parent__r']['Account']['ID_Sapeig__c'] : NULL,
'businessCode' => $r['Account']['ID_Sapeig__c'],
'reference_pdf' => isset($r['Opportunit_Parent__r']) ? $r['Opportunit_Parent__r']['ID_Current_Watabi__c'] : $r['ID_Current_Watabi__c'],
'agentCode' => $r['Owner']['Code_Vendeur__c'],
'productCode' => $r['Product__r']['ProductCode'] ? $r['Product__r']['ProductCode'] : ($r['Product_code_lead__c'] ? $r['Product_code_lead__c'] : $this->request->getSession()->get('productCode')), // <- this default is needed by GRPFactIndivWeb
'departureDate' => $r['Departure_date__c'] ? $r['Departure_date__c'] : $this->request->getSession()->get('departureDate'), // <- this default is needed by GRPFactIndivWeb
'endDate' => $r['Arrival_date__c'],
//'departureCity' => $r['Lead_departure_city__c'] ? $r['Lead_departure_city__c'] : 'PAR',
'departureCity' => 'PAR',
'adults' => $r['Number_of_adults_lead__c'] ? intval($r['Number_of_adults_lead__c']) : '1',
'children' => $r['Number_of_kids_lead__c'] ? intval($r['Number_of_kids_lead__c']) : '0',
'type' => $r['RecordType']['DeveloperName'],
'stageName' => isset($r['Opportunit_Parent__r']) ? $r['Opportunit_Parent__r']['StageName'] : $r['StageName'],
'stageNameFille' => $r['StageName'],
];
if($noValidation) {
//$this->lmdvCache->delete($cacheKey);
return $result;
}
// NOTE : agentCode is not required for the tunnel to work proprely. businessCode is optional too, it will be set as "000001" by Gauthier if not present here.
$is_empty = empty($result['productCode']) || empty($result['departureDate']) || empty($result['endDate']);
if($is_empty) {
$this->logger->error("LMDV SF Api Error $cached getSalesForceData", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
if(empty($result['productCode'])) {
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "The productCode is empty."]), LMDVException::ERROR_SALESFORCE);
}
elseif(empty($result['departureDate'])) {
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "The departureDate is empty."]), LMDVException::ERROR_SALESFORCE);
}
elseif(empty($result['endDate'])) {
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "The endDate is empty."]), LMDVException::ERROR_SALESFORCE);
}
else {
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "Missing required information :" . print_r($result, TRUE)]), LMDVException::ERROR_SALESFORCE);
}
}
return $result;
}
/**
* Gets the travellers from SF with an API call
*
* @param string $idOppSF
* @throws WebServiceException
* @return array
*/
public function getSalesForceParticipants($idOppSF) {
if($_ENV['WEBSERVICE_FAKE_DATA']) {
$result = [];
$p = [
'civilite' => 'Monsieur',
'prenom' => 'Juan Fco',
'nom' => 'Rodriguez Hervella',
'nomJeuneFille' => '',
'dateNaissance' => '10/03/1976',
'nationalite' => 'Espagnole',
'sfId' => 'TODO'
];
$result[] = $p;
$result[] = $p;
return $result;
}
$cacheKey = [
'method' => 'getSalesForceParticipants',
'idOppSF' => $idOppSF,
];
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($idOppSF) {
$item->expiresAfter(self::EXPIRETIME_SF);
$item->tag(self::CACHE_TAG_GETSALESFORCEPARTICIPANTS);
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/data/v48.0/sobjects/Opportunity/' . $idOppSF . '/PAX__r';
$fields = [];
$fields[] = 'Id';
$fields[] = 'Compte__r.Salutation';
$fields[] = 'Compte__r.Firstname';
$fields[] = 'Compte__r.Lastname';
$fields[] = 'Compte__r.Maiden_name__pc';
$fields[] = 'Compte__r.Nationality__pc';
$fields[] = 'Compte__r.PersonBirthdate';
$url .= '?fields=' . implode(',', $fields);
try {
$response = $this->client->request('GET', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['statusCode'] = $response->getStatusCode();
$content['response'] = $response->toArray(FALSE);
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getSalesForceParticipants', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => '']);
throw new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getSalesForceParticipants', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = '';
return $content;
}, self::CACHE_BETA_EXPIRATION);
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
if($cachedContent['statusCode'] != 200) {
$this->logger->error("LMDV SF Api Error $cached getSalesForceParticipants", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' =>print_r($cachedContent['response'], TRUE)]), LMDVException::ERROR_SALESFORCE);
}
if(! isset($cachedContent['response']['done']) || $cachedContent['response']['done'] === FALSE) {
$this->logger->error("LMDV SF Api Error $cached getSalesForceParticipants", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "There is no done key"]), LMDVException::ERROR_SALESFORCE);
}
$this->logger->info("LMDV SF Api Success $cached getSalesForceParticipants", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
///////////////////////////////////////////////////////////////////
// Here we build the return array with the needed information
///////////////////////////////////////////////////////////////////
$r = $cachedContent['response'];
$results = [];
foreach($r['records'] as $record) {
$result = [
'civilite' => $this->getSalutation($record['Compte__r']['Salutation']),
'prenom' => $record['Compte__r']['FirstName'],
'nom' => $record['Compte__r']['LastName'],
'nomJeuneFille' => $record['Compte__r']['Maiden_name__pc'],
'dateNaissance' => $record['Compte__r']['PersonBirthdate'] ? date('d/m/Y', strtotime($record['Compte__r']['PersonBirthdate'])) : '',
'nationalite' => $record['Compte__r']['Nationality__pc'],
'sfId' => $record['Id']
];
$results[] = $result;
}
return $results;
}
protected function getSalutation($sf_value) {
$sf_value = \strtoupper($sf_value);
switch($sf_value) {
case 'MR':
case 'MR.':
$salutation = 'Monsieur';
break;
case 'MRS':
case 'MS':
case 'MS.':
$salutation = 'Madame';
break;
default:
$salutation = 'Monsieur';
break;
}
return $salutation;
}
/**
* Gets the client qui paye information from SalesForce using an API call.
*
* @param string $idOppSF
* @throws WebServiceException
* @return array
*/
public function getSalesForceResponsable($idOppSF) {
if($_ENV['WEBSERVICE_FAKE_DATA']) {
$result = [
'civilite' => 'Monsieur',
'prenom' => 'Juan Fco',
'nom' => 'Rodriguez Hervella',
'telephone' => '0669799808',
'telephone2' => '0671785250',
'adresse' => '34, Rue Defrance',
'codePostal' => '94300',
'ville' => 'Vincennes',
'pays' => 'France',
'email' => 'juan@tuxe.es',
'sfId' => 'TODO'
];
return $result;
}
$cacheKey = [
'method' => 'getSalesForceResponsable',
'idOppSF' => $idOppSF,
];
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($idOppSF) {
$item->expiresAfter(self::EXPIRETIME_SF);
$item->tag(self::CACHE_TAG_GETSALESFORCERESPONSABLE);
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/data/v48.0/sobjects/Opportunity/' . $idOppSF;
$fields = [];
$fields[] = 'Mes_commentaires_sur_le_voyage__c';
$fields[] = 'Account.Id';
$fields[] = 'Account.FirstName';
$fields[] = 'Account.LastName';
$fields[] = 'Account.Salutation';
$fields[] = 'Account.Phone';
$fields[] = 'Account.T_l_phone_2__c';
$fields[] = 'Account.PersonEmail';
$fields[] = 'Account.PersonMailingCountryCode';
$fields[] = 'Account.PersonMailingPostalCode';
$fields[] = 'Account.PersonMailingStreet';
$fields[] = 'Account.PersonMailingCity';
$url .= '?fields=' . implode(',', $fields);
try {
$response = $this->client->request('GET', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['statusCode'] = $response->getStatusCode();
$content['response'] = $response->toArray(FALSE);
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getSalesForceResponsable', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => '']);
throw new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getSalesForceResponsable', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = '';
return $content;
}, self::CACHE_BETA_EXPIRATION);
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
if($cachedContent['statusCode'] != 200) {
$this->logger->error("LMDV SF Api Error $cached getSalesForceResponsable", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => print_r($cachedContent['response'], TRUE)]), LMDVException::ERROR_SALESFORCE);
}
if(! isset($cachedContent['response']['Id'])) {
$this->logger->error("LMDV SF Api Error $cached getSalesForceResponsable", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "There is no Id key"]), LMDVException::ERROR_SALESFORCE);
}
$this->logger->info("LMDV SF Api Success $cached getSalesForceResponsable", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
///////////////////////////////////////////////////////////////////
// Here we build the return array with the needed information
///////////////////////////////////////////////////////////////////
$r = $cachedContent['response'];
$result = [
'civilite' => $this->getSalutation($r['Account']['Salutation']),
'prenom' => $r['Account']['FirstName'],
'nom' => $r['Account']['LastName'],
'telephone' => $r['Account']['Phone'],
'telephone2' => $r['Account']['T_l_phone_2__c'],
'adresse' => $r['Account']['PersonMailingStreet'],
'codePostal' => $r['Account']['PersonMailingPostalCode'],
'ville' => $r['Account']['PersonMailingCity'],
'pays' => $r['Account']['PersonMailingCountryCode'],
'email' => $r['Account']['PersonEmail'],
'sfId' => $r['Account']['Id'],
'comments' => $this->cleanUpComments($r['Mes_commentaires_sur_le_voyage__c']),
];
// If the country is empty, assume 'FR'
if(empty($result['pays'])) {
$result['pays'] = 'FR';
}
else {
$result['pays'] = \strtoupper($result['pays']);
}
return $result;
}
/**
* Gets the title of the voyage from SalesForce using an API call. This is used only in etape0,
* the other steps will use the info retourned by getInfo()
*
* @param VoyageInterface $voyage
* @return string
* The title of the voyage
*/
public function getSalesForceTitleVoyage(VoyageInterface $voyage) {
/** @var \App\Service\Process\ProcessInterface $process */
$process = $voyage->getProcess();
$title = '';
try {
if($process instanceof GIRDirectAgenceProcess || $process instanceof GIRDirectMailProcess) {
// chercher l’info dans le nom de l’opportunité associée
$url = '/services/data/v48.0/sobjects/Opportunity/' . $voyage->getIdOppSF() . '?fields=Name';
$result = $this->_getSalesForceAPI($url);
$title = $result['Name'];
}
elseif($process instanceof GIRDistribProcess || $process instanceof GIRResaWebProcess) {
// chercher l’info dans le champ Familly_Name_LMDV__c de l’objet DEPART
$url = '/services/data/v48.0/query/';
$url .= "?q=SELECT+Familly_Name_LMDV__c+FROM+Product2+WHERE+ProductCode='".$voyage->getProductCode()."'";
$url .= "+AND+Date_de_Depart__c+=+".$voyage->getDepartureDate()."+AND+Date_De_Retour__c+=".$voyage->getEndDate();
$result = $this->_getSalesForceAPI($url);
$title = $result['records'][0]['Familly_Name_LMDV__c'];
}
elseif($process instanceof GRPFactIndivAgenceProcess || $process instanceof GRPFactIndivMailProcess || $process instanceof GRPFactIndivWebProcess) {
// chercher l’info dans le nom de l’opportunité PARENT
$url = '/services/data/v48.0/sobjects/Opportunity/' . $voyage->getIdOppSFParent() . '?fields=Name';
$result = $this->_getSalesForceAPI($url);
$title = $result['Name'];
}
}
catch(\Exception $e) {
// Errors are ignored but saved to the log.
}
$this->request->getSession()->set('TWIG_title', $title);
}
/**
* Auxiliary function to get the Voyage PDF url from SalesForce
*
* @param VoyageInterface $voyage
* @return string
* The voyage's url
*/
protected function getUrlPdfVoyage(VoyageInterface $voyage) {
/** @var \App\Service\Process\ProcessInterface $process */
$process = $voyage->getProcess();
$url_pdf = '';
if($process instanceof GIRDirectAgenceProcess || $process instanceof GIRDirectMailProcess) {
// chercher l’info dans le nom de l’opportunité associée
$url = '/services/data/v48.0/sobjects/Opportunity/' . $voyage->getIdOppSF() . '?fields=Product__r.urlPDF__c';
$result = $this->_getSalesForceAPI($url);
$url_pdf = $result['Product__r']['urlPDF__c'];
}
elseif($process instanceof GIRDistribProcess || $process instanceof GIRResaWebProcess) {
// chercher l’info dans le champ Familly_Name_LMDV__c de l’objet DEPART
$url = '/services/data/v48.0/query/';
$url .= "?q=SELECT+urlPDF__c+FROM+Product2+WHERE+ProductCode='".$voyage->getProductCode()."'";
$url .= "+AND+Date_de_Depart__c+=+".$voyage->getDepartureDate()."+AND+Date_De_Retour__c+=".$voyage->getEndDate();
$result = $this->_getSalesForceAPI($url);
$url_pdf = @$result['records'][0]['urlPDF__c'];
}
return $url_pdf;
}
/**
* Auxiliary function that executes the SF Api call to recover the voyage's title
*
* @param string $idOppSF
* @throws WebServiceException
* @return array
*/
protected function _getSalesForceAPI($url) {
$cacheKey = [
'method' => 'getSalesForceTitleVoyage',
'url' => $url,
];
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($url) {
$item->expiresAfter(self::EXPIRETIME_SF);
$item->tag(self::CACHE_TAG_GETSALESFORCETITLEVOYAGE);
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . $url;
try {
$response = $this->client->request('GET', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['statusCode'] = $response->getStatusCode();
$content['response'] = $response->toArray(FALSE);
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getSalesForceTitleVoyage', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => '']);
throw new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getSalesForceTitleVoyage', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = '';
return $content;
}, self::CACHE_BETA_EXPIRATION);
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
if($cachedContent['statusCode'] != 200) {
$this->logger->error("LMDV SF Api Error $cached getSalesForceTitleVoyage", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => print_r($cachedContent['response'], TRUE)]), LMDVException::ERROR_SALESFORCE);
}
$this->logger->info("LMDV SF Api Success $cached getSalesForceTitleVoyage", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
///////////////////////////////////////////////////////////////////
// Here we build the return array with the needed information
///////////////////////////////////////////////////////////////////
$result = $cachedContent['response'];
return $result;
}
public function getPdfVoyage(VoyageInterface $voyage) {
$result = [];
$process = $voyage->getProcess();
if($process instanceof GIRDirectAgenceProcess ||
$process instanceof GIRDirectMailProcess ||
$process instanceof GIRDistribProcess ||
$process instanceof GIRResaWebProcess) {
$result['type'] = 'url';
$result['content'] = $this->getUrlPdfVoyage($voyage);
}
elseif($process instanceof GRPFactIndivAgenceProcess ||
$process instanceof GRPFactIndivMailProcess ||
$process instanceof GRPFactIndivWebProcess) {
if(! $voyage->getIdOppSFParent()) {
throw new \Exception('_getPdfVoyage called with empty IdOppParent');
}
$result['type'] = 'content';
$result['content'] = $this->_getPdfVoyage($process, $voyage->getIdOppSFParent());
}
return $result;
}
/**
* WS APEX getPdf : this WS is cached by user for saving SF API calls and to improve app performace.
*
* This is used for GRP tunnels in order to download the travel PDF brochure.
*
* @param string $idOppSF
* @throws WebServiceException
* @return string $pdfContent
* The binary PDF content.
*/
protected function _getPdfVoyage(ProcessInterface $process, string $idOppSF) {
if($_ENV['WEBSERVICE_FAKE_DATA']) {
$data = \file_get_contents( __DIR__ . '/tests/grp_travel_brochure.pdf');
return $data;
}
$cacheKey = [
'method' => 'getPdf',
'idOppSF' => $idOppSF,
'sessionId' => $this->request->getSession()->getId()
];
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($process, $idOppSF) {
$item->expiresAfter(self::EXPIRETIME_APEX);
$item->tag(self::CACHE_TAG_GETPDF);
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/apexrest/getPdf/?id=' . $idOppSF . '&process=' . $process->display();
try {
$response = $this->client->request('GET', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['statusCode'] = $response->getStatusCode();
$content['response'] = $response->getContent();
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getPdf', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => '']);
throw new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV Webservice Error (not cached) getPdf', ['debug' => $httpLogs, 'url' => $url, 'statusCode' => $response->getStatusCode()]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = '';
return $content;
}, self::CACHE_BETA_EXPIRATION);
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
$this->logger->info("LMDV Webservice Success $cached getPdf", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'statusCode' => $cachedContent['statusCode']]);
return $cachedContent['response'];
}
/**
* WS APEX retrieveAgentCode : this WS is cached by user for saving SF API calls and to improve app performace.
*
* @param VoyageInterface $voyage
* @throws WebServiceException
* @return array
*/
public function getCodeAgent(VoyageInterface $voyage) {
if($_ENV['WEBSERVICE_FAKE_DATA']) {
$data = [
'agentCode' => 'MAR'
];
return $data;
}
$body = [];
$body['dto'] = [
'requesterCode' => $voyage->getEtape0()->getCodeSociete(),
'firstName' => $voyage->getEtape0()->getPrenomVendeur(),
'lastName' => $voyage->getEtape0()->getNomVendeur(),
'phone' => $voyage->getEtape0()->getTelVendeur(),
'email' => $voyage->getEtape0()->getMailVendeur(),
'process' => $voyage->getProcess()->display()
];
$cacheKey = $body + [
'method' => 'retrieveAgentCode',
'sessionId' => $voyage->getSessionId()
];
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($body) {
$item->expiresAfter(self::EXPIRETIME_APEX);
$item->tag(self::CACHE_TAG_GETCODEAGENT);
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/apexrest/retrieveAgentCode/';
try {
$response = $this->client->request('POST', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
'json' => $body
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['response'] = $response->toArray();
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) retrieveAgentCode', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => '']);
throw new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV Webservice Error (not cached) retrieveAgentCode', ['debug' => $httpLogs, 'url' => $url, 'request' => $body, 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = $body;
return $content;
}, self::CACHE_BETA_EXPIRATION);
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
if(empty($cachedContent['response'])) {
$this->logger->info("LMDV Webservice Error $cached retrieveAgentCode", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => 'agentCode is empty']), LMDVException::ERROR_SALESFORCE);
}
elseif(! isset($cachedContent['response']['Status'])) {
$this->logger->error("LMDV Webservice Error $cached retrieveAgentCode", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' =>"There is no status key"]), LMDVException::ERROR_SALESFORCE);
}
elseif($cachedContent['response']['Status'] == 'Error') {
$this->logger->error("LMDV Webservice Error $cached retrieveAgentCode", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $cachedContent['response']['message']]), LMDVException::ERROR_SALESFORCE);
}
$this->logger->info("LMDV Webservice Success $cached retrieveAgentCode", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
return $cachedContent['response'];
}
/**
* WS APEX getInfo : this WS is cached by user for saving SF API calls and to improve app performace.
*
* Note that SAPEIG error codes are returned through http 500 error messages
*
* @param VoyageInterface $voyage
* @throws WebServiceException
* @return array
*/
public function getInfo(VoyageInterface $voyage) {
// If parameters are not valid, we skip making a call to the WS APEX, to avoid flooding the SF administrator
// with error emails if we know beforehand that the call will not be successfull. Testing for the productCode
// should be enough
if(! $voyage->getProductCode()) {
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => 'Session is empty or not valid, skipping call.']), LMDVException::ERROR_UNKNOWN);
}
$body = [];
$body['dto'] = [
'productCode' => $voyage->getProductCode(),
'departureDate' => $voyage->getDepartureDate(),
'returnDate' => $voyage->getEndDate(),
'departureCity' => $voyage->getDepartureCity(),
'adultNumber' => $voyage->getEtape0()->getNbrAdultes(),
'childNumber' => $voyage->getEtape0()->getNbrEnfants(),
'infantNumber' => "0",
'process' => $voyage->getProcess()->display()
];
if($voyage->getIdOppSF()) {
$body['dto']['opportunityId'] = $voyage->getIdOppSF();
}
if($voyage->getIdOppSFParent()) {
$body['dto']['parentOpportunityId'] = $voyage->getIdOppSFParent();
}
if($voyage->getEtape0()->getCodeAgent()) {
$body['dto']['agentCode'] = $voyage->getEtape0()->getCodeAgent();
}
if($voyage->getEtape0()->getCodeSociete()) {
$body['dto']['requesterCode'] = $voyage->getEtape0()->getCodeSociete();
}
$cacheKey = $body + [
'method' => 'getInfo',
'sessionId' => $voyage->getSessionId()
];
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
static $static_cache = NULL;
$hasStatic = FALSE;
if(! $static_cache) {
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($body, $voyage) {
$item->expiresAfter(self::EXPIRETIME_APEX);
$item->tag(self::CACHE_TAG_GETINFO);
if($_ENV['WEBSERVICE_FAKE_DATA']) {
$code = $voyage->getProductCode();
$data = \file_get_contents( __DIR__ . '/tests/'.$code.'/'.$code.'_getInfo_response.txt');
$data = \json_decode($data, TRUE);
$content = [];
$content['url'] = 'https://lmdv--partial-FAKE.my.salesforce.com' . '/services/apexrest/getInfo/';
$content['timestamp'] = time();
$content['response'] = $data;
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = $body;
return $content;
}
static $static_cache_errors = NULL;
if($static_cache_errors) {
throw $static_cache_errors;
}
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/apexrest/getInfo/';
try {
$response = $this->client->request('POST', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
'json' => $body
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['response'] = $response->toArray(FALSE); // httpClient returns an Exception for http error codes 300-599 unless we passe FALSE here.
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getInfo', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => '']);
$static_cache_errors = new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
throw $static_cache_errors;
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV Webservice Error (not cached) getInfo', ['debug' => $httpLogs, 'url' => $url, 'request' => $body, 'response' => $content['response']]);
$static_cache_errors = new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
throw $static_cache_errors;
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = $body;
return $content;
}, self::CACHE_BETA_EXPIRATION);
$static_cache = $cachedContent;
}
else {
$cachedContent = $static_cache;
$hasStatic = TRUE;
}
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
if(! isset($cachedContent['response']['Status'])) {
if(! $hasStatic) {
$this->logger->error("LMDV Webservice Error $cached getInfo", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
}
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' =>"There is no status key"]), LMDVException::ERROR_SALESFORCE);
}
elseif($cachedContent['response']['Status'] == 'Error') {
if(! $hasStatic) {
$this->logger->error("LMDV Webservice Error $cached getInfo", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
}
//$this->lmdvCache->delete($cacheKey);
if(strstr($cachedContent['response']['message'], 'Stock insuffisant') && $cachedContent['response']['errorType'] == 'SAPEIG') {
$e = new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $cachedContent['response']['message']]), LMDVException::ERROR_OUT_OF_STOCK);
$e->setData(['telephone' => $cachedContent['response']['supportPhone']]);
throw $e;
}
else {
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $cachedContent['response']['message']]), $this->getErrorType($cachedContent['response']['errorType']));
}
}
if(! $hasStatic) {
$this->logger->info("LMDV Webservice Success $cached getInfo", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
}
// Adds session variables that are mapped into the Voyage object by SessionManager
if($cachedContent['response']['departure']['city']['text']) {
$this->request->getSession()->set('GETINFO_departureCity', $cachedContent['response']['departure']['city']['text']);
}
if($cachedContent['response']['destination']['country']['text']) {
$this->request->getSession()->set('GETINFO_country', $cachedContent['response']['destination']['country']['text']);
}
if($cachedContent['response']['title']) {
$this->request->getSession()->set('GETINFO_title', $cachedContent['response']['title']);
}
if($cachedContent['response']['supportPhone']) {
$this->request->getSession()->set('GETINFO_supportPhone', $cachedContent['response']['supportPhone']);
}
return $cachedContent['response'];
}
/**
* WS APEX getQA : this WS is cached by user for saving SF API calls and to improve app performace.
*
* Note that SAPEIG error codes are returned through http 500 error messages
*
* @param VoyageInterface $voyage
* @throws WebServiceException
* @return mixed
*/
public function getQA(VoyageInterface $voyage) {
// If parameters are not valid, we skip making a call to the WS APEX, to avoid flooding the SF administrator
// with error emails if we know beforehand that the call will not be successfull. Testing for the productCode
// should be enough
if(! $voyage->getProductCode()) {
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => 'Session is empty or not valid, skipping call.']), LMDVException::ERROR_UNKNOWN);
}
$body = [];
$body['dto'] = [
'sessionCode' => $this->getSessionCodeGetInfo($voyage),
'productCode' => $voyage->getProductCode(),
'airSegmentsCodes' => $this->getAirSegmentsCodes($voyage),
'staySegments' => $this->getStaySegments($voyage),
'miscellaneousSegments' => $this->getMiscellaneousSegments($voyage),
//'insuranceSegments' => $this->getInsurancesSegments($voyage),
'departureDate' => $voyage->getDepartureDate(),
'endDate' => $voyage->getEndDate(),
'adultNumber' => $voyage->getEtape0()->getNbrAdultes(),
'childNumber' => $voyage->getEtape0()->getNbrEnfants(),
'infantNumber' => "0",
'departureCity' => $voyage->getDepartureCity(),
'process' => $voyage->getProcess()->display()
];
if($voyage->getEtape0()->getCodeAgent()) {
$body['dto']['agentCode'] = $voyage->getEtape0()->getCodeAgent();
}
if($voyage->getEtape0()->getCodeSociete()) {
$body['dto']['requesterCode'] = $voyage->getEtape0()->getCodeSociete();
}
if($voyage->getIdOppSF()) {
$body['dto']['opportunityId'] = $voyage->getIdOppSF();
}
if($voyage->getIdOppSFParent()) {
$body['dto']['parentOpportunityId'] = $voyage->getIdOppSFParent();
}
$cacheKey = $body + [
'method' => 'getQA',
'sessionId' => $voyage->getSessionId()
];
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
static $static_cache = NULL;
$hasStatic = FALSE;
if(! $static_cache) {
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($body, $voyage) {
$item->expiresAfter(self::EXPIRETIME_APEX);
$item->tag(self::CACHE_TAG_GETQA);
if($_ENV['WEBSERVICE_FAKE_DATA']) {
$code = $voyage->getProductCode();
$data = \file_get_contents( __DIR__ . '/tests/'.$code.'/'.$code.'_getQA_response.txt');
$data = \json_decode($data, TRUE);
$content = [];
$content['url'] = 'https://lmdv--partial-FAKE.my.salesforce.com' . '/services/apexrest/getQA/';
$content['timestamp'] = time();
$content['response'] = $data;
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = $body;
return $content;
}
static $static_cache_errors = NULL;
if($static_cache_errors) {
throw $static_cache_errors;
}
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/apexrest/getQA/';
try {
$response = $this->client->request('POST', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
'json' => $body
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['response'] = $response->toArray(FALSE); // httpClient returns an Exception for http error codes 300-599 unless we passe FALSE here.
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV SF Api Error (not cached) getQA', ['debug' => $httpLogs, 'url' => $url, 'request' => '', 'response' => '']);
$static_cache_errors = new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
throw $static_cache_errors;
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV Webservice Error (not cached) getQA', ['debug' => $httpLogs, 'url' => $url, 'request' => $body, 'response' => $content['response']]);
$static_cache_errors = new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
throw $static_cache_errors;
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = $body;
return $content;
}, self::CACHE_BETA_EXPIRATION);
$static_cache = $cachedContent;
}
else {
$cachedContent = $static_cache;
$hasStatic = TRUE;
}
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
if(! isset($cachedContent['response']['Status'])) {
if(! $hasStatic) {
$this->logger->error("LMDV Webservice Error $cached getQA", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
}
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "There is no status key"]), LMDVException::ERROR_SALESFORCE);
}
elseif($cachedContent['response']['Status'] == 'Error') {
if(! $hasStatic) {
$this->logger->error("LMDV Webservice Error $cached getQA", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
}
//$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $cachedContent['response']['message']]), $this->getErrorType($cachedContent['response']['errorType']));
}
if(! $hasStatic) {
$this->logger->info("LMDV Webservice Success $cached getQA", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
}
return $cachedContent['response'];
}
/**
* WS APEX createBooking : we use a cache for this webservice to avoid the user calling multiple times createBooking,
* which can happen if the user submits the form multiple times using CTRL+R (while waiting for a response), or when
* the client goes to the HiPay page and hits the browser back button without doing any paiement.
*
* Note that SAPEIG error codes are returned through http 500 error messages
*
* @param VoyageInterface $voyage
* @throws WebServiceException
* @return array
*/
public function createBooking(VoyageInterface $voyage) {
$body = [];
$body['dto'] = [
'productCode' => $voyage->getProductCode(),
'sessionCode' => $this->getSessionCodeGetQA($voyage),
'airSegmentsCodes' => $this->getAirSegmentsCodes($voyage),
'staySegments' => $this->getStaySegments($voyage),
'miscellaneousSegments' => $this->getMiscellaneousSegments($voyage),
//'insuranceSegments' => $this->getInsurancesSegments($voyage),
"travellers" => [], // <- will be filled up below
'departureDate' => $voyage->getDepartureDate(),
'departureCity' => $voyage->getDepartureCity(),
'endDate' => $voyage->getEndDate(),
'process' => $voyage->getProcess()->display(),
'paymentType' => $voyage->getEtape3()->getTypePayment() == Etape3::PAYMENT_TYPE_ONLINE ? 'Paiement en ligne' : 'Paiement en agence'
];
// Sends PDF contrat de vent to SF encoded in base64
$pdfContent = '';
try {
$pdfContent = $this->print->print($voyage);
$pdfContent = \base64_encode($pdfContent);
}
catch(\Exception $e) {
}
if($pdfContent) {
$body['dto']['pdfContent'] = $pdfContent;
}
if($voyage->getEtape2()->getCivilite()) {
$body['dto']['customer'] = [
'title' => $voyage->getEtape2()->getCivilite() == 'Monsieur' ? 'MR' : 'MRS',
'firstName' => $voyage->getEtape2()->getPrenom(),
'lastName' => $voyage->getEtape2()->getNom(),
'street' => $voyage->getEtape2()->getAdresse(),
'zipCode' => $voyage->getEtape2()->getCodePostal(),
'city' => $voyage->getEtape2()->getVille(),
'country' => $voyage->getEtape2()->getPays(),
'phone' => $voyage->getEtape2()->getTelephone(),
'secondPhone' => $voyage->getEtape2()->getTelephone2(),
'email' => $voyage->getEtape2()->getEmail(),
];
}
// Ticket LMDV-507 : Envoyer les valeurs UTM et Referrer dans l'opportunité
$body['dto']['utm'] = [
'source' => $this->request->cookies->get('_uc_utm_source') ?? '',
'medium' => $this->request->cookies->get('_uc_utm_medium') ?? '',
'campaign' => $this->request->cookies->get('_uc_utm_campaign') ?? '',
'content' => $this->request->cookies->get('_uc_utm_content') ?? '',
'term' => $this->request->cookies->get('_uc_utm_term') ?? '',
'referer' => $this->request->cookies->get('_uc_referrer') ?? '',
];
if($voyage->getEtape2()->getCommentaires()) {
$body['dto']['comment'] = $voyage->getEtape2()->getCommentaires();
}
// Par rapport aux Ids, s'ils sont renseignés, on est dans un cas de mise à jour et sinon c'est une création dans Salesforce
if($voyage->getIdOppSF()) {
$body['dto']['opportunityId'] = $voyage->getIdOppSF();
}
if($voyage->getIdOppSF() && $voyage->getEtape2()->getSfId()) {
$body['dto']['customer']['sfId'] = $voyage->getEtape2()->getSfId();
}
if($voyage->getEtape0()->getCodeSociete()) {
$body['dto']['requesterCode'] = $voyage->getEtape0()->getCodeSociete();
}
if($voyage->getEtape0()->getCodeAgent()) {
$body['dto']['agentCode'] = $voyage->getEtape0()->getCodeAgent();
}
// This is for GRPFactIndivWeb, we must link the new SFP opp with its PARENT.
if($voyage->getIdOppSFParent() && ! $voyage->getIdOppSF()) {
$body['dto']['parentOpportunityId'] = $voyage->getIdOppSFParent();
}
// This is for GIRDistrib, we have to link the agentCode previously created with the new SF opp.
if($voyage->getEtape0()->getCodeAgent() && $voyage->getEtape0()->getMailVendeur()) {
$body['dto']['agentEmail'] = $voyage->getEtape0()->getMailVendeur();
}
// Fills up travellers information
foreach($voyage->getEtape2()->getParticipants() as $participant) {
if($participant->isChild()) {
$type = 'Child';
}
else {
$type = 'Adult';
}
// Some forms use a CountryType others just a textfield
$pays = $participant->getNationalite();
try {
$pays = \Symfony\Component\Intl\Countries::getName($participant->getNationalite());
}
catch(\Exception $e) {
$this->logger->error('Country name not found for country code "' . $participant->getNationalite() . '"');
}
$aux = [
'title' => $participant->getCivilite() == 'Monsieur' ? 'MR' : 'MRS',
'firstName' => $participant->getPrenom(),
'lastName' => $participant->getNom(),
'type' => $type,
'birthDate' => date('Y-m-d', $this->getTimestampFromDate($participant->getDateNaissance())),
'Nationality' => $pays,
];
// Il est possible aussi de renseigner un Id Salesforce de Pax__c si tu veux mettre à jour un voyageur dans Salesforce
if($voyage->getIdOppSF() && $participant->getSfId()) {
$aux['sfId'] = $participant->getSfId();
}
if($participant->getCivilite() == 'Madame') {
$aux['maidenName'] = $participant->getNomJeuneFille();
}
$body['dto']['travellers'][] = $aux;
}
$cacheKey = $body + [
'method' => 'createBooking',
'sessionId' => $voyage->getSessionId()
];
// We do not take into account these two keys for computing the key.
unset($cacheKey['dto']['paymentType']);
unset($cacheKey['dto']['pdfContent']);
$cacheKey = md5(json_encode($cacheKey));
$timestamp = time();
$cachedContent = $this->lmdvCache->get($cacheKey, function (ItemInterface $item) use ($body, $voyage) {
$item->expiresAfter(self::EXPIRETIME_APEX);
$item->tag(self::CACHE_TAG_CREATEBOOKING);
if($_ENV['WEBSERVICE_FAKE_DATA']) {
$code = $voyage->getProductCode();
$data = \file_get_contents( __DIR__ . '/tests/'.$code.'/'.$code.'_createBooking_response.txt');
$data = \json_decode($data, TRUE);
// Because createBooking is extremely slow, we simulate the same behaviour with a PHP sleep.
sleep(30);
$content = [];
$content['url'] = 'https://lmdv--partial-FAKE.my.salesforce.com' . '/services/apexrest/createBooking/';
$content['timestamp'] = time();
$content['response'] = $data;
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = $body;
return $content;
}
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/apexrest/createBooking/';
try {
$response = $this->client->request('POST', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
'json' => $body
]);
$content = [];
$content['url'] = $url;
$content['timestamp'] = time();
$content['response'] = $response->toArray(FALSE); // httpClient returns an Exception for http error codes 300-599 unless we passe FALSE here.
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV Webservice Error createBooking', ['debug' => $httpLogs, 'url' => $url, 'request' => $body, 'response' => '']);
throw new WebServiceException($this->translator->trans('error.page.label_timeout_createBooking'), LMDVException::ERROR_TIMEOUT_CREATEBOOKING);
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV Webservice Error createBooking', ['debug' => $httpLogs, 'url' => $url, 'request' => $body, 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE_CREATEBOOKING);
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = $body;
return $content;
}, self::CACHE_BETA_EXPIRATION);
$cached = '(not cached)';
if($cachedContent['timestamp'] < $timestamp) {
$cached = '(cached)';
}
if(! isset($cachedContent['response']['Status'])) {
$this->logger->error("LMDV Webservice Error $cached createBooking", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "There is no status key"]), LMDVException::ERROR_SALESFORCE_CREATEBOOKING);
}
elseif($cachedContent['response']['Status'] == 'Error') {
$this->logger->error("LMDV Webservice Error $cached createBooking", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
$this->lmdvCache->delete($cacheKey);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $cachedContent['response']['message']]), $this->getErrorTypeCreateBooking($cachedContent['response']['errorType']));
}
// Removes pdfContent from the log INFO to avoid pollution
unset($cachedContent['httpClient']['request']['dto']['pdfContent']);
$this->logger->info("LMDV Webservice Success $cached createBooking", ['debug' => $cachedContent['httpClient']['debug'], 'url' => $cachedContent['url'], 'request' => $cachedContent['httpClient']['request'], 'response' => $cachedContent['response']]);
return $cachedContent['response'];
}
/**
* WS APEX addPayment
*
* Note that SAPEIG error codes are returned through http 500 error messages
*
* @param VoyageInterface $voyage
* @throws WebServiceException
* @return array
*/
public function addPayment(PaymentEventInterface $event) {
if($_ENV['WEBSERVICE_FAKE_DATA']) {
$result = [
'Status' => 'Success'
];
return $result;
}
$this->sf_auth->authenticate();
$url = $this->sf_auth->getInstanceUrl() . '/services/apexrest/AddPayment/';
$voyage = $event->getVoyage();
$transaction = $event->getTransaction();
$body = [];
$body['dto'] = [
"process" => $voyage->getProcess()->display(),
"status" => $event->getType() == HiPayManager::HIPAY_CALLBACK_ACCEPT ? 'OK' : 'REFUSED',
"reason" => $transaction['reason'],
"bookingNumber" => $voyage->getEtape3()->getBookingNumber(),
"opportunityId" => $voyage->getEtape3()->getNewIdOpp(),
"payment" => [
"paymentDate" => date('c', $transaction['date']),
"reference" => $transaction['reference'],
"amount" => $transaction['amount'] * 100, // Amount must be sent in centimes
"code" => 'CB',
"payer" => $voyage->getEtape2()->getNom() . ' ' . $voyage->getEtape2()->getPrenom(),
]
];
if($voyage->getEtape0()->getCodeSociete()) {
$body['dto']['requesterCode'] = $voyage->getEtape0()->getCodeSociete();
}
try {
$response = $this->client->request('POST', $url, [
'timeout' => self::HTTPCLIENT_TIMEOUT,
'headers' => [
'Authorization' => "Bearer " . $this->sf_auth->getAccessToken(),
],
'json' => $body
]);
$content = [];
$content['response'] = $response->toArray(FALSE); // httpClient returns an Exception for http error codes 300-599 unless we passe FALSE here.
}
catch(\Exception $e) {
if($e instanceof TransportException) {
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV Webservice Error AddPayment', ['debug' => $httpLogs, 'url' => $url, 'request' => $body, 'response' => '']);
throw new WebServiceException($this->translator->trans('error.page.label_timeout'), LMDVException::ERROR_TIMEOUT);
}
if($response->getStatusCode() == 401) {
$this->sf_auth->invalidateAccessToken();
}
$httpLogs = [/*$response->getInfo('debug')*/];
$content['response'] = $e->getMessage();
$this->logger->error('LMDV Webservice Error AddPayment', ['debug' => $httpLogs, 'url' => $url, 'request' => $body, 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $response->getStatusCode()]), LMDVException::ERROR_SALESFORCE);
}
$content['httpClient']['debug'] = [/*$response->getInfo('debug')*/];
$content['httpClient']['request'] = $body;
if(! isset($content['response']['Status'])) {
$this->logger->error('LMDV Webservice Error AddPayment', ['debug' => $content['httpClient']['debug'], 'url' => $url, 'request' => $content['httpClient']['request'], 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => "There is no status key"]), LMDVException::ERROR_SALESFORCE);
}
elseif($content['response']['Status'] == 'Error') {
$this->logger->error('LMDV Webservice Error AddPayment', ['debug' => $content['httpClient']['debug'], 'url' => $url, 'request' => $content['httpClient']['request'], 'response' => $content['response']]);
throw new WebServiceException($this->translator->trans("error.webservice.sf_api_error", ['message' => $content['response']['message']]), LMDVException::ERROR_SALESFORCE);
}
$this->logger->info('LMDV Webservice Success AddPayment', ['debug' => $content['httpClient']['debug'], 'url' => $url, 'request' => $content['httpClient']['request'], 'response' => $content['response']]);
return $content['response'];
}
/**
* This is not used at the moment but it shows the way to invalidate
* specific caches if necessary.
*
* @param array $tags
*/
public function invalidateCache(array $tags) {
$this->lmdvCache->invalidateTags($tags);
}
/**
* This is not used at the moment but it shows the way to invalidate all
* caches if necessary
*/
public function invalidateAllCaches() {
$tags = [
self::CACHE_TAG_GETINFO,
self::CACHE_TAG_GETQA,
self::CACHE_TAG_CREATEBOOKING,
self::CACHE_TAG_GETPDF,
self::CACHE_TAG_GETCODEAGENT,
self::CACHE_TAG_GETSALESFORCEDATA,
self::CACHE_TAG_GETSALESFORCEPARTICIPANTS,
self::CACHE_TAG_GETSALESFORCERESPONSABLE,
self::CACHE_TAG_GETSALESFORCETITLEVOYAGE
];
$this->lmdvCache->invalidateTags($tags);
}
/**
* Expects a date as "d/m/Y" and return its timestamp.
*
* @param string $date
* A date in the format "d/m/Y"
* @return int
* The timestamp
*/
protected function getTimestampFromDate($date) {
$date = str_replace('/', '-', $date);
return strtotime($date);
}
/**
* Gets a list of "stay segments" from getInfo(), which we must send later when calling
* getQA/createBooking. Note that "staySegments" must be grouped by "segmentCode", i.e if there are
* two rooms of the same type, instead of sending :
*
* "staySegments" => [â–¼
* [â–¼
* "segmentCode" => "3"
* "rooms" => [â–¼
* [â–¼
* "roomCode" => "IND"
* "adultNumber" => 1
* "childNumber" => 0
* "infantNumber" => "0"
* ]
* ]
* ]
* [â–¼
* "segmentCode" => "3"
* "rooms" => [â–¼
* [â–¼
* "roomCode" => "IND"
* "adultNumber" => 1
* "childNumber" => 0
* "infantNumber" => "0"
* ]
* ]
* ]
* ]
*
* we must send :
*
* "staySegments" => [â–¼
* [â–¼
* "segmentCode" => "3"
* "rooms" => [â–¼
* [â–¼
* "roomCode" => "IND"
* "adultNumber" => 1
* "childNumber" => 0
* "infantNumber" => "0"
* ],
* [â–¼
* "roomCode" => "IND"
* "adultNumber" => 1
* "childNumber" => 0
* "infantNumber" => "0"
* ]
* ]
* ]
* ]
*
* @todo: maybe use a ValueObject instead of an array.
*
* @param VoyageInterface $voyage
* @return array
*/
protected function getStaySegments(VoyageInterface $voyage) {
$staySegments = [];
foreach($voyage->getEtape1()->getChambres() as $chambre) {
$rooms = [
'roomCode' => $chambre->getCode(),
'adultNumber' => $chambre->getNbrAdultes(),
'childNumber' => $chambre->getNbrEnfants(),
'infantNumber' => "0",
];
$segmentCode = $chambre->getSegmentCode();
if(! isset($staySegments[$segmentCode])) {
$staySegments[$segmentCode] = ['segmentCode' => $segmentCode];
}
$staySegments[$segmentCode]['rooms'][] = $rooms;
}
return array_values($staySegments);
}
/**
* The sessionCode of getInfo() must be used for calls to getQA()
*/
protected function getSessionCodeGetInfo(VoyageInterface $voyage) {
$data = $this->getInfo($voyage);
return $data['control']['Session'];
}
/**
* The sessionCode of getQA() must be used for calls to createBooking()
*/
protected function getSessionCodeGetQA(VoyageInterface $voyage) {
$data = $this->getQA($voyage);
return $data['control']['Session'];
}
/**
* Gets a list of "air segments codes" from getInfo(), which we must send later when calling
* getQA/createBooking
*
* @todo : maybe use a ValueObject instead of an array
*
* @param VoyageInterface $voyage
* @return array
*/
protected function getAirSegmentsCodes(VoyageInterface $voyage) {
$data = $this->getInfo($voyage);
$segmentCodes = [];
foreach($data['structuredFlight']['list'] as $l) {
foreach($l['list'] as $l2) {
foreach($l2['codes'] as $c) {
if($c['name'] == 'INTID') {
$segmentCodes[] = $c['value'];
}
}
}
}
return $segmentCodes;
}
/**
* Gets a list of "miscellaneous segments" from getInfo(), which we must send later when calling
* getQA/createBooking.
*
* This function merges the data from "assurances", "prestations optionnelles" et "prestations
* facultatives (visas)" into a single request key called "miscellaneousSegments".
*
* @todo: maybe use a ValueObject instead of an array.
*
* @param VoyageInterface $voyage
* @return array
*/
protected function getMiscellaneousSegments(VoyageInterface $voyage) {
$data = $this->getInfo($voyage);
$miscSegments = [];
// "prestations facultatives (visas)"
if(is_array($data['miscellaneous'])) {
foreach($data['miscellaneous'] as $m) {
foreach($m['codes'] as $c) {
if($c['name'] == 'INTID') {
$miscCode = $c['value'];
}
}
foreach($m['segments'] as $s) {
$segmentCode = $s['code']['value'];
}
$miscSegments[] = ['miscellaneousCode' => $miscCode, 'segmentCode' => $segmentCode, 'quantity' => $voyage->getEtape0()->getNbrAdultes() + $voyage->getEtape0()->getNbrEnfants()];
}
}
// "prestations optionnelles"
if(is_array($data['optional'])) {
foreach($data['optional'] as $m) {
foreach($m['codes'] as $c) {
if($c['name'] == 'INTID') {
$miscCode = $c['value'];
}
}
foreach($m['segments'] as $s) {
$segmentCode = $s['code']['value'];
}
$miscSegments[] = ['miscellaneousCode' => $miscCode, 'segmentCode' => $segmentCode, 'quantity' => $voyage->getEtape0()->getNbrAdultes() + $voyage->getEtape0()->getNbrEnfants()];
}
}
// "assurances"
if(is_array($data['insurances'])) {
foreach($data['insurances'] as $m) {
foreach($m['codes'] as $c) {
if($c['name'] == 'INTID') {
$miscCode = $c['value'];
}
}
foreach($m['segments'] as $s) {
$segmentCode = $s['code']['value'];
}
$miscSegments[] = ['miscellaneousCode' => $miscCode, 'segmentCode' => $segmentCode, 'quantity' => $voyage->getEtape0()->getNbrAdultes() + $voyage->getEtape0()->getNbrEnfants()];
}
}
// Filter available options by options chosen by the user
$miscSegmentsFiltered = [];
$choices1 = $voyage->getEtape1()->getAssurances();
$choices2 = $voyage->getEtape1()->getPrestations();
$choices = array_merge($choices1, $choices2);
if(! empty($choices)) {
foreach($choices as $key) {
list($assurance_name, $segmentCode, $miscCode) = explode('#', $key);
foreach($miscSegments as $m) {
if($segmentCode == $m['segmentCode'] && $miscCode == $m['miscellaneousCode']) {
$miscSegmentsFiltered[] = $m;
}
}
}
}
return $miscSegmentsFiltered;
}
/**
* Gets a list of "insurance segments" from getInfo(), which we must send later when calling
* getQA/createBooking
*
* NOTE: Not use at the moment, because SAPEIG needs both "miscellaneous" and "insurances" keys inside "miscellaneousSegments"
*
* @param VoyageInterface $voyage
* @return array
*/
protected function getInsurancesSegments(VoyageInterface $voyage) {
$data = $this->getInfo($voyage);
$miscSegments = [];
if(is_array($data['insurances'])) {
foreach($data['insurances'] as $m) {
foreach($m['codes'] as $c) {
if($c['name'] == 'INTID') {
$miscCode = $c['value'];
}
}
foreach($m['segments'] as $s) {
$segmentCode = $s['code']['value'];
}
$miscSegments[] = ['miscellaneousCode' => $miscCode, 'segmentCode' => $segmentCode, 'quantity' => 1];
}
}
// Filter available options by options chosen by the user
$miscSegmentsFiltered = [];
$selection = $voyage->getEtape1()->getAssurances();
if(! empty($selection)) {
foreach($selection as $key) {
list($assurance_name, $segmentCode, $miscCode) = explode('#', $key);
foreach($miscSegments as $m) {
if($segmentCode == $m['segmentCode'] && $miscCode == $m['miscellaneousCode']) {
$miscSegmentsFiltered[] = $m;
}
}
}
}
return $miscSegmentsFiltered;
}
/**
* Translate WS errors to LMDVException errors
*
* @param string $errorType
* ErrorType returned by the APEX WS
* @return int
* LMDVException constant
*/
protected function getErrorType($errorType) {
switch($errorType) {
case self::ERROR_TYPE_SAPEIG:
$error = LMDVException::ERROR_SAPEIG;
break;
case self::ERROR_TYPE_NETWORK:
$error = LMDVException::ERROR_TIMEOUT;
break;
case self::ERROR_TYPE_SALESFORCE:
$error = LMDVException::ERROR_SALESFORCE;
break;
default:
$error = LMDVException::ERROR_UNKNOWN;
break;
}
return $error;
}
/**
* Translate WS errors to LMDVException errors
*
* @param string $errorType
* ErrorType returned by the APEX WS
* @return int
* LMDVException constant
*/
protected function getErrorTypeCreateBooking($errorType) {
switch($errorType) {
case self::ERROR_TYPE_SAPEIG:
$error = LMDVException::ERROR_SAPEIG_CREATEBOOKING;
break;
case self::ERROR_TYPE_NETWORK:
$error = LMDVException::ERROR_TIMEOUT_CREATEBOOKING;
break;
case self::ERROR_TYPE_SALESFORCE:
$error = LMDVException::ERROR_SALESFORCE_CREATEBOOKING;
break;
default:
$error = LMDVException::ERROR_UNKNOWN;
break;
}
return $error;
}
/**
* Clean ups SalesForce comments before showing them up in the form
*
* @param string $comment
* The comment that we receive from SalesForce
* @return string
* The comment cleanup for showing in HTML
*/
protected function cleanUpComments($comments) {
$breaks = ['</p><p>', '<br>', '<br/>'];
// Removes first opening <p> and last closing <p>
$comments = \preg_replace('#<p>(.*)</p>#', '$1', \htmlspecialchars_decode($comments, ENT_QUOTES | ENT_HTML401));
// Translates breaks into "\n"
foreach($breaks as $break) {
$comments = str_replace($break, "\n", $comments);
}
// Strip any other HTML tags
$comments = strip_tags($comments);
return $comments;
}
}