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