Как организовать проверку и исправление реквизитов перед записью в управляемых формах 1С

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

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

Разберем, как правильно организовать архитектуру проверки данных в управляемых формах, избежать каскадов процедур и предотвратить нежелательное закрытие формы при нажатии кнопки «Записать».

Проблема асинхронности и почему возникает каскад процедур

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

В результате проверку второго реквизита приходится дублировать в процедуре завершения первого вопроса. Для трех и более реквизитов это приводит к лавинообразному росту количества процедур вида ПередЗаписьюПредложитьИсправить2йРеквизитВопросЗавершение().

Рассмотрим основные способы решения этой задачи.

Вариант 1. Шаблон «Очередь проверок» через единый диспетчер

Чтобы исключить жесткую связку между проверками, применим шаблон очереди. Выделим логику формирования списка ошибок и отдельную процедуру-диспетчер, которая последовательно извлекает задачи из очереди и открывает соответствующие диалоги.

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


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

КонецПроцедуры

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

КонецФункции

&НаКлиенте
Процедура ВыполнитьСледующуюПроверку(ПараметрыКонтекста)
    
    Если ПараметрыКонтекста.ОчередьПроверок.Количество() = 0 Тогда
        // Все вопросы согласованы, перезапускаем запись объекта
        ЭтотОбъект.ПроверкиПройдены = Истина;
        ЭтотОбъект.Записать(ПараметрыКонтекста.ПараметрыЗаписи);
        ЭтотОбъект.ПроверкиПройдены = Ложь;
        Возврат;
    КонецЕсли;
    
    ТекущаяПроверка = ПараметрыКонтекста.ОчередьПроверок[0];
    ПараметрыКонтекста.ОчередьПроверок.Удалить(0);
    
    Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопросПроверки", ЭтотОбъект, ПараметрыКонтекста);
    ПоказатьВопрос(Оповещение, ТекущаяПроверка.Текст, РежимДиалогаВопрос.ДаНет);

КонецПроцедуры

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

КонецПроцедуры

Вариант 2. Использование асинхронных функций платформы (Ждать)

Начиная с версии платформы 8.3.18, в платформе 1С реализованы асинхронные вызовы через ключевые слова Асинх и Ждать. Это позволяет избавиться от объектов ОписаниеОповещения и писать код последовательно в циклах.

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


&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    
    Если Не ЭтотОбъект.ПроверкиПройдены Тогда
        Отказ = Истина;
        ПроверитьРеквизитыИЗаписатьАсинх(ПараметрыЗаписи);
    КонецЕсли;

КонецПроцедуры

&НаКлиенте
Асинх Процедура ПроверитьРеквизитыИЗаписатьАсинх(ПараметрыЗаписи)
    
    СписокРеквизитов = Новый Массив;
    СписокРеквизитов.Добавить("Артикул");
    СписокРеквизитов.Добавить("Наименование");
    СписокРеквизитов.Добавить("НаименованиеПолное");
    
    Для Каждого ИмяРеквизита Из СписокРеквизитов Цикл
        Если СодержитНекорректныеСимволы(Объект[ИмяРеквизита]) Тогда
            ТекстВопроса = СтрШаблон("В поле ""%1"" обнаружены ошибки. Исправить автоматически?", ИмяРеквизита);
            Ответ = Ждать ВопросАсинх(ТекстВопроса, РежимДиалогаВопрос.ДаНет);
            
            Если Ответ = КодВозвратаДиалога.Да Тогда
                Объект[ИмяРеквизита] = ИсправитьСимволы(Объект[ИмяРеквизита]);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    
    // Устанавливаем флаг, чтобы не блокировать повторный вызов записи
    ЭтотОбъект.ПроверкиПройдены = Истина;
    ЭтотОбъект.Записать(ПараметрыЗаписи);
    ЭтотОбъект.ПроверкиПройдены = Ложь;

КонецПроцедуры

Вариант 3. Использование общего модуля в ОписанииОповещения

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


&НаКлиенте
Процедура ЗапроситьОписаниеФайла(ПараметрыФайла)
    
    ДополнительныеПараметры = Новый Структура("Файл", ПараметрыФайла);
    
    // Передаем ссылку на общий клиентский модуль вместо формы
    Оповещение = Новый ОписаниеОповещения(
        "ПослеВводаОписанияФайла", 
        РаботаСФайламиКлиент, 
        ДополнительныеПараметры
    );
    
    ПоказатьВводСтроки(Оповещение, "", "Введите описание файла");

КонецПроцедуры

Процедура в общем модуле должна быть обязательно объявлена с директивой &НаКлиенте и ключевым словом Экспорт:


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

КонецПроцедуры

Решение проблемы с закрытием формы при нажатии «Записать»

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

Разберем причины и способы устранения такого поведения:

  1. Анализ и сохранение структуры параметров записи: В обработчике ПередЗаписью(Отказ, ПараметрыЗаписи) параметр ПараметрыЗаписи содержит служебные свойства выполнения команды. В типовых конфигурациях на БСП за закрытие формы отвечает свойство ПараметрыЗаписи.Закрыть. Если мы передаем сохраненную структуру обратно в метод Записать(ПараметрыКонтекста.ПараметрыЗаписи), форма сохраняет поведение, задуманное пользователем.
  2. Управление стандартными командами формы: Если на форме переопределяется логика команды записи через собственную клиентскую команду, стандартная кнопка «Записать» может оставаться на панели. Чтобы убрать стандартную команду и избежать дублирования кнопок:
    1. Откройте свойства управляемой формы в конфигураторе.
    2. Найдите свойство Состав команд и нажмите «Открыть».
    3. В открывшемся окне найдите стандартную команду Записать и снимите с нее флаг видимости/использования.

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

← На главную