11from itertools import chain
2- from random import randint
2+ from random import randint , choice
3+ import string
4+
5+ # Mapeamento alfanumérico: A=10, B=11, ..., Z=35
6+ _CHAR_VALUES = {c : i + 10 for i , c in enumerate (string .ascii_uppercase )}
37
48# FORMATTING
59############
610
711
812def sieve (dirty : str ) -> str :
913 """
10- Removes specific symbols from a CNPJ (Brazilian Company Registration
11- Number) string.
12-
13- This function takes a CNPJ string as input and removes all occurrences of
14- the '.', '/' and '-' characters from it.
14+ Removes specific symbols from a CNPJ string.
1515
1616 Args:
1717 cnpj (str): The CNPJ string containing symbols to be removed.
@@ -22,67 +22,23 @@ def sieve(dirty: str) -> str:
2222 Example:
2323 >>> sieve("12.345/6789-01")
2424 "12345678901"
25- >>> sieve("98/76.543-2101")
26- "98765432101"
27-
28- .. note::
29- This method should not be used in new code and is only provided for
30- backward compatibility.
3125 """
32-
3326 return "" .join (filter (lambda char : char not in "./-" , dirty ))
3427
3528
3629def remove_symbols (dirty : str ) -> str :
37- """
38- This function is an alias for the `sieve` function, offering a more
39- descriptive name.
40-
41- Args:
42- dirty (str): The dirty string containing symbols to be removed.
43-
44- Returns:
45- str: A new string with the specified symbols removed.
46-
47- Example:
48- >>> remove_symbols("12.345/6789-01")
49- "12345678901"
50- >>> remove_symbols("98/76.543-2101")
51- "98765432101"
52- """
53-
30+ """Alias for sieve()."""
5431 return sieve (dirty )
5532
5633
5734def display (cnpj : str ) -> str | None :
5835 """
59- Will format an adequately formatted numbers-only CNPJ string,
60- adding in standard formatting visual aid symbols for display.
61-
62- Formats a CNPJ (Brazilian Company Registration Number) string for
63- visual display.
64-
65- This function takes a CNPJ string as input, validates its format, and
66- formats it with standard visual aid symbols for display purposes.
67-
68- Args:
69- cnpj (str): The CNPJ string to be formatted for display.
70-
71- Returns:
72- str: The formatted CNPJ with visual aid symbols if it's valid,
73- None if it's not valid.
74-
75- Example:
76- >>> display("12345678901234")
77- "12.345.678/9012-34"
78- >>> display("98765432100100")
79- "98.765.432/1001-00"
36+ Formats a CNPJ string for visual display (legacy, numeric only).
8037
8138 .. note::
8239 This method should not be used in new code and is only provided for
83- backward compatibility.
40+ backward compatibility. Use format_cnpj() instead.
8441 """
85-
8642 if not cnpj .isdigit () or len (cnpj ) != 14 or len (set (cnpj )) == 1 :
8743 return None
8844 return "{}.{}.{}/{}-{}" .format (
@@ -92,29 +48,25 @@ def display(cnpj: str) -> str | None:
9248
9349def format_cnpj (cnpj : str ) -> str | None :
9450 """
95- Formats a CNPJ (Brazilian Company Registration Number) string for visual
96- display.
51+ Formats a CNPJ string for visual display.
9752
98- This function takes a CNPJ string as input, validates its format, and
99- formats it with standard visual aid symbols for display purposes .
53+ Supports both the classic numeric format and the new alphanumeric format
54+ introduced by RFB Nota Técnica 49/2024 (effective July 2026) .
10055
10156 Args:
102- cnpj (str): The CNPJ string to be formatted for display .
57+ cnpj (str): The CNPJ string to be formatted (14 characters, no symbols) .
10358
10459 Returns:
105- str: The formatted CNPJ with visual aid symbols if it's valid,
106- None if it's not valid.
60+ str: The formatted CNPJ if valid, None otherwise.
10761
10862 Example:
10963 >>> format_cnpj("03560714000142")
11064 '03.560.714/0001-42'
111- >>> format_cnpj("98765432100100 ")
112- None
65+ >>> format_cnpj("B3S30714000142 ")
66+ 'B3.S30.714/0001-42'
11367 """
114-
11568 if not is_valid (cnpj ):
11669 return None
117-
11870 return "{}.{}.{}/{}-{}" .format (
11971 cnpj [:2 ], cnpj [2 :5 ], cnpj [5 :8 ], cnpj [8 :12 ], cnpj [12 :14 ]
12072 )
@@ -124,50 +76,125 @@ def format_cnpj(cnpj: str) -> str | None:
12476############
12577
12678
79+ def _char_to_val (c : str ) -> int :
80+ """
81+ Converts a CNPJ character to its numeric value for checksum calculation.
82+
83+ Digits map to their integer value; uppercase letters map to 10-35
84+ (A=10, B=11, ..., Z=35), as defined by RFB Nota Técnica 49/2024.
85+
86+ Args:
87+ c (str): A single character (digit or uppercase letter).
88+
89+ Returns:
90+ int: The numeric value of the character.
91+ """
92+ if c .isdigit ():
93+ return int (c )
94+ return _CHAR_VALUES [c .upper ()]
95+
96+
97+ def _is_valid_chars (cnpj : str ) -> bool :
98+ """
99+ Checks if all characters in a CNPJ are valid (digits or uppercase letters).
100+
101+ Args:
102+ cnpj (str): The CNPJ string to check.
103+
104+ Returns:
105+ bool: True if all characters are valid, False otherwise.
106+ """
107+ return all (c .isdigit () or c .upper () in _CHAR_VALUES for c in cnpj )
108+
109+
110+ def _hashdigit (cnpj : str , position : int ) -> int :
111+ """
112+ Calculates the checksum digit at the given position for the provided CNPJ.
113+
114+ Supports both numeric and alphanumeric CNPJs per RFB Nota Técnica 49/2024.
115+
116+ Args:
117+ cnpj (str): The CNPJ string.
118+ position (int): The position of the checksum digit (13 or 14).
119+
120+ Returns:
121+ int: The calculated checksum digit.
122+ """
123+ weightgen = chain (range (position - 8 , 1 , - 1 ), range (9 , 1 , - 1 ))
124+ val = (
125+ sum (_char_to_val (c ) * w for c , w in zip (cnpj , weightgen )) % 11
126+ )
127+ return 0 if val < 2 else 11 - val
128+
129+
130+ def _checksum (basenum : str ) -> str :
131+ """
132+ Calculates the verifying checksum digits for a given CNPJ base number.
133+
134+ Supports both numeric and alphanumeric base numbers.
135+
136+ Args:
137+ basenum (str): The 12-character CNPJ base number.
138+
139+ Returns:
140+ str: The two verifying checksum digits.
141+ """
142+ d1 = str (_hashdigit (basenum , 13 ))
143+ d2 = str (_hashdigit (basenum + d1 , 14 ))
144+ return d1 + d2
145+
146+
127147def validate (cnpj : str ) -> bool :
128148 """
129- Validates a CNPJ (Brazilian Company Registration Number) by comparing its
130- verifying checksum digits to its base number.
149+ Validates a CNPJ by comparing its verifying checksum digits to its base.
131150
132- This function checks the validity of a CNPJ by comparing its verifying
133- checksum digits to its base number. The input should be a string of digits
134- with the appropriate length.
151+ Supports both the classic numeric format and the new alphanumeric format
152+ introduced by RFB Nota Técnica 49/2024 (effective July 2026).
135153
136154 Args:
137- cnpj (str): The CNPJ to be validated.
155+ cnpj (str): The CNPJ to be validated (14 characters, no symbols) .
138156
139157 Returns:
140- bool: True if the checksum digits match the base number,
141- False otherwise.
158+ bool: True if valid, False otherwise.
142159
143160 Example:
144161 >>> validate("03560714000142")
145162 True
146163 >>> validate("00111222000133")
147164 False
165+ >>> validate("B3S30714000142")
166+ True
148167
149168 .. note::
150169 This method should not be used in new code and is only provided for
151170 backward compatibility.
152171 """
153-
154- if not cnpj .isdigit () or len (cnpj ) != 14 or len (set (cnpj )) == 1 :
172+ if (
173+ not isinstance (cnpj , str )
174+ or len (cnpj ) != 14
175+ or not _is_valid_chars (cnpj )
176+ or len (set (cnpj .upper ())) == 1
177+ ):
155178 return False
156179 return all (
157- _hashdigit (cnpj , i + 13 ) == int (v ) for i , v in enumerate (cnpj [12 :])
180+ _hashdigit (cnpj , i + 13 ) == int (cnpj [12 + i ])
181+ for i in range (2 )
158182 )
159183
160184
161185def is_valid (cnpj : str ) -> bool :
162186 """
163- Returns whether or not the verifying checksum digits of the given `cnpj`
164- match its base number.
187+ Returns whether the verifying checksum digits of the given CNPJ match
188+ its base number.
189+
190+ Supports both the classic numeric format and the new alphanumeric format
191+ introduced by RFB Nota Técnica 49/2024 (effective July 2026).
165192
166193 This function does not verify the existence of the CNPJ; it only
167194 validates the format of the string.
168195
169196 Args:
170- cnpj (str): The CNPJ to be validated, a 14-digit string
197+ cnpj (str): The CNPJ to be validated (14 characters, no symbols).
171198
172199 Returns:
173200 bool: True if the checksum digits match the base number,
@@ -178,85 +205,42 @@ def is_valid(cnpj: str) -> bool:
178205 True
179206 >>> is_valid("00111222000133")
180207 False
208+ >>> is_valid("B3S30714000142")
209+ True
181210 """
182-
183211 return isinstance (cnpj , str ) and validate (cnpj )
184212
185213
186- def generate (branch : int = 1 ) -> str :
214+ def generate (branch : int = 1 , alphanumeric : bool = False ) -> str :
187215 """
188- Generates a random valid CNPJ digit string. An optional branch number
189- parameter can be given; it defaults to 1.
216+ Generates a random valid CNPJ string.
190217
191218 Args:
192- branch (int): An optional branch number to be included in the CNPJ.
219+ branch (int): An optional branch number (default: 1).
220+ alphanumeric (bool): If True, generates a new alphanumeric CNPJ
221+ per RFB Nota Técnica 49/2024. Default: False.
193222
194223 Returns:
195224 str: A randomly generated valid CNPJ string.
196225
197226 Example:
198227 >>> generate()
199228 "30180536000105"
200- >>> generate(1234 )
201- "01745284123455 "
229+ >>> generate(alphanumeric=True )
230+ "B3S30714000142 "
202231 """
203-
204232 branch %= 10000
205233 branch += int (branch == 0 )
206234 branch = str (branch ).zfill (4 )
207- base = str (randint (0 , 99999999 )).zfill (8 ) + branch
208235
209- return base + _checksum (base )
236+ if alphanumeric :
237+ charset = string .digits + string .ascii_uppercase
238+ base = "" .join (choice (charset ) for _ in range (8 )) + branch
239+ # Ensure at least one letter in the base (distinguishes from numeric)
240+ if base [:8 ].isdigit ():
241+ pos = randint (0 , 7 )
242+ base = base [:pos ] + choice (string .ascii_uppercase ) + base [pos + 1 :]
243+ else :
244+ base = str (randint (0 , 99999999 )).zfill (8 ) + branch
210245
211-
212- def _hashdigit (cnpj : str , position : int ) -> int :
213- """
214- Calculates the checksum digit at the given `position` for the provided
215- `cnpj`. The input must contain all elements before `position`.
216-
217- Args:
218- cnpj (str): The CNPJ for which the checksum digit is calculated.
219- position (int): The position of the checksum digit to be calculated.
220-
221- Returns:
222- int: The calculated checksum digit.
223-
224- Example:
225- >>> _hashdigit("12345678901234", 13)
226- 3
227- >>> _hashdigit("98765432100100", 14)
228- 9
229- """
230-
231- weightgen = chain (range (position - 8 , 1 , - 1 ), range (9 , 1 , - 1 ))
232- val = (
233- sum (int (digit ) * weight for digit , weight in zip (cnpj , weightgen )) % 11
234- )
235- return 0 if val < 2 else 11 - val
236-
237-
238- def _checksum (basenum : str ) -> str :
239- """
240- Calculates the verifying checksum digits for a given CNPJ base number.
241-
242- This function computes the verifying checksum digits for a provided CNPJ
243- base number. The `basenum` should be a digit-string of the appropriate
244- length.
245-
246- Args:
247- basenum (str): The base number of the CNPJ for which verifying checksum
248- digits are calculated.
249-
250- Returns:
251- str: The verifying checksum digits.
252-
253- Example:
254- >>> _checksum("123456789012")
255- "30"
256- >>> _checksum("987654321001")
257- "41"
258- """
259-
260- verifying_digits = str (_hashdigit (basenum , 13 ))
261- verifying_digits += str (_hashdigit (basenum + verifying_digits , 14 ))
262- return verifying_digits
246+ return base + _checksum (base )
0 commit comments