Почему программное изменение реквизитов документа в 1С срабатывает только со второго раза?

Программист 1С v8.3 (Управляемые формы) 1С:Управление нашей фирмой Управленческий учет Промышленность, строительство и АПК
← На главную

В практике разработчика 1С иногда возникают ситуации, граничащие с мистикой: код написан верно, объект получается, реквизиты модифицируются, метод Записать() отрабатывает без ошибок, но данные в базе остаются прежними. Самое странное в этой ситуации то, что при повторном запуске того же самого кода для тех же объектов всё отрабатывает идеально. Рассмотрим этот феномен на примере конфигурации 1С:Управление нашей фирмой (УНФ) и разберем причины, по которым система может «игнорировать» изменения при первом проходе.

Анализ исходной ситуации и кода

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


Зак = стр.Заказ.ПолучитьОбъект();
Для каждого спец из Зак.Продукция цикл
    спец.Спецификация = Справочники.Спецификации.СпецификацияПоУмолчанию(спец.Номенклатура, спец.Характеристика);
КонецЦикла;
Попытка
    Зак.Записать(РежимЗаписиДокумента.Проведение);
    стр.ПерепроведенЗак = Истина;
Исключение
    Ошибки = Ошибки + "Не удалось перепровести Заказ " + ОписаниеОшибки;
КонецПопытки;

На первый взгляд, код безупречен. Мы получаем объект, обходим строки, вызываем метод менеджера справочника для получения дефолтной спецификации и записываем документ в режиме проведения. Однако именно здесь могут крыться подводные камни, связанные с архитектурой платформы 1С и механизмом кэширования.

Причина №1: Кэширование на уровне серверных вызовов (Managed Cache)

Метод СпецификацияПоУмолчанию в УНФ часто оптимизирован для работы в высоконагруженных системах. Платформа 1С активно использует механизм повторного использования значений (Cached Values). Если функция в модуле менеджера или общего модуля помечена аннотацией «Повторное использование», она возвращает результат из кэша, если параметры вызова не изменились.

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

Решение: Перед началом массовой обработки данных рекомендуется принудительно очищать кэш повторно используемых значений. Используем команду:

ОбновитьПовторноИспользуемыеЗначения();

Причина №2: Влияние подписок на события и модуля объекта

Рассмотрим ситуацию глубже. Когда мы вызываем метод Записать(РежимЗаписиДокумента.Проведение), система инициирует сложную цепочку событий:

  1. Обработка события ПередЗаписью в модуле объекта и подписках.
  2. Непосредственная запись в БД.
  3. Вызов ОбработкаПроведения.
  4. Обработка события ПриЗаписи.

В типовых конфигурациях (УНФ, ERP, КА) часто существуют подписки на события, которые выполняют «автозаполнение» или «проверку заполнения». Если в такой подписке прописана логика, которая принудительно пересчитывает спецификацию на основе своих алгоритмов и делает это, опираясь на старые данные из базы (а не на измененный объект в памяти), ваши изменения будут перезаписаны исходными значениями прямо в момент записи. Со второго раза это срабатывает, так как некоторые индексы или связанные регистры уже могли обновиться.

Причина №3: Кэширование внешних обработок платформой

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

Это объясняет, почему «с первого раза не сработало» — вы запускали версию кода до исправления, а «со второго раза» (возможно, после перезапуска сеанса или перерегистрации обработки в справочнике «Дополнительные отчеты и обработки») система подтянула актуальный алгоритм.

Совет: При отладке массовых изменений всегда выводите в сообщения или лог версию обработки или текущее время компиляции, чтобы убедиться, что вы работаете с актуальным кодом.

Причина №4: Транзакционные блокировки и PostgreSQL

Если база данных работает на PostgreSQL, стоит учитывать особенности версионности данных. В редких случаях, если документ заблокирован другим фоновым процессом или сеансом (например, заданием по расчету итогов), запись может произойти «частично» в плане фиксации изменений реквизитов, хотя само проведение будет считаться успешным (в рамках вложенных транзакций).

Также важно помнить, что метод Записать(РежимЗаписиДокумента.Проведение) требует больше ресурсов и накладывает больше блокировок, чем просто Записать(). Если логика позволяет, попробуйте сначала записать объект, а затем провести его отдельным вызовом.

Как правильно диагностировать проблему?

Чтобы найти истинную причину «загадки заполнения», проделаем следующие шаги:

  1. Добавим детальное логирование. Не просто меняем реквизит, а проверяем его значение до и после присвоения, а также сразу после метода Записать().
  2. Используем отладчик на сервере. Поставим точку останова в процедуре ПередЗаписью модуля документа Заказ на производство. Посмотрим, не меняется ли значение спецификации обратно в типовом коде конфигурации.
  3. Проверим транзакцию. Убедимся, что внутри цикла не происходит возврата (как в сообщении №8), который прерывает обработку всей пачки документов при ошибке в одном из них.

Рассмотрим улучшенный вариант кода с проверкой и защитой от «тихих» отказов:


// Очищаем кэш перед началом
ОбновитьПовторноИспользуемыеЗначения();

Для каждого СтрСписок Из ОтобранныеЗаказы Цикл
    ЗакОбъект = СтрСписок.Заказ.ПолучитьОбъект();
    Изменено = Ложь;
    
    Для каждого СтрПродукция Из ЗакОбъект.Продукция Цикл
        НоваяСпец = Справочники.Спецификации.СпецификацияПоУмолчанию(СтрПродукция.Номенклатура, СтрПродукция.Характеристика);
        Если СтрПродукция.Спецификация <> НоваяСпец Тогда
            СтрПродукция.Спецификация = НоваяСпец;
            Изменено = Истина;
        КонецЕсли;
    КонецЦикла;
    
    Если Изменено Тогда
        Попытка
            // Используем дополнительные параметры, если нужно отключить типовые проверки
            ЗакОбъект.ОбменДанными.Загрузка = Истина; 
            ЗакОбъект.Записать(РежимЗаписиДокумента.Проведение);
            Сообщить("Заказ " + ЗакОбъект + " успешно обновлен.");
        Исключение
            Инфо = ИнформацияОбОшибке();
            Сообщить("Ошибка при записи заказа " + ЗакОбъект + ": " + ПодробноеОписаниеОшибки(Инфо));
        КонецПопытки;
    КонецЕсли;
КонецЦикла;

Использование флага ОбменДанными.Загрузка = Истина — это радикальный, но эффективный способ проверить, не мешают ли записи подписки на события и внутренние проверки модуля объекта. Если с этим флагом всё записывается с первого раза, значит, причина точно в «перекрывающей» логике конфигурации (подписки, ПередЗаписью).

Таким образом, «магия второго раза» обычно сводится к кэшированию данных платформой или работе механизмов автозаполнения внутри самой конфигурации, которые конфликтуют с внешним кодом. Тщательное логирование и понимание жизненного цикла записи объекта позволяют быстро устранить подобные аномалии.

← На главную