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