Skip to content

Commit 6471f28

Browse files
committed
Add support for Philippines TIN
Fixes #116
1 parent 6d366e3 commit 6471f28

File tree

3 files changed

+504
-0
lines changed

3 files changed

+504
-0
lines changed

stdnum/ph/__init__.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# __init__.py - collection of Philippines numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2023 Leandro Regueiro
5+
#
6+
# This library is free software; you can redistribute it and/or
7+
# modify it under the terms of the GNU Lesser General Public
8+
# License as published by the Free Software Foundation; either
9+
# version 2.1 of the License, or (at your option) any later version.
10+
#
11+
# This library is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public
17+
# License along with this library; if not, write to the Free Software
18+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19+
# 02110-1301 USA
20+
21+
"""Collection of Philippines numbers."""
22+
23+
# provide aliases
24+
from stdnum.ph import tin as vat # noqa: F401

stdnum/ph/tin.py

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# nit.py - functions for handling Philippines TIN numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2023 Leandro Regueiro
5+
#
6+
# This library is free software; you can redistribute it and/or
7+
# modify it under the terms of the GNU Lesser General Public
8+
# License as published by the Free Software Foundation; either
9+
# version 2.1 of the License, or (at your option) any later version.
10+
#
11+
# This library is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public
17+
# License along with this library; if not, write to the Free Software
18+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19+
# 02110-1301 USA
20+
21+
"""TIN (Taxpayer Identification Number, Philippines tax number).
22+
23+
This number consists of 9 digits (for individual taxpayers), or 12 digits (for
24+
bussinesses). A thirteenth digit will be added in the future for businesses).
25+
It is usually separated using hyphens to make it easier to read, like
26+
XXX-XXX-XXX-XXX.
27+
28+
The first nine digits is the actual TIN, while
29+
30+
The first digit identifies the type of taxpayer: 0 for businesses and 1 to 9
31+
for individual taxpayers. The following seven digits are a sequence. The ninth
32+
digit is the check digit.
33+
34+
The last three digits (four in the future), for businesses only, correspond to
35+
the branch code. If the branch code is not specified, then it defaults to 000.
36+
37+
It is not specified in the official documents, but it is not uncommon that some
38+
letters are appended to the businesses TIN to tell whether it is subject to
39+
VAT, usually N to say it is not subject and V to tell that it is.
40+
41+
More information:
42+
43+
* https://www.ntrc.gov.ph/images/journal/j20141112a.pdf
44+
* https://wiki.scn.sap.com/wiki/display/CRM/Philippines
45+
* https://help.sap.com/doc/saphelp_erp2005/6.0/ja-JP/d8/663e399265df0ee10000000a11402f/content.htm
46+
* https://world.salestaxhandbook.com/ph-philippines
47+
48+
>>> validate('239-072-842')
49+
'239072842'
50+
>>> validate('239-072-842-000')
51+
'239072842000'
52+
>>> validate('12345')
53+
Traceback (most recent call last):
54+
...
55+
InvalidLength: ...
56+
>>> validate('12345678X')
57+
Traceback (most recent call last):
58+
...
59+
InvalidFormat: ...
60+
>>> format('239072842000')
61+
'239-072-842-000'
62+
""" # noqa: E501
63+
64+
from stdnum.exceptions import *
65+
from stdnum.util import clean, isdigits
66+
67+
68+
def compact(number):
69+
"""Convert the number to the minimal representation.
70+
71+
This strips the number of any valid separators and removes surrounding
72+
whitespace.
73+
"""
74+
number = clean(number, ' -').upper().strip()
75+
# Normalize unofficial code telling whether it is subject or not to VAT.
76+
number = number.replace('NONVAT', 'N')
77+
number = number.replace('NVAT', 'N')
78+
number = number.replace('NV', 'N')
79+
number = number.replace('VAT', 'V')
80+
# Append the default branch code for business TIN numbers having unofficial
81+
# VAT letter.
82+
if len(number) == 10 and number[-1] in ('N', 'V'):
83+
number = number[:-1] + '000' + number[-1]
84+
return number
85+
86+
87+
def validate(number):
88+
"""Check if the number is a valid Philippines TIN number.
89+
90+
This checks the length and formatting.
91+
"""
92+
number = compact(number)
93+
if len(number) not in (9, 12, 13, 14):
94+
raise InvalidLength()
95+
has_wrong_format = not (isdigits(number)
96+
or (number[-1] in ('N', 'V')
97+
and isdigits(number[:-1])))
98+
if has_wrong_format:
99+
raise InvalidFormat()
100+
return number
101+
102+
103+
def is_valid(number):
104+
"""Check if the number is a valid Philippines TIN number."""
105+
try:
106+
return bool(validate(number))
107+
except ValidationError:
108+
return False
109+
110+
111+
def format(number):
112+
"""Reformat the number to the standard presentation format."""
113+
number = compact(number)
114+
if len(number) < 12:
115+
return '-'.join([number[:3], number[3:6], number[6:]])
116+
return '-'.join([number[:3], number[3:6], number[6:9], number[9:]])

0 commit comments

Comments
 (0)