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

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

В процессе разработки в среде 1С часто возникает задача автоматизации ввода данных. Типичный пример — заполнение табличной части документа «Начисление» или «Учет работ» данными о фактически отработанных часах, которые уже зафиксированы в системе другими документами. В данной статье мы подробно разберем, как реализовать механизм заполнения табличной части (ТЧ) данными из Регистра Накопления и дополнить их значениями из Регистра Сведений (например, тарифами или ценами).

Постановка задачи и выбор метода реализации

Представим ситуацию: у нас есть документ, в котором необходимо заполнить список сотрудников и количество отработанных ими часов за месяц, соответствующий дате документа. Сами данные о часах хранятся в регистре накопления УчетАктов. Дополнительно нам нужно подтянуть «Стоимость часа» для каждого сотрудника из регистра сведений СтоимостьЧаса.

Для реализации этой задачи мы будем использовать кнопку «Заполнить» на форме документа. С точки зрения архитектуры 1С, наиболее правильным будет использование связки процедур: клиентской (для взаимодействия с интерфейсом) и серверной (для выполнения запроса к базе данных).

Шаг 1. Создание команды и работа с периодом

Начнем с создания команды на форме документа. При создании команды выберем тип вызова «Создать на клиенте и процедуру на сервере». Это необходимо, так как работа с базой данных (запросы) возможна только на стороне сервера, а обновление формы инициируется клиентом.

Одной из ключевых задач является правильное определение периода. Если документ датирован, например, 30.09.2016, нам нужно получить данные с начала по конец этого месяца. Проанализируем, как это сделать программно с помощью встроенных функций НачалоМесяца() и КонецМесяца().


&НаКлиенте
Процедура Заполнить(Команда)
    // Вызываем серверную процедуру для получения данных
    ЗаполнитьНаСервере();
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьНаСервере()
    
    // Определяем границы периода на основании даты документа
    ДатаНачала = НачалоМесяца(Объект.Дата);
    ДатаОкончания = КонецМесяца(Объект.Дата);
    
    // Далее будет код запроса...
КонецПроцедуры

Шаг 2. Написание запроса к Регистру Накопления

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

Выясним причину, по которой ТЧ может очищаться и не заполняться. Чаще всего это происходит из-за несовпадения имен полей в результате запроса и имен реквизитов табличной части документа. Мы будем использовать псевдонимы (оператор КАК), чтобы сопоставить данные из регистра с колонками нашей таблицы.


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

Запрос.УстановитьПараметр("НачалоПериода", ДатаНачала);
Запрос.УстановитьПараметр("КонецПериода", ДатаОкончания);

Шаг 3. Соединение с Регистром Сведений (Срез Последних)

Теперь усложним задачу. Для каждого найденного сотрудника нам нужно получить актуальную стоимость часа. Эта информация хранится в независимом регистре сведений. Мы воспользуемся механизмом ЛЕВОГО СОЕДИНЕНИЯ с виртуальной таблицей СрезПоследних.

Проанализируем важный нюанс: при соединении таблиц крайне важно использовать функцию ЕСТЬNULL(). Если для какого-то сотрудника цена в регистре не задана, запрос вернет NULL, что может привести к ошибкам при расчетах. Мы заменим такие значения на 0.


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

Шаг 4. Загрузка данных в табличную часть документа

После того как запрос сформирован и выполнен, нам необходимо перенести данные в объект документа. Существует два основных способа: быстрый метод Загрузить() и построчный обход выборки.

Метод 1: Использование Загрузить(). Рассмотрим этот способ как наиболее производительный. Он подходит, если имена колонок в запросе полностью совпадают с именами реквизитов ТЧ. Обратите внимание, что метод Загрузить() полностью перезаписывает содержимое таблицы.


РезультатЗапроса = Запрос.Выполнить();
ТаблицаДанных = РезультатЗапроса.Выгрузить();

// Очищаем существующие строки и загружаем новые
Объект.Расчеты.Загрузить(ТаблицаДанных);

Метод 2: Построчный обход (Выборка). Проанализируем ситуацию, когда нам нужно выполнить дополнительные действия при добавлении каждой строки, например, рассчитать итоговую сумму (Часы * Цена).


Выборка = Запрос.Выполнить.Выбрать();
Объект.Расчеты.Очистить();

Пока Выборка.Следующий() Цикл
    НоваяСтрока = Объект.Расчеты.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
    
    // Пример дополнительного расчета
    НоваяСтрока.Сумма = НоваяСтрока.Часы * НоваяСтрока.Цена;
КонецЦикла;

Важные рекомендации и "подводные камни"

Разберем несколько ключевых моментов, которые помогут сделать ваш код более профессиональным и надежным:

  1. Контекст выполнения: Всегда выполняйте запросы на сервере. Не пытайтесь получить данные из регистра напрямую на клиенте — платформа этого не позволит.
  2. Параметр Период в Срезе Последних: Всегда передавайте в СрезПоследних дату (обычно КонецМесяца или Объект.Дата). Если параметр не указан, 1С вернет данные на текущую секунду, что будет неверно при работе с документами прошлых периодов.
  3. Псевдонимы полей: Если ваша табличная часть называется СоставРабот, а колонки в ней — Работник и Количество, то в запросе обязательно пишите Сотрудник КАК Работник. Метод Загрузить() ищет строгое соответствие имен.
  4. Предупреждение пользователя: Перед заполнением рекомендуется добавить на клиенте простую проверку: Вопрос("Табличная часть будет очищена. Продолжить?", РежимДиалогаВопрос.ДаНет). Это убережет пользователя от потери случайно введенных вручную данных.

Таким образом, мы рассмотрели полный цикл задачи: от определения границ периода до сложного соединения нескольких таблиц и выгрузки результата в интерфейс. Использование виртуальных таблиц оборотов и срезов последних в сочетании с методом Загрузить() является стандартом эффективной разработки на платформе 1С:Предприятие 8.3.

← На главную