При работе с крупными и сильно измененными конфигурациями (например, 1С:Управление производственным предприятием) разработчики часто сталкиваются со следующей проблемой: документ делает движения по определенному регистру накопления (например, ПартииТоваровБух), но в самом модуле документа нет ни одной явной строчки, указывающей на этот регистр. Попытки поставить точку останова в модуле документа или выгрузить коллекцию движений в табло отладчика через Движения.ПартииТоваровБух.Выгрузить() показывают абсолютно пустую таблицу значений.
В этой статье мы подробно разберем, почему движения могут формироваться незаметно для разработчика, и рассмотрим эффективные методы отладки, которые помогут вам гарантированно найти место генерации движений.
Прежде чем приступать к отладке, важно понять, как именно данные могут записываться в регистр в крупных конфигурациях. Разработчики часто используют запись движений в обход стандартной коллекции документа Движения. Вместо этого в общем модуле может динамически создаваться независимый объект набора записей регистра.
Рассмотрим пример кода, который часто применяется для записи движений по партиям:
НаборЗаписей = РегистрыНакопления.ПартииТоваровБух.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);
// Заполнение набора записей из подготовленной таблицы значений
Для Каждого СтрокаТаблицы Из ТаблицаПартий Цикл
НоваяСтрока = НаборЗаписей.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТаблицы);
КонецЦикла;
НаборЗаписей.Записать();
Поскольку в этом случае запись происходит напрямую через объект НаборЗаписей, коллекция Движения.ПартииТоваровБух внутри самого документа остается пустой на протяжении всего процесса проведения. Именно поэтому интерактивный анализ коллекции в табло отладчика не приносит результатов.
Самый простой и надежный способ поймать момент записи в регистр — использовать обработчик событий самого регистра накопления. Для этого выполним следующие действия по шагам:
ПартииТоваровБух).ПередЗаписью или ПриЗаписи.Пример кода, который мы можем написать в модуле набора записей:
Процедура ПередЗаписью(Отказ, Замещение)
// Ставим точку останова на строчке ниже
Если ЭтотОбъект.Количество() > 0 Тогда
ИмяРегистратора = ЭтотОбъект.Отбор.Регистратор.Значение;
КонецЕсли;
КонецПроцедуры
Когда документ начнет проводить транзакцию и дело дойдет до записи движений в базу данных, система автоматически передаст управление в этот обработчик. В этот момент мы сможем открыть окно Стек вызовов (Call Stack) в отладчике и увидеть всю цепочку процедур и функций, приведшую к записи.
Важное примечание: Если точка останова в модуле набора записей регистра не срабатывает, выясним две главные причины:
-debug.Если код отрабатывает слишком быстро или стандартная отладка по шагам затруднена из-за огромного количества итераций, отличной альтернативой является Замер производительности.
Этот инструмент работает как автоматический трассировщик. Он записывает абсолютно каждую строчку кода, которая была выполнена системой за время замера. Проанализируем алгоритм работы с ним:
Перед нами откроется окно с полным списком всех строк кода, которые выполнялись в системе. Нам остается лишь выполнить поиск (Ctrl + F) по ключевому слову ПартииТоваровБух или НаборЗаписей. Замер покажет точные строки в общих модулях и подписках на события, которые принимали участие в формировании движений.
Если мы отлаживаем именно конфигурацию УПП, то движения могут отсутствовать по чисто архитектурным причинам, заложенным в логику программы. Рассмотрим две основные настройки:
ПартииТоваровБух физически не должны формироваться — все затраты и партии отражаются в регистрах учета затрат (например, УчетЗатратРег).Проведение по партиям. В этом случае отлаживать нужно именно проведение этой обработки, а не сам документ.В типовой архитектуре УПП формирование партионного учета вынесено за пределы документов. Почти все документы используют единые механизмы проведения.
Рассмотрим структуру вызовов. Основная логика по распределению партий и формированию проводок по регистру ПартииТоваровБух сосредоточена в общем модуле УправлениеЗапасамиПартионныйУчет. Рекомендуется открыть данный модуль и найти в нем процедуры:
ДвижениеПартийТоваровСписатьПартииДвижениеПартийТоваровНа СкладахУстановка точек останова непосредственно в начале этих процедур позволит детально проанализировать, какие таблицы данных приходят на вход и почему алгоритм распределения партий может отрабатывать некорректно.
В переписанных базах данных разработчики нередко используют универсальные механизмы проведения, где имя регистра получается динамически. Например:
Для Каждого Движение Из ЭтотОбъект.Движения Цикл
ИмяРегистра = Движение.Метаданные().Имя;
Если Найти(ИмяРегистра, "Партии") > 0 Тогда
// Здесь выполняется логика проведения
КонецЕсли;
КонецЦикла;
При таком подходе глобальный поиск по строке "ПартииТоваровБух" не даст результатов. Чтобы перехватить такое динамическое обращение, мы можем зайти в общий модуль проведения документов (например, ДвиженияДокументов) и установить точку останова с условием.
Для этого кликнем правой кнопкой мыши на точку останова, выберем пункт Свойства точки останова и в поле Условие напишем:
Движение.Метаданные().Имя = "ПартииТоваровБух"
Система остановит выполнение кода ровно в тот момент, когда универсальный алгоритм дойдет до обработки нашего целевого регистра.