Skip to content

Commit

Permalink
new feature: cnpj validation
Browse files Browse the repository at this point in the history
  • Loading branch information
flavioltonon committed Apr 6, 2019
1 parent f6583ec commit ca6965b
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 0 deletions.
125 changes: 125 additions & 0 deletions cnpj.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package brazil

import (
"math/rand"
"regexp"
"strconv"
"time"
)

// CNPJ struct
type cnpj struct {
number cnpjNumber
valid bool
}

func (c cnpj) Number(mask bool) string {
if c.valid && mask {
return string(c.number[:2]) +
"." +
string(c.number[2:5]) +
"." +
string(c.number[5:8]) +
"/" +
string(c.number[8:12]) +
"-" +
string(c.number[12:])
}
return string(c.number)
}

func ParseCNPJ(number string) (cnpj, error) {
number = regexp.MustCompile(`[^0-9]`).ReplaceAllString(number, "")

if len(number) != 14 {
return cnpj{}, errIncorrectLenghtCNPJNumber
}

cnpjNumber := cnpjNumber(number)

if !cnpjNumber.hasValidFirstDigit() {
return cnpj{}, errInvalidCNPJFirstDigit
}

if !cnpjNumber.hasValidSecondDigit() {
return cnpj{}, errInvalidCNPJSecondDigit
}

return cnpj{
number: cnpjNumber,
valid: true,
}, nil
}

func RandomCNPJNumber(mask bool) string {
var multipliers = []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}

source := rand.NewSource(time.Now().UnixNano())
r := rand.New(source)
cnpjNumber := int(r.Int63n(899999999999) + 100000000000)
cnpjString := strconv.Itoa(cnpjNumber)

// Calculate first digit
sum := 0
for i := 0; i < 12; i++ {
number, _ := strconv.Atoi(string(cnpjString[i]))
sum += number * multipliers[i+1]
}
firstDigit := 0
if sum%11 >= 2 {
firstDigit = 11 - sum%11
}

// Calculate second digit
sum = 0
for i := 0; i < 12; i++ {
number, _ := strconv.Atoi(string(cnpjString[i]))
sum += number * multipliers[i]
}
sum += firstDigit * multipliers[12]
secondDigit := 0
if sum%11 >= 2 {
secondDigit = 11 - sum%11
}

if mask {
return cnpjString[:2] + "." + cnpjString[2:5] + "." + cnpjString[5:8] + "/" + cnpjString[8:12] + "-" + strconv.Itoa(firstDigit) + strconv.Itoa(secondDigit)
}
return cnpjString + strconv.Itoa(firstDigit) + strconv.Itoa(secondDigit)
}

type cnpjNumber string

func (c cnpjNumber) hasValidFirstDigit() bool {
var (
multipliers = []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}
sum int
)

for i := 0; i < 12; i++ {
cnpjDigit, _ := strconv.Atoi(string(c[i]))
sum += cnpjDigit * multipliers[i]
}
if sum%11 < 2 {
return string(c[12]) == strconv.Itoa(0)
}

return string(c[12]) == strconv.Itoa(11-sum%11)
}

func (c cnpjNumber) hasValidSecondDigit() bool {
var (
multipliers = []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}
sum int
)

for i := 0; i < 13; i++ {
cnpjDigit, _ := strconv.Atoi(string(c[i]))
sum += cnpjDigit * multipliers[i]
}

if sum%11 < 2 {
return string(c[13]) == strconv.Itoa(0)
}
return string(c[13]) == strconv.Itoa(11-sum%11)
}
108 changes: 108 additions & 0 deletions cnpj_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package brazil_test

import (
"testing"

. "flavioltonon/go-brazil"

. "github.com/smartystreets/goconvey/convey"
)

func TestParseCNPJ(t *testing.T) {
Convey("Given a string named s", t, func() {
var s string

Convey("If s is empty", func() {
s = ""

Convey("And the function ParseCNPJ is called using it as an argument", func() {
cnpj, err := ParseCNPJ(s)

Convey("It should return an error", func() {
So(err, ShouldNotEqual, nil)

Convey("And the CNPJ struct number should be empty", func() {
So(cnpj.Number(false), ShouldEqual, "")
})
})
})
})

Convey("If s is a CNPJ number with an invalid first digit", func() {
s = "11222333000171"

Convey("And the function ParseCNPJ is called using it as an argument", func() {
cnpj, err := ParseCNPJ(s)

Convey("It should return an error", func() {
So(err, ShouldNotEqual, nil)

Convey("And the CNPJ struct number should be empty", func() {
So(cnpj.Number(false), ShouldEqual, "")
})
})
})
})

Convey("If s is a CNPJ number with an invalid second digit", func() {
s = "11222333000182"

Convey("And the function ParseCNPJ is called using it as an argument", func() {
cnpj, err := ParseCNPJ(s)

Convey("It should return an error", func() {
So(err, ShouldNotEqual, nil)

Convey("And the CNPJ struct number should be empty", func() {
So(cnpj.Number(false), ShouldEqual, "")
})
})
})
})

Convey("If s is a valid CNPJ number", func() {
s = "11222333000181"

Convey("And the function ParseCNPJ is called using it as an argument", func() {
cnpj, err := ParseCNPJ(s)

Convey("It should not return an error", func() {
So(err, ShouldEqual, nil)

Convey("And the CNPJ struct number should exist", func() {
So(cnpj.Number(false), ShouldEqual, "11222333000181")
So(cnpj.Number(true), ShouldEqual, "11.222.333/0001-81")
})
})
})
})
})
}

func TestRandomCNPJNumber(t *testing.T) {
Convey("Given the function RandomCNPJNumber", t, func() {
Convey("If its mask argument equals true", func() {
number := RandomCNPJNumber(true)

Convey("It should return a valid CNPJ number", func() {
cnpj, err := ParseCNPJ(number)

So(err, ShouldEqual, nil)
So(cnpj.Number(false), ShouldNotEqual, "")
So(cnpj.Number(true), ShouldNotEqual, "")
})
})

Convey("If its mask argument equals false", func() {
number := RandomCNPJNumber(false)

Convey("It should return a valid CNPJ number", func() {
cnpj, err := ParseCNPJ(number)

So(err, ShouldEqual, nil)
So(cnpj.Number(false), ShouldNotEqual, "")
So(cnpj.Number(true), ShouldNotEqual, "")
})
})
})
}

0 comments on commit ca6965b

Please sign in to comment.