При разработке механизмов проведения документов в системе 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда транзакция проведения прерывается, платформа сообщает, что «Объект не проведен», но текст конкретной ошибки в окне сообщений отсутствует. Рассмотрим причины такого поведения системы, проанализируем типичный программный код и выясним, почему отладчик может вести себя непредсказуемо на строках обхода выборки запроса.
Проанализируем ситуацию, описанную в теме. Разработчик создает объект СообщениеПользователю, заполняет его текст, но при выполнении кода пользователь не видит никакой информации. Основная ошибка заключается в том, что инициализация объекта Новый СообщениеПользователю лишь создает экземпляр сообщения в памяти, но не выводит его на экран. Для того чтобы сообщение попало в информационное окно, необходимо вызвать метод Сообщить().
Рассмотрим фрагмент проблемного кода:
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Недостаточно товара %1 в количестве %2",
ВыборкаНоменклатура.НоменклатураПредставление,
ВыборкаНоменклатура.Количество - ВыборкаНоменклатура.КоличествоОстаток);
// Ошибка: отсутствует вызов Сообщение.Сообщить();
В данном случае, когда переменная Отказ устанавливается в значение Истина, платформа понимает, что проведение должно быть прекращено. Однако, поскольку метод вывода сообщения не был вызван, очередь сообщений остается пустой, и пользователь получает лишь системное уведомление о факте непроведения объекта.
Выясним причину, по которой отладчик может «вылетать» или вести себя странно на строке Пока ВыборкаНоменклатура.Следующий() Цикл. В 1С метод Следующий() при обходе результата запроса ПоГруппировкам выполняет не только переход к следующей записи, но и обращение к агрегатным данным группировки. Если в результатах запроса содержатся некорректные данные или NULL, которые участвуют в расчете итогов, исключение может возникнуть именно в момент вызова Следующий().
Проанализируем запрос из примера. В нем используется ЛЕВОЕ СОЕДИНЕНИЕ с виртуальной таблицей остатков. Если номенклатуры нет на остатке, все поля из правой таблицы (включая количество, сумму и субконто) будут содержать NULL. Хотя автор использовал ЕСТЬNULL для числовых полей, поле Партия (Субконто3) остается равным NULL.
Посмотрим на условие упорядочивания в запросе:
УПОРЯДОЧИТЬ ПО
ВЫРАЗИТЬ(УправленческийОстатки.Субконто3 КАК Документ.ПриходнаяНакладная).МоментВремени УБЫВ
Если Субконто3 равно NULL, попытка обращения к свойству МоментВремени через конструкцию ВЫРАЗИТЬ может приводить к нестабильной работе выборки на уровне СУБД или платформы. В некоторых случаях это вызывает неявную ошибку при получении данных следующей записи группировки.
Разберем еще один важный момент: использование флага Отказ внутри цикла. Рассмотрим фрагмент кода:
Если ВыборкаНоменклатура.Количество > ВыборкаНоменклатура.КоличествоОстаток Тогда
Отказ = Истина;
// ... здесь вывод сообщения ...
КонецЕсли;
Если Отказ Тогда
Продолжить;
КонецЕсли;
С точки зрения методики «1С:Специалист», такая проверка должна выполняться максимально эффективно. Если обнаружена нехватка хотя бы по одной позиции, мы обязаны сообщить об этом пользователю. Однако продолжение цикла (Продолжить) после установки Отказ = Истина может быть опасно, если далее по тексту процедуры ОбработкаПроведения выполняются операции, зависящие от корректности данных (например, расчеты, где возможно деление на ноль).
Иногда поведение системы (когда код ведет себя иначе, чем показывает отладчик) связано с «замусоренным» кэшем 1С. Это происходит при частом динамическом обновлении конфигурации базы данных. В таких случаях отладчик может позиционироваться на одной строке, в то время как фактически исполняется другая версия кода.
Для решения подобных «фантомных» проблем рекомендуется выполнить очистку кэша. Сделать это можно двумя способами:
%AppData%\1C\1Cv8 и %LocalAppData%\1C\1Cv8 (папки с длинными именами-идентификаторами).Для исключения ошибок, подобных описанным в теме, рассмотрим, как должен выглядеть надежный каркас процедуры проведения с контролем остатков:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// 1. Подготовка движений к записи
Движения.Управленческий.Записывать = Истина;
Движения.Записать();
// 2. Установка блокировок
Блокировка = Новый БлокировкаДанных;
// ... настройка элементов блокировки ...
Блокировка.Заблокировать();
// 3. Выполнение запроса
Запрос = Новый Запрос;
// ... текст запроса с ЕСТЬNULL и правильными соединениями ...
РезультатЗапроса = Запрос.Выполнить();
// 4. Обход результатов
ВыборкаНоменклатура = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНоменклатура.Следующий() Цикл
// Сначала проверяем остатки
Нехватка = ВыборкаНоменклатура.Количество - ВыборкаНоменклатура.КоличествоОстаток;
Если Нехватка > 0 Тогда
Отказ = Истина;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Недостаточно товара " + ВыборкаНоменклатура.НоменклатураПредставление;
Сообщение.Сообщить(); // ОБЯЗАТЕЛЬНЫЙ МЕТОД
КонецЕсли;
// Если уже есть отказ, расчеты по этой номенклатуре не проводим
Если Отказ Тогда
Продолжить;
КонецЕсли;
// 5. Алгоритм списания (ФИФО/ЛИФО)
КоличествоСписать = ВыборкаНоменклатура.Количество;
ВыборкаДетальныеЗаписи = ВыборкаНоменклатура.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() И КоличествоСписать > 0 Цикл
// ... логика расчета себестоимости и добавления движений ...
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Соблюдение такой структуры и внимательное отношение к методам вывода сообщений позволит избежать ситуаций с «немыми» ошибками и сделает процесс разработки более предсказуемым. Помните, что отладка — это мощный инструмент, но её показания могут быть искажены некорректным состоянием кэша или неявными исключениями в запросах.