feat(main): Add configurable EU country allow-list

This commit is contained in:
2026-09-11 17:00:33 +02:00
parent 6f7362c42a
commit 065c60b2a3
7 changed files with 124 additions and 4 deletions
+5
View File
@@ -36,6 +36,11 @@ class Config
CONST VALIDATE_COMPANY_CONFIG_KEY = 'WS_VATCHECK_VALIDATE_COMPANY'; CONST VALIDATE_COMPANY_CONFIG_KEY = 'WS_VATCHECK_VALIDATE_COMPANY';
CONST VALIDATE_COMPANY_DEFAULT = false; CONST VALIDATE_COMPANY_DEFAULT = false;
// Configuration key holding the admin-selected list of EU countries allowed
// to order VAT-free (JSON-encoded array of ISO codes). Absent/empty means
// "all countries VatNumber::$euIso supports" — see EuCountryAllowList.
CONST EU_COUNTRIES_CONFIG_KEY = 'WS_VATCHECK_EU_COUNTRIES';
// Article 39bis W.BTW intra-community exemption notice, per invoice language. // Article 39bis W.BTW intra-community exemption notice, per invoice language.
CONST INTRACOMMUNITY_EXEMPTION_TEXTS = [ CONST INTRACOMMUNITY_EXEMPTION_TEXTS = [
'nl' => 'Vrijgestelde intracommunautaire levering – artikel 39bis W.BTW', 'nl' => 'Vrijgestelde intracommunautaire levering – artikel 39bis W.BTW',
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Websmid\VatCheck\Model;
use Websmid\VatCheck\Config\Config;
use Configuration;
class EuCountryAllowList
{
/**
* Every ISO code VatNumber knows a VAT prefix/format for — the maximum
* possible set an admin could enable.
*/
public static function getAllSupportedIsoCodes(): array
{
return array_keys(VatNumber::$euIso);
}
/**
* Falls back to "all supported countries" when nothing has been configured
* yet, so a fresh install/upgrade doesn't silently narrow existing behavior.
* Intersects against the supported set so a stale stored code can never
* make it back out.
*/
public static function getEnabledIsoCodes(): array
{
$stored = json_decode((string) Configuration::get(Config::EU_COUNTRIES_CONFIG_KEY), true);
if (empty($stored) || !is_array($stored)) {
return self::getAllSupportedIsoCodes();
}
return array_values(array_intersect($stored, self::getAllSupportedIsoCodes()));
}
public static function isEnabled(string $isoCode): bool
{
return in_array($isoCode, self::getEnabledIsoCodes(), true);
}
}
+55
View File
@@ -3,10 +3,12 @@
namespace Websmid\VatCheck\Module\Admin; namespace Websmid\VatCheck\Module\Admin;
use Websmid\VatCheck\Config\Config; use Websmid\VatCheck\Config\Config;
use Websmid\VatCheck\Model\EuCountryAllowList;
use Configuration; use Configuration;
use Tools; use Tools;
use HelperForm; use HelperForm;
use Context; use Context;
use Country;
trait SettingsForm trait SettingsForm
{ {
@@ -31,6 +33,15 @@ trait SettingsForm
Config::VALIDATE_COMPANY_CONFIG_KEY, Config::VALIDATE_COMPANY_CONFIG_KEY,
(bool) Tools::getValue(Config::VALIDATE_COMPANY_CONFIG_KEY) (bool) Tools::getValue(Config::VALIDATE_COMPANY_CONFIG_KEY)
); );
$enabledIsoCodes = [];
foreach (EuCountryAllowList::getAllSupportedIsoCodes() as $isoCode) {
if (Tools::getValue(Config::EU_COUNTRIES_CONFIG_KEY . '_' . $isoCode)) {
$enabledIsoCodes[] = $isoCode;
}
}
Configuration::updateValue(Config::EU_COUNTRIES_CONFIG_KEY, json_encode($enabledIsoCodes));
} }
private function renderSettingsForm() private function renderSettingsForm()
@@ -77,8 +88,35 @@ trait SettingsForm
null, null,
Config::VALIDATE_COMPANY_DEFAULT Config::VALIDATE_COMPANY_DEFAULT
), ),
] + $this->getEnabledCountriesFormValues();
}
private function getEnabledCountriesFormValues()
{
$values = [];
foreach (EuCountryAllowList::getEnabledIsoCodes() as $isoCode) {
$values[Config::EU_COUNTRIES_CONFIG_KEY . '_' . $isoCode] = true;
}
return $values;
}
private function getCountryChoices()
{
$supported = EuCountryAllowList::getAllSupportedIsoCodes();
$choices = [];
foreach (Country::getCountries(Context::getContext()->language->id) as $country) {
if (in_array($country['iso_code'], $supported, true)) {
$choices[] = [
'id' => $country['iso_code'],
'name' => $country['name'] . ' (' . $country['iso_code'] . ')',
]; ];
} }
}
return $choices;
}
private function getSettingsFormStructure() private function getSettingsFormStructure()
{ {
@@ -132,6 +170,23 @@ trait SettingsForm
], ],
], ],
], ],
[
'type' => 'checkbox',
'label' => $this->trans('Enabled EU countries', [], 'Modules.Ws_VatCheck.Admin'),
'name' => Config::EU_COUNTRIES_CONFIG_KEY,
'multiple' => true,
'desc' => $this->trans(
'Countries a customer can order VAT-free from with a valid VAT number.',
[],
'Modules.Ws_VatCheck.Admin'
),
'values' => [
'query' => $this->getCountryChoices(),
'id' => 'id',
'name' => 'name',
'value' => 'id',
],
],
], ],
'submit' => [ 'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'), 'title' => $this->trans('Save', [], 'Admin.Actions'),
+2 -1
View File
@@ -4,6 +4,7 @@ namespace Websmid\VatCheck\Module\Front;
use Websmid\VatCheck\TaxManager\CacheTaxManager; use Websmid\VatCheck\TaxManager\CacheTaxManager;
use Websmid\VatCheck\Model\VatNumber; use Websmid\VatCheck\Model\VatNumber;
use Websmid\VatCheck\Model\EuCountryAllowList;
use Websmid\VatCheck\Config\Config; use Websmid\VatCheck\Config\Config;
use Address; use Address;
use Configuration; use Configuration;
@@ -27,7 +28,7 @@ trait FrontHooks
$countryIso = (string)Country::getIsoById((string)$form->getValue('id_country')); $countryIso = (string)Country::getIsoById((string)$form->getValue('id_country'));
$vatNumber = new VatNumber($vatNumber, $countryIso); $vatNumber = new VatNumber($vatNumber, $countryIso);
if( $vatNumber->isNotEuIso() ){ if( $vatNumber->isNotEuIso() || !EuCountryAllowList::isEnabled($countryIso) ){
return true; return true;
} }
+8
View File
@@ -3,6 +3,7 @@
namespace Websmid\VatCheck\Module; namespace Websmid\VatCheck\Module;
use Websmid\VatCheck\Config\Config; use Websmid\VatCheck\Config\Config;
use Websmid\VatCheck\Model\EuCountryAllowList;
use Db; use Db;
use Configuration; use Configuration;
@@ -39,6 +40,13 @@ trait Install
return false; return false;
} }
if (!Configuration::updateValue(
Config::EU_COUNTRIES_CONFIG_KEY,
json_encode(EuCountryAllowList::getAllSupportedIsoCodes())
)) {
return false;
}
return true; return true;
} }
+2 -1
View File
@@ -12,6 +12,7 @@ use Configuration;
use Context; use Context;
use Websmid\VatCheck\Model\VatCache; use Websmid\VatCheck\Model\VatCache;
use Websmid\VatCheck\Model\VatNumber; use Websmid\VatCheck\Model\VatNumber;
use Websmid\VatCheck\Model\EuCountryAllowList;
use Websmid\VatCheck\Adapter\EuValidatorAdapter; use Websmid\VatCheck\Adapter\EuValidatorAdapter;
class CacheTaxManager implements TaxManagerInterface class CacheTaxManager implements TaxManagerInterface
@@ -27,7 +28,7 @@ class CacheTaxManager implements TaxManagerInterface
} }
$isoCode = Country::getIsoById((int)$address->id_country); $isoCode = Country::getIsoById((int)$address->id_country);
if( !array_key_exists($isoCode, VatNumber::$euIso) ){ if( !EuCountryAllowList::isEnabled($isoCode) ){
return false; return false;
} }
+11 -1
View File
@@ -1,6 +1,7 @@
<?php <?php
use Websmid\VatCheck\Config\Config; use Websmid\VatCheck\Config\Config;
use Websmid\VatCheck\Model\EuCountryAllowList;
if (!defined('_PS_VERSION_')) { if (!defined('_PS_VERSION_')) {
exit; exit;
@@ -12,5 +13,14 @@ function upgrade_module_1_0_3($module)
return false; return false;
} }
return Configuration::updateValue(Config::VALIDATE_COMPANY_CONFIG_KEY, Config::VALIDATE_COMPANY_DEFAULT); if (!Configuration::updateValue(Config::VALIDATE_COMPANY_CONFIG_KEY, Config::VALIDATE_COMPANY_DEFAULT)) {
return false;
}
// All countries enabled by default, so upgrading doesn't silently narrow
// exemption eligibility for a shop already relying on the old behavior.
return Configuration::updateValue(
Config::EU_COUNTRIES_CONFIG_KEY,
json_encode(EuCountryAllowList::getAllSupportedIsoCodes())
);
} }