Kiểm tra CPF CNPJ là công việc chạy hai phép kiểm tra chữ số khác nhau cho hai loại số thuế của Brazil: một loại dùng để định danh cá nhân và một loại dùng để định danh tổ chức. Hai loại số này không phải hai cách viết của cùng một thứ, và cũng không dùng chung một thuật toán. Đọc hết bài này, bạn sẽ biết chúng khác nhau ở đâu, vì sao mỗi số có hai chữ số kiểm tra ở cuối, vì sao những dãy toàn chữ số giống nhau luôn bị loại, và vì sao kết quả đạt kiểm tra không đồng nghĩa với một cái tên có thật.
CPF và CNPJ khác nhau ở điểm nào?
Điểm khác nhau căn bản nằm ở đối tượng được định danh. Một loại số gắn với con người cụ thể, loại còn lại gắn với một pháp nhân như công ty hay tổ chức. Vì đối tượng khác nhau, hai loại số cũng được cấp theo hai quy trình khác nhau và có hai thuật toán chữ số kiểm tra khác nhau. Khi một hệ thống dùng sai thuật toán cho sai loại số, kết quả gần như luôn là không đạt, và thông báo lỗi khi đó hoàn toàn vô nghĩa với người dùng.
Điểm chung của cả hai là cấu trúc kiểm tra: mỗi số kết thúc bằng hai chữ số kiểm tra, và hai chữ số này không độc lập với nhau. Chữ số thứ nhất được tính từ phần thân, còn chữ số thứ hai được tính từ phần thân cộng với chữ số thứ nhất vừa tìm ra. Đây là một thiết kế hai tầng khá phổ biến, và nó có hệ quả thực tế: nếu bạn sửa chữ số kiểm tra thứ nhất, chữ số thứ hai cũng phải đổi theo, nên không thể sửa nửa vời.
| Tiêu chí | CPF | CNPJ |
|---|---|---|
| Đối tượng được định danh | Cá nhân | Tổ chức, doanh nghiệp |
| Số chữ số kiểm tra ở cuối | Hai | Hai |
| Cách viết khi in trên giấy | Có dấu phân cách theo nhóm | Có dấu phân cách theo nhóm |
| Dạng dùng khi lưu trữ | Chuỗi chỉ gồm chữ số | Chuỗi chỉ gồm chữ số |
| Có dùng chung thuật toán | Không | Không |
Bảng trên chỉ mô tả cấu trúc. Nó không nói gì về việc số nào đang tồn tại, vì đó là chuyện của cơ quan cấp số, không phải chuyện của một phép tính trên chuỗi ký tự.
Hai chữ số kiểm tra ở cuối mỗi số
Vì hai chữ số kiểm tra được tính liên tiếp, việc kiểm tra cũng phải tiến hành theo hai bước, và cả hai bước phải cùng đạt thì kết luận mới là hợp lệ. Nếu bước thứ nhất không đạt, không có nghĩa bước thứ hai là đúng; nếu bước thứ nhất đạt mà bước thứ hai không đạt, chuỗi đó vẫn là một chuỗi hỏng.
Khi một công cụ chỉ trả về một câu chung chung rằng số này không hợp lệ, người dùng mất khả năng sửa. Cách làm tốt hơn là hiển thị từng chữ số kiểm tra cùng với giá trị mà thuật toán mong đợi, để người dùng thấy ngay chữ số nào lệch. Với dữ liệu được nhập từ biểu mẫu giấy, kinh nghiệm cho thấy lỗi thường rơi vào một trong hai chữ số cuối, vì người nhập thường đọc lướt phần đuôi.
Một sai lầm phổ biến trong các bản triển khai tự viết là tính cả hai chữ số kiểm tra từ cùng một phần thân ban đầu. Kết quả là những số đúng bị báo sai, và những số sai lại lọt qua. Đây là loại lỗi chỉ lộ ra khi chạy trên một bộ dữ liệu thử có cả trường hợp đúng lẫn trường hợp sai, chứ không lộ ra khi thử vài giá trị ngẫu nhiên.
Vì sao dãy toàn chữ số giống nhau luôn bị loại?
Có một nhóm chuỗi mà mọi phép kiểm tra theo kiểu lấy dư đều xử lý đặc biệt: những dãy chỉ gồm một chữ số lặp lại mãi. Với nhiều thuật toán, nhóm này tính ra chữ số kiểm tra trùng với chữ số đang lặp, nên phép kiểm tra sẽ đạt. Điều đó không phải vì những dãy như vậy là số thật, mà vì đây là một điểm mù của toán học được dùng.
Vì biết điểm mù đó, các quy tắc chính thức trong nhiều hệ thống thường loại thẳng những dãy như vậy bằng một điều kiện riêng, đặt trước cả phép tính chữ số kiểm tra. Nếu bạn tự viết hàm kiểm tra mà bỏ qua điều kiện này, hàm của bạn sẽ chấp nhận một nhóm chuỗi mà hệ thống thật từ chối. Đây là ví dụ cho thấy phép kiểm tra chữ số không bao giờ là toàn bộ bộ quy tắc, chỉ là một phần trong đó.
Cách xử lý đúng là coi điều kiện loại trừ này như một tầng riêng, có tên riêng trong mã nguồn, và báo cáo nó như một lý do cụ thể chứ không gộp vào lỗi chữ số kiểm tra. Người dùng nhập một dãy như vậy nên nhận được thông báo rằng dãy này bị loại theo quy tắc, chứ không phải thông báo rằng chữ số kiểm tra sai.
Lỗi gõ mà chữ số kiểm tra bắt được và không bắt được
Hai chữ số kiểm tra bắt được nhiều loại lỗi gõ thường gặp, và bỏ lọt một số loại khác. Chúng nhạy với việc gõ sai một chữ số đơn lẻ ở phần thân, vì mọi thay đổi nhỏ đều làm lệch kết quả phép chia. Chúng cũng thường phát hiện được việc đổi chỗ hai chữ số nằm cạnh nhau, với điều kiện trọng số của hai vị trí đó khác nhau.
Ngược lại, có những biến đổi mà phép kiểm tra không thể phát hiện vì chúng không làm thay đổi kết quả phép chia. Ngoài ra, phép kiểm tra không có khả năng phân biệt một chuỗi được cấp thật với một chuỗi do ai đó dựng mới hoàn toàn. Chỉ cần hiểu quy tắc, một người có thể tạo ra vô số chuỗi đạt kiểm tra mà chưa từng được cấp cho ai.
Vì vậy thông điệp trả về cho người dùng cần rất cẩn thận. Nói rằng một số vượt qua phép kiểm tra là một câu đúng. Nói rằng số đó là số hợp lệ của một người hay một công ty cụ thể là một câu sai, và là loại câu có thể gây hậu quả thật. Nếu bạn muốn xem cách trình bày tách bạch giữa các tầng kiểm tra, công cụ kiểm tra số của trang này hiển thị từng tầng và từng lý do riêng biệt.
Nhập liệu và chuẩn hóa dấu phân cách
Trên giấy tờ, cả hai loại số thường được in kèm dấu chấm hoặc dấu gạch chéo để chia thành nhóm cho dễ đọc. Trong cơ sở dữ liệu, chúng thường được lưu ở dạng chỉ gồm chữ số. Vì vậy bước chuẩn hóa là bắt buộc, và phải chạy trước mọi phép kiểm tra khác.
Vài điểm cần lưu ý khi triển khai bước này:
- Bỏ hết dấu cách, dấu chấm, dấu gạch nối và dấu gạch chéo trước khi đếm độ dài, nếu không bạn sẽ từ chối oan những giá trị hợp lệ chỉ vì cách trình bày.
- Giữ nguyên giá trị gốc bên cạnh giá trị đã chuẩn hóa. Khi cần đối chiếu với giấy tờ, bạn cần thấy đúng chuỗi người dùng đã nhập, không chỉ chuỗi đã sửa.
- Không tự ý thêm chữ số vào đầu chuỗi để đủ độ dài. Việc đệm số không đúng sẽ biến một giá trị sai thành một giá trị sai khác trông có vẻ đúng.
- Ghi nhận rõ chuỗi rỗng và chuỗi chỉ gồm dấu phân cách là hai trường hợp khác nhau, vì thông báo lỗi phù hợp cho mỗi trường hợp không giống nhau.
- Chuẩn hóa giống nhau ở mọi điểm nhập liệu, kể cả khi dữ liệu đến từ tệp tải lên chứ không phải từ bàn phím.
Chuẩn hóa không sửa được lỗi gõ thật, và cũng không phải một biện pháp an toàn. Nó chỉ bảo đảm cùng một số được viết theo hai cách khác nhau sẽ cho ra cùng một kết quả kiểm tra.
Phần dành cho nhà phát triển: hai tầng cho hai loại số
Vì hai loại số dùng hai thuật toán khác nhau, cách triển khai nên tách phần dùng chung khỏi phần riêng. Phần dùng chung gồm chuẩn hóa, kiểm tra tập ký tự và độ dài, cùng việc loại những dãy toàn chữ số giống nhau. Phần riêng là hai hàm tính chữ số kiểm tra, mỗi hàm phục vụ một loại số.
- Đừng để một hàm duy nhất nhận một tham số kiểu loại số rồi rẽ nhánh ở giữa. Hai thuật toán có bảng trọng số khác nhau, và việc trộn chúng vào một hàm là nguồn gốc của những lỗi rất khó tìm.
- Trả về cấu trúc kết quả giống nhau cho cả hai loại, gồm trạng thái, chữ số kiểm tra mong đợi và vị trí sai. Giao diện sẽ thống nhất và việc kiểm thử cũng dễ hơn.
- Viết bộ dữ liệu thử có cả trường hợp đúng, trường hợp sai một chữ số, trường hợp đổi chỗ hai chữ số và trường hợp toàn chữ số giống nhau. Bốn nhóm này phủ gần hết các lỗi triển khai thường gặp.
- Không bao giờ đưa số thật của khách hàng vào bộ dữ liệu thử. Hãy dùng giá trị được dựng riêng, và ghi rõ trong tệp dữ liệu là giá trị giả.
- Nếu hệ thống cần tra cứu tình trạng đăng ký, hãy đặt việc đó ở một tầng hoàn toàn khác. Việc tính chữ số kiểm tra không bao giờ được phụ thuộc vào một lời gọi mạng.
Cách chia này giúp bạn thay đổi hoặc bổ sung một loại số mới mà không phải chạm vào phần đã chạy ổn định.
Điều nên làm tiếp theo
Hãy thử lấy hai chuỗi thử, một cho mỗi loại số, rồi cố tình hoán đổi thuật toán giữa chúng để xem hệ thống của bạn báo lỗi theo cách nào. Nếu kết quả trả về không phân biệt được hai tình huống này, bạn đang thiếu một tầng trong thiết kế. Để hiểu nhóm thuật toán đứng sau hai phép tính này, bài thuật toán chữ số kiểm tra: mod-10, mod-11 và mod-97 so sánh các nhánh với nhau; còn bài kiểm tra số giấy tờ: quy tắc khác nhau theo nước mở rộng vấn đề ra ngoài phạm vi một quốc gia. Nếu bạn đang làm việc với dữ liệu liên hệ quốc tế, bài kiểm thử địa chỉ và số điện thoại quốc tế có thêm ví dụ về cùng kiểu sai sót dữ liệu đầu vào.
Mọi giá trị số trong bài chỉ là ví dụ được dựng riêng cho mục đích giải thích cách kiểm tra; chúng không phải số thuế của bất kỳ cá nhân hay doanh nghiệp nào có thật, không thuộc bất kỳ cơ quan nào, và không được dùng để mạo danh hay để khẳng định một con số là dùng được.