Files
2026-09-15 10:35:56 +08:00

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 []; }
}