Как в 1С программно установить другой документ в качестве регистратора регистра накопления?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Почему не работает прямое присвоение регистратора в обработке проведения

Проанализируем стандартный подход, который часто пытаются использовать разработчики:


Для Каждого Стр Из ТЧПокупатели Цикл
    Движения.ВзаиморасчетыСКонтрагентами.Записывать = Истина;
    Движение = Движения.ВзаиморасчетыСКонтрагентами.Добавить();
    Движение.Период = Дата;
    Движение.Организация = Стр.Организация;
    Движение.Партнер = Стр.Партнер;
    Движение.Сумма = Стр.Сумма;
    Движение.Регистратор = ДокументОснование; // Попытка подмены
КонецЦикла;

Выясним причину, по которой такой код не дает нужного результата. Коллекция Движения, доступная в контексте объекта документа, жестко связана с текущим экземпляром. На уровне ядра платформы 1С при вызове процедуры записи движений значение поля Регистратор для всех формируемых записей принудительно перезаписывается ссылкой на тот документ, которому принадлежит эта коллекция. Любое ручное переопределение свойства Регистратор внутри коллекции Движения платформа просто игнорирует.

Кроме того, действует строгое ограничение метаданных: документ не может стать регистратором записей регистра накопления, если в конфигураторе на вкладке Регистраторы у данного регистра для него не установлен соответствующий флажок. Попытка записать движения по документу, не входящему в состав регистраторов, приведет к аварийному завершению операции с ошибкой платформы.

Решение: программное формирование движений через НаборЗаписей

Рассмотрим, как правильно решить задачу записи движений под другим регистратором. Если нам действительно требуется привязать движения к стороннему документу (например, к ссылке из реквизита ДокументОснование), необходимо работать не через свойство Движения текущего документа, а напрямую через менеджер регистра с использованием объекта РегистрНакопленияНаборЗаписей.

Разберем по шагам корректный алгоритм формирования записей:

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

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


// Создаем набор записей целевого регистра
НаборЗаписей = РегистрыНакопления.ВзаиморасчетыСКонтрагентами.СоздатьНаборЗаписей();

// Устанавливаем отбор по документу-основанию
НаборЗаписей.Отбор.Регистратор.Установить(ДокументОснование);

// Считываем текущие движения, чтобы не затереть их при перезаписи
НаборЗаписей.Прочитать();

// Добавляем новые движения на основании строк табличной части
Для Каждого Стр Из ТЧПокупатели Цикл
    НовоеДвижение = НаборЗаписей.Добавить();
    НовоеДвижение.ВидДвижения = ВидДвиженияНакопления.Приход;
    НовоеДвижение.Период = Дата;
    НовоеДвижение.Организация = Стр.Организация;
    НовоеДвижение.Партнер = Стр.Партнер;
    НовоеДвижение.Сумма = Стр.Сумма;
КонецЦикла;

// Записываем набор записей
НаборЗаписей.Записать();

Обратите внимание: при таком подходе указывать поле Регистратор в строке движения не требуется вовсе, так как оно автоматически заполняется значением, указанным в фильтре Отбор.Регистратор.

Технические риски и скрытые проблемы подхода

Хотя технически запись через НаборЗаписей позволяет достичь цели, этот механизм несет в себе серьезные архитектурные риски, о которых важно помнить перед внедрением в рабочую базу:

  1. Случайное удаление существующих проводок. Если перед добавлением новых строк не вызвать НаборЗаписей.Прочитать(), вызов метода Записать() полностью сотрет все предыдущие движения, которые были у документа-основания в данном регистре.
  2. Поведение при отмене проведения и пометке на удаление. Даже если у документа-основания модуль объекта пуст, на уровне конфигурации для него задано свойство УдалениеДвижений (по умолчанию «Удалять автоматически»). Если пользователь пометит документ-основание на удаление или отменит его проведение, платформа 1С автоматически удалит все его движения по регистру. В результате движения, созданные текущим подчиненным документом, бесследно пропадут.
  3. Блокировки данных и взаимные блокировки (Deadlocks). Модификация записей чужого регистратора внутри транзакции проведения текущего документа захватывает исключительные блокировки сразу на две сущности. При высокой интенсивности работы пользователей это неизбежно спровоцирует взаимоблокировки транзакций и снизит общую производительность системы.
  4. Искажение итогов при удалении данных. Если документ-основание будет физически удален без контроля ссылочной целостности, в таблице итогов регистра останутся «битые» ссылки, что приведет к расхождению остатков и ошибкам при пересчете итогов.

Архитектурные альтернативы подмене регистратора

Рассмотрим более надежные и штатные способы решения бизнес-задач, не нарушающие концепцию ссылочной целостности платформы 1С:

  1. Добавление аналитического измерения в регистр. Это наиболее правильный подход. Регистратором должен оставаться тот документ, который фактически инициирует хозяйственную операцию и проводится в текущий момент. Если в отчетах необходимо видеть привязку к исходному событию, добавьте в регистр измерение (или реквизит) с типом ссылки на документ (например, Заказ, Сделка или ДокументОснование). Вся аналитика останется прозрачной, а типовые механизмы проведения и отмены проведения не будут нарушены.
  2. Штатное проведение документа-основания. Если по методологии учета движения обязан формировать именно документ-основание, то логику проводок следует перенести в его модуль. Текущий документ должен лишь инициировать проведение основания программно:
    
    ОбъектОснование = ДокументОснование.ПолучитьОбъект();
    ОбъектОснование.Записать(РежимЗаписиДокумента.Проведение);
    
  3. Использование корректировочного документа. Если необходимо внести исправления в движения задним числом, лучшей практикой является использование концепции документа ручной корректировки (аналог типовой «Операции» или «Корректировки записей регистров»), где движения принадлежат самому документу корректировки, а в комментариях и аналитике фиксируется основание изменений.
← На главную