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

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

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

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

Типичные ошибки при получении остатков в форме

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

  1. Документ еще может отсутствовать в базе данных: если пользователь создает новый документ или добавляет новую строку, данных по этой строке в таблице базы данных еще нет. Запрос к табличной части сохраненного документа вернет пустой результат или устаревшие данные.
  2. Избыточность выборки: для заполнения конкретной строки нет необходимости пересчитывать остатки по всему документу через соединение таблиц — достаточно запросить остаток только для одной выбранной позиции номенклатуры с передачей ее в качестве параметра.

Пошаговый алгоритм реализации через событие ПриИзменении

Рассмотрим правильную логику работы алгоритма:

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

Пример программного кода на управляемых формах

Посмотрим на пример реализации с учетом требований клиент-серверного разделения платформы «1С:Предприятие 8.3».

На стороне клиента в модуле формы создадим обработчик события изменения номенклатуры:


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

Теперь опишем серверную функцию, которая выполняет запрос к регистру накопления:


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

Важные технические нюансы

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

  1. Установка параметров в виртуальной таблице: условия Номенклатура = &Номенклатура и Склад = &Склад обязательно нужно передавать в параметры самой виртуальной таблицы Остатки(...), а не в секцию ГДЕ. Это позволяет платформе отбирать данные на уровне СУБД с максимальной скоростью.
  2. Обработка отсутствия остатка: если по выбранному товару движений не было, выборка запроса будет пустой (метод Выборка.Следующий() вернет Ложь). В этом случае функция должна гарантированно возвращать число 0.
  3. Расчет для проведенных документов: если открыть и редактировать уже проведенный документ, стандартный запрос на дату документа включит в остаток движения этого же документа. Использование объекта Граница с параметром ВидГраницы.Исключая позволяет получить остаток на момент проведения без учета текущего документа.

Архитектурные рекомендации и альтернативные решения

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

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