Почему форма заказа клиента не открывается в тонком клиенте, но работает в толстом?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

При переходе на управляемые формы или при доработке типовых конфигураций программисты часто сталкиваются с ситуацией, когда код, успешно работающий в толстом клиенте, вызывает фатальную ошибку в тонком клиенте. Типичный сценарий: вы добавили колонки для расчета веса и объема в табличную часть «Товары», написали код расчета в модуле формы, и при открытии документа система выдает ошибку: не обнаружено поле объекта ВесЧислитель. Давайте разберем по шагам, почему это происходит и как правильно реализовать такие вычисления.

Выясняем причину: различия в архитектуре клиентов

Проанализируем ситуацию. В толстом клиенте (особенно в режиме обычного приложения) платформа работает непосредственно с объектами базы данных. Когда вы обращаетесь к реквизиту через точку, например Строка.Номенклатура.ВесЧислитель, система автоматически подгружает объект справочника из базы, чтобы достать нужное значение. Это удобно, но крайне неэффективно с точки зрения производительности сети.

В тонком клиенте всё иначе. На стороне клиента мы имеем дело не с объектами, а с ДаннымиФормы. Когда мы видим в строке табличной части колонку Номенклатура, для тонкого клиента это всего лишь Ссылка (уникальный идентификатор). В этой ссылке нет данных о весе, объеме или других реквизитах. Платформа 1С:Предприятие 8.3 специально запрещает неявные обращения к серверу через точку от ссылки на стороне клиента. Это сделано для того, чтобы интерфейс не «зависал» при каждом обращении к базе данных. Именно поэтому мы получаем ошибку: поле объекта не найдено, так как у Ссылкы нет таких свойств.

Решение 1: Простой перенос расчетов на сторону сервера

Рассмотрим самый очевидный способ исправления. Если нам нужно получить данные реквизитов справочника, мы обязаны сделать это на сервере. Посмотрим на пример кода, который вызывал ошибку:


Для Каждого Строка Из Объект.Товары Цикл
    // ОШИБКА: Обращение к реквизиту через точку от ссылки на клиенте
    Объем1 = Объем1 + Строка.Количество * Строка.Номенклатура.ОбъемЧислитель;            
    Вес1 = Вес1 + Строка.Количество * Строка.Номенклатура.ВесЧислитель;    
КонецЦикла;

Чтобы это заработало, нам необходимо создать серверную функцию. Разберем, как это реализовать правильно:


&НаКлиенте
Процедура РасчитатьИтогиНаКлиенте()
    // Вызываем серверный метод для получения данных
    РасчитатьИтогиНаСервере();
КонецПроцедуры

&НаСервере
Процедура РасчитатьИтогиНаСервере()
    ИтогоВес = 0;
    Для Каждого СтрокаТЧ Из Объект.Товары Цикл
        // На сервере обращение через точку допустимо (хотя и не всегда оптимально)
        СтрокаТЧ.ВесТовара = СтрокаТЧ.Количество * СтрокаТЧ.Номенклатура.ВесЧислитель;
        ИтогоВес = ИтогоВес + СтрокаТЧ.ВесТовара;
    КонецЦикла;
КонецПроцедуры

Решение 2: Оптимизация через пакетное получение данных (Запрос)

Предыдущий метод прост, но имеет скрытую ловушку. Если в документе 100 или 200 строк, обращение СтрокаТЧ.Номенклатура.ВесЧислитель в цикле приведет к тому, что система выполнит 100–200 отдельных микро-запросов к базе данных. Это называется проблемой N+1 запроса. Проанализируем ситуацию: гораздо эффективнее получить все данные одним запросом.

Рассмотрим алгоритм оптимального расчета:

  1. Соберем все уникальные ссылки номенклатуры из табличной части в массив.
  2. Передадим этот массив в запрос на сервер.
  3. Получим таблицу значений с весами и объемами.
  4. В одном цикле распределим данные по строкам документа.

Пример реализации:


&НаСервере
Процедура ОптимизированныйРасчетНаСервере()
    
    МассивНоменклатуры = Объект.Товары.ВыгрузитьКолонку("Номенклатура");
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Ссылка КАК Ссылка,
        |   Номенклатура.ВесЧислитель КАК ВесЧислитель,
        |   Номенклатура.ОбъемЧислитель КАК ОбъемЧислитель
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |   Номенклатура.Ссылка В(&МассивНоменклатуры)";
    
    Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    
    // Используем соответствие для быстрого поиска данных в памяти
    СоответствиеДанных = Новый Соответствие;
    Для Каждого СтрДанные Из РезультатЗапроса Цикл
        СоответствиеДанных.Вставить(СтрДанные.Ссылка, СтрДанные);
    КонецЦикла;
    
    Для Каждого СтрокаТЧ Из Объект.Товары Цикл
        ДанныеНоменклатуры = СоответствиеДанных.Получить(СтрокаТЧ.Номенклатура);
        Если ДанныеНоменклатуры <> Неопределено Тогда
            СтрокаТЧ.ВесТовара = СтрокаТЧ.Количество * ДанныеНоменклатуры.ВесЧислитель;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Решение 3: Использование Библиотеки Стандартных Подсистем (БСП)

Если вы работаете в современной конфигурации (УТ 11, ERP, КА 2), в вашем распоряжении есть мощный инструмент — общий модуль ОбщегоНазначения. Рассмотрим, как сократить код получения реквизитов до одной строки. Выясним, какие методы нам помогут:

Функция ОбщегоНазначения.ЗначенияРеквизитовОбъекта() позволяет получить структуру нужных полей, не заботясь о создании запросов вручную. Однако помните, что вызывать её в цикле — плохая практика. Лучше использовать ОбщегоНазначения.ЗначенияРеквизитовОбъектов() (во множественном числе) для получения данных сразу по всему списку ссылок.

Пример использования БСП:


// Получаем соответствие, где ключом будет ссылка, а значением - структура реквизитов
Реквизиты = "ВесЧислитель, ОбъемЧислитель";
ДанныеВсехТоваров = ОбщегоНазначения.ЗначенияРеквизитовОбъектов(МассивНоменклатуры, Реквизиты);

Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    ТекущиеДанные = ДанныеВсехТоваров[СтрокаТЧ.Номенклатура];
    Если ТекущиеДанные <> Неопределено Тогда
         СтрокаТЧ.ВесТовара = СтрокаТЧ.Количество * ТекущиеДанные.ВесЧислитель;
    КонецЕсли;
КонецЦикла;

Архитектурный совет: хранение данных в документе

Проанализируем еще один важный момент. Если вес и объем критически важны для документа (например, для печати накладных или расчета логистики), правильнее хранить эти значения в реквизитах табличной части документа, а не вычислять их динамически «на лету» только в форме.

Преимущества такого подхода:

Подведем итог: ошибка в тонком клиенте — это не досадный баг, а следствие работы системы безопасности производительности. Всегда переносите получение данных ссылочных полей на сервер и старайтесь минимизировать количество серверных вызовов, используя запросы или кэшированные функции БСП.

← На главную