Почему программное изменение движений документа в 1С:Бухгалтерии 3.0 не сохраняется после проведения и как это исправить?

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

Мы часто сталкиваемся с ситуациями, когда типовой функционал 1С не полностью покрывает наши специфические потребности. Один из таких случаев — необходимость программно корректировать движения документов, например, для более детального учета затрат. Давайте рассмотрим проблему, с которой столкнулся наш коллега, пытаясь выделить начисления отпускных на отдельные статьи затрат в документе "Отражение зарплаты в бухучете" в 1С:Бухгалтерии 3.0, и выясним, почему его первоначальный подход не дал желаемого результата.

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

Выясняем причину: почему изменения не сохранялись?

Прежде чем перейти к решению, проанализируем ситуацию и выясним причину, по которой первоначальный код не работал так, как ожидалось. Проблема кроется в фундаментальном различии между коллекцией движений, которую формирует документ при проведении, и отдельно созданным объектом НаборЗаписей.

Когда вы создаете новый объект с помощью РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей(), вы получаете пустой набор записей регистра. Затем вы заполняете его, читая движения по текущему регистратору:


НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ЭтотОбъект.Ссылка);
НаборЗаписей.Прочитать();

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

Для наглядности рассмотрим исходный (некорректный) фрагмент кода:


// Это НЕКОРРЕКТНЫЙ способ изменения движений документа в ОбработкеПроведения
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ЭтотОбъект.Ссылка);
НаборЗаписей.Прочитать();

Если НаборЗаписей.Количество() = 0 Тогда Возврат; КонецЕсли;

Счет20 = ПланыСчетов.Хозрасчетный.ОсновноеПроизводство;
// ... (определение других счетов)

Для каждого Запись из НаборЗаписей Цикл
    Если Запись.Содержание = "Начислен ежегодный отпуск" Тогда
        Если Запись.СчетДт = Счет20 Тогда 
            Запись.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000000022");
        // ... (другие условия для счетов)
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать(); // Здесь записывается отдельный НаборЗаписей, а не движения документа

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

Разбираем правильный подход: как корректно изменить движения документа?

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

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

  1. Доступ к коллекции движений: Внутри процедуры ОбработкаПроведения (или в подписке на событие, обрабатывающем проведение документа) мы получаем доступ к нужной коллекции движений. Для регистра бухгалтерии "Хозрасчетный" это будет ЭтотОбъект.Движения.Хозрасчетный.

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

  3. Внесение корректировок: Внутри цикла мы можем проверять условия (например, по содержанию записи, счету дебета или кредита) и изменять нужные реквизиты записи движения, такие как субконто, сумму и т.д.

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

Посмотрим на пример корректного кода, адаптированного под исходную задачу:


// Это КОРРЕКТНЫЙ способ изменения движений документа в ОбработкеПроведения
&НаСервере
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // ... (стандартный типовой код проведения документа, если он есть)
    // Например, документ уже сформировал свои типовые движения
    // Здесь мы работаем с уже сформированными движениями

    // Получаем коллекцию движений регистра "Хозрасчетный"
    ПеременнаяДвиженийХозрасчетный = ЭтотОбъект.Движения.Хозрасчетный;

    // Определяем счета для удобства
    Счет20 = ПланыСчетов.Хозрасчетный.ОсновноеПроизводство;
    Счет23 = ПланыСчетов.Хозрасчетный.ВспомогательныеПроизводства;
    Счет25 = ПланыСчетов.Хозрасчетный.ОбщепроизводственныеРасходы;
    Счет26 = ПланыСчетов.Хозрасчетный.ОбщехозяйственныеРасходы;
    Счет4401 = ПланыСчетов.Хозрасчетный.ИздержкиОбращения;
    Счет4402 = ПланыСчетов.Хозрасчетный.КоммерческиеРасходы;

    // Проходим по каждой записи в коллекции движений
    Для каждого ЗаписьДвижения Из ПеременнаяДвиженийХозрасчетный Цикл
        // Проверяем условие: "Начислен ежегодный отпуск"
        Если ЗаписьДвижения.Содержание = "Начислен ежегодный отпуск" Тогда
            // В зависимости от счета дебета, устанавливаем нужную статью затрат
            Если ЗаписьДвижения.СчетДт = Счет20 Тогда 
                ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000000022");
            ИначеЕсли ЗаписьДвижения.СчетДт = Счет23 Тогда 
                ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001053");
            ИначеЕсли ЗаписьДвижения.СчетДт = Счет25 Тогда 
                ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001064");
            ИначеЕсли ЗаписьДвижения.СчетДт = Счет26 Тогда 
                ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001000");
            ИначеЕсли ЗаписьДвижения.СчетДт = Счет4401 Тогда 
                ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001015");
            ИначеЕсли ЗаписьДвижения.СчетДт = Счет4402 Тогда 
                ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001015");
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;

    // Важно: Здесь НЕ НУЖНО вызывать ПеременнаяДвиженийХозрасчетный.Записать();
    // Система 1С сама запишет эту коллекцию движений после завершения ОбработкиПроведения.

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

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

Важные нюансы и альтернативные решения

Мы выяснили, как правильно модифицировать движения, но есть несколько важных моментов, которые стоит учитывать:

  1. Место размещения кода: Наиболее подходящее место для такого кода — это процедура ОбработкаПроведения модуля объекта документа. Также можно использовать подписки на события "ОбработкаПроведения" или "ПриЗаписи" документа, если вы не хотите изменять типовой модуль объекта.

  2. Модификация проведенных документов: Если вам нужно изменить движения уже проведенного документа (например, отдельной обработкой или фоновым заданием), тогда подход с НаборЗаписей становится актуальным. В этом случае вы действительно создаете НаборЗаписей, читаете движения по регистратору, модифицируете их и затем вызываете НаборЗаписей.Записать(). Но это относится к *внешней* по отношению к документу логике, а не к его *собственному* процессу проведения.

  3. Типовой функционал: В некоторых случаях, как было упомянуто на форуме, типовой функционал 1С уже предоставляет возможности для решения подобных задач. Например, в конфигурациях "Зарплата и управление персоналом" (ЗУП) и "Бухгалтерия предприятия" можно настроить отдельные "Способы отражения зарплаты в бухучете" для различных видов начислений (включая отпускные). Это позволяет гибко управлять корреспонденциями счетов и субконто (в том числе статьями затрат) без необходимости программного вмешательства в движения. Если количество подразделений или статей затрат не слишком велико, мы рекомендуем рассмотреть этот вариант как более надежный и поддерживаемый.

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

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

← К списку