Как правильно заполнить табличную часть документа из выборки запроса с несколькими сотрудниками

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

При разработке механизмов автоматизации в 1С программисты часто сталкиваются с задачей заполнения табличных частей (ТЧ) документов на основе данных из регистров или справочников. Типичный пример — распределение сдельных операций между сотрудниками бригады. Однако при написании кода легко допустить логические ошибки, из-за которых в документ попадает только один сотрудник или данные перезаписываются некорректно. В этой статье мы подробно разберем, как избежать таких ошибок и реализовать надежный алгоритм заполнения.

Анализ проблемы: почему заполняется только один сотрудник?

Рассмотрим ситуацию, когда у нас есть список операций в документе и мы хотим для каждой операции назначить исполнителей из определенной бригады. Часто начинающие разработчики используют конструкцию Если РезультатЗапроса.Следующий() Тогда. Это первая и самая критичная ошибка. Метод Следующий() перемещает указатель выборки на одну запись вперед. Если использовать Если, код обработает только первую строку из результата запроса, даже если их там десять.

Вторая проблема заключается в использовании метода НайтиСтроки() внутри цикла. Проанализируем логику: если мы находим существующую строку и просто меняем в ней значение Исполнитель, то при каждой итерации цикла по сотрудникам мы будем просто перезаписывать одного и того же исполнителя в одной и той же строке. В итоге в документе останется только последний сотрудник из выборки.

Шаг 1: Правильный обход результата запроса

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


// Правильный обход выборки сотрудников
ВыборкаСотрудников = Запрос.Выполнить().Выбрать();

Пока ВыборкаСотрудников.Следующий() Цикл
    // Здесь логика обработки каждого сотрудника
    Сообщить("Обрабатываем сотрудника: " + ВыборкаСотрудников.СотрудникИзБригады);
КонецЦикла;

Шаг 2: Логика добавления строк в табличную часть

Разберем, как правильно сопоставить операции и исполнителей. Предположим, в документе ВыпускПродукции уже заполнены СдельныеОперации, и нам нужно наполнить таблицу Исполнители. Нам нужно решить: мы добавляем новые строки или обновляем старые?

Проанализируем ситуацию, когда для каждой операции из списка ТЧСдельныеОперации нужно добавить всех членов бригады в ТЧИсполнители. Рассмотрим правильную структуру вложенных циклов:


// Обходим все операции, которые есть в документе
Для Каждого СтрокаОперация Из Объект.ТЧСдельныеОперации Цикл
    
    // Пропускаем ненужные операции по условию
    Если СтрокаОперация.СдельнаяОперация = НужнаяОперация Тогда
        Продолжить;
    КонецЕсли;

    // Для каждой операции получаем состав бригады через запрос
    // (В идеале запрос лучше вынести из цикла, но разберем базовый пример)
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        // Всегда добавляем НОВУЮ строку для каждого исполнителя
        НоваяСтрока = Объект.ТЧИсполнители.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаОперация); // Копируем данные операции
        НоваяСтрока.Исполнитель = Выборка.СотрудникИзБригады;
        НоваяСтрока.КТУ = 1; // Устанавливаем коэффициент
    КонецЦикла;
    
КонецЦикла;

Шаг 3: Оптимизация с использованием временных таблиц

Выполнение запроса внутри цикла (как показано выше) — это плохая практика, которая существенно замедляет работу системы, особенно если строк в документе много. Выясним, как сделать это профессионально. Рассмотрим алгоритм с использованием Менеджера Временных Таблиц.

  1. Поместим текущую табличную часть документа во временную таблицу в запросе.
  2. Соединим эту временную таблицу с данными о составе бригад (регистр сведений или справочник).
  3. Получим одним запросом готовый плоский список "Операция - Исполнитель".
  4. Загрузим результат в документ.

Посмотрим на пример такого запроса:


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

Запрос.УстановитьПараметр("ТЧОперации", Объект.ТЧСдельныеОперации.Выгрузить());
Запрос.УстановитьПараметр("Бригада", ВыбраннаяБригада);

Результат = Запрос.Выполнить().Выгрузить();
Объект.ТЧИсполнители.Загрузить(Результат);

Важные нюансы и рекомендации

Распределение КТУ: Если одну операцию выполняют несколько человек, КТУ = 1 для каждого может быть некорректным с точки зрения начисления зарплаты. Проанализируем ситуацию: если сумма КТУ должна быть равна 1 на операцию, то в цикле нужно рассчитать 1 / КоличествоСотрудников.

Метод НайтиСтроки(): Помните, что НайтиСтроки() возвращает массив ссылок на строки. Изменяя объект в этом массиве, вы меняете строку в самой табличной части. Если ваша задача — не заменить исполнителя, а добавить новых, этот метод вам не подходит, используйте Добавить().

Использование "Разрешенные": В запросах к кадровым данным (сотрудники, зарплата) всегда используйте ключевое слово РАЗРЕШЕННЫЕ. Это предотвратит ошибки "Нарушение прав доступа", если у пользователя нет прав на просмотр всех сотрудников организации.

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

← На главную