185 lines
7.7 KiB
PHP
185 lines
7.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\CustomerVat\Setup\Patch\Data;
|
|
|
|
use Esun\CustomerVat\Model\EuCountries;
|
|
use Magento\Framework\Setup\ModuleDataSetupInterface;
|
|
use Magento\Framework\Setup\Patch\DataPatchInterface;
|
|
|
|
class ConfigureTaxRules implements DataPatchInterface
|
|
{
|
|
public const EXEMPT_TAX_CLASS = 'eSUN EU VAT Exempt Customer';
|
|
|
|
public function __construct(private readonly ModuleDataSetupInterface $moduleDataSetup)
|
|
{
|
|
}
|
|
|
|
public function apply(): self
|
|
{
|
|
$setup = $this->moduleDataSetup;
|
|
$connection = $setup->getConnection();
|
|
$setup->startSetup();
|
|
|
|
$taxClassTable = $setup->getTable('tax_class');
|
|
$rateTable = $setup->getTable('tax_calculation_rate');
|
|
$ruleTable = $setup->getTable('tax_calculation_rule');
|
|
$calculationTable = $setup->getTable('tax_calculation');
|
|
|
|
$this->ensureTaxClass($taxClassTable, self::EXEMPT_TAX_CLASS, 'CUSTOMER');
|
|
$customerClassIds = array_map('intval', $connection->fetchCol(
|
|
$connection->select()->from($taxClassTable, 'class_id')->where('class_type = ?', 'CUSTOMER')
|
|
));
|
|
$productClassIds = array_map('intval', $connection->fetchCol(
|
|
$connection->select()->from($taxClassTable, 'class_id')->where('class_type = ?', 'PRODUCT')
|
|
));
|
|
|
|
$euStandardRule = $this->ensureRule(
|
|
$ruleTable,
|
|
'eSUN EU Calculated VAT',
|
|
['eSUN EU Standard VAT 19']
|
|
);
|
|
$ukRule = $this->ensureRule($ruleTable, 'eSUN UK VAT 20');
|
|
$legacyRuleIds = array_map('intval', $connection->fetchCol(
|
|
$connection->select()->from($ruleTable, 'tax_calculation_rule_id')
|
|
->where('code IN (?)', ['eSUN EU VAT Exempt 0'])
|
|
));
|
|
$connection->delete(
|
|
$calculationTable,
|
|
['tax_calculation_rule_id IN (?)' => array_merge([$euStandardRule, $ukRule], $legacyRuleIds)]
|
|
);
|
|
|
|
foreach (EuCountries::CODES as $countryId) {
|
|
$nominalRate = EuCountries::RATES[$countryId] ?? 19.0;
|
|
$standardRateId = $this->ensureRate(
|
|
$rateTable,
|
|
sprintf('ESUN-EU-%s-NOMINAL-%s', $countryId, (int)$nominalRate),
|
|
$countryId,
|
|
EuCountries::toAppliedRate($nominalRate)
|
|
);
|
|
$this->link($calculationTable, $standardRateId, $euStandardRule, $customerClassIds, $productClassIds);
|
|
}
|
|
$ukRateId = $this->ensureRate(
|
|
$rateTable,
|
|
'ESUN-GB-NOMINAL-20',
|
|
'GB',
|
|
EuCountries::toAppliedRate(20.0)
|
|
);
|
|
$this->link($calculationTable, $ukRateId, $ukRule, $customerClassIds, $productClassIds);
|
|
|
|
// The database initially contains Magento sample US state tax data. These markets must be tax-free.
|
|
$noTaxRateIds = $connection->fetchCol(
|
|
$connection->select()->from($rateTable, 'tax_calculation_rate_id')->where('tax_country_id IN (?)', ['US', 'CA', 'AU'])
|
|
);
|
|
if ($noTaxRateIds) {
|
|
$connection->delete($calculationTable, ['tax_calculation_rate_id IN (?)' => $noTaxRateIds]);
|
|
}
|
|
|
|
$this->configureWebsites();
|
|
$setup->endSetup();
|
|
return $this;
|
|
}
|
|
|
|
private function ensureTaxClass(string $table, string $name, string $type): int
|
|
{
|
|
$connection = $this->moduleDataSetup->getConnection();
|
|
$id = $connection->fetchOne($connection->select()->from($table, 'class_id')
|
|
->where('class_name = ?', $name)->where('class_type = ?', $type));
|
|
if ($id) {
|
|
return (int)$id;
|
|
}
|
|
$connection->insert($table, ['class_name' => $name, 'class_type' => $type]);
|
|
return (int)$connection->lastInsertId($table);
|
|
}
|
|
|
|
private function ensureRate(string $table, string $code, string $countryId, float $rate): int
|
|
{
|
|
$connection = $this->moduleDataSetup->getConnection();
|
|
$id = $connection->fetchOne($connection->select()->from($table, 'tax_calculation_rate_id')->where('code = ?', $code));
|
|
$data = ['tax_country_id' => $countryId, 'tax_region_id' => 0, 'tax_postcode' => '*', 'code' => $code,
|
|
'rate' => $rate, 'zip_is_range' => null, 'zip_from' => null, 'zip_to' => null];
|
|
if ($id) {
|
|
$connection->update($table, $data, ['tax_calculation_rate_id = ?' => (int)$id]);
|
|
return (int)$id;
|
|
}
|
|
$connection->insert($table, $data);
|
|
return (int)$connection->lastInsertId($table);
|
|
}
|
|
|
|
private function ensureRule(string $table, string $code, array $legacyCodes = []): int
|
|
{
|
|
$connection = $this->moduleDataSetup->getConnection();
|
|
$id = $connection->fetchOne(
|
|
$connection->select()->from($table, 'tax_calculation_rule_id')
|
|
->where('code IN (?)', array_merge([$code], $legacyCodes))
|
|
->order('tax_calculation_rule_id ASC')
|
|
);
|
|
if ($id) {
|
|
$connection->update($table, ['code' => $code], ['tax_calculation_rule_id = ?' => (int)$id]);
|
|
return (int)$id;
|
|
}
|
|
$connection->insert($table, ['code' => $code, 'priority' => 0, 'position' => 0, 'calculate_subtotal' => 0]);
|
|
return (int)$connection->lastInsertId($table);
|
|
}
|
|
|
|
private function link(string $table, int $rateId, int $ruleId, array $customerClassIds, array $productClassIds): void
|
|
{
|
|
$rows = [];
|
|
foreach ($customerClassIds as $customerClassId) {
|
|
foreach ($productClassIds as $productClassId) {
|
|
$rows[] = ['tax_calculation_rate_id' => $rateId, 'tax_calculation_rule_id' => $ruleId,
|
|
'customer_tax_class_id' => $customerClassId, 'product_tax_class_id' => $productClassId];
|
|
}
|
|
}
|
|
if ($rows) {
|
|
$this->moduleDataSetup->getConnection()->insertMultiple($table, $rows);
|
|
}
|
|
}
|
|
|
|
private function configureWebsites(): void
|
|
{
|
|
$connection = $this->moduleDataSetup->getConnection();
|
|
$websiteTable = $this->moduleDataSetup->getTable('store_website');
|
|
$configTable = $this->moduleDataSetup->getTable('core_config_data');
|
|
$countries = [
|
|
'eu' => 'DE',
|
|
'eu_site' => 'DE',
|
|
'uk' => 'GB',
|
|
'gb_site' => 'GB',
|
|
'us_site' => 'US',
|
|
'au' => 'AU',
|
|
'au_site' => 'AU',
|
|
];
|
|
foreach ($countries as $websiteCode => $countryId) {
|
|
$websiteId = (int)$connection->fetchOne(
|
|
$connection->select()->from($websiteTable, 'website_id')->where('code = ?', $websiteCode)
|
|
);
|
|
if (!$websiteId) {
|
|
continue;
|
|
}
|
|
$values = [
|
|
'tax/calculation/price_includes_tax' => '0',
|
|
'tax/calculation/shipping_includes_tax' => '0',
|
|
'tax/calculation/based_on' => 'shipping',
|
|
'tax/calculation/cross_border_trade_enabled' => '1',
|
|
'tax/calculation/apply_after_discount' => '1',
|
|
'tax/defaults/country' => $countryId,
|
|
'tax/display/type' => '1',
|
|
'tax/cart_display/price' => '1',
|
|
'tax/cart_display/subtotal' => '1',
|
|
'tax/cart_display/shipping' => '1',
|
|
'tax/sales_display/price' => '1',
|
|
'tax/sales_display/subtotal' => '1',
|
|
'tax/sales_display/shipping' => '1',
|
|
];
|
|
foreach ($values as $path => $value) {
|
|
$connection->insertOnDuplicate($configTable,
|
|
['scope' => 'websites', 'scope_id' => $websiteId, 'path' => $path, 'value' => $value], ['value']);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function getDependencies(): array { return []; }
|
|
public function getAliases(): array { return []; }
|
|
}
|