Немного теории Стек вызовов - это инструмент, который показывает последовательность процедур и функций, исполнение которых привело к той строке модуля, что отлаживается сейчас. Стек большинство разработчиков используют при отладке исполнения кода. Также многие заметили, что начиная с платформы 8.3.15 в Журнал регистрации базы 1С и в операторе "Попытка ... Исключение ... КонецПопытки" возможно увидеть полный стек вызовов всех процедур, а не только вызов последней строки как было раньше (и this is хорошо).
Для начала расскажу как первый раз применил получение и разбор стека исполнения самим кодом 1С. В конец дам общий принцип по применению на практике.
Возникла проблема: При закрытии месяца в 1С: ERP, при выполнении операции "Формирование записей книг покупок и продаж" произошла ошибка: Операцию не удалось выполнить по причине того, что Отражение документов в регл учете невозможно поместить в запрещенный период. Дате 01.06.2019 соответствует запрет изменения данных для пользователя "Администратор (Михаил)" по 31.05.2020 (установлена общая дата запрета)
Сформировалась задача, которую нужно было быстро сделать: Не нужно применять контроль даты запрета в случае выполнения операции "Формирование записей книг покупок и продаж" при закрытии месяца.
Приступив к решению задачи:
- Отыскал исходную ошибку по Журналу регистрации 1С
При выполнении операции "Формирование записей книг покупок и продаж" произошла ошибка:
Не удалось выполнить по причине Отражение документов в регл учете невозможно поместить в запрещенный период.
Дате 01.06.2019 соответствует запрет изменения данных для пользователя "ГлавныйБухгалтерЗаместитель" по 29.02.2020 (установлена общая дата запрета)
{ОбщийМодуль.РеглУчетПроведениеСервер.Модуль(3509)}: ВызватьИсключение ОписаниеОшибки;
{ОбщийМодуль.РеглУчетПроведениеСервер.Модуль(733)}: ПроверитьДатуЗапретаДляОтраженияДокументовВРеглУчете(ШаблонЗапретаДанных);
{ОбщийМодуль.УчетНДСУПСлужебный.Модуль(9191)}: РеглУчетПроведениеСервер.ВернутьДокументыКОтражению(ПараметрыРасчета.МенеджерВременныхТаблиц);
{ОбщийМодуль.УчетНДСУПСлужебный.Модуль(1602)}: ВернутьДокументыКОтражениюВУчете(ПараметрыРасчета);
{ОбщийМодуль.УчетНДСУПСлужебный.Модуль(1588)}: ВыполнитьРасчет(ПараметрыРасчета);
{ОбщийМодуль.УчетНДСУПСлужебный.Модуль(1506)}: ВыполнитьРасчетСЗамеромВремени(ПараметрыРасчета);
{ОбщийМодуль.УчетНДСУП.Модуль(7742)}: УчетНДСУПСлужебный.СформироватьЗаписиКнигиПокупокПродаж(КонецПериода, МассивОрганизаций, МассивСчетовФактур);
{ОбщийМодуль.ЗакрытиеМесяцаСервер.Модуль(13608)}: УчетНДСУП.ВыполнитьЗаданияПоФормированиюКнигиПокупокПродаж(
{(1)}:ЗакрытиеМесяцаСервер.Выполнить_ФормированиеЗаписейКнигПокупокИПродаж(Параметры[0])
{ОбщийМодуль.ОбщегоНазначения.Модуль(4856)}: Выполнить ИмяМетода + "(" + ПараметрыСтрока + ")";
{Обработка.ОперацииЗакрытияМесяца.МодульМенеджера(1558)}: ОбщегоНазначения.ВыполнитьМетодКонфигурации(
{(1)}:Обработки.ОперацииЗакрытияМесяца.ВыполнитьРасчетЭтапов(Параметры[0])
{ОбщийМодуль.ОбщегоНазначения.Модуль(4856)}: Выполнить ИмяМетода + "(" + ПараметрыСтрока + ")";
{ОбщийМодуль.УчетНДСУПСлужебный.Модуль(1526)}: ВызватьИсключение ТекстСообщения;
{ОбщийМодуль.УчетНДСУП.Модуль(7742)}: УчетНДСУПСлужебный.СформироватьЗаписиКнигиПокупокПродаж(КонецПериода, МассивОрганизаций, МассивСчетовФактур);
{ОбщийМодуль.ЗакрытиеМесяцаСервер.Модуль(13608)}: УчетНДСУП.ВыполнитьЗаданияПоФормированиюКнигиПокупокПродаж(
{(1)}:ЗакрытиеМесяцаСервер.Выполнить_ФормированиеЗаписейКнигПокупокИПродаж(Параметры[0])
{ОбщийМодуль.ОбщегоНазначения.Модуль(4856)}: Выполнить ИмяМетода + "(" + ПараметрыСтрока + ")";
{Обработка.ОперацииЗакрытияМесяца.МодульМенеджера(1558)}: ОбщегоНазначения.ВыполнитьМетодКонфигурации(
{(1)}:Обработки.ОперацииЗакрытияМесяца.ВыполнитьРасчетЭтапов(Параметры[0])
{ОбщийМодуль.ОбщегоНазначения.Модуль(4856)}: Выполнить ИмяМетода + "(" + ПараметрыСтрока + ")";
Открыл в конфигураторе ОбщийМодуль.РеглУчетПроведениеСервер строку № 3509 с текстом ВызватьИсключение ОписаниеОшибки;
Процедура ПроверитьДатуЗапретаДляОтраженияДокументовВРеглУчете(ШаблонЗапретаДанных, НаборЗаписей = Неопределено, ОтражаемыйДокумент = Неопределено)
Если НаборЗаписей = Неопределено Тогда
Отбор = Новый Структура;
НаборЗаписей = Новый Структура("Регистр, Отбор", "РегистрСведений.ОтражениеДокументовВРеглУчете", Отбор);
Иначе
НаборЗаписей.ДополнительныеСвойства.Вставить("ПроверкаДатыЗапретаИзменения", Ложь);
КонецЕсли;
ОписаниеДанных = Новый Структура("НоваяВерсия, Данные", Истина, НаборЗаписей);
ОписаниеОшибки = "";
Если ДатыЗапретаИзменения.НайденЗапретИзмененияДанных(ШаблонЗапретаДанных, ОписаниеДанных, ОписаниеОшибки) Тогда
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Установка нового статуса отражения в учете';
|en = 'Set new status of recording in accounting'", ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка()),
УровеньЖурналаРегистрации.Ошибка,
?(ОтражаемыйДокумент = Неопределено, ОтражаемыйДокумент, ОтражаемыйДокумент.Метаданные()),
?(ОтражаемыйДокумент = Неопределено, ОтражаемыйДокумент, ОтражаемыйДокумент),
ОписаниеОшибки);
ВызватьИсключение ОписаниеОшибки;
КонецЕсли;
КонецПроцедуры
Вижу, что процедура обща
я (вызывается для разных ситуаций), на первый взгляд нет никакого признака показывающего, что вызов идет из обработки закрытия месяца.- В поисках решения сразу пришли мысли: протянуть из обработки закрытия какой-нибудь параметр функции или сделать некий глобальный признак. Стал смотреть стек вызовов всех процедур (см. выше блок "Текст ошибки со стеком вызова")
читаю в нём:
....
ЗакрытиеМесяцаСервер.Выполнить_ФормированиеЗаписейКнигПокупокИПродаж(Параметры[0])
...
Обработки.ОперацииЗакрытияМесяца.ВыполнитьРасчетЭтапов
....
и тут загорается лампочка!
Можно ведь по тексту стека вызовов понять, что вызов идёт из закрытия месяца!
- Нашёл, как получить стек вызовов в коде. Строку с данными стека вызовов можно получить только следующей комбинацией ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()). Причем отдельно в ИнформацияОбОшибке() данных о стеке не содержится, она загадочным образом достается только функцией ПодробноеПредставлениеОшибки().
Попытка
ВызватьИсключение "Любой текст";
Исключение
ПолныйТекстОшибкиВключаяСтекВызовов = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
- Далее создал расширение конфигурации, в которое перетащил процедуру &Вместо("ПроверитьДатуЗапретаДляОтраженияДокументовВРеглУчете") из ОбщийМодуль.РеглУчетПроведениеСервер. Описал условие Если [...] Тогда, где поиском выражений-маркеров в полном тексте ошибки понимаю, что идёт вызов этапа формирования книг из обработки закрытия месяца и не применяю контроль даты запрета.
Разбор стека самим кодом помог определять откуда пользователь стартует вызов, чтобы снять типовое ограничение в общем методе.
Какой могу сделать вывод в заключение:
Используя инъекцию конструкции (см. ниже) в код конфигурации (напрямую или через расширение) можно дополнительно анализировать данные стека вызовов. Возможность, которая немного расширяет горизонты разработки условий.
Попытка
ВызватьИсключение "Любой текст";
Исключение
ПолныйТекстОшибкиВключаяСтекВызовов = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
Если Найти(ПолныйТекстОшибкиВключаяСтекВызовов, "Выражение-маркер 1") > 0
И ...
И Найти(ПолныйТекстОшибкиВключаяСтекВызовов, "Выражение-маркер N") > 0 Тогда
//Исполняемый код 1
Иначе
//Исполняемый код 2
КонецЕсли;
КонецПопытки;
Коллеги, все знают о том, что попытки исключения - это затратный по времени механизм его нужно применять с осторожностью, это не панацея, я лично против него.
Основная мысль заложенная в данную публикацию это ИДЕЯ как вытащить дерево стека в сам исполняемый код, идея которую можно положить себе в ячейку памяти. Возможно она пригодиться!
На практике такой приём позволяет зашить часть возможностей отладки в СВОЙ КОД 1С, чтобы он получал и анализировал дерево стека и направлял алгоритм в разные ветки, в зависимости от наличия в тексте стека вызова определенных процедур (выражений-маркеров).
Ссылка на компетенции по 1С:ERP - команда со знаниями, умениями и успешными проектами.