Deprecated: Constant E_STRICT is deprecated in /home/pastorz/old-espace-client/vendor/symfony/error-handler/ErrorHandler.php on line 58

Deprecated: Constant E_STRICT is deprecated in /home/pastorz/old-espace-client/vendor/symfony/error-handler/ErrorHandler.php on line 76
Symfony Profiler

src/Service/ColissimoService.php line 78

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Entity\Batch;
  4. use App\Entity\Carrier;
  5. use App\Entity\CarrierInsurancePriceRange;
  6. use App\Entity\DeliveryNote;
  7. use App\Entity\EstimationRequest;
  8. use App\Entity\SupportRequest;
  9. use App\Twig\FormatExtension;
  10. use Com\Tecnick\Color\Exception;
  11. use Doctrine\Common\Collections\Collection;
  12. use Doctrine\ORM\EntityManagerInterface;
  13. use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
  14. use setasign\Fpdi\PdfParser\Filter\FilterException;
  15. use setasign\Fpdi\PdfParser\PdfParserException;
  16. use setasign\Fpdi\PdfParser\Type\PdfTypeException;
  17. use setasign\Fpdi\PdfReader\PdfReaderException;
  18. use setasign\Fpdi\Tcpdf\Fpdi;
  19. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  20. use Symfony\Component\HttpFoundation\Response;
  21. use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
  22. use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
  23. use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
  24. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  25. use Symfony\Contracts\HttpClient\HttpClientInterface;
  26. use Symfony\Contracts\HttpClient\ResponseInterface;
  27. use Throwable;
  28. class ColissimoService
  29. {
  30.     private const BASE_URL 'https://ws.colissimo.fr';
  31.     private const ENDPOINTS = array(
  32.         'getToken' => '/widget-colissimo/rest/authenticate.rest',
  33.         'generateLabel' => '/sls-ws/SlsServiceWSRest/2.0/generateLabel',
  34.         'checkGenerateLabel' => '/sls-ws/SlsServiceWSRest/2.0/checkGenerateLabel',
  35.     );
  36.     public function __construct(
  37.         private ParameterBagInterface  $parameterBag,
  38.         private BrandService           $brandService,
  39.         private HttpClientInterface    $httpClient,
  40.         private EntityManagerInterface $em,
  41.         private PdfGenerationService   $pdfGenerationService,
  42.         private LabelService           $labelService
  43.     )
  44.     {
  45.     }
  46.     public function getBaseUrl(): string
  47.     {
  48.         return self::BASE_URL;
  49.     }
  50.     /**
  51.      * @return array|null
  52.      * @throws ClientExceptionInterface
  53.      * @throws RedirectionExceptionInterface
  54.      * @throws ServerExceptionInterface
  55.      * @throws TransportExceptionInterface
  56.      */
  57.     public function getToken(): ?array
  58.     {
  59.         $response $this->call('POST'self::ENDPOINTS[__FUNCTION__], array('login' => $this->parameterBag->get("colissimo.login"), 'password' => $this->parameterBag->get("colissimo.password")));
  60.         return json_decode($response->getContent(), true);
  61.     }
  62.     /**
  63.      * @param string $method
  64.      * @param string $endpoint
  65.      * @param array|null $bodyParams
  66.      * @param array $queryParams
  67.      * @return ResponseInterface
  68.      * @throws TransportExceptionInterface
  69.      */
  70.     private function call(string $methodstring $endpoint, array $bodyParams null, array $queryParams = array(), array $batchesIds null): ResponseInterface
  71.     {
  72.         $params = array(
  73.             'json' => $bodyParams
  74.         );
  75.         if (isset($bodyParams['letter'])) {
  76.             $params['user_data'] = [
  77.                 'productCode' => $bodyParams['letter']['service']['productCode'],
  78.                 'batchesIds' => $batchesIds
  79.             ];
  80.         }
  81.         return $this->httpClient->request($methodself::BASE_URL $endpoint '?' http_build_query($queryParams), $params);
  82.     }
  83.     /**
  84.      * @param DeliveryNote|null $note
  85.      * @param bool|null $generateOneOnly
  86.      * @param Collection|null $batches
  87.      * @param bool|null $isReturnLabel
  88.      * @return Response
  89.      * @throws ClientExceptionInterface
  90.      * @throws CrossReferenceException
  91.      * @throws Exception
  92.      * @throws FilterException
  93.      * @throws PdfParserException
  94.      * @throws PdfReaderException
  95.      * @throws PdfTypeException
  96.      * @throws RedirectionExceptionInterface
  97.      * @throws ServerExceptionInterface
  98.      * @throws TransportExceptionInterface
  99.      */
  100.     public function generateLabel(?DeliveryNote $note null, ?bool $generateOneOnly false, ?Collection $batches null, ?bool $isReturnLabel false, ?bool $debug false): Response
  101.     {
  102.         if (!$note && !$batches) throw new Exception("DeliveryNote null, Batches null");
  103.         //Has to generate labels for each batch, and then merge them together
  104.         $allParcels $this->getAllParcelParams($note?->getBatches() ?? $batches$isReturnLabel);
  105.         if ($generateOneOnly) {
  106.             $allParcels array_slice($allParcels01);
  107.             $allParcels[0]['letter']['parcel']['weight'] = 1;
  108.         }else if(!$isReturnLabel){
  109.             ($note ?? $batches->first()->getDeliveryNote())->getShipment()->setTrackingNumbers([]);
  110.         }
  111.         if ($debug) return $this->checkGenerateLabel($allParcels[0]);
  112.         //Download and merge all parcel labels
  113.         return $this->concurrentMergeDownloads($allParcels$note);
  114.     }
  115.     /**
  116.      * @param mixed $batches
  117.      * @param bool|null $isReturnLabel
  118.      * @return array|null
  119.      */
  120.     private function getAllParcelParams(mixed $batches, ?bool $isReturnLabel false): array|null
  121.     {
  122.         if ($batches->isEmpty()) return null;
  123.         $parcels = ['combinedParcels' => []];
  124.         foreach ($batches as $key => $batch) {
  125.             if ($batch->isIndividualShipment()) $parcels[$key] = [$batch];
  126.             else $parcels['combinedParcels'][] = $batch;
  127.         }
  128.         $parcelParams = [];
  129.         foreach ($parcels as $key => $parcel) {
  130.             if ($parcel$parcelParams[] = $this->getParcelParams($parcel$isReturnLabel);
  131.         }
  132.         return $parcelParams;
  133.     }
  134.     /**
  135.      * @param array $parcel
  136.      * @param bool|null $isReturnLabel
  137.      * @return array
  138.      */
  139.     private function getParcelParams(array $parcel, ?bool $isReturnLabel false): array
  140.     {
  141.         /** @var DeliveryNote $note */
  142.         $note $parcel[0]->getDeliveryNote();
  143.         if ($isReturnLabel$productCode 'CORE';
  144.         else if (isset($note->getShipment()->getDeliveryAddress()['productCode'])) {
  145.             $productCode $note->getShipment()->getDeliveryAddress()['productCode'];
  146.         } else {
  147.             $productCode $note->getShipment()->isDeliveryAgainstSignature() || $note->getShipment()->isInsurance() ? 'DOS' 'DOM';
  148.         }
  149.         if (in_array(strtoupper($productCode), ['ACP''CDI'])) $productCode 'BPR';
  150.         $brandAddress $isReturnLabel $this->brandService->getBrand()->getWarehouseAddress() : $this->brandService->getBrand()->getAddress();
  151.         $senderAddress = [
  152.             "companyName" => $this->brandService->getBrand()->getCompanyName(),
  153.             "line2" => $brandAddress->getAddress(),
  154.             "countryCode" => $brandAddress->getCountry()->getCode(),
  155.             "city" => $brandAddress->getCity(),
  156.             "zipCode" => preg_replace('/\s/'''$brandAddress->getZipcode())
  157.         ];
  158.         $addresseeFirstname trim($note->getShipment() ? $note->getShipment()->getDeliveryAddress()['firstname'] : $note->getUser()->getLastname());
  159.         $addresseeLastname trim($note->getShipment() ? $note->getShipment()->getDeliveryAddress()['lastname'] : $note->getUser()->getLastname());
  160.         $countryCode = isset($note->getShipment()?->getDeliveryAddress()['countryCode']) ? $note->getShipment()?->getDeliveryAddress()['countryCode'] : 'FR';
  161.         $zipcode preg_replace('/\s/'''$note->getShipment() ? $note->getShipment()->getDeliveryAddress()['zipcode'] : $note->getUser()->getFallbackAddress()->getZipcode());
  162.         if ($countryCode === 'FR'$zipcode FormatExtension::formatZipCode($zipcode);
  163.         $addresseeAddress = [
  164.             "lastName" => strlen($addresseeFirstname) ? $addresseeFirstname '.',
  165.             "firstName" => strlen($addresseeLastname) ? $addresseeLastname '.',
  166.             "line0" => $note->getShipment() ? $note->getShipment()->getDeliveryAddress()['address_line2'] ?? '' $note->getUser()->getFallbackAddress()->getAddressLine2(),
  167.             "line1" => $note->getShipment() ? $note->getShipment()->getDeliveryAddress()['address_line1'] ?? '' $note->getUser()->getFallbackAddress()->getAddressLine1(),
  168.             "line2" => $note->getShipment() ? $note->getShipment()->getDeliveryAddress()['address'] : $note->getUser()->getFallbackAddress()->getAddress(),
  169.             "countryCode" => $countryCode,
  170.             "city" => $note->getShipment() ? $note->getShipment()->getDeliveryAddress()['city'] : $note->getUser()->getFallbackAddress()->getCity(),
  171.             "zipCode" => $zipcode,
  172.             "mobileNumber" => $note->getUser()->getPhone(),
  173.             "email" => $note->getUser()->getEmail()
  174.         ];
  175.         $weight array_sum(array_map(fn(Batch $b) => $b->getWeight(), $parcel));
  176.         if(!$isReturnLabel){
  177.             $maxInsuranceValue max($note->getShipment()->getCarrier()->getInsurancePriceRanges()->map(fn(CarrierInsurancePriceRange $priceRange) => $priceRange->getInsuranceValue())->toArray());
  178.         }
  179.         $data = array(
  180.             "contractNumber" => $this->parameterBag->get('colissimo.login'),
  181.             "password" => $this->parameterBag->get('colissimo.password'),
  182.             "outputFormat" => [
  183.                 "x" => 0,
  184.                 "y" => 0,
  185.                 "outputPrintingType" => "PDF_10x15_300dpi"
  186.             ],
  187.             "letter" => [
  188.                 "service" => [
  189.                     "productCode" => $productCode,
  190.                     "depositDate" => (new \DateTime())->format('Y-m-d'),
  191.                     "orderNumber" => $note->getId(),
  192.                     "commercialName" => $this->brandService->getBrand()->getName()
  193.                 ],
  194.                 "parcel" => [
  195.                     "insuranceValue" => !$isReturnLabel && $note->getShipment()->isInsurance() ? intval(min($maxInsuranceValue$note->getShipment()->getInsuredValue() ?? array_sum(array_map(fn(Batch $batch) => $batch->getTotalAmount(), $note->getBatches()->toArray()))) * 100) : "0",
  196.                     "weight" => $weight ?: 1,
  197.                 ],
  198.                 "sender" => [
  199.                     "senderParcelRef" => ($isReturnLabel "SAV_" "") . $note->getCode(),
  200.                     "address" => $isReturnLabel $addresseeAddress $senderAddress
  201.                 ],
  202.                 "addressee" => [
  203.                     "addresseeParcelRef" => ($isReturnLabel "SAV_" "") . $note->getCode(),
  204.                     "address" => $isReturnLabel $senderAddress $addresseeAddress
  205.                 ]
  206.             ]
  207.         );
  208.         if (!$isReturnLabel && $note->getShipment()->getCarrier()->getIsServicePoint()) {
  209.             $data['letter']['parcel']['pickupLocationId'] = $note->getShipment()->getDeliveryAddress()['pickupLocationId'];
  210.         }
  211.         return ['data' => $data'batchesIds' => array_map(fn(Batch $b) => $b->getId(), $parcel)];
  212.     }
  213.     /**
  214.      * @param array $parcel
  215.      * @return Response
  216.      * @throws ClientExceptionInterface
  217.      * @throws RedirectionExceptionInterface
  218.      * @throws ServerExceptionInterface
  219.      * @throws TransportExceptionInterface
  220.      */
  221.     private function checkGenerateLabel(array $parcel): Response
  222.     {
  223.         return new Response($this->call('POST'endpointself::ENDPOINTS[__FUNCTION__], bodyParams$parcel['data'], batchesIds$parcel['batchesIds'])->getContent());
  224.     }
  225.     /**
  226.      * @param array $parcels
  227.      * @param DeliveryNote|null $note
  228.      * @return Response
  229.      * @throws ClientExceptionInterface
  230.      * @throws CrossReferenceException
  231.      * @throws Exception
  232.      * @throws FilterException
  233.      * @throws PdfParserException
  234.      * @throws PdfReaderException
  235.      * @throws PdfTypeException
  236.      * @throws RedirectionExceptionInterface
  237.      * @throws ServerExceptionInterface
  238.      * @throws TransportExceptionInterface
  239.      * @throws \Exception
  240.      */
  241.     public function concurrentMergeDownloads(array $parcels, ?DeliveryNote $note null): Response
  242.     {
  243.         $outPut = new Fpdi();
  244.         $outPut->setPrintFooter(false);
  245.         $outPut->setPrintHeader(false);
  246.         //Send all requests
  247.         $responses = [];
  248.         foreach ($parcels as $parcel) {
  249.             $responses[] = $this->call('POST'endpointself::ENDPOINTS['generateLabel'], bodyParams$parcel['data'], batchesIds$parcel['batchesIds']);
  250.         }
  251.         //Asynchronously handle them
  252.         foreach ($this->httpClient->stream($responses) as $response => $chunk) {
  253.             if ($chunk->isLast()) {
  254.                 $isBiggerLabel in_array($response->getInfo('user_data')['productCode'], ['A2P']);
  255.                 $isReturnLabel in_array($response->getInfo('user_data')['productCode'], ['CORE']);
  256.                 $batchesIds $response->getInfo('user_data')['batchesIds'];
  257.                 $raw $response->getContent();
  258.                 $content $this->getResponseSlicedToPDF($raw);
  259.                 $fileName uniqid() . "_label.pdf";
  260.                 file_put_contents(ConstantsService::TMP_PDFS_DIR $fileName$content);
  261.                 $outPut->AddPage(format: [100150]);
  262.                 $outPut->setSourceFile(ConstantsService::TMP_PDFS_DIR $fileName);
  263.                 $tplId $outPut->importPage(1);
  264.                 $outPut->Rect(00100150style'F'fill_color: [255255255]);
  265.                 if ($isReturnLabel$yOffset 3;
  266.                 else $yOffset $isBiggerLabel 15;
  267.                 $outPut->useTemplate($tplIdx$isBiggerLabel 3y$yOffsetheight140);
  268.                 if (!$isReturnLabel) {
  269.                     foreach ($batchesIds as $key => $value) {
  270.                         $batch $this->em->getRepository(Batch::class)->find($value);
  271.                         $trackingNumber $this->getTrackingNumber($raw);
  272.                         $batch->getDeliveryNote()->getShipment()->addTrackingNumber($trackingNumber);
  273.                         $this->labelService->generateLabel($batch->getDeliveryNote()->getShipment(), $trackingNumber);
  274.                         $this->em->persist($batch);
  275.                         $this->em->persist($batch->getDeliveryNote()->getShipment());
  276.                     }
  277.                 }
  278.                 unlink(ConstantsService::TMP_PDFS_DIR $fileName);
  279.             }
  280.         }
  281.         $result $outPut->Output(dest"S");
  282.         $this->em->flush();
  283.         return new Response($resultResponse::HTTP_OK, [
  284.             'Content-Type' => 'application/pdf',
  285.             'Content-Disposition' => 'inline; filename=label' . ($note '_' $note->getCode() : '') . '.pdf'
  286.         ]);
  287.     }
  288.     private function getResponseSlicedToPDF(string $input): string|Response
  289.     {
  290.         $start_tag "%PDF-1.3";
  291.         $end_tag "%%EOF";
  292.         $output "";
  293.         $start_pos strpos($input$start_tag);
  294.         $end_pos strpos($input$end_tag);
  295.         if ($start_pos !== false && $end_pos !== false) {
  296.             $end_pos += strlen($end_tag);
  297.             $output substr($input$start_pos$end_pos $start_pos);
  298.         }
  299.         return $output;
  300.     }
  301.     private function getTrackingNumber(string $input): string
  302.     {
  303.         preg_match('/\{"messages".*"pdfUrl":.*}}/'$input$matches);
  304.         if (!$matches) throw new Exception("Error while parsing tracking number.");
  305.         $json json_decode($matches[0]);
  306.         if (!isset($json->labelV2Response) || !isset($json->labelV2Response->parcelNumber)) throw new Exception("No tracking number found.");
  307.         return $json->labelV2Response->parcelNumber;
  308.     }
  309.     /**
  310.      * @param EstimationRequest $estimationRequest
  311.      * @return array
  312.      * @throws Exception
  313.      */
  314.     public function generateEstimationLabels(EstimationRequest $estimationRequest): array
  315.     {
  316.         try {
  317.             $trackingNumbers = [];
  318.             $pdfs = [];
  319.             for ($i 0$i $estimationRequest->getLabelQuantity(); $i++) {
  320.                 ['pdf' => $pdf'trackingNumber' => $trackingNumber] = $this->generateEstimationLabel($estimationRequest);
  321.                 $pdfs[] = $pdf;
  322.                 $trackingNumbers[] = $trackingNumber;
  323.             }
  324.             $pdf $this->pdfGenerationService->mergePdfs($pdfs);
  325.             return ['pdf' => $pdf'trackingNumbers' => $trackingNumbers];
  326.         } catch (Throwable) {
  327.             throw new Exception('Erreur lors de la génération des étiquettes');
  328.         }
  329.     }
  330.     private function generateEstimationLabel(EstimationRequest $estimationRequest): array
  331.     {
  332.         $maxInsuranceValue max($this->em->getRepository(Carrier::class)->findOneByInternalId(ConstantsService::CARRIER_ID_COLISSIMO)->getInsurancePriceRanges()->map(fn(CarrierInsurancePriceRange $priceRange) => $priceRange->getInsuranceValue())->toArray());
  333.         $data = array(
  334.             "contractNumber" => $this->parameterBag->get('colissimo.login'),
  335.             "password" => $this->parameterBag->get('colissimo.password'),
  336.             "outputFormat" => [
  337.                 "x" => 0,
  338.                 "y" => 0,
  339.                 "outputPrintingType" => "PDF_10x15_300dpi"
  340.             ],
  341.             "letter" => [
  342.                 "service" => [
  343.                     "productCode" => 'CORE',
  344.                     "depositDate" => (new \DateTime())->format('Y-m-d'),
  345.                     "orderNumber" => $estimationRequest->getId(),
  346.                     "commercialName" => $this->brandService->getBrand()->getName()
  347.                 ],
  348.                 "parcel" => [
  349.                     "insuranceValue" => $estimationRequest->getInsuredPrice() ? intval(min($estimationRequest->getInsuredPrice(), $maxInsuranceValue) * 100) : "0",
  350.                     "weight" => $estimationRequest->getWeight(),
  351.                 ],
  352.                 "sender" => [
  353.                     "senderParcelRef" => "ESTIM_" $estimationRequest->getId(),
  354.                     "address" => $this->getEstimationLabelSender($estimationRequest)
  355.                 ],
  356.                 "addressee" => [
  357.                     "addresseeParcelRef" => "ESTIM_" $estimationRequest->getId(),
  358.                     "address" => $this->getEstimationLabelAddressee()
  359.                 ]
  360.             ]
  361.         );
  362.         $content $this->call('POST'endpointself::ENDPOINTS['generateLabel'], bodyParams$data)->getContent();
  363.         $pdf $this->getResponseSlicedToPDF($content);
  364.         $trackingNumber $this->getTrackingNumber($content);
  365.         return ['pdf' => $pdf'trackingNumber' => $trackingNumber];
  366.     }
  367.     private function getEstimationLabelSender(EstimationRequest $estimationRequest): array
  368.     {
  369.         $addresseeFirstname trim($estimationRequest->getAddress()['firstname']);
  370.         $addresseeLastname trim($estimationRequest->getAddress()['lastname']);
  371.         return [
  372.             "lastName" => strlen($addresseeLastname) ? $addresseeLastname '.',
  373.             "firstName" => strlen($addresseeFirstname) ? $addresseeFirstname '.',
  374.             "line0" => $estimationRequest->getAddress()['address_line2'] ?? '',
  375.             "line1" => $estimationRequest->getAddress()['address_line1'] ?? '',
  376.             "line2" => $estimationRequest->getAddress()['address'],
  377.             "countryCode" => $estimationRequest->getAddress()['countryCode'],
  378.             "city" => $estimationRequest->getAddress()['city'],
  379.             "zipCode" => FormatExtension::formatZipCode($estimationRequest->getAddress()['zipcode']),
  380.             "mobileNumber" => '0600000000',
  381.             "email" => $estimationRequest->getUser()->getEmail()
  382.         ];
  383.     }
  384.     private function getEstimationLabelAddressee(): array
  385.     {
  386.         $brand $this->brandService->getBrand();
  387.         return [
  388.             "companyName" => $brand->getCompanyName(),
  389.             "line2" => $brand->getAddress()->getAddress(),
  390.             "countryCode" => $brand->getAddress()->getCountry()->getCode(),
  391.             "city" => $brand->getAddress()->getCity(),
  392.             "zipCode" => $brand->getAddress()->getZipcode(),
  393.             "mobileNumber" => $brand->getPhone(),
  394.             "email" => $brand->getContact()
  395.         ];
  396.     }
  397.     /**
  398.      * @param SupportRequest $supportRequest
  399.      * @return array
  400.      * @throws Exception
  401.      */
  402.     public function generateSupportLabels(SupportRequest $supportRequest): array
  403.     {
  404.         try {
  405.             $trackingNumbers = [];
  406.             $pdfs = [];
  407.             $parcels $this->getAllParcelParams($supportRequest->getBatches(), isReturnLabeltrue);
  408.             foreach ($parcels as $parcel) {
  409.                 ['pdf' => $pdf'trackingNumber' => $trackingNumber] = $this->generateSupportLabel($parcel['data']);
  410.                 $pdfs[] = $pdf;
  411.                 $trackingNumbers[] = $trackingNumber;
  412.             }
  413.             $pdf $this->pdfGenerationService->mergePdfs($pdfs);
  414.             return ['pdf' => $pdf'trackingNumbers' => $trackingNumbers];
  415.         } catch (Throwable $e) {
  416.             throw new Exception('Erreur lors de la génération des étiquettes');
  417.         }
  418.     }
  419.     private function generateSupportLabel(array $parcel): array
  420.     {
  421.         $content $this->call('POST'endpointself::ENDPOINTS['generateLabel'], bodyParams$parcel)->getContent();
  422.         $pdf $this->getResponseSlicedToPDF($content);
  423.         $trackingNumber $this->getTrackingNumber($content);
  424.         return ['pdf' => $pdf'trackingNumber' => $trackingNumber];
  425.     }
  426. }