Каждый разработчик на платформе «1С:Предприятие 8» регулярно сталкивается с тривиальной, на первый взгляд, задачей — заполнением или очисткой определенной колонки в табличной части (ТЧ) документа или справочника на форме. Например, при изменении валюты нужно обнулить цены, пересчитать ставки НДС или заполнить статусы строк. Действие чаще всего происходит в контекстном серверном вызове управляемой формы.
Существует как минимум три популярных алгоритма решения этой задачи. Но какой из них является наиболее «правильным» с точки зрения производительности, потребления памяти, сетевого трафика и удобства пользователя? В этой статье мы подробно проанализируем каждый вариант, разберем скрытые механизмы платформы и определим оптимальное решение по «феншую» разработки.
Прежде чем перейти к анализу алгоритмов, разберем важное архитектурное различие, о котором часто забывают начинающие разработчики. На управляемой форме реквизит Объект.Товары имеет тип ДанныеФормыКоллекция, а не привычную ТаблицаЗначений. Это специальный тип данных, оптимизированный для передачи между клиентом и сервером.
У объекта ДанныеФормыКоллекция отсутствует встроенный метод ЗаполнитьЗначения(), который есть у обычной таблицы значений. Это ограничение напрямую влияет на то, какими методами мы вынуждены пользоваться при решении нашей задачи.
Рассмотрим подробнее три основных алгоритма заполнения колонки.
---
Этот метод кажется самым простым и очевидным. Мы просто перебираем строки нашей коллекции на форме и устанавливаем новое значение для нужного реквизита.
// Вариант 1: Обход коллекции в цикле
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
СтрокаТЧ.Цена = 0;
КонецЦикла;
Как это работает «под капотом» платформы:
Платформа последовательно считывает каждую строку коллекции ДанныеФормыКоллекция и напрямую изменяет значение свойства конкретного элемента. Поскольку 1С — это интерпретируемый язык, код выполняется силами виртуальной машины платформы строка за строкой.
Преимущества метода («UI-феншуй»):
Недостатки метода:
---
Поскольку у ДанныеФормыКоллекция нет метода быстрой вставки, разработчики часто прибегают к промежуточному преобразованию в полноценную таблицу значений, чтобы использовать быстрый платформенный метод ЗаполнитьЗначения().
// Вариант 2: Использование промежуточной ТЗ
ТаблицаДанных = Объект.Товары.Выгрузить();
ТаблицаДанных.ЗаполнитьЗначения(0, "Цена");
Объект.Товары.Загрузить(ТаблицаДанных);
Как это работает «под капотом» платформы:
Метод Выгрузить() создает в памяти сервера копию всей таблицы. Метод ТЗ ЗаполнитьЗначения() реализован на уровне платформенного кода C++ и работает молниеносно. Затем метод Загрузить() полностью очищает коллекцию на форме и заполняет ее заново данными из ТЗ.
Преимущества метода:
Недостатки и скрытый оверхед:
Загрузить() полностью перезапускает отрисовку таблицы на клиенте. Для пользователя это выглядит как резкое «моргание» экрана. При этом фокус ввода и активная строка гарантированно сбрасываются на самую первую строчку. Если пользователь редактировал 500-ю строку и сработало это автозаполнение — ему придется заново искать глазами место, на котором он остановился.Выгрузить() без параметров копирует абсолютно все колонки табличной части. В реальных конфигурациях (особенно ERP, Комплексная автоматизация или Управление торговлей) табличные части документов могут содержать по 30–50 колонок, включая скрытые и служебные реквизиты. Выгружать и загружать обратно сотни килобайт или даже мегабайты лишних данных ради обнуления одной числовой колонки — крайне неоптимально.---
Этот метод часто предлагают те, кто пытается использовать методы оптимизации работы с объектами базы данных. Идея состоит в том, чтобы временно преобразовать реквизит формы в тяжелый прикладной объект (например, ДокументОбъект), выполнить быструю замену колонки с помощью массива и вернуть его обратно на форму.
// Вариант 3: Преобразование в объект (с исправлением критической ошибки типа данных)
ДокументОбъект = РеквизитФормыВЗначение("Объект", Тип("ДокументОбъект.РеализацияТоваровУслуг"));
РазмерТаблицы = ДокументОбъект.Товары.Количество();
МассивЗначений = Новый Массив(РазмерТаблицы);
// Нам обязательно нужно заполнить массив нужным типом (например, числом 0)
// Иначе массив будет содержать "Неопределено", что вызовет ошибку при записи в СУБД!
Для Индекс = 0 По РазмерТаблицы - 1 Цикл
МассивЗначений[Индекс] = 0;
КонецЦикла;
ДокументОбъект.Товары.ЗагрузитьКолонку(МассивЗначений, "Цена");
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
Внимание: критический баг оригинального кода!
В исходном коде, предложенном на форуме (Новый Массив(мЗначение.Товары.Количество)), создается пустой массив, элементы которого по умолчанию имеют тип Неопределено. Метод ЗагрузитьКолонку() послушно запишет эти Неопределено в колонку "Цена". Когда пользователь нажмет кнопку «Записать», платформа попытается записать пустое значение в числовое поле базы данных и выбросит критическое исключение несоответствия типов данных. Чтобы этого избежать, массив приходится принудительно инициализировать в цикле (как показано в примере выше), что полностью лишает этот вариант изначального смысла быстродействия.
Почему этот вариант — худший выбор:
РеквизитФормыВЗначение для тяжелых документов запускает полную сериализацию всего объекта. Платформа тратит колоссальные ресурсы процессора и памяти на сборку объекта со всеми его реквизитами шапки, другими табличными частями, проверками прав доступа и служебными свойствами. Использовать такую операцию ради банального заполнения одной колонки на форме — грубая архитектурная ошибка.---
Проанализируем крайне важный аспект, который упустили участники обсуждения на форуме. Речь идет об оптимизации сетевого трафика в клиент-серверной архитектуре платформы «1С:Предприятие 8».
Платформа 1С использует интеллектуальный механизм синхронизации данных между клиентом и сервером. Она умеет передавать по сети только измененные («грязные») строки коллекции ДанныеФормыКоллекция.
Если в таблице документа содержится, к примеру, 5 000 строк, а пользователь интерактивно изменил значение ячейки всего в одной строке, при контекстном вызове сервера обратно на клиентский компьютер будет передан пакет данных, содержащий информацию только об одной этой строке.
Что же происходит при использовании разных вариантов заполнения?
На тонком клиенте (особенно при работе пользователей через веб-сервер, VPN или мобильный интернет) это вызовет длительную задержку («зависание» интерфейса программы), которая по времени будет в десятки раз более критичной для пользователя, чем скорость выполнения самого кода на сервере приложений.
---
Проанализировав все плюсы и минусы, мы можем составить четкий свод правил для выбора наилучшего алгоритма заполнения колонок.
Используем только Вариант 1 (прямой обход в цикле).
Он работает быстро, не тратит лишнюю память на копирование колонок, гарантирует полную стабильность интерфейса для пользователя (фокус ввода и скроллбар остаются на месте) и защищает от скрытых багов типизации данных.
Если колонка таблицы должна содержать вычисляемые значения (например, остатки товаров на складах, текущие цены, статусы или картинки состояния), не нужно хранить эти данные физически в табличной части документа.
Правильным архитектурным решением будет вывод таких данных через:
Наличие таблиц такого объема в интерактивной форме документа — это само по себе серьезная проблема проектирования интерфейса (UX). Ни один пользователь физически не способен просмотреть или обработать вручную 10 000 строк в документе.
Если же такая задача действительно стоит (например, при загрузке огромных прайс-листов или ведомостей), правильнее отказаться от выполнения кода в контексте формы и применить один из следующих подходов:
Используйте правильные платформенные механизмы, берегите нервы пользователей от «прыгающего» интерфейса и пишите красивый, производительный код по стандартам «1С:Предприятия»!