diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2014-10-17 21:54:28 +0200 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2014-10-17 21:54:28 +0200 |
commit | e2948bb6bd0ebf7b3828ac989a56d0e61982c545 (patch) | |
tree | 23008d1a5df284a5b304c7ca5108e781f96bd43e | |
parent | 2700b7a064b0d0b7dcb71c91de33207826c15cea (diff) | |
parent | e5250be45092363504ff61c868938439ec958985 (diff) |
Add Ecuadorian CI and RUC numbers
Add modules for Ecuadorian Identification Card (CI - Cédula de
identidad) and Fiscal Numbers (RUC - Registro Único de Contribuyentes)
See: https://github.com/arthurdejong/python-stdnum/pull/12
-rw-r--r-- | stdnum/ec/__init__.py | 24 | ||||
-rw-r--r-- | stdnum/ec/ci.py | 78 | ||||
-rw-r--r-- | stdnum/ec/ruc.py | 93 | ||||
-rw-r--r-- | tests/test_ec_ci.doctest | 65 | ||||
-rw-r--r-- | tests/test_ec_ruc.doctest | 164 |
5 files changed, 424 insertions, 0 deletions
diff --git a/stdnum/ec/__init__.py b/stdnum/ec/__init__.py new file mode 100644 index 0000000..18d02a8 --- /dev/null +++ b/stdnum/ec/__init__.py @@ -0,0 +1,24 @@ +# __init__.py - collection of Ecuadorian numbers +# coding: utf-8 +# +# Copyright (C) 2014 Jonathan Finlay +# +# 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 Ecuadorian numbers.""" + +# provide vat as an alias +from stdnum.ec import ruc as vat diff --git a/stdnum/ec/ci.py b/stdnum/ec/ci.py new file mode 100644 index 0000000..9343e47 --- /dev/null +++ b/stdnum/ec/ci.py @@ -0,0 +1,78 @@ +# ci.py - functions for handling Ecuadorian personal identity codes +# coding: utf-8 +# +# Copyright (C) 2014 Jonathan Finlay +# Copyright (C) 2014 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 +# 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 + +"""CI (Cédula de identidad, Ecuadorian personal identity code). + +The CI is a 10 digit number used to identify Ecuadorian citizens. + +>>> validate('171430710-3') +'1714307103' +>>> validate('1714307104') # invalid check digit +Traceback (most recent call last): + ... +InvalidChecksum: ... +>>> validate('171430710') # digit missing +Traceback (most recent call last): + ... +InvalidLength: ... +""" + +from stdnum.exceptions import * +from stdnum.util import clean + + +def compact(number): + """Convert the number to the minimal representation. This strips the + number of any valid separators and removes surrounding whitespace.""" + return clean(number, ' -').upper().strip() + + +def _checksum(number): + """Calculate a checksum over the number.""" + fold = lambda x: x - 9 if x > 9 else x + return sum(fold((2 - (i % 2)) * int(n)) + for i, n in enumerate(number)) % 10 + + +def validate(number): + """Checks to see if the number provided is a valid CI number. This + checks the length, formatting and check digit.""" + number = compact(number) + if len(number) != 10: + raise InvalidLength() + if not number.isdigit(): + raise InvalidFormat() + if number[:2] < '01' or number[:2] > '24': + raise InvalidComponent() # invalid province code + if number[2] > '5': + raise InvalidComponent() # third digit wrong + if _checksum(number) != 0: + raise InvalidChecksum() + return number + + +def is_valid(number): + """Checks to see if the number provided is a valid CI number. This + checks the length, formatting and check digit.""" + try: + return bool(validate(number)) + except ValidationError: + return False diff --git a/stdnum/ec/ruc.py b/stdnum/ec/ruc.py new file mode 100644 index 0000000..8887467 --- /dev/null +++ b/stdnum/ec/ruc.py @@ -0,0 +1,93 @@ +# ruc.py - functions for handling Ecuadorian fiscal numbers +# coding: utf-8 +# +# Copyright (C) 2014 Jonathan Finlay +# Copyright (C) 2014 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 +# 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 + +"""RUC (Registro Único de Contribuyentes, Ecuadorian company tax number). + +The RUC is a tax identification number for legal entities. It has 13 digits +where the third digit is a number denoting the type of entity. + +>>> validate('1792060346-001') +'1792060346001' +>>> validate('1763154690001') # invalid check digit +Traceback (most recent call last): + ... +InvalidChecksum: ... +>>> validate('179206034601') # too short +Traceback (most recent call last): + ... +InvalidLength: ... +""" + +from stdnum.ec import ci +from stdnum.exceptions import * + + +__all__ = ['compact', 'validate', 'is_valid'] + + +# use the same compact function as CI +compact = ci.compact + + +def _checksum(number, weights): + """Calculate a checksum over the number given the weights.""" + 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 RUC number. This + checks the length, formatting, check digit and check sum.""" + number = compact(number) + if len(number) != 13: + raise InvalidLength() + if not number.isdigit(): + raise InvalidFormat() + if number[:2] < '01' or number[:2] > '24': + raise InvalidComponent() # invalid province code + if number[2] < '6': + # 0..5 = natural RUC: CI plus establishment number + if number[-3:] == '000': + raise InvalidComponent() # establishment number wrong + ci.validate(number[:10]) + elif number[2] == '6': + # 6 = public RUC + if number[-4:] == '0000': + raise InvalidComponent() # establishment number wrong + if _checksum(number[:9], (3, 2, 7, 6, 5, 4, 3, 2, 1)) != 0: + raise InvalidChecksum() + elif number[2] == '9': + # 9 = juridical RUC + if number[-3:] == '000': + raise InvalidComponent() # establishment number wrong + if _checksum(number[:10], (4, 3, 2, 7, 6, 5, 4, 3, 2, 1)) != 0: + raise InvalidChecksum() + else: + raise InvalidComponent() # third digit wrong + return number + + +def is_valid(number): + """Checks to see if the number provided is a valid RUC number. This + checks the length, formatting and check digit.""" + try: + return bool(validate(number)) + except ValidationError: + return False diff --git a/tests/test_ec_ci.doctest b/tests/test_ec_ci.doctest new file mode 100644 index 0000000..08efb8b --- /dev/null +++ b/tests/test_ec_ci.doctest @@ -0,0 +1,65 @@ +test_ec_ci.doctest - more detailed doctests for stdnum.ec.ci module + +Copyright (C) 2014 Jonathan Finlay +Copyright (C) 2014 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 +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.ec.ci. It tries to +cover more corner cases and detailed functionality that is not really useful +as module documentation. + +>>> from stdnum.ec import ci +>>> from stdnum.exceptions import * + + +Normal values that should just work. + +>>> ci.validate('1714307103') +'1714307103' +>>> ci.validate('171430710-3') +'1714307103' +>>> ci.validate('0602910945') +'0602910945' +>>> ci.validate('0926687856') +'0926687856' +>>> ci.validate('0910005917') +'0910005917' + + +Some invalid numbers. + +>>> ci.validate('1714307104') # invalid check digit +Traceback (most recent call last): + ... +InvalidChecksum: ... +>>> ci.validate('171430710') # digit missing +Traceback (most recent call last): + ... +InvalidLength: ... +>>> ci.validate('123A567890') # contains a letter +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> ci.validate('1784307108') # third digit wrong +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> ci.validate('8814307107') # invalid province code +Traceback (most recent call last): + ... +InvalidComponent: ... diff --git a/tests/test_ec_ruc.doctest b/tests/test_ec_ruc.doctest new file mode 100644 index 0000000..2f1065f --- /dev/null +++ b/tests/test_ec_ruc.doctest @@ -0,0 +1,164 @@ +test_ec_ruc.doctest - more detailed doctests for stdnum.ec.ruc module + +Copyright (C) 2014 Jonathan Finlay +Copyright (C) 2014 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 +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.ec.ruc. It tries to +cover more corner cases and detailed functionality that is not really useful +as module documentation. + +>>> from stdnum.ec import ruc +>>> from stdnum.exceptions import * + + +Normal natural RUC values (third digit less than 6) that should just work. + +>>> numbers = ''' +... 0101016905001 +... 0602910945001 +... 0910005917001 +... 0926687856001 +... 1001152287001 +... 1102755442001 +... 1104552037001 +... 1311919078001 +... 1700672486001 +... 1702264233001 +... 1704159860001 +... 1710034065001 +... 1710585264001 +... 1710589373001 +... 1713238234001 +... 1714307103001 +... 1721788659001 +... 1803557964001 +... ''' +>>> [x for x in numbers.splitlines() if x and not ruc.is_valid(x)] +[] + + +Normal public RUC values (third digit is 6) that should just work. + +>>> numbers = ''' +... 0160001910001 +... 0260001060001 +... 0360001040001 +... 0560000540001 +... 0660000280001 +... 0660000600001 +... 0660000870001 +... 0968529830001 +... 1060000420001 +... 1060000690001 +... 1060008080001 +... 1060024600001 +... 1360000630001 +... 1560000780001 +... 1760001040001 +... 1760001550001 +... 1760009880001 +... 1768007390001 +... 1768152130001 +... 2160011760001 +... ''' +>>> [x for x in numbers.splitlines() if x and not ruc.is_valid(x)] +[] + + +Normal juridical RUC values (third digit is 9) that should just work. + +>>> numbers = ''' +... 0190155722001 +... 0490002669001 +... 0590041920001 +... 0790024656001 +... 0990138850001 +... 0992397535001 +... 1190015110001 +... 1390007791001 +... 1390089410001 +... 1390091474001 +... 1790011674001 +... 1790085783001 +... 1790325083001 +... 1791280172001 +... 1791714350001 +... 1792060346001 +... 1792141869001 +... 1792373255001 +... 1890001323001 +... 1890037646001 +... ''' +>>> [x for x in numbers.splitlines() if x and not ruc.is_valid(x)] +[] + + +Values that are invalid in one way or another: + +>>> ruc.validate('179206034601') # too short +Traceback (most recent call last): + ... +InvalidLength: ... +>>> ruc.validate('17920603A6001') # contains a character +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> ruc.validate('0170000610001') # third digit invalid +Traceback (most recent call last): + ... +InvalidComponent: ... + +>>> ruc.validate('1763154690001') # invalid check digit in natural RUC +Traceback (most recent call last): + ... +InvalidChecksum: ... +>>> ruc.validate('0160000610001') # invalid check digit in public RUC +Traceback (most recent call last): + ... +InvalidChecksum: ... +>>> ruc.validate('0190115799001') # invalid check digit in juridical RUC +Traceback (most recent call last): + ... +InvalidChecksum: ... + +>>> ruc.validate('8810034069001') # invalid province code in natural RUC +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> ruc.validate('8868152120001') # invalid province code in public RUC +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> ruc.validate('8892397539001') # invalid province code in juridical RUC +Traceback (most recent call last): + ... +InvalidComponent: ... + +>>> ruc.validate('0926687856000') # invalid establishment in natural RUC +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> ruc.validate('1760001550000') # invalid establishment in public RUC +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> ruc.validate('0992397535000') # invalid establishment in juridical RUC +Traceback (most recent call last): + ... +InvalidComponent: ... |