пятница, 20 мая 2011 г.

C++: Функции проверки ИНН, ОГРН и ОГРНИП

     Вам приходилось писать тех.задание для программистов? Нет? Ах, вы сами пишите программы :-) ... я тоже, конечно, нет, нет, да что-нибудь напишу. Но в основной своей массе мне приходиться объяснять нашим разработчикам ЧТО же нам надо (иногда доходит до абсурда - говоришь уже конкретный программный код :-)) Вот и в этот раз возникла задача описать алгоритмически процесс проверки ИНН юридических и физических лиц, а так же ОГРН юридических лиц и ОГРНИП физических лиц - индивидуальных предпринимателей. Задача, по-сути, тривиальная и, к тому же, алгоритмы проверки найти в интернете не составляет труда. Тем не менее, что бы лишний раз удостовериться в корректности найденных алгоритмов (и иметь рабочую программную версию) было принято решение предварительно реализовать их самостоятельно на языке C++. Что из этого получилось - смотрите ниже.
     Конечно, я осознаю, что это реализация не в духе "чистого" C++, поэтому любые комментарии и оптимизации приветствуются :-).
     Итак, вашему вниманию предлагаю алгоритмы проверки ИНН, ОГРН, ОГРНИП (дабы вы лишний раз не утруждали себя поиском ;-)), а так же функции их практической реализации.
     В процессе написания функций проверки потребовалось преобразование типов из string в int и double. Встроенных инструментов для этих целей C++ не предлагает, кроме средств C библиотеки: atoi() и т.п. Поэтому была написана дополнительная функция неявного преобразования (посредством stringstream), возможно решение не очень изящное, но вполне рабочее :).

template <class T> T str_to_dig(string str)
{
    stringstream sstr;
    T dig_val;
    if (str.empty())
        return 0;
    sstr << str;
    sstr >> dig_val;
    return dig_val;
}

     Проверка ИНН юридического лица.
     Алгоритм проверки:
1. Необходимо вычислить контрольную сумма ИНН с весовыми коэффициентами: [2,4,10,3,5,9,4,6,8,0] (для этого каждый разряд ИНН умножается на соответствующий коэффициент: 1-ый разряд на 2, 2-ой на 4 и т.д.).
2. Затем вычисляется контрольное число, как остаток от деления контрольной суммы на 11.
3. В случае, если контрольное число больше 9, то контрольное число необходимо дополнительно вычислить как остаток от деления контрольного числа (вычисленного в п.2) на 10.
4. Полученное контрольное число сравнивается с 10 знаком ИНН, в случае их равенства - ИНН считается корректным.

     Функция проверки:
int inn10_check(string *inn)
{
    //Ошибки возвращаемые функцией:
    // 1 - неверная длина ИНН
    // 2 - в номере ИНН некорректный символ (отличный от 0..9)
    // 3 - ИНН не правильный
    // В случае корректного ИНН возвращается 0
    if (inn->length() != 10) return 1;
    int summ(0), ost(0), weights[10] = {2,4,10,3,5,9,4,6,8,0};
    string digit;
    for (int i = 0; i < 10; i++)
    {
        if (!isdigit(inn->at(i))) return 2;
        digit = inn->at(i);
        summ += str_to_dig<int> (digit)*weights[i];
    }
    ost = summ%11;
    if (ost > 9) ost = ost%10;
    if (ost == str_to_dig<int> (digit)) return 0;
    return 3;
}

Проверка ИНН физического лица 
     Алгоритм проверки:
     Проверка ИНН физического лица осуществляется на основе вычисления 2-х контрольных чисел.
1. Подсчитывается контрольная сумма для первых 11-ти знаков ИНН со следующими весовыми коэффициентами: [7,2,4,10,3,5,9,4,6,8,0] (для этого каждый разряд ИНН умножается на соответствующий коэффициент: 1-ый разряд на 7, 2-ой на 2 и т.д.).
2. Вычисляется первое контрольное число (КЧ1) как остаток от деления контрольной суммы (из п.1) на 11.
3. В случае, если КЧ1 больше 9, то вычисляется новое КЧ1 как остаток от деления КЧ1 на 10.
4. Подсчитывается контрольная сумма для всех 12-ти знаков ИНН со следующими весовыми коэффициентами [3,7,2,4,10,3,5,9,4,6,8,0].
5. Вычисляется второе контрольное число (КЧ2) как остаток от деления контрольной суммы (из п.4) на 11.
6. В случае, если КЧ2 больше 9, то вычисляется новое КЧ2 как остаток от деления КЧ2 на 10.
7. Полученные контрольные числа КЧ1 и КЧ2 сравниваются с 11 и 12 знаками ИНН соответственно. В случае их равенства - ИНН корректен.

     Функция проверки:
int inn12_check(string *inn)
{
    //Ошибки возвращаемые функцией:
    // 1 - неверная длина ИНН
    // 2 - в номере ИНН некорректный символ (отличный от 0..9)
    // 3 - ИНН не правильный
    // В случае корректного ИНН возвращается 0
    if (inn->length() != 12) return 1;
    int summ(0), ost1(0), ost2(0);
    int weights1[11] = {7,2,4,10,3,5,9,4,6,8,0}; 
    int weights2[12] = {3,7,2,4,10,3,5,9,4,6,8,0};
    string digit;
    for (int i = 0; i < 12; i++)
    {
        if (!isdigit(inn->at(i))) return 2;
        digit = inn->at(i);
        summ += str_to_dig<int> (digit)*weights2[i];
    }
    ost2 = summ%11;
    if (ost2 > 9) ost2 = ost2%10;
    summ = 0;
    for (int i = 0; i < 11; i++)
    {
        digit = inn->at(i);
        summ += str_to_dig<int> (digit)*weights1[i];
    }
    ost1 = summ%11;
    if (ost1 > 9) ost1 = ost1%10;
    if (ost1 == str_to_dig<int> (digit))
    {
        digit = inn->at(11);
        if (ost2 == str_to_dig<int> (digit)) return 0;
    }
    return 3;
}

Проверка ОГРН юридического лица
     Алгоритм проверки:
1. Вычисляется контрольное число как остаток от деления числа состоящего из первых 12 знаков ОГРН на 11.
2. В случае, если контрольное число больше 9, то вычисляется новое контрольное число как остаток от деления контрольного числа (из п. 1) на 10.
3. Контрольное число сравнивается с 13 знаком ОГРН, в случае их равенства ОГРН считается корректным.

     Функция проверки:
int ogrn13_check(string *ogrn)
{
    //Ошибки возвращаемые функцией:
    // 1 - неверная длина ОГРН
    // 2 - в номере ОГРН некорректный символ (отличный от 0..9)
    // 3 - ОГРН не правильный
    // В случае корректного ОГРН возвращается 0
    // ВНИМАНИЕ: в функции используется тип double!
    if (ogrn->length() != 13) return 1;
    double d1(0);
    int ost(0);
    string digit;
    for (int i=0; i < 13; i++)
    {
        if (!isdigit(ogrn->at(i))) return 2;
    }
    d1 = str_to_dig<double> (ogrn->substr(0,12));
    ost = d1 - (floor(d1/11))*11;
    if (ost > 9) ost = ost%10;
    digit = ogrn->at(12);
    if (ost == str_to_dig<int> (digit)) return 0;
    return 3;
}

Проверка ОГРНИП физического лица - индивидуального предпринимателя
     Алгоритм проверки:
1. Вычисляется контрольное число как остаток от деления числа состоящего из первых 14 знаков ОГРНИП на 13.
2. В случае, если контрольное число больше 9, то вычисляется новое контрольное число как остаток от деления контрольного числа (из п. 1) на 10.
3. Контрольное число сравнивается с 15 знаком ОГРНИП, в случае их равенства ОГРНИП считается корректным.

     Функция проверки:
int ogrn15_check(string *ogrn)
{
    //Ошибки возвращаемые функцией:
    // 1 - неверная длина ОГРНИП
    // 2 - в номере ОГРНИП некорректный символ (отличный от 0..9)
    // 3 - ОГРНИП не правильный
    // В случае корректного ОГРНИП возвращается 0
    // ВНИМАНИЕ: в функции используется тип double!
    if (ogrn->length() != 15) return 1;
    double d1(0);
    int ost(0);
    string digit;
    for (int i=0; i < 15; i++)
    {
        if (!isdigit(ogrn->at(i))) return 2;
    }
    d1 = str_to_dig<double> (ogrn->substr(0,14));
    ost = d1 - (floor(d1/13))*13;
    if (ost > 9) ost = ost%10;
    digit = ogrn->at(14);
    if (ost == str_to_dig<int> (digit)) return 0;
    return 3;
}

     Как видно из функций проверки ОГРН/ОГРНИП, алгоритмы не сильно разнятся друг от друга (в отличии от алгоритмов проверки ИНН), поэтому резонно возникает вопрос - "Можно ли их объединить в одну функцию?". Все можно, если очень захотеть :-). Свой вариант такой функции вам предложить готов и я:
//обобщенная функция проверки ОГРН/ОГРНИП, в качестве второго
//аргумента принимает значение характеризующее тип проверяемого
//регистрационного номера: 0 - ОГРН, 1 - ОГРНИП
//по-умолчанию проверяется ОГРН
int ogrn_check(string *, int ogrn_type = 0);

int ogrn_check(string *ogrn, int ogrn_type)
{
    //Ошибки возвращаемые функцией:
    // 1 - не верно указан тип проверяемого ОГРН/ОГРНИП
    // 2 - неверная длина ОГРН/ОГРНИП
    // 3 - в номере ОГРН/ОГРНИП некорректный символ (отличный от 0..9)
    // 4 - ОГРН/ОГРНИП не правильный
    // В случае корректного ОГРН/ОГРНИП возвращается 0
    // ВНИМАНИЕ: в функции используется тип double!
    unsigned int ogrn_length(13), ogrn_div(11), ost(0);
    double d1;
    string digit;
    switch (ogrn_type)
    {
        case 0:
                    break;
        case 1:
                    ogrn_length = 15;
                    ogrn_div = 13;
                    break;
        default:    return 1;
    }
    if (ogrn->length() != ogrn_length) return 2;
    for (unsigned int i = 0; i < ogrn_length; i++)
    {
        if (!isdigit(ogrn->at(i))) return 3;
    }
    d1 = str_to_dig<double> (ogrn->substr(0,(ogrn_length-1)));
    ost = d1 - (floor(d1/ogrn_div))*ogrn_div;
    if (ost > 9) ost = ost%10;
    digit = ogrn->at(ogrn_length-1);
    if (ost == str_to_dig<unsigned int>(digit)) return 0;
    return 4;
}

     В заключении следует указать необходимые заголовочные файлы, требуемые для указанных функций, как говорится "на всякий случай" :-):
#include <iostream>
#include <string>
#include <sstream>
#include <cmath>

     На этом все - спасибо всем за внимание ;-).

2 комментария:

  1. СПА-СИ-БО!! Огромнейшее!! Считается ,что в интернете масса готового кода по расчету ИИН, а вот и нетушкки!! Я вас аж на 3-й странице поиска нашла! И что б на С++ было. Не пропадайте! Вы нам очень нужны!

    ОтветитьУдалить