Почему флаг Проведен равен Истине в ОбработкеПроведения и как избежать дублирования движений при перепроведении?

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

При разработке механизмов проведения документов начинающие программисты часто сталкиваются с неочевидным поведением платформы 1С. Типичная ситуация: мы пытаемся проверить, проведен ли документ, непосредственно внутри процедуры ОбработкаПроведения, чтобы ограничить какие-либо действия, но флаг Проведен всегда возвращает Истина, даже если мы только что нажали кнопку «Провести» для записанного документа. В рамках данной статьи мы разберем внутренние механизмы платформы, выясним, почему так происходит, и решим проблему «бесконечного» списания остатков при перепроведении.

Механизм работы флага Проведен в транзакции

Для начала проанализируем ситуацию, описанную пользователем: при попытке проверить ЭтотОбъект.Проведен внутри ОбработкаПроведения, система всегда выдает Истина. Это не ошибка платформы, а строго заложенная логика. Давайте разберем последовательность действий, которые выполняет 1С при нажатии кнопки «Провести»:

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

Таким образом, внутри ОбработкаПроведения проверять ЭтотОбъект.Проведен бессмысленно — он всегда будет равен Истина. Если же нам критически важно узнать, был ли документ проведен до текущего момента, нам следует обращаться к состоянию объекта в базе данных через ссылку:


// Пример проверки исходного состояния
Если Ссылка.Проведен Тогда
    // Документ уже был проведен до этого вызова
КонецЕсли;

Проблема дублирования движений и свойство Удаление движений

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

Ключ к решению кроется в свойстве документа «Удаление движений» (находится на вкладке «Основные» в конфигураторе). Рассмотрим три режима работы этого свойства:

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

Для большинства учетных задач оптимальным выбором является режим «Удалять автоматически». Это избавляет от необходимости вручную писать код очистки регистров в начале процедуры проведения.

Разбор кода: как не стоит писать запросы

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

Проблемный участок:


Для Каждого Строка Из Оборудование Цикл
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Оборудование = &Оборудование ...";
    // выполнение запроса к остаткам для каждой строки ТЧ
КонецЦикла;

Такой подход вызывает столько обращений к серверу БД, сколько строк в вашей табличной части. Если в документе 100 позиций, система выполнит 100 отдельных запросов, что вызовет колоссальные задержки и блокировки. Рассмотрим правильный алгоритм:

  1. Выгружаем все необходимые данные из табличной части во временную таблицу или массив.
  2. Передаем этот массив в один запрос как параметр.
  3. Получаем все остатки по всем товарам документа одним пакетом.
  4. Обходим результат запроса и формируем движения.

Оптимизация работы с регистрами

Посмотрим на пример того, как правильно организовать запись в регистр сведений или накопления внутри ОбработкаПроведения. Обязательно используйте свойство Записывать = Истина для наборов записей, чтобы платформа знала, что данные нужно зафиксировать в БД по завершении процедуры.


Процедура ОбработкаПроведения(Отказ, Режим)
    // 1. Очистка движений (если не стоит автоудаление)
    Движения.РСЗаявки.Записывать = Истина;
    Движения.РСЗаявки.Очистить();
    
    // 2. Подготовка данных одним запросом (пропустим для краткости)
    // ...
    
    // 3. Формирование движений
    Движение = Движения.РСЗаявки.Добавить();
    Движение.Период = Дата;
    Движение.Статус = Статус;
    // Заполнение остальных полей...
КонецПроцедуры

Проверки в пользовательском интерфейсе

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


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

Подведем итоги

Мы выяснили, что странное поведение флага Проведен — это штатный механизм работы транзакции в 1С. Чтобы избежать логических ошибок и «размножения» записей в регистрах, всегда проверяйте настройки удаления движений в метаданных документа. Помните, что грамотное использование автоматических механизмов платформы позволяет избежать написания сотен строк лишнего кода и защищает базу данных от некорректных остатков. А использование пакетных запросов вместо запросов в цикле сделает вашу конфигурацию быстрой и устойчивой к высоким нагрузкам.

← На главную