Как вывести результат запроса остатков в элемент обычной формы 1С и исправить ошибку «Поле объекта недоступно для записи»?

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

При разработке интерфейсных решений в обычных формах 1С (например, в конфигурации «Управление торговлей 10.3») часто возникает необходимость отображать справочную информацию — в частности, остатки товаров на текущем складе и по всей компании при переходе между строками табличной части. В процессе реализации разработчики сталкиваются с рядом типовых ошибок: невозможностью напрямую записать значение в элемент формы, неправильным поведением функции ЕСТЬNULL и неоптимальными запросами внутри циклов.

Расмотрим подробно причины возникновения данных проблем и проанализируем оптимальные способы вывода остатков номенклатуры на форму.

1. Причина ошибки «Поле объекта недоступно для записи»

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


{Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента.Форма(2833)}: Поле объекта недоступно для записи (Остаток)
ЭлементыФормы.Остаток = ВыборкаОстатка.Остаток;

Выясним причину такого поведения платформы. Коллекция ЭлементыФормы содержит объекты элементов управления (кнопки, поля ввода, надписи и т. д.), а не сами данные или реквизиты формы. Попытка переопределить сам объект управления прямым присвоением приводит к ошибке доступа.

Способ обращения зависит от типа элемента формы:

  1. Поле ввода (Элемент управления «Поле ввода»): Если элемент связан с реквизитом или используется для ввода/отображения данных, значение присваивается его свойству Значение: ЭлементыФормы.Остаток.Значение = ВыборкаОстатка.Остаток;
  2. Текстовая надпись (Элемент управления «Надпись»): Если на форме размещен статичный или динамический текст, изменить отображаемое значение можно через свойство Заголовок:
    
    ЭлементыФормы.Остаток.Заголовок = "На складе: " + Строка(ВыборкаОстатка.Остаток);
    
  3. Реквизит формы или табличной части: Допустимо создать отдельный реквизит на форме или в табличной части (например, ТекСтрокаТовары.РеквизитОстаток) и связать его с элементом формы через поле данных (Data Binding). В таком случае запись производится непосредственно в реквизит.

2. Почему функция ЕСТЬNULL не возвращает 0 для отсутствующих остатков

Рассмотрим следующий фрагмент запроса, в котором разработчик пытается подставить 0 вместо отсутствующих значений:


ВЫБРАТЬ
    ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
ГДЕ
    ТоварыНаСкладахОстатки.Номенклатура = &Номенклатура

Начинающие разработчики часто замечают, что если остатка по номенклатуре нет, запрос возвращает пустую выборку (0 строк), а не одну строку со значением 0.

Причина поведения платформы: Виртуальная таблица РегистрНакопления.ТоварыНаСкладах.Остатки physically хранит только активные записи с остатками. Если по позиции номенклатуры движений не было или остаток равен нулю, виртуальная таблица не формирует строки. Функция ЕСТЬNULL обрабатывает значение NULL только тогда, когда строка существует в выборке (например, при левом или правом соединении). Если запрос возвращает пустой результат, функция ЕСТЬNULL не вызывается.

Для гарантированного получения нуля по позициям без остатков необходимо применять ЛЕВОЕ СОЕДИНЕНИЕ исходного списка позиций (табличной части или массив ссылок) с виртуальной таблицей остатков.

3. Оптимизация запросов: передача параметров по ссылкам и отказ от циклов

Проанализируем частые ошибки при построении запросов остатков:

  1. Фильтрация по наименованию вместо ссылки: Условие вида ГДЕ Склад.Наименование = &Склад не только снижает производительность из-за обращения через точку к реквизитам справочника, но и может приводить к ошибкам при наличии одинаковых наименований. Сравнение всегда следует выполнять по ссылке: ГДЕ Склад = &Склад.
  2. Выполнение запроса в цикле: Вызов Запрос.Выполнить() внутри цикла Для каждого ТекСтрока Из Товары создает высокую нагрузку на СУБД. Все данные необходимо получать одним запросом, передавая массив ссылок в параметры виртуальной таблицы.
  3. Использование параметров виртуальной таблицы: Условия отбора следует выносить в параметры виртуальной таблицы, а не в секцию ГДЕ, чтобы платформа 1С рассчитывала остатки только по нужным позициям:
    
    ВЫБРАТЬ
        ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
        ТоварыНаСкладахОстатки.КоличествоОстаток КАК Остаток
    ИЗ
        РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&МассивНоменклатуры) И Склад = &Склад) КАК ТоварыНаСкладахОстатки
    

4. Готовое решение: выборка остатка по складу и по фирме для текущей строки

Рассмотрим практическую реализацию получения остатка по текущему складу и общему остатку по всей фирме при навигации по табличной части документа (в событии ПриАктивизацииСтроки или отдельной процедуре).

Скомпонуем единый запрос, который получает сразу оба остатка (по выбранному складу и суммарный остаток по организации) без совершения лишних обращений к базе данных:


Процедура Вывод_остатка()

    // Проверяем наличие выделенной строки в табличной части
    ТекДанные = ЭлементыФормы.Товары.ТекущиеДанные;
    Если ТекДанные = Неопределено ИЛИ НЕ ЗначениеЗаполнено(ТекДанные.Номенклатура) Тогда
        ЭлементыФормы.Остаток.Значение = "Номенклатура не выбрана";
        Возврат;
    КонецЕсли;

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

    Запрос.УстановитьПараметр("Склад", ЭтаФорма.Склад);
    Запрос.УстановитьПараметр("Номенклатура", ТекДанные.Номенклатура);

    Выборка = Запрос.Выполнить().Выбрать();

    ОстСклад = 0;
    ОстОбщий = 0;

    Если Выборка.Следующий() Тогда
        ОстСклад = ?(Выборка.ОстатокСклад = NULL, 0, Выборка.ОстатокСклад);
        ОстОбщий = ?(Выборка.ОстатокОбщий = NULL, 0, Выборка.ОстатокОбщий);
    КонецЕсли;

    // Оформление элемента формы в зависимости от количества
    Если ОстСклад <= 0 Тогда
        ЭлементыФормы.Остаток.ЦветТекста = WebЦвета.Красный;
    ИначеЕсли ОстСклад <= 2 Тогда
        ЭлементыФормы.Остаток.ЦветТекста = WebЦвета.Черный;
    Иначе
        ЭлементыФормы.Остаток.ЦветТекста = WebЦвета.Зеленый;
    КонецЕсли;

    // Вывод итоговой информации в элемент формы
    ЭлементыФормы.Остаток.Значение = "На Складе: " + Строка(ОстСклад) + " шт. | По Фирме: " + Строка(ОстОбщий) + " шт.";

КонецПроцедуры

Разберем ключевые особенности данного решения:

  1. Агрегатные функции и условные выражения: Конструкция СУММА(ВЫБОР КОГДА ...) позволяет вычислить остаток по конкретному складу и общий остаток компании в рамках одного запроса к виртуальной таблице.
  2. Безопасный обход выборки: Использование конструкции Если Выборка.Следующий() Тогда гарантирует правильное считывание данных и защищает от ошибок обращений к незаполненной выборке.
  3. Проверка заполнения данных: Перед запуском запроса контролируется наличие выделенной строки с помощью проверки ТекущиеДанные.
  4. Визуальное выделение: Свойство ЦветТекста элемента формы позволяет динамически менять цвет текста (например, выделять красным позиции с нулевым или минимальным остатком).

Таким образом, правильное обращение к свойствам элементов формы (Значение или Заголовок), отбор по ссылкам в параметрах виртуальных таблиц и оптимизация структуры запросов позволяют корректно выводить оперативную информацию об остатках без снижения производительности формы.

← На главную