Как сделать колонку в документе Заказ поставщику с остатками товаров на текущем складе в 1С?

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

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

Рассмотрим, как правильно реализовать отображение остатков в документе, проанализируем различные программные подходы, разберем частые ошибки производительности и выясним, как адаптировать решение под разные конфигурации 1С.

Важные архитектурные правила перед началом разработки

Прежде чем приступать к доработке, обратим внимание на базовые принципы работы с остатками в платформе 1С:

  1. Не создавайте реквизит «Остаток» в метаданных табличной части документа. Хранение вычисляемого остатка в реквизитах базы данных — серьезная ошибка. Эти значения устареют в момент любого следующего движения по складу, а сам документ неоправданно увеличит объем базы. Колонка остатка должна быть исключительно расчетным элементом экранной формы.
  2. Учитывайте регистры конфигурации. В типовых конфигурациях (например, 1С:Управление торговлей, 1С:Управление торговым предприятием) учет наличия товаров ведется по разным регистрам накопления. Например, в одних конфигурациях используется ТоварыНаСкладах, в других — ПартииТоваровНаСкладахНалоговыйУчет или СвободныеОстатки. Перед написанием запроса обязательно уточните структуру регистров вашей базы.
  3. Определите место хранения реквизита «Склад». В зависимости от настроек программы склад может указываться как в шапке документа (реквизит Склад самого документа), так и построчно в табличной части Товары.

Вариант 1. Заполнение колонки по нажатию кнопки (оптимальный по производительности)

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

Выполним следующие шаги:

  1. В редакторе формы добавим в табличное поле Товары новую колонку (например, с именем ОстатокНаСкладе) без привязки к данным объекта (без указания реквизита документа).
  2. На командную панель добавим кнопку «Показать остатки» и назначим ей процедуру-обработчик нажатия.
  3. В модуле формы напишем процедуру, которая соберет список номенклатуры из документа, одним пакетным запросом получит актуальные остатки из регистра накопления и заполнит ячейки.

Посмотрим на пример программного кода для обработчика кнопки:


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

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

Вариант 2. Динамическое отображение через событие «ПриПолученииДанных»

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

Важное предостережение: никогда не выполняйте запрос к базе данных внутри цикла по каждой отдельной строке. Запрос должен выполняться один раз для всей порции видимых строк (коллекции ОформленияСтрок).

Рассмотрим правильную реализацию обработчика ТоварыПриПолученииДанных:


Процедура ТоварыПриПолученииДанных(Элемент, ОформленияСтрок)
    
    // Собираем список номенклатуры только для видимых на экране строк
    МассивНоменклатуры = Новый Массив;
    СоответствиеОформлений = Новый Соответствие;
    
    Для Каждого Оформление Из ОформленияСтрок Цикл
        ТекНоменклатура = Оформление.ДанныеСтроки.Номенклатура;
        Если ЗначениеЗаполнено(ТекНоменклатура) Тогда
            МассивНоменклатуры.Добавить(ТекНоменклатура);
            СоответствиеОформлений.Вставить(ТекНоменклатура, Оформление);
        КонецЕсли;
    КонецЦикла;
    
    Если МассивНоменклатуры.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Запрашиваем остатки пакетом по видимым позициям
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
        |	ТоварыНаСкладахОстатки.КоличествоОстаток КАК КоличествоОстаток
        |ИЗ
        |	РегистрНакопления.ТоварыНаСкладах.Остатки(
        |		&Период,
        |		Номенклатура В (&МассивНоменклатуры)
        |			И Склад = &Склад) КАК ТоварыНаСкладахОстатки";
        
    Запрос.УстановитьПараметр("Период", ?(ЭтоНовый(), ТекущаяДата(), ЭтотОбъект.МоментВремени()));
    Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
    Запрос.УстановитьПараметр("Склад", ЭтотОбъект.Склад);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        Оформление = СоответствиеОформлений.Получить(Выборка.Номенклатура);
        Если Оформление <> Неопределено Тогда
            Оформление.Ячейки.ОстатокНаСкладе.ОтображатьТекст = Истина;
            Оформление.Ячейки.ОстатокНаСкладе.Текст = Формат(Выборка.КоличествоОстаток, "ЧЦ=15; ЧДЦ=3");
            
            // Выделение цветом отрицательных или нулевых остатков
            Если Выборка.КоличествоОстаток <= 0 Тогда
                Оформление.Ячейки.ОстатокНаСкладе.ЦветТекста = Новый Цвет(255, 0, 0);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Вариант 3. Предварительное кэширование остатков при открытии формы

Проанализируем еще один подход: при открытии документа остатки по складам считываются в переменную модуля формы типа ТаблицаЗначений. Затем при отрисовке строк значения быстро извлекаются из кэша.

Разберем реализацию по шагам:

  1. В самом начале модуля формы объявим переменную: Перем мТаблицаОстатков;
  2. Создадим процедуру заполнения остатков и вызовем ее в событии формы ПриОткрытии:

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

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

Особенности реализации в управляемых формах (1С 8.3)

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

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