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