lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 2.1-20-gcd94bf1

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

python-stdnum branch master updated. 2.1-20-gcd94bf1



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  cd94bf1147508a5132f0439e8d6c1ed3e2a7e14f (commit)
      from  293136bc3a83f53bb13eda0806cb6d4b7dfcea0d (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=cd94bf1147508a5132f0439e8d6c1ed3e2a7e14f

commit cd94bf1147508a5132f0439e8d6c1ed3e2a7e14f
Author: dotbit1 <68584562+dotbit1@users.noreply.github.com>
Date:   Thu Feb 27 09:57:41 2025 +0200

    Support the new Romanian ONRC format
    
    Closes https://github.com/arthurdejong/python-stdnum/pull/465

diff --git a/stdnum/ro/onrc.py b/stdnum/ro/onrc.py
index ea33558..8b2eb5d 100644
--- a/stdnum/ro/onrc.py
+++ b/stdnum/ro/onrc.py
@@ -1,8 +1,8 @@
 # onrc.py - functions for handling Romanian ONRC numbers
 # coding: utf-8
 #
-# Copyright (C) 2020 Dimitrios Josef Moustos
-# Copyright (C) 2020 Arthur de Jong
+# Copyright (C) 2020-2024 Dimitrios Josef Moustos
+# Copyright (C) 2020-2025 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
@@ -23,9 +23,14 @@
 
 All businesses in Romania have the to register with the National Trade
 Register Office to receive a registration number. The number contains
-information about the type of company, county, a sequence number and
-registration year. This number can change when registration information
-changes.
+information about the type of company and registration year.
+
+On 2024-07-26 a new format was introduced and for a while both old and new
+formats need to be valid.
+
+More information:
+
+* 
https://targetare.ro/blog/schimbari-importante-la-registrul-comertului-ce-trebuie-sa-stii-despre-noul-format-al-numarului-de-ordine
 
 >>> validate('J52/750/2012')
 'J52/750/2012'
@@ -33,7 +38,13 @@ changes.
 Traceback (most recent call last):
     ...
 InvalidComponent: ...
-"""
+>>> validate('J2012000750528')
+'J2012000750528'
+>>> validate('J2012000750529')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+"""  # noqa: E501
 
 from __future__ import annotations
 
@@ -41,18 +52,18 @@ import datetime
 import re
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # These characters should all be replaced by slashes
 _cleanup_re = re.compile(r'[ /\\-]+')
 
 # This pattern should match numbers that for some reason have a full date
-# as last field
-_onrc_fulldate_re = 
re.compile(r'^([A-Z][0-9]+/[0-9]+/)\d{2}[.]\d{2}[.](\d{4})$')
+# as last field for the old format
+_old_onrc_fulldate_re = 
re.compile(r'^([A-Z][0-9]+/[0-9]+/)\d{2}[.]\d{2}[.](\d{4})$')
 
-# This pattern should match all valid numbers
-_onrc_re = re.compile(r'^[A-Z][0-9]+/[0-9]+/[0-9]+$')
+# This pattern should match all valid numbers in the old format
+_old_onrc_re = re.compile(r'^[A-Z][0-9]+/[0-9]+/[0-9]+$')
 
 # List of valid counties
 _counties = set(list(range(1, 41)) + [51, 52])
@@ -69,19 +80,16 @@ def compact(number: str) -> str:
     if number[2:3] == '/':
         number = number[:1] + '0' + number[1:]
     # convert trailing full date to year only
-    m = _onrc_fulldate_re.match(number)
+    m = _old_onrc_fulldate_re.match(number)
     if m:
         number = ''.join(m.groups())
     return number
 
 
-def validate(number: str) -> str:
-    """Check if the number is a valid ONRC."""
-    number = compact(number)
-    if not _onrc_re.match(number):
+def _validate_old_format(number: str) -> None:
+    # old YJJ/XXXX/AAAA format
+    if not _old_onrc_re.match(number):
         raise InvalidFormat()
-    if number[:1] not in 'JFC':
-        raise InvalidComponent()
     county, serial, year = number[1:].split('/')
     if len(serial) > 5:
         raise InvalidLength()
@@ -89,8 +97,58 @@ def validate(number: str) -> str:
         raise InvalidComponent()
     if len(year) != 4:
         raise InvalidLength()
-    if int(year) < 1990 or int(year) > datetime.date.today().year:
+    # old format numbers will not be issued after 2024
+    if int(year) < 1990 or int(year) > 2024:
+        raise InvalidComponent()
+
+
+def _calc_check_digit(number: str) -> str:
+    """Calculate the check digit for the new ONRC format."""
+    # replace letters with digits
+    number = str(ord(number[0]) % 10) + number[1:]
+    return str(sum(int(n) for n in number[:-1]) % 10)
+
+
+def _validate_new_format(number: str) -> None:
+    # new YAAAAXXXXXXJJC format, no slashes
+    if not isdigits(number[1:]):
+        raise InvalidFormat()
+    if len(number) != 14:
+        raise InvalidLength()
+    year = int(number[1:5])
+    if year < 1990 or year > datetime.date.today().year:
+        raise InvalidComponent()
+    # the registration year determines which counties are allowed
+    # companies registered after 2024-07-26 have 00 as county code
+    county = int(number[11:13])
+    if year < 2024:
+        if county not in _counties:
+            raise InvalidComponent()
+    elif year == 2024:
+        if county not in _counties.union([0]):
+            raise InvalidComponent()
+    else:
+        if county != 0:
+            raise InvalidComponent()
+    if number[-1] != _calc_check_digit(number):
+        raise InvalidChecksum
+
+
+def validate(number: str) -> str:
+    """Check if the number is a valid ONRC."""
+    number = compact(number)
+    # J: legal entities (e.g., LLC, SA, etc.)
+    # F: sole proprietorships, individual and family businesses
+    # C: cooperative societies.
+    if number[:1] not in 'JFC':
         raise InvalidComponent()
+    if '/' in number:
+        # old YJJ/XXXX/AAAA format, still supported but will be phased out, 
companies
+        # will get a new number
+        _validate_old_format(number)
+    else:
+        # new YAAAAXXXXXXJJC format, no slashes
+        _validate_new_format(number)
     return number
 
 
diff --git a/tests/test_ro_onrc.doctest b/tests/test_ro_onrc.doctest
index 6b47a5f..973d00d 100644
--- a/tests/test_ro_onrc.doctest
+++ b/tests/test_ro_onrc.doctest
@@ -1,6 +1,6 @@
 test_ro_onrc.doctest - more detailed doctests for the stdnum.ro.onrc module
 
-Copyright (C) 2020 Arthur de Jong
+Copyright (C) 2020-2025 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
@@ -24,7 +24,7 @@ This file contains more detailed doctests for the 
stdnum.ro.onrc module.
 >>> from stdnum.exceptions import *
 
 
-Test some corner cases.
+Test some corner cases of the old format.
 
 >>> onrc.validate('J/52/750/2012')
 'J52/750/2012'
@@ -62,6 +62,40 @@ InvalidComponent: ...
 Traceback (most recent call last):
     ...
 InvalidComponent: ...
+>>> onrc.validate('J22/1515/2007/123')  # too many components
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+
+
+Test some corner cases of the new format.
+
+>>> onrc.validate('J2016012984401')
+'J2016012984401'
+>>> onrc.validate('J201601298440')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> onrc.validate('J1822012984405')  # year too old
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> onrc.validate('J2112012984408')  # year in the future
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> onrc.validate('J2016012984007')  # number before 2024 with 0 county
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> onrc.validate('J2024012984994')  # number from 2024 with invalid county
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> onrc.validate('J2025012984401')  # number after 2024 with non-0 county
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 
 
 These have been found online and should all be valid numbers.
@@ -72,203 +106,143 @@ These have been found online and should all be valid 
numbers.
 ... C22/4/2018
 ... C30/2/2009
 ... F01 /592 /2013
+... F01 587 2023
+... F01 988 2003
 ... F01/1477/2011
-... F04/876/2004
 ... F05/696/2006
-... F06/340/2011
-... F07/176/2010
 ... F07/477/2003
 ... F08/1095/2005
-... F10/841/2012
-... F11/78/2012
 ... F12/617/2011
 ... F13/1917/2005
 ... F14/217/2013
-... F14/218/2016
-... F18/1032/2011
 ... F20/491/2013
-... F30/98/2008
 ... F31/786/2011
 ... F32/639/2013
 ... F36/64/2008
-... F37/832/2013
-... F40/682/2004
 ... F5/1055/2013
 ... F5/2659/2013
 ... F5/550/2012
-... F5/678/2013
 ... F6/496/2015
 ... F8/1302/2013
 ... F8/672/2012
 ... J 01/585/2002
 ... J 40/1323/2005
-... J01/173/2004
 ... J01/568/2015
 ... J02/1253/2008
 ... J02/143/2011
-... J02/169/01.02.2006
 ... J02/238/2012
 ... J02/310/2004
-... J03/1336/2007
-... J03/179/2004
-... J03/2299/1992
 ... J03/442/2002
-... J03/630/2002
-... J03/803/2009
 ... J03/920/2002
 ... J04/368/2006
-... J04/822/1994
 ... J05/1117/2004
-... J05/1464/2011
 ... J05/1729/09.09.1991
-... J05/256/2010
 ... J05/5114/1994
 ... J05/582/2011
-... J06/1282/1994
 ... J06/307/2009
-... J06/330/2003
 ... J06/52/2001
-... J06/837/2008
+... J07 4 2024
 ... J07/147/2001
 ... J07/208/1998
-... J08/1233/2005
-... J08/1617/1997
 ... J08/1716/1997
-... J08/215/27.02.1998
 ... J08/2679/2008
 ... J08/2720/2007
 ... J08/545/2001
-... J08/61/2010
-... J08/68/2003
-... J08/926/2008
-... J09/2118/1994
+... J09 49 2023
+... J09 87 2024
 ... J1/384/13.05.2015
-... J1/398/2005
-... J1/499/2012
-... J1/628/2016
 ... J10/460/2012
-... J12/1103/2003
-... J12/1366/2003
 ... J12/1701/16.10.1996
 ... J12/1811/2002
-... J12/1816/2007
-... J12/1932/1993
 ... J12/2322/2002
 ... J12/2553/1992
 ... J12/3388/2016
-... J12/3828/16.06.2017
 ... J12/4365/2006
 ... J12/487/2012
-... J12/573/2010
 ... J12/595/1991
 ... J12/633/2012
 ... J12/850/2014
 ... J13/197/2012
 ... J13/2949/1991
-... J13/529/2006
 ... J13/60/2005
 ... J13/674/2016
-... J15/1279/1993
-... J15/680/2005
-... J16/1148/2005
-... J16/1930/2013
-... J16/2036/2011
 ... J17/1241/1992
-... J17/2697/1992
-... J17/861/2012
 ... J18/583/2015
 ... J18/877/2006
-... J19/230/2007
-... J19/55/2002
 ... J20/2034/1993
-... J21/512/2016
 ... J21/565/1992
-... J22/1249/2012
-... J22/1774/2008
-... J22/3673/1994
 ... J22/874/1996
 ... J22/930/2016
 ... J23/134/27.01.2006
 ... J23/1657/14.06.2012
-... J23/461/2009
 ... J23/4802/2017
 ... J23/864/2017
 ... J24/1082/2015
-... J24/246/2005
 ... J24/908/2006
 ... J26/1077/2008
-... J29/264/2014
 ... J30/428/1993
-... J30/477/2014
-... J30/482/2014
 ... J30/722/08.08.2016
 ... J30/734/2014
 ... J30/855/1993
-... J30/99/2009
-... J31 / 248 / 1994
 ... J32/2241/1994
-... J32/331/2011
 ... J32/631/2013
-... J33/277/2011
-... J33/451/2012
 ... J35/1056/2008
-... J35/48/2013
-... J35/625/2003
 ... J36/25/2001
 ... J36/284/2017
-... J36/369/2015
 ... J36/637/2018
-... J36/691/2008
 ... J37/384/2009
-... J38/163/23.02.2006
 ... J39 /771 /2005
+... J40 14850 2021
+... J40 1546 2018
+... J40 3509 2020
+... J40 4214 2024
+... J40 4834 2000
+... J40 5302 2020
+... J40 8068 2024
+... J40 8364 2024
+... J40 9214 2024
 ... J40/10944/2006
-... J40/11566/2011
 ... J40/11591/2003
 ... J40/11591/2009
 ... J40/1293/23.10.2014
-... J40/13920/2003
 ... J40/1472/1992
-... J40/1498/2010
-... J40/1667/2010
 ... J40/17202/2006
 ... J40/19473/2006
-... J40/19754/2007
-... J40/20281/2004
-... J40/23948/14.12.1994
-... J40/24874/1994
-... J40/2810/2017
-... J40/2867/2004
-... J40/3758/2008
 ... J40/3905/30.03.2015
 ... J40/4206/2008
-... J40/4463/2008
 ... J40/4502/2011
 ... J40/467/1998
 ... J40/5139/1998
-... J40/7613/2003
 ... J40/7888/1999
-... J40/8005/1995
-... J40/8577/2006
 ... J40/8633/2013
-... J40/8910/2007
-... J40/8998/2010
-... J40/9712/2008
 ... J5/1243/2016
 ... J5/2916/03.09.1993
 ... J51/159/2016
 ... J51/229/2011
-... J51/74/2009
-... J52/128/2014
 ... J52/474/1994
-... J52/60/2004
-... J6/974/2016
-... J7/2/2005
 ... j05/1455/2012
 ... j30/61/2010
 ... j39/151/2019
 ... j40/3674/2002
 ...
+... C2005000007045
+... C2005000011220
+... C2009000002022
+... J1992002621040
+... J1993001036400
+... J1994000536225
+... J2007007766403
+... J2012000750528
+... J2016012984401
+... J2021016333409
+... J2024012585407
+... J2024030812006
+... J2024035703000
+... J2024044457006
+... J2025007112004
+... J2025066599008
+... J2025085871002
+...
 ... '''
 >>> [x for x in numbers.splitlines() if x and not onrc.is_valid(x)]
 []

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

Summary of changes:
 stdnum/ro/onrc.py          |  96 +++++++++++++++++++++------
 tests/test_ro_onrc.doctest | 162 +++++++++++++++++++--------------------------
 2 files changed, 145 insertions(+), 113 deletions(-)


hooks/post-receive
-- 
python-stdnum