8000 Add Indian GSTIN (VAT number) · cedk/python-stdnum@26a7e7b · GitHub
[go: up one dir, main page]

Skip to content

Commit 26a7e7b

Browse files
vairag22arthurdejong
authored andcommitted
Add Indian GSTIN (VAT number)
Closes arthurdejong#279
1 parent ca560cd commit 26a7e7b

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

stdnum/in_/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@
1919
# 02110-1301 USA
2020

2121
"""Collection of Indian numbers."""
22+
23+
# provide aliases
24+
from stdnum.in_ import gstin as vat # noqa: F401

stdnum/in_/gstin.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# gstin.py - functions for handling Indian VAT numbers
2+
#
3+
# Copyright (C) 2021 Gaurav Chauhan
4+
#
5+
# This library is free software; you can redistribute it and/or
6+
# modify it under the terms of the GNU Lesser General Public
7+
# License as published by the Free Software Foundation; either
8+
# version 2.1 of the License, or (at your option) any later version.
9+
#
10+
# This library is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
# Lesser General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU Lesser General Public
16+
# License along with this library; if not, write to the Free Software
17+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18+
# 02110-1301 USA
19+
20+
"""GSTIN (Goods and Services Tax identification number, Indian VAT number).
21+
22+
The Goods and Services Tax identification number (GSTIN) is a 15 digit unique
23+
identifier assigned to all business entities in India registered under the
24+
Goods and Services Tax (GST) Act, 2017.
25+
26+
Each GSTIN begins with a 2 digit state code, the next 10 characters are the
27+
holder's PAN, the 13th character is an alphanumeric digit that represents the
28+
number of GSTIN registrations made in a state or union territory for same the
29+
PAN, the 14th character is 'Z' and the last character is an alphanumeric
30+
check digit calculated using Luhn mod 36 algorithm.
31+
32+
More information:
33+
34+
* https://bajajfinserv.in/insights/what-is-goods-and-service-tax-identification-number
35+
* https://ddvat.gov.in/docs/List%20of%20State%20Code.pdf
36+
* https://en.wikipedia.org/wiki/Goods_and_Services_Tax_(India)
37+
38+
>>> validate('27AAPFU0939F1ZV')
39+
'27AAPFU0939F1ZV'
40+
>>> validate('27AAPFU0939F1Z')
41+
Traceback (most recent call last):
42+
...
43+
InvalidLength: ...
44+
>>> validate('369296450896540')
45+
Traceback (most recent call last):
46+
...
47+
InvalidFormat: ...
48+
>>> validate('27AAPFU0939F1AA')
49+
Traceback (most recent call last):
50+
...
51+
InvalidComponent: ...
52+
>>> validate('27AAPFU0939F1ZO')
53+
Traceback (most recent call last):
54+
...
55+
InvalidChecksum: ...
56+
>>> to_pan('27AAPFU0939F1ZV')
57+
'AAPFU0939F'
58+
>>> info('27AAPFU0939F1ZV')['state']
59+
'Maharashtra'
60+
"""
61+
62+
import re
63+
64+
from stdnum import luhn
65+
from stdnum.exceptions import *
66+
from stdnum.in_ import pan
67+
from stdnum.util import clean
68+
69+
70+
_GSTIN_RE = re.compile(r'^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][0-9A-Z]{3}$')
71+
72+
_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
73+
74+
_STATE_CODES = {
75+
'01': 'Jammu and Kashmir',
76+
'02': 'Himachal Pradesh',
77+
'03': 'Punjab',
78+
'04': 'Chandigarh',
79+
'05': 'Uttarakhand',
80+
'06': 'Haryana',
81+
'07': 'Delhi',
82+
'08': 'Rajasthan',
83+
'09': 'Uttar Pradesh',
84+
'10': 'Bihar',
85+
'11': 'Sikkim',
86+
'12': 'Arunachal Pradesh',
87+
'13': 'Nagaland',
88+
'14': 'Manipur',
89+
'15': 'Mizoram',
90+
'16': 'Tripura',
91+
'17': 'Meghalaya',
92+
'18': 'Assam',
93+
'19': 'West Bengal',
94+
'20': 'Jharkhand',
95+
'21': 'Orissa',
96+
'22': 'Chattisgarh',
97+
'23': 'Madhya Pradesh',
98+
'24': 'Gujarat',
99+
'25': 'Daman and Diu',
100+
'26': 'Dadar and Nagar Haveli',
101+
'27': 'Maharashtra',
102+
'28': 'Andhra Pradesh',
103+
'29': 'Karnataka',
104+
'30': 'Goa',
105+
'31': 'Lakshadweep',
106+
'32': 'Kerala',
107+
'33': 'Tamil Nadu',
108+
'34': 'Puducherry',
109+
'35': 'Anadaman and Nicobar Islands',
110+
'36': 'Telangana',
111+
'37': 'Andhra Pradesh (New)',
112+
}
113+
114+
115+
def compact(number):
116+
"""Convert the number to the minimal representation. This strips the
117+
number of any valid separators and removes surrounding whitespace."""
118+
return clean(number, ' -').upper().strip()
119+
120+
121+
def validate(number):
122+
"""Check if the number provided is a valid GSTIN. This checks the length,
123+
formatting and check digit."""
124+
number = compact(number)
125+
if len(number) != 15:
126+
raise InvalidLength()
127+
if not _GSTIN_RE.match(number):
128+
raise InvalidFormat()
129+
if number[:2] not in _STATE_CODES or number[12] == '0' or number[13] != 'Z':
130+
raise InvalidComponent()
131+
pan.validate(number[2:12])
132+
luhn.validate(number, _ALPHABET)
133+
return number
134+
135+
136+
def is_valid(number):
137+
"""Check if the number provided is a valid GSTIN. This checks the length,
138+
formatting and check digit."""
139+
try:
140+
return bool(validate(number))
141+
except ValidationError:
142+
return False
143+
144+
145+
def to_pan(number):
146+
"""Convert the number to a PAN."""
147+
number = compact(number)
148+
return number[2:12]
149+
150+
151+
def info(number):
152+
"""Provide information that can be decoded locally from GSTIN (without
153+
API)."""
154+
number = validate(number)
155+
return {
156+
'state': _STATE_CODES.get(number[:2]),
157+
'pan': number[2:12],
158+
'holder_type': pan.info(number[2:12])['holder_type'],
159+
'initial': number[6],
160+
'registration_count': _ALPHABET.index(number[12]),
161+
}

0 commit comments

Comments
 (0)
0