В чем разница между методами Записать() и Очистить() у регистров и как их правильно использовать при проведении?

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

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

Фундаментальное различие: Оперативная память против Базы данных

Для начала разберем природу этих двух методов. Несмотря на то, что оба они кажутся направленными на одно и то же — изменение состава записей, — они работают на разных уровнях архитектуры системы.

Метод Очистить() — это метод коллекции. Представьте его как команду «Очистить» у обычной Табличной части или Массива. Вызывая Движения.ИмяРегистра.Очистить(), мы работаем исключительно с оперативной памятью сервера приложений. Мы приказываем коллекции в памяти забыть все строки, которые в ней находились. При этом никаких запросов к СУБД (SQL) в этот момент не отправляется. Если мы выполним Очистить() и завершим процедуру проведения, не вызывая запись, в базе данных фактически ничего не изменится (если только не сработают автоматические механизмы платформы).

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

Разбор по шагам: Влияние свойства «Удалять движения автоматически»

Рассмотрим, как ведет себя платформа в зависимости от настроек документа. Проанализируем свойство «Удалять движения», которое может принимать значения «Удалять автоматически при отмене проведения», «Удалять автоматически» или «Не удалять автоматически».

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

Рассмотрим типичный пример кода, который может привести к ошибкам, если не понимать механику платформы:


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

Как предотвратить появление дублей?

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

Чтобы гарантировать корректность, рассмотрим правильную последовательность действий перед циклом формирования движений:


Движения.Остатки.Записывать = Истина;
// Очищаем коллекцию в памяти от возможных старых данных
Движения.Остатки.Очистить(); 

Для Каждого Стр Из СписокНоменклатуры Цикл
    Движение = Движения.Остатки.Добавить();
    // Заполнение полей...
КонецЦикла;

Здесь Очистить() гарантирует, что в коллекции «Остатки» находятся только те записи, которые мы создали в текущей итерации проведения.

Явный вызов Записать(): Когда это действительно нужно?

Рассмотрим подробнее использование метода Записать() внутри процедуры ОбработкаПроведения. По стандартам разработки «1С», лишние вызовы Записать() не приветствуются, так как это лишняя нагрузка на сервер базы данных. Однако существует так называемая «новая методика проведения».

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


Движения.Остатки.Записывать = Истина;
Движения.Остатки.Очистить();

// Формируем движения...
Для Каждого Стр Из Товары Цикл
    Движение = Движения.Остатки.ДобавитьРасход();
    // ...
КонецЦикла;

// Явно записываем в базу, чтобы запрос к остаткам «увидел» эти данные
Движения.Остатки.Записать(); 

// Теперь делаем запрос к виртуальным таблицам остатков для контроля
// Если обнаружим минус — устанавливаем Отказ = Истина

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

Проанализируем ситуацию с транзакциями

Любая процедура ОбработкаПроведения выполняется внутри одной большой транзакции. Что это означает для методов Записать() и Очистить()?

  1. Если вы вызвали Записать(), данные физически отправились в базу данных, но они еще не «зафиксированы» для других пользователей (в зависимости от уровня изоляции).
  2. Если в коде возникла ошибка или вы установили Отказ = Истина, транзакция откатывается. Все, что вы «записали» методом Записать(), будет удалено из БД, и состояние регистров вернется к тому, каким оно было до начала проведения.
  3. Метод Очистить() в рамках транзакции просто обнуляет текущую коллекцию в памяти, помогая вам подготовить «чистый лист» для формирования новых данных.

Итоговые рекомендации для разработчика

Подведем итог нашему анализу и сформулируем правила хорошего тона при работе с регистрами:

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

← На главную