Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
path: root/stdnum/ie
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2014-02-17 22:40:18 +0100
committerArthur de Jong <arthur@arthurdejong.org>2014-02-17 22:40:18 +0100
commita4012f59d8afa3dcb3030809cb77705cb964cf28 (patch)
tree9801d71e63ac4072d6a84582a8e7d313aad58af7 /stdnum/ie
parent71d9837302612de4b53c64e91a5c2a2b56192f30 (diff)
Add support for 2013 extension of Irish PPS Numbers
References: - https://www.welfare.ie/en/Pages/PPSN.aspx - http://www.citizensinformation.ie/en/social_welfare/irish_social_welfare_system/personal_public_service_number.html
Diffstat (limited to 'stdnum/ie')
-rw-r--r--stdnum/ie/pps.py38
-rw-r--r--stdnum/ie/vat.py16
2 files changed, 40 insertions, 14 deletions
diff --git a/stdnum/ie/pps.py b/stdnum/ie/pps.py
index 0447bf3..baa27e8 100644
--- a/stdnum/ie/pps.py
+++ b/stdnum/ie/pps.py
@@ -1,6 +1,7 @@
# pps.py - functions for handling Irish PPS numbers
#
# Copyright (C) 2012, 2013 Arthur de Jong
+# Copyright (C) 2014 Olivier Dony
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -19,17 +20,32 @@
"""PPS No (Personal Public Service Number, Irish personal number).
-The Personal Public Service number consists of 8 digits. The first seven
-are numeric and the last is the check character. The number is sometimes
-be followed by an extra letter that can be a 'W', 'T' or an 'X' and is
-ignored for the check algorithm.
+The Personal Public Service number consists of 7 digits, and one or
+two letters. The first letter is a check character.
+When present (which should be the case for new numbers as of 2013),
+the second letter can be 'A' (for individuals) or 'H' (for
+non-individuals, such as limited companies, trusts, partnerships
+and unincorporated bodies). Pre-2013 values may have 'W', 'T',
+or 'X' as the second letter ; it is ignored by the check.
->>> validate('6433435F')
+>>> validate('6433435F') # pre-2013
'6433435F'
+>>> validate('6433435FT') # pre-2013 with special final 'T'
+'6433435FT'
+>>> validate('6433435FW') # pre-2013 format for married women
+'6433435FW'
>>> validate('6433435E') # incorrect check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
+>>> validate('6433435OA') # 2013 format (personal)
+'6433435OA'
+>>> validate('6433435IH') # 2013 format (non-personal)
+'6433435IH'
+>>> validate('6433435VH') # 2013 format (incorrect check)
+Traceback (most recent call last):
+ ...
+InvalidChecksum: ...
"""
import re
@@ -39,7 +55,7 @@ from stdnum.ie import vat
from stdnum.util import clean
-pps_re = re.compile('^\d{7}[A-W][WTX]?$')
+pps_re = re.compile('^\d{7}[A-W][AHWTX]?$')
"""Regular expression used to check syntax of PPS numbers."""
@@ -55,8 +71,14 @@ def validate(number):
number = compact(number)
if not pps_re.match(number):
raise InvalidFormat()
- if number[7] != vat.calc_check_digit(number[:7]):
- raise InvalidChecksum()
+ if len(number) == 9 and number[8] in 'AH':
+ # new 2013 format
+ if number[7] != vat.calc_check_digit(number[:7] + number[8:]):
+ raise InvalidChecksum()
+ else:
+ # old format, last letter ignored
+ if number[7] != vat.calc_check_digit(number[:7]):
+ raise InvalidChecksum()
return number
diff --git a/stdnum/ie/vat.py b/stdnum/ie/vat.py
index 1988093..c3740e7 100644
--- a/stdnum/ie/vat.py
+++ b/stdnum/ie/vat.py
@@ -22,8 +22,10 @@
The Irish VAT number consists of 8 digits. The last digit is a check
letter, the second digit may be a number, a letter, "+" or "*".
->>> validate('IE 6433435F')
+>>> validate('IE 6433435F') # pre-2013 format
'6433435F'
+>>> validate('IE 6433435OA') # 2013 format
+'6433435OA'
>>> validate('6433435E') # incorrect check digit
Traceback (most recent call last):
...
@@ -53,8 +55,10 @@ def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
alphabet = 'WABCDEFGHIJKLMNOPQRSTUV'
- number = (7 - len(number)) * '0' + number
- return alphabet[sum((8 - i) * int(n) for i, n in enumerate(number)) % 23]
+ number = compact(number).zfill(7)
+ return alphabet[(
+ sum((8 - i) * int(n) for i, n in enumerate(number[:7])) +
+ 9 * alphabet.index(number[7:])) % 23]
def validate(number):
@@ -63,15 +67,15 @@ def validate(number):
number = compact(number)
if not number[:1].isdigit() or not number[2:7].isdigit():
raise InvalidFormat()
- if len(number) != 8:
+ if len(number) not in (8, 9):
raise InvalidLength()
if number[:7].isdigit():
# new system
- if number[-1] != calc_check_digit(number[:-1]):
+ if number[7] != calc_check_digit(number[:7] + number[8:]):
raise InvalidChecksum()
elif number[1] in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ+*':
# old system
- if number[-1] != calc_check_digit(number[2:-1] + number[0]):
+ if number[7] != calc_check_digit(number[2:7] + number[0]):
raise InvalidChecksum()
else:
raise InvalidFormat()