Почему программные изменения движений документа не сохраняются и как это исправить?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет
← К списку

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

Понимание жизненного цикла движений документа

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

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

Решение 1: Изменение движений в процессе проведения документа (в обработчике ОбработкаПроведения)

Если нам необходимо скорректировать движения прямо во время проведения документа, мы должны работать непосредственно с коллекцией Движения, доступной в модуле объекта документа. Рассмотрим подробнее, как это сделать.

Суть подхода:

  1. Платформа формирует движения и помещает их во временную коллекцию Движения.
  2. В процедуре ОбработкаПроведения мы получаем доступ к этой коллекции.
  3. Мы можем итерировать по записям каждого регистра в этой коллекции, изменять их или добавлять новые.
  4. По завершении ОбработкаПроведения, платформа автоматически записывает все модифицированные наборы записей из коллекции Движения в базу данных.

Пример работы с движениями регистра бухгалтерии:

Допустим, нам нужно изменить субконто для определенных проводок в регистре бухгалтерии Хозрасчетный. Мы можем сделать это следующим образом:


// В модуле объекта документа, в процедуре ОбработкаПроведения

// Убедимся, что движения по Хозрасчетному регистру записываются
Движения.Хозрасчетный.Записывать = Истина;

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

Важные моменты:

Решение 2: Корректировка движений уже проведенного документа (после записи)

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

Разберем по шагам, как это сделать:

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

Ключевые моменты для сохранения изменений и предотвращения их перезаписи:

Пример кода для корректировки движений после записи:


// Предположим, у нас есть ссылка на документ, который нужно скорректировать
ДокументСсылка = СсылкаНаДокументКорректировки;

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

// Создаем набор записей для регистра Хозрасчетный
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();

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

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

// Устанавливаем флаг ОбменДанными.Загрузка для предотвращения срабатывания стандартных обработчиков
НаборЗаписей.ОбменДанными.Загрузка = Истина;

// Перебираем записи и вносим необходимые изменения
Для Каждого Запись Из НаборЗаписей Цикл
    // Например, если счет дебета 26 и нужно изменить субконто "СтатьиЗатрат"
    Если Запись.СчетДт.Код = "26" И Запись.СубконтоДт.Свойство("СтатьиЗатрат") Тогда
        // Проанализируем текущее значение и установим новое
        Если Запись.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоНаименованию("Старые расходы") Тогда
            Запись.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоНаименованию("Новые расходы");
        КонецЕсли;
    КонецЕсли;
    // Можем также изменять суммы, валютные суммы, количество и т.д.
    // Запись.Сумма = Запись.Сумма * 1.1;
КонецЦикла;

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

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

Современные подходы и лучшие практики для модификации движений

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

1. Подписки на события (ПодпискиНаСобытия)

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

2. Расширения конфигурации (Расширения)

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

Пример использования расширения с аннотацией &После:


// В модуле объекта документа из расширения
&После("ОбработкаПроведения")
Процедура Документ_ОбработкаПроведенияПосле(Отказ, РежимПроведения)
    // Здесь ДокументОбъект - это ссылка на текущий документ
    // Мы имеем доступ к коллекции Движения ДокументОбъект.Движения
    
    // Проанализируем и скорректируем движения, если это необходимо
    Для Каждого ЗаписьДвижения Из ДокументОбъект.Движения.Хозрасчетный Цикл
        Если ЗаписьДвижения.СчетДт.Код = "26" Тогда
            // Например, установим конкретное субконто
            Если ЗаписьДвижения.СубконтоДт.Свойство("СтатьиЗатрат") Тогда
                ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоНаименованию("Административные расходы");
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

3. Использование регистров сведений для правил изменения аналитики

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

Пример концепции регистра сведений "ПравилаОтраженияЗатрат":

Пример использования такого регистра в коде:


// В процедуре ОбработкаПроведения или в подписке/расширении
Для Каждого ЗаписьДвижения Из Движения.Хозрасчетный Цикл
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    Правила.НовыйСчет,
        |    Правила.НоваяСтатьяЗатрат
        |ИЗ
        |    РегистрСведений.ПравилаОтраженияЗатрат КАК Правила
        |ГДЕ
        |    Правила.ИсходныйСчет = &ИсходныйСчет
        |    И Правила.ИсходнаяСтатьяЗатрат = &ИсходнаяСтатьяЗатрат
        |    И Правила.Действует = ИСТИНА";
    
    Запрос.УстановитьПараметр("ИсходныйСчет", ЗаписьДвижения.СчетДт);
    
    // Проверим, есть ли субконто "СтатьиЗатрат"
    Если ЗаписьДвижения.СубконтоДт.Свойство("СтатьиЗатрат") Тогда
        Запрос.УстановитьПараметр("ИсходнаяСтатьяЗатрат", ЗаписьДвижения.СубконтоДт.СтатьиЗатрат);
    Иначе
        Запрос.УстановитьПараметр("ИсходнаяСтатьяЗатрат", Неопределено); // Или другое значение по умолчанию
    КонецЕсли;
    
    РезультатЗапроса = Запрос.Выполнить().Выбрать();
    
    Если РезультатЗапроса.Следующий() Тогда
        // Если найдено правило, применяем его
        ЗаписьДвижения.СчетДт = РезультатЗапроса.НовыйСчет;
        Если ЗаписьДвижения.СубконтоДт.Свойство("СтатьиЗатрат") Тогда
            ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = РезультатЗапроса.НоваяСтатьяЗатрат;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Частые ошибки и важные рекомендации

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

Следуя этим рекомендациям и выбирая подходящий подход, мы сможем эффективно и безопасно управлять движениями документов в 1С.

← К списку