src/Service/WebServiceLMDV.php line 795

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Entity\VoyageInterface;
  4. use App\Exception\LMDVException;
  5. use App\Exception\WebServiceException;
  6. use App\Service\Payment\PaymentEventInterface;
  7. use App\Service\Process\ProcessInterface;
  8. use App\ValueObject\Flight;
  9. use App\ValueObject\Money;
  10. use App\ValueObject\PriceDetail;
  11. use Psr\Log\LoggerInterface;
  12. use Symfony\Component\HttpFoundation\RequestStack;
  13. use Symfony\Component\Lock\LockFactory;
  14. use Symfony\Contracts\HttpClient\HttpClientInterface;
  15. use Symfony\Contracts\Translation\TranslatorInterface;
  16. class WebServiceLMDV implements WebServiceLMDVInterface {
  17.   public const TYPE_OPP_PARENT 0;
  18.   public const TYPE_OPP_FILLE 1;
  19.   public const TIMEOUT_LOCK_CREATEBOOKING 60;
  20.   /**
  21.    * @var \Symfony\Component\HttpFoundation\Request
  22.    */
  23.   protected $request;
  24.   /**
  25.    * @var \App\Service\SalesForceConnectorInterface
  26.    */
  27.   protected $sf;
  28.   /**
  29.    * @var \Psr\Log\LoggerInterface
  30.    */
  31.   protected $logger;
  32.   /**
  33.    * @var \Symfony\Contracts\Translation\TranslatorInterface
  34.    */
  35.   protected $translator;
  36.   /**
  37.    * @var \Symfony\Component\Lock\LockFactory
  38.    */
  39.   protected $lockFactory;
  40.   /**
  41.    * Constructor with dependency injection.
  42.    *
  43.    * @param RequestStack $request_stack
  44.    * @param HttpClientInterface $client
  45.    * @param LoggerInterface $logger
  46.    * @param array $config
  47.    */
  48.   public function __construct(RequestStack $request_stackSalesForceConnectorInterface $sfLoggerInterface $loggerTranslatorInterface $translatorLockFactory $lockFactory)
  49.   {
  50.     $this->request =  $request_stack->getCurrentRequest();
  51.     $this->sf $sf;
  52.     $this->logger $logger;
  53.     $this->translator $translator;
  54.     $this->lockFactory $lockFactory;
  55.   }
  56.   /**
  57.    * Gets the binary PDF content from a WS APEX call.
  58.    *
  59.    * @param VoyageInterface $voyage
  60.    * @return array $result
  61.    *    An array avec two keys "type" (content or url) and "content" with the binary content or the url, If "content" is empty, it means there is no PDF.
  62.    */
  63.   public function getPdfVoyage(VoyageInterface $voyage) {
  64.     $data $this->sf->getPdfVoyage($voyage);
  65.     return $data;
  66.   }
  67.   /**
  68.    * Gets the codeAgent calling a WS APEX that will create it if it does not exist.
  69.    *
  70.    * This is used in GIRDistrib.
  71.    *
  72.    * @param VoyageInterface $voyage
  73.    * @return string
  74.    */
  75.   public function getCodeAgent(VoyageInterface $voyage) {
  76.       $data $this->sf->getCodeAgent($voyage);
  77.       $agentCode $data['agentCode'];
  78.       return $agentCode;
  79.   }
  80.   /**
  81.    * Returns a Money object
  82.    *
  83.    * @param VoyageInterface $voyage
  84.    * @param string $no_error
  85.    *    If it is TRUE, we don't bubble the exception and instead we will return NULL.
  86.    * @return \App\ValueObject\Money
  87.    */
  88.   public function getTotalAmount(VoyageInterface $voyage$no_error FALSE) : ?Money {
  89.     // This condition is to avoid calling getQA if we are not yet in etape1
  90.     if(! $voyage->getEtape1()) {
  91.       return NULL;
  92.     }
  93.     // If we don't fill up correctly etape1 and we go back to etape0, when arriving
  94.     // a second time to etape1, we will not return NULL because getEtape1()
  95.     // will be (partially) filled up, so we must use a try/catch here to avoid triggering an
  96.     // error in case of problems (for example, if assurances had not yet been selected)
  97.     try {
  98.       $data $this->sf->getQA($voyage);
  99.     }
  100.     catch(\Exception $e) {
  101.       // getBoxRecap uses $no_error = TRUE to avoid bubbling.
  102.       if($no_error) {
  103.         return NULL;
  104.       }
  105.       else {
  106.         throw $e;
  107.       }
  108.     }
  109.     $money = new Money(
  110.       $data['prices']['totalAmount'],
  111.       $data['prices']['Currency'],
  112.       $data['prices']['Decimals']
  113.     );
  114.     return $money;
  115.   }
  116.     /**
  117.    * Returns a Money object
  118.    *
  119.    * @param VoyageInterface $voyage
  120.    * @return \App\ValueObject\Money
  121.    */
  122.   public function getTotalCommission(VoyageInterface $voyage) : ?Money {
  123.     try {
  124.       $data $this->sf->getQA($voyage);
  125.     }
  126.     catch(\Exception $e) {
  127.       throw $e;
  128.     }
  129.     $money = new Money(
  130.       $data['prices']['Commission'] ?? 0,
  131.       $data['prices']['Currency'],
  132.       $data['prices']['Decimals']
  133.     );
  134.     return $money;
  135.   }
  136.   /**
  137.    * Returns a Money object after having substracted the assurances costs.
  138.    *
  139.    * @param VoyageInterface $voyage
  140.    * @return \App\ValueObject\Money
  141.    */
  142.   public function getTotalAmountHorsAssurances(VoyageInterface $voyage) : Money {
  143.     $data $this->sf->getQA($voyage);
  144.     $details $data['prices']['DetailPrices'];
  145.     $costs = new Money(0$data['prices']['Currency'], $data['prices']['Decimals']);
  146.     foreach($details as $d) {
  147.       if(is_array(@$d['codes'])) {
  148.         foreach($d['codes'] as $c) {
  149.           if($c['name'] == 'Formule') {
  150.             if(strlen($c['value']) == && $c['value'][0] == 'A' && $c['value'][1] == 'S' && \is_numeric($c['value'][2])) {
  151.               $aux = new Money($d['unitPrice'], $data['prices']['Currency'], $data['prices']['Decimals']);
  152.               $aux $aux->multiply($d['quantity']);
  153.               $costs $costs->add($aux);
  154.             }
  155.           }
  156.         }
  157.       }
  158.     }
  159.     $total $this->getTotalAmount($voyage);
  160.     $result $total->substract($costs);
  161.     return $result;
  162.   }
  163.   /**
  164.    * Returns a Money object.
  165.    *
  166.    * @param VoyageInterface $voyage
  167.    * @return \App\ValueObject\Money
  168.    */
  169.   public function getPayment(VoyageInterface $voyage) : Money {
  170.     $data $this->sf->getQA($voyage);
  171.     $money = new Money(
  172.       $data['prices']['Payment']['Amount'],
  173.       $data['prices']['Payment']['Currency'],
  174.       $data['prices']['Payment']['Decimals']
  175.     );
  176.     return $money;
  177.   }
  178.   /**
  179.    * Returns an array of PriceDetail objects
  180.    *
  181.    * @param VoyageInterface $voyage
  182.    * @return \App\ValueObject\PriceDetail[]
  183.    */
  184.   public function getPriceDetails(VoyageInterface $voyage) {
  185.     $data $this->sf->getQA($voyage);
  186.     $decimals $data['prices']['Decimals'];
  187.     $details = [];
  188.     foreach($data['prices']['DetailPrices'] as $d) {
  189.       $priceDetail = new PriceDetail(
  190.         $d['CommissionPercentage'] > $d['title'] . " (" $d['CommissionPercentage'] . "%)" $d['title'],
  191.         $d['quantity'],
  192.         new Money($d['unitPrice'], $data['prices']['Currency'], $decimals),
  193.         new Money($d['subTotalPrice'], $data['prices']['Currency'], $decimals)
  194.       );
  195.       $details[] = $priceDetail;
  196.     }
  197.     return $details;
  198.   }
  199.   public function getMiscellaneous(VoyageInterface $voyage) {
  200.     $data $this->sf->getInfo($voyage);
  201.     $miscellaneous = [];
  202.     if(is_array(@$data['miscellaneous'])) {
  203.       foreach($data['miscellaneous'] as $m) {
  204.         foreach($m['segments'] as $s) {
  205.           foreach($s['descriptions'] as $d) {
  206.             $visa_name $d['text'];
  207.           }
  208.           $segment_code $s['code']['value'];
  209.           $group $s['group'];
  210.         }
  211.         $miscellaneous_code $m['codes'][0]['value'];
  212.         $miscellaneous[$group][] = [$visa_name => $visa_name '#' $segment_code '#' $miscellaneous_code '#' $group];
  213.       }
  214.     }
  215.     return $miscellaneous;
  216.   }
  217.   public function getAssurances(VoyageInterface $voyage) {
  218.     $data $this->sf->getInfo($voyage);
  219.     $assurances = [];
  220.     if(is_array(@$data['insurances'])) {
  221.       foreach($data['insurances'] as $m) {
  222.         foreach($m['segments'] as $s) {
  223.           foreach($s['descriptions'] as $d) {
  224.             $assurance_name $d['text'];
  225.           }
  226.           $segment_code $s['code']['value'];
  227.           $group $s['group'];
  228.         }
  229.         $miscellaneous_code $m['codes'][0]['value'];
  230.         $assurances[$group][] = [$assurance_name => $assurance_name '#' $segment_code '#' $miscellaneous_code '#' $group];
  231.       }
  232.     }
  233.     return $assurances;
  234.   }
  235.   public function getAssuranceDescription($choice$key$value) {
  236.     list($assurance_name$segment_code$miscellaneous_code$group) = explode('#'$value);
  237.     $debug '';
  238.     if(in_array($_ENV['APP_ENV'], ['dev''test'])) {
  239.       $debug ' ('$segment_code .') ';
  240.     }
  241.     $text 'etape1.assurances.desc.' $segment_code;
  242.     $textTranslated $this->translator->trans($text);
  243.     // Translation has not been found, better do not show anything
  244.     if($textTranslated == $text) {
  245.       $textTranslated '';
  246.     }
  247.     return $key $debug  $textTranslated;
  248.   }
  249.   public function getAssuranceGroupLabel($group$assurance_choices) {
  250.     $paragraphs = [];
  251.     foreach($assurance_choices as $arr) {
  252.       foreach($arr as $value) {
  253.         list($assurance_name$segment_code$miscellaneous_code$g) = explode('#'$value);
  254.         // When showing the assurances label (etape1.assurances.label.GROUP),
  255.         // we assume grp = 2 => "assistance" and grp = 3 => "annulation" but this is not
  256.         // always the case, so we are forced to search by substrings inside "assurance_name"
  257.         // TODO : The APEX WS should always send "assistances" with group 2 and "assurances" with group 3
  258.         if($group != 0) {
  259.           if(strstr($assurance_name'ANNU')) {
  260.             if($group != 3) {
  261.               $this->logger->error("Assurances group mismatch, expected '3' (ANNU) received '$group'");
  262.               $group 3;
  263.             }
  264.           }
  265.           if(strstr($assurance_name'ASSIS')) {
  266.             if($group != 2) {
  267.               $this->logger->error("Assurances group mismatch, expected '2' (ASSIS) received '$group'");
  268.               $group 2;
  269.             }
  270.           }
  271.         }
  272.         $text 'etape1.assurances.desc.' $segment_code;
  273.         $textTranslated $this->translator->trans($text);
  274.         if($textTranslated == $text) {
  275.           $textTranslated '';
  276.         }
  277.         elseif(strstr($textTranslated'<small></small>')) {
  278.           $textTranslated '';
  279.         }
  280.         else {
  281.           $paragraphs[] = $textTranslated;
  282.         }
  283.       }
  284.     }
  285.     $text "etape1.assurances.label.GROUP". (int)$group;
  286.     $textTranslated $this->translator->trans($text);
  287.     $output '<h2>'.$textTranslated.'</h2>';
  288.     if(count($paragraphs) > 1) {
  289.       $output .= $this->translator->trans('etape1.assurances.label.multiples', ['total' => count($paragraphs)]);
  290.       $output .= '<ul>';
  291.       foreach($paragraphs as $p) {
  292.         $output .= '<li>' $p '</li>';
  293.       }
  294.       $output .= '</ul>';
  295.     }
  296.     else {
  297.       foreach($paragraphs as $p) {
  298.         $output .= '<p>' $p '</p>';
  299.       }
  300.     }
  301.     return $output;
  302.   }
  303.   public function getPrestations(VoyageInterface $voyage) {
  304.     $data $this->sf->getInfo($voyage);
  305.     $prestations = [];
  306.     if(is_array(@$data['optional'])) {
  307.       foreach($data['optional'] as $o) {
  308.         foreach($o['segments'] as $s) {
  309.           foreach($s['descriptions'] as $d) {
  310.             $prestations_name $d['text'];
  311.           }
  312.           $segment_code $s['code']['value'];
  313.         }
  314.         $optional_code $o['codes'][0]['value'];
  315.         $prestations[] = [$prestations_name => $prestations_name '#' $segment_code '#' $optional_code];
  316.       }
  317.     }
  318.     return $prestations;
  319.   }
  320.   /**
  321.    * Returns an array of values like this :
  322.    *
  323.    * ```php
  324.    * [
  325.    *   'code' => ''
  326.    *   'segmentCode' => ''
  327.    *   'category' => ''
  328.    *   'persons' => [
  329.    *     'max' => '',
  330.    *     'min' => ''
  331.    *   ],
  332.    *   'adults' => [
  333.    *      'max' => '',
  334.    *      'min' => ''
  335.    *   ],
  336.    *   'children' => [
  337.    *      'max' => '',
  338.    *      'min' => ''
  339.    *   ]
  340.    * ]
  341.    * ```
  342.    *
  343.    * @todo: This should be converted to an ValueObject object.
  344.    *
  345.    * @param VoyageInterface $voyage
  346.    * @throws WebServiceException
  347.    * @return array
  348.    */
  349.   public function getChambresInformation(VoyageInterface $voyage) {
  350.     $data $this->sf->getInfo($voyage);
  351.     $results = [];
  352.     foreach($data['stays'] as $s) {
  353.       $result = [];
  354.       $room $s['rooms'][0];
  355.       foreach($room['occupancies'] as $o) {
  356.         if(isset($o['max']) && $o['max'] == 99) {
  357.           $o['max'] = 3;
  358.         }
  359.         if($o['unit'] == 'Person') {
  360.           $result['persons']['max'] = isset($o['max']) ? $o['max'] : 3;
  361.           $result['persons']['min'] = isset($o['min']) ? $o['min'] : 0;
  362.         }
  363.         if($o['unit'] == 'Adult') {
  364.           $result['adults']['max'] = isset($o['max']) ? $o['max'] : 3;
  365.           $result['adults']['min'] = isset($o['min']) ? $o['min'] : 0;
  366.         }
  367.         if($o['unit'] == 'Child') {
  368.           $result['children']['max'] = isset($o['max']) ? $o['max'] : 3;
  369.           $result['children']['min'] = isset($o['min']) ? $o['min'] : 0;
  370.         }
  371.       }
  372.       $result['category'] = $room['descriptions'][0]['text'];
  373.       $result['code'] = $room['code']['value'];
  374.       $segmentCode '';
  375.       foreach($s['codes'] as $c) {
  376.         if($c['name'] == 'INTID') {
  377.           $segmentCode $c['value'];
  378.         }
  379.       }
  380.       $result['segmentCode'] = $segmentCode;
  381.       // Add element to the final array
  382.       $results[] = $result;
  383.     }
  384.     // Filtering : do not show rooms that have "min persons" > "total persons"
  385.     foreach($results as $key => $result) {
  386.       if($result['persons']['min'] > ($voyage->getEtape0()->getNbrAdultes() + $voyage->getEtape0()->getNbrEnfants())) {
  387.         unset($results[$key]);
  388.       }
  389.     }
  390.     return $results;
  391.   }
  392.   /**
  393.    * Gets the travel "title" from getInfo()
  394.    *
  395.    * @param VoyageInterface $voyage
  396.    * @return string
  397.    */
  398.   public function getTitleVoyage(VoyageInterface $voyage) {
  399.     $data $this->sf->getInfo($voyage);
  400.     $title $data['title'];
  401.     return $title;
  402.   }
  403.   /**
  404.    * Gets the travel nights from getInfo()
  405.    *
  406.    * @param VoyageInterface $voyage
  407.    * @return int
  408.    *    The number of nights
  409.    */
  410.   public function getRoomNights(VoyageInterface $voyage) {
  411.     $data $this->sf->getInfo($voyage);
  412.     foreach($data['durations'] as $d) {
  413.       if($d['Unit'] == 'Night') {
  414.         return $d['Value'];
  415.       }
  416.     }
  417.   }
  418.   /**
  419.    * Gets the travel days from getInfo()
  420.    *
  421.    * @param VoyageInterface $voyage
  422.    * @return int
  423.    *    The number of days
  424.    */
  425.   public function getRoomDays(VoyageInterface $voyage) {
  426.     $data $this->sf->getInfo($voyage);
  427.     foreach($data['durations'] as $d) {
  428.       if($d['Unit'] == 'Day') {
  429.         return $d['Value'];
  430.       }
  431.     }
  432.   }
  433.   /**
  434.    * Gets a list of Flight objects from getInfo()
  435.    *
  436.    * @param VoyageInterface $voyage
  437.    * @param string $type
  438.    * @return \App\ValueObject\Flight[] flights
  439.    */
  440.   protected function getVols(VoyageInterface $voyage$type) {
  441.     $data $this->sf->getInfo($voyage);
  442.     $vols = [];
  443.     foreach($data['structuredFlight']['list'] as $l) {
  444.        foreach($l['list'] as $l2) {
  445.          if($l2['direction'] == $type) {
  446.            foreach($l2['segments'] as $s) {
  447.              $flight = new Flight(
  448.                  $s['flight']['number'], // $flightNumber
  449.                  $s['operatedBy']['code'], // $flightCode
  450.                  $s['operatedBy'], // $operatedBy
  451.                  isset($s['carrier']) ? $s['carrier'] : ($s['operatedBy']['text'] ? $s['operatedBy'] : ''),
  452.                  $s['from']['city'],
  453.                  $s['to']['city'],
  454.                  strtotime($s['begin']['value']),
  455.                  strtotime($s['end']['value']),
  456.                  $l2['descriptions'][0]['text'],
  457.                  strtotime($s['end']['value']) - strtotime($s['begin']['value'])
  458.              );
  459.              $vols[] = $flight;
  460.            }
  461.          }
  462.        }
  463.     }
  464.     usort($vols, function(Flight $aFlight $b) {
  465.       return ($a->getBeginDate() <= $b->getBeginDate()) ? -1;
  466.     });
  467.     return $vols;
  468.   }
  469.   /**
  470.    * Gets information about the departure flight from getInfo()
  471.    *
  472.    * @param VoyageInterface $voyage
  473.    * @return \App\ValueObject\Flight $flight
  474.    */
  475.   public function getVolAller(VoyageInterface $voyage) {
  476.     $vols_aller = [];
  477.     try {
  478.       $vols_aller $this->getVols($voyage'Outbound');
  479.     }
  480.     catch(\Exception $e) {
  481.       // This is not blocking but we save the parsing error to the log
  482.       $this->logger->error("getVols error", ['message' => $e->getMessage(), 'backtrace' => $e->getTrace()]);
  483.     }
  484.     return $vols_aller;
  485.   }
  486.   /**
  487.    * Gets information about the return flight from getInfo()
  488.    *
  489.    * @param VoyageInterface $voyage
  490.    * @return \App\ValueObject\Flight $flight
  491.    */
  492.   public function getVolRetour(VoyageInterface $voyage) {
  493.     $vols_retour = [];
  494.     try {
  495.      $vols_retour $this->getVols($voyage'Inbound');
  496.     }
  497.     catch(\Exception $e) {
  498.       // This is not blocking but we save the parsing error to the log
  499.       $this->logger->error("getVols error", ['message' => $e->getMessage(), 'backtrace' => $e->getTrace()]);
  500.     }
  501.     return $vols_retour;
  502.   }
  503.   /**
  504.    * Sets some $session variables to be used by TWIG extensions functions in order to
  505.    * show the banner box that contains the summary of the travel.
  506.    *
  507.    * @param VoyageInterface $voyage
  508.    */
  509.   public function getBoxRecap(VoyageInterface $voyage) {
  510.     $price '';
  511.     $title '';
  512.     $roomNights '';
  513.     $roomDays '';
  514.     // totalAmount: this can only be called when we are sure that getQA works, from step2 onwards only,
  515.     // because even with a try/catch block inside getTotalAmount, LMDV gets an error message from SalesForce.
  516.     $routeName $this->request->get('_route');
  517.     if(strstr($routeName'step0') || strstr($routeName'step1')) {
  518.       $price NULL;
  519.     }
  520.     else {
  521.       $price $this->getTotalAmount($voyageTRUE);
  522.     }
  523.     if(! $price) {
  524.       $this->logger->error("getBoxRecap error", ['message' => "We didn't call getQA() because we are not yet ready."'backtrace' => []]);
  525.     }
  526.     // titleVoyage
  527.     try {
  528.       $title $this->getTitleVoyage($voyage);
  529.     }
  530.     catch(\Exception $e) {
  531.       $this->logger->error("getBoxRecap error", ['message' => $e->getMessage(), 'backtrace' => $e->getTrace()]);
  532.     }
  533.     // roomNights
  534.     try {
  535.       $roomNights $this->getRoomNights($voyage);
  536.     }
  537.     catch(\Exception $e) {
  538.       $this->logger->error("getBoxRecap error", ['message' => $e->getMessage(), 'backtrace' => $e->getTrace()]);
  539.     }
  540.     // roomDays
  541.     try {
  542.       $roomDays $this->getRoomDays($voyage);
  543.     }
  544.     catch(\Exception $e) {
  545.       $this->logger->error("getBoxRecap error", ['message' => $e->getMessage(), 'backtrace' => $e->getTrace()]);
  546.     }
  547.     // Save values into session to use them in TwigExtension class.
  548.     $this->request->getSession()->set('TWIG_totalPrice'$price);
  549.     $this->request->getSession()->set('TWIG_title'$title);
  550.     $this->request->getSession()->set('TWIG_roomNights'$roomNights);
  551.     $this->request->getSession()->set('TWIG_roomDays'$roomDays);
  552.   }
  553.   /**
  554.    * creates the booking in SAPEIG.
  555.    *
  556.    * To avoid calling multiple times createBooking when using CTRL-R, we use an exclusive lock to be sure that only one request will be sent.
  557.    * We do not use exclusive locks for other WS requests because they are fast, but createBooking is extremely slow (15s - 30s).
  558.    *
  559.    * The lock is a blocking one, because if the process that acquired the lock finishes,
  560.    * the response will be cached and the second process will received a cached response.
  561.    *
  562.    * @param VoyageInterface $voyage
  563.    * @return array
  564.    */
  565.   public function createBooking(VoyageInterface $voyage) {
  566.     $key $voyage->getSessionId();
  567.     $lockW $this->lockFactory->createLock($keyself::TIMEOUT_LOCK_CREATEBOOKING);
  568.     if($lockW->acquire(TRUE)) {
  569.       try {
  570.         $content $this->sf->createBooking($voyage);
  571.       }
  572.       catch(\Exception $e) {
  573.         $lockW->release();
  574.         throw $e;
  575.       }
  576.       $lockW->release();
  577.       return $content;
  578.     }
  579.     else {
  580.       // This is a blocking lock so we should never arrive here !.
  581.       throw new WebServiceException('Exclusive lock failed to acquire without waiting'LMDVException::ERROR_TIMEOUT_CREATEBOOKING);
  582.     }
  583.   }
  584.   /**
  585.    * Adds a payment by HiPay to SAPEIG
  586.    *
  587.    * @param PaymentEventInterface  $event
  588.    * @return array
  589.    */
  590.   public function addPayment(PaymentEventInterface $event) {
  591.     return $this->sf->addPayment($event);
  592.   }
  593.   /**
  594.    * Auxiliary function that answer the question "is this IdOPP the child of another one" ?
  595.    *
  596.    * @param string $id_opp
  597.    *    The opportunity ID
  598.    * @return boolean
  599.    *    returns TRUE if there is an opportunity parent, FALSE otherwise
  600.    */
  601.   public function isOpportunityFille($id_opp) {
  602.     // We just need to know if this opp has a parent, so no validation is needed.
  603.     $result $this->sf->getSalesForceData($id_oppTRUE);
  604.     if($result['type'] == 'Groupe_Fille' && empty($result['idOppSFParent'])) {
  605.       throw new \Exception($this->translator->trans("error.opportunity.fille.no_parent"));
  606.     }
  607.     if(! empty($result['idOppSFParent'])) {
  608.       return TRUE;
  609.     }
  610.     else {
  611.       return FALSE;
  612.     }
  613.   }
  614.   /**
  615.    * If $id_opp is of type FILLE, we must retrieve the FILLE data + the PARENT businessCode
  616.    * which must be used for calling getInfo/getQA/createBooking
  617.    *
  618.    * If we get a FILLE opp but we discover that there is no PARENT, then we must switch to the
  619.    * tunnel as if they were not a FILLE, but a PARENT (GIRDirect)
  620.    *
  621.    * The $voyage object member "idOppSF" holds the opp. used to prefill up fields, it can be a FILLE
  622.    * opp (if there is a parent one which will then be saved in "idOppSFParent") or it can be an opp without
  623.    * any PARENT. Thus, in order to discover what type of opp is used in "idOppSF", we have to consider the tunnel
  624.    * we are in and if there is something in "idOppSFParent". The type of opp. received is not saved in
  625.    * the $voyage object (TODO: This is confusing and maybe we should better structure it, because as we
  626.    * have a member "idOppSFParent" we might tend to think that "idOppSF" is always a FILLE, which is not the case.)
  627.    *
  628.    * Only for GRPFactIndivWeb, we must empty "idOppSF" opp and save the incoming opp as "idOppSFParent",
  629.    * which will be sent in createBooking in order to create the FILLE opp and to attach it to the PARENT.
  630.    *
  631.    * @param string $id_opp
  632.    *    The id_opp we received as a parameter
  633.    * @param string $type
  634.    *    The id_opp can be of type FILLE or PARENT
  635.    * @param bool $is_grp
  636.    *    TRUE if we are asking for a GRP opp (GRP*) or not (GIR*)
  637.    */
  638.   public function getSalesForceDataIntoSession($id_opp$type$is_grp) {
  639.     $result $this->sf->getSalesForceData($id_opp);
  640.     $session $this->request->getSession();
  641.     if($type == self::TYPE_OPP_PARENT) {
  642.       $session->set('businessCode'$result['businessCode']);
  643.     }
  644.     elseif($type == self::TYPE_OPP_FILLE) {
  645.       $session->set('businessCode'$result['businessCodeParent']);
  646.       $session->set('idOppSFParent'$result['idOppSFParent']);
  647.     }
  648.     else {
  649.       throw new \Exception($this->translator->trans("error.opportunity.type_unknown"));
  650.     }
  651.     // Raise an error if we are GRP* and we don't have businessCode
  652.     if($is_grp && empty($session->get('businessCode'))) {
  653.       throw new \Exception($this->translator->trans("error.opportunity.businessCode"));
  654.     }
  655.     if($is_grp && ! empty($result['reference_pdf'])) {
  656.       $session->set('reference_pdf'$result['reference_pdf']);
  657.     }
  658.     // Testing for emptiness before overriding session information, because we can have $_GET parameters
  659.     // that we do not want to overwrite with empty values.
  660.     if(! empty($result['agentCode'])) {
  661.       $session->set('agentCode'$result['agentCode']);
  662.     }
  663.     if(! empty($result['productCode'])) {
  664.       $session->set('productCode'$result['productCode']);
  665.     }
  666.     if(! empty($result['departureDate'])) {
  667.       $session->set('departureDate'$result['departureDate']);
  668.     }
  669.     if(! empty($result['endDate'])) {
  670.       $session->set('endDate'$result['endDate']);
  671.     }
  672.     if(! empty($result['departureCity'])) {
  673.       $session->set('departureCity'$result['departureCity']);
  674.     }
  675.     if(! empty($result['adults'])) {
  676.       $session->set('adults'$result['adults']);
  677.     }
  678.     if(! empty($result['children'])) {
  679.       $session->set('children'$result['children']);
  680.     }
  681.     if(! empty($result['stageName'])) {
  682.       $session->set('stageName'$result['stageName']);
  683.       if(!empty($result['stageNameFille']) && $is_grp && $type == self::TYPE_OPP_FILLE  && !($this->isAgent()
  684.               || (in_array($_ENV['APP_ENV'], ['dev''test']) && $this->request->query->get('test_agence')))){
  685.             $session->set('stageName'$result['stageNameFille']);
  686.       }
  687.     }
  688.   }
  689.   public function getSalesForceParticipants($idOppSF) {
  690.     return $this->sf->getSalesForceParticipants($idOppSF);
  691.   }
  692.   public function getSalesForceResponsable($idOppSF) {
  693.     return $this->sf->getSalesForceResponsable($idOppSF);
  694.   }
  695.   public function getSalesForceTitleVoyage(VoyageInterface $voyage) {
  696.     return $this->sf->getSalesForceTitleVoyage($voyage);
  697.   }
  698.   public function getVolsWarning(VoyageInterface $voyage) {
  699.     $data $this->sf->getInfo($voyage);
  700.     if($data['productStatus'] == 'Available') {
  701.       return FALSE;
  702.     }
  703.     elseif($data['productStatus'] == 'Request') {
  704.       return TRUE;
  705.     }
  706.     else {
  707.       return FALSE;
  708.     }
  709.   }
  710.   public function isAgent() {
  711.     if(strpos($_ENV['IP_ADDRESS_AGENCE'],  $this->request->getClientIp()) !== FALSE) {
  712.       return TRUE;
  713.     }
  714.     else {
  715.       return FALSE;
  716.     }
  717.   }
  718. }