<?php declare(strict_types=1);
namespace Acris\Tax;
use Acris\Tax\Components\Service\VatIdValidationService;
use Doctrine\DBAL\Connection;
use Shopware\Core\Content\Rule\Aggregate\RuleCondition\RuleConditionEntity;
use Shopware\Core\Content\Rule\RuleEntity;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;
use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;
use Shopware\Core\Framework\Plugin\Context\UpdateContext;
use Shopware\Core\Framework\Plugin\Context\ActivateContext;
use Shopware\Core\System\Country\CountryEntity;
class AcrisTaxCS extends Plugin
{
const CUSTOM_FIELD_SET_NAME = 'acris_tax_specific_country';
const DEFAULT_CUSTOM_CUSTOMER_HAS_VAT_ID_RULE_NAME = 'ACRIS customer personal data has VAT Reg.No. (Shopware standard)';
const DEFAULT_CUSTOM_BILLING_ADDRESS_HAS_VAT_ID_RULE_NAME = 'ACRIS billing address has VAT Reg.No.';
const DEFAULT_CUSTOM_SHIPPING_ADDRESS_HAS_VAT_ID_RULE_NAME = 'ACRIS shipping address has VAT Reg.No.';
const DEFAULT_CUSTOM_CUSTOMER_HAS_VALID_VAT_ID_RULE_NAME = 'ACRIS customer personal data has valid VAT Reg.No.';
const DEFAULT_CUSTOM_BILLING_ADDRESS_HAS_VALID_VAT_ID_RULE_NAME = 'ACRIS billing address has valid VAT Reg.No.';
const DEFAULT_CUSTOM_SHIPPING_ADDRESS_HAS_VALID_VAT_ID_RULE_NAME = 'ACRIS shipping address has valid VAT Reg.No.';
const DEFAULT_CUSTOM_SAME_COUNTRY_IN_BILLING_AND_SHIPPING_ADDRESS_NAME = 'ACRIS same country in billing and shipping address';
public function update(UpdateContext $updateContext): void
{
if(version_compare($updateContext->getCurrentPluginVersion(), '2.3.0', '<')
&& version_compare($updateContext->getUpdatePluginVersion(), '2.3.0', '>=')) {
$this->replaceExistingRuleNames($updateContext->getContext());
}
if(version_compare($updateContext->getCurrentPluginVersion(), '6.1.0', '<')
&& version_compare($updateContext->getUpdatePluginVersion(), '6.1.0', '>=')) {
$this->assignDefaultVatIdCheckForCountries($updateContext->getContext());
}
}
public function postUpdate(UpdateContext $context): void
{
if(version_compare($context->getCurrentPluginVersion(), '6.2.0', '<')
&& version_compare($context->getUpdatePluginVersion(), '6.2.0', '>=')) {
$this->insertDefaultValues($context->getContext());
$this->setDefaultAdvancedTaxFreeRulesSettings($context->getContext());
}
if(version_compare($context->getCurrentPluginVersion(), '6.4.0', '<')
&& version_compare($context->getUpdatePluginVersion(), '6.4.0', '>=')) {
$this->insertDefaultValues($context->getContext());
}
if(version_compare($context->getCurrentPluginVersion(), '6.9.0', '<')
&& version_compare($context->getUpdatePluginVersion(), '6.9.0', '>=')) {
$this->insertDefaultValues($context->getContext());
}
}
public function activate(ActivateContext $activateContext): void
{
$this->insertDefaultValues($activateContext->getContext());
$this->assignDefaultVatIdCheckForCountries($activateContext->getContext());
$this->setDefaultAdvancedTaxFreeRulesSettings($activateContext->getContext());
}
public function uninstall(UninstallContext $context): void
{
if ($context->keepUserData()) {
return;
}
$this->removeRules($context->getContext());
$this->cleanupDatabase();
}
private function cleanupDatabase(): void
{
$connection = $this->container->get(Connection::class);
$connection->executeStatement('DROP TABLE IF EXISTS acris_tax_vat_id_validation_log');
$connection->executeStatement('DROP TABLE IF EXISTS acris_tax_customer_group_rule');
$connection->executeStatement('DROP TABLE IF EXISTS acris_tax_country_rule');
try {
$connection->executeStatement('ALTER TABLE `customer` DROP `acrisTaxVatIdValidationLogs`');
$connection->executeStatement('ALTER TABLE `customer_group` DROP `acrisRules`');
$connection->executeStatement('ALTER TABLE `country` DROP `acrisRules`');
} catch (\Exception $e) { }
}
private function replaceExistingRuleNames(Context $context)
{
$ruleConditionRepository = $this->container->get('rule_condition.repository');
/** @var EntitySearchResult $existingRuleConditionSearchResult */
$existingRuleConditionSearchResult = $ruleConditionRepository->search((new Criteria())->addFilter(
new MultiFilter(MultiFilter::CONNECTION_OR, [
new EqualsFilter('type', 'customerHasVatIdBillingRule'),
new EqualsFilter('type', 'customerHasVatIdShippingRule')
])
), $context);
if ($existingRuleConditionSearchResult->count() <= 0) {
return;
}
$changedRuleConditions = [];
/** @var RuleConditionEntity $ruleConditionEntity */
foreach ($existingRuleConditionSearchResult->getElements() as $ruleConditionEntity) {
$value = $ruleConditionEntity->getValue();
if(!is_array($value)) {
continue;
}
if(array_key_exists('customerHasVatIdBilling', $value)) {
$value = ['customerHasVatId' => $value['customerHasVatIdBilling']];
} elseif(array_key_exists('customerHasVatIdShipping', $value)) {
$value = ['customerHasVatId' => $value['customerHasVatIdShipping']];
} else {
continue;
}
$changedRuleConditions[] = [
'id' => $ruleConditionEntity->getId(),
'type' => 'customerHasVatIdRule',
'value' => $value
];
}
if(!empty($changedRuleConditions)) {
$ruleConditionRepository->upsert($changedRuleConditions, $context);
}
}
private function assignDefaultVatIdCheckForCountries(Context $context): void
{
/** @var EntityRepositoryInterface $countryRepository */
$countryRepository = $this->container->get('country.repository');
$criteria = new Criteria();
$criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_AND, [
new EqualsAnyFilter('iso', VatIdValidationService::DEFAULT_SPECIFIC_COUNTRIES)
]));
/** @var EntitySearchResult $countryIds */
$countries = $countryRepository->search($criteria, $context);
if ($countries->count() === 0 || !$countries->first()) return;
$updateData = [];
/** @var CountryEntity $country */
foreach ($countries->getEntities()->getElements() as $country) {
$customFields = $country->getTranslation('customFields') ?? [];
if (array_key_exists('acris_tax_specific_country_validate_vat_id', $customFields)) continue;
$customFields['acris_tax_specific_country_validate_vat_id'] = true;
$updateData[] = [
'id' => $country->getId(),
'customFields' => $customFields
];
}
if (!empty($updateData)) {
$countryRepository->update($updateData, $context);
}
}
private function insertDefaultValues(Context $context): void
{
/** @var EntityRepositoryInterface $ruleRepository */
$ruleRepository = $this->container->get('rule.repository');
$rules = [
[
'name' => self::DEFAULT_CUSTOM_CUSTOMER_HAS_VAT_ID_RULE_NAME,
'priority' => 100,
'conditions' => [
[
'type' => 'customerHasVatIdRule',
'value' => [
'customerHasVatId' => true
]
]
]
], [
'name' => self::DEFAULT_CUSTOM_BILLING_ADDRESS_HAS_VAT_ID_RULE_NAME,
'priority' => 100,
'conditions' => [
[
'type' => 'billingAddressHasVatIdRule',
'value' => [
'billingAddressHasVatId' => true
]
]
]
], [
'name' => self::DEFAULT_CUSTOM_SHIPPING_ADDRESS_HAS_VAT_ID_RULE_NAME,
'priority' => 100,
'conditions' => [
[
'type' => 'shippingAddressHasVatIdRule',
'value' => [
'shippingAddressHasVatId' => true
]
]
]
], [
'name' => self::DEFAULT_CUSTOM_CUSTOMER_HAS_VALID_VAT_ID_RULE_NAME,
'priority' => 100,
'conditions' => [
[
'type' => 'customerHasVatIdValidRule',
'value' => [
'customerHasVatId' => true
]
]
]
], [
'name' => self::DEFAULT_CUSTOM_BILLING_ADDRESS_HAS_VALID_VAT_ID_RULE_NAME,
'priority' => 100,
'conditions' => [
[
'type' => 'billingAddressHasVatIdValidRule',
'value' => [
'billingAddressHasVatId' => true
]
]
]
], [
'name' => self::DEFAULT_CUSTOM_SHIPPING_ADDRESS_HAS_VALID_VAT_ID_RULE_NAME,
'priority' => 100,
'conditions' => [
[
'type' => 'shippingAddressHasVatIdValidRule',
'value' => [
'shippingAddressHasVatId' => true
]
]
]
], [
'name' => self::DEFAULT_CUSTOM_SAME_COUNTRY_IN_BILLING_AND_SHIPPING_ADDRESS_NAME,
'priority' => 100,
'conditions' => [
[
'type' => 'sameCountryInBillingAddressAndShippingAddressRule',
'value' => [
'isSameCountryInBillingAddressAndShippingAddress' => true
]
]
]
]];
foreach ($rules as $rule) {
try {
$this->createIfNotExists($ruleRepository, [['name' => 'name', 'value' => $rule['name']]], $rule, $context);
} catch (\Throwable $e) {
// Do nothing - prevent a Shopware bug for deleting cache for rules
// TODO: Fix missing rule id now!
}
}
}
private function createIfNotExists(EntityRepositoryInterface $repository, array $equalFields, array $data, Context $context): void
{
$filters = [];
foreach ($equalFields as $equalField) {
$filters[] = new EqualsFilter($equalField['name'], $equalField['value']);
}
if (sizeof($filters) > 1) {
$filter = new MultiFilter(MultiFilter::CONNECTION_OR, $filters);
} else {
$filter = array_shift($filters);
}
$searchResult = $repository->search((new Criteria())->addFilter($filter), $context);
if (empty($searchResult->first())) {
$repository->create([$data], $context);
}
}
private function removeRules(Context $context): void
{
/** @var EntityRepositoryInterface $rulesRepository */
$rulesRepository = $this->container->get('rule.repository');
/** @var EntitySearchResult $rulesResult */
$rulesResult = $rulesRepository->search((new Criteria())->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
new EqualsFilter('name', self::DEFAULT_CUSTOM_CUSTOMER_HAS_VAT_ID_RULE_NAME),
new EqualsFilter('name', self::DEFAULT_CUSTOM_BILLING_ADDRESS_HAS_VAT_ID_RULE_NAME),
new EqualsFilter('name', self::DEFAULT_CUSTOM_SHIPPING_ADDRESS_HAS_VAT_ID_RULE_NAME),
])), $context);
if ($rulesResult->getTotal() > 0 && $rulesResult->first()) {
$ruleIds = [];
/** @var RuleEntity $rule */
foreach ($rulesResult->getEntities()->getElements() as $rule) {
$ruleIds[] = ['id' => $rule->getId()];
}
$rulesRepository->delete($ruleIds, $context);
}
}
private function setDefaultAdvancedTaxFreeRulesSettings(Context $context)
{
/** @var EntityRepositoryInterface $ruleRepository */
$ruleRepository = $this->container->get('rule.repository');
$configService = $this->container->get('Shopware\Core\System\SystemConfig\SystemConfigService');
if($configService->get('AcrisTaxCS.config.acrisAdvancedTaxRules') === null) {
/** @var IdSearchResult $idSearchResult */
$idSearchResult = $ruleRepository->searchIds((new Criteria())->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
new EqualsFilter('name', self::DEFAULT_CUSTOM_CUSTOMER_HAS_VAT_ID_RULE_NAME),
new EqualsFilter('name', self::DEFAULT_CUSTOM_BILLING_ADDRESS_HAS_VAT_ID_RULE_NAME),
new EqualsFilter('name', self::DEFAULT_CUSTOM_SHIPPING_ADDRESS_HAS_VAT_ID_RULE_NAME),
])), $context);
if ($idSearchResult->getTotal() > 0 && !empty($idSearchResult->firstId())) {
$configService->set('AcrisTaxCS.config.acrisAdvancedTaxRules', $idSearchResult->getIds());
}
}
}
}