<?php declare(strict_types=1);
namespace Acris\Tax\Storefront\Subscriber;
use Acris\Tax\Components\Service\VatIdValidationService;
use Acris\Tax\Core\Checkout\Customer\Validation\Constraint\CustomerVatId;
use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Customer\Event\CustomerRegisterEvent;
use Shopware\Core\Checkout\Customer\Event\GuestCustomerRegisterEvent;
use Shopware\Core\Framework\Validation\BuildValidationEvent;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\PlatformRequest;
use Shopware\Core\System\Country\CountryEntity;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
class VatIdValidationSubscriber implements EventSubscriberInterface
{
/**
* @var SystemConfigService
*/
private $systemConfigService;
/**
* @var RequestStack
*/
private $requestStack;
/**
* @var VatIdValidationService
*/
private $vatIdValidationService;
/**
* @var array
*/
private $vatIds;
/**
* @var bool
*/
private $addressAllowed;
private int $count;
public function __construct(SystemConfigService $systemConfigService, RequestStack $requestStack, VatIdValidationService $vatIdValidationService)
{
$this->systemConfigService = $systemConfigService;
$this->requestStack = $requestStack;
$this->vatIdValidationService = $vatIdValidationService;
$this->vatIds = [];
$this->addressAllowed = false;
$this->count = 0;
}
public static function getSubscribedEvents(): array
{
return [
'framework.validation.customer.profile.update' => 'onValidateCustomerUpdate',
'framework.validation.customer.create' => 'onValidateCustomerCreate',
'framework.validation.address.create' => 'onValidateAddress',
'framework.validation.address.update' => 'onValidateAddress',
'framework.validation.order.update' => 'onValidateOrder',
'framework.validation.order.create' => 'onValidateOrder',
CustomerRegisterEvent::class => 'onCustomerRegister',
GuestCustomerRegisterEvent::class => 'onGuestCustomerRegister'
];
}
public function onValidateCustomerUpdate(BuildValidationEvent $event): void
{
if(empty($this->requestStack) === true || empty($request = $this->requestStack->getCurrentRequest()) === true) {
return;
}
$salesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
$salesChannelId = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
if(!$salesChannelContext instanceof SalesChannelContext || empty($salesChannelId) === true || empty($request->get('vatIds')) === true || !is_array($request->get('vatIds'))) {
return;
}
$customer = $salesChannelContext->getCustomer();
if(!$customer instanceof CustomerEntity) {
return;
}
$billingAddress = $customer->getDefaultBillingAddress();
if (empty($billingAddress) === true) {
return;
}
$billingAddressArray = $this->convertAddress($billingAddress, $customer->getId(), $customer->getCompany(), $event->getData());
if (empty($billingAddressArray) === true || !array_key_exists('company', $billingAddressArray) || empty($billingAddressArray['company'])) {
return;
}
$this->vatIdValidationService->checkVatIdsForCustomer($request->get('vatIds'), $customer, $salesChannelContext);
$this->checkVatIdValidation($billingAddressArray, $event, $salesChannelId, $salesChannelContext, $request->get('vatIds'), 'customer-update', false, 'customerUpdate');
}
public function onValidateCustomerCreate(BuildValidationEvent $event): void
{
if(empty($this->requestStack) === true || empty($request = $this->requestStack->getCurrentRequest()) === true) {
return;
}
$salesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
$salesChannelId = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
if(!$salesChannelContext instanceof SalesChannelContext || empty($salesChannelId) === true || empty($request->get('vatIds')) === true || !is_array($request->get('vatIds'))) {
return;
}
$billingAddress = $request->get('billingAddress');
if (empty($billingAddress) === true) {
$billingAddress = $request->get('address');
}
if (empty($billingAddress) === true || !array_key_exists('company', $billingAddress) || empty($billingAddress['company'])) {
return;
}
$this->checkVatIdValidation($billingAddress, $event, $salesChannelId, $salesChannelContext, $request->get('vatIds'), 'customer-create', false, 'customerCreate');
}
public function onValidateAddress(BuildValidationEvent $event): void
{
$this->count++;
$currentAddress = 'billingAddress';
if(empty($this->requestStack) === true || empty($request = $this->requestStack->getCurrentRequest()) === true) {
return;
}
$salesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
$salesChannelId = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
if(!$salesChannelContext instanceof SalesChannelContext || empty($salesChannelId) === true) {
return;
}
//check address
$address = $request->get('shippingAddress');
if (!empty($address)) $currentAddress = 'shippingAddress';
if ($this->addressAllowed !== true) {
$this->addressAllowed = true;
if (!empty($address)) {
return;
}
}
if (!empty($address)) {
if (!$request->request->has('differentShippingAddress') || boolval($request->request->get('differentShippingAddress')) !== true) {
return;
}
}
if (empty($address) === true) {
$address = $request->get('address');
}
if (empty($address) === true || !array_key_exists('company', $address) || empty($address['company'])) {
return;
}
if (!empty($address) && array_key_exists('customFields', $address) && !empty($address['customFields'])) {
$customFields = $address['customFields'];
if (is_array($customFields) && array_key_exists('acris_address_vat_id', $customFields) && empty($customFields['acris_address_vat_id']) !== true) {
$addressVatId = $customFields['acris_address_vat_id'];
if (empty($addressVatId) || !is_string($addressVatId) || in_array($addressVatId, $this->vatIds)) return;
$this->vatIds[] = $addressVatId;
$this->checkVatIdValidation($address, $event, $salesChannelId, $salesChannelContext, [$addressVatId], 'address-save-or-update', false, $currentAddress);
}
}
}
public function onValidateOrder(BuildValidationEvent $event)
{
if(empty($this->requestStack) === true || empty($request = $this->requestStack->getCurrentRequest()) === true) {
return;
}
$salesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
$salesChannelId = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
if(!$salesChannelContext instanceof SalesChannelContext || empty($salesChannelId) === true) {
return;
}
if (!$this->systemConfigService->get('AcrisTaxCS.config.validateVatIDOnOrder', $salesChannelId)) {
return;
}
// validate personal data and billing address
if(empty($salesChannelContext->getCustomer()) !== true && !empty($salesChannelContext->getCustomer()->getDefaultBillingAddress())) {
$billingAddressVatId = !empty($salesChannelContext->getCustomer() && !empty($salesChannelContext->getCustomer()->getDefaultBillingAddress())) ? $this->getBillingAddressVatId($salesChannelContext->getCustomer()->getDefaultBillingAddress()) : null;
$billingAddressArray = $this->convertAddress($salesChannelContext->getCustomer()->getDefaultBillingAddress(), $salesChannelContext->getCustomer()->getId(), $salesChannelContext->getCustomer()->getCompany());
if (empty($billingAddressArray) !== true && array_key_exists('company', $billingAddressArray) && !empty($billingAddressArray['company'])) {
if (!empty($salesChannelContext->getCustomer()->getVatIds()) && is_array($salesChannelContext->getCustomer()->getVatIds())) {
$billingAddressArray['vatIdValidationEntry'] = 'personalData';
$this->checkVatIdValidation($billingAddressArray, $event, $salesChannelId, $salesChannelContext, $salesChannelContext->getCustomer()->getVatIds(), 'order-save-or-update', true, 'orderAddress');
}
if (!empty($billingAddressVatId) && is_string($billingAddressVatId)) {
$billingAddressArray['vatIdValidationEntry'] = 'billingAddress';
if (array_key_exists('addressCompany', $billingAddressArray) && !empty($billingAddressArray['addressCompany'])) $billingAddressArray['company'] = $billingAddressArray['addressCompany'];
$this->checkVatIdValidation($billingAddressArray, $event, $salesChannelId, $salesChannelContext, [$billingAddressVatId], 'order-save-or-update', true, 'orderAddress');
}
}
}
// validate shipping address
if(empty($salesChannelContext->getCustomer()) !== true && !empty($salesChannelContext->getCustomer()->getDefaultShippingAddress())) {
$shippingAddressVatId = !empty($salesChannelContext->getCustomer() && !empty($salesChannelContext->getCustomer()->getDefaultShippingAddress())) ? $this->getBillingAddressVatId($salesChannelContext->getCustomer()->getDefaultShippingAddress()) : null;
$shippingAddressArray = $this->convertAddress($salesChannelContext->getCustomer()->getDefaultShippingAddress(), $salesChannelContext->getCustomer()->getId(), $salesChannelContext->getCustomer()->getCompany());
if (empty($shippingAddressArray) !== true && array_key_exists('company', $shippingAddressArray) && !empty($shippingAddressArray['company'])) {
if (!empty($shippingAddressVatId) && is_string($shippingAddressVatId)) {
$shippingAddressArray['vatIdValidationEntry'] = 'shippingAddress';
if (array_key_exists('addressCompany', $shippingAddressArray) && !empty($shippingAddressArray['addressCompany'])) $shippingAddressArray['company'] = $shippingAddressArray['addressCompany'];
$this->checkVatIdValidation($shippingAddressArray, $event, $salesChannelId, $salesChannelContext, [$shippingAddressVatId], 'order-save-or-update', true, 'orderAddress');
}
}
}
}
public function onCustomerRegister(CustomerRegisterEvent $event): void
{
if (empty($event->getCustomerId())) return;
$this->vatIdValidationService->assignTheCustomerToLogEntity($event->getCustomer(), $event->getSalesChannelContext());
}
public function onGuestCustomerRegister(GuestCustomerRegisterEvent $event): void
{
if (empty($event->getCustomerId())) return;
$this->vatIdValidationService->assignTheCustomerToLogEntity($event->getCustomer(), $event->getSalesChannelContext());
}
private function convertAddress(CustomerAddressEntity $addressEntity, string $customerId, ?string $company, ?DataBag $dataBag = null): array
{
$addressData = [
'customerId' => $customerId,
'street' => $addressEntity->getStreet(),
'city' => $addressEntity->getCity(),
'zipcode' => $addressEntity->getZipcode(),
'countryId' => $addressEntity->getCountryId()
];
if (!empty($addressEntity->getCompany())) $addressData['addressCompany'] = $addressEntity->getCompany();
if (!empty($company)) {
if (array_key_exists('addressCompany', $addressData) && !empty($addressData['addressCompany'])) {
$addressData['company'] = $company;
}
}
if (!empty($dataBag) && !empty($dataBag->get('company'))) {
$addressData['company'] = $dataBag->get('company');
}
return $addressData;
}
private function validateVatIds($vatIds, array $billingAddress, BuildValidationEvent $event, SalesChannelContext $salesChannelContext, ?CountryEntity $country, string $cause, bool $checkOrder, string $currentAddress)
{
if(empty($vatIds)) {
return;
}
foreach ($vatIds as $vatId) {
if(empty($vatId)) {
continue;
}
$options = [
'address' => $billingAddress,
'vatId' => $vatId,
'context' => $event->getContext(),
'salesChannelContext' => $salesChannelContext,
'country' => $country,
'cause' => $cause,
'isCheckOrder' => $checkOrder,
'currentAddress' => $currentAddress
];
$properties = $event->getDefinition()->getProperties();
if (!array_key_exists('vatIds', $properties) || $cause = 'customer-update') {
$event->getDefinition()->add('vatIds', new CustomerVatId($options));
}
}
}
private function checkVatIdValidation(array $address, $event, string $salesChannelId, SalesChannelContext $salesChannelContext, ?array $vatIds, string $cause, bool $checkOrder, string $currentAddress): void
{
if (empty($vatIds)) return;
$countryId = array_key_exists('countryId', $address) && !empty($address['countryId']) ? $address['countryId'] : null;
if (empty($countryId)) return;
$country = $this->vatIdValidationService->getCountry($countryId, $event->getContext());
$vatIdFormatValidation = $this->systemConfigService->get('AcrisTaxCS.config.validateVatIDFormatAllEUCountries', $salesChannelId);
$vatIdApiServiceValidation = $this->systemConfigService->get('AcrisTaxCS.config.VatIDAPIValidationAllEUCountries', $salesChannelId);
if ((!empty($vatIdFormatValidation) && ($vatIdFormatValidation === VatIdValidationService::DEFAULT_ENABLE_FORMAT_VAT_ID_ALL_EU_COUNTRIES || ($vatIdFormatValidation === VatIdValidationService::DEFAULT_OPTION_STANDARD && $this->vatIdValidationService->checkCountryFormatValidation($country))))
|| (!empty($vatIdApiServiceValidation) && (($vatIdApiServiceValidation === VatIdValidationService::DEFAULT_ENABLE_VAT_ID_VALIDATION_API_SERVICE_ALL_EU_COUNTRIES || $vatIdApiServiceValidation === VatIdValidationService::DEFAULT_ENABLE_VAT_ID_COMPANY_VALIDATION_API_SERVICE_ALL_EU_COUNTRIES
|| $vatIdApiServiceValidation === VatIdValidationService::DEFAULT_ENABLE_VAT_ID_ADDRESS_VALIDATION_API_SERVICE_ALL_EU_COUNTRIES || $vatIdApiServiceValidation === VatIdValidationService::DEFAULT_ENABLE_VAT_ID_COMPANY_ADDRESS_VALIDATION_API_SERVICE_ALL_EU_COUNTRIES)
|| ($vatIdApiServiceValidation === VatIdValidationService::DEFAULT_OPTION_STANDARD && $this->vatIdValidationService->checkCountryVatIdValidationApiService($country))))) {
$this->validateVatIds($vatIds, $address, $event, $salesChannelContext, $country, $cause, $checkOrder, $currentAddress);
}
}
private function getBillingAddressVatId(CustomerAddressEntity $address): ?string
{
if (empty($address->getCustomFields()) || !is_array($address->getCustomFields()) || !array_key_exists('acris_address_vat_id', $address->getCustomFields()) || empty($address->getCustomFields()['acris_address_vat_id'])) return null;
return $address->getCustomFields()['acris_address_vat_id'];
}
}