В разработке на платформе 1С:Предприятие 8.3 часто возникает задача логирования изменений или автоматического заполнения связанных данных. Одной из типовых задач является добавление записи в историю (табличную часть) при изменении ответственного лица или исполнителя. В данной статье мы подробно разберем, как реализовать этот механизм, избежав типичных ошибок новичков, связанных с контекстом выполнения кода и типами данных.
Представим ситуацию: в документе есть основной реквизит Исполнитель и табличная часть ИсторияИзменений. Нам необходимо, чтобы при каждом выборе нового значения в поле Исполнитель в таблицу автоматически добавлялась строка с указанием этого исполнителя и текущего пользователя, который внес изменения. Выясним причину, по которой обычное написание кода в одном методе часто приводит к ошибкам.
Основная сложность заключается в разделении полномочий между клиентом (интерфейсом) и сервером (базой данных):
ПриИзменении) всегда возникает на клиенте.ПараметрыСеанса (например, для получения текущего пользователя) возможно только на сервере.Объект (данные формы) требует серверного контекста для корректной работы с метаданными и типами.Для начала проанализируем ситуацию на форме. Нам нужно выбрать элемент управления (поле ввода), соответствующий реквизиту Исполнитель, и создать для него событие ПриИзменении. Важно понимать, что этот обработчик должен иметь директиву &НаКлиенте.
Рассмотрим пример кода клиентской процедуры:
&НаКлиенте
Процедура ИсполнительПриИзменении(Элемент)
// Вызываем серверную процедуру для выполнения логики
ДобавитьЗаписьВИсториюНаСервере();
КонецПроцедуры
Обратите внимание: мы не передаем Элемент на сервер без необходимости, так как объекты интерфейса на сервере недоступны. Наша цель на клиенте — просто "отловить" факт изменения и передать управление серверному коду.
Теперь разберем, что происходит "под капотом" на сервере. Здесь мы можем свободно обращаться к Объекту (который представляет собой данные нашего документа на форме) и к системным параметрам. Используем директиву &НаСервере.
&НаСервере
Процедура ДобавитьЗаписьВИсториюНаСервере()
// Проверяем, заполнено ли значение, чтобы не плодить пустые строки
Если ЗначениеЗаполнено(Объект.Исполнитель) Тогда
// Добавляем новую строку в табличную часть
НоваяСтрока = Объект.ТекущийИсполнитель.Добавить();
// Заполняем реквизиты новой строки
НоваяСтрока.Исполнитель = Объект.Исполнитель;
// Важный момент: получение текущего пользователя через параметры сеанса
// Это сработает только в серверном контексте
НоваяСтрока.КемИзменено = ПараметрыСеанса.ТекущийПользователь;
// Можно также добавить дату изменения для полноты картины
НоваяСтрока.ДатаИзменения = ТекущаяДатаСеанса();
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию, с которой столкнулся автор темы: ошибка "Переменная не определена (ПараметрыСеанса)". Она возникает именно тогда, когда разработчик пытается обратиться к параметрам сеанса в процедуре с директивой &НаКлиенте. Тонкий клиент 1С не знает о существовании этой глобальной переменной, так как она хранится в памяти сервера приложений.
Еще один критически важный момент — соответствие типов. Часто реквизит КемИзменено в табличной части имеет тип СправочникСсылка.ФизическиеЛица, в то время как ПараметрыСеанса.ТекущийПользователь возвращает СправочникСсылка.Пользователи. Если типы не совпадают, строка добавится, но поле останется пустым. Проверьте настройки в конфигураторе: типы должны быть идентичны или реквизит должен иметь составной тип.
Если ваша конфигурация разработана на базе Библиотеки стандартных подсистем (БСП), правильнее будет получать текущего пользователя через специализированные функции общего модуля. Это делает код более устойчивым к изменениям прав доступа.
Рассмотрим, как изменить строку заполнения автора:
// Вместо прямого обращения к параметрам сеанса используем БСП
НоваяСтрока.КемИзменено = Пользователи.ТекущийПользователь();
Посмотрим на ситуацию со стороны пользователя. Когда мы программно добавляем строку на сервере, фокус в таблице может остаться на старом месте. Чтобы сделать интерфейс более отзывчивым, мы можем вернуть идентификатор новой строки на клиент и активизировать её.
Разберем по шагам улучшенный вариант:
&НаКлиенте
Процедура ИсполнительПриИзменении(Элемент)
// Получаем идентификатор новой строки из серверной функции
ИдентификаторСтроки = ДобавитьЗаписьИВернутьID();
// Устанавливаем фокус на новую строку в таблице
Если ИдентификаторСтроки <> Неопределено Тогда
Элементы.ТекущийИсполнитель.ТекущаяСтрока = ИдентификаторСтроки;
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ДобавитьЗаписьИВернутьID()
НоваяСтрока = Объект.ТекущийИсполнитель.Добавить();
НоваяСтрока.Исполнитель = Объект.Исполнитель;
НоваяСтрока.КемИзменено = ПараметрыСеанса.ТекущийПользователь;
// Возвращаем внутренний идентификатор строки для клиента
Возврат НоваяСтрока.ПолучитьИдентификатор();
КонецФункции
Мы выяснили, что для автоматического добавления строк в табличную часть необходимо четко разделять логику на клиентскую (обработка события) и серверную (изменение данных объекта). Помните, что любая модификация данных Объекта в управляемых формах должна сопровождаться пониманием того, где находится контекст в данный момент. Используйте проверку заполненности значений и следите за типизацией реквизитов, чтобы ваша информационная система работала стабильно и предсказуемо.