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