From a4012f59d8afa3dcb3030809cb77705cb964cf28 Mon Sep 17 00:00:00 2001 From: Arthur de Jong Date: Mon, 17 Feb 2014 22:40:18 +0100 Subject: 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 --- stdnum/ie/pps.py | 38 ++++++++++++++++++++++++++++++-------- stdnum/ie/vat.py | 16 ++++++++++------ 2 files changed, 40 insertions(+), 14 deletions(-) (limited to 'stdnum/ie') 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() -- cgit v1.2.3