Skip to content

Company Lookup and EU VAT

The Company Lookup extension covers three related jobs for a store selling to businesses in the EU. It adds a company search to the Create Account page that fills in the address and registration fields from DatReal. It checks a customer's VAT number against the EU VIES service, moves them between two customer groups on the result, and replaces Magento's tax rate resolution with EU VAT rules for intra-EU B2B and cross-border B2C sales.

This module changes core tax configuration on install

setup:upgrade creates customer groups and tax classes, repoints the core General customer group at a new tax class, creates a Latvian tax rule, force-writes tax/calculation/based_on to shipping, and installs a nightly cron job that reassigns customer groups store-wide. The EU VAT rules are also on by default. Read What installation changes before installing on a store with existing tax rules or customer groups.

Documentation

  • Benefits covers what the extension is worth to a B2B store.

Installation

Installation Command:

bash
composer require magebitcom/magento2-venta-company-lookup

Post-Installation Steps:

bash
bin/magento module:enable Magebit_CompanyLookup
bin/magento setup:upgrade
bin/magento cache:flush

Configuration Access: Navigate to Stores > Configuration > Magebit > Company lookup & EU VAT to access the configuration options.

Requirements

RequirementVersion
PHP8.3 or higher
PHP extensionsoap, used for the VIES calls
Magento2.4.x
Hyvä theme moduleany current release
Venta Themeany current release

composer.json declares only magento/framework, so Composer will not enforce any of the above. The module uses typed class constants, which are PHP 8.3 syntax, so an install on 8.1 or 8.2 fails at compile time rather than at install time. It also needs ext-soap and the Hyvä Modal and HyvaCsp view models.

The store makes outbound calls to two services, neither of which needs an account or an API key:

ServiceUsed for
datreal.comCompany lookup by registration number
ec.europa.euVAT number validation through the EU VIES SOAP service

No API keys

Both providers are keyless. DatReal is called as an unauthenticated request and VIES checkVat is a public EU Commission endpoint. There is no API key field in the configuration because there is nothing to enter.

What installation changes

Four data patches run on setup:upgrade. On a fresh Venta store they produce a working B2B tax setup. On a store with its own tax rules and customer groups they can change how existing orders are taxed, so review each one first.

ChangeDetail
Customer tax classes createdTax Exempt and EU VAT Registered
Customer group createdTax Exempt, and it is selected into the VIES configuration
Core General group modifiedIts customer tax class is repointed to EU VAT Registered. Existing tax rules matched on General's previous class stop matching.
Tax Exempt group tax classThe group is first created against the Tax Exempt tax class, then a later patch repoints it to Retail Customer, falling back to tax class ID 3 if no class of that name exists. The end state is Retail Customer.
Cron job installedmagebit_company_lookup_vies_tax_groups, 02:00 daily. On its first run it reassigns customer groups store-wide. See VIES VAT validation and customer groups.
Product tax class createdLV VAT Law Art. 143.1, and its ID is appended to the Art. 143.1 configuration field
Tax rule createdMagebit EU B2B LV Domestic Standard VAT, priority 0, covering all Latvian tax rates, with the product tax class hardcoded to ID 2. Skipped without a message if the store has no Latvian tax rate.
Core config writtentax/calculation/based_on is set to shipping
Legacy config migratedSettings from the predecessor modules Magebit_DatReal, Magebit_EuVat, and the magebit_customer VIES cron are copied to the new paths, preserving scope and never overwriting a value already set

The created tax rule targets fixed tax class IDs, so check it under Stores > Tax Rules after installing and confirm it matches the classes your store actually uses.

The module also forces the address fieldset onto the registration form by setting show_address_fields on customer_form_register. A store that deliberately kept address fields off signup gets them back.

Extension Configuration

Every field is available at default, website, and store view scope. The values below are the ones the installer writes.

Company lookup and EU VAT configuration section

DatReal company lookup

FieldDefaultDescription
API country filterLatviaThe country sent with each lookup. DatReal accepts one country per request, from Latvia, Lithuania, Estonia, and Finland. Leave empty to omit the country parameter.

This field applies only to requests that reach /companylookup/company/search without a country parameter of their own, which in practice means direct API calls rather than the registration form. The form refuses to search until the shopper has picked one of the four supported countries, and then sends that country, which takes precedence.

VIES VAT synchronization

FieldDefaultDescription
Customer group when VAT is validGeneral (group ID 1)Assigned when VIES confirms an active VAT registration for the default billing address. The default is the literal ID 1, not a lookup by name, so on a store where group 1 is something else this points at the wrong group.
Customer group when VAT is invalidset at installAssigned when VIES reports the VAT ID as invalid for the billing country and number used. Set at install to the Tax Exempt group the module creates.

WARNING

Customer group when VAT is invalid has no system default, only the value written at install. Clicking Use system value on it leaves the field empty, and the VIES sync then stops running for that scope with only a log entry to show for it.

EU VAT rules

FieldDefaultDescription
Enable EU VAT rulesYesApplies the module's EU VAT resolution instead of Magento's tax rules for the participating tax classes.
Supplier country (dispatch / origin)LatviaThe country supplies are dispatched from, used to detect intra-EU sales.

Latvia, domestic reverse charge (Art. 143.1)

FieldDefaultDescription
Product tax classesset at installFor buyers in the valid-VAT group with a Latvian shipping address, VAT is forced to 0% for these product tax classes. Other domestic B2B lines fall back to Magento tax rules. Set at install to the LV VAT Law Art. 143.1 class.

B2C cross-border (OSS-style destination VAT)

FieldDefaultDescription
Destination VAT % overrides (JSON)emptyISO2 country code to standard VAT percent, for example {"EE":24,"DE":19}. Merged over the built-in table, with the overrides winning.

Invalid JSON is discarded silently and treated as no overrides. There is no validation message and no log entry, so check the effect on a test order rather than trusting the save.

Company search on the registration page

A Find Company field appears above the address fields on Create Account. The shopper enters a company registration number and the module looks it up.

  1. The Search button stays disabled until the query is at least two characters and the form's country is Latvia, Lithuania, Estonia, or Finland.
  2. The lookup is an exact match on the registration number. There is no name search and no partial match, so a wrong digit returns nothing rather than a list to choose from.
  3. On a match, a modal shows the company name, address, registration number, and VAT number, with Confirm and Continue and Cancel.
  4. On confirm, the form is filled: street, city, postcode, country, region, and company, with the registration number written to taxvat and the VAT number to vat_id.
  5. The search field is then replaced by a summary card with a Change Company button, which clears the filled fields and leaves the country in place.

Region is matched against the region dropdown by name, with accents normalised and retries to survive the dropdown re-rendering after a country change. Latvia, Estonia, and Lithuania have region mappings. Finland has none, so a Finnish lookup fills the address but leaves the region empty.

Address parsing differs per country because DatReal formats them differently: for Finland the city field is authoritative and the address is street only, while for Latvia and Estonia the address is split on its first comma.

If DatReal is unavailable the shopper sees a message saying company lookup is temporarily unavailable and that registration can be completed without it. Any other failure shows "Could not find the company".

VIES VAT validation and customer groups

The module keeps customers in one of the two configured groups based on whether VIES says their default billing VAT ID is registered. Three triggers do this.

TriggerWhen it runs
Account creationAfter the account is created, against the default billing address or the first address
Address save in My AccountOn the address form post only, and it refreshes the session so the storefront sees the new group at once
Nightly cron02:00 every day, in the default cron group

A VAT number that VIES reports as invalid moves the customer to the invalid group. A VIES outage, timeout, or SOAP fault leaves the group untouched, so a service failure never downgrades anyone.

The nightly job rewrites groups for every customer

The cron reads the whole customer_entity table on each run, with no incremental cursor, batching, or limit, and it moves any customer with no VAT ID on their default billing address into the invalid group. A customer manually placed in Wholesale, VIP, or any other B2B tier is moved out of it at 02:00. A customer with no default billing address counts as having no VAT ID and is moved too.

There is no admin setting for the schedule, the batch size, or the delay, and no CLI command to run or skip it. On a store where customer groups are managed by hand, disable this cron job before installing.

The job pauses 250 milliseconds after every customer it looks at, and calls VIES once per customer that has a VAT ID. On 10,000 customers that is over 40 minutes of waiting before any network time is counted.

EU VAT rate rules

With Enable EU VAT rules on, the module answers Magento's tax rate lookup itself for the customer tax classes carried by the valid-VAT group, the invalid-VAT group, and the not-logged-in group, and Magento's own tax rules are bypassed for those. Where two of those groups share a tax class, the same class serves both.

The rules apply only when all of these hold: the field is on, both the origin and the destination country are EU members, and the customer tax class is the one on the valid-VAT group, the one on the invalid-VAT group, or the one on the not-logged-in group. Any other group, including a custom Wholesale or Retailer group, falls through to plain Magento tax rules with no VAT logic applied.

Business buyers in the valid-VAT group:

SituationRate
Destination differs from origin, buyer is not a Latvian VAT registrant0%, as an intra-EU reverse charge
Buyer is a Latvian VAT registrant, product is in an Art. 143.1 tax class0%, as a domestic reverse charge
Buyer is a Latvian VAT registrant, product is not in an Art. 143.1 tax classThe standard rate of the origin country
Destination is LatviaThe Latvian standard rate
Anything else0%

Consumers, meaning the invalid-VAT group and guests: a Latvia to Latvia order on an Art. 143.1 product class gets the Latvian rate, and everything else gets the destination country's standard rate from the built-in table.

A buyer counts as a Latvian VAT registrant when they are logged in, their default billing country is LV, and their VAT ID starts with LV.

Limits to check before going live

The tax engine is built for a Latvian supplier selling into the EU. On any other origin country, the behaviours below change or break.

  • Domestic B2B outside Latvia is charged 0% - With a non-Latvian origin, a business buyer in the origin country falls through to the final 0% case. A German store selling to a German business would charge no VAT.
  • The Latvian registrant rate uses the origin country - A Latvian VAT registrant buying a product outside an Art. 143.1 class is charged the origin country's standard rate, not the destination country's.
  • There is no distance selling threshold and no per-country opt-in - Every EU B2C order gets the destination country's rate as soon as the feature is on. A store below the €10,000 OSS threshold will charge the wrong VAT.
  • One rate per country - The rate table and the override field both hold a single standard rate per country, so reduced rates for books, food, or medicine cannot be expressed. A catalog with mixed rates will mis-calculate cross-border B2C.
  • The rate table is a fixed snapshot - It holds the 27 member states' standard rates, goes stale silently, and the JSON override field is the only way to correct it.
  • The Art. 143.1 and Latvian registrant paths read the frontend customer session - They return false for guests, admin-created orders, REST orders, and cron. An admin placing an order for a Latvian B2B customer does not get the 0% treatment.
  • tax/calculation/based_on is only set once, at install - An admin who later switches it back to Billing Address gets wrong VAT with no warning.

With EU VAT rules on, the module flushes the tax rate cache before each collectTotals(). That keeps the rates correct and costs time on large carts.

Developer notes

Company search endpoint

ItemValue
URLGET /companylookup/company/search
Parametersq, the registration number. country, checked against the four supported codes and falling back to the configured country.
ResponseAlways HTTP 200. {"results":[…]} with zero or one entry, or {"results":[],"error":…} on failure, with "code":"api_maintenance" when DatReal is down.

The route takes no form key and needs no customer session, since it serves the Create Account page where the visitor is by definition not yet a customer.

Frontend integration points

The registration template is a CSP-safe Alpine component named initDatrealCompanySearch, registered through Alpine.data() on alpine:init with the inline script registered via HyvaCsp. Its modal is referenced as datreal-modal.

It dispatches four window events for other modules to listen on:

EventPayload
datreal-company-confirmednone
datreal-address-resolution{regionResolved, postcodeResolved}
datreal-address-prefill-completenone
datreal-cancel-manual-fillnone

It also listens for a datreal-country-changed event and looks for a #billing-datreal-hidden-fields element to switch into a second fill mode. Neither is provided by this module. The event has no dispatcher here and the element is never rendered here, so that second mode stays inactive unless another module or the theme supplies them.

Overlap with User Type Switcher

There is no code dependency between this module and User Type Switcher, but both write to the same company and vat_id address fields and both can change tax for business customers. Running the two together needs testing, since each is unaware of the other's tax decisions.

Troubleshooting

The company search box does not appear

The block is attached to customer_form_register as an unnamed child, so it renders only if the theme's register template outputs unnamed children. Confirm the active theme is Venta Theme or a child of it. If the address fields are missing as well, the layout override that forces them on is not being applied.

Search does nothing and the button stays greyed out

The button needs at least two characters and a supported country selected on the form. Latvia, Lithuania, Estonia, and Finland are the only countries DatReal is queried for.

A company that exists returns nothing

The lookup is an exact match on the registration number. Check the number digit by digit, and check that the country selected on the form is the company's country of registration.

Customer groups keep changing back overnight

That is the nightly VIES sync. Any customer whose default billing address has no VAT ID is moved to the invalid-VAT group on every run. Disable the magebit_company_lookup_vies_tax_groups cron job if groups are managed by hand.

VAT is wrong on an order

  • Confirm which customer tax class the buyer's group carries. Only the classes on the valid-VAT, invalid-VAT and not-logged-in groups get the module's rules; everything else uses Magento tax rules.
  • Confirm tax/calculation/based_on is still shipping. The module sets it at install and never rechecks it.
  • Check Stores > Tax Rules for the rule the installer created, since it uses a hardcoded product tax class ID that may not match your store.
  • Review Limits to check before going live. A non-Latvian origin store hits several cases the engine does not handle.

The cross-border VAT rate is not the one I configured

  • Invalid JSON in Destination VAT % overrides (JSON) is discarded silently, with no validation message and no log entry. Re-check the value parses, then confirm the effect on a test order rather than trusting the save.
  • The overrides are merged over the built-in table, so a country absent from your JSON still gets the built-in rate.
  • Only one standard rate per country can be expressed. A reduced-rate product will not get its reduced rate from this module.

Address fields reappeared on Create Account

The module forces show_address_fields on customer_form_register, because the company lookup has nothing to fill without them. A store that deliberately hid those fields gets them back on install. Removing the layout override also removes the lookup's ability to autofill.

The installer's tax rule is missing

Magebit EU B2B LV Domestic Standard VAT is only created when the store already has at least one Latvian tax rate. On a store with no LV rate the patch skips it without a message, and Stores > Tax Rules will show no such rule. Add the Latvian tax rates first, then re-run setup:upgrade, or create the rule by hand.

Region is empty after a lookup

Finland has no region mapping, so Finnish lookups leave the region blank. Latvia, Estonia, and Lithuania are mapped.