Как массово и эффективно изменить записи регистра бухгалтерии в 1С

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

Работа с регистрами бухгалтерии в крупных базах данных (например, 1С:УПП или 1С:ERP) часто сопряжена с необходимостью ретроспективного изменения данных. Типичная ситуация: добавлено новое измерение, которое нужно заполнить за прошлые периоды (начиная с 2008 года и далее). В этой статье мы разберем, как правильно организовать процесс массовой правки проводок, избежав при этом критического падения производительности и блокировок системы.

Анализ проблемы: почему стандартный подход не всегда работает

Часто начинающие разработчики пытаются подойти к задаче «в лоб»: прочитать все записи регистра и перебрать их в цикле. Однако в платформе 1С объект РегистрБухгалтерииНаборЗаписей имеет существенное ограничение — он не может функционировать без установленного фильтра по конкретному регистратору. Если вы попробуете выполнить метод Прочитать() без отбора, система выдаст ошибку или вернет пустой результат.

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

Шаг 1. Оптимизированная выборка регистраторов

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

Рассмотрим пример запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	Управленческий.Регистратор КАК Регистратор
    |ИЗ
    |	РегистрБухгалтерии.Управленческий КАК Управленческий
    |ГДЕ
    |	Управленческий.Период МЕЖДУ &ДатаНачала И &ДатаКонца";

Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаКонца", ДатаКонца);

РезультатЗапроса = Запрос.Выполнить();
ВыборкаРегистратор = РезультатЗапроса.Выбрать();

Использование ключевого слова ВЫБРАТЬ РАЗЛИЧНЫЕ критически важно, так как один документ может порождать десятки проводок, а нам нужна ссылка на него один раз для формирования набора записей.

Шаг 2. Работа с набором записей вместо объектов документов

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

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


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

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

Шаг 3. Ускорение записи через отключение итогов

При массовом изменении проводок за длительный период система 1С после каждой записи набора будет пытаться пересчитать итоги (остатки и обороты). Если записей миллионы, это превратит обработку в бесконечный процесс.

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

  1. Отключение итогов: РегистрыБухгалтерии.Управленческий.УстановитьИспользованиеИтогов(Ложь);
  2. Выполнение массовой записи данных (наш цикл с наборами записей).
  3. Включение итогов: РегистрыБухгалтерии.Управленческий.УстановитьИспользованиеИтогов(Истина);
  4. Полный пересчет итогов: РегистрыБухгалтерии.Управленческий.ПересчитатьИтоги();

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

Шаг 4. Использование транзакций и пакетов

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

Посмотрим на пример реализации пакетной обработки:


Счетчик = 0;
НачатьТранзакцию();

Пока ВыборкаРегистратор.Следующий() Цикл
    // ... код обработки набора записей ...
    
    Счетчик = Счетчик + 1;
    Если Счетчик % 1000 = 0 Тогда
        ЗафиксироватьТранзакцию();
        НачатьТранзакцию();
    КонецЕсли;
КонецЦикла;

Если ТранзакцияАктивна() Тогда
    ЗафиксироватьТранзакцию();
КонецЕсли;

Ошибки, которых стоит избегать

Проанализируем некоторые заблуждения, встречающиеся на форумах:

Таким образом, мы выяснили, что наиболее эффективным и безопасным способом изменения проводок является сочетание точного запроса к регистраторам и использование объекта НаборЗаписей с периодической фиксацией транзакций.

← На главную