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

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При разработке и сопровождении конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с аппаратной особенностью (так называемой «фичей») движка платформы. Суть проблемы заключается в том, что формула, назначенная колонке табличной части (ТЧ) со ссылочным типом данных, стабильно срабатывает дважды. Первый раз — непосредственно в момент интерактивного выбора элемента из справочника, а второй раз — при потере фокуса этой ячейкой (например, при нажатии клавиши Enter, переходе на другую строку или клике мышью).

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

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

Почему это происходит? Выясняем причину поведения платформы

Для того чтобы эффективно бороться с проблемой, необходимо понять внутреннюю механику работы интерфейса 1С 7.7. Когда пользователь работает с ячейкой табличной части документа, процесс выбора значения делится на два этапа:

  1. Интерактивный выбор значения. Пользователь нажимает кнопку выбора («педальку») в поле ввода, открывается форма списка справочника, в которой выбирается нужный элемент. В этот момент выбранное значение передается в поле табличной части. Платформа фиксирует изменение и в первый раз инициирует выполнение формулы, прописанной в свойствах колонки. Однако ячейка ТЧ все еще остается в режиме активного редактирования (на ней стоит фокус, мигает курсор).
  2. Завершение редактирования и потеря фокуса. Когда пользователь пытается покинуть ячейку (перейти на другой реквизит шапки или ТЧ, нажать Enter), платформа завершает сеанс редактирования данной строки. Движок 1С 7.7 воспринимает выход из режима редактирования ячейки как окончательное подтверждение ввода и повторно запускает расчет формулы колонки. Если в документе включена опция автоматического ввода новой строки, то вызовов может быть даже больше двух.

Теперь, когда логика работы платформы ясна, рассмотрим практические методы решения этой проблемы.

Решение 1. Использование флага редактирования строки (классический метод с форума)

Это самый простой способ, который не требует переписывания структуры метаданных и интерфейса. Мы можем контролировать однократность выполнения кода с помощью переменной-флага на уровне модуля формы документа.

Разберем этот алгоритм по шагам:

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

Посмотрим на пример программной реализации этого метода:


// Объявляем переменную в самом начале модуля формы документа
Перем ФлагПартияВыбрана;

Процедура ПриНачалеРедактированияСтроки()   
    // Сбрасываем флаг при начале редактирования любой строки ТЧ
    ФлагПартияВыбрана = 0;
КонецПроцедуры

Процедура ПриВыбореПартии()
    // Проверяем, выбран ли элемент и не обрабатывался ли он ранее в этой строке
    Если (ФлагПартияВыбрана = 0) И (Материал.Выбран() = 1) Тогда 
        
        // Взводим флаг, чтобы предотвратить второй вызов при потере фокуса
        ФлагПартияВыбрана = 1;
        
        // Наш полезный код
        Если Вопрос("Обновить характеристики выбранного материала?", "Да+Нет") = "Да" Тогда
            ТекМатериал = СоздатьОбъект("Справочник.Материалы");
            Если ТекМатериал.НайтиЭлемент(Материал) = 1 Тогда
                // Производим необходимые действия с объектом справочника
                ТекМатериал.Записать();  
                Форма.Обновить(); // Принудительно обновляем визуальное отображение формы
            КонецЕсли;
        КонецЕсли;  
        
    КонецЕсли;  
КонецПроцедуры

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

Решение 2. Кэширование значения (сравнение с предыдущим состоянием)

Иногда использование предопределенной процедуры ПриНачалеРедактированияСтроки() нежелательно или невозможно (например, если логика работы ТЧ сложна и завязана на динамическое добавление строк программными методами). В этом случае можно обойтись сравнением текущего значения с сохраненным предыдущим значением реквизита.

Для этого мы также объявляем переменную модуля формы для хранения старого значения:


Перем ПредыдущееЗначениеМатериала;

Процедура ПриВыбореПартии()
    // Если текущее значение в ячейке совпадает с сохраненным — это повторный холостой вызов
    Если Материал = ПредыдущееЗначениеМатериала Тогда
        Возврат; 
    КонецЕсли;
    
    Если Материал.Выбран() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Запоминаем новое значение, чтобы отсечь повторный вызов при потере фокуса
    ПредыдущееЗначениеМатериала = Материал;
    
    // Исполняемый алгоритм
    Предупреждение("Вы выбрали новый материал: " + СокрЛП(Материал.Наименование));
КонецПроцедуры

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

Решение 3. Перехват выбора через предопределенные процедуры формы (самый надежный способ)

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

Для реализации этого способа выполните следующие шаги:

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

Проанализируем пример реализации данного подхода:


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

Процедура ОбработкаВыбораЗначения(ВыбЗнач, ИдентЭлемента, ФлагСтандОбр)
    Если ИдентЭлемента = "Материал" Тогда
        // Проверяем, что пользователь действительно выбрал элемент, а не закрыл форму выбора
        Если ПолучитьПустоеЗначение("Справочник.Материалы") <> ВыбЗнач Тогда
            
            // Здесь пишем всю необходимую логику, включая вопросы пользователю.
            // Этот код гарантированно выполнится строго один раз!
            Если Вопрос("Вы подтверждаете выбор материала " + СокрЛП(ВыбЗнач.Наименование) + "?", "Да+Нет") = "Да" Тогда
                // Записываем выбранное значение непосредственно в реквизит текущей строки ТЧ
                Материал = ВыбЗнач; 
            КонецЕсли;
            
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

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

Решение 4. Использование системного метода ВыполнятьФормулуТолькоПриИзменении()

В платформе 1С 7.7 существует встроенная функция управления поведением расчета формул на форме — ВыполнятьФормулуТолькоПриИзменении(). Если вызвать этот метод при открытии формы, платформа теоретически должна блокировать вызовы формул колонок, если значение в ячейке фактически не поменялось.

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


Процедура ПриОткрытии()
    // Устанавливаем режим выполнения формул только при реальном изменении данных
    ВыполнятьФормулуТолькоПриИзменении(1);
КонецПроцедуры

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

Резюме и рекомендации

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

← На главную