Как найти и открыть последний созданный документ по бизнес-процессу в 1С

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

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

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

Анализ исходной проблемы и архитектурных ошибок

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

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

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

Почему нельзя делать выборку «без запроса»?

На форумах начинающие разработчики часто спрашивают: «Можно ли обойтись без написания запроса?». Технически в платформе «1С:Предприятие» существует метод выборки объектов через менеджер менеджера документа:

Выборка = Документы.ОценкаЗначимостиРиска.Выбрать();

Однако использовать этот метод для решения нашей задачи категорически не рекомендуется по следующим причинам:

Именно поэтому использование механизма запросов (язык SQL на уровне платформы 1С) является единственным профессиональным и верным решением. Запрос позволяет СУБД выполнить поиск на уровне индексов таблицы и вернуть на сервер приложений только одну нужную нам ссылку.

Шаг 1. Пишем оптимизированный запрос для поиска последнего документа

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


ВЫБРАТЬ ПЕРВЫЕ 1
    ОценкаЗначимостиРиска.Ссылка КАК Ссылка
ИЗ
    Документ.ОценкаЗначимостиРиска КАК ОценкаЗначимостиРиска
ГДЕ
    ОценкаЗначимостиРиска.БизнесПроцесс = &БизнесПроцесс
УПОРАДОЧИТЬ ПО
    ОценкаЗначимостиРиска.МоментВремени УБЫВ

Давайте проанализировать ключевые особенности этого запроса:

Конструкция ПЕРВЫЕ 1: Этот оператор указывает СУБД, что нас интересует только одна запись. Как только база данных найдет первую строку, соответствующую условиям сортировки и отбора, она мгновенно прекратит сканирование таблицы. Это колоссально экономит ресурсы сервера.

Сортировка по Моменту Времени: Обратите внимание, что мы сортируем результат не по полю Дата, а по виртуальному полю МоментВремени. Почему это важно? Дата в 1С хранится с точностью до секунды. Если в рамках одной секунды (например, при групповой обработке или программном создании) будут созданы два документа по одному бизнес-процессу, сортировка по дате станет нестабильной — СУБД может вернуть эти документы в случайном порядке. МоментВремени включает в себя не только дату и время, но и уникальный идентификатор ссылки (GUID), что гарантирует абсолютно точную и последовательную сортировку на уровне базы данных. В качестве альтернативы можно использовать конструкцию: УПОРАДОЧИТЬ ПО Дата УБЫВ, Ссылка УБЫВ.

Шаг 2. Реализуем функцию получения документа в модуле менеджера или общей функции

Теперь оформим наш запрос в виде готовой функции на языке BSL. Эту функцию лучше всего разместить в модуле менеджера документа ОценкаЗначимостиРиска с директивой экспорта, либо непосредственно в модуле формы (если она используется локально). Мы рассмотрим вариант функции, которую можно вызывать из любого места конфигурации.

Обратите внимание на то, как мы возвращаем результат. Нам не нужно использовать циклы Пока Выборка.Следующий(). Достаточно использовать простое условие:


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

Шаг 3. Интегрируем логику в событие открытия формы документа

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

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

В модуле формы документа пишем следующий код:


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

И на клиентской стороне в обработчике ПриОткрытии мы выполняем перенаправление:


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

Резюме и правила хорошего тона при разработке в 1С

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

  1. Никогда не делайте выборку всей таблицы через метод Выбрать(), если вам нужно найти конкретный документ по произвольному реквизиту. Всегда используйте запросы с конструкцией ГДЕ.
  2. Если вам нужен только один самый свежий или самый первый документ, обязательно ограничивайте выборку инструкцией ПЕРВЫЕ 1. Это минимизирует нагрузку на СУБД.
  3. Для сортировки по времени используйте МоментВремени, а не обычную дату. Это убережет вашу систему от логических коллизий, когда несколько документов создаются в одну секунду.
  4. Избегайте избыточных проверок количества документов, если ваша конечная цель — получить сам документ. Сразу выполняйте поиск документа: если результат пустой — значит, документов нет. Это экономит драгоценное время работы сервера баз данных.

Применяя эти несложные, но крайне важные правила оптимизации, вы сможете создавать надежные и быстродействующие механизмы в любых прикладных решениях на платформе «1С:Предприятие 8»!

← На главную