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