diff --git a/docs/index.rst b/docs/index.rst index e9c76c32..6acfebe0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -298,6 +298,7 @@ Available formats ro.onrc rs.pib ru.inn + ru.ogrn se.orgnr se.personnummer se.postnummer diff --git a/docs/stdnum.ru.ogrn.rst b/docs/stdnum.ru.ogrn.rst new file mode 100644 index 00000000..aee48283 --- /dev/null +++ b/docs/stdnum.ru.ogrn.rst @@ -0,0 +1,5 @@ +stdnum.ru.ogrn +============== + +.. automodule:: stdnum.ru.ogrn + :members: \ No newline at end of file diff --git a/stdnum/ru/ogrn.py b/stdnum/ru/ogrn.py new file mode 100644 index 00000000..087cfeb5 --- /dev/null +++ b/stdnum/ru/ogrn.py @@ -0,0 +1,97 @@ + +# ogrn.py - functions for handling Russian company registration numbers +# coding: utf-8 +# +# Copyright (C) 2010-2024 Arthur de Jong and others +# +# 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 + +"""OGRN (Primary State Registration Number). + +The OGRN is a Russian identifier for legal entities that consists of either 13 or 15 digits. + +>>> validate('1022200525819') +True +>>> validate('1022500001325') +True +>>> validate('10277395526422') # 14 digits +Traceback (most recent call last): + ... +InvalidLength: ... +>>> validate('1022500001328') # invalid check digit +Traceback (most recent call last): + ... +InvalidChecksum: ... +>>> validate('0022500001325') # starts with 0 +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> validate('102250000') # too short +Traceback (most recent call last): + ... +InvalidLength: ... +>>> validate('385768585948949') # 15 digits +True +""" + +import re + +from stdnum.exceptions import * + + +def validate(text): + """Determine if the given string is a valid OGRN.""" + if text[0] == '0': + raise InvalidComponent() + control_digit = int(text[-1]) + if control_digit != calculate_control_digit(text): + raise InvalidChecksum() + return True + + +def format(text): + """Normalize the given string to a valid OGRN.""" + if not isinstance(text, str): + return None + match = re.compile(r'\b(\d{13}|\d{15})\b').search(text) + if match is None: + raise InvalidLength() + return match.group(1) + + +def calculate_control_digit(grn): + """Calculate the control digit of the OGRN based on its length.""" + if len(grn) == 13: + number = int(grn[:12]) + mod_result = number % 11 + calculated_digit = mod_result if mod_result != 10 else 0 + return calculated_digit + elif len(grn) == 15: + number = int(grn[:14]) + mod_result = number % 13 + calculated_digit = mod_result if mod_result != 10 else 0 + return calculated_digit + else: + raise InvalidLength() + + +def is_valid(text): + """Check if the number is a valid OGRN.""" + try: + normalized_text = format(text) + return bool(normalized_text and validate(normalized_text)) + except ValidationError: + return False diff --git a/tests/test_ru_ogrn.doctest b/tests/test_ru_ogrn.doctest new file mode 100644 index 00000000..41f009da --- /dev/null +++ b/tests/test_ru_ogrn.doctest @@ -0,0 +1,43 @@ +test_ru_ogrn.doctest - more detailed doctests for the stdnum.ru.ogrn module + +Copyright (C) 2010-2024 Arthur de Jong and others + +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.ru.ogrn module. + +These tests validate the format, normalization, and validity of various +OGRN numbers, ensuring they conform to expected behavior. + +>>> from stdnum.ru import ogrn +>>> from stdnum.exceptions import * + +This is a list of OGRNs that should all be valid numbers: + +>>> valid_numbers = ''' +... +... 1027739552642 +... 1022600000092 +... 1022500000566 +... 1027100000311 +... 1022400007508 +... 1022300001811 +... 1022300000502 +... +... ''' +>>> [x for x in valid_numbers.splitlines() if x and not ogrn.is_valid(x)] +[] diff --git a/tox.ini b/tox.ini index f338dd9a..7b0bbb36 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,7 @@ [tox] envlist = py{27,36,37,38,39,310,311,312,py,py3},flake8,docs,headers skip_missing_interpreters = true +requires = virtualenv<20.22.0 [testenv] deps = pytest