При автоматизации процессов в среде 1С:Предприятие 7.7 мы часто сталкиваемся с задачей программного проведения документов из внешних обработок или других модулей. Казалось бы, задача тривиальна: вызвать метод проведения и, если что-то пошло не так, сообщить об ошибке. Однако "семерка" имеет свои архитектурные особенности, которые могут поставить в тупик даже опытного специалиста, привыкшего к современным версиям платформы.
В этой статье мы подробно разберем, как работает метод Провести(), почему стандартная обработка исключений не помогает выявить причину отказа, и какие подводные камни существуют при проверке статуса документа. Также мы рассмотрим способы получения детализированного описания ошибки.
В первую очередь давайте обратимся к синтаксису и поведению основного метода. При работе с объектом документа (созданным через СоздатьОбъект) мы используем функцию Провести(). У нее есть важная особенность: она возвращает числовое значение, которое является единственным достоверным индикатором успеха операции в текущем контексте.
Синтаксис выглядит следующим образом:
Результат = ДокОбъект.Провести(Режим, Формула);
Где Результат может принимать два значения:
Самая распространенная ошибка начинающих разработчиков в 1С 7.7 — это попытка проверить статус документа через метод Проведен() сразу после вызова команды проведения. Давайте разберем, почему так делать категорически нельзя.
Представим ситуацию: у вас есть документ, который уже был проведен ранее. Вы пытаетесь перепровести его программно с новыми данными или просто обновить движения. Допустим, в модуле проведения срабатывает проверка (например, не хватает товара на складе), и устанавливается отказ в проведении (СтатусВозврата(0)).
Рассмотрим ошибочный код, который часто встречается:
// ОШИБОЧНЫЙ ПОДХОД
ДокОбъект.Провести();
Если ДокОбъект.Проведен() = 0 Тогда
Сообщить("Ошибка проведения!");
КонецЕсли;
Почему этот код неверен? Дело в том, как платформа 7.7 обрабатывает транзакции:
СтатусВозврата(0)), транзакция откатывается (Rollback).Следовательно, метод ДокОбъект.Проведен() вернет 1 (Истина), хотя наша попытка перепроведения провалилась. Программа посчитает, что все прошло успешно, но на самом деле изменения не вступили в силу.
Правильное решение: всегда ориентируйтесь только на возвращаемое значение самого метода.
// ПРАВИЛЬНЫЙ ПОДХОД
Если ДокОбъект.Провести() = 0 Тогда
Сообщить("Не удалось провести документ: " + ДокОбъект.ТекущийДокумент());
// Здесь обрабатываем ошибку
Иначе
Сообщить("Успешно!");
КонецЕсли;
В современных языках программирования и в платформе 1С 8.х мы привыкли использовать конструкцию Попытка ... Исключение для перехвата ошибок. В 1С 7.7 этот механизм тоже есть, но он работает иначе в контексте проведения документов.
Давайте разберем код, который не даст желаемого результата:
Попытка
Статус = ДокОбъект.Провести();
Исключение
Сообщить(ОписаниеОшибки()); // Сюда мы почти никогда не попадем при логических ошибках
КонецПопытки;
Причины, по которым документ не проводится, делятся на два типа:
СтатусВозврата(0). Для платформы это не является "аварийной" ситуацией (Exception), это просто результат выполнения функции. Поэтому блок Исключение не сработает.Исключение.Таким образом, если документ не провелся из-за нехватки товара, Попытка это проигнорирует, и код пойдет дальше. Единственный способ узнать об этом — проверить возвращаемое значение метода Провести().
Это самый сложный вопрос в рамках штатного функционала 1С 7.7. Когда документ не проводится, модуль проведения обычно выводит сообщение пользователю через процедуру Сообщить(). Например: "На складе недостаточно товара Х".
Проблема в том, что этот текст уходит в окно сообщений (Message Window) и программно "достать" его из объекта ДокОбъект невозможно. Метод Провести() возвращает только 0 или 1.
Давайте рассмотрим варианты решения этой задачи.
Если вам необходимо во внешней обработке получить текст причины, по которой документ не провелся, лучше всего использовать проверенные временем внешние компоненты, такие как FormEx или 1C++. Эти библиотеки расширяют возможности "семерки" и позволяют перехватывать вывод в окно сообщений.
Пример логики с использованием FormEx (псевдокод):
// Включаем перехват сообщений
Сервис = СоздатьОбъект("Сервис"); // Объект FormEx
Сервис.ПерехватСообщений(1);
Если ДокОбъект.Провести() = 0 Тогда
ТекстОшибки = Сервис.ПолучитьСообщения(); // Получаем то, что платформа вывела в окно
ЗаписатьЛог("Ошибка: " + ТекстОшибки);
КонецЕсли;
Сервис.ПерехватСообщений(0); // Выключаем перехват
Если использование внешних компонент невозможно, придется дорабатывать конфигурацию. Идея заключается в том, чтобы модуль проведения не просто сообщал ошибку пользователю, но и сохранял её текст куда-то, откуда его может прочитать вызывающая обработка.
Мы можем использовать глСписокСообщений или подобную переменную в Глобальном Модуле, но это требует изменения кода во всех документах конфигурации, что трудоемко.
Иногда возникает вопрос: может ли метод Провести() вернуть 0, если в модуле проведения нет явного СтатусВозврата(0)? Да, такие ситуации возможны, и мы должны их учитывать:
Провести() сразу вернет 0, даже не запуская модуль проведения.Подводя итог, сформулируем универсальный алгоритм для проведения документов в 1С 7.7 во внешних обработках:
// 1. Создаем объект
Док = СоздатьОбъект("Документ.Реализация");
Если Док.НайтиДокумент(НужнаяСсылка) = 1 Тогда
// 2. Пытаемся провести, проверяя возвращаемое значение
// Режим = 0 (проводить в любом случае)
Попытка
Результат = Док.Провести(0);
Исключение
// Ловим системные краши (редко, но бывает)
Сообщить("Критическая ошибка платформы при проведении: " + ОписаниеОшибки());
Результат = 0;
КонецПопытки;
// 3. Анализируем результат
Если Результат = 0 Тогда
Сообщить("Документ НЕ проведен! " + Док.ТекущийДокумент());
// Здесь мы знаем, что произошел отказ.
// Причину ищем визуально в окне сообщений или используем FormEx для перехвата.
Иначе
Сообщить("Документ успешно проведен.");
КонецЕсли;
КонецЕсли;
Используя этот подход, вы избежите ложных срабатываний при перепроведении и сможете корректно обрабатывать ситуации, когда документ не удается провести по бизнес-логике или техническим причинам.