В практике разработчика 1С часто возникает задача модификации стандартного механизма проведения. Например, когда типовое решение формирует проводки, которые не совсем подходят под специфику бизнес-процесса предприятия. В таких случаях нам нужно не просто добавить новые записи, а сначала «вычистить» ошибочные или лишние проводки, созданные системой автоматически, и заменить их своими. Разберем, как это сделать корректно, избегая типичных ошибок с блокировками и потерей данных.
Рассмотрим ситуацию, с которой столкнулся автор темы. Попытка создать новый набор записей через РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей() внутри процедуры ОбработкаПроведения является методически неверной. Проанализируем причину: когда документ проводится, платформа уже открыла транзакцию и подготовила коллекцию Движения. Создавая отдельный объект набора записей и пытаясь его записать, вы фактически пытаетесь работать «в обход» стандартного механизма регистрации движений документа, что может привести к конфликтам блокировок или к тому, что изменения просто будут перезаписаны платформой в конце процедуры проведения.
Правильный подход заключается в использовании встроенной коллекции Движения. Выясним, как это реализовать на практике.
Вместо создания нового набора записей, мы обратимся к уже существующим движениям документа. Если документ уже имеет проводки (например, сформированные ранее в этой же процедуре типовым кодом), мы можем их прочитать и проанализировать. Выполним выборочное удаление. Важный нюанс: нельзя удалять элементы коллекции прямо во время цикла Для каждого, так как это нарушит индекс обхода. Правильнее сначала собрать нужные записи в отдельный массив.
Рассмотрим правильный алгоритм удаления:
// Работаем с существующими движениями документа
НаборЗаписей = Движения.Хозрасчетный;
// Если записи еще не подгружены в память, читаем их
// (В ОбработкеПроведения они обычно уже доступны, если это не первое проведение)
НаборЗаписей.Прочитать();
ЗаписиДляУдаления = Новый Массив;
// Определяем ссылки на счета заранее, чтобы не вызывать НайтиПоКоду в цикле
Счет62_01 = ПланыСчетов.Хозрасчетный.НайтиПоКоду("62.01");
Счет62_02 = ПланыСчетов.Хозрасчетный.НайтиПоКоду("62.02");
Для каждого Запись Из НаборЗаписей Цикл
// Проверяем условия для удаления
Если (Запись.СчетДт = Счет62_01 И Запись.СчетКт = Счет62_02)
Или (Запись.СчетДт = Счет62_01 И Запись.СчетКт = Счет62_01) Тогда
ЗаписиДляУдаления.Добавить(Запись);
КонецЕсли;
КонецЦикла;
// Теперь удаляем собранные записи из коллекции
Для каждого Запись Из ЗаписиДляУдаления Цикл
НаборЗаписей.Удалить(Запись);
КонецЦикла;
После того как мы очистили набор от ненужных данных, проанализируем процесс добавления новых записей. При добавлении проводки в регистр бухгалтерии крайне важно заполнить не только счета и суммы, но и все ключевые измерения, а также субконто.
Важный момент: Если вы не заполните измерение Организация, проводки могут «не схлопнуться» в оборотно-сальдовой ведомости (ОСВ) или вовсе не отобразиться в отчетах по конкретной организации. Также обратите внимание на заполнение субконто по типам.
Посмотрим на пример создания проводки:
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Период = ЭтотОбъект.Дата;
НоваяЗапись.Регистратор = ЭтотОбъект.Ссылка;
НоваяЗапись.Организация = ЭтотОбъект.Организация; // Обязательное измерение!
НоваяЗапись.СчетДт = Счет62_01;
НоваяЗапись.СчетКт = Счет62_01;
// Заполнение Субконто. Безопаснее использовать поиск по виду субконто,
// а не по индексу в массиве, но в простом случае можно и так:
ВидСубкКонтрагент = НоваяЗапись.СчетКт.ВидыСубконто[0].ВидСубконто;
НоваяЗапись.СубконтоКт.Вставить(ВидСубкКонтрагент, ЭтотОбъект.Контрагент);
НоваяЗапись.СубконтоДт.Вставить(ВидСубкКонтрагент, ЭтотОбъект.Контрагент);
ВидСубкДоговор = НоваяЗапись.СчетКт.ВидыСубконто[1].ВидСубконто;
НоваяЗапись.СубконтоКт.Вставить(ВидСубкДоговор, ДоговорПроводки);
НоваяЗапись.СубконтоДт.Вставить(ВидСубкДоговор, ДоговорПроводки);
// Сумма проводки
НоваяЗапись.Сумма = ОставшаясяСумма;
В обработчике ОбработкаПроведения у нас есть два пути для сохранения движений:
Движения.Хозрасчетный.Записывать = Истина;. В этом случае платформа сама запишет все накопленные в коллекции Движения.Хозрасчетный записи после завершения процедуры проведения. Это рекомендуемый способ.НаборЗаписей.Записать(); принудительно. Это может потребоваться, если вам нужно, чтобы записи попали в базу данных немедленно (например, для использования в последующих запросах внутри этой же транзакции). Однако злоупотреблять этим не стоит.Разберем несколько советов, которые сделают ваш код более профессиональным и устойчивым к изменениям конфигурации:
Запись.СчетДт = ПланыСчетов.Хозрасчетный.НайтиПоКоду("62.01") можно использовать более быструю проверку: Запись.СчетДт.Код = "62.01". Это работает быстрее, так как сравниваются строки, а не ссылки, требующие подгрузки объектов.Подразделение, Валюта или НалоговоеНазначение. Даже если сумма и контрагент совпадают, различие в пустом и незаполненном подразделении создаст разные строки в отчете.ВидыСубконто[0] может привести к ошибке, если порядок субконто в плане счетов изменится. Проанализируем ситуацию: лучше использовать цикл по Запись.СчетДт.ВидыСубконто и проверять ВидСубконто.ТипЗначения, чтобы точно знать, куда записывать Контрагента, а куда — Договор.Соблюдение этих правил позволит вам эффективно управлять проводками документов, сохраняя чистоту кода и высокую производительность системы 1С.