Как при вводе на основании открыть существующий документ и отменить создание нового?

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

При разработке в среде 1С:Предприятие (особенно на Обычных формах) часто возникает задача: ограничить создание дублирующих документов. Типичный сценарий — пользователь вводит документ на основании другого (например, «Контроль качества» на основании «Заказ-наряда»). Если такой документ уже был создан ранее, система по умолчанию все равно создаст новый пустой объект и откроет его форму. Наша цель — перехватить этот процесс, найти существующий документ, показать его пользователю, а открытие формы нового — отменить.

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

Почему стандартная логика не закрывает форму?

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

Установка СтандартнаяОбработка = Ложь в данном случае отключает только механизм автоматического копирования реквизитов по метаданным, но не останавливает процесс инициализации и отображения формы. Для решения проблемы нам нужно передать сигнал из модуля объекта в модуль формы о том, что открытие нужно прервать.

Шаг 1. Поиск существующего документа в модуле объекта

Первым делом проанализируем ситуацию в модуле объекта. Нам необходимо реализовать функцию поиска, которая проверит, не вводился ли уже документ по данному основанию. Рассмотрим пример кода в обработчике ОбработкаЗаполнения.

Мы будем использовать механизм ДополнительныеСвойства. Это специальная структура, которая «живет» внутри объекта и доступна на протяжении всего времени его существования в памяти. Она идеально подходит для передачи флагов между разными модулями.


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

Функция поиска ПроверитьСуществованиеДокумента должна выполнять запрос к базе. Важно проверять не только наличие ссылки, но и то, что документ не помечен на удаление (если это требуется по бизнес-логике).


Функция ПроверитьСуществованиеДокумента(ДокументОснование)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |   Док.Ссылка КАК Ссылка
        |ИЗ
        |   Документ.BR_КонтрольКачества КАК Док
        |ГДЕ
        |   Док.ЗаказНаряд = &Основание
        |   И НЕ Док.ПометкаУдаления";
    
    Запрос.УстановитьПараметр("Основание", ДокументОснование);
    РезультатЗапроса = Запрос.Выполнить();
    
    Если РезультатЗапроса.Пустой() Тогда
        Возврат Неопределено;
    Иначе
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Ссылка;
    КонецЕсли;
    
КонецФункции

Шаг 2. Прерывание открытия в модуле формы

Теперь выясним причину, по которой флаг в ДополнительныеСвойства сам по себе не работает. Флаг установлен, но форма все равно открывается, потому что ее никто не остановил. Нам нужно зайти в Модуль формы документа и прописать логику в событии ПередОткрытием.

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


Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
    
    // Проверяем наличие нашего секретного флага в объекте
    Если ЭтотОбъект.ДополнительныеСвойства.Свойство("ОтменаОткрытия") Тогда
        
        // Если флаг установлен и он равен Истина - отменяем открытие этой формы
        Если ЭтотОбъект.ДополнительныеСвойства.ОтменаОткрытия = Истина Тогда
            Отказ = Истина; 
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

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

Альтернативный подход: Перехват команды на форме-владельце

Рассмотрим ситуацию с другой стороны. Можно ли вообще не запускать процесс создания нового документа, если старый уже существует? Да, это возможно, если мы управляем кнопками в форме «Заказ-наряда» (источника).

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

  1. В форме документа-основания находим командную панель.
  2. Добавляем кнопку «Создать контроль качества» (или заменяем стандартную).
  3. В обработчике нажатия пишем код, который сначала ищет существующий документ.
  4. Если документ есть — открываем его.
  5. Если документа нет — вызываем штатную процедуру заполнения.

Пример кода для кнопки в форме «Заказ-наряда»:


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

Сравнение методов

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

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

Распространенные ошибки

Разберем типичные ошибки, которые допускают разработчики при реализации этой задачи:

  1. Использование события ПриОткрытии: В этом событии параметр Отказ уже недоступен. Если вы попытаетесь вызвать ЭтаФорма.Закрыть() в ПриОткрытии, форма может успеть «мелькнуть» перед пользователем, что выглядит непрофессионально. Всегда используйте ПередОткрытием.
  2. Забытая проверка на ПометкуУдаления: Если старый документ помечен на удаление, пользователь, скорее всего, хочет создать новый актуальный документ. Не забудьте добавить И НЕ ПометкаУдаления в ваш SQL-запрос.
  3. Проблемы с Управляемыми формами: Помните, что описанный выше метод с ДополнительныеСвойства в ПередОткрытием работает стабильно именно в Обычных формах. В Управляемых формах (УФ) объект и форма живут в разных контекстах, и передача свойств через ЭтотОбъект в момент открытия формы работает иначе (требуется использование параметров формы или временного хранилища).

Подводя итог, можно сказать, что связка ОбработкаЗаполнения + ДополнительныеСвойства + ПередОткрытием является «золотым стандартом» для решения задачи контроля дублей при вводе на основании в классических конфигурациях 1С.

← На главную