-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpf-tools.el
More file actions
213 lines (191 loc) · 8.72 KB
/
Copy pathcpf-tools.el
File metadata and controls
213 lines (191 loc) · 8.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
;;; cpf-tools.el --- Tools to generate and format CPF and CNPJ numbers -*- lexical-binding: t; -*-
;; Copyright (C) 2023
;; Author: JJ
;; URL: https://github.com/jjnilton/cpf-tools
;; Version: 0.1.1
;; Keywords: convenience
;; Package-Requires: ((emacs "27.1"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program 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 General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This package generates and formats Brazilian identification numbers known as CPF and CNPJ.
;; CPF (Cadastro de Pessoas Físicas) is a personal identification number used by individuals,
;; while CNPJ (Cadastro Nacional da Pessoa Jurídica) is a registration number used by legal entities.
;; The package includes the following functions:
;; - generate-cpf: Generates a valid random CPF number.
;; - generate-cnpj: Generates a valid random CNPJ number.
;; - format-cpf-region: Formats a region of text in a buffer as CPF numbers.
;; - format-cnpj-region: Formats a region of text in a buffer as CNPJ numbers.
;; These functions can be useful for applications that need to generate test data or validate user input.
;; The package also includes functions for checking the validity of CPF and CNPJ numbers,
;; based on the official validation algorithms used by the Brazilian government.
;; These functions can be used to ensure that user input is valid and to prevent errors in data processing.
;; To use this package, simply require the `cpf-cnpj` feature:
;;
;; (require 'cpf-cnpj)
;;
;; Then you can call the functions provided by the package. For example:
;;
;; (cpf-tools/generate-cpf) ; => "12345678910"
;; (cpf-tools/generate-cnpj) ; => "12345678000190"
;;
;; Note: these functions accepts prefixes:
;;
;; nil ; outputs the unformatted string to the *Messages* buffer
;; C-u ; inserts the unformatted generated CPF/CNPJ string at point
;; C-u C-u ; outputs the generated and formatted CPF/CNPJ to the *Messages* buffer
;; C-u C-u C-u ; inserts and formats the generated CPF/CNPJ string at point
;;
;; (cpf-tools/format-cpf-region (point-min) (point-max)); => "123.456.789-10"
;; (cpf-tools/format-cnpj-region (point-min) (point-max)) => => "12.345.678/0001-90"
;; (cpf-tools/format-cpf-cnpj-region (point-min) (point-max)) => "123.456.789-10" or "12.345.678/0001-90"
;;; Code:
;;;###autoload
(defun cpf-tools/generate-cpf (&optional arg)
"Generate a CPF number. C-u to insert, C-u C-u to format, C-u C-u C-u to insert formatted."
(interactive "P")
(let* ((cpf (mapcar (lambda (digit) (random 10)) (number-sequence 1 9))))
(dotimes (i 2)
(let (
(verifier-digit nil)
(multipliers (number-sequence (1+ (length cpf)) 2 -1)))
(dotimes (i (length cpf))
(let (
(digit (nth i cpf))
(multipliers (nth i multipliers)))
(push (* digit multipliers) verifier-digit)))
(let* (
(remainder (% (apply '+ verifier-digit) 11))
(verifier-digit (if (< remainder 2) 0 (- 11 remainder))))
(nconc cpf (list verifier-digit)))))
(cond ((equal arg nil)
(message (mapconcat 'number-to-string cpf "")))
((equal arg '(4))
(insert (mapconcat 'number-to-string cpf "")))
((equal arg '(16))
(message (format-cpf (mapconcat 'number-to-string cpf ""))))
((equal arg '(64))
(insert (format-cpf (mapconcat 'number-to-string cpf "")))))))
(defun validate-cpf (cpf-string)
"Take a CPF (11-digit string) and checks if is valid."
(let ((cpf (mapcar #'string-to-number
(split-string-and-unquote
(replace-regexp-in-string "[^0-9]" "" cpf-string) ""))))
(if (= (length cpf) 11)
(let ((cpf-without-digits (seq-subseq cpf 0 -2))
(verifiers-digits (seq-subseq cpf -2)))
(catch 'return
(dotimes (i 2)
(let ((verifier-digit (find-verifier-digit-cpf cpf-without-digits)))
(if (= verifier-digit (nth i verifiers-digits))
(progn
(nconc cpf-without-digits (list verifier-digit)))
(progn
(throw 'return nil)))))
t))
nil)))
(defun find-verifier-digit-cpf (cpf-without-digits-as-list)
"Receive a CPF number without the verification digits and returns the verification digit."
(let* ((cpf cpf-without-digits-as-list)
(verifier-digit nil)
(multipliers (number-sequence (1+ (length cpf)) 2 -1)))
(dotimes (i (length cpf))
(let (
(current-digit (nth i cpf))
(current-multiplier (nth i multipliers)))
(push (* current-digit current-multiplier) verifier-digit)))
(let* ((remainder (% (apply '+ verifier-digit) 11))
(verifier-digit (if (< remainder 2) 0 (- 11 remainder))))
verifier-digit)))
(defun format-cpf (cpf-string)
"Format string as CPF."
(if (not (= (length (replace-regexp-in-string "[^0-9]" "" cpf-string)) 11))
(user-error "Invalid CPF length."))
(replace-regexp-in-string
"^\\([0-9]\\{3\\}\\)\\([0-9]\\{3\\}\\)\\([0-9]\\{3\\}\\)\\([0-9]\\{2\\}\\)$"
"\\1.\\2.\\3-\\4"
cpf-string))
(defun cpf-tools/format-cpf-region (beg end)
"Format marked region as CPF."
(interactive "r")
(let ((cpf (format-cpf (buffer-substring beg end))))
(delete-region beg end)
(insert cpf)))
(defun find-next-verifier-digit-cnpj (cnpj-string)
(let ((cnpj (mapcar #'string-to-number (split-string-and-unquote cnpj-string "")))
(verifiers (if (= (length cnpj-string) 12) '(5 4 3 2 9 8 7 6 5 4 3 2) '(6 5 4 3 2 9 8 7 6 5 4 3 2))))
(let ((verifier-digit nil))
(dotimes (i (length cnpj))
(push (* (nth i cnpj) (nth i verifiers)) verifier-digit))
(let ((verifier-digit (% (apply '+ verifier-digit) 11)))
(if (> 2 verifier-digit)
(number-to-string 0)
(number-to-string (- 11 verifier-digit)))))))
(defun validate-cnpj (cnpj-string)
"Receive a CNPJ string, checks if CNPJ number is valid."
(let ((cleaned-cnpj (replace-regexp-in-string "[^0-9]" "" cnpj-string)))
(if (/= (length cleaned-cnpj) 14)
nil
(let ((cnpj cnpj-string)
(cnpj-without-digits (substring cnpj 0 -2))
(verifiers-digits (substring cnpj -2)))
(catch 'return
(dotimes (i 2)
(if (=
(find-next-verifier-digit-cnpj cnpj-without-digits)
(string-to-number (substring verifiers-digits i (1+ i))))
(setf cnpj-without-digits
(concat cnpj-without-digits (substring verifiers-digits i (1+ i))))
(throw 'return nil)))
t)))))
;;;###autoload
(defun cpf-tools/generate-cnpj (&optional arg)
"Generate a valid CNPJ number."
(interactive "P")
(let ((cnpj (mapconcat 'number-to-string
(append (mapcar (lambda (digit) (random 10)) (number-sequence 1 8)) '(0 0 0 1)) "")))
(dotimes (i 2)
(setf cnpj (concat cnpj (find-next-verifier-digit-cnpj cnpj))))
(cond ((equal arg nil)
(message cnpj))
((equal arg '(4))
(insert cnpj))
((equal arg '(16))
(message (format-cnpj cnpj)))
((equal arg '(64))
(insert (format-cnpj cnpj))))))
(defun format-cnpj (cnpj-string)
"Formats string as CNPJ."
(if (not (= (length (replace-regexp-in-string "[^0-9]" "" cnpj-string)) 14))
(user-error "Invalid CNPJ length.")
(replace-regexp-in-string
"^\\([0-9]\\{2\\}\\)\\([0-9]\\{3\\}\\)\\([0-9]\\{3\\}\\)\\([0-9]\\{4\\}\\)\\([0-9]\\{2\\}\\)$"
"\\1.\\2.\\3/\\4-\\5"
cnpj-string)))
;;;###autoload
(defun cpf-tools/format-cnpj-region (beg end)
"Format marked region as CNPJ."
(interactive "r")
(let ((cnpj (format-cnpj (buffer-substring beg end))))
(delete-region beg end)
(insert cnpj)))
;;;###autoload
(defun cpf-tools/format-cpf-cnpj-region (beg end)
"Format marked region as CPF or CNPJ based on the length."
(interactive "r")
(let ((cpf-or-cnpj (buffer-substring beg end)))
(cond ((= (length cpf-or-cnpj) 11) (cpf-tools/format-cpf-region beg end))
((= (length cpf-or-cnpj) 14) (cpf-tools/format-cnpj-region beg end))
(t (message "Invalid input length.")))
)
)
(provide 'cpf-tools)
;;; cpf-tools.el ends here