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