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