Как реализовать вывод остатков товаров в списке справочника Номенклатура в 1С 8.3 на обычных формах

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

При переходе с платформы 1С 8.1 на 8.3 или при обновлении старых конфигураций, таких как Управление торговлей 10.3, разработчики часто сталкиваются с тем, что привычные доработки перестают функционировать. Одной из самых востребованных задач является отображение актуальных остатков и резервов прямо в форме списка или форме выбора справочника Номенклатура. Это позволяет менеджеру по продажам оперативно видеть наличие товара, не заходя в дополнительные отчеты.

В этой статье мы подробно разберем, как реализовать этот функционал, почему может не срабатывать точка останова в коде и как правильно связать программные процедуры с событиями формы в режиме обычного приложения.

Разбор проблемы: почему не вызывается процедура

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

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

Способ 1: Привязка события через свойства формы

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

  1. Откройте форму справочника Номенклатура (обычно это ФормаВыбора или ФормаСписка) в конфигураторе.
  2. Найдите основной элемент таблицы, который отображает список товаров. Обычно он называется СправочникСписок и имеет тип ТабличноеПоле.
  3. Нажмите на него правой кнопкой мыши и выберите пункт Свойства.
  4. В открывшейся панели свойств перейдите на закладку События (иконка с лупой).
  5. Найдите событие ПриПолученииДанных. Скорее всего, поле ввода рядом с ним будет пустым.
  6. Нажмите на кнопку с лупой или выберите из выпадающего списка вашу процедуру СправочникСписокПриПолученииДанных.

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

Способ 2: Программное назначение обработчика

Иногда требуется назначить обработчик программно, например, чтобы минимизировать изменения в визуальной части формы при частых обновлениях конфигурации. Для этого используется метод УстановитьДействие. Проанализируем, как это сделать правильно в процедуре ПриОткрытии.


Процедура ПриОткрытии()
    // Назначаем обработчик события ПриПолученииДанных для списка
    ЭлементыФормы.СправочникСписок.УстановитьДействие("ПриПолученииДанных", Новый Действие("СправочникСписокПриПолученииДанных"));
    
    // Инициализируем запрос для получения остатков, чтобы не создавать его каждый раз
    ИнициализироватьЗапросОстатков();
КонецПроцедуры

Важный нюанс: при использовании метода УстановитьДействие вторым параметром должен передаваться объект типа Действие. Если просто указать строку с именем процедуры, система выдаст ошибку «Несоответствие типов».

Реализация эффективного запроса к остаткам

Событие ПриПолученииДанных срабатывает очень часто (при прокрутке, изменении размера окна), поэтому код внутри него должен быть максимально оптимизирован. Ни в коем случае нельзя выполнять запрос в цикле по строкам. Правильный подход — получить данные одним запросом для всего массива видимых строк.

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


// В модуле формы объявим переменную для запроса
Перем ЗапросОстатки;

Процедура ИнициализироватьЗапросОстатков()
    ЗапросОстатки = Новый Запрос;
    ЗапросОстатки.Текст = 
    "ВЫБРАТЬ
    |	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |	ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) КАК ОстатокНаСкладе,
    |	ЕСТЬNULL(ТоварыВРезервеНаСкладахОстатки.КоличествоОстаток, 0) КАК РезервОстаток,
    |	ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) - ЕСТЬNULL(ТоварыВРезервеНаСкладахОстатки.КоличествоОстаток, 0) КАК СвободныйОстаток
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&Номенклатура) И Склад = &Склад) КАК ТоварыНаСкладахОстатки
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(, Номенклатура В (&Номенклатура) И Склад = &Склад) КАК ТоварыВРезервеНаСкладахОстатки
    |		ПО ТоварыНаСкладахОстатки.Номенклатура = ТоварыВРезервеНаСкладахОстатки.Номенклатура";
КонецПроцедуры

Обработка данных в событии ПриПолученииДанных

Теперь объединим всё вместе. В процедуре ПриПолученииДанных мы получаем массив ссылок на номенклатуру, которая в данный момент видна на экране, передаем её в запрос и заполняем текстовые ячейки колонок.


Процедура СправочникСписокПриПолученииДанных(Элемент, ОформленияСтрок)
    // Проверим видимость колонок, чтобы не выполнять тяжелый запрос впустую
    Если Элемент.Колонки.Остаток.Видимость Или Элемент.Колонки.СвободныйОстаток.Видимость Тогда
        
        МассивНоменклатуры = Новый Массив;
        Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
            // ДанныеСтроки содержат ссылку на объект текущей строки
            Если НЕ ОформлениеСтроки.ДанныеСтроки.ЭтоГруппа Тогда
                МассивНоменклатуры.Добавить(ОформлениеСтроки.ДанныеСтроки.Ссылка);
            КонецЕсли;
        КонецЦикла;
        
        Если МассивНоменклатуры.Количество() = 0 Тогда
            Возврат;
        КонецЕсли;

        // Устанавливаем параметры. Склад можно брать из константы или настроек пользователя
        ЗапросОстатки.УстановитьПараметр("Номенклатура", МассивНоменклатуры);
        ЗапросОстатки.УстановитьПараметр("Склад", Константы.ОсновнойСклад.Получить());
        
        ТЗ_Остатки = ЗапросОстатки.Выполнить().Выгрузить();
        
        // Обходим строки оформления и заполняем ячейки
        Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
            Если НЕ ОформлениеСтроки.ДанныеСтроки.ЭтоГруппа Тогда
                СтрокаОстатков = ТЗ_Остатки.Найти(ОформлениеСтроки.ДанныеСтроки.Ссылка, "Номенклатура");
                
                Если СтрокаОстатков <> Неопределено Тогда
                    ОформлениеСтроки.Ячейки.Остаток.УстановитьТекст(СтрокаОстатков.ОстатокНаСкладе);
                    ОформлениеСтроки.Ячейки.СвободныйОстаток.УстановитьТекст(СтрокаОстатков.СвободныйОстаток);
                    
                    // Дополнительно можно раскрасить отрицательный свободный остаток
                    Если СтрокаОстатков.СвободныйОстаток < 0 Тогда
                        ОформлениеСтроки.Ячейки.СвободныйОстаток.ЦветТекста = ЦветаСтиля.ЦветОтрицательногоИтога;
                    КонецЕсли;
                КонецЕсли;
            КонецЕсли;
        КонецЦикла;
        
    КонецЕсли;
КонецПроцедуры

Рекомендации по отладке и производительности

Проанализируем несколько критических моментов, которые помогут избежать «тормозов» системы:

  1. Проверка на серверную отладку: Если ваша база работает в клиент-серверном варианте, убедитесь, что на сервере включен режим отладки (параметр -debug). В обычных формах часть кода может выполняться в контексте, который требует серверной отладки для срабатывания точек останова.
  2. Использование метода Сообщить(): Если вы сомневаетесь, заходит ли выполнение в процедуру, добавьте в начало Сообщить("Событие сработало!");. Это самый надежный способ проверить связь события и обработчика без настройки отладчика.
  3. Минимизация обращений к константам: В примере выше используется Константы.ОсновнойСклад.Получить() прямо внутри обработчика. Это не совсем оптимально. Лучше получить значение склада один раз при открытии формы и сохранить его в переменную модуля.
  4. Кэширование данных: Для высоконагруженных систем рекомендуется кэшировать полученные остатки в ТаблицуЗначений на уровне модуля формы и обновлять их, например, раз в 30 секунд, чтобы не перегружать сервер SQL постоянными запросами при каждом движении мыши пользователя.

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

← На главную