Как ограничить количество документов, создаваемых на основании в 1С

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

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

Метод 1: Проверка в модуле объекта (ОбработкаЗаполнения)

Самый распространенный и логичный способ — внедрить проверку непосредственно в момент создания нового документа. Для этого используется стандартный обработчик ОбработкаЗаполнения в модуле документа. Рассмотрим, как это работает: когда пользователь нажимает кнопку «Создать на основании», платформа инициализирует новый объект и вызывает данную процедуру, передавая в нее ссылку на документ-основание.

Проанализируем пример кода, который выполняет поиск уже созданных документов в базе данных:


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

Разберем, почему мы используем именно ВызватьИсключение. Если просто вывести сообщение пользователю, форма нового документа все равно откроется. Исключение же прерывает процесс создания, и пустая форма не будет докучать пользователю. Также обратите внимание на условие НЕ Заселение.ПометкаУдаления. Это важно: если предыдущий документ был ошибочным и его пометили на удаление, система должна позволить создать новый корректный документ.

Метод 2: Интеллектуальное перенаправление в форме

Иногда простая блокировка может раздражать пользователя. Более «дружелюбный» интерфейс подразумевает, что вместо ошибки система предложит открыть уже существующий документ. Реализовать это сложнее, так как ОбработкаЗаполнения выполняется на сервере в контексте объекта, а открытие формы — это клиентская операция.

Рассмотрим алгоритм реализации такого «умного» открытия:

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

Посмотрим на пример реализации в модуле формы:


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

Метод 3: Использование регистров для контроля «Остатков»

Метод с простым запросом к документам хорош для простых систем, но в высоконагруженных базах он имеет два недостатка: скорость работы (запрос по неиндексированным полям основания может быть медленным) и «гонка данных» (когда два пользователя одновременно создают документы). Выясним, как сделать решение профессиональным.

Для гарантированного контроля рекомендуется использовать Регистр Сведений или Регистр Накопления (в зависимости от задачи). Рассмотрим вариант с регистром сведений «СостоянияОснований». Мы можем создать запись в регистре в момент проведения подчиненного документа. Если при попытке записи нового документа система видит активную запись в регистре — создание запрещается.

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

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

Место проверки: ОбработкаЗаполнения vs ПередЗаписью

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

Для обеспечения 100% надежности проверку дублей стоит дублировать в событии ПередЗаписью модуля объекта. Проанализируем, как это реализовать:


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

Дополнительные рекомендации

Рассмотрим еще несколько практических советов, которые помогут сделать систему удобнее:

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

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

← На главную