Skip to content

Commit c5125c7

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

File tree

3 files changed

+502
-0
lines changed

3 files changed

+502
-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

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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 digit identifies the type of taxpayer: 0 for businesses and 1 to 9
29+
for individual taxpayers. The following seven digits are a sequence. The ninth
30+
digit is the check digit.
31+
32+
The last three digits (four in the future), for businesses only, correspond to
33+
the branch code. If the branch code is not specified, then it defaults to 000.
34+
35+
It is not specified in the official documents, but it is not uncommon that some
36+
letters are appended to the businesses TIN to tell whether it is subject to
37+
VAT, usually N to say it is not subject and V to tell that it is.
38+
39+
More information:
40+
41+
* https://www.ntrc.gov.ph/images/journal/j20141112a.pdf
42+
* https://wiki.scn.sap.com/wiki/display/CRM/Philippines
43+
* https://help.sap.com/doc/saphelp_erp2005/6.0/ja-JP/d8/663e399265df0ee10000000a11402f/content.htm
44+
* https://world.salestaxhandbook.com/ph-philippines
45+
46+
>>> validate('239-072-842')
47+
'239072842'
48+
>>> validate('239-072-842-000')
49+
'239072842000'
50+
>>> validate('12345')
51+
Traceback (most recent call last):
52+
...
53+
InvalidLength: ...
54+
>>> validate('12345678X')
55+
Traceback (most recent call last):
56+
...
57+
InvalidFormat: ...
58+
>>> format('239072842000')
59+
'239-072-842-000'
60+
""" # noqa: E501
61+
62+
from stdnum.exceptions import *
63+
from stdnum.util import clean, isdigits
64+
65+
66+
def compact(number):
67+
"""Convert the number to the minimal representation.
68+
69+
This strips the number of any valid separators and removes surrounding
70+
whitespace.
71+
"""
72+
number = clean(number, ' -').upper().strip()
73+
# Normalize unofficial code telling whether it is subject or not to VAT.
74+
number = number.replace('NONVAT', 'N')
75+
number = number.replace('NVAT', 'N')
76+
number = number.replace('NV', 'N')
77+
number = number.replace('VAT', 'V')
78+
# Append the default branch code for business TIN numbers having unofficial
79+
# VAT letter.
80+
if len(number) == 10 and number[-1] in ('N', 'V'):
81+
number = number[:-1] + '000' + number[-1]
82+
return number
83+
84+
85+
def validate(number):
86+
"""Check if the number is a valid Philippines TIN number.
87+
88+
This checks the length and formatting.
89+
"""
90+
number = compact(number)
91+
if len(number) not in (9, 12, 13, 14):
92+
raise InvalidLength()
93+
has_wrong_format = not (isdigits(number) or
94+
(number[-1] in ('N', 'V') and
95+
isdigits(number[:-1])))
96+
if has_wrong_format:
97+
raise InvalidFormat()
98+
return number
99+
100+
101+
def is_valid(number):
102+
"""Check if the number is a valid Philippines TIN number."""
103+
try:
104+
return bool(validate(number))
105+
except ValidationError:
106+
return False
107+
108+
109+
def format(number):
110+
"""Reformat the number to the standard presentation format."""
111+
number = compact(number)
112+
if len(number) < 12:
113+
return '-'.join([number[:3], number[3:6], number[6:]])
114+
return '-'.join([number[:3], number[3:6], number[6:9], number[9:]])

0 commit comments

Comments
 (0)