Как массово загрузить ИНН и СНИЛС для контрагентов-физлиц в 1С:БГУ 2.0 из файла?

Программист 1С v8.3 (Управляемые формы) Бухгалтерский учет Сфера услуг, туризм и социальный сектор
← На главную

Перед нами стоит задача: есть текстовый файл с тысячами строк вида "ФИО ИНН СНИЛС". Необходимо автоматизировать загрузку этих данных в «Бухгалтерию государственного учреждения, редакция 2.0», чтобы у существующих контрагентов-физлиц заполнились поля ИНН и СНИЛС. Ручной ввод такого объема данных займет много времени и неизбежно приведет к ошибкам, поэтому разработаем специальную обработку.

Рассмотрим несколько подходов к решению этой задачи, от самого простого до наиболее корректного, учитывающего все нюансы конфигурации и производительности.

Решение 1: Использование типовой обработки «Загрузка данных из табличного документа»

Прежде чем писать собственный код, всегда стоит проверить, нет ли в конфигурации стандартного инструмента для решения нашей задачи. И он есть! Во многих программах 1С, включая БГУ, встроена универсальная обработка Загрузка данных из табличного документа.

Преимущества этого подхода:

Этот способ является предпочтительным для пользователей и бухгалтеров, так как он не требует навыков программирования и является штатным, поддерживаемым фирмой "1С" механизмом.

Решение 2: Создание собственной обработки для загрузки

Если по каким-то причинам стандартный инструмент не подходит или требуется более сложная логика, можно написать собственную обработку. Проанализируем этот процесс по шагам, от наивного первого варианта до финального рабочего кода.

Шаг 1. Первоначальный вариант кода

Начнем с простого алгоритма: читаем файл построчно, для каждой строки ищем контрагента по ФИО и обновляем его данные.

В качестве отправной точки можно использовать следующий код:


Процедура ЗагрузитьДанныеИзФайла(ПутьКФайлу)
    Текст = Новый ТекстовыйДокумент;
    Текст.Прочитать(ПутьКФайлу);
    МассивСтрок = СтрРазделить(Текст.ПолучитьТекст(), Символы.ПС);
    
    Для Каждого Строка Из МассивСтрок Цикл
        Если ПустаяСтрока(Строка) Тогда
            Продолжить;
        КонецЕсли;
        
        // Разделителем может быть пробел или табуляция
        МассивЧастей = СтрРазделить(Строка, " "); 
        
        Если МассивЧастей.Количество() >= 3 Тогда
            ФИО = МассивЧастей[0] + " " + МассивЧастей[1] + " " + МассивЧастей[2]; // Пример для Ф И О
            ИНН = МассивЧастей[3];
            СНИЛС = МассивЧастей[4];

            // Проверка на существование контрагента
            СсылкаНаКонтрагента = Справочники.Контрагенты.НайтиПоНаименованию(ФИО);
            
            Если НЕ СсылкаНаКонтрагента.Пустая() Тогда
                // Обновляем существующую запись
                ОбъектКонтрагент = СсылкаНаКонтрагента.ПолучитьОбъект();
                ОбъектКонтрагент.ИНН = ИНН;
                ОбъектКонтрагент.СНИЛС = СНИЛС; // В БГУ реквизита СНИЛС у контрагента нет
                ОбъектКонтрагент.Записать();
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Этот код уже автоматизирует процесс, но у него есть серьезные недостатки, которые мы разберем дальше.

Шаг 2. Проблема полных тезок и ее решение

Главный недостаток первого варианта — использование метода НайтиПоНаименованию(). Этот метод возвращает ссылку на первый найденный элемент с указанным наименованием. Если в базе есть несколько контрагентов с одинаковым ФИО (полные тезки), данные всегда будут записываться только одному из них, что приведет к ошибкам в учете.

Правильный подход — использовать запрос для поиска. Запрос позволяет не просто найти, а посчитать, сколько элементов соответствует условию.

Усовершенствуем наш код, чтобы он пропускал загрузку для тезок и выводил их список для ручной обработки.


Функция НайтиКонтрагентовПоФИО(ФИО)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    Контрагенты.Ссылка
        |ИЗ
        |    Справочник.Контрагенты КАК Контрагенты
        |ГДЕ
        |    Контрагенты.Наименование = &ФИО";
    
    Запрос.УстановитьПараметр("ФИО", ФИО);
    
    Возврат Запрос.Выполнить().Выгрузить(); // Возвращаем таблицу с результатами
КонецФункции

// Основной цикл загрузки
ПропущенныеКонтрагенты = Новый СписокЗначений;
Для Каждого Строка Из МассивСтрок Цикл
    // ... получение ФИО, ИНН, СНИЛС из строки ...
    Данные = ПолучитьДанныеИзСтроки(Строка);

    НайденныеКонтрагенты = НайтиКонтрагентовПоФИО(Данные.ФИО);
    
    Если НайденныеКонтрагенты.Количество() = 1 Тогда
        // Найден ровно один контрагент - можно обновлять
        СсылкаНаКонтрагента = НайденныеКонтрагенты[0].Ссылка;
        // ... код записи данных ...
    ИначеЕсли НайденныеКонтрагенты.Количество() > 1 Тогда
        // Найдено несколько тезок, пропускаем
        ПропущенныеКонтрагенты.Добавить(Данные.ФИО);
    Иначе
        // Контрагент не найден, ничего не делаем
    КонецЕсли;
КонецЦикла;

// Выводим список пропущенных для ручной проверки
Если ПропущенныеКонтрагенты.Количество() > 0 Тогда
    Сообщить("Следующие контрагенты не были загружены из-за дублирования ФИО:");
    // ... цикл вывода списка ...
КонецЕсли;

Такой подход делает нашу обработку гораздо надежнее.

Шаг 3. Учет архитектуры «1С:Бухгалтерия государственного учреждения 2.0»

На этом этапе мы сталкиваемся с главной особенностью конфигурации БГУ. Личные данные физического лица (включая ИНН и СНИЛС) хранятся не в справочнике Контрагенты, а в связанном с ним справочнике ФизическиеЛица.

Элемент справочника Контрагенты имеет реквизит ЮридическоеФизическоеЛицо, который как раз и ссылается на соответствующий элемент справочника ФизическиеЛица. Именно объект физического лица нам и нужно изменять.

Еще один важный момент: после программной записи объекта данные не всегда сразу обновляются в динамических списках. Чтобы ИНН появился в списке контрагентов, нужно после записи связанного физлица пересохранить и сам объект контрагента.

Вот финальный, корректный код для БГУ 2.0:

Сначала функция поиска. Добавим в нее отбор по виду контрагента, чтобы искать только физических лиц.


Функция НайтиКонтрагентаПоФИО(ФИО)
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   КонтрагентыСправочник.Ссылка КАК КонтрагентСсылка
    |ИЗ
    |   Справочник.Контрагенты КАК КонтрагентыСправочник
    |ГДЕ
    |   КонтрагентыСправочник.Наименование = &ФИО
    |   И КонтрагентыСправочник.ВидКонтрагента = ЗНАЧЕНИЕ(Перечисление.ЮрФизЛицо.ФизЛицо)";
    
    Запрос.УстановитьПараметр("ФИО", ФИО);
    Возврат Запрос.Выполнить().Выбрать();
КонецФункции

Теперь процедура изменения данных, которая учитывает связь контрагента и физлица.


Процедура ИзменитьКонтрагента(КонтрагентСсылка, Данные)
    // Получаем объект контрагента
    ОбъектКонтрагент = КонтрагентСсылка.ПолучитьОбъект();
    
    // Через него получаем объект связанного физлица
    ОбъектФизЛицо = ОбъектКонтрагент.ЮридическоеФизическоеЛицо.ПолучитьОбъект();
    
    // Заполняем реквизиты у физлица
    ОбъектФизЛицо.ИНН = Данные.ИНН;
    ОбъектФизЛицо.СтраховойНомерПФР = Данные.СНИЛС; // Правильное имя реквизита для СНИЛС
    
    // Записываем сначала объект физлица
    ОбъектФизЛицо.Записать();
    
    // Затем пересохраняем объект контрагента, чтобы обновились все данные
    ОбъектКонтрагент.Записать();
КонецПроцедуры

И основной цикл, который теперь вызывает эти функции:


Функция ЗаполнитьИННСНИЛС(МассивСтрок)
    ПропущенныеКонтрагенты = Новый СписокЗначений;
    Для Каждого Строка Из МассивСтрок Цикл
        //...пропускаем пустые строки...
        Данные = ПолучитьДанныеИзСтроки(Строка); // Функция парсит строку в структуру
        
        ВыборкаКонтрагентов = НайтиКонтрагентаПоФИО(Данные.ФИО);

        Если ВыборкаКонтрагентов.Количество() = 1 Тогда
            ВыборкаКонтрагентов.Следующий();
            ИзменитьКонтрагента(ВыборкаКонтрагентов.КонтрагентСсылка, Данные);
        ИначеЕсли ВыборкаКонтрагентов.Количество() > 1 Тогда
            ПропущенныеКонтрагенты.Добавить(Данные.ФИО);
        КонецЕсли;
    КонецЦикла;
    Возврат ПропущенныеКонтрагенты;
КонецФункции

Дополнительные рекомендации

  1. Вопрос производительности: "запрос в цикле". Опытные разработчики знают, что выполнять запрос внутри цикла — не лучшая практика для больших объемов данных. Каждая итерация создает нагрузку на сервер. Для тысяч строк это может быть заметно. Более оптимальный подход — сначала считать все ФИО из файла во временную таблицу и затем одним запросом с соединением этой таблицы и справочника контрагентов найти все нужные ссылки. Однако в нашей задаче основным "тормозом" будет не чтение, а запись объектов (Объект.Записать()), которая является более ресурсоемкой операцией. Поэтому для задачи на 1000-2000 строк запрос в цикле является допустимым компромиссом.
  2. Указание типа контрагента. При работе с БГУ важно корректно указывать не только ВидКонтрагента = "Физическое лицо", но и, для корректного формирования отчетности (например, форм 169/769), рекомендуется проверять, что установлен Тип контрагента = "7 - Физическое лицо".

В результате мы прошли путь от простой, но неполноценной идеи до надежного решения, которое учитывает дубликаты, особенности архитектуры конкретной конфигурации и обеспечивает корректную запись данных.

← На главную