<?php
namespace App\EventSubscriber\Shipping;
use App\Ecommerce\Model\Order;
use App\Model\Customer;
use App\Model\Product;
use App\Repository\OrderRepository;
use Factory\CartBundle\Classes\Traits\Controller\GetCartTrait;
use Factory\Shipping\CustomerServiceBundle\Classes\CSCarrier;
use Factory\ShippingBundle\Classes\Carrier\DeAddress;
use Factory\ShippingBundle\Classes\Carrier\SimplePackage;
use Factory\ShippingBundle\Classes\Carrier\UsAddress;
use Factory\ShippingBundle\Classes\ShippingEvents;
use Factory\ShippingBundle\Contract\Carrier\AddressContract;
use Factory\ShippingBundle\Contract\Carrier\PackageContract;
use Factory\ShippingBundle\Event\ShippingEvent;
use Pimcore;
use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Pimcore\Model\DataObject\Data\QuantityValue;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Pimcore\Model\WebsiteSetting;
use Pimcore\Model\DataObject\PriceSystemRule;
class CarrierRatesSubscriber implements EventSubscriberInterface
{
use GetCartTrait;
/**
* @var RequestStack
*/
protected $requestStack;
/**
* @var OrderRepository
*/
protected $orderRepository;
public static function getSubscribedEvents()
{
return [
ShippingEvents::GET_SINGLE_CARRIER_RATES_BEFORE => [
['updatePackageAndAddress', 10]
],
ShippingEvents::UPDATE_CARRIER_LIST => [
['updateCarriers', 20]
]
];
}
public function __construct(RequestStack $requestStack, OrderRepository $orderRepository)
{
$this->requestStack = $requestStack;
$this->orderRepository = $orderRepository;
}
/**
* This method is used by GetCartTrait
*
* @param string $serviceName
*
* @return object
*/
protected function get(string $serviceName)
{
return Pimcore::getContainer()->get($serviceName);
}
/**
* @return null|Customer
*/
protected function getUser()
{
$env = Factory::getInstance()->getEnvironment();
if ($env->getCurrentUserId() > 0) {
return Customer::getById($env->getCurrentUserId());
}
return null;
}
public function updatePackageAndAddress(ShippingEvent $event)
{
if ($this->isRequestFromCsr()) {
// Pass to next listener
return;
}
$event->setArgument(
'package',
$this->getPackage($event->getArgument('package'), $event)
);
$event->setArgument(
'sourceAddress',
$this->getSourceAddress($event->getArgument('sourceAddress'), $event, 'default')
);
$event->setArgument(
'destinationAddress',
$this->getDestinationAddress($event->getArgument('destinationAddress'), $event)
);
$event->setArgument(
'options',
$this->getOptions($event->getArgument('options'), $event)
);
}
public function updateCarriers(ShippingEvent $event) {
$updatedCarrierList = $event->getSubject();
$unsetCSShipping = false;
if ($this->isQuoteProcess()) {
$user = $this->getUser();
if ($user === null) {
return;
}
$addressId = $this->getRequest()->cookies->get('__billing_address_id');
if ($addressId == 'customer') {
$address = $user->getDefaultBillingAddressObject();
} else {
$address = $user->getAddress($addressId);
}
$division = $this->getRequest()->cookies->get('__division');
$isEU = false;
if ($address !== null && $division == 'Neuroscience') {
$orderPriceSystemRule = $this->getAddressPricingRule($division);
$countryCode = $address->getCountry();
$isEU = $this->getAddressIsEU($orderPriceSystemRule, $countryCode);
}
if ($isEU) {
foreach ($event->getSubject() as $key => $carrier) {
if (!$carrier instanceof CSCarrier) {
unset($updatedCarrierList[$key]);
}
}
} else {
$unsetCSShipping = true;
}
} else {
$unsetCSShipping = true;
}
if ($unsetCSShipping) {
foreach ($event->getSubject() as $key => $carrier) {
if ($carrier instanceof CSCarrier) {
unset($updatedCarrierList[$key]);
}
}
}
$event->setSubject($updatedCarrierList);
}
private function getAddressPricingRule($division)
{
$priceSystemRule = new PriceSystemRule\Listing();
$priceSystemRule->addConditionParam("`Name` LIKE ?", '%'.$division.'%');
return $priceSystemRule->current();
}
private function getAddressIsEU($orderPriceSystemRule, $countryCode)
{
$actions = $orderPriceSystemRule->getActions();
foreach ($actions as $action) {
if ($action->getLabel() == 'Europe') {
if (in_array($countryCode, $action->getCountries())) {
return true;
}
return false;
}
}
return false;
}
protected function getPackage(PackageContract $package, ShippingEvent $event)
{
$weights = [];
foreach ($this->getPackageItems() as $item) {
$weights[] = [
'weight' => $item->getProduct()->getWeight(),
'quantity' => $item->getCount()
];
}
$package->setWeight($this->addQuantityValues($weights,'lb'));
return $package;
}
protected function getSourceAddress(AddressContract $sourceAddress, ShippingEvent $event, string $destRegion = 'default')
{
if ($setting = WebsiteSetting::getByName($destRegion.'_storage_city')) {
$sourceAddress->setCity($setting->getData() ?? '');
}
if ($setting = WebsiteSetting::getByName($destRegion.'_storage_zip_code')) {
$sourceAddress->setZipCode($setting->getData() ?? '');
}
if ($setting = WebsiteSetting::getByName($destRegion.'_storage_country')) {
$sourceAddress->setCountryCode($setting->getData() ?? '');
}
return $sourceAddress;
}
protected function getDestinationAddress(AddressContract $destinationAddress, ShippingEvent $event)
{
$address = $this->getAddress();
$destinationAddress->setCity($address->getCity());
$destinationAddress->setZipCode($address->getZipCode());
$destinationAddress->setCountryCode($address->getCountry());
$destinationAddress->setState($address->getState());
return $destinationAddress;
}
protected function getOptions(array $options, ShippingEvent $event)
{
return [];
}
protected function getAddress()
{
if ($this->isOrderingQuoteProcess()) {
$addressId = $this->getRequest()->cookies->get('__order_shipping_address_id');
} else {
$addressId = $this->getRequest()->cookies->get('__shipping_address_id');
}
if (!$user = $this->getUser()) {
throw new \Exception("Couldn't get customer info!");
}
$address = $user->getDefaultShippingAddressObject();
if ($addressId == 'delivery') {
$address = $this->getAddressFromOrder();
} else {
$address = $user->getAddress($addressId);
}
if (!$address) {
throw new \Exception("Customer must have default shipping address!");
}
return $address;
}
protected function getAddressFromOrder()
{
$order = $this->getOrderFromReferer();
if (!$order) {
return null;
}
return $this->orderRepository->createAddressFromOrder('delivery', $order);
}
protected function getOrderFromReferer()
{
$httpReferer = $this->getRequest()->server->get('HTTP_REFERER');
if (preg_match('/\/(order\-quote|checkout|quote)\/(\d+)\/(.*)$/', $httpReferer, $matches)) {
return Order::getById($matches[2]);
}
if (preg_match('/\/(order\-quote|checkout|quote)\/(\w+)\/(.*)$/', $httpReferer, $matches)) {
$path = base64_decode(hex2bin($matches[2]));
return Order::getByPath($path);
}
return null;
}
/**
* @param array $values
*
* @return QuantityValue
*/
private function addQuantityValues(array $values, string $unit)
{
$value = 0;
foreach ($values as $val) {
$value += $this->convert($val['weight'], $unit)->getValue() * $val['quantity'];
}
return new QuantityValue($value, $unit);
}
/**
* @return Product[]
*/
private function getPackageItems()
{
if ($this->isOrderingQuoteProcess()) {
$order = $this->getOrderFromReferer();
return $order->getItems();
}
$division = $this->getRequest()->cookies->get('__division');
$cartName = $division.'_cart';
$cart = $this->getCart($cartName);
return $cart->getItems();
}
/**
* @return boolean
*/
private function isOrderingQuoteProcess(): bool
{
$httpReferer = $this->getRequest()->server->get('HTTP_REFERER');
return preg_match('/\/(order\-quote)\/(.*)/', $httpReferer, $matches);
}
/**
* @return boolean
*/
private function isQuoteProcess(): bool
{
$httpReferer = $this->getRequest()->server->get('HTTP_REFERER');
return preg_match('/\/(quote)\/(.*)/', $httpReferer, $matches);
}
private function isRequestFromCsr()
{
return !!$this->getRequest()->get('order');
}
protected function getRequest(): Request
{
return $this->requestStack->getCurrentRequest();
}
/**
* @param QuantityValue $value
* @param string $targetUnit
*
* @return QuantityValue
*/
private function convert(QuantityValue $value, string $targetUnit)
{
return $value;
}
}