Автоматизация оценки профессиональных, финансовых или операционных рисков — частая задача в корпоративных системах. Обычно пользователю предлагается выбрать параметры по нескольким шкалам (например, вероятность наступления события, частота подверженности опасности и тяжесть последствий), а система должна мгновенно рассчитать итоговый уровень угрозы.
В этой статье мы подробно разберем, как правильно реализовать этот алгоритм в 1С:Предприятие. Мы рассмотрим теорию оценки рисков, разберем и исправим типичные ошибки в коде 1С, с которыми сталкиваются начинающие разработчики, а также спроектируем профессиональную и масштабируемую архитектуру решения с использованием регистров сведений.
В процессе обсуждения подобных задач часто возникает дилемма: какую математическую модель использовать для расчета? Рассмотрим два основных подхода:
Риск = Вероятность + Подверженность + Последствия. Этот подход прост в реализации, но имеет существенный недостаток: он "сглаживает" критические угрозы. Смертельный исход при минимальной вероятности может дать такой же средний балл, как регулярная мелкая царапина.ИндексРиска = Вероятность * Подверженность * Последствия. Умножение нелинейно увеличивает итоговую оценку при возрастании тяжести последствий, что позволяет корректно выделять критические риски, требующие немедленного вмешательства.В зависимости от требований вашего бизнеса вы можете использовать любую из этих моделей. Ниже мы детально разберем программную реализацию обоих вариантов в 1С.
Часто разработчики начинают с написания простых условных конструкций в модуле формы. Однако здесь легко совершить ошибку приведения типов. Проанализируем типичный некорректный код, который часто приводит к ошибке "Преобразование значения к типу Булево не может быть выполнено":
// ПРИМЕР С ОШИБКОЙ
Если ПочтиНевозможно Тогда
Вероятность = 1;
КонецЕсли;
Выясним причину этой ошибки. Конструкция Если...Тогда ожидает, что выражение между ключевым словом Если и Тогда вернет логическое значение (Истина или Ложь). Если переменная ПочтиНевозможно содержит строку, ссылку или число, платформа 1С попытается преобразовать его к типу Булево и выдаст ошибку времени выполнения.
Чтобы исправить эту ситуацию, нам необходимо сравнивать выбранное пользователем значение с конкретным эталоном (строкой или элементом перечисления). Разберем по шагам корректный вариант функции расчета на клиенте:
&НаКлиенте
Функция РассчитатьИндексРискаЛокально(ВыбраннаяВероятность, ВыбраннаяПодверженность, ВыбранныйРезультат)
БаллВероятности = 0;
БаллПодверженности = 0;
БаллРезультата = 0;
// Оцифровка шкалы Вероятности
Если ВыбраннаяВероятность = "Почти невозможно" Тогда
БаллВероятности = 1;
ИначеЕсли ВыбраннаяВероятность = "Маловероятно" Тогда
БаллВероятности = 2;
ИначеЕсли ВыбраннаяВероятность = "Возможно" Тогда
БаллВероятности = 4;
ИначеЕсли ВыбраннаяВероятность = "Почти всегда" Тогда
БаллВероятности = 6;
КонецЕсли;
// Оцифровка шкалы Подверженности
Если ВыбраннаяПодверженность = "Редко" Тогда
БаллПодверженности = 1;
ИначеЕсли ВыбраннаяПодверженность = "Еженедельно" Тогда
БаллПодверженности = 3;
ИначеЕсли ВыбраннаяПодверженность = "Постоянно" Тогда
БаллПодверженности = 6;
КонецЕсли;
// Оцифровка шкалы Последствий (Результата)
Если ВыбранныйРезультат = "Первая помощь" Тогда
БаллРезультата = 1;
ИначеЕсли ВыбранныйРезультат = "Инвалидность" Тогда
БаллРезультата = 4;
ИначеЕсли ВыбранныйРезультат = "Смертельный случай" Тогда
БаллРезультата = 6;
КонецЕсли;
// Пример расчета по методу сложения долей
СуммаБаллов = БаллВероятности + БаллПодверженности + БаллРезультата;
МаксимальноВозможнаяСумма = 18; // 6 + 6 + 6
ПроцентРиска = (СуммаБаллов / МаксимальноВозможнаяСумма) * 100;
// Определение текстового уровня риска
Если ПроцентРиска < 30 Тогда
Возврат "Низкий уровень риска";
ИначеЕсли ПроцентРиска >= 30 И ПроцентРиска < 70 Тогда
Возврат "Средний уровень риска";
Иначе
Возврат "Высокий уровень риска";
КонецЕсли;
КонецФункции
Этот код полностью работоспособен, однако он обладает серьезным недостатком — жестким кодированием параметров (hardcoding). Если бизнес-пользователи завтра захотят изменить веса коэффициентов, добавить новые уровни или переименовать пункты меню, вам придется переписывать программный код конфигурации.
Для создания гибкой и легко поддерживаемой системы мы полностью откажемся от написания портянок из условий Если...Тогда. Перенесем все настройки шкал и весов в базу данных. Спроектируем структуру метаданных:
Перечисление.ВероятностиРиска со значениями: ПочтиНевозможно, Маловероятно, Возможно, ПочтиВсегда.Перечисление.ПодверженностиРиска со значениями: Редко, Еженедельно, Постоянно.Перечисление.ПоследствияРиска со значениями: ПерваяПомощь, Инвалидность, СмертельныйСлучай.ВесаПараметровРиска:
ПараметрОценки (Тип: ЛюбаяСсылка на наши перечисления или составной тип).Балл (Тип: Число, длина 5, точность 2).ШкалаУровнейРиска:
НижняяГраница (Тип: Число, хранит минимальный балл диапазона).ВерхняяГраница (Тип: Число, хранит максимальный балл диапазона).УровеньРиска (Тип: Строка или Справочник "УровниРисков").Теперь напишем универсальную функцию расчета в общем модуле. Данная функция будет динамически получать веса из регистра сведений, рассчитывать итоговый индекс путем перемножения (методология Файна-Кинни) и сопоставлять результат со шкалой диапазонов с помощью запроса.
// Функция рассчитывает индекс профессионального риска и возвращает его категорию
Функция РассчитатьКатегориюРиска(Вероятность, Подверженность, Последствия) Экспорт
// Инициализируем баллы значениями по умолчанию
БаллВр = 0;
БаллПд = 0;
БаллПс = 0;
// Запрос для быстрого получения баллов по выбранным значениям
ЗапросБаллов = Новый Запрос;
ЗапросБаллов.Текст =
"ВЫБРАТЬ
| Веса.ПараметрОценки КАК Параметр,
| Веса.Балл КАК Балл
|ИЗ
| РегистрСведений.ВесаПараметровРиска КАК Веса
|ГДЕ
| Веса.ПараметрОценки В(&СписокПараметров)";
СписокПараметров = Новый Массив;
СписокПараметров.Добавить(Вероятность);
СписокПараметров.Добавить(Подверженность);
СписокПараметров.Добавить(Последствия);
ЗапросБаллов.УстановитьПараметр("СписокПараметров", СписокПараметров);
ВыборкаБаллов = ЗапросБаллов.Выполнить().Выбрать();
Пока ВыборкаБаллов.Следующий() Цикл
Если ВыборкаБаллов.Параметр = Вероятность Тогда
БаллВр = ВыборкаБаллов.Балл;
ИначеЕсли ВыборкаБаллов.Параметр = Подверженность Тогда
БаллПд = ВыборкаБаллов.Балл;
ИначеЕсли ВыборкаБаллов.Параметр = Последствия Тогда
БаллПс = ВыборкаБаллов.Балл;
КонецЕсли;
КонецЦикла;
// Вычисление индекса риска по мультипликативной модели (Файн-Кинни)
ИндексРиска = БаллВр * БаллПд * БаллПс;
// Запрос для определения категории риска по шкале диапазонов
ЗапросШкалы = Новый Запрос;
ЗапросШкалы.Текст =
"ВЫБРАТЬ
| Шкала.УровеньРиска КАК Уровень
|ИЗ
| РегистрСведений.ШкалаУровнейРиска КАК Шкала
|ГДЕ
| &ИндексРиска МЕЖДУ Шкала.НижняяГраница И Шкала.ВерхняяГраница";
ЗапросШкалы.УстановитьПараметр("ИндексРиска", ИндексРиска);
ВыборкаШкалы = ЗапросШкалы.Выполнить().Выбрать();
Если ВыборкаШкалы.Следующий() Тогда
Возврат ВыборкаШкалы.Уровень;
Иначе
Возврат "Не определен (вне шкалы диапазонов)";
КонецЕсли;
КонецФункции
Проанализируем, как связать этот алгоритм с интерфейсом пользователя на управляемой форме. Предположим, у нас есть документ оценки рисков, на форму которого выведены реквизиты: Объект.Вероятность, Объект.Подверженность и Объект.Последствия.
Для обеспечения мгновенной реакции интерфейса нам необходимо назначить обработчик события ПриИзменении для каждого из этих трех элементов управления на форме. Все они могут вызывать одну общую процедуру:
&НаКлиенте
Процедура ОценкаРискаПриИзменении(Элемент)
// Проверяем заполненность всех ключевых полей перед началом расчета
Если ЗначениеЗаполнено(Объект.Вероятность)
И ЗначениеЗаполнено(Объект.Подверженность)
И ЗначениеЗаполнено(Объект.Последствия) Тогда
// Вызываем серверный контекст для выполнения расчета по регистрам сведений
Объект.УровеньРиска = ПолучитьУровеньРискаНаСервере(Объект.Вероятность, Объект.Подверженность, Объект.Последствия);
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьУровеньРискаНаСервере(Вероятность, Подверженность, Последствия)
// Вызываем нашу универсальную функцию из общего модуля
Возврат ПроведениеОценкиРисковСервер.РассчитатьКатегориюРиска(Вероятность, Подверженность, Последствия);
КонецФункции
Рассмотренный нами подход с вынесением логики в регистры сведений предоставляет следующие преимущества:
Применение мультипликативного метода Файна-Кинни совместно с гибкой архитектурой метаданных 1С позволит вам создать надежную, отказоустойчивую и удобную систему мониторинга рисков любой сложности.