В процессе разработки на платформе «1С:Предприятие 8» часто возникает задача модификации уже существующих движений документа. Например, когда в табличную часть документа с помощью подбора загружаются текущие записи регистра, пользователь редактирует некоторые поля (вводит новые значения), и при проведении документа эти изменения должны отразиться в регистре сведений. При этом крайне важно не плодить новые дублирующие записи, а корректно обновить уже существующие движения.
Типичная ошибка начинающих разработчиков заключается в попытке использовать метод Добавить(), что приводит к созданию новых строк в регистре вместо изменения старых. В этой статье мы подробно разберем, как правильно реализовать изменение существующих движений, проанализируем поведение платформы и рассмотрим лучшие практики разработки.
Для работы с движениями документа в регистре сведений используется специальный объект платформы — Набор записей. Чтобы изменить существующие движения, не добавляя новые, необходимо придерживаться классического пошагового алгоритма. Рассмотрим его подробно:
Посмотрим на практический пример реализации этого алгоритма в коде:
// 1. Создаем набор записей регистра сведений
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
// 2. Устанавливаем отбор по конкретному документу (регистратору)
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);
// 3. Считываем существующие движения из базы данных
НаборЗаписей.Прочитать();
// 4. Модифицируем считанные данные
Для Каждого Запись Из НаборЗаписей Цикл
// Например, ищем нужную строку по измерению Номенклатура
СтрокаТЧ = СсылкаНаДокумент.Товары.Найти(Запись.Номенклатура, "Номенклатура");
Если СтрокаТЧ <> Неопределено Тогда
// Меняем ресурсы регистра на новые значения из табличной части документа
Запись.НовоеЗначениеРесурса1 = СтрокаТЧ.Значение1;
Запись.НовоеЗначениеРесурса2 = СтрокаТЧ.Значение2;
КонецЕсли;
КонецЦикла;
// 5. Записываем измененный набор в базу данных
НаборЗаписей.Записать();
Разберем одну из главных ловушек, в которую попадают разработчики. Коллекция Движения у объекта ДокументОбъект в оперативной памяти изначально всегда является пустой (количество элементов в ней равно 0), даже если документ уже проведен и его движения физически присутствуют в таблицах базы данных SQL.
Если вы не вызовете метод Прочитать(), а сразу начнете модифицировать набор или вызовете метод Записать(), платформа посчитает, что ваш набор пуст. В результате вызов метода Записать() просто полностью очистит и удалит все существующие движения этого документа в базе данных! Метод Прочитать() жизненно необходим: он принудительно считывает данные из БД и наполняет коллекцию в оперативной памяти текущими записями, которые затем можно безопасно редактировать.
Способ изменения записей напрямую зависит от структуры самого регистра сведений в конфигураторе. Проанализируем два возможных сценария:
1. Регистр подчинен регистратору. В этом случае у каждой записи жестко определено поле Регистратор. Изменение записей происходит строго по схеме, описанной выше — через создание набора записей с отбором по ссылке на документ.
2. Регистр является независимым. У такого регистра нет поля Регистратор, и он не связан с документами напрямую. Если вам нужно изменить конкретную строку в независимом регистре, то использовать набор записей с отбором по всем измерениям может быть избыточно. В таком случае гораздо эффективнее применить Менеджер записи. Он позволяет работать со строго одной строкой таблицы напрямую, как с отдельным объектом.
Рассмотрим пример использования менеджера записи для независимого регистра:
МенеджерЗаписи = РегистрыСведений.ИмяНезависимогоРегистра.СоздатьМенеджерЗаписи();
// Заполняем все ключевые измерения для однозначной идентификации строки
МенеджерЗаписи.Измерение1 = ЗначениеИзмерения1;
МенеджерЗаписи.Измерение2 = ЗначениеИзмерения2;
// Считываем запись из базы данных
МенеджерЗаписи.Прочитать();
// Если запись с такими ключевыми полями существует в базе
Если МенеджерЗаписи.Выбран() Тогда
// Меняем необходимые ресурсы
МенеджерЗаписи.Ресурс1 = НовоеЗначение1;
МенеджерЗаписи.Ресурс2 = НовоеЗначение2;
// Записываем обновленные данные
МенеджерЗаписи.Записать();
КонецЕсли;
Важно понимать архитектурные последствия прямого изменения движений проведенного документа. Если вы программно или вручную изменили движения в базе данных, то при первом же перепроведении этого документа стандартными средствами (например, групповое перепроведение за месяц) все ваши изменения будут безвозвратно утеряны. Алгоритм в процедуре ОбработкаПроведения модуля объекта заново сформирует движения «с нуля» на основании данных документа, проигнорировав ваши ручные правки.
В типовых конфигурациях 1С (таких как ERP, Комплексная автоматизация, Управление торговлей или Бухгалтерия предприятия) эта проблема успешно решается внедрением механизма ручной корректировки движений. Выясним, как спроектировать этот механизм самостоятельно:
РучнаяКорректировка с типом Булево.ОбработкаПроведения модуля объекта документа добавьте проверку этого флага в самом начале выполнения:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Если пользователь включил ручную корректировку, то стандартный алгоритм проведения выполняться не должен
Если РучнаяКорректировка Тогда
Возврат;
КонецЕсли;
// Далее идет стандартный программный код формирования движений
// ...
КонецПроцедуры
Если флаг РучнаяКорректировка установлен в значение Истина, платформа не будет перезаписывать движения при проведении документа. Если же пользователь решит отказаться от ручных изменений и снимет этот флаг, то при следующем проведении документ автоматически пересчитает движения по стандартному программному алгоритму.
Если бизнес-логика вашей системы требует изменить движения регистра, но при этом исходный документ заблокирован от изменений (например, период закрыт для редактирования) или вам крайне нежелательно снимать конфигурацию с поддержки для внедрения механизмов ручной корректировки, воспользуйтесь стандартным решением — создайте или используйте типовой документ «Корректировка записей регистров».
Этот документ выступает в роли нового регистратора для нужного вам регистра сведений. С его помощью вы сможете сделать новые сторнирующие или корректирующие записи, при этом исходный документ и его оригинальные движения останутся абсолютно нетронутыми. Это гарантирует сохранение истории учета и целостность вашей базы данных.
Если код по модификации существующих движений вы пишете непосредственно внутри процедуры ОбработкаПроведения самого документа-регистратора, то вызывать метод Записать() у набора записей внутри этой процедуры не нужно. Более того, явный вызов метода Записать() внутри транзакции проведения негативно сказывается на производительности системы и может приводить к избыточным блокировкам СУБД.
Вместо явной записи достаточно взвести управляющее свойство набора движений:
Движения.ИмяВашегоРегистра.Записывать = Истина;
Платформа 1С самостоятельно проанализирует состояние коллекции и запишет все измененные движения в базу данных в момент успешного завершения транзакции проведения документа.