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