11from itertools import chain
2- from random import randint
2+ from random import choices , randint
3+ from string import ascii_uppercase , digits
34
45# FORMATTING
56############
@@ -83,7 +84,12 @@ def display(cnpj: str) -> str | None:
8384 backward compatibility.
8485 """
8586
86- if not cnpj .isdigit () or len (cnpj ) != 14 or len (set (cnpj )) == 1 :
87+ if (
88+ len (cnpj ) != 14
89+ or not _is_alphanumeric (cnpj [:12 ])
90+ or not cnpj [12 :].isdigit ()
91+ or len (set (cnpj )) == 1
92+ ):
8793 return None
8894 return "{}.{}.{}/{}-{}" .format (
8995 cnpj [:2 ], cnpj [2 :5 ], cnpj [5 :8 ], cnpj [8 :12 ], cnpj [12 :]
@@ -124,6 +130,27 @@ def format_cnpj(cnpj: str) -> str | None:
124130############
125131
126132
133+ def _is_alphanumeric (cnpj : str ) -> bool :
134+ """
135+ Checks whether all characters are digits or uppercase letters.
136+
137+ Args:
138+ cnpj (str): The CNPJ string to be validated.
139+
140+ Returns:
141+ bool: True if all characters are either digits or uppercase letters,
142+ False otherwise.
143+
144+ Example:
145+ >>> _is_alphanumeric("035ABC1400Z142")
146+ True
147+ >>> _is_alphanumeric("0011-22200013!")
148+ False
149+ """
150+
151+ return all (char in (digits + ascii_uppercase ) for char in cnpj )
152+
153+
127154def validate (cnpj : str ) -> bool :
128155 """
129156 Validates a CNPJ (Brazilian Company Registration Number) by comparing its
@@ -151,7 +178,12 @@ def validate(cnpj: str) -> bool:
151178 backward compatibility.
152179 """
153180
154- if not cnpj .isdigit () or len (cnpj ) != 14 or len (set (cnpj )) == 1 :
181+ if (
182+ len (cnpj ) != 14
183+ or not _is_alphanumeric (cnpj [:12 ])
184+ or not cnpj [12 :].isdigit ()
185+ or len (set (cnpj )) == 1
186+ ):
155187 return False
156188 return all (
157189 _hashdigit (cnpj , i + 13 ) == int (v ) for i , v in enumerate (cnpj [12 :])
@@ -209,6 +241,34 @@ def generate(branch: int = 1) -> str:
209241 return base + _checksum (base )
210242
211243
244+ def generate_alphanumeric (branch : str = "1" ) -> str :
245+ """
246+ Generates a random valid alphanumeric CNPJ digit string. An optional branch
247+ number parameter can be given; it defaults to '1'.
248+
249+ Args:
250+ branch (str): An optional branch number to be included in the CNPJ.
251+
252+ Returns:
253+ str: A randomly generated valid alphanumeric CNPJ string.
254+
255+ Example:
256+ >>> generate_alphanumeric()
257+ "9359QAG9000184"
258+ >>> generate_alphanumeric('1234')
259+ "NX9K79E2123400"
260+ """
261+
262+ branch = branch [:4 ] if len (branch ) >= 4 else branch .zfill (4 )
263+ branch = (
264+ "0001" if branch == "0000" or not _is_alphanumeric (branch ) else branch
265+ )
266+
267+ base = "" .join (choices (digits * 3 + ascii_uppercase , k = 8 )) + branch
268+
269+ return base + _checksum (base )
270+
271+
212272def _hashdigit (cnpj : str , position : int ) -> int :
213273 """
214274 Calculates the checksum digit at the given `position` for the provided
@@ -230,7 +290,10 @@ def _hashdigit(cnpj: str, position: int) -> int:
230290
231291 weightgen = chain (range (position - 8 , 1 , - 1 ), range (9 , 1 , - 1 ))
232292 val = (
233- sum (int (digit ) * weight for digit , weight in zip (cnpj , weightgen )) % 11
293+ sum (
294+ (ord (digit ) - 48 ) * weight for digit , weight in zip (cnpj , weightgen )
295+ )
296+ % 11
234297 )
235298 return 0 if val < 2 else 11 - val
236299
0 commit comments