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