Как в управляемой форме сравнить старое и новое значение реквизита табличной части при изменении?

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

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

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

Почему не стоит использовать чтение из базы через Объект.Ссылка

Иногда предлагают сравнить текущую строку документа со строкой из базы через обращение Объект.Ссылка.ТабличнаяЧасть. Проанализируем риски и архитектурные ограничения такого подхода:

  1. Новый документ: Если документ еще ни разу не был записан, свойство Объект.Ссылка не заполнено, и прочитать данные из базы невозможно.
  2. Новые строки: Если пользователь добавил новую строку в уже существующий документ, в базе данных для нее нет соответствия.
  3. Нарушение порядка строк: При перемещении, сортировке или удалении строк обращение по индексу к табличной части из Объект.Ссылка приведет к сопоставлению с совершенно другой строкой.
  4. Производительность: Чтение объекта из базы требует дополнительного серверного вызова и нагрузки на СУБД при каждом изменении строки на клиенте.

Способ 1. Использование клиентской переменной формы и события «ПриНачалеРедактирования»

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

Посмотрим на пример реализации в коде формы:


&НаКлиенте
Перем ПредыдущееЗначениеРеквизита;

&НаКлиенте
Процедура ТоварыПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
    Если НоваяСтрока Тогда
        ПредыдущееЗначениеРеквизита = Неопределено;
    Иначе
        ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
        Если ТекущаяСтрока <> Неопределено Тогда
            ПредыдущееЗначениеРеквизита = ТекущаяСтрока.Номенклатура;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

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

Обратим внимание: в конце процедуры ПриИзменении обязательно нужно обновить значение переменной ПредыдущееЗначениеРеквизита. Если пользователь поменяет номенклатуру в этой же строке еще раз, не выходя из режима редактирования строки, переменная будет содержать актуальное предыдущее значение.

Способ 2. Использование события поля «ОбработкаВыбора»

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

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


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

Важный нюанс: если пользователь не выбирает значение из списка, а очищает поле с помощью кнопки очистки (крестик), событие ОбработкаВыбора не сработает — для этого платформа вызывает отдельное событие Очистка.

Способ 3. Добавление служебной колонки в реквизиты формы

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

  1. В дереве реквизитов формы открываем табличную часть Объект.Товары и добавляем новую колонку формы (не реквизит метаданных документа), например СтараяНоменклатура.
  2. При открытии формы или чтении данных на сервере копируем исходные значения номенклатуры в служебную колонку.
  3. В обработчике ПриИзменении выполняем проверку:

&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Если ТекущаяСтрока.Реквизит1 = ТекущаяСтрока.СтараяНоменклатура Тогда
        ТекущаяСтрока.Реквизит1 = ТекущаяСтрока.Номенклатура;
    КонецЕсли;
    
    // Синхронизируем служебную колонку с новым значением
    ТекущаяСтрока.СтараяНоменклатура = ТекущаяСтрока.Номенклатура;
КонецПроцедуры

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

← На главную