Добрый день, Коллеги!
Возникла задача, понять и вытащить алгоритм проверки контрагентов в ФНС из типовой регламентированной отчетности. Алгоритм проверки на статус регистрации в ФНС и верность заполнения КПП. Задача развилась в идею вывести статус проверки ФНС в список справочника Контрагентов. Так же необходимо проверять статус ежедневно, чтобы держать базу с актуальными данными.
В современных конфигурациях (УТ11, КА2, ERP) данный алгоритм реализован на уровне типового решения, в старых конфигурациях в роде моей Комплексной Автоматизации 1.1 нет такой возможности, поэтому можно в путь пустить – творчество.
Программирование ведётся на конфигурации Комплексная Автоматизация 1.1.96.3 в режиме совместимости 8.2.13 (думаю, подойдёт к УТ10.3 и УПП), обычные формы, но в управляемых как по мне было бы на много проще, но с другой стороны в тех конфигурациях велосипед уже есть.
Что вы узнаете, прочитав данную статью:
- Как получить данные из ФНС о контрагенте - http://npchk.nalog.ru
- Как вывести статус ФНС в табличную часть формы
- Создание в таблице формы колонки – картинки
- Как выделить цветом данные при выводе строк в таблице обычной формы и другое
Приступим.
Создаем новые объекты конфигурации
Сейчас все помешаны на расширениях, но не все могут этим воспользоваться, по этому представляю список доработок.
Общий список новых объектов:
- Общий модуль – «котПроверкаКонтрагентов»
- Картинка – «котСтатусыФНС»
- Регистр сведений «котКонтрагентыСтатусФНС»
- Регламентное задание «котЕжедневнаяПроверкаКонтрагентов»
Общий модуль – «котПроверкаКонтрагентов»
Создаем новый объект конфигурации – «общий модуль».
В моём случае название модуля – «котПроверкаКонтрагентов».
Настройки модуля:
///////////////////////////////////////////////////////////////////////////////////
// Данная разработка скачана из публикации:
// //infostart.ru/public/755141/
// Котов Д.В. (//infostart.ru/profile/564942/)
// Дата начала разработки - 30.01.2018
// На основание бесплатного сервиса (http://npchk.nalog.ru)
// Важно понимать, что на данный момент сервис работает в тестовом режиме
///////////////////////////////////////////////////////////////////////////////////
Процедура ПроверкаКонтрагентов() Экспорт
ДанныеПолучены = ЛОЖЬ;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| СтатусыФНС.Контрагент
|ИЗ
| РегистрСведений.котКонтрагентыСтатусФНС КАК СтатусыФНС
|ГДЕ
| СтатусыФНС.Статус = ""1""";
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
УстановитьФильтр = ЛОЖЬ;
Иначе
УстановитьФильтр = ИСТИНА;
КонтрагентыКоторыеНеДействуют = Результат.Выгрузить();
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Контрагенты.Ссылка КАК Контрагент,
| Контрагенты.ИНН,
| Контрагенты.КПП,
| ВЫРАЗИТЬ("""" КАК СТРОКА(100)) КАК Состояние,
| &ТекущайДата КАК Дата
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ИНН <> """"" +
?(УстановитьФильтр,"И НЕ Контрагенты.Ссылка В(&ГруппаКонтрагентов)","");
Запрос.УстановитьПараметр("ТекущайДата",ТекущаяДата());
Запрос.УстановитьПараметр("ГруппаКонтрагентов",КонтрагентыКоторыеНеДействуют);
ДанныеКонтрагента = Запрос.Выполнить().Выгрузить();
ПолучитьРезультатПроверкиВебСервисом(ДанныеКонтрагента,ДанныеПолучены);
Если ДанныеПолучены Тогда
Для Каждого Строка Из ДанныеКонтрагента Цикл
НаборЗаписей = РегистрыСведений.котКонтрагентыСтатусФНС.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Контрагент.Установить(Строка.Контрагент);
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() > 0 Тогда
ЗаписьТекущая = НаборЗаписей.Получить(0);
Пропускать = ЗаписьТекущая.Статус = "1";
Иначе
Пропускать = ЛОЖЬ;
КонецЕсли;
Если НЕ Пропускать Тогда
НаборЗаписей.Очистить();
НаборЗаписей.Записать();
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Контрагент = Строка.Контрагент;
НоваяЗапись.Статус = Строка.Состояние;
НаборЗаписей.Записать(ИСТИНА);
КонецЕслИ;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Функция ПроверитьКонтрагентаПоСсылке(Контрагент,Дата = Неопределено) Экспорт
Если Дата = Неопределено Тогда
Дата = ТекущаяДата();
КонецЕсли;
Если Контрагент.ИНН <> "" Тогда
ДанныеПолучены = ЛОЖЬ;
ДанныеКонтрагента = Новый ТаблицаЗначений;
ДанныеКонтрагента.Колонки.Добавить("Контрагент", Новый ОписаниеТипов("СправочникСсылка.Контрагенты"));
ДанныеКонтрагента.Колонки.Добавить("ИНН", Новый ОписаниеТипов("Строка",,Новый КвалификаторыСтроки(12)));
ДанныеКонтрагента.Колонки.Добавить("КПП", Новый ОписаниеТипов("Строка",,Новый КвалификаторыСтроки(9)));
ДанныеКонтрагента.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата",,,Новый КвалификаторыДаты(ЧастиДаты.Дата)));
ДанныеКонтрагента.Колонки.Добавить("Состояние", Новый ОписаниеТипов("Строка",,Новый КвалификаторыСтроки(100)));
ТекущийКонтрагент = ДанныеКонтрагента.Добавить();
ТекущийКонтрагент.Контрагент = Контрагент;
ТекущийКонтрагент.ИНН = Контрагент.ИНН;
ТекущийКонтрагент.КПП = Контрагент.КПП;
ТекущийКонтрагент.Дата = Дата;
ПолучитьРезультатПроверкиВебСервисом(ДанныеКонтрагента,ДанныеПолучены);
Если ДанныеПолучены Тогда
Ответ = ДанныеКонтрагента[0].Состояние;
СостояниеФНС = СостояниеНаОсновеОтветаСервиса(Контрагент,Ответ);
Иначе
СостояниеФНС = "Сервер ФНС - не доступен, попробуйте позже";
КонецЕсли;
Иначе
СостояниеФНС = "У контрагента не заполнен ИНН";
КонецЕсли;
Возврат СостояниеФНС;
КонецФункции
Процедура ПолучитьРезультатПроверкиВебСервисом(ДанныеКонтрагентов, ЕстьДоступКВебСервисуФНС)
КоличествоКонтрагентов = ДанныеКонтрагентов.Количество();
ЕстьДоступКВебСервисуФНС = ЕстьДоступКВебСервисуФНС();
Если КоличествоКонтрагентов = 0
ИЛИ НЕ ЕстьДоступКВебСервисуФНС
Тогда
Возврат;
КонецЕсли;
Прокси = ПолучитьWSПрокси();
ПространствоИмен = "http://ws.unisoft/FNSNDSCAWS2/Request";
РазмерПорции = 10000; //возможно ограничения, в типовой так написано, максимум в запросе 10 000 контрагентов
КоличествоЗапросов = ?(КоличествоКонтрагентов % РазмерПорции = 0, КоличествоКонтрагентов / РазмерПорции, Цел(КоличествоКонтрагентов / РазмерПорции) + 1);
Для НомерПорции = 1 По КоличествоЗапросов Цикл
МинимальныйНомерКонтрагента = Мин(РазмерПорции * (НомерПорции - 1), КоличествоКонтрагентов);
МаксимальныйНомерКонтрагента = Мин(РазмерПорции * НомерПорции, КоличествоКонтрагентов) - 1;
WSЗапрос = Прокси.ФабрикаXDTO.Создать(Прокси.ФабрикаXDTO.Тип(ПространствоИмен, "NdsRequest2"));
Для ИндексТекущегоКонтрагента = МинимальныйНомерКонтрагента По МаксимальныйНомерКонтрагента Цикл
ДанныеКонтрагента = ДанныеКонтрагентов[ИндексТекущегоКонтрагента];
ДобавитьКонтрагентаВЗапросКСервису(WSЗапрос, Прокси, ПространствоИмен, ДанныеКонтрагента);
КонецЦикла;
Если WSЗапрос.NP.Количество() = 0 Тогда
Продолжить;
КонецЕсли;
ЕстьДоступКВебСервисуФНС = ЕстьДоступКВебСервисуФНС();
Попытка
NdsResponse = Прокси.NdsRequest2(WSЗапрос);
Исключение
ЗаписьЖурналаРегистрации("Проверка контрагентов.NdsRequest",УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
NdsResponse = Неопределено; // Страхуемся от ошибки "Timeout wаs reached".
КонецПопытки;
Если NdsResponse <> Неопределено Тогда
ОбработатьОтветСервиса(NdsResponse, ДанныеКонтрагентов, МинимальныйНомерКонтрагента, МаксимальныйНомерКонтрагента);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура ДобавитьКонтрагентаВЗапросКСервису(WSЗапрос, Прокси, ПространствоИмен, ДанныеКонтрагента)
Попытка
WSКонтрагент = Прокси.ФабрикаXDTO.Создать(Прокси.ФабрикаXDTO.Тип(ПространствоИмен, "NdsRequest2_NP"));
WSКонтрагент.INN = ДанныеКонтрагента.ИНН;
//ИП - проверяются БЕЗ указания КПП, иначе вернётся ошибка с кодом "10"
Если ДанныеКонтрагента.Контрагент.ЮрФизЛицо = Перечисления.ЮрФизЛицо.ЮрЛицо Тогда
КонтрагентЮР = ИСТИНА;
Иначе
КонтрагентЮР = ЛОЖЬ;
КонецЕсли;
Если ЗначениеЗаполнено(ДанныеКонтрагента.КПП) И КонтрагентЮР Тогда
WSКонтрагент.KPP = ДанныеКонтрагента.КПП;
Иначе
WSКонтрагент.KPP = "";
КонецЕсли;
Если ЗначениеЗаполнено(ДанныеКонтрагента.Дата)Тогда
WSКонтрагент.DT = ДатаСтрокой(ДанныеКонтрагента.Дата);
КонецЕсли;
WSЗапрос.NP.Добавить(WSКонтрагент);
Исключение
ФиксацияОшибкиВЖурналеРегистрации("Проверка контрагентов. Ошибка в заполнение запроса по контрагенту код: " +
ДанныеКонтрагента.Контрагент.Код,ИнформацияОбОшибке());
ДанныеКонтрагента.Состояние = "W"; //Ошибка в данных
КонецПопытки;
КонецПроцедуры
Процедура ОбработатьОтветСервиса(NdsResponse, ДанныеКонтрагентовДляПроверкиСервисом, МинимальныйНомерКонтрагента, МаксимальныйНомерКонтрагента)
ИндексТекущегоОтвета = 0;
Для ИндексТекущегоКонтрагента = МинимальныйНомерКонтрагента По МаксимальныйНомерКонтрагента Цикл
ДанныеКонтрагента = ДанныеКонтрагентовДляПроверкиСервисом[ИндексТекущегоКонтрагента];
Попытка
ОтветПоКонтрагенту = NdsResponse.NP[ИндексТекущегоОтвета];
СостояниеВОтвете = ОтветПоКонтрагенту.State;
ДанныеКонтрагента.Состояние = СостояниеВОтвете;
Исключение
ФиксацияОшибкиВЖурналеРегистрации("Проверка контрагентов.Обработка ответа от веб-сервиса",ИнформацияОбОшибке());
КонецПопытки;
ИндексТекущегоОтвета = ИндексТекущегоОтвета + 1;
КонецЦикла;
КонецПроцедуры
Функция ЕстьДоступКВебСервисуФНС()
Возврат ПолучитьWSПрокси() <> Неопределено;
КонецФункции
Функция ПолучитьWSПрокси()
WSПрокси = Неопределено;
Попытка
WSПрокси = ОбщегоНазначения.WSПрокси(
"http://npchk.nalog.ru/FNSNDSCAWS_2?wsdl",
"http://ws.unisoft",
"FNSNDSCAWS2",
"FNSNDSCAWS2_Port",
Неопределено,
Неопределено,
120);
Исключение
ФиксацияОшибкиВЖурналеРегистрации("Проверка контрагентов. Ошибка подключени WSПрокси.",ИнформацияОбОшибке())
КонецПопытки;
Возврат WSПрокси;
КонецФункции
Функция СостояниеНаОсновеОтветаСервиса(ДанныеКонтрагента, Ответ) Экспорт
Если Ответ = "0" Тогда
Состояние = "Контрагент есть в базе ФНС";
ИначеЕсли Ответ = "1" Тогда
Если СтрДлина(ДанныеКонтрагента.ИНН) = 12 Тогда
Состояние = "Не действует";
Иначе
Состояние = "Не действует или изменен КПП";
КонецЕсли;
ИначеЕсли Ответ = "3" Тогда
Состояние = "КПП не соответствует данным базы ФНС";
ИначеЕсли Ответ = "4" Тогда
Состояние = "Контрагент отсутствует в базе ФНС";
ИначеЕсли Ответ = "5" ИЛИ Ответ = "6" ИЛИ Ответ = "8" Тогда
Состояние = "Ошибка. Некорректный ИНН";
ИначеЕсли Ответ = "7" ИЛИ Ответ = "9" Тогда
Состояние = "Ошибка. Некорректный КПП";
ИначеЕсли Ответ = "11" ИЛИ Ответ = "12" Тогда
Состояние = "Ошибка. Некорректная дата проверки";
ИначеЕсли Ответ = "10" Тогда
Состояние = "Ошибка. ИП заведено как юридическое лицо";
ИначеЕсли Ответ = "W" Тогда
Состояние = "Возникла ошибка заполнения данных контрагента";
КонецЕсли;
Возврат Состояние;
КонецФункции
Функция НомерКартинкиСтатуса(Статус) Экспорт
Если Статус = "0" Тогда
СтатусКартинка = 3;
ИначеЕсли Статус = "1" Тогда
СтатусКартинка = 2;
Иначе
СтатусКартинка = 1;
КонецЕсли;
Возврат СтатусКартинка;
КонецФункции
Функция ДатаСтрокой(Дата)
Результат = Неопределено;
Если ТипЗнч(Дата) = Тип("Строка") Тогда
Результат = Дата;
ИначеЕсли ТипЗнч(Дата) = Тип("Дата") Тогда
Результат = Формат(Дата, "ДФ=dd.MM.yyyy");
КонецЕсли;
Возврат Результат;
КонецФункции
Процедура ФиксацияОшибкиВЖурналеРегистрации(ИмяСобытия,ИнформацияОбОшибке)
ЗаписьЖурналаРегистрации(НСтр("ru = '"+ИмяСобытия+"'"),УровеньЖурналаРегистрации.Ошибка,,,ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
КонецПроцедуры
Процедура ОчиститьРегистрСведенийСДаннымиПроверки() Экспорт
НаборЗаписей = РегистрыСведений.котКонтрагентыСтатусФНС.СоздатьНаборЗаписей();
НаборЗаписей.Записать();
КонецПроцедуры
Картинка – «котСтатусыФНС»
Создаем новый объект – Общие картинки, название «котСтатусыФНС».
Подгружаем картинку - <<< скачайте
Описание иконок статуса ФНС:
Регистр сведений – «котКонтрагентыСтатусФНС»
Создаем регистр сведений – «котКонтрагентыСтатусФНС»
Основные
Периодичность: Непериодический
Режим записи: Независимый
Данные
Измерение – Контрагент – тип СправочникСсылка.Контрагенты
Ресурс – Статус – тип Строка (150)
Настройка прав за вами!
Регламентное задание «котЕжедневнаяПроверкаКонтрагентов»
Создаем объект конфигурации регламентное задание «котЕжедневнаяПроверкаКонтрагентов».
Имя метода – тыкаем по «лупе» и автоматически создаем процедуру в нашем модуле «котПроверкаКонтрагентов».
В созданную процедуру дописываем код вызова процедуры проверки контрагентов:
Процедура котЕжедневнаяПроверкаКонтрагентов() Экспорт
ПроверкаКонтрагентов();
КонецПроцедуры
Доработка объектов конфигурации
Мы определились с новыми объектами, переходим к изменению объектов конфигурации.
Общий список изменений:
- Справочник – «Контрагенты», ФормаСписка
- Справочник – «Контрагенты», ФормаВыбора
Необходимо убедиться, что выше перечисленные объекты сняты с «замка».
Общий принцип доработки обоих форм (повторяем и для ФормаСписка и для ФормаВыбора)
Необходимо создать колонку «котСтатусФНС» в таблице формы – «СправочникСписок».
ВНИМАНИЕ! Не забывайте что 1С поддерживает копирование, да же колонок из одной формы в другую.
Добавляем колонку и настраиваем значение свойств:
Так же задаем свойства полю ввода:
Находим процедуру «ПриВыводеСтроки» событий таблицы формы «СправочникСписок» и проваливаемся в неё если есть или проваливаемся чтобы создать искомую:
В процедуру добавляем код:
//Котов Д.В. +++ 31.01.2018
//Так же можно выделять цветом всю строку целиком, тут ваш полёт фантазии не ограничен
Если НЕ ОформлениеСтроки.ДанныеСтроки.ЭтоГруппа Тогда
оформлениеСтроки.Ячейки.котСтатусФНС.ОтображатьТекст = ЛОЖЬ;
оформлениеСтроки.Ячейки.котСтатусФНС.ОтображатьФлажок = ЛОЖЬ;
оформлениеСтроки.Ячейки.котСтатусФНС.ОтображатьКартинку = ИСТИНА;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| котКонтрагентыСтатусФНС.Статус
|ИЗ
| РегистрСведений.котКонтрагентыСтатусФНС КАК котКонтрагентыСтатусФНС
|ГДЕ
| котКонтрагентыСтатусФНС.Контрагент = &Контрагент";
Запрос.УстановитьПараметр("Контрагент", ОформлениеСтроки.ДанныеСтроки.Ссылка);
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
ОформлениеСтроки.Ячейки.котСтатусФНС.ИндексКартинки = 0;
Иначе
Выборка = Результат.Выбрать();
Выборка.Следующий();
ОформлениеСтроки.Ячейки.котСтатусФНС.ИндексКартинки = котПроверкаКонтрагентов.НомерКартинкиСтатуса(Выборка.Статус);
КонецЕсли;
КонецЕсли;
//Котов Д.В. --- 31.01.2018
Проверяем работу
Для проверки работы, так удобнее, нам потребуется простая обработка с двумя кнопками.
Одна "Проверить контрагентов" будет вызывать процедуру –
котПроверкаКонтрагентов. котЕжедневнаяПроверкаКонтрагентов();
Вторая кнопка вызовет процедуру –
котПроверкаКонтрагентов. ОчиститьРегистрСведенийСДаннымиПроверки();
Запустив обработку, нажимаем кнопку «Проверить контрагентов» и ждём.
ВНИМАНИЕ! Выполнение кода может ни к чему не привести, так как сервер ФНС не стабилен! Может откликнуться только со 2 раза… 10…100 раза… или сразу. По этому если регистр сведений не заполнился, жмем ещё раз!
Когда видим, что регистр сведений успешно заполнен:
Открываем смело справочник контрагентов и видим радостную картинку:
Запускаем регламентное задание
Для запуска регламентного задания, вам потребуется - КонсольЗаданий:
Можно скачать с ИТС: https://its.1c.ru/db/metod8dev/content/3752/hdoc
Так же расширенная версия консоли заданий включена в "Инструменты разработчика": http://devtool1c.ucoz.ru/
Настраиваем задание, регулярность выполнение ваше пожелания:
Резюме
Функционал можно расширить, в моём случае помимо фиксации статуса ФНС будет так же фиксация в разрезе документов реализации и заказов, чтобы можно было подсвечивать заказы и реализации как в новых конфигурациях в зависимости от проблемности контрагента. Под проблемностью мы конечно понимает либо нет регистрации в ФНС или содержит ошибки КПП.
Кто внимательный то мог заметить, что в модуле есть функция ПроверитьКонтрагентаПоСсылке(Контрагент,Дата = Неопределено), данная функция позволяет по ссылке контрагента проверить его статус в ФНС, ответ ввиде строковой расшифровки, успешная – «Контрагент есть в базе ФНС». Если дата не указана, проверка производиться на текущую дату, иначе можно посмотреть допустим на момент сделки.
Так же вы можете расшифровать статус ФНС (он числовой от 0 до 12) с помощью функции СостояниеНаОсновеОтветаСервиса(ДанныеКонтрагента, Ответ).
Не забывайте важный момент – сервис работает в тестовом режиме – а это может быть всё что угодно!
Удачного кодинга!
Посткриптум
Наверное многие так же как я в режиме мистической совместимости 8.2.13 ведёт разработку и на управляемых формах. Так вот, всё, что я описывал в пункте «Доработка объектов конфигурации», намного проще реализуется не в таблице обычной формы, а в динамическом списке управляемой формы. Смотрите сами как легко (без кода почти) добавить колонку со статусом.
- Корректируем запрос добавив наш статус:
ВЫБРАТЬ
Контрагенты.Код,
Контрагенты.Наименование,
Контрагенты.ИНН,
Контрагенты.КПП,
Контрагенты.НаименованиеПолное,
ВЫБОР
КОГДА котКонтрагентыСтатусФНС.Статус ЕСТЬ NULL
ТОГДА 0
КОГДА котКонтрагентыСтатусФНС.Статус = "0"
ТОГДА 3
КОГДА котКонтрагентыСтатусФНС.Статус = "1"
ТОГДА 2
ИНАЧЕ 1
КОНЕЦ КАК ФНС
ИЗ
Справочник.Контрагенты КАК Контрагенты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.котКонтрагентыСтатусФНС КАК котКонтрагентыСтатусФНС
ПО Контрагенты.Ссылка = котКонтрагентыСтатусФНС.Контрагент
- Перетаскиваем новую колонку в таблицу на форме и настраиваем ряд свойств:
Результат работы:
Как по мне динамический список и СКД это просто магия и песня!
Предлагаю вашему внимаю мою процедуру программного создания динамического списка:
Описание файлов к публикации
Зачем тут файлы? Ну возможно, кому-то лень всё это повторить и проще накатить сравнением объединением готовый продукт, который легко ставится на КА 1.1, по всей видимости встанет и на УПП 1.3, УТ 10.3.
А также это единственная материальная благодарность автора, от которой не откажусь ;)
ПроверкаКонтрагентов.cf – готовый CF, содержащий рабочий алгоритм, описанный в данный статье, устанавливается сравнением объединением (кто умеет).
котИмитацияРаботыРегламетногоЗадания.epf – обработка, описанная выше.
котКонтрагенты.epf – обработка содержит пример работы динамического списка из посткриптума.