Валидация форм React, часть 3. Катомные методы в Yup, валидация данных юридических лиц

Материалы к видео

Валидация данных юридического лица — здесь набор методов для валидации ИНН, КПП, ОГРН(ИП), ОКПО… в реализации для typescript.

Видео 1, — простая валидация формы регистрации с подтверждение пароля.

Видео 2 — валидация файлов, изменения схемы валидации в зависимости от данных полей формы.

Генератор тестовых данных юр. лица

Декларирование методов Yup в typescript

Нужно запомнить, что в typescipt первым аргументом метода будет this. Тип у this будет такой же как у ассоциированной с ним схемы.

// В данном методе this - это StringShema
function checkPayment(this: yup.StringSchema, ref: any, msg: string) {
  return this.test({
    name: 'checkPaymentMethod',
    exclusive: false,
    message: msg || 'accountNumber',
    params: { reference: ref.path},
    test: function(value: string){
      return checkPaymetAccount(value, this.resolve(ref))
    }
  })
}

Добавление метода будет принимать тип схемы нашего нового метода:

yup.addMethod<yup.StringSchema>(yup.string, 'checkPaymentMethod', checkPayment);

А перед всем этим мы должны задекларировать модуль, добавив типы методов соответствующих интерфейсов:

declare module "yup" {
  interface StringSchema {
    checkPaymentMethod(ref: Ref): StringSchema;
    checkCorrespondentMethod(ref: Ref): StringSchema;
  },
interface NumberSchema {
    ...
  },
  ......
}

Код в видео

App.js

import React from 'react';
import { Formik } from 'formik'
import yup, { checkBIK, isINNLegalEntity } from './validation'
import './App.scss'


function App() {

  const getError = (touched, error) => {
    return touched && error && <p key={error} className={'error'}>{error}</p>
  }

  const validationsSchema = yup.object().shape({
    bik: yup.string().test('BIK', 'БИК не валидный', checkBIK),
    accountNumber: yup.string().checkPaymentMethod(yup.ref('bik'), 'Сообщение'),
    inn: yup.string().test('INN', 'ИНН не валидный', isINNLegalEntity)
  })

  return (
    <div>
      <Formik
        initialValues={{
          bik: '',
          accountNumber: '',
          inn: ''
        }}
        validateOnBlur
        onSubmit={(values) => { console.log(values) }}
        validationSchema={validationsSchema}
      >
        {({ values, errors, touched, handleChange, handleBlur, isValid, handleSubmit, dirty }) => (
          <div className={`from`}>
            <p>
              <label htmlFor={`bik`}>БИК</label><br />
              <input
                className={'input'}
                type={`text`}
                name={`bik`}
                onChange={handleChange}
                onBlur={handleBlur}
                value={values.bik}
              />
            </p>
            {getError(touched.bik, errors.bik)}

            <p>
              <label htmlFor={`accountNumber`}>Р/C</label><br />
              <input
                className={'input'}
                type={`text`}
                name={`accountNumber`}
                onChange={handleChange}
                onBlur={handleBlur}
                value={values.accountNumber}
              />
            </p>
            {getError(touched.accountNumber, errors.accountNumber)}

            <p>
              <label htmlFor={`inn`}>ИНН</label><br />
              <input
                className={'input'}
                type={`text`}
                name={`inn`}
                onChange={handleChange}
                onBlur={handleBlur}
                value={values.inn}
              />
            </p>
            {getError(touched.inn, errors.inn)}




            <button
              disabled={!isValid || !dirty}
              onClick={handleSubmit}
              type={`submit`}
            >Отправить</button>
          </div>
        )}
      </Formik>
    </div>
  );
}

export default App;



// Р / с 40702810038050103285
// К / с 30101810400000000225
// БИК 044525225

validation.js

import * as yup from 'yup'

export const checkBIK = (value) => {
  if (!/^\d{9}$/.test(value)) return false
  const thirdPart = value.slice(-3);
  if (+thirdPart === 0 || +thirdPart === 1 || +thirdPart === 2) return true
  return +thirdPart >= 50 && +thirdPart < 1000
}

export const checkPaymetAccount = (value, bik) => {
  const valueToString = value ? value.toString() : '';
  if (checkBIK(bik)) {
    if (!/[^0-9]/.test(valueToString) && valueToString.length === 20) {
      const bikRs = bik.toString().slice(-3) + valueToString;
      let checkSum = 0;
      const coefficients = [7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1];
      for (var i in coefficients) {
        checkSum += coefficients[i] * (Number(bikRs[i]) % 10);
      }
      return checkSum % 10 === 0;
    }
  }
  return false;
};

export const isINNLegalEntity = (value) => {
  const valueToString = value ? value.toString() : ''
  const getN = (index) => (parseInt(valueToString[index]))
  if (valueToString.length === 10) {
    const dgt10 = ((
      2 * getN(0) + 4 * getN(1) + 10 * getN(2) +
      3 * getN(3) + 5 * getN(4) + 9 * getN(5) +
      4 * getN(6) + 6 * getN(7) + 8 * getN(8)
    ) % 11) % 10
    return (getN(9) === dgt10)
  }
  return false
}

function checkPayment(ref, msg) {
  return this.test({
    name: 'checkPaymentMethod',
    exclusive: false,
    message: msg || 'accountNumber not valid', // Сообщение об ошибке
    params: { reference: ref ? ref.path : undefined },
    test: function (value) {
      return checkPaymetAccount(value, this.resolve(ref)) // Функция валидации
    }
  })
}

yup.addMethod(yup.string, 'checkPaymentMethod', checkPayment)


export default yup