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

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

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

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

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

---

Анализ ошибок: почему код «не работает»?

Рассмотрим исходный алгоритм, который часто пытаются использовать новички (и который был представлен в вопросе автора темы):


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

&НаКлиенте
Процедура Заполнить(Команда)
    Данные = ЗаполнитьНачисления();
КонецПроцедуры

Давайте проанализируем эту ситуацию и выделим три критические ошибки в данном коде:

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

---

Вариант решения №1: Быстрое заполнение методом «Загрузить»

Если перед заполнением документа нам нужно полностью очистить его табличную часть и перезаписать ее актуальными данными из регистра, самым быстрым и лаконичным решением будет использование метода Загрузить().

Этот метод работает на сервере и позволяет в одну строчку скопировать данные из структуры ТаблицаЗначений в табличную часть документа. Главное условие — имена колонок в результате запроса (псевдонимы) должны строго совпадать с именами реквизитов табличной части документа.

Разберем по шагам правильную реализацию этого подхода:


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

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

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

---

Вариант решения №2: Построчный обход и гибкое заполнение

Что делать, если нам нужно не просто стереть всё и записать заново, а, например, дополнить существующую табличную часть, проверить каждую строчку по сложным условиям или рассчитать дополнительные параметры «на лету»? В этом случае лучшим выбором станет построчный обход выборки запроса.

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


&НаСервере
Процедура ЗаполнитьНачисленияПострочноНаСервере()
	
	// По желанию пользователя можно очистить ТЧ, либо закомментировать строку, чтобы добавлять новые записи к уже имеющимся
	Объект.Начисления.Очистить(); 
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	РаботникиСрезПоследних.Сотрудник,
		|	РаботникиСрезПоследних.СпособРасчета,
		|	РаботникиСрезПоследних.Оклад
		|ИЗ
		|	РегистрСведений.Работники.СрезПоследних(&ДатаСреза, ) КАК РаботникиСрезПоследних
		|ГДЕ
		|	РаботникиСрезПоследних.СтатусРаботника = ЗНАЧЕНИЕ(Перечисление.СтатусРаботника.Принят)";
		
	РабочаяДата = ?(ЗначениеЗаполнено(Объект.Дата), Объект.Дата, ТекущаяДата());
	Запрос.УстановитьПараметр("ДатаСреза", РабочаяДата);
	
	// Выбираем данные последовательно
	Выборка = Запрос.Выполнить().Выбрать();
	
	Пока Выборка.Следующий() Цикл
		
		// Добавляем новую строку в табличную часть "Начисления" нашего документа
		НоваяСтрока = Объект.Начисления.Добавить();
		
		// Используем полезную функцию для быстрого копирования совпадающих свойств
		ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
		
		// Здесь можно написать любую дополнительную логику обработки, например:
		// Если Выборка.Оклад > 100000 Тогда
		//     НоваяСтрока.Комментарий = "Повышенный оклад";
		// КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

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

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

---

Важные архитектурные нюансы для начинающих

Проанализируем несколько неочевидных проблем, из-за которых кнопка «Заполнить» может работать некорректно в реальных базах данных:

  1. Проблема пустой даты в новом документе. Когда пользователь только что создал документ и еще не успел его записать (или выбрать дату), поле Объект.Date (или Объект.Дата) содержит пустое значение даты — 01.01.0001 0:00:00. Если передать такую дату в виртуальную таблицу СрезПоследних, система вернет пустой результат, так как на начало времен в регистре еще не было никаких записей. Обязательно используйте конструкцию проверки заполненности даты, подставляя ТекущаяДата() в качестве альтернативного параметра запроса.
  2. Контроль статуса увольнения сотрудников. Срез последних по условию СтатусРаботника = Принят будет работать корректно только тогда, когда в регистре «Работники» ведется полноценная история изменений. При увольнении сотрудника в систему должна обязательно записываться строка со статусом Уволен. Если запись о приеме просто удаляется из базы (что в корне неверно), нарушается хронология и целостность учета.
  3. Оптимизация производительности регистра сведений. Чтобы при увеличении штата сотрудников база данных не начинала тормозить при нажатии кнопки заполнения, убедитесь, что в регистре сведений «Работники» для измерения Сотрудник установлено свойство «Индексировать» (или оно является ведущим). Это позволит СУБД мгновенно находить актуальные срезы по каждому физическому лицу.

Мы детально рассмотрели процесс получения информации из регистра и заполнения табличной части. Выбирайте первый вариант с методом Загрузить() для простых задач перезаполнения и второй вариант с обходом выборки — для сложных комплексных алгоритмов заполнения документов.

← На главную