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