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