Как изменить существующие движения документа в регистре сведений без добавления новых записей?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

Базовый алгоритм изменения движений через набор записей

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

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

Посмотрим на практический пример реализации этого алгоритма в коде:


// 1. Создаем набор записей регистра сведений
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();

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

// 3. Считываем существующие движения из базы данных
НаборЗаписей.Прочитать();

// 4. Модифицируем считанные данные
Для Каждого Запись Из НаборЗаписей Цикл
    // Например, ищем нужную строку по измерению Номенклатура
    СтрокаТЧ = СсылкаНаДокумент.Товары.Найти(Запись.Номенклатура, "Номенклатура");
    Если СтрокаТЧ <> Неопределено Тогда
        // Меняем ресурсы регистра на новые значения из табличной части документа
        Запись.НовоеЗначениеРесурса1 = СтрокаТЧ.Значение1;
        Запись.НовоеЗначениеРесурса2 = СтрокаТЧ.Значение2;
    КонецЕсли;
КонецЦикла;

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

Критически важный нюанс: почему метод Прочитать() обязателен?

Разберем одну из главных ловушек, в которую попадают разработчики. Коллекция Движения у объекта ДокументОбъект в оперативной памяти изначально всегда является пустой (количество элементов в ней равно 0), даже если документ уже проведен и его движения физически присутствуют в таблицах базы данных SQL.

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

Различия в работе с подчиненными и независимыми регистрами

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

1. Регистр подчинен регистратору. В этом случае у каждой записи жестко определено поле Регистратор. Изменение записей происходит строго по схеме, описанной выше — через создание набора записей с отбором по ссылке на документ.

2. Регистр является независимым. У такого регистра нет поля Регистратор, и он не связан с документами напрямую. Если вам нужно изменить конкретную строку в независимом регистре, то использовать набор записей с отбором по всем измерениям может быть избыточно. В таком случае гораздо эффективнее применить Менеджер записи. Он позволяет работать со строго одной строкой таблицы напрямую, как с отдельным объектом.

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


МенеджерЗаписи = РегистрыСведений.ИмяНезависимогоРегистра.СоздатьМенеджерЗаписи();

// Заполняем все ключевые измерения для однозначной идентификации строки
МенеджерЗаписи.Измерение1 = ЗначениеИзмерения1;
МенеджерЗаписи.Измерение2 = ЗначениеИзмерения2;

// Считываем запись из базы данных
МенеджерЗаписи.Прочитать();

// Если запись с такими ключевыми полями существует в базе
Если МенеджерЗаписи.Выбран() Тогда
    // Меняем необходимые ресурсы
    МенеджерЗаписи.Ресурс1 = НовоеЗначение1;
    МенеджерЗаписи.Ресурс2 = НовоеЗначение2;
    
    // Записываем обновленные данные
    МенеджерЗаписи.Записать();
КонецЕсли;

Проблема перепроведения документов и механизм «Ручной корректировки»

Важно понимать архитектурные последствия прямого изменения движений проведенного документа. Если вы программно или вручную изменили движения в базе данных, то при первом же перепроведении этого документа стандартными средствами (например, групповое перепроведение за месяц) все ваши изменения будут безвозвратно утеряны. Алгоритм в процедуре ОбработкаПроведения модуля объекта заново сформирует движения «с нуля» на основании данных документа, проигнорировав ваши ручные правки.

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

  1. Добавьте в реквизиты вашего документа новый реквизит РучнаяКорректировка с типом Булево.
  2. В процедуре ОбработкаПроведения модуля объекта документа добавьте проверку этого флага в самом начале выполнения:

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

Если флаг РучнаяКорректировка установлен в значение Истина, платформа не будет перезаписывать движения при проведении документа. Если же пользователь решит отказаться от ручных изменений и снимет этот флаг, то при следующем проведении документ автоматически пересчитает движения по стандартному программному алгоритму.

Использование специального документа корректировки

Если бизнес-логика вашей системы требует изменить движения регистра, но при этом исходный документ заблокирован от изменений (например, период закрыт для редактирования) или вам крайне нежелательно снимать конфигурацию с поддержки для внедрения механизмов ручной корректировки, воспользуйтесь стандартным решением — создайте или используйте типовой документ «Корректировка записей регистров».

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

Особенности записи движений внутри транзакции проведения

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

Вместо явной записи достаточно взвести управляющее свойство набора движений:

Движения.ИмяВашегоРегистра.Записывать = Истина;

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

← На главную