Каждому программисту время от времени приходится выдавать сообщения о тех или иных ошибках в данных. И часто возникает дилемма: либо черкнуть что-то простое, и продолжить решать поставленную задачу; либо, глубоко вздохнув, начать писать развернутое сообщение.
Это хороший тест, проверьте себя. Прагматы выбирают первый вариант, зануды (не в обиду) - второй, а гении … - вообще игнорируют .
Это сообщение адресовано второй категории, к которой отношу и себя. Итак, выбор сделан, решено дать развернутое сообщение об ошибке. Но вот незадача – в той процедуре, в которой возникла ошибка, нет сведений для развернутого сообщения. Не беда. Добавляем параметры в процедуру и передаем в них недостающие сведения. Но и тут проблемы. В вызывающей процедуре имеется только часть этих сведений. Не очень радует, а что делать?
Ниже приводится простая процедура для преодоления этих трудностей. В комментариях к ней приводится пример использования.
Куда вставить эту процедуру? Можно в какой-нибудь общий модуль, можно в разные модули, где она понадобится. Например, в модуль управляемой формы, чтобы лишний раз не дергать сервер.
// Процедура обрабатывает текст ошибки
//
// Назначение:
//
// 1. Иерархическая обработка ошибок.
// На каждом уровне иерархии ошибку можно дополнить уточняющими
// сведениями о месте ее возникновения.
// Примерамии иерархии являются: вложенный вызовов процедур,
// вложенные циклы, вложенные условия "Если Тогда Иначе" и т. д.
//
// 2. Гибкость выбора места вывода ошибок.
// Вопрос: где выводить ошибку, в процедуре, где она возникла,
// или в вызывающей процедуре решается гибко.
// Если внешнюю процедуру ошибка интересует, то ошибка будет передана ей,
// иначе вывод ошибки будет сделан во внутренней процедуре.
//
// 3. Накопление списка ошибок и выдачи их единым результатом,
//
//
//
// Параметры
//
// ТекстНакопительОшибок - либо Неопределено, либо строка. Входной и выходной параметр.
// Текст, содержащий список ошибок
// ТекстОшибки - строка, входной параметр.
// Текст ошибки или собранный ранее накопитель ошибок
// КоординатыОшибки - любой тип, входной параметр.
// Приводится к строковому типу. Текст информирующий пользователя
// о месте возникновения ошибки. Следует указать место в
// именительном падеже, например "Строка 1" или ЭтотОбъект.Ссылка
//
//
// Описание
//
// 1. Если ТекстНакопительОшибок - имеет тип "Строка", то ТекстОшибки и КоординатыОшибки
// будут добавлены в ТекстНакопительОшибок, при этом, вывода на экран не будет
//
// 2. В противном случае ТекстОшибки и КоординатыОшибки будут выведены на экран
//
// Замечание 1. Повторные одинаковые ошибки накопитель отбрасывает
// Замечание 2. Если ТекстОшибки - не простая ошибка, а уже ранее собранный накопитель
// ошибок, то КоординатыОшибки будут добавлены слева к уже существующим
// координатам каждой из ошибок
//
//
//
// Пример.
//
//
// Процедура ОбработатьСтроку(НомерСтроки, Ошибки = Неопределено)
// ОшибкуОтдать(Ошибки, "Лимит превышен", "Строка " + НомерСтроки);
// КонецПроцедуры
//
//
// Процедура ВариантСУточнением(Ошибки = Неопределено)
// Ош = "";
// ОбработатьСтроку(1, Ош);
// ОбработатьСтроку(2, Ош);
// Если ЗначениеЗаполнено(Ош) Тогда
// ОшибкуОтдать(Ошибки, Ош, "ТЧ Данные"); // отдаем ошибку нижнего уровня наверх,
// // уточняя своими координатами
// КонецЕсли;
// КонецПроцедуры
//
//
// Процедура ВариантБезУточнения(Ошибки = Неопределено)
// ОбработатьСтроку(1, Ошибки); // Проносим ошибку без контроля
// ОбработатьСтроку(2, Ошибки); //
// КонецПроцедуры
//
//
// Процедура Испытание()
// ВариантСУточнением(); // выдаст:
// // Лимит превышен. См. ТЧ Данные, Строка 1
// // Лимит превышен. См. ТЧ Данные, Строка 2
//
// Ошибки = "";
// ВариантСУточнением(Ошибки);
// Если Ошибки <> "" Тогда
// ОшибкуОтдать(, Ошибки, "Испытание"); // выдаст:
// // Лимит превышен. См. Испытание, ТЧ Данные, Строка 1
// // Лимит превышен. См. Испытание, ТЧ Данные, Строка 2
// КонецЕсли;
//
//
//
// ВариантБезУточнения(); // выдаст:
// // Лимит превышен. См. Строка 1
// // Лимит превышен. См. Строка 2
//
// Ошибки = "";
// ВариантБезУточнения(Ошибки);
// Если Ошибки <> "" Тогда
// ОшибкуОтдать(, Ошибки, "Испытание"); // выдаст:
// // Лимит превышен. См. Испытание, Строка 1
// // Лимит превышен. См. Испытание, Строка 2
// КонецЕсли;
// КонецПроцедуры
//
//
Процедура ОшибкуОтдать(ТекстНакопительОшибок = Неопределено, ТекстОшибки = "", КоординатыОшибки = Неопределено) Экспорт
Если не ЗначениеЗаполнено(ТекстОшибки) Тогда
Если не ЗначениеЗаполнено(ТекстНакопительОшибок) или не ЗначениеЗаполнено(КоординатыОшибки) Тогда
Возврат;
КонецЕсли;
КонецЕсли;
РазделительСписка = "..|.."; // между ошибками
Сцепка = ":,:"; // между текстом ошибки и координатами
// Возможно пользователь использовал Символы.ПС в своих нуждах, поэтому
// временно заменим все Символы.ПС на другую комбинацию символов.
// Потом вернем назад
ВместоПС = "{;}"; // временно заменяет Символы.ПС
ТекстНак = СтрЗаменить(СокрЛП(ТекстНакопительОшибок), Символы.ПС, ВместоПС);
ТекстНак = СтрЗаменить(ТекстНак, РазделительСписка, Символы.ПС);
ТекстТек = СтрЗаменить(СокрЛП(ТекстОшибки), Символы.ПС, ВместоПС);
ТекстТек = СтрЗаменить(ТекстТек, РазделительСписка, Символы.ПС);
Коорд = СокрЛП(КоординатыОшибки);
// Разложение параметра ТекстНакопительОшибок в массив строк
мвОшибки = Новый Массив;
Для к = 1 По СтрЧислоСтрок(ТекстНак) Цикл
мвОшибки.Добавить(СтрПолучитьСтроку(ТекстНак, к));
КонецЦикла;
// Разложение параметра ТекстОшибки на строки и их добавление в массив строк
Для НомерСтроки = 1 По СтрЧислоСтрок(ТекстТек) Цикл
ТекстСтроки = СокрЛП(СтрПолучитьСтроку(ТекстТек, НомерСтроки));
КоордСтроки = "";
к = Найти(ТекстСтроки, Сцепка);
Если к > 0 Тогда
КоордСтроки = СокрЛП(Сред(ТекстСтроки, к + СтрДлина(Сцепка)));
ТекстСтроки = СокрЛП(Лев(ТекстСтроки, к-1));
КонецЕсли;
Если Коорд <> "" Тогда
Если КоордСтроки = "" Тогда
КоордСтроки = Коорд;
Иначе
КоордСтроки = Коорд + ", " + КоордСтроки;
КонецЕсли;
КонецЕсли;
Если КоордСтроки <> "" Тогда
ТекстСтроки = ТекстСтроки + Сцепка + КоордСтроки;
КонецЕсли;
Если мвОшибки.Найти(ТекстСтроки) = Неопределено Тогда
мвОшибки.Добавить(ТекстСтроки);
ТекстНак = ТекстНак + ?(ТекстНак = "", "", Символы.ПС) + ТекстСтроки;
КонецЕсли;
КонецЦикла;
Если ТипЗнч(ТекстНакопительОшибок) = Тип("Строка") Тогда
ТекстНак = СтрЗаменить(ТекстНак, Символы.ПС, РазделительСписка);
ТекстНак = СтрЗаменить(ТекстНак, ВместоПС, Символы.ПС);
ТекстНакопительОшибок = ТекстНак;
Иначе
ТекстНак = "";
Для каждого ТекстСтроки Из мвОшибки Цикл
ТекстСтр = ТекстСтроки;
Если СтрДлина(ТекстСтр) > 100 Тогда
ТекстСтр = СтрЗаменить(ТекстСтр, Сцепка, ВместоПС + "См. ");
Иначе
ТекстСтр = СтрЗаменить(ТекстСтр, Сцепка, ". См. ");
КонецЕсли;
ТекстНак = ТекстНак + ?(ТекстНак = "", "", РазделительСписка) + ТекстСтр;
КонецЦикла;
ТекстНак = СтрЗаменить(ТекстНак, РазделительСписка, Символы.ПС); // каждую ошибку с новой строки
ТекстНак = СтрЗаменить(ТекстНак, ВместоПС, Символы.ПС); // обратная замена
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ТекстНак;
Сообщение.Сообщить();
КонецЕсли;
КонецПроцедуры