lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 1.13-26-g54e2e8f

[Date Prev][Date Next] [Thread Prev][Thread Next]

python-stdnum branch master updated. 1.13-26-g54e2e8f



This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-stdnum".

The branch, master has been updated
       via  54e2e8fda313c1cb47c4cfdc71f42be272fe74e4 (commit)
      from  356a729bf31ad8e971f3049523ddf4498dd999ca (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://arthurdejong.org/git/python-stdnum/commit/?id=54e2e8fda313c1cb47c4cfdc71f42be272fe74e4

commit 54e2e8fda313c1cb47c4cfdc71f42be272fe74e4
Author: Leandro Regueiro <leandro.regueiro@gmail.com>
Date:   Fri Apr 10 18:17:19 2020 +0200

    Add support for El Salvador TIN number
    
    Closes https://github.com/arthurdejong/python-stdnum/issues/133
    Closes https://github.com/arthurdejong/python-stdnum/pull/215

diff --git a/stdnum/sv/__init__.py b/stdnum/sv/__init__.py
new file mode 100644
index 0000000..dd63b08
--- /dev/null
+++ b/stdnum/sv/__init__.py
@@ -0,0 +1,24 @@
+# __init__.py - collection of El Salvador numbers
+# coding: utf-8
+#
+# Copyright (C) 2019 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 El Salvador numbers."""
+
+# provide aliases
+from stdnum.sv import nit as vat  # noqa: F401
diff --git a/stdnum/sv/nit.py b/stdnum/sv/nit.py
new file mode 100644
index 0000000..ed2f5f9
--- /dev/null
+++ b/stdnum/sv/nit.py
@@ -0,0 +1,121 @@
+# nit.py - functions for handling El Salvador NIT numbers
+# coding: utf-8
+#
+# Copyright (C) 2020 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
+
+"""NIT (Número de Identificación Tributaria, El Salvador tax number).
+
+This number consists of 14 digits, usually separated into four groups
+using hyphens to make it easier to read, like XXXX-XXXXXX-XXX-X.
+
+The first four digits indicate the code for the municipality of birth
+for natural persons or the municipality of stablishment for juridical
+persons. NIT for El Salvador nationals begins with either 0 or 1, and
+for foreigners it begins with 9.
+
+The following six digits indicate the date of birth for the natural
+person, or the stablishment date for the juridical person, using the
+format DDMMYY, where DD is the day, MM is the month, and YY is the
+year. For example XXXX-051180-XXX-X is (November 5 1980)
+
+The next 3 digits are a sequential number.
+
+The last digit is the check digit, which is used to verify the number
+was correctly typed.
+
+More information:
+
+* https://es.wikipedia.org/wiki/Identificaci%C3%B3n_tributaria
+* https://www.listasal.info/articulos/nit-el-salvador.shtml
+* https://tramitesyrequisitos.com/el-salvador/nit/#Estructura_del_NIT
+* 
https://www.svcommunity.org/forum/programacioacuten/como-calcular-digito-verificador-del-dui-y-nit/msg951882/#msg951882
+
+>>> validate('0614-050707-104-8')
+'06140507071048'
+>>> validate('SV 0614-050707-104-8')
+'06140507071048'
+>>> validate('0614-050707-104-0')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('12345678')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> format('06140507071048')
+'0614-050707-104-8'
+"""
+
+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()
+    if number.startswith('SV'):
+        return number[2:]
+    return number
+
+
+def calc_check_digit(number):
+    """Calculate the check digit."""
+    # Old NIT
+    if number[10:13] <= '100':
+        weights = (14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2)
+        total = sum(int(n) * w for n, w in zip(number, weights))
+        return str((total % 11) % 10)
+    # New NIT
+    weights = (2, 7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
+    total = sum(int(n) * w for n, w in zip(number, weights))
+    return str((-total % 11) % 10)
+
+
+def validate(number):
+    """Check if the number is a valid El Salvador NIT number.
+
+    This checks the length, formatting and check digit.
+    """
+    number = compact(number)
+    if len(number) != 14:
+        raise InvalidLength()
+    if not isdigits(number):
+        raise InvalidFormat()
+    if number[0] not in ('0', '1', '9'):
+        raise InvalidComponent()
+    if number[-1] != calc_check_digit(number):
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Check if the number is a valid El Salvador NIT number."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
+
+
+def format(number):
+    """Reformat the number to the standard presentation format."""
+    number = compact(number)
+    return '-'.join([number[:4], number[4:-4], number[-4:-1], number[-1]])
diff --git a/tests/test_sv_nit.doctest b/tests/test_sv_nit.doctest
new file mode 100644
index 0000000..2816111
--- /dev/null
+++ b/tests/test_sv_nit.doctest
@@ -0,0 +1,263 @@
+test_sv_nit.doctest - more detailed doctests for stdnum.sv.nit module
+
+Copyright (C) 2020 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.sv.nit module. It
+tries to test more corner cases and detailed functionality that is not really
+useful as module documentation.
+
+>>> from stdnum.sv import nit
+
+
+Tests for some corner cases.
+
+>>> nit.validate('06140507071048')
+'06140507071048'
+>>> nit.validate('0614-050707-104-8')
+'06140507071048'
+>>> nit.validate('SV 0614-050707-104-8')
+'06140507071048'
+>>> nit.format('06140507071048')
+'0614-050707-104-8'
+>>> nit.validate('12345')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> nit.validate('VV345678901234')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> nit.validate('52345678901234')
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> nit.validate('0614-050707-104-0')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+
+
+These have been found online and should all be valid numbers.
+
+>>> numbers = '''
+...
+... 0209-200875-101-2
+... 0210-191171-001-6
+... 02101411580016
+... 03150507931017
+... 0404-131259-001-1
+... 0412-270681-101-2
+... 0427-151046-001-8
+... 05012306111015
+... 0511-010611-001-9
+... 0511-021279-102-2
+... 0511-090193-101-0
+... 0511-210590-101-2
+... 0511-231267-101-9
+... 0511-281111-101-6
+... 0511-310300-102-9
+... 05110108740018
+... 0514-160953-001-9
+... 0602-010373-101-4
+... 0602-100972-101-0
+... 0602-221085-101-7
+... 0604-160265-001-0
+... 06040904540018
+... 0614-010294-106-1
+... 0614-010462-002-1
+... 0614-010858-001-7
+... 0614-010890-101-0
+... 0614-010900-102-1
+... 0614-020190-101-2
+... 0614-020202-109-1
+... 0614-020557-009-6
+... 0614-020914-109-2
+... 0614-030593-102-9
+... 0614-031006-106-7
+... 0614-040100-101-6
+... 0614-040393-101-0
+... 0614-041204-103-6
+... 0614-050110-101-8
+... 0614-050205-103-0
+... 0614-050716-104-7
+... 0614-051279-134-2
+... 0614-060154-005-2
+... 0614-070108-106-2
+... 0614-070353-014-0
+... 0614-070590-102-1
+... 0614-070685-142-7
+... 0614-071107-103-0
+... 0614-080202-102-8
+... 0614-080609-105-5
+... 0614-080645-001-2
+... 0614-081193-102-9
+... 0614-090390-102-9
+... 0614-090598-101-1
+... 0614-090684-002-0
+... 0614-100215-103-5
+... 0614-100395-101-9
+... 0614-100856-006-8
+... 0614-110394-103-7
+... 0614-110416-107-8
+... 0614-110785-132-6
+... 0614-111267-001-6
+... 0614-120299-103-8
+... 0614-120563-013-4
+... 0614-120913-104-2
+... 0614-120913-109-3
+... 0614-130499-102-4
+... 0614-131009-102-7
+... 0614-131228-003-9
+... 0614-140700-101-4
+... 0614-140909-104-5
+... 0614-140961-006-0
+... 0614-141292-102-4
+... 0614-150304-102-7
+... 0614-150311-102-5
+... 0614-150397-102-4
+... 0614-150415-110-1
+... 0614-150416-103-4
+... 0614-150965-001-7
+... 0614-160277-001-5
+... 0614-160295-103-5
+... 0614-160407-101-6
+... 0614-160814-102-7
+... 0614-160953-001-0
+... 0614-170860-013-0
+... 0614-170902-101-4
+... 0614-171285-002-9
+... 0614-180901-101-6
+... 0614-181102-101-0
+... 0614-181191-101-6
+... 0614-190377-001-6
+... 0614-190618-110-4
+... 0614-190697-107-5
+... 0614-191190-107-7
+... 0614-191192-106-0
+... 0614-200217-104-0
+... 0614-200302-104-2
+... 0614-200905-102-4
+... 0614-210253-001-4
+... 0614-210297-103-6
+... 0614-210494-104-5
+... 0614-220277-002-3
+... 0614-220310-102-4
+... 0614-220402-101-6
+... 0614-220605-102-8
+... 0614-220610-105-0
+... 0614-220993-101-0
+... 0614-230803-103-0
+... 0614-230817-107-9
+... 0614-230952-002-2
+... 0614-231061-011-4
+... 0614-240209-105-4
+... 0614-240715-105-5
+... 0614-241272-105-6
+... 0614-250582-002-0
+... 0614-250795-012-4
+... 0614-250796-105-4
+... 0614-260406-104-1
+... 0614-260710-106-0
+... 0614-270696-101-2
+... 0614-271008-102-7
+... 0614-280142-002-7
+... 0614-280194-105-3
+... 0614-280268-104-7
+... 0614-280297-188-6
+... 0614-280454-004-4
+... 0614-280470-120-7
+... 0614-280590-147-1
+... 0614-280617-101-9
+... 0614-280815-109-0
+... 0614-291100-103-0
+... 0614-300312-104-3
+... 0614-300380-107-9
+... 0614-300713-103-5
+... 0614-300907-101-3
+... 0614-301002-107-0
+... 0614-310797-109-0
+... 0614-310888-102-7
+... 0614-310898-102-1
+... 06140108140066
+... 06140301081039
+... 06140302891026
+... 06140501350010
+... 06140709041094
+... 06140710770019
+... 06140812901018
+... 06140902941060
+... 06141010971041
+... 06141102081026
+... 06141212961030
+... 06141305941039
+... 06141306780010
+... 06141311011019
+... 06141405881084
+... 06141410021050
+... 06141503041027
+... 06141512001054
+... 06141601921043
+... 06141605771097
+... 06141705901028
+... 06141901001027
+... 06141901831364
+... 06141905061032
+... 06142110700016
+... 06142111001058
+... 06142206981017
+... 06142502781139
+... 06142507001042
+... 06142507891013
+... 06142510021011
+... 06142703771050
+... 06142806761142
+... 06142809931022
+... 06143011931011
+... 06143110860018
+... 06151909680013
+... 0616-061058-003-8
+... 0616-100883-103-9
+... 0619-260185-101-4
+... 0715-231060-003-2
+... 0715-260862-001-6
+... 0802-230151-001-8
+... 0803-130153-001-8
+... 0821-010148-001-1
+... 0821-060256-001-5
+... 0906-040354-001-6
+... 1008-070753-001-8
+... 1010-060176-101-0
+... 1108-250573-101-6
+... 1109-070374-101-1
+... 1121-190563-001-0
+... 1122-060865-001-4
+... 1217-160992-101-8
+... 12171609921018
+... 1319-260280-101-8
+... 1408-160337-001-8
+... 1408-260786-101-2
+... 14162001681012
+... 9450-270760-001-1
+... 9483-140205-101-6
+... 9483-260110-101-0
+... 94833011640013
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not nit.is_valid(x)]
+[]

-----------------------------------------------------------------------

Summary of changes:
 stdnum/{uy => sv}/__init__.py |   6 +-
 stdnum/sv/nit.py              | 121 +++++++++++++++++++
 tests/test_sv_nit.doctest     | 263 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 387 insertions(+), 3 deletions(-)
 copy stdnum/{uy => sv}/__init__.py (85%)
 create mode 100644 stdnum/sv/nit.py
 create mode 100644 tests/test_sv_nit.doctest


hooks/post-receive
-- 
python-stdnum