Skip to content

Commit a954601

Browse files
authored
feat: add support for company tin and vat in Oman (#41)
1 parent 99a374c commit a954601

File tree

4 files changed

+202
-0
lines changed

4 files changed

+202
-0
lines changed

stdnum/om/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Collection of Omani numbers."""
2+
from stdnum.om import tin as business_tin # noqa: F401
3+
from stdnum.om import cid as personal_tin # noqa: F401
4+
from stdnum.om import vat as vat # noqa: F401

stdnum/om/cid.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""CID (Omani Civil ID Number).
2+
3+
The Omani Civil ID Number is a unique identifier assigned to citizens and residents
4+
of Oman. It is used for identification purposes on the Omani National ID Card
5+
(Civil Card).
6+
7+
The CID in Oman is a numeric sequence comprising exactly 8 digits.
8+
9+
More information:
10+
* https://www.rop.gov.om/english/id_card.html
11+
12+
>>> validate('12345678')
13+
'12345678'
14+
>>> validate('1234 5678')
15+
'12345678'
16+
>>> is_valid('12345678')
17+
True
18+
>>> is_valid('') # empty
19+
False
20+
>>> is_valid('1234567') # too short
21+
False
22+
>>> is_valid('123456789') # too long
23+
False
24+
>>> is_valid('1234567A') # non-digits
25+
False
26+
>>> format('12345678')
27+
'12345678'
28+
"""
29+
30+
from stdnum.exceptions import *
31+
from stdnum.util import clean, isdigits
32+
33+
34+
def compact(number):
35+
"""Convert the number to the minimal representation. This strips the
36+
number of any valid separators and removes surrounding whitespace."""
37+
return clean(number, ' -').strip()
38+
39+
40+
def validate(number):
41+
"""Check if the number is a valid Civil ID Number.
42+
43+
This checks that the number is a numeric sequence of exactly 8 digits."""
44+
number = compact(number)
45+
if not number:
46+
raise InvalidFormat("Empty number provided")
47+
if len(number) != 8:
48+
raise InvalidLength("The number must be exactly 8 digits long")
49+
if not isdigits(number):
50+
raise InvalidFormat("The number must contain only digits")
51+
return number
52+
53+
54+
def is_valid(number):
55+
"""Check if the number provided is a valid Civil ID Number."""
56+
try:
57+
return bool(validate(number))
58+
except ValidationError:
59+
return False
60+
61+
62+
def format(number):
63+
"""Format the number according to the standard presentation format."""
64+
return compact(number)

stdnum/om/tin.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""TIN (Omani Tax Identification Number).
2+
3+
The Omani Tax Identification Number is a unique identifier assigned by the
4+
Oman Tax Authority to taxpayers (companies only, not individuals).
5+
6+
The TIN in Oman is a numeric sequence comprising up to seven digits, represented as {xxxxxxx}.
7+
8+
More information:
9+
* https://tms.taxoman.gov.om/
10+
* https://lookuptax.com/docs/tax-identification-number/oman-tax-id-guide
11+
* https://www.oecd.org/content/dam/oecd/en/topics/policy-issue-focus/aeoi/oman-tin.pdf
12+
13+
>>> validate('123456')
14+
'123456'
15+
>>> validate('1 234 567')
16+
'1234567'
17+
>>> is_valid('1234567')
18+
True
19+
>>> is_valid('') # empty
20+
False
21+
>>> is_valid('12345678') # too long
22+
False
23+
>>> is_valid('123456A') # non-digits
24+
False
25+
>>> format('1234567')
26+
'1234567'
27+
"""
28+
29+
from stdnum.exceptions import *
30+
from stdnum.util import clean, isdigits
31+
32+
33+
def compact(number):
34+
"""Convert the number to the minimal representation. This strips the
35+
number of any valid separators and removes surrounding whitespace."""
36+
return clean(number, ' -').strip()
37+
38+
39+
def validate(number):
40+
"""Check if the number is a valid TIN.
41+
42+
This checks that the number is a numeric sequence of up to seven digits."""
43+
number = compact(number)
44+
if not number:
45+
raise InvalidFormat("Empty number provided")
46+
if len(number) > 7:
47+
raise InvalidLength("The number must be at most 7 digits long")
48+
if not isdigits(number):
49+
raise InvalidFormat("The number must contain only digits")
50+
return number
51+
52+
53+
def is_valid(number):
54+
"""Check if the number provided is a valid TIN."""
55+
try:
56+
return bool(validate(number))
57+
except ValidationError:
58+
return False
59+
60+
61+
def format(number):
62+
"""Format the number according to the standard presentation format."""
63+
return compact(number)

stdnum/om/vat.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""VAT (Omani Value Added Tax number) or VATIN.
2+
3+
The VAT number (VATIN) is issued by the Oman Tax Authority for VAT registered entities.
4+
The VATIN consists of an 'OM' prefix followed by 10 digits where:
5+
- First digit after the prefix is typically '1'
6+
- The following digits form a sequential number
7+
- The last digit is a check digit
8+
9+
VAT was introduced in Oman in April 2021.
10+
11+
More information:
12+
* https://tms.taxoman.gov.om/portal/web/taxportal/vat-tax
13+
* https://tms.taxoman.gov.om/portal/web/taxportal/tax-data-validation
14+
* https://lookuptax.com/docs/tax-identification-number/oman-tax-id-guide
15+
* https://www.oecd.org/content/dam/oecd/en/topics/policy-issue-focus/aeoi/oman-tin.pdf
16+
17+
>>> validate('1234567890')
18+
'1234567890'
19+
>>> validate('OM1234567890')
20+
'1234567890'
21+
>>> validate('OM1 2345678 90')
22+
'1234567890'
23+
>>> is_valid('1234567890')
24+
True
25+
>>> is_valid('123456789') # too short
26+
False
27+
>>> is_valid('12345678901') # too long
28+
False
29+
>>> is_valid('123456789A') # non-digits
30+
False
31+
>>> format('1234567890')
32+
'OM1 2345678 90'
33+
"""
34+
35+
from stdnum.exceptions import *
36+
from stdnum.util import clean, isdigits
37+
38+
39+
def compact(number):
40+
"""Convert the number to the minimal representation. This strips the
41+
number of any valid separators and removes surrounding whitespace."""
42+
number = clean(number, ' -').upper().strip()
43+
if number.startswith('OM'):
44+
number = number[2:]
45+
return number
46+
47+
48+
def validate(number):
49+
"""Check if the number is a valid VAT number.
50+
51+
This checks the length and formatting."""
52+
number = compact(number)
53+
if len(number) != 10:
54+
raise InvalidLength()
55+
if not isdigits(number):
56+
raise InvalidFormat()
57+
return number
58+
59+
60+
def is_valid(number):
61+
"""Check if the number provided is a valid VAT number."""
62+
try:
63+
return bool(validate(number))
64+
except ValidationError:
65+
return False
66+
67+
68+
def format(number):
69+
"""Format the number according to the standard presentation format."""
70+
number = compact(number)
71+
return 'OM{} {} {}'.format(number[0], number[1:8], number[8:])

0 commit comments

Comments
 (0)