Как установить контроль ввода документа на основании и запретить создание дублей?

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

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

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

Реализация проверки в процедуре ОбработкаЗаполнения

Основная точка входа при создании документа на основании — это процедура ОбработкаЗаполнения, расположенная в модуле объекта подчиненного документа. Именно здесь мы анализируем данные, которые передаются из родительского объекта.

Разберем типичную ошибку, с которой сталкиваются начинающие разработчики. При попытке выполнить запрос в этой процедуре нельзя обращаться к реквизиту ЭтотОбъект.ДокументОснование, так как на момент выполнения заполнения документ еще не записан в базу данных, и реквизит может быть не инициализирован. Правильный подход — использовать параметр ДанныеЗаполнения.

Проанализируем пример кода, который решает задачу блокировки повторного ввода:


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

Почему важно проверять пометку на удаление?

Обратим внимание на условие И НЕ ПроформаНаОплату.ПометкаУдаления в тексте запроса. Если пользователь создал документ по ошибке и пометил его на удаление, система должна позволить создать новый корректный документ на основании того же заказа. Без этой проверки пользователь окажется в «тупике»: старый документ не нужен, а новый создать нельзя.

Альтернативный вариант: Открытие существующей формы

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

Для реализации такого сценария в обычном приложении УПП следует использовать один из следующих подходов:

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

Рассмотрим пример кода для модуля формы:


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

Гарантированная защита в событии ПередЗаписью

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

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

Технические нюансы: Индексирование реквизита

Чтобы система работала быстро даже при наличии сотен тысяч документов, крайне важно проверить свойства реквизита ДокументОснование в конфигураторе. Установите свойство Индексировать (или Индексировать с дополнительным упорядочиванием). Это позволит SQL-серверу мгновенно находить связанные записи, не перебирая всю таблицу документов.

Использование подписок на события

Если ваша конфигурация УПП находится на поддержке и вы хотите минимизировать изменения в типовых объектах, рекомендуется использовать Подписки на события. Вы можете создать одну общую подписку на событие ПередЗаписью для всех документов, требующих контроля уникальности основания. Это позволит хранить всю логику проверок в одном общем модуле, не изменяя код самих документов.

Разберем, как это работает:

  1. Создаем новый объект ПодпискаНаСобытие.
  2. В качестве источника выбираем нужные типы документов.
  3. В качестве события выбираем ПередЗаписью.
  4. В обработчике подписки пишем универсальный запрос, проверяющий наличие других документов с таким же значением реквизита ДокументОснование.

Резюме: Для надежного контроля в УПП лучше всего сочетать ОбработкаЗаполнения (для оперативного информирования пользователя) и ПередЗаписью (для жесткого контроля целостности данных), не забывая про индексацию реквизитов поиска.

← На главную