From 6f7362c42a095b188c17a1ac80e412dfe1623996 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Fri, 11 Sep 2026 16:55:32 +0200 Subject: [PATCH] Add company-name cross-check against VIES response --- src/Adapter/EuValidatorAdapter.php | 34 +++++++++++++++++++++++--- src/Config/Config.php | 4 +++ src/Model/CompanyNameMatcher.php | 39 ++++++++++++++++++++++++++++++ src/Module/Admin/SettingsForm.php | 39 +++++++++++++++++++++++++++--- src/Module/Install.php | 10 +++++++- src/TaxManager/CacheTaxManager.php | 2 +- upgrade/upgrade-1.0.3.php | 6 ++++- 7 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 src/Model/CompanyNameMatcher.php diff --git a/src/Adapter/EuValidatorAdapter.php b/src/Adapter/EuValidatorAdapter.php index 6a2dcf9..5f01c93 100644 --- a/src/Adapter/EuValidatorAdapter.php +++ b/src/Adapter/EuValidatorAdapter.php @@ -7,21 +7,25 @@ use PH7\Eu\Vat\Provider\Europa; use PH7\Eu\Vat\Exception; use Websmid\VatCheck\Config\Config; use Websmid\VatCheck\Model\VatCache; +use Websmid\VatCheck\Model\CompanyNameMatcher; use PrestaShopLogger; use Configuration; class EuValidatorAdapter -{ +{ protected $vatNr = ''; protected $isoCode = ''; + protected $company = ''; + private $checkStatus; - public function __construct(string $vatNr, string $isoCode) + public function __construct(string $vatNr, string $isoCode, string $company = '') { $this->vatNr = str_replace($isoCode, '', $vatNr); $this->isoCode = $isoCode; + $this->company = $company; } public function checkAndStore() @@ -46,7 +50,7 @@ class EuValidatorAdapter $fullVat = $this->isoCode.$this->vatNr; $vatCache = VatCache::getOrCreateByVat($fullVat); $vatCache->vat_nr = $fullVat; - $vatCache->valid = $euVatValidator->check(); + $vatCache->valid = $euVatValidator->check() && $this->isCompanyNameAcceptable($euVatValidator); $vatCache->save(); } @@ -54,6 +58,30 @@ class EuValidatorAdapter return true; } + /** + * Only rejects on a confirmed mismatch. Skips the check entirely when it's + * disabled, when the address has no company on file, or when VIES didn't + * return a usable name for this member state (not every country discloses + * one) — there's nothing meaningful to compare against in those cases. + */ + private function isCompanyNameAcceptable(Validator $validator): bool + { + if (!Configuration::get(Config::VALIDATE_COMPANY_CONFIG_KEY)) { + return true; + } + + if ('' === trim($this->company)) { + return true; + } + + $viesName = $validator->getName(); + if (!CompanyNameMatcher::hasUsableName($viesName)) { + return true; + } + + return CompanyNameMatcher::matches($this->company, $viesName); + } + /** * Decide what to do when VIES itself can't be reached, per the configured * Config::OFFLINE_MODE_CONFIG_KEY policy. Returning true here tells diff --git a/src/Config/Config.php b/src/Config/Config.php index 4ed708a..72c91d6 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -32,6 +32,10 @@ class Config self::OFFLINE_MODE_LAST_KNOWN_OR_VALID => 'Reuse the last known result, otherwise mark valid', ]; + // Configuration key toggling the company-name cross-check against VIES. + CONST VALIDATE_COMPANY_CONFIG_KEY = 'WS_VATCHECK_VALIDATE_COMPANY'; + CONST VALIDATE_COMPANY_DEFAULT = false; + // Article 39bis W.BTW intra-community exemption notice, per invoice language. CONST INTRACOMMUNITY_EXEMPTION_TEXTS = [ 'nl' => 'Vrijgestelde intracommunautaire levering – artikel 39bis W.BTW', diff --git a/src/Model/CompanyNameMatcher.php b/src/Model/CompanyNameMatcher.php new file mode 100644 index 0000000..5dddf18 --- /dev/null +++ b/src/Model/CompanyNameMatcher.php @@ -0,0 +1,39 @@ + (bool) Configuration::get( + Config::VALIDATE_COMPANY_CONFIG_KEY, + null, + null, + null, + Config::VALIDATE_COMPANY_DEFAULT + ), ]; } @@ -99,6 +109,29 @@ trait SettingsForm ), 'values' => $choices, ], + [ + 'type' => 'switch', + 'label' => $this->trans('Also check the company name', [], 'Modules.Ws_VatCheck.Admin'), + 'name' => Config::VALIDATE_COMPANY_CONFIG_KEY, + 'is_bool' => true, + 'desc' => $this->trans( + 'Compare the address\'s company name against the name VIES returns for the VAT number, when available. Not every member state discloses one.', + [], + 'Modules.Ws_VatCheck.Admin' + ), + 'values' => [ + [ + 'id' => 'validate_company_on', + 'value' => true, + 'label' => $this->trans('Enabled', [], 'Admin.Global'), + ], + [ + 'id' => 'validate_company_off', + 'value' => false, + 'label' => $this->trans('Disabled', [], 'Admin.Global'), + ], + ], + ], ], 'submit' => [ 'title' => $this->trans('Save', [], 'Admin.Actions'), diff --git a/src/Module/Install.php b/src/Module/Install.php index e6cb9ee..ac0c931 100644 --- a/src/Module/Install.php +++ b/src/Module/Install.php @@ -31,7 +31,15 @@ trait Install private function registerConfiguration() { - return Configuration::updateValue(Config::OFFLINE_MODE_CONFIG_KEY, Config::OFFLINE_MODE_DEFAULT); + if (!Configuration::updateValue(Config::OFFLINE_MODE_CONFIG_KEY, Config::OFFLINE_MODE_DEFAULT)) { + return false; + } + + if (!Configuration::updateValue(Config::VALIDATE_COMPANY_CONFIG_KEY, Config::VALIDATE_COMPANY_DEFAULT)) { + return false; + } + + return true; } private function registerHooks() diff --git a/src/TaxManager/CacheTaxManager.php b/src/TaxManager/CacheTaxManager.php index 5256ad0..014c159 100644 --- a/src/TaxManager/CacheTaxManager.php +++ b/src/TaxManager/CacheTaxManager.php @@ -36,7 +36,7 @@ class CacheTaxManager implements TaxManagerInterface } $vatPrefix = VatNumber::$euIso[$isoCode]; - $euValidator = new EuValidatorAdapter($address->vat_number, $vatPrefix); + $euValidator = new EuValidatorAdapter($address->vat_number, $vatPrefix, (string) $address->company); if( !$euValidator->checkAndStore() ){ return false; } diff --git a/upgrade/upgrade-1.0.3.php b/upgrade/upgrade-1.0.3.php index 3a670f7..a92a5e0 100644 --- a/upgrade/upgrade-1.0.3.php +++ b/upgrade/upgrade-1.0.3.php @@ -8,5 +8,9 @@ if (!defined('_PS_VERSION_')) { function upgrade_module_1_0_3($module) { - return Configuration::updateValue(Config::OFFLINE_MODE_CONFIG_KEY, Config::OFFLINE_MODE_DEFAULT); + if (!Configuration::updateValue(Config::OFFLINE_MODE_CONFIG_KEY, Config::OFFLINE_MODE_DEFAULT)) { + return false; + } + + return Configuration::updateValue(Config::VALIDATE_COMPANY_CONFIG_KEY, Config::VALIDATE_COMPANY_DEFAULT); }