From 85215202f5b84e919cb1cea49ebf07509075b5fd Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Sun, 16 Mar 2025 19:34:38 -0300 Subject: [PATCH 01/29] feat: added birth certificate generator --- .../birth_certificate/brazil/generator.go | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 pkg/mocai/entities/birth_certificate/brazil/generator.go diff --git a/pkg/mocai/entities/birth_certificate/brazil/generator.go b/pkg/mocai/entities/birth_certificate/brazil/generator.go new file mode 100644 index 0000000..f27f4ea --- /dev/null +++ b/pkg/mocai/entities/birth_certificate/brazil/generator.go @@ -0,0 +1,144 @@ +package birth_certificate + +import ( + "fmt" + "math/rand" + "strconv" + "time" +) + +// BrazilianBirthCertificate represents the structure of a Brazilian birth certificate +// containing all its components and the final certificate number +type BrazilianBirthCertificate struct { + VitalRecordsOffice int + ArchiveCode int + ServieType int + BirthYear int + CertificateType int + BookNumber int + PageNumber int + TermNumber int + CheckDigits int + BirthCertificateNumber string +} + +// GenerateBirthCertificate generates a valid Brazilian birth certificate number +// If formatted is true, returns the number with separators (-) +// Returns a pointer to BrazilianBirthCertificate and error if any validation fails +func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error) { + + // Create a new random source + r := rand.New(rand.NewSource(time.Now().UnixNano())) + + // Registry Office + // 1. Vital Records Office [6 digits] + vitalRecordsOffice := r.Intn(999999) + 1 + if vitalRecordsOffice < 0 { + return nil, ErrInvalidVitalRecordsOffice + } + + // 2. Archive [2 digits] + archiveCode := 1 + + // 3. Civil Registry of Natural Persons [2 digits] + servieType := 55 + + // 4. Birth Year [4 digits] + currentYear := time.Now().Year() + birthYear := r.Intn(currentYear-2010+1) + 2010 + + // 5. Certificate type [1 digit] + certificateType := 1 + + // 6. Book number [5 digits] + bookNumber := r.Intn(99999) + 1 + if bookNumber < 0 { + return nil, ErrInvalidBookNumber + } + + // 7. Page number [3 digits] + pageNumber := r.Intn(999) + 1 + if pageNumber < 0 { + return nil, ErrInvalidPageNumber + } + + // 8. Term number [7 digits] + termNumber := r.Intn(9999999) + 1 + if termNumber < 0 { + return nil, ErrInvalidTermNumber + } + + // Number without check digits [30 digits] + numberWithoutCheckDigits := fmt.Sprintf("%06d%02d%02d%04d%d%05d%03d%07d", + vitalRecordsOffice, archiveCode, servieType, birthYear, certificateType, bookNumber, pageNumber, termNumber) + if len(numberWithoutCheckDigits) != 30 { + return nil, ErrInvalidNumberWithoutCheckDigits + } + + // 9. Check digits calculation [2 digits] + checkDigits := calculateCheckDigits(numberWithoutCheckDigits) + + birthCertificateNumber := fmt.Sprintf("%s%02d", numberWithoutCheckDigits, checkDigits) + if formatted { + birthCertificateNumber = fmt.Sprintf("%06d %02d %02d %04d %d %05d %03d %07d-%02d", + vitalRecordsOffice, archiveCode, servieType, birthYear, certificateType, bookNumber, pageNumber, termNumber, checkDigits) + } + + brazilianCertificate := &BrazilianBirthCertificate{ + VitalRecordsOffice: vitalRecordsOffice, + ArchiveCode: archiveCode, + ServieType: servieType, + BirthYear: birthYear, + CertificateType: certificateType, + BookNumber: bookNumber, + PageNumber: pageNumber, + TermNumber: termNumber, + CheckDigits: checkDigits, + BirthCertificateNumber: birthCertificateNumber, + } + + return brazilianCertificate, nil +} + +// calculateCheckDigits calculates the check digits for the birth certificate number +// using a weight-based algorithm +func calculateCheckDigits(number string) int { + sum := 0 + weight := 2 + + for i := len(number) - 1; i >= 0; i-- { + digit, _ := strconv.Atoi(string(number[i])) + sum += digit * weight + weight++ + if weight > 9 { + weight = 2 + } + } + + remainder := sum % 11 + checkDigit1 := 11 - remainder + if checkDigit1 >= 10 { + checkDigit1 = 1 + } + + numberWithFirstCheckDigit := number + strconv.Itoa(checkDigit1) + sum = 0 + weight = 3 + + for i := len(numberWithFirstCheckDigit) - 1; i >= 0; i-- { + digit, _ := strconv.Atoi(string(numberWithFirstCheckDigit[i])) + sum += digit * weight + weight++ + if weight > 9 { + weight = 2 + } + } + + remainder = sum % 11 + checkDigit2 := 11 - remainder + if checkDigit2 >= 10 { + checkDigit2 = 1 + } + + return checkDigit1*10 + checkDigit2 +} From 689fc08983d9098e5091a810b54c20cc44b69a83 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Sun, 16 Mar 2025 19:35:09 -0300 Subject: [PATCH 02/29] feat: added birth certificate errors --- .../birth_certificate/brazil/errors.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 pkg/mocai/entities/birth_certificate/brazil/errors.go diff --git a/pkg/mocai/entities/birth_certificate/brazil/errors.go b/pkg/mocai/entities/birth_certificate/brazil/errors.go new file mode 100644 index 0000000..0474689 --- /dev/null +++ b/pkg/mocai/entities/birth_certificate/brazil/errors.go @@ -0,0 +1,18 @@ +package birth_certificate + +import "errors" + +// Package birth_certificate defines common errors used during gender generation and validation. +// These errors represent specific failure scenarios and should be wrapped with additional +// context when returned. +var ( + ErrInvalidBirthCertificate = errors.New("invalid birth certificate") + ErrInvalidVitalRecordsOffice = errors.New("invalid vital records office number") + ErrInvalidArchive = errors.New("invalid archive number") + ErrInvalidVitalRecordsService = errors.New("invalid vital records service number") + ErrInvalidBirthYear = errors.New("invalid birth year") + ErrInvalidBookNumber = errors.New("invalid book number") + ErrInvalidPageNumber = errors.New("invalid page number") + ErrInvalidTermNumber = errors.New("invalid term number") + ErrInvalidNumberWithoutCheckDigits = errors.New("invalid number without check digits") +) From 4cd19e1f5512ee94b56c112f3f08c8093195be90 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Sun, 16 Mar 2025 19:35:47 -0300 Subject: [PATCH 03/29] feat: added birth certificate validator --- .../birth_certificate/brazil/validator.go | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 pkg/mocai/entities/birth_certificate/brazil/validator.go diff --git a/pkg/mocai/entities/birth_certificate/brazil/validator.go b/pkg/mocai/entities/birth_certificate/brazil/validator.go new file mode 100644 index 0000000..335422d --- /dev/null +++ b/pkg/mocai/entities/birth_certificate/brazil/validator.go @@ -0,0 +1,35 @@ +package birth_certificate + +import ( + "regexp" + "strconv" + "strings" +) + +// ValidateBirthCertificate checks if a Brazilian birth certificate number is valid +// It validates the format, length, and check digits of the certificate number +func ValidateBirthCertificate(certificate string) bool { + // Remove any formatting + certificate = strings.ReplaceAll(certificate, "-", "") + + // Check if the certificate number has exactly 32 digits + if len(certificate) != 32 { + return false + } + + // Verify that the certificate contains only numeric characters + match, _ := regexp.MatchString("^[0-9]+$", certificate) + if !match { + return false + } + + // Extract the number without check digits + number := certificate[:30] + informedCheckDigit, _ := strconv.Atoi(certificate[30:]) + + // Calculate the check digits using the validation algorithm + calculatedCheckDigit := calculateCheckDigits(number) + + // Compare the informed check digits with the calculated ones + return informedCheckDigit == calculatedCheckDigit +} From 90b84be86fd72c43534d8ffe40e572ffd2f65bd6 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Sun, 16 Mar 2025 19:37:02 -0300 Subject: [PATCH 04/29] feat: added brazilian birth certificate example --- examples/mock_example.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/mock_example.go b/examples/mock_example.go index 96d5ecd..8ce7c2a 100644 --- a/examples/mock_example.go +++ b/examples/mock_example.go @@ -5,6 +5,7 @@ import ( "github.com/brazzcore/mocai/pkg/mocai/constants" "github.com/brazzcore/mocai/pkg/mocai/entities/address" + brazilian_birth_certificate "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/brazil" company "github.com/brazzcore/mocai/pkg/mocai/entities/company/brazil" "github.com/brazzcore/mocai/pkg/mocai/entities/person" "github.com/brazzcore/mocai/pkg/mocai/entities/phone" @@ -33,7 +34,13 @@ func GenerateMockExample() { company_mock, err := company.GenerateCompany() if err != nil { - fmt.Println("Error generating company:", err) + fmt.Print(err) + return + } + + birth_certificate_mock, err := brazilian_birth_certificate.GenerateBirthCertificate(false) + if err != nil { + fmt.Print(err) return } @@ -43,6 +50,8 @@ func GenerateMockExample() { fmt.Printf("Person: %s %s, %s, %d years old, CPF: %s\n", person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age, person_mock.CPF) + fmt.Printf("Birth Certificate: %s\n", birth_certificate_mock.BirthCertificateNumber) + fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.CompanyName, company_mock.CNPJ) fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n", From 944ff89021eaf554ca188292d344d1f495575629 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Sun, 16 Mar 2025 19:39:32 -0300 Subject: [PATCH 05/29] refactor: changed import reference name --- examples/mock_example.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mock_example.go b/examples/mock_example.go index 8ce7c2a..5d28387 100644 --- a/examples/mock_example.go +++ b/examples/mock_example.go @@ -6,7 +6,7 @@ import ( "github.com/brazzcore/mocai/pkg/mocai/constants" "github.com/brazzcore/mocai/pkg/mocai/entities/address" brazilian_birth_certificate "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/brazil" - company "github.com/brazzcore/mocai/pkg/mocai/entities/company/brazil" + brazilian_company "github.com/brazzcore/mocai/pkg/mocai/entities/company/brazil" "github.com/brazzcore/mocai/pkg/mocai/entities/person" "github.com/brazzcore/mocai/pkg/mocai/entities/phone" "github.com/brazzcore/mocai/pkg/mocai/translations" @@ -32,7 +32,7 @@ func GenerateMockExample() { fmt.Print(err) } - company_mock, err := company.GenerateCompany() + company_mock, err := brazilian_company.GenerateCompany() if err != nil { fmt.Print(err) return From f07cbf8cf6ac5dd952cbfbc64f6af69cc421ee70 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Mon, 17 Mar 2025 16:43:26 -0300 Subject: [PATCH 06/29] fix: fixed function to generate valid check digit --- .../birth_certificate/brazil/generator.go | 94 ++++++++++--------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/pkg/mocai/entities/birth_certificate/brazil/generator.go b/pkg/mocai/entities/birth_certificate/brazil/generator.go index f27f4ea..d3c42a1 100644 --- a/pkg/mocai/entities/birth_certificate/brazil/generator.go +++ b/pkg/mocai/entities/birth_certificate/brazil/generator.go @@ -3,7 +3,6 @@ package birth_certificate import ( "fmt" "math/rand" - "strconv" "time" ) @@ -12,13 +11,13 @@ import ( type BrazilianBirthCertificate struct { VitalRecordsOffice int ArchiveCode int - ServieType int + ServiceType int BirthYear int CertificateType int BookNumber int PageNumber int TermNumber int - CheckDigits int + CheckDigits string BirthCertificateNumber string } @@ -32,7 +31,7 @@ func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error // Registry Office // 1. Vital Records Office [6 digits] - vitalRecordsOffice := r.Intn(999999) + 1 + vitalRecordsOffice := r.Intn(899999) + 100000 if vitalRecordsOffice < 0 { return nil, ErrInvalidVitalRecordsOffice } @@ -41,7 +40,7 @@ func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error archiveCode := 1 // 3. Civil Registry of Natural Persons [2 digits] - servieType := 55 + serviceType := 55 // 4. Birth Year [4 digits] currentYear := time.Now().Year() @@ -51,43 +50,45 @@ func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error certificateType := 1 // 6. Book number [5 digits] - bookNumber := r.Intn(99999) + 1 + bookNumber := r.Intn(89999) + 10000 if bookNumber < 0 { return nil, ErrInvalidBookNumber } // 7. Page number [3 digits] - pageNumber := r.Intn(999) + 1 + pageNumber := r.Intn(899) + 100 if pageNumber < 0 { return nil, ErrInvalidPageNumber } // 8. Term number [7 digits] - termNumber := r.Intn(9999999) + 1 + termNumber := r.Intn(8999999) + 1000000 if termNumber < 0 { return nil, ErrInvalidTermNumber } // Number without check digits [30 digits] numberWithoutCheckDigits := fmt.Sprintf("%06d%02d%02d%04d%d%05d%03d%07d", - vitalRecordsOffice, archiveCode, servieType, birthYear, certificateType, bookNumber, pageNumber, termNumber) + vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber) if len(numberWithoutCheckDigits) != 30 { + print(numberWithoutCheckDigits) return nil, ErrInvalidNumberWithoutCheckDigits } // 9. Check digits calculation [2 digits] checkDigits := calculateCheckDigits(numberWithoutCheckDigits) + print("checkDigits: ", checkDigits) - birthCertificateNumber := fmt.Sprintf("%s%02d", numberWithoutCheckDigits, checkDigits) + birthCertificateNumber := fmt.Sprintf("%s%02s", numberWithoutCheckDigits, checkDigits) if formatted { - birthCertificateNumber = fmt.Sprintf("%06d %02d %02d %04d %d %05d %03d %07d-%02d", - vitalRecordsOffice, archiveCode, servieType, birthYear, certificateType, bookNumber, pageNumber, termNumber, checkDigits) + birthCertificateNumber = fmt.Sprintf("%06d %02d %02d %04d %d %05d %03d %07d-%02s", + vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber, checkDigits) } brazilianCertificate := &BrazilianBirthCertificate{ VitalRecordsOffice: vitalRecordsOffice, ArchiveCode: archiveCode, - ServieType: servieType, + ServiceType: serviceType, BirthYear: birthYear, CertificateType: certificateType, BookNumber: bookNumber, @@ -101,44 +102,47 @@ func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error } // calculateCheckDigits calculates the check digits for the birth certificate number -// using a weight-based algorithm -func calculateCheckDigits(number string) int { - sum := 0 - weight := 2 - - for i := len(number) - 1; i >= 0; i-- { - digit, _ := strconv.Atoi(string(number[i])) - sum += digit * weight - weight++ - if weight > 9 { - weight = 2 - } +// using a weight-based algorithm / using Mod 11 +func calculateCheckDigits(number string) string { + dv1Weights := [30]int{ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, } - remainder := sum % 11 - checkDigit1 := 11 - remainder - if checkDigit1 >= 10 { - checkDigit1 = 1 + sumDV1 := 0 + for i := 0; i < 30; i++ { + // reverse access: b1 = last digit [position 29], b30 = first [position 0] + digit := int(number[29-i] - '0') + sumDV1 += digit * dv1Weights[i] } - numberWithFirstCheckDigit := number + strconv.Itoa(checkDigit1) - sum = 0 - weight = 3 - - for i := len(numberWithFirstCheckDigit) - 1; i >= 0; i-- { - digit, _ := strconv.Atoi(string(numberWithFirstCheckDigit[i])) - sum += digit * weight - weight++ - if weight > 9 { - weight = 2 - } + dv1 := sumDV1 % 11 + if dv1 == 10 { + dv1 = 1 } - remainder = sum % 11 - checkDigit2 := 11 - remainder - if checkDigit2 >= 10 { - checkDigit2 = 1 + dv2Weights := [31]int{ + 9, // first term: dv1 * 9 + 8, 7, 6, 5, 4, 3, 2, 1, 0, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, } - return checkDigit1*10 + checkDigit2 + sumDV2 := 0 + // first term: dv1 * 9 + sumDV2 += dv1 * dv2Weights[0] + + // remaining terms: b1 to b30 with remaining weights + for i := 0; i < 30; i++ { + digit := int(number[29-i] - '0') + sumDV2 += digit * dv2Weights[i+1] + } + + dv2 := sumDV2 % 11 + if dv2 == 10 { + dv2 = 1 + } + + return fmt.Sprintf("%d%d", dv1, dv2) } From 7c7d555b8743b615cb638bf2afaedac19a1f932c Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Mon, 17 Mar 2025 16:44:49 -0300 Subject: [PATCH 07/29] fix: fixed birth certificate validation func --- pkg/mocai/entities/birth_certificate/brazil/validator.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/mocai/entities/birth_certificate/brazil/validator.go b/pkg/mocai/entities/birth_certificate/brazil/validator.go index 335422d..019c5a8 100644 --- a/pkg/mocai/entities/birth_certificate/brazil/validator.go +++ b/pkg/mocai/entities/birth_certificate/brazil/validator.go @@ -2,7 +2,6 @@ package birth_certificate import ( "regexp" - "strconv" "strings" ) @@ -11,6 +10,7 @@ import ( func ValidateBirthCertificate(certificate string) bool { // Remove any formatting certificate = strings.ReplaceAll(certificate, "-", "") + certificate = strings.ReplaceAll(certificate, " ", "") // Check if the certificate number has exactly 32 digits if len(certificate) != 32 { @@ -25,9 +25,11 @@ func ValidateBirthCertificate(certificate string) bool { // Extract the number without check digits number := certificate[:30] - informedCheckDigit, _ := strconv.Atoi(certificate[30:]) - // Calculate the check digits using the validation algorithm + // Get the informed check digits (last 2 digits) + informedCheckDigit := certificate[30:] + + // Calculate the expected check digits calculatedCheckDigit := calculateCheckDigits(number) // Compare the informed check digits with the calculated ones From 39a47b87e7aa81689df1d0bedd09d278704ab022 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Mon, 17 Mar 2025 16:59:09 -0300 Subject: [PATCH 08/29] fix: fixed comment on birth certificate errors --- pkg/mocai/entities/birth_certificate/brazil/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mocai/entities/birth_certificate/brazil/errors.go b/pkg/mocai/entities/birth_certificate/brazil/errors.go index 0474689..f22a1e8 100644 --- a/pkg/mocai/entities/birth_certificate/brazil/errors.go +++ b/pkg/mocai/entities/birth_certificate/brazil/errors.go @@ -2,7 +2,7 @@ package birth_certificate import "errors" -// Package birth_certificate defines common errors used during gender generation and validation. +// Package birth_certificate defines common errors used during birth certificate generation and validation. // These errors represent specific failure scenarios and should be wrapped with additional // context when returned. var ( From aea3e6ee8bdf60a98ded36b9ebe6506f53307b5f Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Tue, 18 Mar 2025 16:52:10 -0300 Subject: [PATCH 09/29] docs: updated main readme --- README.md | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0026a77..ba0f451 100644 --- a/README.md +++ b/README.md @@ -38,20 +38,21 @@ Basic Usage Import the library into your project and start generating mocks: ``` -package main +package examples import ( "fmt" - "github.com/brazzcore/mocai/pkg/mocai/constants" "github.com/brazzcore/mocai/pkg/mocai/entities/address" + brazilian_birth_certificate "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/brazil" + brazilian_company "github.com/brazzcore/mocai/pkg/mocai/entities/company/brazil" "github.com/brazzcore/mocai/pkg/mocai/entities/person" "github.com/brazzcore/mocai/pkg/mocai/entities/phone" "github.com/brazzcore/mocai/pkg/mocai/translations" ) -func main() { - // Set the language to pt-BR +func GenerateMockExample() { + // Set the language to pt-BR translations.SetLanguage("ptbr") // Generate mock data @@ -70,12 +71,27 @@ func main() { fmt.Print(err) } - fmt.Printf("Person: %s %s, %s, %d years old\n", - person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age) + company_mock, err := brazilian_company.GenerateCompany() + if err != nil { + fmt.Print(err) + return + } + + birth_certificate_mock, err := brazilian_birth_certificate.GenerateBirthCertificate(false) + if err != nil { + fmt.Print(err) + return + } + + fmt.Printf("Person: %s %s, %s, %d years old, CPF: %s\n", + person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age, person_mock.CPF) + + fmt.Printf("Birth Certificate: %s\n", birth_certificate_mock.BirthCertificateNumber) + + fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.CompanyName, company_mock.CNPJ) fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n", address_mock.Street, address_mock.Number, address_mock.City, address_mock.State, address_mock.UF, address_mock.ZIP) - fmt.Printf("Phone: (%s) %s\n", phone_mock.AreaCode, phone_mock.Number) } ``` From c6b68093f5e38ae8ae5cf0612926bcefe590f34d Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Tue, 18 Mar 2025 16:54:32 -0300 Subject: [PATCH 10/29] docs: updated main readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba0f451..d1de382 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ func GenerateMockExample() { ``` ### Examples -The ***examples*** folder contains samples of how to use the library. To run the examples, navigate to the folder and execute: +The ***examples*** folder contains samples of how to use the library. To run the examples, navigate to the root folder and execute: ``` go run main.go From 71163aa0b23185b0c889fdc50b5263340c3effec Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Tue, 18 Mar 2025 16:56:45 -0300 Subject: [PATCH 11/29] docs: updated readme in portuguese --- docs/localization/pt/README-PT.md | 81 +++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/docs/localization/pt/README-PT.md b/docs/localization/pt/README-PT.md index 6d55d5d..a7e07c8 100644 --- a/docs/localization/pt/README-PT.md +++ b/docs/localization/pt/README-PT.md @@ -11,17 +11,17 @@ Uma biblioteca Go para geração de dados de teste, permitindo criar mocks de en O nome Mocai é uma homenagem à iniciativa brasileira por trás da biblioteca. Ele surgiu da combinação de "mock" (termo em inglês para simulação ou dados fictícios) com "açaí", uma fruta típica da Amazônia brasileira, conhecida por sua energia e versatilidade. Assim como o açaí é essencial para muitos brasileiros, o Mocai busca ser uma ferramenta essencial para desenvolvedores que precisam de dados de teste eficientes e de qualidade. 🇧🇷 ## 🛠️ Principais Recursos -- Geração de Dados Aleatórios: Crie mocks de entidades com dados variados e realistas. -- Consistência: Garanta que os dados gerados sejam consistentes e adequados para testes. -- Facilidade de Uso: Interface simples e intuitiva para integração rápida em seus projetos. -- Extensibilidade: Adicione novas entidades ou personalize as existentes conforme suas necessidades. -- Open Source: Colabore, sugira melhorias e contribua para o crescimento da biblioteca. +- **Geração de Dados Aleatórios:** Crie mocks de entidades com dados variados e realistas. +- **Consistência:** Garanta que os dados gerados sejam consistentes e adequados para testes. +- **Facilidade de Uso:** Interface simples e intuitiva para integração rápida em seus projetos. +- **Extensibilidade:** Adicione novas entidades ou personalize as existentes conforme suas necessidades. +- **Open Source:** Colabore, sugira melhorias e contribua para o crescimento da biblioteca. ## 🚀 Por que usar o Mocai? -- Produtividade: Reduza o tempo gasto na criação de dados de teste. -- Qualidade: Melhore a cobertura e a eficácia dos seus testes com dados realistas. -- Flexibilidade: Adapte os mocks às necessidades específicas do seu projeto. -- Comunidade: Faça parte de uma comunidade open-source que valoriza a colaboração e a inovação. +- **Produtividade:** Reduza o tempo gasto na criação de dados de teste. +- **Qualidade:** Melhore a cobertura e a eficácia dos seus testes com dados realistas. +- **Flexibilidade:** Adapte os mocks às necessidades específicas do seu projeto. +- **Comunidade:** Faça parte de uma comunidade open-source que valoriza a colaboração e a inovação. ## 🚀 Como Começar @@ -36,29 +36,68 @@ Uso Básico Importe a biblioteca em seu projeto e comece a gerar mocks: ``` -package main +package examples import ( - "fmt" - "github.com/brazzcore/mocai/pkg/mocai" - + "fmt" + + "github.com/brazzcore/mocai/pkg/mocai/entities/address" + brazilian_birth_certificate "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/brazil" + brazilian_company "github.com/brazzcore/mocai/pkg/mocai/entities/company/brazil" + "github.com/brazzcore/mocai/pkg/mocai/entities/person" + "github.com/brazzcore/mocai/pkg/mocai/entities/phone" + "github.com/brazzcore/mocai/pkg/mocai/translations" ) -func main() { - // Gerar um mock em português (pt-br) - mock := mocai.GenerateMocai("pt-br") +func GenerateMockExample() { + // Defina o idioma para pt-BR + translations.SetLanguage("ptbr") + + // Gerar dados mock + person_mock, err := person.GeneratePerson() + if err != nil { + fmt.Print(err) + } + + address_mock, err := address.GenerateAddress() + if err != nil { + fmt.Print(err) + } + + phone_mock, err := phone.GeneratePhone() + if err != nil { + fmt.Print(err) + } + + company_mock, err := brazilian_company.GenerateCompany() + if err != nil { + fmt.Print(err) + return + } + + birth_certificate_mock, err := brazilian_birth_certificate.GenerateBirthCertificate(false) + if err != nil { + fmt.Print(err) + return + } + + fmt.Printf("Person: %s %s, %s, %d years old, CPF: %s\n", + person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age, person_mock.CPF) + + fmt.Printf("Birth Certificate: %s\n", birth_certificate_mock.BirthCertificateNumber) + + fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.CompanyName, company_mock.CNPJ) - fmt.Println("Pessoa:", mock.Person.FirstName, mock.Person.LastName) - fmt.Println("Endereço:", mock.Address.Street, mock.Address.Number) - fmt.Println("Telefone:", mock.Phone.AreaCode, mock.Phone.Number) + fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n", + address_mock.Street, address_mock.Number, address_mock.City, address_mock.State, address_mock.UF, address_mock.ZIP) + fmt.Printf("Phone: (%s) %s\n", phone_mock.AreaCode, phone_mock.Number) } ``` ### Exemplos -A pasta ***examples*** contém exemplos de como usar a biblioteca. Para executar os exemplos, navegue até a pasta e execute: +A pasta ***examples*** contém exemplos de como usar a biblioteca. Para executar os exemplos, navegue até a pasta raiz e execute: ``` -cd examples go run main.go ``` From 343485f53ede0785696321e7e63349d0f8e20ad7 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Thu, 20 Mar 2025 20:10:29 -0300 Subject: [PATCH 12/29] remove: folders deleted after restructuring packages --- .../birth_certificate/brazil/errors.go | 18 --- .../birth_certificate/brazil/generator.go | 148 ------------------ .../birth_certificate/brazil/validator.go | 37 ----- pkg/mocai/generator/interface.go | 5 - 4 files changed, 208 deletions(-) delete mode 100644 pkg/mocai/entities/birth_certificate/brazil/errors.go delete mode 100644 pkg/mocai/entities/birth_certificate/brazil/generator.go delete mode 100644 pkg/mocai/entities/birth_certificate/brazil/validator.go delete mode 100644 pkg/mocai/generator/interface.go diff --git a/pkg/mocai/entities/birth_certificate/brazil/errors.go b/pkg/mocai/entities/birth_certificate/brazil/errors.go deleted file mode 100644 index f22a1e8..0000000 --- a/pkg/mocai/entities/birth_certificate/brazil/errors.go +++ /dev/null @@ -1,18 +0,0 @@ -package birth_certificate - -import "errors" - -// Package birth_certificate defines common errors used during birth certificate generation and validation. -// These errors represent specific failure scenarios and should be wrapped with additional -// context when returned. -var ( - ErrInvalidBirthCertificate = errors.New("invalid birth certificate") - ErrInvalidVitalRecordsOffice = errors.New("invalid vital records office number") - ErrInvalidArchive = errors.New("invalid archive number") - ErrInvalidVitalRecordsService = errors.New("invalid vital records service number") - ErrInvalidBirthYear = errors.New("invalid birth year") - ErrInvalidBookNumber = errors.New("invalid book number") - ErrInvalidPageNumber = errors.New("invalid page number") - ErrInvalidTermNumber = errors.New("invalid term number") - ErrInvalidNumberWithoutCheckDigits = errors.New("invalid number without check digits") -) diff --git a/pkg/mocai/entities/birth_certificate/brazil/generator.go b/pkg/mocai/entities/birth_certificate/brazil/generator.go deleted file mode 100644 index d3c42a1..0000000 --- a/pkg/mocai/entities/birth_certificate/brazil/generator.go +++ /dev/null @@ -1,148 +0,0 @@ -package birth_certificate - -import ( - "fmt" - "math/rand" - "time" -) - -// BrazilianBirthCertificate represents the structure of a Brazilian birth certificate -// containing all its components and the final certificate number -type BrazilianBirthCertificate struct { - VitalRecordsOffice int - ArchiveCode int - ServiceType int - BirthYear int - CertificateType int - BookNumber int - PageNumber int - TermNumber int - CheckDigits string - BirthCertificateNumber string -} - -// GenerateBirthCertificate generates a valid Brazilian birth certificate number -// If formatted is true, returns the number with separators (-) -// Returns a pointer to BrazilianBirthCertificate and error if any validation fails -func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error) { - - // Create a new random source - r := rand.New(rand.NewSource(time.Now().UnixNano())) - - // Registry Office - // 1. Vital Records Office [6 digits] - vitalRecordsOffice := r.Intn(899999) + 100000 - if vitalRecordsOffice < 0 { - return nil, ErrInvalidVitalRecordsOffice - } - - // 2. Archive [2 digits] - archiveCode := 1 - - // 3. Civil Registry of Natural Persons [2 digits] - serviceType := 55 - - // 4. Birth Year [4 digits] - currentYear := time.Now().Year() - birthYear := r.Intn(currentYear-2010+1) + 2010 - - // 5. Certificate type [1 digit] - certificateType := 1 - - // 6. Book number [5 digits] - bookNumber := r.Intn(89999) + 10000 - if bookNumber < 0 { - return nil, ErrInvalidBookNumber - } - - // 7. Page number [3 digits] - pageNumber := r.Intn(899) + 100 - if pageNumber < 0 { - return nil, ErrInvalidPageNumber - } - - // 8. Term number [7 digits] - termNumber := r.Intn(8999999) + 1000000 - if termNumber < 0 { - return nil, ErrInvalidTermNumber - } - - // Number without check digits [30 digits] - numberWithoutCheckDigits := fmt.Sprintf("%06d%02d%02d%04d%d%05d%03d%07d", - vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber) - if len(numberWithoutCheckDigits) != 30 { - print(numberWithoutCheckDigits) - return nil, ErrInvalidNumberWithoutCheckDigits - } - - // 9. Check digits calculation [2 digits] - checkDigits := calculateCheckDigits(numberWithoutCheckDigits) - print("checkDigits: ", checkDigits) - - birthCertificateNumber := fmt.Sprintf("%s%02s", numberWithoutCheckDigits, checkDigits) - if formatted { - birthCertificateNumber = fmt.Sprintf("%06d %02d %02d %04d %d %05d %03d %07d-%02s", - vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber, checkDigits) - } - - brazilianCertificate := &BrazilianBirthCertificate{ - VitalRecordsOffice: vitalRecordsOffice, - ArchiveCode: archiveCode, - ServiceType: serviceType, - BirthYear: birthYear, - CertificateType: certificateType, - BookNumber: bookNumber, - PageNumber: pageNumber, - TermNumber: termNumber, - CheckDigits: checkDigits, - BirthCertificateNumber: birthCertificateNumber, - } - - return brazilianCertificate, nil -} - -// calculateCheckDigits calculates the check digits for the birth certificate number -// using a weight-based algorithm / using Mod 11 -func calculateCheckDigits(number string) string { - dv1Weights := [30]int{ - 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, - } - - sumDV1 := 0 - for i := 0; i < 30; i++ { - // reverse access: b1 = last digit [position 29], b30 = first [position 0] - digit := int(number[29-i] - '0') - sumDV1 += digit * dv1Weights[i] - } - - dv1 := sumDV1 % 11 - if dv1 == 10 { - dv1 = 1 - } - - dv2Weights := [31]int{ - 9, // first term: dv1 * 9 - 8, 7, 6, 5, 4, 3, 2, 1, 0, - 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - } - - sumDV2 := 0 - // first term: dv1 * 9 - sumDV2 += dv1 * dv2Weights[0] - - // remaining terms: b1 to b30 with remaining weights - for i := 0; i < 30; i++ { - digit := int(number[29-i] - '0') - sumDV2 += digit * dv2Weights[i+1] - } - - dv2 := sumDV2 % 11 - if dv2 == 10 { - dv2 = 1 - } - - return fmt.Sprintf("%d%d", dv1, dv2) -} diff --git a/pkg/mocai/entities/birth_certificate/brazil/validator.go b/pkg/mocai/entities/birth_certificate/brazil/validator.go deleted file mode 100644 index 019c5a8..0000000 --- a/pkg/mocai/entities/birth_certificate/brazil/validator.go +++ /dev/null @@ -1,37 +0,0 @@ -package birth_certificate - -import ( - "regexp" - "strings" -) - -// ValidateBirthCertificate checks if a Brazilian birth certificate number is valid -// It validates the format, length, and check digits of the certificate number -func ValidateBirthCertificate(certificate string) bool { - // Remove any formatting - certificate = strings.ReplaceAll(certificate, "-", "") - certificate = strings.ReplaceAll(certificate, " ", "") - - // Check if the certificate number has exactly 32 digits - if len(certificate) != 32 { - return false - } - - // Verify that the certificate contains only numeric characters - match, _ := regexp.MatchString("^[0-9]+$", certificate) - if !match { - return false - } - - // Extract the number without check digits - number := certificate[:30] - - // Get the informed check digits (last 2 digits) - informedCheckDigit := certificate[30:] - - // Calculate the expected check digits - calculatedCheckDigit := calculateCheckDigits(number) - - // Compare the informed check digits with the calculated ones - return informedCheckDigit == calculatedCheckDigit -} diff --git a/pkg/mocai/generator/interface.go b/pkg/mocai/generator/interface.go deleted file mode 100644 index 1ca254f..0000000 --- a/pkg/mocai/generator/interface.go +++ /dev/null @@ -1,5 +0,0 @@ -package generator - -// Generator is an interface that defines the signature of a data generation function. -// It returns a value of type `interface{}`, allowing flexibility in the type of data generated. -type Generator func() interface{} From 38cb2ef55876096e7741ba266131af84f7fe3280 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Thu, 20 Mar 2025 20:11:26 -0300 Subject: [PATCH 13/29] refactor: restructured packages for birth certificate --- .../countries/brazilian_birth_certificate.go | 152 ++++++++++++++++++ .../birth_certificate/countries/errors.go | 18 +++ .../birth_certificate/countries/validator.go | 37 +++++ .../entities/birth_certificate/generator.go | 16 ++ 4 files changed, 223 insertions(+) create mode 100644 pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go create mode 100644 pkg/mocai/entities/birth_certificate/countries/errors.go create mode 100644 pkg/mocai/entities/birth_certificate/countries/validator.go create mode 100644 pkg/mocai/entities/birth_certificate/generator.go diff --git a/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go b/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go new file mode 100644 index 0000000..0c007df --- /dev/null +++ b/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go @@ -0,0 +1,152 @@ +package countries + +import ( + "fmt" + "math/rand" + "time" +) + +// BrazilianBirthCertificate represents the structure of a Brazilian birth certificate +// containing all its components and the final certificate number +type BrazilianBirthCertificate struct { + VitalRecordsOffice int + ArchiveCode int + ServiceType int + BirthYear int + CertificateType int + BookNumber int + PageNumber int + TermNumber int + CheckDigits string + CertificateNumber string +} + +// GenerateBirthCertificate generates a valid Brazilian birth certificate number +// If formatted is true, returns the number with separators (-) +// Returns a pointer to BrazilianBirthCertificate and error if any validation fails +func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error) { + + // Create a new random source + r := rand.New(rand.NewSource(time.Now().UnixNano())) + + // Registry Office + // 1. Vital Records Office [6 digits] + vitalRecordsOffice := r.Intn(899999) + 100000 + if vitalRecordsOffice < 0 { + return nil, ErrInvalidVitalRecordsOffice + } + + // 2. Archive [2 digits] + archiveCode := 1 + + // 3. Civil Registry of Natural Persons [2 digits] + serviceType := 55 + + // 4. Birth Year [4 digits] + currentYear := time.Now().Year() + birthYear := r.Intn(currentYear-2010+1) + 2010 + + // 5. Certificate type [1 digit] + certificateType := 1 + + // 6. Book number [5 digits] + bookNumber := r.Intn(89999) + 10000 + if bookNumber < 0 { + return nil, ErrInvalidBookNumber + } + + // 7. Page number [3 digits] + pageNumber := r.Intn(899) + 100 + if pageNumber < 0 { + return nil, ErrInvalidPageNumber + } + + // 8. Term number [7 digits] + termNumber := r.Intn(8999999) + 1000000 + if termNumber < 0 { + return nil, ErrInvalidTermNumber + } + + // Number without check digits [30 digits] + numberWithoutCheckDigits := fmt.Sprintf("%06d%02d%02d%04d%d%05d%03d%07d", + vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber) + if len(numberWithoutCheckDigits) != 30 { + print(numberWithoutCheckDigits) + return nil, ErrInvalidNumberWithoutCheckDigits + } + + // 9. Check digits calculation [2 digits] + checkDigits := calculateCheckDigits(numberWithoutCheckDigits) + print("checkDigits: ", checkDigits) + + certificateNumber := fmt.Sprintf("%s%02s", numberWithoutCheckDigits, checkDigits) + if formatted { + certificateNumber = fmt.Sprintf("%06d %02d %02d %04d %d %05d %03d %07d-%02s", + vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber, checkDigits) + } + + if certificateNumber == "" { + return nil, ErrInvalidBirthCertificate + } + + brazilianCertificate := &BrazilianBirthCertificate{ + VitalRecordsOffice: vitalRecordsOffice, + ArchiveCode: archiveCode, + ServiceType: serviceType, + BirthYear: birthYear, + CertificateType: certificateType, + BookNumber: bookNumber, + PageNumber: pageNumber, + TermNumber: termNumber, + CheckDigits: checkDigits, + CertificateNumber: certificateNumber, + } + + return brazilianCertificate, nil +} + +// calculateCheckDigits calculates the check digits for the birth certificate number +// using a weight-based algorithm / using Mod 11 +func calculateCheckDigits(number string) string { + dv1Weights := [30]int{ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, + } + + sumDV1 := 0 + for i := 0; i < 30; i++ { + // reverse access: b1 = last digit [position 29], b30 = first [position 0] + digit := int(number[29-i] - '0') + sumDV1 += digit * dv1Weights[i] + } + + dv1 := sumDV1 % 11 + if dv1 == 10 { + dv1 = 1 + } + + dv2Weights := [31]int{ + 9, // first term: dv1 * 9 + 8, 7, 6, 5, 4, 3, 2, 1, 0, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + } + + sumDV2 := 0 + // first term: dv1 * 9 + sumDV2 += dv1 * dv2Weights[0] + + // remaining terms: b1 to b30 with remaining weights + for i := 0; i < 30; i++ { + digit := int(number[29-i] - '0') + sumDV2 += digit * dv2Weights[i+1] + } + + dv2 := sumDV2 % 11 + if dv2 == 10 { + dv2 = 1 + } + + return fmt.Sprintf("%d%d", dv1, dv2) +} diff --git a/pkg/mocai/entities/birth_certificate/countries/errors.go b/pkg/mocai/entities/birth_certificate/countries/errors.go new file mode 100644 index 0000000..6766fa3 --- /dev/null +++ b/pkg/mocai/entities/birth_certificate/countries/errors.go @@ -0,0 +1,18 @@ +package countries + +import "errors" + +// Errors for Birth Certificate data generation failures. +// These errors represent specific failure scenarios and should be wrapped with additional +// context when returned. +var ( + ErrInvalidBirthCertificate = errors.New("invalid birth certificate") + ErrInvalidVitalRecordsOffice = errors.New("invalid vital records office number") + ErrInvalidArchive = errors.New("invalid archive number") + ErrInvalidVitalRecordsService = errors.New("invalid vital records service number") + ErrInvalidBirthYear = errors.New("invalid birth year") + ErrInvalidBookNumber = errors.New("invalid book number") + ErrInvalidPageNumber = errors.New("invalid page number") + ErrInvalidTermNumber = errors.New("invalid term number") + ErrInvalidNumberWithoutCheckDigits = errors.New("invalid number without check digits") +) diff --git a/pkg/mocai/entities/birth_certificate/countries/validator.go b/pkg/mocai/entities/birth_certificate/countries/validator.go new file mode 100644 index 0000000..92d6ce5 --- /dev/null +++ b/pkg/mocai/entities/birth_certificate/countries/validator.go @@ -0,0 +1,37 @@ +package countries + +import ( + "regexp" + "strings" +) + +// ValidateBirthCertificate checks if a Brazilian birth certificate number is valid +// It validates the format, length, and check digits of the certificate number +func ValidateBirthCertificate(certificate string) bool { + // Remove any formatting + certificate = strings.ReplaceAll(certificate, "-", "") + certificate = strings.ReplaceAll(certificate, " ", "") + + // Check if the certificate number has exactly 32 digits + if len(certificate) != 32 { + return false + } + + // Verify that the certificate contains only numeric characters + match, _ := regexp.MatchString("^[0-9]+$", certificate) + if !match { + return false + } + + // Extract the number without check digits + number := certificate[:30] + + // Get the informed check digits (last 2 digits) + informedCheckDigit := certificate[30:] + + // Calculate the expected check digits + calculatedCheckDigit := calculateCheckDigits(number) + + // Compare the informed check digits with the calculated ones + return informedCheckDigit == calculatedCheckDigit +} diff --git a/pkg/mocai/entities/birth_certificate/generator.go b/pkg/mocai/entities/birth_certificate/generator.go new file mode 100644 index 0000000..bcc062e --- /dev/null +++ b/pkg/mocai/entities/birth_certificate/generator.go @@ -0,0 +1,16 @@ +package birth_certificate + +import "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/countries" + +type BirthCertificate struct { + BrazilianBirthCertificate *countries.BrazilianBirthCertificate +} + +func GenerateBirthCertificate(formatted bool) (*BirthCertificate, error) { + createdBrazilianBirthCertificate, err := countries.GenerateBirthCertificate(formatted) + if err != nil { + return nil, err + } + + return &BirthCertificate{BrazilianBirthCertificate: createdBrazilianBirthCertificate}, nil +} From 08e1a8e8622ef40ac7d96a3e6709fe18440d8c35 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Thu, 20 Mar 2025 20:12:10 -0300 Subject: [PATCH 14/29] feat: added example for birth certificate --- examples/mock_example.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/mock_example.go b/examples/mock_example.go index 5d28387..ef89b82 100644 --- a/examples/mock_example.go +++ b/examples/mock_example.go @@ -5,8 +5,8 @@ import ( "github.com/brazzcore/mocai/pkg/mocai/constants" "github.com/brazzcore/mocai/pkg/mocai/entities/address" - brazilian_birth_certificate "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/brazil" - brazilian_company "github.com/brazzcore/mocai/pkg/mocai/entities/company/brazil" + "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate" + "github.com/brazzcore/mocai/pkg/mocai/entities/company" "github.com/brazzcore/mocai/pkg/mocai/entities/person" "github.com/brazzcore/mocai/pkg/mocai/entities/phone" "github.com/brazzcore/mocai/pkg/mocai/translations" @@ -32,13 +32,13 @@ func GenerateMockExample() { fmt.Print(err) } - company_mock, err := brazilian_company.GenerateCompany() + company_mock, err := company.GenerateCompany(false) if err != nil { fmt.Print(err) return } - birth_certificate_mock, err := brazilian_birth_certificate.GenerateBirthCertificate(false) + birth_certificate_mock, err := birth_certificate.GenerateBirthCertificate(false) if err != nil { fmt.Print(err) return @@ -50,9 +50,9 @@ func GenerateMockExample() { fmt.Printf("Person: %s %s, %s, %d years old, CPF: %s\n", person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age, person_mock.CPF) - fmt.Printf("Birth Certificate: %s\n", birth_certificate_mock.BirthCertificateNumber) + fmt.Printf("Birth Certificate: %s\n", birth_certificate_mock.BrazilianBirthCertificate.CertificateNumber) - fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.CompanyName, company_mock.CNPJ) + fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.BrazilianCompany.CompanyName, company_mock.BrazilianCompany.CNPJ) fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n", address_mock.Street, address_mock.Number, address_mock.City, address_mock.State, address_mock.UF, address_mock.ZIP) From f94442ffc06c7c3ce80f2f6065ca1eebff18e4e8 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Thu, 20 Mar 2025 20:13:38 -0300 Subject: [PATCH 15/29] refactor: added parameter to format company data --- pkg/mocai/entities/company/countries/brazilian_company.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/mocai/entities/company/countries/brazilian_company.go b/pkg/mocai/entities/company/countries/brazilian_company.go index 8563550..55e1701 100644 --- a/pkg/mocai/entities/company/countries/brazilian_company.go +++ b/pkg/mocai/entities/company/countries/brazilian_company.go @@ -19,7 +19,7 @@ type BrazilianCompany struct { var rng = rand.New(rand.NewSource(time.Now().UnixNano())) -func GenerateBrazilianCompany() (*BrazilianCompany, error) { +func GenerateBrazilianCompany(formatted bool) (*BrazilianCompany, error) { lang := translations.GetLanguage() // Get the list of company names @@ -34,7 +34,7 @@ func GenerateBrazilianCompany() (*BrazilianCompany, error) { companyName := companyNames[rng.Intn(len(companyNames))] // Generate a random CNPJ without a mask - cnpj, err := cnpj.GenerateCNPJ(false) + cnpj, err := cnpj.GenerateCNPJ(formatted) if err != nil { return nil, err } @@ -45,7 +45,7 @@ func GenerateBrazilianCompany() (*BrazilianCompany, error) { } if cnpj == "" { - return nil, fmt.Errorf("%s: CNPJ is empty", ErrGeneratingBrazilianCompany) + return nil, fmt.Errorf("%s: CNPJ is empty", ErrGeneratingCNPJ) } createdCompany := &BrazilianCompany{ From 2ca2b8d1e7ab8eb8fdd9a9182bb92ca306c0b1c2 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Thu, 20 Mar 2025 20:14:26 -0300 Subject: [PATCH 16/29] refactor: changed company error comment --- pkg/mocai/entities/company/countries/errors.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/mocai/entities/company/countries/errors.go b/pkg/mocai/entities/company/countries/errors.go index f2e6fc9..701602a 100644 --- a/pkg/mocai/entities/company/countries/errors.go +++ b/pkg/mocai/entities/company/countries/errors.go @@ -2,9 +2,10 @@ package countries import "errors" -// Error constants for Company data generation failures. +// Errors for Company data generation failures. // These errors represent specific failure scenarios and should be wrapped with additional // context when returned. var ( ErrGeneratingBrazilianCompany = errors.New("error generating brazilian company") + ErrGeneratingCNPJ = errors.New("error generating CNPJ") ) From ca76f5fa45649b900307aa729f31fe1b9f1e21d0 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Thu, 20 Mar 2025 20:15:18 -0300 Subject: [PATCH 17/29] refactor: added parameter to format company data --- pkg/mocai/entities/company/generator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/mocai/entities/company/generator.go b/pkg/mocai/entities/company/generator.go index 5dc0fb0..2404578 100644 --- a/pkg/mocai/entities/company/generator.go +++ b/pkg/mocai/entities/company/generator.go @@ -8,8 +8,8 @@ type Company struct { } // GenerateCompany generates all companies available. -func GenerateCompany() (*Company, error) { - createdCompanyBrazilian, err := countries.GenerateBrazilianCompany() +func GenerateCompany(formatted bool) (*Company, error) { + createdCompanyBrazilian, err := countries.GenerateBrazilianCompany(formatted) if err != nil { return nil, err From c145019a9dd9bd9d0dcb533d6f6e19159734ddb3 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Thu, 20 Mar 2025 20:17:56 -0300 Subject: [PATCH 18/29] fix: fixed unit test for company --- pkg/mocai/entities/company/countries/generator_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/mocai/entities/company/countries/generator_test.go b/pkg/mocai/entities/company/countries/generator_test.go index 6b51ce5..39f968d 100644 --- a/pkg/mocai/entities/company/countries/generator_test.go +++ b/pkg/mocai/entities/company/countries/generator_test.go @@ -7,7 +7,7 @@ import ( ) func TestGenerateCompany(t *testing.T) { - company, err := GenerateBrazilianCompany() + company, err := GenerateBrazilianCompany(false) if err != nil { t.Fatalf("Failed to generate company: %v", err) } @@ -18,7 +18,7 @@ func TestGenerateCompany(t *testing.T) { } func TestIfCompanyHasAValidCNPJ(t *testing.T) { - company, err := GenerateBrazilianCompany() + company, err := GenerateBrazilianCompany(false) if err != nil { t.Fatalf("Failed to generate company: %v", err) } From fe5796f2be221051b200161c4fd635b58bb0f8d3 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Thu, 20 Mar 2025 20:20:24 -0300 Subject: [PATCH 19/29] refactor: removed useless prints --- .../birth_certificate/countries/brazilian_birth_certificate.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go b/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go index 0c007df..60cecd1 100644 --- a/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go +++ b/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go @@ -71,13 +71,11 @@ func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error numberWithoutCheckDigits := fmt.Sprintf("%06d%02d%02d%04d%d%05d%03d%07d", vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber) if len(numberWithoutCheckDigits) != 30 { - print(numberWithoutCheckDigits) return nil, ErrInvalidNumberWithoutCheckDigits } // 9. Check digits calculation [2 digits] checkDigits := calculateCheckDigits(numberWithoutCheckDigits) - print("checkDigits: ", checkDigits) certificateNumber := fmt.Sprintf("%s%02s", numberWithoutCheckDigits, checkDigits) if formatted { From a82807c95c960cd743ee8ec867a59d9d02a82d7c Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 11:16:20 -0300 Subject: [PATCH 20/29] remove: files changed for generalization --- .../countries/brazilian_birth_certificate.go | 150 ------------------ .../birth_certificate/countries/errors.go | 18 --- .../birth_certificate/countries/validator.go | 37 ----- .../entities/birth_certificate/generator.go | 16 -- 4 files changed, 221 deletions(-) delete mode 100644 pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go delete mode 100644 pkg/mocai/entities/birth_certificate/countries/errors.go delete mode 100644 pkg/mocai/entities/birth_certificate/countries/validator.go delete mode 100644 pkg/mocai/entities/birth_certificate/generator.go diff --git a/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go b/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go deleted file mode 100644 index 60cecd1..0000000 --- a/pkg/mocai/entities/birth_certificate/countries/brazilian_birth_certificate.go +++ /dev/null @@ -1,150 +0,0 @@ -package countries - -import ( - "fmt" - "math/rand" - "time" -) - -// BrazilianBirthCertificate represents the structure of a Brazilian birth certificate -// containing all its components and the final certificate number -type BrazilianBirthCertificate struct { - VitalRecordsOffice int - ArchiveCode int - ServiceType int - BirthYear int - CertificateType int - BookNumber int - PageNumber int - TermNumber int - CheckDigits string - CertificateNumber string -} - -// GenerateBirthCertificate generates a valid Brazilian birth certificate number -// If formatted is true, returns the number with separators (-) -// Returns a pointer to BrazilianBirthCertificate and error if any validation fails -func GenerateBirthCertificate(formatted bool) (*BrazilianBirthCertificate, error) { - - // Create a new random source - r := rand.New(rand.NewSource(time.Now().UnixNano())) - - // Registry Office - // 1. Vital Records Office [6 digits] - vitalRecordsOffice := r.Intn(899999) + 100000 - if vitalRecordsOffice < 0 { - return nil, ErrInvalidVitalRecordsOffice - } - - // 2. Archive [2 digits] - archiveCode := 1 - - // 3. Civil Registry of Natural Persons [2 digits] - serviceType := 55 - - // 4. Birth Year [4 digits] - currentYear := time.Now().Year() - birthYear := r.Intn(currentYear-2010+1) + 2010 - - // 5. Certificate type [1 digit] - certificateType := 1 - - // 6. Book number [5 digits] - bookNumber := r.Intn(89999) + 10000 - if bookNumber < 0 { - return nil, ErrInvalidBookNumber - } - - // 7. Page number [3 digits] - pageNumber := r.Intn(899) + 100 - if pageNumber < 0 { - return nil, ErrInvalidPageNumber - } - - // 8. Term number [7 digits] - termNumber := r.Intn(8999999) + 1000000 - if termNumber < 0 { - return nil, ErrInvalidTermNumber - } - - // Number without check digits [30 digits] - numberWithoutCheckDigits := fmt.Sprintf("%06d%02d%02d%04d%d%05d%03d%07d", - vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber) - if len(numberWithoutCheckDigits) != 30 { - return nil, ErrInvalidNumberWithoutCheckDigits - } - - // 9. Check digits calculation [2 digits] - checkDigits := calculateCheckDigits(numberWithoutCheckDigits) - - certificateNumber := fmt.Sprintf("%s%02s", numberWithoutCheckDigits, checkDigits) - if formatted { - certificateNumber = fmt.Sprintf("%06d %02d %02d %04d %d %05d %03d %07d-%02s", - vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber, checkDigits) - } - - if certificateNumber == "" { - return nil, ErrInvalidBirthCertificate - } - - brazilianCertificate := &BrazilianBirthCertificate{ - VitalRecordsOffice: vitalRecordsOffice, - ArchiveCode: archiveCode, - ServiceType: serviceType, - BirthYear: birthYear, - CertificateType: certificateType, - BookNumber: bookNumber, - PageNumber: pageNumber, - TermNumber: termNumber, - CheckDigits: checkDigits, - CertificateNumber: certificateNumber, - } - - return brazilianCertificate, nil -} - -// calculateCheckDigits calculates the check digits for the birth certificate number -// using a weight-based algorithm / using Mod 11 -func calculateCheckDigits(number string) string { - dv1Weights := [30]int{ - 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, - } - - sumDV1 := 0 - for i := 0; i < 30; i++ { - // reverse access: b1 = last digit [position 29], b30 = first [position 0] - digit := int(number[29-i] - '0') - sumDV1 += digit * dv1Weights[i] - } - - dv1 := sumDV1 % 11 - if dv1 == 10 { - dv1 = 1 - } - - dv2Weights := [31]int{ - 9, // first term: dv1 * 9 - 8, 7, 6, 5, 4, 3, 2, 1, 0, - 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - } - - sumDV2 := 0 - // first term: dv1 * 9 - sumDV2 += dv1 * dv2Weights[0] - - // remaining terms: b1 to b30 with remaining weights - for i := 0; i < 30; i++ { - digit := int(number[29-i] - '0') - sumDV2 += digit * dv2Weights[i+1] - } - - dv2 := sumDV2 % 11 - if dv2 == 10 { - dv2 = 1 - } - - return fmt.Sprintf("%d%d", dv1, dv2) -} diff --git a/pkg/mocai/entities/birth_certificate/countries/errors.go b/pkg/mocai/entities/birth_certificate/countries/errors.go deleted file mode 100644 index 6766fa3..0000000 --- a/pkg/mocai/entities/birth_certificate/countries/errors.go +++ /dev/null @@ -1,18 +0,0 @@ -package countries - -import "errors" - -// Errors for Birth Certificate data generation failures. -// These errors represent specific failure scenarios and should be wrapped with additional -// context when returned. -var ( - ErrInvalidBirthCertificate = errors.New("invalid birth certificate") - ErrInvalidVitalRecordsOffice = errors.New("invalid vital records office number") - ErrInvalidArchive = errors.New("invalid archive number") - ErrInvalidVitalRecordsService = errors.New("invalid vital records service number") - ErrInvalidBirthYear = errors.New("invalid birth year") - ErrInvalidBookNumber = errors.New("invalid book number") - ErrInvalidPageNumber = errors.New("invalid page number") - ErrInvalidTermNumber = errors.New("invalid term number") - ErrInvalidNumberWithoutCheckDigits = errors.New("invalid number without check digits") -) diff --git a/pkg/mocai/entities/birth_certificate/countries/validator.go b/pkg/mocai/entities/birth_certificate/countries/validator.go deleted file mode 100644 index 92d6ce5..0000000 --- a/pkg/mocai/entities/birth_certificate/countries/validator.go +++ /dev/null @@ -1,37 +0,0 @@ -package countries - -import ( - "regexp" - "strings" -) - -// ValidateBirthCertificate checks if a Brazilian birth certificate number is valid -// It validates the format, length, and check digits of the certificate number -func ValidateBirthCertificate(certificate string) bool { - // Remove any formatting - certificate = strings.ReplaceAll(certificate, "-", "") - certificate = strings.ReplaceAll(certificate, " ", "") - - // Check if the certificate number has exactly 32 digits - if len(certificate) != 32 { - return false - } - - // Verify that the certificate contains only numeric characters - match, _ := regexp.MatchString("^[0-9]+$", certificate) - if !match { - return false - } - - // Extract the number without check digits - number := certificate[:30] - - // Get the informed check digits (last 2 digits) - informedCheckDigit := certificate[30:] - - // Calculate the expected check digits - calculatedCheckDigit := calculateCheckDigits(number) - - // Compare the informed check digits with the calculated ones - return informedCheckDigit == calculatedCheckDigit -} diff --git a/pkg/mocai/entities/birth_certificate/generator.go b/pkg/mocai/entities/birth_certificate/generator.go deleted file mode 100644 index bcc062e..0000000 --- a/pkg/mocai/entities/birth_certificate/generator.go +++ /dev/null @@ -1,16 +0,0 @@ -package birth_certificate - -import "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/countries" - -type BirthCertificate struct { - BrazilianBirthCertificate *countries.BrazilianBirthCertificate -} - -func GenerateBirthCertificate(formatted bool) (*BirthCertificate, error) { - createdBrazilianBirthCertificate, err := countries.GenerateBirthCertificate(formatted) - if err != nil { - return nil, err - } - - return &BirthCertificate{BrazilianBirthCertificate: createdBrazilianBirthCertificate}, nil -} From 2cf5c79c42fedb324ec7a912a24eaeb633f59f49 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 11:17:27 -0300 Subject: [PATCH 21/29] feat: added certificate generator --- pkg/mocai/entities/certificate/generator.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 pkg/mocai/entities/certificate/generator.go diff --git a/pkg/mocai/entities/certificate/generator.go b/pkg/mocai/entities/certificate/generator.go new file mode 100644 index 0000000..003b3b7 --- /dev/null +++ b/pkg/mocai/entities/certificate/generator.go @@ -0,0 +1,16 @@ +package certificate + +import "github.com/brazzcore/mocai/pkg/mocai/entities/certificate/countries" + +type Certificate struct { + BrazilianCertificates *countries.BrazilianCertificates +} + +func GenerateCertificate(formatted bool) (*Certificate, error) { + createdBrazilianCertificates, err := countries.GenerateBrazilianCertificates(formatted) + if err != nil { + return nil, err + } + + return &Certificate{BrazilianCertificates: createdBrazilianCertificates}, nil +} From 5f9d28a8d5b98377eec7d2d95d5402108abdd6bf Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 11:18:56 -0300 Subject: [PATCH 22/29] feat: added generation model for brazilian certificates --- .../countries/brazilian_certificates.go | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 pkg/mocai/entities/certificate/countries/brazilian_certificates.go diff --git a/pkg/mocai/entities/certificate/countries/brazilian_certificates.go b/pkg/mocai/entities/certificate/countries/brazilian_certificates.go new file mode 100644 index 0000000..ef13017 --- /dev/null +++ b/pkg/mocai/entities/certificate/countries/brazilian_certificates.go @@ -0,0 +1,258 @@ +package countries + +import ( + "fmt" + "math/rand" + "time" +) + +// Brazilian certificate types +// 1 - for birth certificate +// 2 - for marriage certificate +// 3 - for death certificate +const ( + brazilianBirthCertificateType = 1 + brazilianMarriageCertificateType = 2 + brazilianDeathCertificateType = 3 +) + +// BaseCertificate is the base structure for all brazilian certificates +type BaseCertificate struct { + VitalRecordsOffice int + ArchiveCode int + ServiceType int + BirthYear int + CertificateType int + BookNumber int + PageNumber int + TermNumber int + CheckDigits string + CertificateNumber string +} + +// BirthCertificate represents a brazilian birth certificate +type BirthCertificate struct { + BaseCertificate +} + +// MarriageCertificate represents a brazilian marriage certificate +type MarriageCertificate struct { + BaseCertificate +} + +// DeathCertificate represents a brazilian death certificate +type DeathCertificate struct { + BaseCertificate +} + +// BrazilianCertificates represents all brazilian certificates +type BrazilianCertificates struct { + BirthCertificate *BirthCertificate + MarriageCertificate *MarriageCertificate + DeathCertificate *DeathCertificate +} + +// GenerateBrazilianCertificates generates a valid brazilian certificates +// If formatted is true, returns the number with separators (-) +// Returns a pointer to BrazilianCertificates and error if any validation fails +func GenerateBrazilianCertificates(formatted bool) (*BrazilianCertificates, error) { + createdBirthCertificate, err := generateBirthCertificate(formatted) + if err != nil { + return nil, err + } + + createdMarriageCertificate, err := generateMarriageCertificate(formatted) + if err != nil { + return nil, err + } + + createdDeathCertificate, err := generateDeathCertificate(formatted) + if err != nil { + return nil, err + } + + createdBrazilianCertificates := &BrazilianCertificates{ + BirthCertificate: createdBirthCertificate, + MarriageCertificate: createdMarriageCertificate, + DeathCertificate: createdDeathCertificate, + } + + return createdBrazilianCertificates, nil +} + +// generateCertificates generates a valid brazilian certificate +// If formatted is true, returns the number with separators (-) +// certificateType is the type of certificate [1 - for birth certificate, 2 - for marriage certificate, 3 - for death certificate] +// Returns a pointer to BaseCertificate and error if any validation fails +func generateCertificate(formatted bool, certificateType int) (*BaseCertificate, error) { + // Registry Office + // 1. Vital Records Office [6 digits] + vitalRecordsOffice := generateRandomNumber(100000, 899999) + if vitalRecordsOffice < 0 { + return nil, ErrInvalidVitalRecordsOffice + } + + // 2. Archive [2 digits] + archiveCode := 1 + + // 3. Civil Registry of Natural Persons [2 digits] + serviceType := 55 + + // 4. Birth Year [4 digits] + birthYear := generateRandomYear(2010) + + // 5. Certificate type [1 digit] + //certificateType + + // 6. Book number [5 digits] + bookNumber := generateRandomNumber(10000, 89999) + if bookNumber < 0 { + return nil, ErrInvalidBookNumber + } + + // 7. Page number [3 digits] + pageNumber := generateRandomNumber(100, 899) + if pageNumber < 0 { + return nil, ErrInvalidPageNumber + } + + // 8. Term number [7 digits] + termNumber := generateRandomNumber(1000000, 8999999) + if termNumber < 0 { + return nil, ErrInvalidTermNumber + } + + // Number without check digits [30 digits] + numberWithoutCheckDigits := fmt.Sprintf("%06d%02d%02d%04d%d%05d%03d%07d", + vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber) + if len(numberWithoutCheckDigits) != 30 { + return nil, ErrInvalidNumberWithoutCheckDigits + } + + // 9. Check digits calculation [2 digits] + checkDigits := calculateCheckDigits(numberWithoutCheckDigits) + + certificateNumber := fmt.Sprintf("%s%02s", numberWithoutCheckDigits, checkDigits) + if formatted { + certificateNumber = fmt.Sprintf("%06d %02d %02d %04d %d %05d %03d %07d-%02s", + vitalRecordsOffice, archiveCode, serviceType, birthYear, certificateType, bookNumber, pageNumber, termNumber, checkDigits) + } + + if certificateNumber == "" { + return nil, ErrInvalidBirthCertificate + } + + createdBaseCertificate := &BaseCertificate{ + VitalRecordsOffice: vitalRecordsOffice, + ArchiveCode: archiveCode, + ServiceType: serviceType, + BirthYear: birthYear, + CertificateType: certificateType, + BookNumber: bookNumber, + PageNumber: pageNumber, + TermNumber: termNumber, + CheckDigits: checkDigits, + CertificateNumber: certificateNumber, + } + + return createdBaseCertificate, nil +} + +// generateBirthCertificate generates a valid Brazilian birth certificate +func generateBirthCertificate(formatted bool) (*BirthCertificate, error) { + base, err := generateCertificate(formatted, brazilianBirthCertificateType) + if err != nil { + return nil, err + } + + createdBirthCertificate := &BirthCertificate{ + BaseCertificate: *base, + } + + return createdBirthCertificate, nil +} + +// generateMarriageCertificate generates a valid Brazilian marriage certificate +func generateMarriageCertificate(formatted bool) (*MarriageCertificate, error) { + base, err := generateCertificate(formatted, brazilianMarriageCertificateType) + if err != nil { + return nil, err + } + + createdMarriageCertificate := &MarriageCertificate{ + BaseCertificate: *base, + } + + return createdMarriageCertificate, nil +} + +// generateDeathCertificate generates a valid Brazilian death certificate +func generateDeathCertificate(formatted bool) (*DeathCertificate, error) { + base, err := generateCertificate(formatted, brazilianDeathCertificateType) + if err != nil { + return nil, err + } + + createdDeathCertificate := &DeathCertificate{ + BaseCertificate: *base, + } + + return createdDeathCertificate, nil +} + +// generateRandomNumber generates a random number between min and max +func generateRandomNumber(min, max int) int { + return rand.Intn(max-min+1) + min +} + +// generateRandomYear generates a random year between startYear and the current year +func generateRandomYear(startYear int) int { + currentYear := time.Now().Year() + return rand.Intn(currentYear-startYear+1) + startYear +} + +// calculateCheckDigits calculates the check digits for the birth certificate number +// using a weight-based algorithm / using Mod 11 +func calculateCheckDigits(number string) string { + dv1Weights := [30]int{ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, + } + + sumDV1 := 0 + for i := 0; i < 30; i++ { + // reverse access: b1 = last digit [position 29], b30 = first [position 0] + digit := int(number[29-i] - '0') + sumDV1 += digit * dv1Weights[i] + } + + dv1 := sumDV1 % 11 + if dv1 == 10 { + dv1 = 1 + } + + dv2Weights := [31]int{ + 9, // first term: dv1 * 9 + 8, 7, 6, 5, 4, 3, 2, 1, 0, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + } + + sumDV2 := 0 + // first term: dv1 * 9 + sumDV2 += dv1 * dv2Weights[0] + + // remaining terms: b1 to b30 with remaining weights + for i := 0; i < 30; i++ { + digit := int(number[29-i] - '0') + sumDV2 += digit * dv2Weights[i+1] + } + + dv2 := sumDV2 % 11 + if dv2 == 10 { + dv2 = 1 + } + + return fmt.Sprintf("%d%d", dv1, dv2) +} From 3566391b041dd36c9a48f9cfb6a65c2f1f321653 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 11:22:08 -0300 Subject: [PATCH 23/29] fix: changed error to a generic error --- .../entities/certificate/countries/brazilian_certificates.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mocai/entities/certificate/countries/brazilian_certificates.go b/pkg/mocai/entities/certificate/countries/brazilian_certificates.go index ef13017..6b61c23 100644 --- a/pkg/mocai/entities/certificate/countries/brazilian_certificates.go +++ b/pkg/mocai/entities/certificate/countries/brazilian_certificates.go @@ -139,7 +139,7 @@ func generateCertificate(formatted bool, certificateType int) (*BaseCertificate, } if certificateNumber == "" { - return nil, ErrInvalidBirthCertificate + return nil, ErrInvalidCertificate } createdBaseCertificate := &BaseCertificate{ From d7d0412fa15573d78f9ee625389af5db9ed468fa Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 11:22:48 -0300 Subject: [PATCH 24/29] feat: added certificate errors --- .../entities/certificate/countries/errors.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 pkg/mocai/entities/certificate/countries/errors.go diff --git a/pkg/mocai/entities/certificate/countries/errors.go b/pkg/mocai/entities/certificate/countries/errors.go new file mode 100644 index 0000000..88fd04a --- /dev/null +++ b/pkg/mocai/entities/certificate/countries/errors.go @@ -0,0 +1,18 @@ +package countries + +import "errors" + +// Errors for Certificate data generation failures. +// These errors represent specific failure scenarios and should be wrapped with additional +// context when returned. +var ( + ErrInvalidCertificate = errors.New("invalid certificate") + ErrInvalidVitalRecordsOffice = errors.New("invalid vital records office number") + ErrInvalidArchive = errors.New("invalid archive number") + ErrInvalidVitalRecordsService = errors.New("invalid vital records service number") + ErrInvalidBirthYear = errors.New("invalid birth year") + ErrInvalidBookNumber = errors.New("invalid book number") + ErrInvalidPageNumber = errors.New("invalid page number") + ErrInvalidTermNumber = errors.New("invalid term number") + ErrInvalidNumberWithoutCheckDigits = errors.New("invalid number without check digits") +) From cdeb81ed175b8f745c5c4fbe1ef588ce5be3d084 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 11:23:23 -0300 Subject: [PATCH 25/29] feat: added certificate validator --- .../certificate/countries/validator.go | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 pkg/mocai/entities/certificate/countries/validator.go diff --git a/pkg/mocai/entities/certificate/countries/validator.go b/pkg/mocai/entities/certificate/countries/validator.go new file mode 100644 index 0000000..92d6ce5 --- /dev/null +++ b/pkg/mocai/entities/certificate/countries/validator.go @@ -0,0 +1,37 @@ +package countries + +import ( + "regexp" + "strings" +) + +// ValidateBirthCertificate checks if a Brazilian birth certificate number is valid +// It validates the format, length, and check digits of the certificate number +func ValidateBirthCertificate(certificate string) bool { + // Remove any formatting + certificate = strings.ReplaceAll(certificate, "-", "") + certificate = strings.ReplaceAll(certificate, " ", "") + + // Check if the certificate number has exactly 32 digits + if len(certificate) != 32 { + return false + } + + // Verify that the certificate contains only numeric characters + match, _ := regexp.MatchString("^[0-9]+$", certificate) + if !match { + return false + } + + // Extract the number without check digits + number := certificate[:30] + + // Get the informed check digits (last 2 digits) + informedCheckDigit := certificate[30:] + + // Calculate the expected check digits + calculatedCheckDigit := calculateCheckDigits(number) + + // Compare the informed check digits with the calculated ones + return informedCheckDigit == calculatedCheckDigit +} From 0cfefc998c838769deb0335e00a22c0203930939 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 11:25:08 -0300 Subject: [PATCH 26/29] feat: added mock examples for certificates --- examples/mock_example.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/mock_example.go b/examples/mock_example.go index ef89b82..b1bf13a 100644 --- a/examples/mock_example.go +++ b/examples/mock_example.go @@ -5,7 +5,7 @@ import ( "github.com/brazzcore/mocai/pkg/mocai/constants" "github.com/brazzcore/mocai/pkg/mocai/entities/address" - "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate" + "github.com/brazzcore/mocai/pkg/mocai/entities/certificate" "github.com/brazzcore/mocai/pkg/mocai/entities/company" "github.com/brazzcore/mocai/pkg/mocai/entities/person" "github.com/brazzcore/mocai/pkg/mocai/entities/phone" @@ -38,7 +38,7 @@ func GenerateMockExample() { return } - birth_certificate_mock, err := birth_certificate.GenerateBirthCertificate(false) + certificate_mock, err := certificate.GenerateCertificate(false) if err != nil { fmt.Print(err) return @@ -50,7 +50,11 @@ func GenerateMockExample() { fmt.Printf("Person: %s %s, %s, %d years old, CPF: %s\n", person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age, person_mock.CPF) - fmt.Printf("Birth Certificate: %s\n", birth_certificate_mock.BrazilianBirthCertificate.CertificateNumber) + fmt.Printf("Birth Certificate: %s\n", certificate_mock.BrazilianCertificates.BirthCertificate.CertificateNumber) + + fmt.Printf("Marriage Certificate: %s\n", certificate_mock.BrazilianCertificates.MarriageCertificate.CertificateNumber) + + fmt.Printf("Death Certificate: %s\n", certificate_mock.BrazilianCertificates.DeathCertificate.CertificateNumber) fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.BrazilianCompany.CompanyName, company_mock.BrazilianCompany.CNPJ) From 11f03caef6035203d7568c95ef70d4bff1f820be Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 13:17:50 -0300 Subject: [PATCH 27/29] docs: updated main readme --- README.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d1de382..3161b13 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,10 @@ package examples import ( "fmt" + "github.com/brazzcore/mocai/pkg/mocai/constants" "github.com/brazzcore/mocai/pkg/mocai/entities/address" - brazilian_birth_certificate "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/brazil" - brazilian_company "github.com/brazzcore/mocai/pkg/mocai/entities/company/brazil" + "github.com/brazzcore/mocai/pkg/mocai/entities/certificate" + "github.com/brazzcore/mocai/pkg/mocai/entities/company" "github.com/brazzcore/mocai/pkg/mocai/entities/person" "github.com/brazzcore/mocai/pkg/mocai/entities/phone" "github.com/brazzcore/mocai/pkg/mocai/translations" @@ -71,13 +72,13 @@ func GenerateMockExample() { fmt.Print(err) } - company_mock, err := brazilian_company.GenerateCompany() + company_mock, err := company.GenerateCompany(false) if err != nil { fmt.Print(err) return } - birth_certificate_mock, err := brazilian_birth_certificate.GenerateBirthCertificate(false) + certificate_mock, err := certificate.GenerateCertificate(false) if err != nil { fmt.Print(err) return @@ -86,9 +87,13 @@ func GenerateMockExample() { fmt.Printf("Person: %s %s, %s, %d years old, CPF: %s\n", person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age, person_mock.CPF) - fmt.Printf("Birth Certificate: %s\n", birth_certificate_mock.BirthCertificateNumber) + fmt.Printf("Birth Certificate: %s\n", certificate_mock.BrazilianCertificates.BirthCertificate.CertificateNumber) - fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.CompanyName, company_mock.CNPJ) + fmt.Printf("Marriage Certificate: %s\n", certificate_mock.BrazilianCertificates.MarriageCertificate.CertificateNumber) + + fmt.Printf("Death Certificate: %s\n", certificate_mock.BrazilianCertificates.DeathCertificate.CertificateNumber) + + fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.BrazilianCompany.CompanyName, company_mock.BrazilianCompany.CNPJ) fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n", address_mock.Street, address_mock.Number, address_mock.City, address_mock.State, address_mock.UF, address_mock.ZIP) From 74d8055c96f378ad634ad89d63c45f2bb76397a2 Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 13:18:24 -0300 Subject: [PATCH 28/29] docs: updated portuguese readme --- docs/localization/pt/README-PT.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/localization/pt/README-PT.md b/docs/localization/pt/README-PT.md index a7e07c8..255da40 100644 --- a/docs/localization/pt/README-PT.md +++ b/docs/localization/pt/README-PT.md @@ -41,16 +41,17 @@ package examples import ( "fmt" + "github.com/brazzcore/mocai/pkg/mocai/constants" "github.com/brazzcore/mocai/pkg/mocai/entities/address" - brazilian_birth_certificate "github.com/brazzcore/mocai/pkg/mocai/entities/birth_certificate/brazil" - brazilian_company "github.com/brazzcore/mocai/pkg/mocai/entities/company/brazil" + "github.com/brazzcore/mocai/pkg/mocai/entities/certificate" + "github.com/brazzcore/mocai/pkg/mocai/entities/company" "github.com/brazzcore/mocai/pkg/mocai/entities/person" "github.com/brazzcore/mocai/pkg/mocai/entities/phone" "github.com/brazzcore/mocai/pkg/mocai/translations" ) func GenerateMockExample() { - // Defina o idioma para pt-BR + // Defina o idioma para pt-BR translations.SetLanguage("ptbr") // Gerar dados mock @@ -69,13 +70,13 @@ func GenerateMockExample() { fmt.Print(err) } - company_mock, err := brazilian_company.GenerateCompany() + company_mock, err := company.GenerateCompany(false) if err != nil { fmt.Print(err) return } - birth_certificate_mock, err := brazilian_birth_certificate.GenerateBirthCertificate(false) + certificate_mock, err := certificate.GenerateCertificate(false) if err != nil { fmt.Print(err) return @@ -84,9 +85,13 @@ func GenerateMockExample() { fmt.Printf("Person: %s %s, %s, %d years old, CPF: %s\n", person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age, person_mock.CPF) - fmt.Printf("Birth Certificate: %s\n", birth_certificate_mock.BirthCertificateNumber) + fmt.Printf("Birth Certificate: %s\n", certificate_mock.BrazilianCertificates.BirthCertificate.CertificateNumber) - fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.CompanyName, company_mock.CNPJ) + fmt.Printf("Marriage Certificate: %s\n", certificate_mock.BrazilianCertificates.MarriageCertificate.CertificateNumber) + + fmt.Printf("Death Certificate: %s\n", certificate_mock.BrazilianCertificates.DeathCertificate.CertificateNumber) + + fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.BrazilianCompany.CompanyName, company_mock.BrazilianCompany.CNPJ) fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n", address_mock.Street, address_mock.Number, address_mock.City, address_mock.State, address_mock.UF, address_mock.ZIP) From dd1d8a8f07ba5ca74eff90d366bbe66ae4ca40ae Mon Sep 17 00:00:00 2001 From: Welliton Fernandes Leal Date: Fri, 21 Mar 2025 13:24:21 -0300 Subject: [PATCH 29/29] docs: added comment the certificate generator --- pkg/mocai/entities/certificate/generator.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/mocai/entities/certificate/generator.go b/pkg/mocai/entities/certificate/generator.go index 003b3b7..780a8b9 100644 --- a/pkg/mocai/entities/certificate/generator.go +++ b/pkg/mocai/entities/certificate/generator.go @@ -2,10 +2,14 @@ package certificate import "github.com/brazzcore/mocai/pkg/mocai/entities/certificate/countries" +// Certificate represents all certificates available type Certificate struct { BrazilianCertificates *countries.BrazilianCertificates } +// GenerateCertificate generates all certificates available. +// If formatted is true, returns the number with separators (-). +// Returns a pointer to Certificate and error if any validation fails func GenerateCertificate(formatted bool) (*Certificate, error) { createdBrazilianCertificates, err := countries.GenerateBrazilianCertificates(formatted) if err != nil {