При разработке интерфейсных решений в обычных формах 1С (например, в конфигурации «Управление торговлей 10.3») часто возникает необходимость отображать справочную информацию — в частности, остатки товаров на текущем складе и по всей компании при переходе между строками табличной части. В процессе реализации разработчики сталкиваются с рядом типовых ошибок: невозможностью напрямую записать значение в элемент формы, неправильным поведением функции ЕСТЬNULL и неоптимальными запросами внутри циклов.
Расмотрим подробно причины возникновения данных проблем и проанализируем оптимальные способы вывода остатков номенклатуры на форму.
Разберем ситуацию, когда при попытке присвоить значение элементу формы возникло исключение:
{Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента.Форма(2833)}: Поле объекта недоступно для записи (Остаток)
ЭлементыФормы.Остаток = ВыборкаОстатка.Остаток;
Выясним причину такого поведения платформы. Коллекция ЭлементыФормы содержит объекты элементов управления (кнопки, поля ввода, надписи и т. д.), а не сами данные или реквизиты формы. Попытка переопределить сам объект управления прямым присвоением приводит к ошибке доступа.
Способ обращения зависит от типа элемента формы:
Значение:
ЭлементыФормы.Остаток.Значение = ВыборкаОстатка.Остаток;
Заголовок:
ЭлементыФормы.Остаток.Заголовок = "На складе: " + Строка(ВыборкаОстатка.Остаток);
ТекСтрокаТовары.РеквизитОстаток) и связать его с элементом формы через поле данных (Data Binding). В таком случае запись производится непосредственно в реквизит.Рассмотрим следующий фрагмент запроса, в котором разработчик пытается подставить 0 вместо отсутствующих значений:
ВЫБРАТЬ
ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
ГДЕ
ТоварыНаСкладахОстатки.Номенклатура = &Номенклатура
Начинающие разработчики часто замечают, что если остатка по номенклатуре нет, запрос возвращает пустую выборку (0 строк), а не одну строку со значением 0.
Причина поведения платформы: Виртуальная таблица РегистрНакопления.ТоварыНаСкладах.Остатки physically хранит только активные записи с остатками. Если по позиции номенклатуры движений не было или остаток равен нулю, виртуальная таблица не формирует строки. Функция ЕСТЬNULL обрабатывает значение NULL только тогда, когда строка существует в выборке (например, при левом или правом соединении). Если запрос возвращает пустой результат, функция ЕСТЬNULL не вызывается.
Для гарантированного получения нуля по позициям без остатков необходимо применять ЛЕВОЕ СОЕДИНЕНИЕ исходного списка позиций (табличной части или массив ссылок) с виртуальной таблицей остатков.
Проанализируем частые ошибки при построении запросов остатков:
ГДЕ Склад.Наименование = &Склад не только снижает производительность из-за обращения через точку к реквизитам справочника, но и может приводить к ошибкам при наличии одинаковых наименований. Сравнение всегда следует выполнять по ссылке: ГДЕ Склад = &Склад.Запрос.Выполнить() внутри цикла Для каждого ТекСтрока Из Товары создает высокую нагрузку на СУБД. Все данные необходимо получать одним запросом, передавая массив ссылок в параметры виртуальной таблицы.ГДЕ, чтобы платформа 1С рассчитывала остатки только по нужным позициям:
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстатки.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&МассивНоменклатуры) И Склад = &Склад) КАК ТоварыНаСкладахОстатки
Рассмотрим практическую реализацию получения остатка по текущему складу и общему остатку по всей фирме при навигации по табличной части документа (в событии ПриАктивизацииСтроки или отдельной процедуре).
Скомпонуем единый запрос, который получает сразу оба остатка (по выбранному складу и суммарный остаток по организации) без совершения лишних обращений к базе данных:
Процедура Вывод_остатка()
// Проверяем наличие выделенной строки в табличной части
ТекДанные = ЭлементыФормы.Товары.ТекущиеДанные;
Если ТекДанные = Неопределено ИЛИ НЕ ЗначениеЗаполнено(ТекДанные.Номенклатура) Тогда
ЭлементыФормы.Остаток.Значение = "Номенклатура не выбрана";
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СУММА(ВЫБОР КОГДА ТоварыНаСкладахОстатки.Склад = &Склад ТОГДА ТоварыНаСкладахОстатки.КоличествоОстаток ИНАЧЕ 0 КОНЕЦ) КАК ОстатокСклад,
| СУММА(ТоварыНаСкладахОстатки.КоличествоОстаток) КАК ОстатокОбщий
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК ТоварыНаСкладахОстатки";
Запрос.УстановитьПараметр("Склад", ЭтаФорма.Склад);
Запрос.УстановитьПараметр("Номенклатура", ТекДанные.Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
ОстСклад = 0;
ОстОбщий = 0;
Если Выборка.Следующий() Тогда
ОстСклад = ?(Выборка.ОстатокСклад = NULL, 0, Выборка.ОстатокСклад);
ОстОбщий = ?(Выборка.ОстатокОбщий = NULL, 0, Выборка.ОстатокОбщий);
КонецЕсли;
// Оформление элемента формы в зависимости от количества
Если ОстСклад <= 0 Тогда
ЭлементыФормы.Остаток.ЦветТекста = WebЦвета.Красный;
ИначеЕсли ОстСклад <= 2 Тогда
ЭлементыФормы.Остаток.ЦветТекста = WebЦвета.Черный;
Иначе
ЭлементыФормы.Остаток.ЦветТекста = WebЦвета.Зеленый;
КонецЕсли;
// Вывод итоговой информации в элемент формы
ЭлементыФормы.Остаток.Значение = "На Складе: " + Строка(ОстСклад) + " шт. | По Фирме: " + Строка(ОстОбщий) + " шт.";
КонецПроцедуры
Разберем ключевые особенности данного решения:
СУММА(ВЫБОР КОГДА ...) позволяет вычислить остаток по конкретному складу и общий остаток компании в рамках одного запроса к виртуальной таблице.Если Выборка.Следующий() Тогда гарантирует правильное считывание данных и защищает от ошибок обращений к незаполненной выборке.ТекущиеДанные.ЦветТекста элемента формы позволяет динамически менять цвет текста (например, выделять красным позиции с нулевым или минимальным остатком).Таким образом, правильное обращение к свойствам элементов формы (Значение или Заголовок), отбор по ссылкам в параметрах виртуальных таблиц и оптимизация структуры запросов позволяют корректно выводить оперативную информацию об остатках без снижения производительности формы.