<?php
namespace CioPaymentMethodCostcenter\Subscriber;
use CioCostcenter\Definition\Costcenter\CostcenterEntity;
use CioCostcenter\Definition\CostcenterAddress\CostcenterAddressEntity;
use CioCustomerPermissionGroups\Event\CustomerAclRolesEvent;
use CioCustomerPermissionGroups\Service\CustomerPermissionService;
use CioPaymentMethodCostcenter\Controller\ChooseAddressController;
use CioPaymentMethodCostcenter\Error\OnlyCostcenterAddressError;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
use Shopware\Core\Checkout\Cart\Order\CartConvertedEvent;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Customer\Event\CustomerLogoutEvent;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Event\ShopwareSalesChannelEvent;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Storefront\Event\StorefrontRenderEvent;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\EventDispatcher\Event;
class CheckoutSubscriber implements EventSubscriberInterface
{
private ?Request $request;
private EntityRepositoryInterface $orderRepository;
private EntityRepository $customerAddressRepository;
private EntityRepository $customerRepository;
private EntityRepository $costcenterAddressRepository;
private EntityRepository $costcenterRepository;
private Container $container;
private CustomerPermissionService $checkCustomerPermissionsService;
public function __construct(RequestStack $requestStack, EntityRepository $orderRepository, EntityRepository $customerAddressRepository, EntityRepository $customerRepository, EntityRepository $costcenterAddressRepository, EntityRepository $costcenterRepository, Container $container, CustomerPermissionService $checkCustomerPermissionsService)
{
$this->request = $requestStack->getCurrentRequest();
$this->orderRepository = $orderRepository;
$this->customerAddressRepository = $customerAddressRepository;
$this->customerRepository = $customerRepository;
$this->costcenterAddressRepository = $costcenterAddressRepository;
$this->costcenterRepository = $costcenterRepository;
$this->container = $container;
$this->checkCustomerPermissionsService = $checkCustomerPermissionsService;
}
public static function getSubscribedEvents(): array
{
// Return the events to listen to as array like this: <event to listen to> => <method to execute>
return [
CheckoutOrderPlacedEvent::class => 'onCheckoutOrderPlacedEvent',
StorefrontRenderEvent::class => 'onStorefrontRender',
CustomerLogoutEvent::class => 'onCustomerLogoutEvent',
CartConvertedEvent::class => 'onCartConvertedEvent',
CustomerAclRolesEvent::class => 'onCustomerAclRolesEvent'
];
}
public function onCustomerAclRolesEvent(CustomerAclRolesEvent $event)
{
$event->addRoles([
[
'title' => 'CHECKOUT_CAN_USE_PROFILE_ADDRESS',
'description' => 'Kunde kann im Profil gepflegte Adressen verwenden.'
],
[
'title' => 'CHECKOUT_CAN_USE_COSTCENTER_ADDRESS',
'description' => 'Kunde kann die seinen Kostenstellen zugehörigen Adressen als Lieferadresse verwenden.'
],
[
'title' => 'ALL_COSTCODES',
'description' => 'Kunde kann im Checkout nicht nur seine im Account hinterlegten Kostenstellen verwenden.'
]
]);
}
public function onCheckoutOrderPlacedEvent(CheckoutOrderPlacedEvent $event)
{
$customFields = [];
if ($currentCustomFields = $event->getOrder()->getCustomFields()) {
if (is_array($currentCustomFields)) {
$customFields = $currentCustomFields;
}
}
if ($this->request) {
$customFields = array_merge($customFields, [
'cio_costcenter' => $this->request->request->get('cio_costcenter')
]);
}
$event->getOrder()->setCustomFields($customFields);
try {
$this->orderRepository->update([
[
'id' => $event->getOrder()->getId(),
'customFields' => $customFields
]
], $event->getContext());
} catch (\Throwable $e) {
}
}
public function onStorefrontRender(StorefrontRenderEvent $event)
{
$route = $event->getRequest()->get('_route');
$eventParams = $event->getParameters();
if ($route == 'frontend.checkout.confirm.page') {
$session = $event->getRequest()->getSession();
$currentCustomer = $this->getCurrentCustomer($event);
$costcenterAdresses = [];
$costcenters = [];
$personallyCostcentersOfCustomer = [];
$costcenterBillingAdresses = [];
$overrideBillingAddress = null;
$defaultBillingAddress = $currentCustomer->getDefaultBillingAddress();
$currentlySelectedOverrideBillingAddress = null;
/** @var Cart $cart */
$cart = $event->getParameters()['page']->getCart();
if ($currentCustomer instanceof CustomerEntity) {
if ($currentCustomer->getExtension('costcenters') instanceof EntityCollection) {
$costcenters = $currentCustomer->getExtension('costcenters')->getElements();
$personallyCostcentersOfCustomer = $costcenters;
}
if ($this->checkCustomerPermissionsService->check($event->getSalesChannelContext()->getCustomer(), 'ALL_COSTCODES', $event->getContext()) === true) {
$costcenters = $this->costcenterRepository->search(new Criteria(), $event->getContext())->getElements();
}
usort($costcenters, function (CostcenterEntity $a, CostcenterEntity $b) {
/** @var $a CostcenterEntity */
/** @var $b CostcenterEntity */
return strcmp($a->getNumber(), $b->getNumber());
});
usort($personallyCostcentersOfCustomer, function (CostcenterEntity $a, CostcenterEntity $b) {
/** @var $a CostcenterEntity */
/** @var $b CostcenterEntity */
return strcmp($a->getNumber(), $b->getNumber());
});
// load all costcenter addresses for costcenter
$costcenterIds = [];
foreach ($costcenters as $costcenter) {
if ($costcenter instanceof CostcenterEntity) {
$costcenterIds[] = $costcenter->getId();
}
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsAnyFilter('costcenterId', $costcenterIds));
$criteria->addAssociation('country');
$costcenterAddressStorage = [];
/** @var CostcenterAddressEntity $costcenterAddress */
foreach ($this->costcenterAddressRepository->search($criteria, $event->getContext())->getElements() as $costcenterAddress) {
if (!isset($costcenterAddressStorage[$costcenterAddress->getCostcenterId()])) {
$costcenterAddressStorage[$costcenterAddress->getCostcenterId()] = [];
}
$costcenterAddressStorage[$costcenterAddress->getCostcenterId()][] = $costcenterAddress;
}
/** @var CostcenterEntity $costcenter */
foreach ($costcenters as $costcenter) {
$costcenterCompany = $costcenter->getCompany();
$costcenterBillingAdresses[$costcenter->getNumber()] = [
'id' => $costcenterCompany->getId(),
'costcenterNummer' => $costcenter->getNumber(),
'salutationName' => $costcenterCompany->getSalutation() ? $costcenterCompany->getSalutation()->getDisplayName() : '',
'company' => $costcenterCompany->getCompany() ?? '',
'department' => $costcenterCompany->getDepartment() ?? '',
'firstName' => $costcenterCompany->getFirstName(),
'lastName' => $costcenterCompany->getLastName(),
'street' => $costcenterCompany->getStreet(),
'zipcode' => $costcenterCompany->getZipcode(),
'city' => $costcenterCompany->getCity(),
'country' => $costcenterCompany->getCountry() ? $costcenterCompany->getCountry()->getName() : '',
'data' => json_encode([
'id' => $costcenterCompany->getId(),
'salutationName' => $costcenterCompany->getSalutation() ? $costcenterCompany->getSalutation()->getDisplayName() : '',
'company' => $costcenterCompany->getCompany() ?? '',
'department' => $costcenterCompany->getDepartment() ?? '',
'firstName' => $costcenterCompany->getFirstName(),
'lastName' => $costcenterCompany->getLastName(),
'street' => $costcenterCompany->getStreet(),
'zipcode' => $costcenterCompany->getZipcode(),
'city' => $costcenterCompany->getCity(),
'country' => $costcenterCompany->getCountry() ? $costcenterCompany->getCountry()->getName() : '',
'costcenterId' => $costcenter->getId(),
'costcenterNumber' => $costcenter->getNumber(),
'salutationId' => $costcenterCompany->getSalutationId(),
'countryId' => $costcenterCompany->getCountry(),
'countryStateId' => null,
'title' => '',
'phoneNumber' => '',
'additionalAddressLine1' => '',
'additionalAddressLine2' => ''
])
];
if (!isset($costcenterAddressStorage[$costcenter->getId()])) {
$costcenterAddressStorage[$costcenter->getId()] = [];
}
foreach ($costcenterAddressStorage[$costcenter->getId()] as $costcenterAddress) {
/** @var CostcenterAddressEntity $costcenterAddress */
$costcenterAdresses[] = [
'id' => $costcenterAddress->getId(),
'costcenterId' => $costcenter->getId(),
'costcenterNumber' => $costcenter->getNumber(),
'company' => $costcenterAddress->getCompany(),
'department' => $costcenterAddress->getDepartment(),
'firstName' => $event->getSalesChannelContext()->getCustomer()->getFirstName(),
'lastName' => $event->getSalesChannelContext()->getCustomer()->getLastName(),
'street' => $costcenterAddress->getStreet(),
'zipcode' => $costcenterAddress->getZipcode(),
'city' => $costcenterAddress->getCity(),
'country' => $costcenterAddress->getCountry()?->getName(),
'label' => join(' | ', [
($costcenterAddress->getCompany() ? $costcenterAddress->getCompany() : $costcenterAddress->getFirstName() . " " . $costcenterAddress->getLastName()),
$costcenterAddress->getStreet(),
$costcenterAddress->getZipcode(),
$costcenterAddress->getCity(),
$costcenterAddress->getCountry()?->getName()
]),
'data' => json_encode([
'id' => $costcenterAddress->getId(),
'company' => $costcenterAddress->getCompany(),
'department' => $costcenterAddress->getDepartment(),
'firstName' => $event->getSalesChannelContext()->getCustomer()->getFirstName(),
'lastName' => $event->getSalesChannelContext()->getCustomer()->getLastName(),
'street' => $costcenterAddress->getStreet(),
'zipcode' => $costcenterAddress->getZipcode(),
'city' => $costcenterAddress->getCity(),
'country' => $costcenterAddress->getCountry()?->getName(),
'costcenterId' => $costcenter->getId(),
'costcenterNumber' => $costcenter->getNumber(),
'salutationId' => '',
'countryId' => '',
'countryStateId' => '',
'title' => '',
'phoneNumber' => '',
'additionalAddressLine1' => '',
'additionalAddressLine2' => ''
])
];
}
}
if (isset($eventParams['context']) && $eventParams['context']->getPaymentMethod() && $eventParams['context']->getPaymentMethod()->getName() == 'Kostenstelle') {
$overrideBillingAddress = true;
}
}
if ($session->get(ChooseAddressController::ADDRESS_TYPE_SESSION_DATA_KEY) === null) {
if ($this->checkCustomerPermissionsService->check($event->getSalesChannelContext()->getCustomer(), 'CHECKOUT_CAN_USE_PROFILE_ADDRESS', $event->getContext()) === false) {
if (count($costcenterAdresses) > 0) {
$firstCostcenterAddress = $costcenterAdresses[array_key_first($costcenterAdresses)];
$firstCostcenterAddressData = json_decode($firstCostcenterAddress['data'], true);
$session->set(ChooseAddressController::ADDRESS_TYPE_SESSION_DATA_KEY, json_encode($firstCostcenterAddressData));
$session->set(ChooseAddressController::ADDRESS_TYPE_SESSION_TYPE_KEY, ChooseAddressController::ADDRESS_TYPE_COSTCENTER);
} else {
$cart->addErrors(new OnlyCostcenterAddressError());
}
}
}
$addressResultCollection = $this->customerAddressRepository->search(
(new Criteria())->addFilter(new EqualsFilter('customerId', $event->getSalesChannelContext()->getCustomer()->getId())),
$event->getContext()
);
$sessionType = null;
try {
$sessionData = $event->getRequest()->getSession()->get(ChooseAddressController::ADDRESS_TYPE_SESSION_DATA_KEY);
$sessionType = $event->getRequest()->getSession()->get(ChooseAddressController::ADDRESS_TYPE_SESSION_TYPE_KEY);
$currentSelectedOverrideAddress = json_decode($sessionData, true);
} catch (\Throwable $throwable) {
}
if ($sessionType === ChooseAddressController::ADDRESS_TYPE_COSTCENTER) {
$currentlySelectedOverrideAddress = [
'type' => ChooseAddressController::ADDRESS_TYPE_COSTCENTER,
'data' => json_decode($session->get(ChooseAddressController::ADDRESS_TYPE_SESSION_DATA_KEY), true)
];
} else {
$currentlySelectedOverrideAddress = [
'type' => ChooseAddressController::ADDRESS_TYPE_CUSTOMER,
'data' => null
];
}
$selectedCostcenterIdOfCustomer = isset($costcenters[0]) ? $costcenters[0]->get('id') : null;
if ($currentlySelectedOverrideAddress['data'] && $currentlySelectedOverrideAddress['data']['costcenterId']) {
$selectedCostcenterIdOfCustomer = $currentlySelectedOverrideAddress['data']['costcenterId'];
} elseif($personallyCostcentersOfCustomer) {
$selectedCostcenterIdOfCustomer = $personallyCostcentersOfCustomer[0]->get('id');
}
// type of current selected address (default customer address or costcenter address)
$event->setParameter('currentAddressType', $sessionType);
// list of all default customer addresses
$event->setParameter('allAvailableDefaultShippingAddresses', $addressResultCollection);
// list of addresses from costcenters
$event->setParameter('addressBookShippingAddressesForCheckout', $costcenterAdresses);
// array of currently showed override address
$event->setParameter('currentlySelectedOverrideAddress', $currentlySelectedOverrideAddress);
// array of available costcenters
$event->setParameter('allAvailableCostcentersOfCustomer', $costcenters);
$event->setParameter('selectedCostcenterIdOfCustomer', $selectedCostcenterIdOfCustomer);
// array of available billing addresses from costcenter companys
$event->setParameter('allAvailableDefaultBillingAddress', $costcenterBillingAdresses);
// array of currently address key
$event->setParameter('overrideBillingAddress', $overrideBillingAddress);
}
}
public function onCustomerLogoutEvent(CustomerLogoutEvent $event)
{
// reset selection in session
$this->request->getSession()->set(ChooseAddressController::ADDRESS_TYPE_SESSION_TYPE_KEY, null);
$this->request->getSession()->set(ChooseAddressController::ADDRESS_TYPE_SESSION_DATA_KEY, null);
}
public function onCartConvertedEvent(CartConvertedEvent $event)
{
$request = $this->request;
$isOverrideActive = $request->get('cioOverrideShippingAddressActive') !== null;
$addressOverrideData = $request->get('cioOverrideShippingAddressData');
$billingAddressOverrideData = $request->get('cioOverrideBillingAddressData');
if ($addressOverrideData || $billingAddressOverrideData) {
try {
/** @var EntityRepositoryInterface $orderRepository */
$orderRepository = $this->container->get('order_address.repository');
/** @var EntityRepositoryInterface $orderDeliveryRepository */
$orderDeliveryRepository = $this->container->get('order_delivery.repository');
/** @var EntityRepositoryInterface $salutationRepository */
$salutationRepository = $this->container->get('salutation.repository');
/** @var EntityRepositoryInterface $countryStateRepository */
$countryStateRepository = $this->container->get('country_state.repository');
/** @var EntityRepositoryInterface $countryRepository */
$countryRepository = $this->container->get('country.repository');
$originalOrder = $event->getConvertedCart();
if ($billingAddressOverrideData) {
$currentCustomer = $this->getCurrentCustomer($event);
$originalOrderBillingAddress = $currentCustomer->getDefaultBillingAddress();
$currentSelectedOverrideBillingAddress = json_decode($billingAddressOverrideData, true);
$currentSelectedOverrideBillingAddress = json_decode($currentSelectedOverrideBillingAddress, true);
$salutation = null;
if ($currentSelectedOverrideBillingAddress['salutationId'] && Uuid::isValid($currentSelectedOverrideBillingAddress['salutationId'])) {
$salutation = $salutationRepository->search(new Criteria([$currentSelectedOverrideBillingAddress['salutationId']]), $event->getContext())->first();
}
$salutationId = $salutation ? $salutation->getId() : $originalOrderBillingAddress->getSalutationId();
$countryState = null;
if ($currentSelectedOverrideBillingAddress['countryStateId'] && Uuid::isValid($currentSelectedOverrideBillingAddress['countryStateId'])) {
$countryState = $countryStateRepository->search(new Criteria([$currentSelectedOverrideBillingAddress['countryStateId']]), $event->getContext())->first();
}
$countryStateId = $countryState ? $countryState->getId() : $originalOrderBillingAddress->getCountryStateId();
$country = null;
if ($currentSelectedOverrideBillingAddress['countryId'] && Uuid::isValid($currentSelectedOverrideBillingAddress['countryId'])) {
$country = $countryRepository->search(new Criteria([$currentSelectedOverrideBillingAddress['countryId']]), $event->getContext())->first();
}
$countryId = $country ? $country->getId() : $originalOrderBillingAddress->getCountryId();
$newOrderAddressId = Uuid::randomHex();
$originalOrder['addresses'][0] = [
'id' => $newOrderAddressId,
'company' => $currentSelectedOverrideBillingAddress['company'],
'department' => $currentSelectedOverrideBillingAddress['department'],
'title' => $currentSelectedOverrideBillingAddress['title'],
'firstName' => $currentSelectedOverrideBillingAddress['firstName'],
'lastName' => $currentSelectedOverrideBillingAddress['lastName'],
'street' => $currentSelectedOverrideBillingAddress['street'],
'zipcode' => (string)$currentSelectedOverrideBillingAddress['zipcode'],
'phoneNumber' => (string)$currentSelectedOverrideBillingAddress['phoneNumber'],
'additionalAddressLine1' => (string)$currentSelectedOverrideBillingAddress['additionalAddressLine1'],
'additionalAddressLine2' => (string)$currentSelectedOverrideBillingAddress['additionalAddressLine2'],
'city' => $currentSelectedOverrideBillingAddress['city'],
'salutationId' => $salutationId,
'countryStateId' => $countryStateId,
'countryId' => $countryId
];
$originalOrder['billingAddressId'] = $newOrderAddressId;
}
if ($addressOverrideData) {
$currentSelectedOverrideAddress = json_decode($addressOverrideData, true);
/** @var array $originalOrderShippingAddress */
$originalOrderShippingAddress = $event->getConvertedCart()['deliveries'][0]['shippingOrderAddress'];
// start validate foreign keys or fallback to default
$salutation = null;
if ($currentSelectedOverrideAddress['salutationId'] && Uuid::isValid($currentSelectedOverrideAddress['salutationId'])) {
$salutation = $salutationRepository->search(new Criteria([$currentSelectedOverrideAddress['salutationId']]), $event->getContext())->first();
}
$salutationId = $salutation ? $salutation->getId() : (array_key_exists('salutationId', $originalOrderShippingAddress) ? $originalOrderShippingAddress['salutationId'] : null);
$countryState = null;
if ($currentSelectedOverrideAddress['countryStateId'] && Uuid::isValid($currentSelectedOverrideAddress['countryStateId'])) {
$countryState = $countryStateRepository->search(new Criteria([$currentSelectedOverrideAddress['countryStateId']]), $event->getContext())->first();
}
$countryStateId = $countryState ? $countryState->getId() : (array_key_exists('countryStateId', $originalOrderShippingAddress) ? $originalOrderShippingAddress['countryStateId'] : null);
$country = null;
if ($currentSelectedOverrideAddress['countryId'] && Uuid::isValid($currentSelectedOverrideAddress['countryId'])) {
$country = $countryRepository->search(new Criteria([$currentSelectedOverrideAddress['countryId']]), $event->getContext())->first();
}
$countryId = $country ? $country->getId() : (array_key_exists('countryId', $originalOrderShippingAddress) ? $originalOrderShippingAddress['countryId'] : null);
// end validate foreign keys or fallback to default
$newOrderAddressId = Uuid::randomHex();
$originalOrder['deliveries'][0]['shippingOrderAddress'] = [
'id' => $newOrderAddressId,
'company' => $currentSelectedOverrideAddress['company'],
'department' => $currentSelectedOverrideAddress['department'],
'title' => $currentSelectedOverrideAddress['title'],
'firstName' => $currentSelectedOverrideAddress['firstName'],
'lastName' => $currentSelectedOverrideAddress['lastName'],
'street' => $currentSelectedOverrideAddress['street'],
'zipcode' => (string)$currentSelectedOverrideAddress['zipcode'],
'phoneNumber' => (string)$currentSelectedOverrideAddress['phoneNumber'],
'additionalAddressLine1' => (string)$currentSelectedOverrideAddress['additionalAddressLine1'],
'additionalAddressLine2' => (string)$currentSelectedOverrideAddress['additionalAddressLine2'],
'city' => $currentSelectedOverrideAddress['city'],
'salutationId' => $salutationId,
'countryStateId' => $countryStateId,
'countryId' => $countryId
];
if ($originalOrderShippingAddress['id'] === $originalOrder['billingAddressId']) {
$originalOrder['billingAddressId'] = $originalOrderShippingAddress['id'];
$originalOrder['addresses'][0] = $originalOrderShippingAddress;
}
}
$event->setConvertedCart($originalOrder);
} catch (\Throwable $throwable) {
dd($throwable);
}
}
$request->getSession()->set(ChooseAddressController::ADDRESS_TYPE_SESSION_TYPE_KEY, null);
$request->getSession()->set(ChooseAddressController::ADDRESS_TYPE_SESSION_DATA_KEY, null);
}
public function getCurrentCustomer(ShopwareSalesChannelEvent $event): ?CustomerEntity
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('id', $event->getSalesChannelContext()->getCustomer()->getId()));
$criteria->addAssociation('costcenters');
$criteria->addAssociation('defaultBillingAddress');
$criteria->addAssociation('company.salutation');
return $this->customerRepository->search($criteria, $event->getContext())->first();
}
}