diff --git a/stdnum/ph/__init__.py b/stdnum/ph/__init__.py new file mode 100644 index 00000000..f5dee782 --- /dev/null +++ b/stdnum/ph/__init__.py @@ -0,0 +1,24 @@ +# __init__.py - collection of Philippines numbers +# coding: utf-8 +# +# Copyright (C) 2023 Leandro Regueiro +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +"""Collection of Philippines numbers.""" + +# provide aliases +from stdnum.ph import tin as vat # noqa: F401 diff --git a/stdnum/ph/tin.py b/stdnum/ph/tin.py new file mode 100644 index 00000000..c967a2dc --- /dev/null +++ b/stdnum/ph/tin.py @@ -0,0 +1,114 @@ +# tin.py - functions for handling Philippines TIN numbers +# coding: utf-8 +# +# Copyright (C) 2023 Leandro Regueiro +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +"""TIN (Taxpayer Identification Number, Philippines tax number). + +This number consists of 9 digits (for individual taxpayers), or 12 digits (for +bussinesses). A thirteenth digit will be added in the future for businesses). +It is usually separated using hyphens to make it easier to read, like +XXX-XXX-XXX-XXX. + +The first digit identifies the type of taxpayer: 0 for businesses and 1 to 9 +for individual taxpayers. The following seven digits are a sequence. The ninth +digit is the check digit. + +The last three digits (four in the future), for businesses only, correspond to +the branch code. If the branch code is not specified, then it defaults to 000. + +It is not specified in the official documents, but it is not uncommon that some +letters are appended to the businesses TIN to tell whether it is subject to +VAT, usually N to say it is not subject and V to tell that it is. + +More information: + +* https://www.ntrc.gov.ph/images/journal/j20141112a.pdf +* https://wiki.scn.sap.com/wiki/display/CRM/Philippines +* https://help.sap.com/doc/saphelp_erp2005/6.0/ja-JP/d8/663e399265df0ee10000000a11402f/content.htm +* https://world.salestaxhandbook.com/ph-philippines + +>>> validate('239-072-842') +'239072842' +>>> validate('239-072-842-000') +'239072842000' +>>> validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> validate('12345678X') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> format('239072842000') +'239-072-842-000' +""" # noqa: E501 + +from stdnum.exceptions import * +from stdnum.util import clean, isdigits + + +def compact(number): + """Convert the number to the minimal representation. + + This strips the number of any valid separators and removes surrounding + whitespace. + """ + number = clean(number, ' -').upper().strip() + # Normalize unofficial code telling whether it is subject or not to VAT. + number = number.replace('NONVAT', 'N') + number = number.replace('NVAT', 'N') + number = number.replace('NV', 'N') + number = number.replace('VAT', 'V') + # Append the default branch code for business TIN numbers having unofficial + # VAT letter. + if len(number) == 10 and number[-1] in ('N', 'V'): + number = number[:-1] + '000' + number[-1] + return number + + +def validate(number): + """Check if the number is a valid Philippines TIN number. + + This checks the length and formatting. + """ + number = compact(number) + if len(number) not in (9, 12, 13, 14): + raise InvalidLength() + has_wrong_format = not (isdigits(number) or + (number[-1] in ('N', 'V') and + isdigits(number[:-1]))) + if has_wrong_format: + raise InvalidFormat() + return number + + +def is_valid(number): + """Check if the number is a valid Philippines TIN number.""" + try: + return bool(validate(number)) + except ValidationError: + return False + + +def format(number): + """Reformat the number to the standard presentation format.""" + number = compact(number) + if len(number) < 12: + return '-'.join([number[:3], number[3:6], number[6:]]) + return '-'.join([number[:3], number[3:6], number[6:9], number[9:]]) diff --git a/tests/test_ph_tin.doctest b/tests/test_ph_tin.doctest new file mode 100644 index 00000000..b694bf82 --- /dev/null +++ b/tests/test_ph_tin.doctest @@ -0,0 +1,364 @@ +test_ph_tin.doctest - more detailed doctests for stdnum.ph.tin module + +Copyright (C) 2023 Leandro Regueiro + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA + + +This file contains more detailed doctests for the stdnum.ph.tin module. It +tries to test more corner cases and detailed functionality that is not really +useful as module documentation. + +>>> from stdnum.ph import tin + + +Tests for some corner cases. + +>>> tin.validate('239-072-842') +'239072842' +>>> tin.validate('239-072-842V') +'239072842000V' +>>> tin.validate('239-072-842-000') +'239072842000' +>>> tin.validate('239-072-842-000N') +'239072842000N' +>>> tin.validate('239-072-842-0000') +'2390728420000' +>>> tin.validate('239-072-842-0000V') +'2390728420000V' +>>> tin.validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> tin.validate('12345678X') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> tin.validate('X23456789') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> tin.validate('X23456789V') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> tin.format('239072842') +'239-072-842' +>>> tin.format('239072842V') +'239-072-842-000V' +>>> tin.format('239072842000') +'239-072-842-000' +>>> tin.format('2390728420000') +'239-072-842-0000' + + +These have been found online and should all be valid numbers. + +>>> numbers = ''' +... +... 005-011-362-000 +... 000-050-036-008 +... 000-050-043-012 +... 000-111-016-000 +... 000-143-093 +... 000-145-715-000 +... 000-171-929-000 +... 000-177-334-000 Non-VAT +... 000-329-993-000 +... 000-333-948-000 +... 000-348-276 +... 000-360-191-000 +... 000-360-191-011 +... 000-384-446-000 +... 000-400-353-000 +... 000-413-851-000 +... 000-474-804-000 +... 000-502-501 +... 001-609-075-000 +... 001-707-269-000 +... 002-286-250-000 +... 003-508-768-000 +... 003-509-124-000 +... 004-589-684-003 +... 004-591-735-000 VAT +... 004-622-551-000 +... 004-780-008-005 +... 004-840-797 +... 004-845-988-000 +... 005-249-453-014 +... 052-104-040-767 +... 100-192-890 +... 100-200-013-000 +... 100-200-909-000 +... 100-455-411-000 +... 103-768-233 +... 104-035-802-000 +... 146-475-945 +... 162-880-637-000 +... 175-130-354-000 +... 177-438-716-000 +... 193-599-790 +... 200-671-974-000 +... 200-937-884-006 +... 222-175-867-001 +... 223-376-875-000 +... 223-518-336-000 Non-VAT +... 224-527-159-000 +... 230-338-434-000 +... 232-702-168-000 +... 239-072-842 +... 254-577-685-000 +... 320-157-356-094 +... 340-003-498-120 VAT +... 348-717-000 +... 109-102-132 NV +... 124-951-682 NV +... 004-754-985 VAT +... 890-135-517-438NV +... 182-843-767 NV +... 000-073-706 vat +... 000-310-215-020 Vat +... 935-112-421NV +... 005-160-067 VAT +... 005-881-689 VAT +... 005-168-597 VAT +... 005-988-752 VAT +... 926-792-477 NV +... 103-989-095-000-VAT +... 922-917-654 NV +... 005-159-765 VAT +... 005-168-902 Vat +... 115-707-189 vat +... 002-332-000-013 VAT +... 103-915-682-VAT +... 005-161-557 vat +... 000-081-553-028 Vat +... 100-079-484 VAT +... 100-080-002 VAT +... 129-202-158 vat +... 000-074-274 vat +... 132-371-491 Vat +... 004-749-477 NV +... 000-076-775 vat +... 148-016-939 Vat +... 000-075-040V +... 004-751-415 VAT +... 000-264-680 VAT +... 127-471-217 NVAT +... 005-162-365 VAT +... 890-003-375-571 VAT +... 100-082-501 Vat +... 890-100-078-619 VAT +... 113-000-074-274 Vat +... 000-844-851-000-VAT +... 113-000-264-828 vat +... 132-369-800 VAT +... 003-375-397 VAT +... 131-840-857 NV +... 000-050-043-014 VAT +... 000-075-476 Vat +... 100-080-156 VAT +... 000-330-890-000 vat +... 005-162-036 NV +... 001-648-111-004 VAT +... 900-757-474 NV +... 936-888-467NV +... 004-475-204-002 VAT +... 890-000-074-144 VAT +... 900-754-474NV +... 123-998-322 vat +... 152-325-282 Vat +... 004-326-444-VAT +... 000-331-997-001 VAT +... 175-494-492 NVAT +... 906-750-006-000 NV +... 000-163-731-001 vat +... 000-066-016-002 Vat +... 890-122-433-129 Vat +... 932-699-595 NV +... 100-082-182 Vat +... 101-171-867VAT +... 000-075-354 Vat +... 000-075-589-VAT +... 005-161-741VAT +... 100-079-033 Vat +... 202-845-266-000 Vat +... 179-386-284 VAT +... 163-800-132 NV +... 000-069-873-005 vat +... 108-683-346 Vat +... 141-841-341 VAT +... 118-437-177 NV +... 222-837-340-000 VAT +... 100-078-928 VAT +... 000-076-380 VAT +... 123-980-652VAT +... 174-602-454 VAT +... 189-801-842V +... 005-814-108 Vat +... 905-082-345-000 VAT +... 000-169-318-024 vat +... 890-100-080-867 Vat +... 923-717-148 vat +... 124-957-408 VAT +... 189-770-986 VAT +... 148-847-257 NV +... 920-521-606 NV +... 105-307-083 NV +... 001-006-503 VAT +... 146-200-558-000 Vat +... 946-940-013 Vat +... 006-843-862 Vat +... 181-715-165 NV +... 181-721-741 Vat +... 003-876-506VAT +... 101-611-294-000Vat +... 160-903-052 VAT +... 000-172-881-002 vat +... 000-171-929-015 VAT +... 148-032-938 NV +... 913-887-131 vat +... 002-645-341VAT +... 000-077-154 VAT +... 002-623-291 VAT +... 158-022-176 VAT +... 005-170-769 VAT +... 100-084-030 VAT +... 271-275-226-001 NV +... 925-885-337 NV +... 000-132-948-002 Vat +... 005-160-816 VAT +... 295-211-531-000 vat +... 190-480-131 VAT +... 000-076-516 VAT +... 006-595-182 +... 004-774-849 +... 230-222-975 +... 000-527-434 +... 005-378-235 +... 000-528-611 +... 002-829-391 +... 118-247-725 +... 000-604-041 +... 301-172-053 +... 007-993-450 +... 130-725-714 +... 008-744-348 +... 007-995-035 +... 005-591-328 +... 007-308-455 +... 007-993-442 +... 230-222-578 +... 000-391-438 +... 107-771-398 +... 101-563-982 +... 123-304-975 +... 113-423-143 +... 102-096-694 +... 102-081-718 +... 120-115-828 +... 217-197-087 +... 132-203-939 +... 111-805-104 +... 117-798-339 +... 117-798-397 +... 111-708-099 +... 117-798-388 +... 111-805-120 +... 108-750-432 +... 111-805-058 +... 156-609-278 +... 120-099-848 +... 120-960-387 +... 120-960-779 +... 101-550-602 +... 120-130-884 +... 265-857-209 +... 235-572-492 +... 000-463-069 +... 004-774-849 +... 000-137-755 +... 230-000-334-038 +... 235-572-492 +... 172958490 +... 102 650 969 000 +... 003-509-116-000 +... 004-460-118-000 +... 136-421-419 +... 203-742-960 +... 202-346-036-000 +... 948-159-869 +... 168-338-289 +... 183-803-717-001 +... 239-569-635 +... 008-222-233 +... 117-411-362 +... 925-868-738-000 +... 918-271-855-000 +... 275-374-483 +... 005-106-150 +... 146-199-843 +... 204-727-396 +... 246-188-725 +... 204-727-396 +... 116-636-114 +... 116-636-447 +... 221-215-023-000 +... 185-645-285 +... 108-935-298 +... 421187521 +... 454910793000 +... 170-172-592 +... 135-482-989 +... 111-161-025 +... 901-128-486-000 +... 126-623-464 +... 134-387-859 +... 922-427-587 +... 113-510-437 +... 937-467-730 +... 928-201-914 +... 734-680-373 +... 923-260-290 +... 115-895-948 +... 106-179-520 +... 225-659-044 +... 132-144-207 +... 132-946-740 +... 139-166-843 +... 131-282-586 +... 103-797-751 +... 115-783-383 +... 119-857-973 +... 115-957-357 +... 183-123-226 +... 103-561-688 +... 113-267-001 +... 000-916-384-000 +... 218-145-247 +... 111-784-903 +... 291-624-692 +... 120-669-246 +... 249-448-045 +... 268-492-045 +... 245-061-183 +... 153-780-713 +... 266-292-128 +... 401-934-883 +... +... ''' +>>> [x for x in numbers.splitlines() if x and not tin.is_valid(x)] +[]