Source code for vat_validator.vies

"""
This module allows interaction with VIES (VAT Information Exchange Service)
web service. It can be used to validate VAT codes and fetch other information
from the entity with that VAT.

**Usage**:


>>> check_vat('PT', '502011378')
CheckVATResult(country_code='PT',
               vat='502011378',
               request_date=2019-08-01 00:00:00+02:00,
               valid=True,
               name='UNIVERSIDADE DO MINHO',
               address='LG DO PACO\\nBRAGA\\n4700-320 BRAGA')

.. seealso::

   VIES on the European Comission website:
   http://ec.europa.eu/taxation_customs/vies/

   The functions hereby described operate on this WSDL url:
   http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
"""
import xml.etree.ElementTree as ET
from datetime import date, datetime
from typing import Optional
from urllib import request

from vat_validator.countries import EU_COUNTRY_CODES
from vat_validator.sanitizers import sanitize_vat


[docs]class CheckVATResult: """Represents the result obtained by running the function :func:`check_vat`. :param country_code: IS0 3166 country code. :param vat: VAT number. :param request_date: date when this result was queried. :param valid: whether this VAT is valid or not. :param name: optional name of the entity associated with this VAT. :param address: optional address of the entity associated with this VAT. """ def __init__( self, country_code: str, vat: str, request_date: Optional[date], valid: bool, name: Optional[str], address: Optional[str], ): self.country_code = country_code self.vat = vat self.request_date = request_date self.valid = valid self.name = name self.address = address def __eq__(self, other) -> bool: if not isinstance(other, CheckVATResult): return False return ( self.country_code == other.country_code and self.vat == other.vat and self.request_date == other.request_date and self.valid == other.valid and self.name == other.name and self.address == other.address ) def __repr__(self) -> str: fields = ", ".join( "{}={}".format(attr, repr(value)) for attr, value in vars(self).items() ) return "CheckVATResult({})".format(fields)
[docs]def check_vat(country_code: str, vat: str) -> CheckVATResult: """Checks if given VAT code is valid and (if possible) fetches the name and address of the entity with that code. :param country_code: valid IS0 3166 country code. :param vat: VAT number to check. :return: instance of :class:`CheckVATResult`. :raises ValueError: when the country code is invalid or the VAT is empty. """ if country_code not in EU_COUNTRY_CODES: raise ValueError("Invalid country code: '{}'".format(country_code)) sanitized_vat = sanitize_vat(country_code, vat) # Prepare the SOAP request envelope = ET.Element( "soapenv:Envelope", attrib={ "xmlns:hs": "urn:ec.europa.eu:taxud:vies:services:checkVat:types", "xmlns:soapenv": "http://schemas.xmlsoap.org/soap/envelope/", }, ) body = ET.SubElement(envelope, "soapenv:Body") action = ET.SubElement(body, "hs:checkVat") ET.SubElement(action, "hs:countryCode").text = country_code ET.SubElement(action, "hs:vatNumber").text = sanitized_vat payload = ET.tostring(envelope, encoding="utf-8") # Send the SOAP request to VIES Webservice url = "http://ec.europa.eu/taxation_customs/vies/services/checkVatService" req = request.Request(url, data=payload) res = request.urlopen(req) # Parse the result res_envelope = ET.fromstring(res.read()) namespace = "urn:ec.europa.eu:taxud:vies:services:checkVat:types" request_date = res_envelope.find(".//{{{}}}requestDate".format(namespace)) valid = res_envelope.find(".//{{{}}}valid".format(namespace)) name = res_envelope.find(".//{{{}}}name".format(namespace)) address = res_envelope.find(".//{{{}}}address".format(namespace)) return CheckVATResult( country_code=country_code, vat=sanitized_vat, request_date=datetime.strptime(request_date.text, "%Y-%m-%d%z") if request_date is not None and request_date.text is not None else None, valid=valid is not None and valid.text == "true", name=name.text if name is not None else None, address=address.text if address is not None else None, )