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