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

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

Приветствуем вас, коллеги! Сегодня мы детально разберем классическую, но от этого не менее интересную задачу из практики программирования в среде «1С:Предприятие 8.3». Мы проанализируем ситуацию, когда необходимо сформировать движения по регистру накопления для документа, у которого в свойствах метаданных свойство Проведение установлено в значение Запретить.

Такой сценарий часто встречается при интеграции со сторонними системами (например, ВетИС, ЕГАИС, Честный Знак), где документы служат лишь транспортными контейнерами или логами операций, но при этом должны фиксировать остатки или обороты в оперативных регистрах. Давайте шаг за шагом разберем, почему стандартные подходы здесь дают сбой, и как правильно реализовать эту задачу без нарушения архитектуры системы.

Выясним причину первоначальной неудачи автора темы и рассмотрим два проверенных способа решения проблемы с примерами кода, а также разберем критически важные подводные камни, которые часто упускают из виду начинающие разработчики.


Почему не работал первоначальный вариант автора?

Рассмотрим код, который автор темы пытался выполнить в обработчике формы ПриЗаписиНаСервере:


НаборДвижений = ТекущийОбъект.Движения.vetis_ДанныеДляОформленияПроизвЭВСД;
НаборДвижений.Записывать = Истина;

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

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

В платформе «1С:Предприятие» механизмы записи движений жестко разделены по контексту работы с документом:

  1. Для проводимых документов: при проведении документа платформа инициализирует транзакцию, внутри которой вызывается обработчик события ОбработкаПроведения (в модуле объекта). По окончании этого обработчика платформа автоматически анализирует коллекцию Движения документа. Те наборы записей, у которых свойство Записывать установлено в значение Истина, автоматически записываются в базу данных.
  2. Для непроводимых документов: при обычной записи документа (даже если у него установлено свойство Записывать = Истина для какого-то регистра) автоматический механизм записи движений платформой не запускается. Платформа игнорирует этот флаг, поскольку документ не проходит через транзакцию проведения.

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


Вариант решения №1. Использование коллекции объекта «Движения» с явной записью

Если вы хотите продолжить работу через объект документа (например, внутри модуля формы или модуля объекта при записи), вам достаточно добавить всего одну строчку кода в конец вашего алгоритма — вызвать метод Записать() для конкретного набора движений.

Рассмотрим подробнее, как будет выглядеть правильный код в обработчике ПриЗаписиНаСервере:


&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Получаем набор движений из объекта документа
    НаборДвижений = ТекущийОбъект.Движения.vetis_ДанныеДляОформленияПроизвЭВСД;
    
    // Очищаем старые движения перед добавлением новых
    НаборДвижений.Очистить();
    
    // Заполняем новые движения на основании таблицы данных
    Для Каждого СтрокаДанных Из ТаблицаДляНабораЗаписей Цикл
        
        Движение = НаборДвижений.Добавить();
        Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
        Движение.Период = ТекущийОбъект.Дата; // Рекомендуется использовать дату документа, а не ТекущаяДата()
        
        // Заполнение измерений
        Движение.Площадка = СтрокаДанных.Площадка;
        Движение.Номенклатура = СтрокаДанных.Номенклатура;
        
        // Заполнение ресурсов
        Движение.Количество = СтрокаДанных.Количество;
        
    КонецЦикла;
    
    // КРИТИЧЕСКИ ВАЖНО: Явно вызываем метод Записать()
    Попытка
        НаборДвижений.Записать(Истина); // Параметр Истина означает замещение старых записей
    Исключение
        ЗаписьЖурналаРегистрации("ЗаписьДвижений", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
        Отказ = Истина; // Отменяем транзакцию записи документа, если движения не записались
    КонецПопытки;

КонецПроцедуры

Преимущества этого метода:


Вариант решения №2. Использование независимого набора записей регистра (Выбор автора)

Этот способ является более универсальным. Он позволяет записывать движения по регистру из любого места конфигурации: из общих модулей, внешних обработок или фоновых заданий, имея на руках лишь ссылку на документ-регистратор (без необходимости получать «тяжелый» объект документа в память через метод ПолучитьОбъект()).

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


// Создаем пустой набор записей регистра накопления
НаборЗаписей = РегистрыНакопления.vetis_ДанныеДляОформленияПроизвЭВСД.СоздатьНаборЗаписей();

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

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

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

Обратите внимание на конструкцию НаборЗаписей.Отбор.Регистратор.Установить(Объект.Ссылка). Это ключевой момент. Регистры накопления устроены так, что любая запись в них обязана иметь ссылку на документ-регистратор. Установка отбора гарантирует, что мы работаем с движениями только этого конкретного документа.


Критические подводные камни, о которых важно знать

Если вы реализуете движения для непроводимого документа, вы берете на себя всю ответственность, которую платформа 1С обычно несет при проведении стандартных документов. Разберем опасные ловушки, в которые можно попасть, если не предусмотреть защитные механизмы.

1. Проблема пометки на удаление (Очень важно!)

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

Решение: Нам необходимо написать обработчик в модуле объекта документа в событии ПередЗаписью. Мы должны проверять, меняется ли статус пометки удаления, и при необходимости очищать движения.


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

2. Запись движений для НОВОГО (еще не записанного в БД) документа

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

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

3. Использование параметра «Замещать»

Метод Записать() принимает булев параметр Замещать. По умолчанию он равен Истина. Если вы случайно укажете Записать(Ложь), платформа попытается добавить новые строки к уже существующим движениям этого документа. Это гарантированно вызовет ошибку дублирования записей в СУБД из-за нарушения уникальности ключевых полей (Период, Регистратор, НомерСтроки, Измерения).

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


Резюме

Мы детально изучили проблему программной генерации движений для непроводимых документов в 1С. Подведем итог проделанной работы:

Надеемся, данный разбор поможет вам избежать частых ошибок при проектировании нестандартных механизмов учета в ваших конфигурациях на платформе «1С:Предприятие 8.3»!

← На главную