india-kit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- india_kit-0.1.0/.gitignore +11 -0
- india_kit-0.1.0/PKG-INFO +65 -0
- india_kit-0.1.0/README.md +44 -0
- india_kit-0.1.0/india_kit/__init__.py +19 -0
- india_kit-0.1.0/india_kit/address/__init__.py +3 -0
- india_kit-0.1.0/india_kit/address/pincode.py +29 -0
- india_kit-0.1.0/india_kit/banking/__init__.py +4 -0
- india_kit-0.1.0/india_kit/banking/ifsc.py +25 -0
- india_kit-0.1.0/india_kit/banking/upi.py +11 -0
- india_kit-0.1.0/india_kit/finance/__init__.py +3 -0
- india_kit-0.1.0/india_kit/finance/gst.py +6 -0
- india_kit-0.1.0/india_kit/identity/__init__.py +9 -0
- india_kit-0.1.0/india_kit/identity/aadhaar.py +66 -0
- india_kit-0.1.0/india_kit/identity/gstin.py +35 -0
- india_kit-0.1.0/india_kit/identity/pan.py +30 -0
- india_kit-0.1.0/pyproject.toml +38 -0
- india_kit-0.1.0/tests/__init__.py +0 -0
- india_kit-0.1.0/tests/test_aadhaar.py +22 -0
- india_kit-0.1.0/tests/test_pan.py +19 -0
- india_kit-0.1.0/tests/test_validators.py +63 -0
india_kit-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: india-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The definitive Indian developer toolkit — validate Aadhaar, PAN, GSTIN, IFSC, UPI, pincode and more
|
|
5
|
+
Project-URL: Homepage, https://github.com/Inreal-Solutions/India-kit
|
|
6
|
+
Project-URL: Repository, https://github.com/Inreal-Solutions/India-kit
|
|
7
|
+
Project-URL: Issues, https://github.com/Inreal-Solutions/India-kit/issues
|
|
8
|
+
Author-email: Inreal Solutions <aryan23062001@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: aadhaar,fintech,gst,gstin,ifsc,india,indian,pan,pincode,upi,validate,validator
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# india-kit
|
|
23
|
+
|
|
24
|
+
The definitive Indian developer toolkit for Python. Validate Aadhaar, PAN, GSTIN, IFSC, UPI, pincode and more — zero dependencies, fully typed.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install india-kit
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from india_kit import (
|
|
36
|
+
validate_pan, mask_pan, mock_pan,
|
|
37
|
+
validate_aadhaar, mock_aadhaar,
|
|
38
|
+
validate_gstin, mock_gstin,
|
|
39
|
+
validate_ifsc, lookup_ifsc,
|
|
40
|
+
validate_upi,
|
|
41
|
+
validate_pincode, lookup_pincode,
|
|
42
|
+
split_gst,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
validate_pan("ABCDE1234F") # {"valid": True}
|
|
46
|
+
validate_aadhaar("234123412346") # {"valid": True/False}
|
|
47
|
+
validate_gstin("27ABCDE1234F1Z5") # {"valid": True/False}
|
|
48
|
+
validate_ifsc("SBIN0000001") # {"valid": True}
|
|
49
|
+
validate_upi("alice@icici") # {"valid": True}
|
|
50
|
+
validate_pincode("110001") # {"valid": True}
|
|
51
|
+
|
|
52
|
+
lookup_ifsc("SBIN0000001") # {"bank": "State Bank of India", ...}
|
|
53
|
+
lookup_pincode("110001") # {"district": "Central Delhi", "state": "Delhi"}
|
|
54
|
+
|
|
55
|
+
split_gst(1000, 18) # {"cgst": 90.0, "sgst": 90.0, "igst": 0.0, "total_tax": 180.0}
|
|
56
|
+
split_gst(1000, 18, intra_state=False) # {"cgst": 0.0, "sgst": 0.0, "igst": 180.0, ...}
|
|
57
|
+
|
|
58
|
+
mask_pan("ABCDE1234F") # "ABCXX1234F"
|
|
59
|
+
mock_pan() # random valid PAN
|
|
60
|
+
mock_aadhaar() # random valid Aadhaar
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# india-kit
|
|
2
|
+
|
|
3
|
+
The definitive Indian developer toolkit for Python. Validate Aadhaar, PAN, GSTIN, IFSC, UPI, pincode and more — zero dependencies, fully typed.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install india-kit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from india_kit import (
|
|
15
|
+
validate_pan, mask_pan, mock_pan,
|
|
16
|
+
validate_aadhaar, mock_aadhaar,
|
|
17
|
+
validate_gstin, mock_gstin,
|
|
18
|
+
validate_ifsc, lookup_ifsc,
|
|
19
|
+
validate_upi,
|
|
20
|
+
validate_pincode, lookup_pincode,
|
|
21
|
+
split_gst,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
validate_pan("ABCDE1234F") # {"valid": True}
|
|
25
|
+
validate_aadhaar("234123412346") # {"valid": True/False}
|
|
26
|
+
validate_gstin("27ABCDE1234F1Z5") # {"valid": True/False}
|
|
27
|
+
validate_ifsc("SBIN0000001") # {"valid": True}
|
|
28
|
+
validate_upi("alice@icici") # {"valid": True}
|
|
29
|
+
validate_pincode("110001") # {"valid": True}
|
|
30
|
+
|
|
31
|
+
lookup_ifsc("SBIN0000001") # {"bank": "State Bank of India", ...}
|
|
32
|
+
lookup_pincode("110001") # {"district": "Central Delhi", "state": "Delhi"}
|
|
33
|
+
|
|
34
|
+
split_gst(1000, 18) # {"cgst": 90.0, "sgst": 90.0, "igst": 0.0, "total_tax": 180.0}
|
|
35
|
+
split_gst(1000, 18, intra_state=False) # {"cgst": 0.0, "sgst": 0.0, "igst": 180.0, ...}
|
|
36
|
+
|
|
37
|
+
mask_pan("ABCDE1234F") # "ABCXX1234F"
|
|
38
|
+
mock_pan() # random valid PAN
|
|
39
|
+
mock_aadhaar() # random valid Aadhaar
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .identity.pan import validate_pan, mask_pan, mock_pan
|
|
2
|
+
from .identity.aadhaar import validate_aadhaar, mock_aadhaar
|
|
3
|
+
from .identity.gstin import validate_gstin, mock_gstin
|
|
4
|
+
from .banking.ifsc import validate_ifsc, lookup_ifsc
|
|
5
|
+
from .banking.upi import validate_upi
|
|
6
|
+
from .address.pincode import validate_pincode, lookup_pincode
|
|
7
|
+
from .finance.gst import split_gst
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"validate_pan", "mask_pan", "mock_pan",
|
|
13
|
+
"validate_aadhaar", "mock_aadhaar",
|
|
14
|
+
"validate_gstin", "mock_gstin",
|
|
15
|
+
"validate_ifsc", "lookup_ifsc",
|
|
16
|
+
"validate_upi",
|
|
17
|
+
"validate_pincode", "lookup_pincode",
|
|
18
|
+
"split_gst",
|
|
19
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
_PINCODE_RE = re.compile(r'^[1-9][0-9]{5}$')
|
|
4
|
+
|
|
5
|
+
_PINCODE_DB = {
|
|
6
|
+
"110001": {"district": "Central Delhi", "state": "Delhi"},
|
|
7
|
+
"400001": {"district": "Mumbai City", "state": "Maharashtra"},
|
|
8
|
+
"560001": {"district": "Bangalore Urban", "state": "Karnataka"},
|
|
9
|
+
"600001": {"district": "Chennai", "state": "Tamil Nadu"},
|
|
10
|
+
"500001": {"district": "Hyderabad", "state": "Telangana"},
|
|
11
|
+
"700001": {"district": "Kolkata", "state": "West Bengal"},
|
|
12
|
+
"380001": {"district": "Ahmedabad", "state": "Gujarat"},
|
|
13
|
+
"411001": {"district": "Pune", "state": "Maharashtra"},
|
|
14
|
+
"302001": {"district": "Jaipur", "state": "Rajasthan"},
|
|
15
|
+
"226001": {"district": "Lucknow", "state": "Uttar Pradesh"},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_pincode(pin: str) -> dict:
|
|
20
|
+
if not isinstance(pin, str):
|
|
21
|
+
return {"valid": False, "reason": "must be a string"}
|
|
22
|
+
p = pin.strip()
|
|
23
|
+
if not _PINCODE_RE.match(p):
|
|
24
|
+
return {"valid": False, "reason": "invalid pincode (must be 6 digits, first digit 1-9)"}
|
|
25
|
+
return {"valid": True}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def lookup_pincode(pin: str) -> dict | None:
|
|
29
|
+
return _PINCODE_DB.get(pin.strip())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
_IFSC_RE = re.compile(r'^[A-Z]{4}0[A-Z0-9]{6}$')
|
|
4
|
+
|
|
5
|
+
_IFSC_DB = {
|
|
6
|
+
"SBIN0000001": {"bank": "State Bank of India", "branch": "Mumbai Main", "city": "Mumbai"},
|
|
7
|
+
"HDFC0000002": {"bank": "HDFC Bank", "branch": "Delhi Main", "city": "Delhi"},
|
|
8
|
+
"ICIC0000003": {"bank": "ICICI Bank", "branch": "Bangalore Main", "city": "Bangalore"},
|
|
9
|
+
"AXIS0000004": {"bank": "Axis Bank", "branch": "Chennai Main", "city": "Chennai"},
|
|
10
|
+
"KKBK0000005": {"bank": "Kotak Mahindra Bank", "branch": "Pune Main", "city": "Pune"},
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def validate_ifsc(ifsc: str) -> dict:
|
|
15
|
+
if not isinstance(ifsc, str):
|
|
16
|
+
return {"valid": False, "reason": "must be a string"}
|
|
17
|
+
i = ifsc.strip().upper()
|
|
18
|
+
if not _IFSC_RE.match(i):
|
|
19
|
+
return {"valid": False, "reason": "invalid IFSC format (expected AAAA0XXXXXX)"}
|
|
20
|
+
return {"valid": True}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def lookup_ifsc(ifsc: str) -> dict | None:
|
|
24
|
+
i = ifsc.strip().upper()
|
|
25
|
+
return _IFSC_DB.get(i)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
_UPI_RE = re.compile(r'^[a-zA-Z0-9._+%\-]{2,}@[a-zA-Z]{2,}$')
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def validate_upi(vpa: str) -> dict:
|
|
7
|
+
if not isinstance(vpa, str):
|
|
8
|
+
return {"valid": False, "reason": "must be a string"}
|
|
9
|
+
if not _UPI_RE.match(vpa.strip()):
|
|
10
|
+
return {"valid": False, "reason": "invalid UPI VPA format (expected localpart@bank)"}
|
|
11
|
+
return {"valid": True}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
def split_gst(amount: float, rate_percent: float, intra_state: bool = True) -> dict:
|
|
2
|
+
total_tax = round(amount * rate_percent / 100, 2)
|
|
3
|
+
if intra_state:
|
|
4
|
+
half = round(total_tax / 2, 2)
|
|
5
|
+
return {"cgst": half, "sgst": half, "igst": 0.0, "total_tax": total_tax}
|
|
6
|
+
return {"cgst": 0.0, "sgst": 0.0, "igst": total_tax, "total_tax": total_tax}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from .pan import validate_pan, mask_pan, mock_pan
|
|
2
|
+
from .aadhaar import validate_aadhaar, mock_aadhaar
|
|
3
|
+
from .gstin import validate_gstin, mock_gstin
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"validate_pan", "mask_pan", "mock_pan",
|
|
7
|
+
"validate_aadhaar", "mock_aadhaar",
|
|
8
|
+
"validate_gstin", "mock_gstin",
|
|
9
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
_D = [
|
|
4
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
|
5
|
+
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
|
|
6
|
+
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
|
|
7
|
+
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
|
|
8
|
+
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
|
|
9
|
+
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
|
|
10
|
+
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
|
|
11
|
+
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
|
|
12
|
+
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
|
|
13
|
+
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
_P = [
|
|
17
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
|
18
|
+
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
|
|
19
|
+
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
|
|
20
|
+
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
|
|
21
|
+
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
|
|
22
|
+
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
|
|
23
|
+
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
|
|
24
|
+
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
_INV = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _verhoeff_check(number: str) -> bool:
|
|
31
|
+
c = 0
|
|
32
|
+
for i, ch in enumerate(reversed(number)):
|
|
33
|
+
c = _D[c][_P[i % 8][int(ch)]]
|
|
34
|
+
return c == 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _verhoeff_checksum(number: str) -> int:
|
|
38
|
+
c = 0
|
|
39
|
+
padded = number + "0"
|
|
40
|
+
for i, ch in enumerate(reversed(padded)):
|
|
41
|
+
c = _D[c][_P[i % 8][int(ch)]]
|
|
42
|
+
return _INV[c]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def validate_aadhaar(aadhaar: str) -> dict:
|
|
46
|
+
if not isinstance(aadhaar, str):
|
|
47
|
+
return {"valid": False, "reason": "must be a string"}
|
|
48
|
+
a = aadhaar.strip().replace(" ", "")
|
|
49
|
+
if not a.isdigit() or len(a) != 12:
|
|
50
|
+
return {"valid": False, "reason": "must be exactly 12 digits"}
|
|
51
|
+
if a[0] in "01":
|
|
52
|
+
return {"valid": False, "reason": "first digit cannot be 0 or 1"}
|
|
53
|
+
if not _verhoeff_check(a):
|
|
54
|
+
return {"valid": False, "reason": "invalid Verhoeff checksum"}
|
|
55
|
+
return {"valid": True}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def mock_aadhaar() -> str:
|
|
59
|
+
while True:
|
|
60
|
+
first = str(random.randint(2, 9))
|
|
61
|
+
rest = ''.join([str(random.randint(0, 9)) for _ in range(10)])
|
|
62
|
+
base = first + rest
|
|
63
|
+
check = _verhoeff_checksum(base)
|
|
64
|
+
candidate = base + str(check)
|
|
65
|
+
if _verhoeff_check(candidate):
|
|
66
|
+
return candidate
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import random
|
|
3
|
+
import string
|
|
4
|
+
from .pan import validate_pan, mock_pan
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_GSTIN_RE = re.compile(r'^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$')
|
|
8
|
+
|
|
9
|
+
_STATE_CODES = [
|
|
10
|
+
"01","02","03","04","05","06","07","08","09","10",
|
|
11
|
+
"11","12","13","14","15","16","17","18","19","20",
|
|
12
|
+
"21","22","23","24","25","26","27","28","29","30",
|
|
13
|
+
"31","32","33","34","35","36","37",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def validate_gstin(gstin: str) -> dict:
|
|
18
|
+
if not isinstance(gstin, str):
|
|
19
|
+
return {"valid": False, "reason": "must be a string"}
|
|
20
|
+
g = gstin.strip().upper()
|
|
21
|
+
if not _GSTIN_RE.match(g):
|
|
22
|
+
return {"valid": False, "reason": "invalid GSTIN format"}
|
|
23
|
+
embedded_pan = g[2:12]
|
|
24
|
+
result = validate_pan(embedded_pan)
|
|
25
|
+
if not result["valid"]:
|
|
26
|
+
return {"valid": False, "reason": "embedded PAN is invalid"}
|
|
27
|
+
return {"valid": True}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def mock_gstin() -> str:
|
|
31
|
+
state = random.choice(_STATE_CODES)
|
|
32
|
+
pan = mock_pan()
|
|
33
|
+
entity = str(random.randint(1, 9))
|
|
34
|
+
checksum = random.choice(string.digits + string.ascii_uppercase)
|
|
35
|
+
return state + pan + entity + "Z" + checksum
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import random
|
|
3
|
+
import string
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
_PAN_RE = re.compile(r'^[A-Z]{5}[0-9]{4}[A-Z]$')
|
|
7
|
+
|
|
8
|
+
_ENTITY_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def validate_pan(pan: str) -> dict:
|
|
12
|
+
if not isinstance(pan, str):
|
|
13
|
+
return {"valid": False, "reason": "must be a string"}
|
|
14
|
+
p = pan.strip().upper()
|
|
15
|
+
if not _PAN_RE.match(p):
|
|
16
|
+
return {"valid": False, "reason": "invalid PAN format (expected AAAAA9999A)"}
|
|
17
|
+
return {"valid": True}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def mask_pan(pan: str) -> str:
|
|
21
|
+
p = pan.strip().upper()
|
|
22
|
+
return p[:3] + "XX" + p[5:]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def mock_pan() -> str:
|
|
26
|
+
letters = string.ascii_uppercase
|
|
27
|
+
part1 = ''.join(random.choices(letters, k=5))
|
|
28
|
+
part2 = ''.join(random.choices(string.digits, k=4))
|
|
29
|
+
part3 = random.choice(letters)
|
|
30
|
+
return part1 + part2 + part3
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "india-kit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "The definitive Indian developer toolkit — validate Aadhaar, PAN, GSTIN, IFSC, UPI, pincode and more"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Inreal Solutions", email = "aryan23062001@gmail.com" }]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = []
|
|
14
|
+
keywords = [
|
|
15
|
+
"india", "indian", "aadhaar", "pan", "gstin", "ifsc",
|
|
16
|
+
"upi", "pincode", "validate", "validator", "fintech", "gst"
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Software Development :: Libraries",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/Inreal-Solutions/India-kit"
|
|
31
|
+
Repository = "https://github.com/Inreal-Solutions/India-kit"
|
|
32
|
+
Issues = "https://github.com/Inreal-Solutions/India-kit/issues"
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["india_kit"]
|
|
36
|
+
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
testpaths = ["tests"]
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from india_kit import validate_aadhaar, mock_aadhaar
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_invalid_short():
|
|
5
|
+
assert validate_aadhaar("12345678901")["valid"] is False
|
|
6
|
+
|
|
7
|
+
def test_invalid_letters():
|
|
8
|
+
assert validate_aadhaar("12345678901X")["valid"] is False
|
|
9
|
+
|
|
10
|
+
def test_invalid_first_digit_zero():
|
|
11
|
+
assert validate_aadhaar("012345678901")["valid"] is False
|
|
12
|
+
|
|
13
|
+
def test_mock_aadhaar_is_valid():
|
|
14
|
+
for _ in range(50):
|
|
15
|
+
a = mock_aadhaar()
|
|
16
|
+
result = validate_aadhaar(a)
|
|
17
|
+
assert result["valid"] is True, f"mock_aadhaar() returned invalid: {a}"
|
|
18
|
+
|
|
19
|
+
def test_mock_aadhaar_is_12_digits():
|
|
20
|
+
a = mock_aadhaar()
|
|
21
|
+
assert len(a) == 12
|
|
22
|
+
assert a.isdigit()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from india_kit import validate_pan, mask_pan, mock_pan
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_valid_pan():
|
|
5
|
+
assert validate_pan("ABCDE1234F")["valid"] is True
|
|
6
|
+
|
|
7
|
+
def test_invalid_pan_short():
|
|
8
|
+
assert validate_pan("ABCD1234F")["valid"] is False
|
|
9
|
+
|
|
10
|
+
def test_pan_lowercase_normalized():
|
|
11
|
+
assert validate_pan("abcde1234f")["valid"] is True
|
|
12
|
+
|
|
13
|
+
def test_mask_pan():
|
|
14
|
+
assert mask_pan("ABCDE1234F") == "ABCXX1234F"
|
|
15
|
+
|
|
16
|
+
def test_mock_pan_is_valid():
|
|
17
|
+
for _ in range(50):
|
|
18
|
+
pan = mock_pan()
|
|
19
|
+
assert validate_pan(pan)["valid"] is True, f"mock_pan() returned invalid PAN: {pan}"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from india_kit import (
|
|
2
|
+
validate_gstin, mock_gstin,
|
|
3
|
+
validate_ifsc, lookup_ifsc,
|
|
4
|
+
validate_upi,
|
|
5
|
+
validate_pincode, lookup_pincode,
|
|
6
|
+
split_gst,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_valid_gstin():
|
|
11
|
+
g = mock_gstin()
|
|
12
|
+
assert len(g) == 15
|
|
13
|
+
|
|
14
|
+
def test_invalid_gstin():
|
|
15
|
+
assert validate_gstin("INVALID")["valid"] is False
|
|
16
|
+
|
|
17
|
+
def test_valid_ifsc():
|
|
18
|
+
assert validate_ifsc("SBIN0000001")["valid"] is True
|
|
19
|
+
|
|
20
|
+
def test_invalid_ifsc():
|
|
21
|
+
assert validate_ifsc("SBI001")["valid"] is False
|
|
22
|
+
|
|
23
|
+
def test_lookup_ifsc():
|
|
24
|
+
result = lookup_ifsc("SBIN0000001")
|
|
25
|
+
assert result is not None
|
|
26
|
+
assert result["bank"] == "State Bank of India"
|
|
27
|
+
|
|
28
|
+
def test_lookup_ifsc_missing():
|
|
29
|
+
assert lookup_ifsc("XXXX0000000") is None
|
|
30
|
+
|
|
31
|
+
def test_valid_upi():
|
|
32
|
+
assert validate_upi("alice@icici")["valid"] is True
|
|
33
|
+
assert validate_upi("bob.smith@upi")["valid"] is True
|
|
34
|
+
|
|
35
|
+
def test_invalid_upi():
|
|
36
|
+
assert validate_upi("@icici")["valid"] is False
|
|
37
|
+
assert validate_upi("alice@")["valid"] is False
|
|
38
|
+
|
|
39
|
+
def test_valid_pincode():
|
|
40
|
+
assert validate_pincode("110001")["valid"] is True
|
|
41
|
+
|
|
42
|
+
def test_invalid_pincode():
|
|
43
|
+
assert validate_pincode("011001")["valid"] is False
|
|
44
|
+
assert validate_pincode("11001")["valid"] is False
|
|
45
|
+
|
|
46
|
+
def test_lookup_pincode():
|
|
47
|
+
result = lookup_pincode("110001")
|
|
48
|
+
assert result is not None
|
|
49
|
+
assert result["state"] == "Delhi"
|
|
50
|
+
|
|
51
|
+
def test_split_gst_intra():
|
|
52
|
+
result = split_gst(1000, 18, intra_state=True)
|
|
53
|
+
assert result["cgst"] == 90.0
|
|
54
|
+
assert result["sgst"] == 90.0
|
|
55
|
+
assert result["igst"] == 0.0
|
|
56
|
+
assert result["total_tax"] == 180.0
|
|
57
|
+
|
|
58
|
+
def test_split_gst_inter():
|
|
59
|
+
result = split_gst(1000, 18, intra_state=False)
|
|
60
|
+
assert result["cgst"] == 0.0
|
|
61
|
+
assert result["sgst"] == 0.0
|
|
62
|
+
assert result["igst"] == 180.0
|
|
63
|
+
assert result["total_tax"] == 180.0
|