Giter Site home page Giter Site logo

pyvat's Introduction

pyvat -- VAT validation and calculation for Python

image

With EU VAT handling rules becoming ever more ridiculous and complicated, businesses within the EU are faced with the complexity of having to validate VAT numbers. pyvat was built for Iconfinder's marketplace to handle just this problem.

Validation of VAT numbers is performed in two steps: firstly, the VAT number is checked against an expression for the given country if one such is available, after which it is checked against a registry if one such is available.

Calculation of VAT rates for sales is supported within the EU for items covered by the new EU directive for VAT on telecommunications, broadcasting and electronic services.

Installation

To install requests, do yourself a favor and don't use anything other than pip:

$ pip install pyvat

Usage

pyvat exposes its functionality through three simple methods:

pyvat.check_vat_number(vat_number, country_code=None)

Test if a VAT number is valid.

If possible, the VAT number will be checked against available registries.

Parameters
  • vat_number -- VAT number to validate.
  • country_code -- Optional country code. Should be supplied if known, as there is no guarantee that naively entered VAT numbers contain the correct alpha-2 country code prefix for EU countries just as not all non-EU countries have a reliable country code prefix. Default None prompting detection.
Returns

True if the VAT number can be fully asserted as valid or False if not, otherwise None indicating that the VAT number may or may not be valid.

pyvat.is_vat_number_format_valid(vat_number, country_code=None)

Test if the format of a VAT number is valid.

Parameters
  • vat_number -- VAT number to validate.
  • country_code -- Optional country code. Should be supplied if known, as there is no guarantee that naively entered VAT numbers contain the correct alpha-2 country code prefix for EU countries just as not all non-EU countries have a reliable country code prefix. Default None prompting detection.
Returns

True if the VAT number can be fully asserted as valid or False if not, otherwise None indicating that the VAT number may or may not be valid.

pyvat.get_sale_vat_charge(date, item_type, buyer, seller)

Get the VAT charge for performing the sale of an item.

Currently only supports determination of the VAT charge for telecommunications, broadcasting and electronic services in the EU.

Parameters
  • date (datetime.date) -- Sale date.
  • item_type (pyvat.ItemType) -- Type of the item being sold.
  • buyer (pyvat.Party) -- Buyer.
  • seller (pyvat.Party) -- Seller.
Returns

The VAT charge to be applied to the sale of an item.

For more detailed documentation, see the full pyvat documentation.

pyvat's People

Contributors

aladagemre avatar alexander-schillemans avatar evanpurkhiser avatar janmanx avatar jastertdc avatar jbma avatar kdeldycke avatar martinleblanc avatar matiaslaurarasmussen avatar mrmebelman avatar nickbruun avatar ojarva avatar rathcke avatar sniku avatar zoomie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyvat's Issues

ServerError

I have 36,000 VATs that I need to verify. I split the data into chunks of 100 with a time gap of 2 seconds. Even then, I am encountering the error ServerError: MS_MAX_CONCURRENT_REQ.

How do I get around this?

Croation Vat Numbers are always invalid.

Valid VAT numbers for Croatia don't seem to be valid according to your libraries.

I have tried to track down this issue. It looks like pyvat.VAT_NUMBER_EXPRESSIONS['hr'] is not set.

We might send you a pull request for this if you're ok.

pyvat.exceptions.ServerError: ServerError: MS_UNAVAILABLE

  File "/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/pyvat/__init__.py", line 212, in check_vat_number
    country_code)
  File "/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/pyvat/registries.py", line 136, in check_vat_number
    raise ServerError(fault_code)
pyvat.exceptions.ServerError: ServerError: MS_UNAVAILABLE

Hi, I am getting this error from a few days. Can someone confirm this problem?

GB no longer in EU issues

Not sure if this is intended behaviour, but with the UK leaving the EU and the recent changes using the PyVAT to check for vat numbers in the GB now throws and error:

NotImplementedError: cannot determine VAT charge for a sale of item <ItemType.generic_electronic_service: 2> between <pyvat.Party: country code = GB, is business = True> and <pyvat.Party: country code = GB, is business = True>

This is with Pyvat v1.3.7.

Is the library no longer supporting GB vat?

Brexit

Hello there and thanks for developing this great package!

Brexit will be finalised on December 31, 2020.

Are there any plans to remove UK from a list of EU countries? Or better yet, configure it to stay there until Dec 31?

Thank you!

pyvat.exceptions.ServerError: ServerError: MS_MAX_CONCURRENT_REQ

Hi,

getting this error from time to time. What can we do?

    response = pyvat.check_vat_number(self.ustid)
  File "/usr/local/lib/python3.9/site-packages/pyvat/__init__.py", line 211, in check_vat_number
    return VAT_REGISTRIES[country_code].check_vat_number(vat_number,
  File "/usr/local/lib/python3.9/site-packages/pyvat/registries.py", line 136, in check_vat_number
    raise ServerError(fault_code)
pyvat.exceptions.ServerError: ServerError: MS_MAX_CONCURRENT_REQ

Support for OSS VAT numbers starting with EU

Some companies that have no physical establishment in EU are still subject to VAT and the EU provide them a VAT number starting with "EU", e.g. : EU372041333 for OpenAI

Would it be possible to add support for this kind of VAT number ?

Add support for selling from non-EU countries

We (ungleich.ch) are interested in using this module with residency in CH. Basically the rule set that applies is:

  • CH to CH sale: 7.7% at the moment (8% before) for B2C and B2B
  • CH to EU: as defined by EU rules
  • CH to UK: a) no VAT b) VAT similar to EU rules - we got contradicting information from the UK government about this. One person claims it's like (b), their website says otherwise if you are under a certain revenue limit.
  • CH to non-EU-non-UK: 0%
  • non-EU,EU to CH: (to be verified): 0%

I believe that CH as a non-EU country could become a sample of a standard case for trading from outside the EU, as I suspect that ruleset for other countries are very similar.

Are you open for integrating non-EU rules into this module, too?

Bug: Austian VAT validation is wrong implemented

Hi,

pyvat.is_vat_number_format_valid("ATU76127777") => is True

pyvat.is_vat_number_format_valid("76127777", country_code="AT") => is False but should be also True

I could private a fix if the maintainer except that this is a bug.

Is this project?

Hi,

before I am going to start a new fork I wanted to ask if this package is maintained by anyone?

get_sale_vat_charge raise NotImplementedError for Danish electronic services

As the doc says "Currently only supports determination of the VAT charge for telecommunications, broadcasting and electronic services in the EU." I expected to get the right vat rate doing:

import pyvat
import datetime
pyvat.get_sale_vat_charge(datetime.datetime.now(), pyvat.ItemType.generic_electronic_service, pyvat.Party("DK", True), pyvat.Party("DK", False))

but I'm getting:

NotImplementedError: cannot determine VAT charge for a sale of item ItemType.generic_electronic_service: 2 between pyvat.Party: country code = DK, is business = False and pyvat.Party: country code = DK, is business = True

shouldn't I get a valid VAT charge for this case?

Ireland VAT changes not included in official release.

Could you cut a release that includes the Ireland VAT changes from February? I'm looking to repackage this for the fedora ecosystm and I'd like to be able to pull from an official release.

I think just tagging and releasing current master as 1.3.16 would be fine:

image

VIES has changed XML response format

Seems as of today the VIES service has changed their XML response format.

pyvat.check_vat_number("DK12345678")
ValueError: expected response XML root element to be a SOAP envelope

The XML appears to be missing the soap:Envelope which the current implementation expects:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Header/><env:Body><ns2:checkVatResponse xmlns:ns2="urn:ec.europa.eu:taxud:vies:services:checkVat:types"><ns2:countryCode>DK</ns2:countryCode><ns2:vatNumber></ns2:vatNumber><ns2:requestDate>2022-08-12+02:00</ns2:requestDate><ns2:valid>false</ns2:valid><ns2:name>---</ns2:name><ns2:address>---</ns2:address></ns2:checkVatResponse></env:Body></env:Envelope>

Old version on pip

Hey,
It looks like your pip package is not updated, it's the 1.3.1 version from 2014, with the old VAT rates.
Would it be possible to update it?

VIES registry mocking

Unittests might fail if the VIES registry API is down. This happened to me in the last few weeks.

To prevent this failure, I mocked registries responses with the code:

import unittest
import arrow
from httmock import all_requests, response, HTTMock
try:
    from lxml import etree
except ImportError:
    from xml.etree import ElementTree as etree
from pyvat import check_vat_number, is_vat_number_format_valid


class TestVIES(unittest.TestCase):

    @all_requests
    def mock_registry_response(self, url, request):
        """ Mock VIES registry responses. """
        # Parse request
        soap_query = etree.fromstring(request.body)
        VAT_NS = '{urn:ec.europa.eu:taxud:vies:services:checkVat:types}'
        country_code = soap_query.find('.//{}countryCode'.format(VAT_NS)).text
        vat_number = soap_query.find('.//{}vatNumber'.format(VAT_NS)).text

        # Check VAT number
        assert is_vat_number_format_valid(vat_number, country_code) is True

        # Normalize parsed values
        country_code = country_code.strip().upper()
        vat_number = ''.join([c for c in vat_number if c.isalnum()]).upper()

        # Build-up response
        request_date = arrow.now().format('YYYY-MM-DDZZ')
        company_name = 'ICONFINDER ApS'
        company_address = 'Pilestr\xe6de 43 2\n1112 K\xf8benhavn K'
        content = \
            '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/' \
            'envelope/"><soap:Body><checkVatResponse xmlns="urn:ec.europ' \
            'a.eu:taxud:vies:services:checkVat:types"><countryCode>{}</c' \
            'ountryCode><vatNumber>{}</vatNumber><requestDate>{}</reques' \
            'tDate><valid>true</valid><name>{}</name><address>{}</addres' \
            's></checkVatResponse></soap:Body></soap:Envelope>'.format(
                country_code, vat_number, request_date, company_name,
                company_address)
        headers = {'Content-Type': 'text/xml'}
        return response(200, content, headers, None, 5, request)

    def test_vies_response_parsing(self):
        with HTTMock(self.mock_registry_response):
            results = check_vat_number('DK 33 77 94 37', 'DK')
            self.assertEqual(results.business_name, 'ICONFINDER ApS')
            self.assertEqual(
                results.business_address,
                'Pilestr\xe6de 43 2\n1112 K\xf8benhavn K')

Feel free to reuse it in pyvat if appropriate.

Test occasionally fail as the VIES API seem to rate limit the requests

======================================================================
1) ERROR: check_vat_number('..', country_code='..')
----------------------------------------------------------------------
   Traceback (most recent call last):
    tests/test_validators.py line 257 in test_dk__country_code
      check_vat_number(vat_number, country_code)
    pyvat/__init__.py line 212 in check_vat_number
      country_code)
    pyvat/registries.py line 133 in check_vat_number
      raise ServerError(fault_code)
   ServerError: ServerError: MS_MAX_CONCURRENT_REQ
   -------------------- >> begin captured logging << --------------------
   urllib3.connectionpool: DEBUG: Starting new HTTP connection (1): ec.europa.eu:80
   urllib3.connectionpool: DEBUG: http://ec.europa.eu:80 "POST /taxation_customs/vies/services/checkVatService HTTP/1.1" 200 None
   --------------------- >> end captured logging << ---------------------
-----------------------------------------------------------------------------

TypeError: super() takes at least 1 argument (0 given)

We're getting the following errors for our that use pyvat on our CI server, but I'm unable to reproduce the failure locally. However, before even figuring out why VIES service is returning server error, the error thrown below seems to originate in wrongly used super call, though I don't see what exactly is wrong there

  File "/home/runner/.local/lib/python2.7/site-packages/pyvat/__init__.py", line 212, in check_vat_number
    country_code)
  File "/home/runner/.local/lib/python2.7/site-packages/pyvat/registries.py", line 133, in check_vat_number
    raise ServerError(fault_code)
  File "/home/runner/.local/lib/python2.7/site-packages/pyvat/exceptions.py", line 3, in __init__
    super().__init__("ServerError: {}".format(fault_code))
TypeError: super() takes at least 1 argument (0 given)

Any idea what's happening here?

Support Isle of Man and Monaco VAT numbers

Isle of Man (country code: IM) share its VAT registry with the UK. Same thing for Monaco (country code: MC), which is sharing its registry with France.

These exceptions are not supported by pyvat:

>>> from pyvat import is_vat_number_format_valid
>>> print is_vat_number_format_valid('GB 000 8920 15', 'IM')
None
>>> print is_vat_number_format_valid('FR 12 0000 4520 2', 'MC')
None

Here I expect is_vat_number_format_valid() method to return True in the two example above, as if the query was performed against the canonical country:

>>> print is_vat_number_format_valid('FR 12 0000 4520 2', 'FR')
True
>>> print is_vat_number_format_valid('GB 000 8920 15', 'GB')
True

pyvat uses insecure api endpoint

The library connects to the vat validation API via http only.
This of course has some performance benefits over https, but is less secure.

In my opinion the user of the library should be able to decide whether to use http or https.
Https should be the default, though.

Finnish (FI) VAT codes are not validating properly

Hi,
I tried validating this VAT code for Finland: '12345678--' and it returned True, even though it contains two dashes in the end (which is not ok).
Maybe you could add this to the test tests/test_validators.py for FI (Finland):

('12345678--', False),

Packaging pyvat into an RPM to upload to Fedora repo

Hello folks,

I am not sure what would be the best way to contact the maintainers of the package, but I want to let you know that I am creating an RPM package with pyvat which will be called python3-pyvat. I created a ticket in bugzilla for this https://bugzilla.redhat.com/show_bug.cgi?id=2109069 and it will eventually be available for Fedora.
There is not much action need from your side other than reading through this message and possibly advertising the availability in Fedora when that happens. Also, if you see any issues with having pyvat packaged please let me know slightly_smiling_face

Cheers

PyPI release

The latest version on PyPI is 1.3.1 released back in 2014. But, I see there have been several improvements since then. Are you guys planning on releasing the newest version to PyPI?

Wrong Documentation

According to the documentation, the return value of check_vat_number is either True, False or None.
Actually, it returns a VatNumberCheckResult object.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.