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