diff --git a/src/Provider/pt_BR/Company.php b/src/Provider/pt_BR/Company.php index 721f0568a6..69b466f7ab 100644 --- a/src/Provider/pt_BR/Company.php +++ b/src/Provider/pt_BR/Company.php @@ -33,4 +33,23 @@ public function cnpj($formatted = true) return $formatted ? vsprintf('%d%d.%d%d%d.%d%d%d/%d%d%d%d-%d%d', str_split($n)) : $n; } + + /** + * A random CNPJ alphanumeric. + * + * @see http://en.wikipedia.org/wiki/CNPJ + * + * @param bool $formatted If the number should have dots/slashes/dashes or not. + * + * @return string + */ + public function cnpjAlpha($formatted = true) + { + $pool = array_merge(range('A', 'Z'), range('0', '9')); + $n = implode('', array_map(fn () => $pool[array_rand($pool)], range(0, 7))) . '0001'; + $n .= check_digit_alpha($n); + $n .= check_digit_alpha($n); + + return $formatted ? vsprintf('%s%s.%s%s%s.%s%s%s/%s%s%s%s-%s%s', str_split($n)) : $n; + } } diff --git a/src/Provider/pt_BR/check_digit.php b/src/Provider/pt_BR/check_digit.php index 35f9de8c43..75279c5f13 100644 --- a/src/Provider/pt_BR/check_digit.php +++ b/src/Provider/pt_BR/check_digit.php @@ -37,3 +37,29 @@ function check_digit($numbers) return $verifier; } + +/** + * Like check_digit(), but accepts alphanumeric strings (A–Z map to 10–35). + * + * @param string $str + * + * @return int + */ +function check_digit_alpha($str) +{ + $str = strtoupper((string) $str); + $length = strlen($str); + $second_algorithm = $length >= 12; + $verifier = 0; + + for ($i = 1; $i <= $length; ++$i) { + $c = $str[$length - $i]; + $val = ord($c) - 48; + $multiplier = $second_algorithm ? ($i >= 9 ? $i - 7 : $i + 1) : $i + 1; + $verifier += $val * $multiplier; + } + + $verifier = 11 - ($verifier % 11); + + return $verifier >= 10 ? 0 : $verifier; +} diff --git a/test/Provider/pt_BR/CompanyTest.php b/test/Provider/pt_BR/CompanyTest.php index 690353fdc4..3c58c41478 100644 --- a/test/Provider/pt_BR/CompanyTest.php +++ b/test/Provider/pt_BR/CompanyTest.php @@ -7,6 +7,8 @@ use Faker\Provider\pt_BR\Company; use Faker\Test\TestCase; +use function Faker\Provider\pt_BR\check_digit_alpha; + /** * @group legacy */ @@ -20,6 +22,23 @@ public function testCnpjFormatIsValid(): void self::assertMatchesRegularExpression('/\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}/', $cnpj); } + public function testCnpjAlphaFormatIsValid(): void + { + $cnpj = $this->faker->cnpjAlpha(false); + self::assertMatchesRegularExpression('/^[A-Z0-9]{8}\d{4}\d{2}$/', $cnpj); + + $cnpj = $this->faker->cnpjAlpha(true); + self::assertMatchesRegularExpression('/^[A-Z0-9]{2}\.[A-Z0-9]{3}\.[A-Z0-9]{3}\/\d{4}-\d{2}$/', $cnpj); + } + + public function testCnpjAlphaCheckDigitsAreValid(): void + { + $cnpj = $this->faker->cnpjAlpha(false); + + self::assertSame((int) $cnpj[12], check_digit_alpha(substr($cnpj, 0, 12))); + self::assertSame((int) $cnpj[13], check_digit_alpha(substr($cnpj, 0, 13))); + } + protected function getProviders(): iterable { yield new Company($this->faker);