vendor/store.shopware.com/wexoshipping/src/Subscriber/Checkout.php line 78

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Wexo\Shipping\Subscriber;
  3. use Shopware\Core\Checkout\Cart\Error\ErrorCollection;
  4. use Shopware\Core\Checkout\Shipping\Aggregate\ShippingMethodPrice\ShippingMethodPriceCollection;
  5. use Shopware\Core\Checkout\Shipping\ShippingMethodEntity;
  6. use Shopware\Core\Framework\Uuid\Uuid;
  7. use Shopware\Core\System\SystemConfig\SystemConfigService;
  8. use Shopware\Storefront\Event\RouteRequest\ShippingMethodRouteRequestEvent;
  9. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
  10. use Symfony\Component\Asset\PackageInterface;
  11. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  12. use Symfony\Contracts\Translation\TranslatorInterface;
  13. use Wexo\Shipping\Core\Content\Cart\CartExtension;
  14. use Wexo\Shipping\Core\Content\ShippingMethodDeadline\ShippingMethodDeadlineEntity;
  15. use Wexo\Shipping\Core\Content\ShippingMethodType\ShippingMethodTypeEntity;
  16. use Wexo\Shipping\Service\ShippingDeadlineFormatter;
  17. use Wexo\Shipping\Service\ShippingPriceCalculator;
  18. use Wexo\Shipping\Struct\ParcelShopConfiguration;
  19. use Wexo\Shipping\Service\TypeOptionService;
  20. use Wexo\Shipping\Struct\ShippingPriceRange;
  21. /**
  22.  * Class Checkout
  23.  * @package Wexo\Shipping\Subscriber
  24.  */
  25. class Checkout implements EventSubscriberInterface
  26. {
  27.     const UNIFIED_VIEW_TYPE_KEY 'unified';
  28.     private TypeOptionService $typeOptionService;
  29.     private SystemConfigService $systemConfigService;
  30.     private TranslatorInterface $translator;
  31.     private ShippingPriceCalculator $shippingPriceCalculator;
  32.     private ShippingDeadlineFormatter $shippingDeadlineFormatter;
  33.     private PackageInterface $package;
  34.     /**
  35.      * Checkout constructor.
  36.      * @param TypeOptionService $typeOptionService
  37.      * @param SystemConfigService $systemConfigService
  38.      * @param TranslatorInterface $translator
  39.      * @param ShippingPriceCalculator $shippingPriceCalculator
  40.      * @param ShippingDeadlineFormatter $shippingDeadlineFormatter
  41.      * @param PackageInterface $package
  42.      */
  43.     public function __construct(
  44.         TypeOptionService $typeOptionService,
  45.         SystemConfigService $systemConfigService,
  46.         TranslatorInterface $translator,
  47.         ShippingPriceCalculator $shippingPriceCalculator,
  48.         ShippingDeadlineFormatter $shippingDeadlineFormatter,
  49.         PackageInterface $package
  50.     ) {
  51.         $this->typeOptionService $typeOptionService;
  52.         $this->systemConfigService $systemConfigService;
  53.         $this->translator $translator;
  54.         $this->shippingPriceCalculator $shippingPriceCalculator;
  55.         $this->shippingDeadlineFormatter $shippingDeadlineFormatter;
  56.         $this->package $package;
  57.     }
  58.     /**
  59.      * @return string[]
  60.      */
  61.     public static function getSubscribedEvents()
  62.     {
  63.         return [
  64.             CheckoutConfirmPageLoadedEvent::class => 'addShippingMethodData',
  65.             ShippingMethodRouteRequestEvent::class => 'addShippingMethodType'
  66.         ];
  67.     }
  68.     /**
  69.      * @param ShippingMethodRouteRequestEvent $event
  70.      */
  71.     public function addShippingMethodType(ShippingMethodRouteRequestEvent $event): void
  72.     {
  73.         $event->getCriteria()->addAssociation('shippingMethodType');
  74.         $event->getCriteria()->addAssociation('prices');
  75.     }
  76.     /**
  77.      * @param CheckoutConfirmPageLoadedEvent $event
  78.      */
  79.     public function addShippingMethodData(CheckoutConfirmPageLoadedEvent $event): void
  80.     {
  81.         $this->addShippingMethodDeadlines($event);
  82.         $this->addShippingMethodTypeConfiguration($event);
  83.     }
  84.     /**
  85.      * @param CheckoutConfirmPageLoadedEvent $event
  86.      */
  87.     public function addShippingMethodDeadlines(CheckoutConfirmPageLoadedEvent $event): void
  88.     {
  89.         foreach ($event->getPage()->getShippingMethods() as $shippingMethod) {
  90.             if ($shippingMethod->hasExtension('shippingMethodDeadline')) {
  91.                 /** @var ShippingMethodDeadlineEntity $deadline */
  92.                 $deadline $shippingMethod->getExtension('shippingMethodDeadline');
  93.                 $formattedDeadline $this->shippingDeadlineFormatter->getFormattedDeadline($deadline);
  94.                 $shippingMethod->addExtension('formattedDeadline'$formattedDeadline);
  95.             }
  96.         }
  97.     }
  98.     /**
  99.      * @param CheckoutConfirmPageLoadedEvent $event
  100.      */
  101.     public function addShippingMethodTypeConfiguration(CheckoutConfirmPageLoadedEvent $event): void
  102.     {
  103.         $shippingMethods $event->getPage()->getSalesChannelShippingMethods();
  104.         $shippingAddress $event->getSalesChannelContext()->getCustomer()->getActiveShippingAddress();
  105.         $parcelShopConfigurations = [];
  106.         $cart $event->getPage()->getCart();
  107.         $cartErrors $cart->getErrors();
  108.         $cart->setErrors(new ErrorCollection);
  109.         $clonedCart = clone $cart;
  110.         foreach ($shippingMethods as $shippingMethod) {
  111.             if (!$event->getPage()->getShippingMethods()->get($shippingMethod->getId())) {
  112.                 continue;
  113.             }
  114.             $shippingCosts $this->shippingPriceCalculator->getShippingCostsForCart(
  115.                 $clonedCart,
  116.                 $shippingMethod,
  117.                 $this->shippingPriceCalculator
  118.                     ->createSalesChannelContextWithShippingMethod($event->getSalesChannelContext(), $shippingMethod)
  119.             );
  120.             if ($shippingCosts) {
  121.                 $pageShippingMethod $event->getPage()->getShippingMethods()->get($shippingMethod->getId());
  122.                 if ($pageShippingMethod) {
  123.                     $pageShippingMethod->addExtension('calculatedPrice'$shippingCosts);
  124.                 }
  125.             }
  126.             $shippingMethodType $shippingMethod->getExtension('shippingMethodType');
  127.             if (!$shippingMethodType) {
  128.                 continue;
  129.             }
  130.             $typeKey $shippingMethodType->getTypeKey();
  131.             $optionType $this->typeOptionService->getType($typeKey);
  132.             $parcelShopConfigurations[$typeKey] = [
  133.                 'shippingMethodId' => $shippingMethod->getId(),
  134.                 'typeKey' => $typeKey,
  135.                 'street' => $shippingAddress->getStreet(),
  136.                 'zipcode' => $shippingAddress->getZipcode(),
  137.                 'countryCode' => $shippingAddress->getCountry()->getIso(),
  138.                 'amount' => $this->getNumberOfShopsToLoad($event->getSalesChannelContext()->getSalesChannel()
  139.                     ->getId()),
  140.                 'images' => [
  141.                     $typeKey => $this->package->getUrl($optionType $optionType->getLogoUrl() : '')
  142.                 ],
  143.                 'shippingCosts' => [
  144.                     $typeKey => [
  145.                         'total' => $shippingCosts->getTotalPrice(),
  146.                         'currency' => $event->getSalesChannelContext()->getCurrency()->getSymbol()
  147.                     ]
  148.                 ],
  149.                 'translations' => $this->getTranslations()
  150.             ];
  151.         }
  152.         $selectedParcelShop null;
  153.         if ($event->getPage()->getCart()->hasExtension(CartExtension::KEY)) {
  154.             $selectedParcelShop $event->getPage()->getCart()->getExtension(CartExtension::KEY)->parcelShop;
  155.         }
  156.         $salesChannelId $event->getSalesChannelContext()->getSalesChannel()->getId();
  157.         if (!empty($parcelShopConfigurations) && $this->isUnifiedView($salesChannelId)) {
  158.             $this->addUnifiedShippingMethodConfiguration($event$parcelShopConfigurations);
  159.         }
  160.         $event->getPage()->addExtension(
  161.             'shipping_method_type_configuration',
  162.             new ParcelShopConfiguration($parcelShopConfigurations$selectedParcelShop)
  163.         );
  164.         $cart->setErrors($cartErrors);
  165.     }
  166.     /**
  167.      * Create a "fake" shipping method to add to the checkout
  168.      * @param CheckoutConfirmPageLoadedEvent $event
  169.      * @param $parcelShopConfigurations
  170.      */
  171.     protected function addUnifiedShippingMethodConfiguration(
  172.         CheckoutConfirmPageLoadedEvent $event,
  173.         &$parcelShopConfigurations
  174.     ): void {
  175.         $salesChannelId $event->getSalesChannelContext()->getSalesChannel()->getId();
  176.         $unifiedShippingMethod = new ShippingMethodEntity(new ShippingMethodPriceCollection());
  177.         $unifiedShippingMethod->setUniqueIdentifier(Uuid::randomHex());
  178.         $unifiedShippingMethod->setId(Uuid::randomHex());
  179.         $unifiedShippingMethod->setTranslated(['name' => $this->getUnifiedViewName($salesChannelId)]);
  180.         $unifiedShippingMethod->setName($this->getUnifiedViewName($salesChannelId));
  181.         $unifiedShippingMethod->setActive(true);
  182.         $unifiedShippingMethodTypeEntity = new ShippingMethodTypeEntity();
  183.         $unifiedShippingMethodTypeEntity->setShippingMethod($unifiedShippingMethod);
  184.         $unifiedShippingMethodTypeEntity->setTypeKey(self::UNIFIED_VIEW_TYPE_KEY);
  185.         $unifiedShippingMethod->setExtensions(['shippingMethodType' => $unifiedShippingMethodTypeEntity]);
  186.         $shippingMethodIds = [];
  187.         $images = [];
  188.         $shippingCosts = [];
  189.         $minPrice null;
  190.         $maxPrice null;
  191.         foreach ($parcelShopConfigurations as $typeKey => $configuration) {
  192.             $shippingMethodIds[$typeKey] = $configuration['shippingMethodId'];
  193.             $images[$typeKey] = $configuration['images'][$typeKey];
  194.             $shippingCosts[$typeKey] = $configuration['shippingCosts'][$typeKey];
  195.             if (!isset($minPrice)) {
  196.                 $minPrice $shippingCosts[$typeKey]['total'];
  197.                 $maxPrice $shippingCosts[$typeKey]['total'];
  198.                 continue;
  199.             }
  200.             $minPrice min($minPrice$shippingCosts[$typeKey]['total']);
  201.             $maxPrice max($maxPrice$shippingCosts[$typeKey]['total']);
  202.         }
  203.         $shippingPriceRange = new ShippingPriceRange($minPrice$maxPrice);
  204.         $unifiedShippingMethod->addExtension('shippingPriceRange'$shippingPriceRange);
  205.         $event->getPage()->addExtension('unified_parcel_shop_method'$unifiedShippingMethod);
  206.         $shippingAddress $event->getSalesChannelContext()->getCustomer()->getActiveShippingAddress();
  207.         $parcelShopConfigurations = [
  208.             self::UNIFIED_VIEW_TYPE_KEY => [
  209.                 'shippingMethodIds' => $shippingMethodIds,
  210.                 'typeKey' => self::UNIFIED_VIEW_TYPE_KEY,
  211.                 'street' => $shippingAddress->getStreet(),
  212.                 'zipcode' => $shippingAddress->getZipcode(),
  213.                 'countryCode' => $shippingAddress->getCountry()->getIso(),
  214.                 'amount' => $this->getNumberOfShopsToLoad($event->getSalesChannelContext()->getSalesChannel()->getId()),
  215.                 'images' => $images,
  216.                 'shippingCosts' => $shippingCosts,
  217.                 'translations' => $this->getTranslations()
  218.             ]
  219.         ];
  220.     }
  221.     /**
  222.      * @param string|null $salesChannelId
  223.      * @return int
  224.      */
  225.     protected function getNumberOfShopsToLoad(?string $salesChannelId): int
  226.     {
  227.         return $this->systemConfigService->getInt(
  228.             'WexoShipping.config.numberOfShopsToLoad',
  229.             $salesChannelId
  230.         );
  231.     }
  232.     /**
  233.      * @param string|null $salesChannelId
  234.      * @return bool
  235.      */
  236.     protected function isUnifiedView(?string $salesChannelId): bool
  237.     {
  238.         return $this->systemConfigService->getBool(
  239.             'WexoShipping.config.unifiedView',
  240.             $salesChannelId
  241.         );
  242.     }
  243.     /**
  244.      * @param string|null $salesChannelId
  245.      * @return string
  246.      */
  247.     protected function getUnifiedViewName(?string $salesChannelId): string
  248.     {
  249.         return $this->systemConfigService->getString(
  250.             'WexoShipping.config.unifiedViewName',
  251.             $salesChannelId
  252.         );
  253.     }
  254.     /**
  255.      * @return array
  256.      */
  257.     protected function getTranslations(): array
  258.     {
  259.         return [
  260.             'select' => $this->translator->trans('shippingMethod.select'),
  261.             'selected' => $this->translator->trans('shippingMethod.selected'),
  262.             'showOnMap' => $this->translator->trans('shippingMethod.showOnMap'),
  263.             'freeLabel' => $this->translator->trans('labels.free'),
  264.             'validation' => [
  265.                 'parcelShopMissing' => $this->translator->trans('shippingMethod.validation.parcelShopMissing'),
  266.                 'shippingCommentMissing' => $this->translator->trans('shippingMethod.validation.shippingCommentMissing')
  267.             ]
  268.         ];
  269.     }
  270. }