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_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.
CONST INTRACOMMUNITY_EXEMPTION_TEXTS = [
'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);
}
}
+56 -1
View File
@@ -3,10 +3,12 @@
namespace Websmid\VatCheck\Module\Admin;
use Websmid\VatCheck\Config\Config;
use Websmid\VatCheck\Model\EuCountryAllowList;
use Configuration;
use Tools;
use HelperForm;
use Context;
use Country;
trait SettingsForm
{
@@ -31,6 +33,15 @@ trait SettingsForm
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()
@@ -77,7 +88,34 @@ trait SettingsForm
null,
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()
@@ -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' => [
'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\Model\VatNumber;
use Websmid\VatCheck\Model\EuCountryAllowList;
use Websmid\VatCheck\Config\Config;
use Address;
use Configuration;
@@ -27,7 +28,7 @@ trait FrontHooks
$countryIso = (string)Country::getIsoById((string)$form->getValue('id_country'));
$vatNumber = new VatNumber($vatNumber, $countryIso);
if( $vatNumber->isNotEuIso() ){
if( $vatNumber->isNotEuIso() || !EuCountryAllowList::isEnabled($countryIso) ){
return true;
}
+8
View File
@@ -3,6 +3,7 @@
namespace Websmid\VatCheck\Module;
use Websmid\VatCheck\Config\Config;
use Websmid\VatCheck\Model\EuCountryAllowList;
use Db;
use Configuration;
@@ -39,6 +40,13 @@ trait Install
return false;
}
if (!Configuration::updateValue(
Config::EU_COUNTRIES_CONFIG_KEY,
json_encode(EuCountryAllowList::getAllSupportedIsoCodes())
)) {
return false;
}
return true;
}
+2 -1
View File
@@ -12,6 +12,7 @@ use Configuration;
use Context;
use Websmid\VatCheck\Model\VatCache;
use Websmid\VatCheck\Model\VatNumber;
use Websmid\VatCheck\Model\EuCountryAllowList;
use Websmid\VatCheck\Adapter\EuValidatorAdapter;
class CacheTaxManager implements TaxManagerInterface
@@ -27,7 +28,7 @@ class CacheTaxManager implements TaxManagerInterface
}
$isoCode = Country::getIsoById((int)$address->id_country);
if( !array_key_exists($isoCode, VatNumber::$euIso) ){
if( !EuCountryAllowList::isEnabled($isoCode) ){
return false;
}
+11 -1
View File
@@ -1,6 +1,7 @@
<?php
use Websmid\VatCheck\Config\Config;
use Websmid\VatCheck\Model\EuCountryAllowList;
if (!defined('_PS_VERSION_')) {
exit;
@@ -12,5 +13,14 @@ function upgrade_module_1_0_3($module)
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())
);
}