Мы часто сталкиваемся с ситуациями, когда типовой функционал 1С не полностью покрывает наши специфические потребности. Один из таких случаев — необходимость программно корректировать движения документов, например, для более детального учета затрат. Давайте рассмотрим проблему, с которой столкнулся наш коллега, пытаясь выделить начисления отпускных на отдельные статьи затрат в документе "Отражение зарплаты в бухучете" в 1С:Бухгалтерии 3.0, и выясним, почему его первоначальный подход не дал желаемого результата.
Задача заключалась в том, чтобы после проведения документа автоматически изменить статьи затрат для отпускных. Изначально была предпринята попытка получить набор записей регистра бухгалтерии, изменить его и записать. Однако, несмотря на то, что в отладчике изменения были видны, после записи документа они не сохранялись. Мы вместе разберем, в чем заключалась причина этого поведения и как правильно реализовать задуманное.
Прежде чем перейти к решению, проанализируем ситуацию и выясним причину, по которой первоначальный код не работал так, как ожидалось. Проблема кроется в фундаментальном различии между коллекцией движений, которую формирует документ при проведении, и отдельно созданным объектом НаборЗаписей.
Когда вы создаете новый объект с помощью РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей(), вы получаете пустой набор записей регистра. Затем вы заполняете его, читая движения по текущему регистратору:
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ЭтотОбъект.Ссылка);
НаборЗаписей.Прочитать();
После этого вы изменяете элементы этого отдельно созданного объекта НаборЗаписей в памяти. Однако этот объект не связан напрямую с той коллекцией движений, которую сам документ ЭтотОбъект формирует и записывает в процессе своего проведения. Система 1С при проведении документа работает со своей внутренней коллекцией движений, доступной через свойство Движения документа (например, ЭтотОбъект.Движения.Хозрасчетный). Ваши изменения в отдельно созданном НаборЗаписей не влияют на эту внутреннюю коллекцию. Поэтому, когда документ завершает проведение, он записывает свои "родные" движения, игнорируя ваши корректировки в стороннем объекте.
Для наглядности рассмотрим исходный (некорректный) фрагмент кода:
// Это НЕКОРРЕКТНЫЙ способ изменения движений документа в ОбработкеПроведения
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ЭтотОбъект.Ссылка);
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() = 0 Тогда Возврат; КонецЕсли;
Счет20 = ПланыСчетов.Хозрасчетный.ОсновноеПроизводство;
// ... (определение других счетов)
Для каждого Запись из НаборЗаписей Цикл
Если Запись.Содержание = "Начислен ежегодный отпуск" Тогда
Если Запись.СчетДт = Счет20 Тогда
Запись.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000000022");
// ... (другие условия для счетов)
КонецЕсли;
КонецЕсли;
КонецЦикла;
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать(); // Здесь записывается отдельный НаборЗаписей, а не движения документа
Как мы видим, после всех изменений вызывается метод НаборЗаписей.Записать(). Этот метод записывает именно тот объект НаборЗаписей, который вы создали, а не коллекцию движений текущего документа. Поэтому после завершения процедуры проведения документа, его фактические движения остаются без изменений.
Для того чтобы программно изменить движения документа во время его проведения, нам необходимо работать непосредственно с коллекцией движений, которая формируется самим документом. Эта коллекция доступна через свойство Движения текущего объекта документа (ЭтотОбъект).
Рассмотрим подробнее по шагам, как это сделать:
Доступ к коллекции движений: Внутри процедуры ОбработкаПроведения (или в подписке на событие, обрабатывающем проведение документа) мы получаем доступ к нужной коллекции движений. Для регистра бухгалтерии "Хозрасчетный" это будет ЭтотОбъект.Движения.Хозрасчетный.
Обход записей: Получив коллекцию, мы можем пройтись по ее записям в цикле. Каждая запись в этой коллекции представляет собой движение, которое документ собирается записать.
Внесение корректировок: Внутри цикла мы можем проверять условия (например, по содержанию записи, счету дебета или кредита) и изменять нужные реквизиты записи движения, такие как субконто, сумму и т.д.
Запись движений: После того как цикл завершится и все необходимые корректировки будут внесены, нам не нужно вызывать метод Записать() для коллекции движений. Система 1С автоматически запишет эту коллекцию движений в рамках транзакции проведения документа. Это одно из ключевых отличий от работы с отдельно созданным НаборЗаписей.
Посмотрим на пример корректного кода, адаптированного под исходную задачу:
// Это КОРРЕКТНЫЙ способ изменения движений документа в ОбработкеПроведения
&НаСервере
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// ... (стандартный типовой код проведения документа, если он есть)
// Например, документ уже сформировал свои типовые движения
// Здесь мы работаем с уже сформированными движениями
// Получаем коллекцию движений регистра "Хозрасчетный"
ПеременнаяДвиженийХозрасчетный = ЭтотОбъект.Движения.Хозрасчетный;
// Определяем счета для удобства
Счет20 = ПланыСчетов.Хозрасчетный.ОсновноеПроизводство;
Счет23 = ПланыСчетов.Хозрасчетный.ВспомогательныеПроизводства;
Счет25 = ПланыСчетов.Хозрасчетный.ОбщепроизводственныеРасходы;
Счет26 = ПланыСчетов.Хозрасчетный.ОбщехозяйственныеРасходы;
Счет4401 = ПланыСчетов.Хозрасчетный.ИздержкиОбращения;
Счет4402 = ПланыСчетов.Хозрасчетный.КоммерческиеРасходы;
// Проходим по каждой записи в коллекции движений
Для каждого ЗаписьДвижения Из ПеременнаяДвиженийХозрасчетный Цикл
// Проверяем условие: "Начислен ежегодный отпуск"
Если ЗаписьДвижения.Содержание = "Начислен ежегодный отпуск" Тогда
// В зависимости от счета дебета, устанавливаем нужную статью затрат
Если ЗаписьДвижения.СчетДт = Счет20 Тогда
ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000000022");
ИначеЕсли ЗаписьДвижения.СчетДт = Счет23 Тогда
ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001053");
ИначеЕсли ЗаписьДвижения.СчетДт = Счет25 Тогда
ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001064");
ИначеЕсли ЗаписьДвижения.СчетДт = Счет26 Тогда
ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001000");
ИначеЕсли ЗаписьДвижения.СчетДт = Счет4401 Тогда
ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001015");
ИначеЕсли ЗаписьДвижения.СчетДт = Счет4402 Тогда
ЗаписьДвижения.СубконтоДт.СтатьиЗатрат = Справочники.СтатьиЗатрат.НайтиПоКоду("000001015");
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Важно: Здесь НЕ НУЖНО вызывать ПеременнаяДвиженийХозрасчетный.Записать();
// Система 1С сама запишет эту коллекцию движений после завершения ОбработкиПроведения.
КонецПроцедуры
В этом примере мы работаем непосредственно с коллекцией ЭтотОбъект.Движения.Хозрасчетный. Все изменения, внесенные в элементы ЗаписьДвижения внутри цикла, будут применены к реальным движениям документа. После завершения процедуры ОбработкаПроведения, система 1С автоматически сохранит эти измененные движения в базе данных, и они будут видны в проводках.
Мы выяснили, как правильно модифицировать движения, но есть несколько важных моментов, которые стоит учитывать:
Место размещения кода: Наиболее подходящее место для такого кода — это процедура ОбработкаПроведения модуля объекта документа. Также можно использовать подписки на события "ОбработкаПроведения" или "ПриЗаписи" документа, если вы не хотите изменять типовой модуль объекта.
Модификация проведенных документов: Если вам нужно изменить движения уже проведенного документа (например, отдельной обработкой или фоновым заданием), тогда подход с НаборЗаписей становится актуальным. В этом случае вы действительно создаете НаборЗаписей, читаете движения по регистратору, модифицируете их и затем вызываете НаборЗаписей.Записать(). Но это относится к *внешней* по отношению к документу логике, а не к его *собственному* процессу проведения.
Типовой функционал: В некоторых случаях, как было упомянуто на форуме, типовой функционал 1С уже предоставляет возможности для решения подобных задач. Например, в конфигурациях "Зарплата и управление персоналом" (ЗУП) и "Бухгалтерия предприятия" можно настроить отдельные "Способы отражения зарплаты в бухучете" для различных видов начислений (включая отпускные). Это позволяет гибко управлять корреспонденциями счетов и субконто (в том числе статьями затрат) без необходимости программного вмешательства в движения. Если количество подразделений или статей затрат не слишком велико, мы рекомендуем рассмотреть этот вариант как более надежный и поддерживаемый.
Производительность: Помните, что добавление сложной логики в процедуру ОбработкаПроведения может повлиять на производительность системы, особенно при проведении большого количества документов. Всегда стремитесь к максимально эффективному коду.
Совместно разобрав эту проблему, мы теперь понимаем, как эффективно и корректно программно изменять движения документов в 1С:Предприятии 8. Надеемся, что это решение поможет вам в вашей работе!
← К списку