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