В процессе разработки интерфейсов на платформе 1С:Предприятие 8.3 программисты часто сталкиваются со специфическим поведением таблиц управляемых форм. Один из наиболее коварных сценариев — когда стандартное событие таблицы ПередОкончаниемРедактирования стабильно отрабатывает при изменении одних колонок, но полностью игнорируется (пропускается) при изменении других, например, числовых показателей.
В этой статье мы подробно проанализируем, почему возникает эта проблема, разберем особенности жизненного цикла редактирования строки в управляемых формах, а также рассмотрим надежные архитектурные решения для реализации контроля уникальности строк.
Представим рабочую ситуацию: на форме программно или интерактивно создана таблица (например, список долей распределения заработной платы по статьям калькуляции и судам). В таблице реализовано требование уникальности — нельзя добавлять строки с одинаковой комбинацией ключевых полей (в данном случае это колонки СтатьяКалькуляции и Судно). Третья колонка — числовая (доля распределения).
Для проверки уникальности разработчик использует событие таблицы ПередОкончаниемРедактирования. Однако при тестировании выявляется странная закономерность:
ПередОкончаниемРедактирования попросту не вызывается. Строка успешно сохраняется в памяти формы, приводя к появлению дубликатов.Давайте разберем по шагам, какие скрытые механизмы платформы приводят к такому поведению и как это исправить.
Основная и наиболее частая причина «исчезновения» клиентских событий завершения редактирования строки кроется во взаимодействии клиента и сервера при изменении данных. В управляемом приложении 1С действует жесткое правило: если во время изменения значения в ячейке происходит серверный вызов, который приводит к обновлению структуры или данных формы, режим интерактивного редактирования строки аварийно завершается на стороне платформы.
Даже если в модуле формы отсутствуют явные обработчики для числовой колонки, платформа может выполнять неявные действия:
ПриИзменении может быть назначен не на сам элемент формы (поле ввода ячейки), а непосредственно на реквизит объекта или формы на уровне метаданных.ПередОкончаниемРедактирования и ПриОкончанииРедактирования просто пропускаются.Обратим внимание на условие, которое разработчики часто используют в обработчике события:
Если Не НоваяСтрока Или НоваяСтрока И Не ОтменаРедактирования Тогда
// Код проверки уникальности
КонецЕсли;
В языке программирования 1С оператор И имеет более высокий приоритет, чем оператор ИЛИ. Из-за этого платформа вычисляет данное выражение следующим образом:
(Не НоваяСтрока) Или (НоваяСтрока И (Не ОтменаРедактирования))
Рассмотрим, как вычисляется это условие в различных сценариях:
НоваяСтрока = Ложь), левая часть выражения Не НоваяСтрока превращается в Истина. Всё условие становится истинным, независимо от правой части. Проверка выполняется.НоваяСтрока = Истина) и пользователь нажимает клавишу Esc для отмены (ОтменаРедактирования = Истина), левая часть дает Ложь, правая часть вычисляется как Истина И Ложь = Ложь. Условие целиком дает Ложь, проверка обходится. Это правильное поведение.НоваяСтрока или ОтменаРедактирования принимают неочевидные для платформы значения, приоритет операторов может сыграть злую шутку.Для предотвращения логических ошибок всегда явно расставляйте скобки, даже если вы уверены в приоритетах по умолчанию:
Если (Не НоваяСтрока) Или (НоваяСтрока И Не ОтменаРедактирования) Тогда
// Код проверки
КонецЕсли;
Если таблица формы и её колонки создаются программно в коде модуля формы (например, в процедуре ПриСозданииНаСервере), важно помнить о правильной привязке путей к данным и типах элементов управления.
Если для числовой колонки неверно сопоставлен путь к данным (например, тип колонки в программно созданной таблице значений на форме не совпадает с типом элемента управления ПолеФормы), платформа может некорректно обрабатывать завершение ввода. В этом случае строка просто сбрасывает фокус ввода без инициации штатных событий окончания редактирования всей строки.
Также проверьте свойство таблицы РежимРедактирования. Оно должно быть установлено в значение ВходПриВводе или Непосредственно. При определенных сочетаниях свойств колонок изменение числового значения может интерпретироваться платформой как завершение интерактивного ввода с автоматическим сохранением без вызова клиентских проверок.
Использование только клиентского события ПередОкончаниемРедактирования для контроля уникальности строк — это архитектурная уязвимость (антипаттерн). Эту проверку легко обойти при копировании строк кнопками меню, программном заполнении или по нажатию клавиш отмены в специфических сценариях.
Рассмотрим два надежных способа организации валидации данных в таблицах.
Поскольку уникальность строки определяется ключевыми полями (СтатьяКалькуляции и Судно), логично выполнять проверку сразу при их изменении, а не дожидаться, пока пользователь заполнит всю строку и попытается выйти из нее.
Добавим обработчик события ПриИзменении для элементов управления ключевых колонок:
&НаКлиенте
Процедура ДолиПоСтатьямЗаработнойПлатыСтатьяКалькуляцииПриИзменении(Элемент)
ТекущиеДанные = Элементы.ДолиПоСтатьямЗаработнойПлаты.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Выполняем проверку дубликатов в таблице
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("СтатьяКалькуляции", ТекущиеДанные.СтатьяКалькуляции);
ПараметрыОтбора.Вставить("Судно", ТекущиеДанные.Судно);
НайденныеСтроки = ДолиПоСтатьямЗаработнойПлаты.НайтиСтроки(ПараметрыОтбора);
// Если найдено более одной строки (с учетом текущей редактируемой)
Если НайденныеСтроки.Количество() > 1 Тогда
ТекстОшибки = "Строка с такой статьей калькуляции и судном уже существует в таблице!";
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстОшибки, , "ДолиПоСтатьямЗаработнойПлаты.СтатьяКалькуляции");
// Очищаем некорректно заполненное поле
ТекущиеДанные.СтатьяКалькуляции = ПредопределенноеЗначение("Справочник.СтатьиКалькуляции.ПустаяСсылка");
КонецЕсли;
КонецПроцедуры
Для обеспечения 100% защиты от дублирования данных (в том числе при программном заполнении таблицы или копировании строк) обязательно реализуйте проверку на сервере в событии формы ПередЗаписьюНаСервере или в модуле объекта в событии ПередЗаписью.
Проанализируем пример серверной проверки с использованием свертки таблицы значений для быстрого поиска дубликатов:
&НаСервере
Функция ПроверитьТабличнуюЧастьНаСервере() Экспорт
// Выгружаем данные табличной части во временную Таблицу Значений
ТаблицаПроверки = ДолиПоСтатьямЗаработнойПлаты.Выгрузить();
// Запоминаем исходное количество строк
ИсходноеКоличество = ТаблицаПроверки.Количество();
// Сворачиваем по уникальным ключевым полям
ТаблицаПроверки.Свернуть("СтатьяКалькуляции, Судно");
// Если количество строк уменьшилось, значит были дубли
Если ТаблицаПроверки.Количество() <> ИсходноеКоличество Тогда
Возврат Ложь;
КонецЕсли;
Возврат Истина;
КонецФункции
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Если Не ПроверитьТабличнуюЧастьНаСервере() Тогда
Отказ = Истина;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Запись невозможна: таблица распределения содержит дублирующиеся строки по статьям калькуляции и судам!";
Сообщение.Сообщить();
КонецЕсли;
КонецПроцедуры
Если в вашей форме перестает работать событие ПередОкончаниемРедактирования, выполните следующие шаги:
ПриИзменении у колонок таблицы и связанных реквизитов, которые могут вызывать сервер.НоваяСтрока и ОтменаРедактирования.