From 4753c09e81a0adb6b62d12ad60f5a5107d5033c2 Mon Sep 17 00:00:00 2001 From: Arthur de Jong Date: Fri, 17 May 2013 13:04:14 +0200 Subject: Implement validate() for Finnish numbers --- stdnum/fi/alv.py | 31 ++++++++++++++++++++++--------- stdnum/fi/hetu.py | 54 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 58 insertions(+), 27 deletions(-) (limited to 'stdnum') diff --git a/stdnum/fi/alv.py b/stdnum/fi/alv.py index 509ad9c..ed9457c 100644 --- a/stdnum/fi/alv.py +++ b/stdnum/fi/alv.py @@ -1,7 +1,7 @@ # vat.py - functions for handling Finnish VAT numbers # coding: utf-8 # -# Copyright (C) 2012 Arthur de Jong +# Copyright (C) 2012, 2013 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,14 +22,15 @@ The number is an 8-digit code with a weighted checksum. ->>> compact('FI- 2077474-0') +>>> validate('FI 20774740') '20774740' ->>> is_valid('FI 20774740') -True ->>> is_valid('FI 20774741') # invalid check digit -False +>>> validate('FI 20774741') # invalid check digit +Traceback (most recent call last): + ... +InvalidChecksum: ... """ +from stdnum.exceptions import * from stdnum.util import clean @@ -48,11 +49,23 @@ def checksum(number): return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11 +def validate(number): + """Checks to see if the number provided is a valid VAT number. This + checks the length, formatting and check digit.""" + number = compact(number) + if not number.isdigit(): + raise InvalidFormat() + if len(number) != 8: + raise InvalidLength() + if checksum(number) != 0: + raise InvalidChecksum() + return number + + def is_valid(number): """Checks to see if the number provided is a valid VAT number. This checks the length, formatting and check digit.""" try: - number = compact(number) - except: + return bool(validate(number)) + except ValidationError: return False - return number.isdigit() and len(number) == 8 and checksum(number) == 0 diff --git a/stdnum/fi/hetu.py b/stdnum/fi/hetu.py index b22465d..69b543e 100644 --- a/stdnum/fi/hetu.py +++ b/stdnum/fi/hetu.py @@ -2,6 +2,7 @@ # coding: utf-8 # # Copyright (C) 2011 Jussi Judin +# Copyright (C) 2012, 2013 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -25,12 +26,16 @@ See http://www.vaestorekisterikeskus.fi/default.aspx?id=45 for checksum calculation details and http://tarkistusmerkit.teppovuori.fi/tarkmerk.htm#hetu1 for historical details. ->>> is_valid('131052-308T') -True ->>> is_valid('131052-308U') -False ->>> is_valid('310252-308Y') -False +>>> validate('131052-308T') +'131052-308T' +>>> validate('131052-308U') +Traceback (most recent call last): + ... +InvalidChecksum: ... +>>> validate('310252-308Y') +Traceback (most recent call last): + ... +InvalidComponent: ... >>> compact('131052a308t') '131052A308T' """ @@ -38,12 +43,15 @@ False import re import datetime +from stdnum.exceptions import * +from stdnum.util import clean + _century_codes = { '+': 1800, '-': 1900, 'A': 2000, - } +} # Finnish personal identity codes are composed of date part, century # indicating sign, individual number and control character. @@ -56,23 +64,21 @@ _hetu_re = re.compile(r'^(?P[0123]\d)(?P[01]\d)(?P\d\d)' def compact(number): """Convert the HETU to the minimal representation. This strips surrounding whitespace and converts it to upper case.""" - return number.strip().upper() + return clean(number, '').upper().strip() def _calc_checksum(number): return '0123456789ABCDEFHJKLMNPRSTUVWXY'[int(number) % 31] -def is_valid(number): +def validate(number): """Checks to see if the number provided is a valid HETU. It checks the format, whether a valid date is given and whether the check digit is correct.""" - try: - match = _hetu_re.search(compact(number)) - if not match: - return False - except: - return False + number = compact(number) + match = _hetu_re.search(number) + if not match: + raise InvalidFormat() day = int(match.group('day')) month = int(match.group('month')) year = int(match.group('year')) @@ -82,12 +88,24 @@ def is_valid(number): try: datetime.date(century + year, month, day) except ValueError: - return False + raise InvalidComponent() # for historical reasons individual IDs start from 002 if individual < 2: - return False + raise InvalidComponent() checkable_number = '%02d%02d%02d%03d' % (day, month, year, individual) - return match.group('control') == _calc_checksum(checkable_number) + if match.group('control') != _calc_checksum(checkable_number): + raise InvalidChecksum() + return number + + +def is_valid(number): + """Checks to see if the number provided is a valid HETU. It checks the + format, whether a valid date is given and whether the check digit is + correct.""" + try: + return bool(validate(number)) + except ValidationError: + return False # This is here just for completeness as there are no different length forms -- cgit v1.2.3