All files / lib/validations/validate cnpjAndCpf.js

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104                                                                2x   2x   2x                   2x     29x     2x     40x     455x                     40x           8x 40x                 2x           4x             2x     2x    
import {
  __,
  addIndex,
  allPass,
  ap,
  apply,
  both,
  complement,
  equals,
  either,
  isEmpty,
  length,
  map,
  modulo,
  multiply,
  nth,
  pipe,
  replace,
  split,
  subtract,
  sum,
  take,
  toString,
  when,
} from 'ramda'
 
// CNPJ = String of length 14
// CPF = String of length 11
// ID = CNPJ or CPF
// RAW_ID = ID before special characters cleanup
// DIGIT = Number from 0 to 9
 
const repeatedNumberRegex = /^(.)\1+$/
 
const mapIndexed = addIndex(map)
 
const weightMasks = {
  // for cpf
  9: [10, 9, 8, 7, 6, 5, 4, 3, 2],
  10: [11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
  // for cnpj
  12: [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2],
  13: [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2],
}
 
// String -> String
const clean = replace(/[^\d]+/g, '')
 
// [String] -> ID -> Boolean
const hasOnlyOneNumber = subject => repeatedNumberRegex.test(subject)
 
// ID -> Boolean
const hasValidForm = complement(either(isEmpty, hasOnlyOneNumber))
 
// [Number] -> ID -> DIGIT
const generateDigitWithMask = mask => pipe(
  take(length(mask)),
  split(''),
  mapIndexed((el, i) => el * mask[i]),
  sum,
  multiply(__, 10),
  modulo(__, 11),
  when(
    equals(__, 10),
    subtract(10, __)
  )
)
 
// Number -> ID -> DIGIT
const digit = index => pipe(
  nth(index),
  Number
)
 
// Number -> ID -> Boolean
const validateDigit = index => subject =>
  apply(
    equals,
    ap([
      digit(index),
      generateDigitWithMask(weightMasks[index], index),
    ], [subject])
  )
 
// [Number] -> ID -> [Number] -> ID -> Boolean
const validateDigits = pipe(
  ap([validateDigit]),
  allPass
)
 
// [Number] -> ID -> Boolean
const validateId = indexes => pipe(
  toString,
  clean,
  both(hasValidForm, validateDigits(indexes))
)
 
// ID -> Boolean
export const cnpj = validateId([12, 13])
 
// ID -> Boolean
export const cpf = validateId([9, 10])