Руководство прикладного разработчика#

Термины#

Термин

Значение

Platform V

Набор программных продуктов Platform V, представляющих совокупность функциональных возможностей и позволяющих обеспечить быстрое конструирование информационных систем из множества готовых компонентов

Редакция

Отдельный расчетный алгоритм, который использует зафиксированный в версии набор входных/выходных параметров

Продуктовая фабрика

Подсистема, которая выполняет обслуживание группы продуктов или автоматизацию группы функций продуктов

Сокращения#

Сокращение

Значение

АС

Автоматизированная система

НСИ

Нормативно-справочная информация

DSL

Domain-Specific Language, предметно-ориентированный язык, который используется в Продукте

Системные требования#

Аппаратные требования#

В качестве клиентской части Конструктора Стратегий Продукта используется АРМ, реализуемое по технологии "тонкого" клиента (WEB-интерфейс). Минимальные требования к составу оборудования системного блока типового АРМ по WEB-технологии и их характеристики:

  • процессор — Pentium III;

  • объем оперативной памяти — 512 Мб;

  • сетевая карта;

  • видеокарта — SVGA 32 Мб с поддержкой видео режима с глубиной цвета не хуже HiColor (65536 цветов);

  • жёсткий диск — 10 Гб;

  • мониторы с разрешающей способностью не менее 1024х768 пикселей.

Программные требования#

Требования к ПО:

  • сервер приложений WildFly 10.1.0.Final;

  • OpenJDK 1.8.

Подключение и конфигурирование#

Для корректной установки Продукта требуется наличие следующих компонентов – модулей в сервере приложений, от которых зависит Продукт:

<dependencies>
    <module name="org.dom4j"/>
    <module name="deployment.custodian-distr-impl-ear.ear" services="import" optional="true"/>
    <module name="deployment.dpl-lite-ear-${version}.ear" meta-inf="import" optional="true"/>
    <module name="deployment.seap-lib-${version}.ear" meta-inf="import" services="import" optional="true" />
</dependencies>

Миграция на текущую версию#

Возможности Продукта доступны сторонним Продуктовым фабрикам и другим приложениям исключительно через API. Поэтому выполнять миграцию приложения на текущую версию Продукта нужно только в случае изменения используемой приложением части спецификации пользовательского API.

Сведения о способах использования API Product Pricing можно найти в документе Описание API и в разделе Описание параметров API.

Разработка первого приложения с использованием программного продукта#

Требования к окружению приведены в разделе "Системные требования" документа "Руководства по установке". Подключение и конфигурирование продукта описано в разделе "Подключение и конфигурирование" документа "Руководство прикладного разработчика". Примеры использования API программного продукта, а также описание параметров запроса и ответа, приведены в приведены в разделе “Описание параметров API” и в документе "Описание API".

Использование программного продукта#

Примеры использования API программного продукта описаны в разделе “Описание параметров API” и в документе "Описание API". Продукт Platform V Product Pricing – решение для быстрой реализации алгоритмов и управления бизнес-логикой на основе специализированного языка, формально описывающего процесс принятия решений в виде системы бизнес-правил – декларативных утверждений, указывающих на выполнение некоторых действий в случае выполнения определенных условий. Сферы применения: организация алгоритмов в области принятия решений и расчетов по гибкой тарификации услуг, процентных ставок, комиссий, страховых премий, инвестиционных предложений, прочих финансовых услуг с учетом динамически меняющихся параметров (объемов потребления услуг, применения тарифных планов, ставок межбанковского кредитования и т.д.).

Часто встречающиеся проблемы и пути их устранения#

В настоящее время информации о возможных проблемах не имеется.

Предметно-ориентированный язык (DSL)#

В рамках Product Pricing реализован внешний предметно-ориентированный язык (DSL=DomainSpecific Language) с ограниченным набором конструкций и упрощенной грамматикой, созданной для удобства чтения, понимания и написания кода.

Грамматика состоит из простых односложных конструкций (присваивание, вызов функции, арифметические действия, сравнение). Основной акцент при определении грамматики был сделан на то, что в процессе выполнения программы нам необходимо вычислить результат (набор выходных параметров). Соответственно большая часть конструкций ориентирована на присвоение переменной какого-либо значения.

В грамматике не предполагается введение операторов перехода (goto). Также не предполагается введение циклов с задаваемым количеством шагов или условием (for, while).

Описание грамматики DSL#

Каждое правило DSL состоит из набора присвоений переменных, объявления функций и результата. В конце строки обязательно ставится точка с запятой (";").

Типы данных#

Поддерживаемые типы данных делятся на простые, составные и списковые:

Простые типы данных:

  • Строка — задаётся в кавычках: "успешно".

  • Целое число: 14.

  • Дробное число: 17.6.

  • Логическое выражение – ДА / НЕТ: ДА.

  • Дата:13.01.1998.

Составные типы данных:

  • Наборы полей: [название: "базовый", код: "base", значение: 15].

  • Встроенные наборы полей.

Списковые типы. Тип элементов списка в свою очередь также может быть простым, набором полей или встроенным набором полей:

  • список;

  • список([имя: "Иван", возраст: 47], имя: "Федор", возраст: 14]).

Переменные#

Правила именования переменных#

В имени переменной могут быть использованы русские буквы (в верхнем/нижнем регистре), цифры, символ подчеркивания ("_"). Первым символом в названии обязательно должна идти буква. Именем переменной не могут являться зарезервированные слова.

Зарезервированные слова представлены в таблице ниже:

к

списку

максимум

минимум

первый

и

это

задана

задан

задано

не

список

результат

ДА

НЕТ

или

вход

выход

добавить

ошибка

все

индекс

Итого, корректные названия переменных:

  • ставка;

  • Процент1_;

  • средний_остаток.

Некорректные названия переменных:

  • 1сумма (в начале имени переменной должна идти буква);

  • ___сумма (в начале имени переменной должна идти буква);

  • Summ (в имени не могут использоваться латинские буквы);

  • Клиент!возраст (допустимые специальные символы для использования - "." и "_");

  • список (зарезервированное слово).

Категории переменных#

Для читабельности переменные разделены на следующие категории:

  • Входные параметры (идут с префиксом "вход.") – те параметры, которые передаются на вход правилу: вход.возраст_ клиента.

  • Выходные параметры (идут с префиксом "выход.") – те параметры, которые возвращаются из правила: выход.процентная_ ставка.

  • Временные переменные – те переменные, которые создаются в самом правиле (идут без префикса): переменная.

Присвоение значений переменным#

Присвоение значений переменным бывает двух типов - с условием и без него. В свою очередь присвоение с условием может быть как однострочным (в случае единственного условия), или многострочным (блоковым), когда условий несколько.

Присвоение без условий#

DSL оперирует с переменными, которым может быть присвоено:

  • конкретное значение:

    • переменная: = 14.0;

    • переменная: = "успешно";

    • переменная: = ДА;

  • значение другой переменной:

    переменная_1:= 3;

    переменная_2:= переменная_1;

  • набор полей:

    переменная:= название:"базовый", значение:10];

  • список:

    • переменная:=("а","б","в");

    • переменная:=(67.5);

    • переменная:=(переменная_1, переменная_2, переменная_3);

  • результат вычисления функции:

    переменная:=разница_в_днях(вход.дата_открытия_вклада, текущая_дата);
    
  • результат выполнения арифметического выражения с участием, как конкретных значений, так и других переменных, и функций:

    • переменная:=вход.сумма * 2 + 50;

    • переменная:=(вход.сумма * вход.процент + 1) * 2;

Присвоение с одним условием (однострочное)#

В случае если переменной должно быть присвоено значение только при выполнении определенного условия, необходимо использовать условную операцию присвоения:

<переменная>:=<логическое_выражение> =><результат_вычисления>;

Например:

комиссия:=сумма>500=>0;

Логические выражение#

Каждое отдельное логическое выражение должно возвращать значения: истина, ложь.

Логическое выражение может представлять собой:

  • Конструкцию сравнения вида:

    <операнд_1> <оператор сравнения> <операнд_2>

    Операндом может являться переменная, константа, вызов функции или арифметическое выражение.

  • Проверку на существование значения у переменной:

    <переменная> [не] задан.

  • Результат выполнения функции, которая возвращает значения ДА или НЕТ.

  • Значение логической переменной.

Примеры логических выражений:

  • вход.возраст_клиента > 55;

  • вход.возраст_клиента > получить_пенсионный_возраст();

  • 55 < вход.возраст_клиента;

  • вход.сумма * 2 < 10000;

  • разница_в_днях(вход.дата_открытия_вклада, текущая_дата) < 365;

  • вход.возраст_клиента задан;

  • вход.пол_клиента не задан;

  • список_содержит(список_строк, "123").

Логические выражения могут быть скомбинированы с использованием операторов "и", "или" и сгруппированы между собой при помощи скобок. Пример:

(вход.возраст_клиента > 60 и вход.пол_клиента = "мужской") или
    (вход.возраст_клиента > 55 и вход.пол_клиента = "женский")

В логических выражениях могут быть использован оператор отрицания "не". Пример:

  • не (вход.возраст_клиента > 55);

  • не (вход.пол_клиента = "Мужской").

Результат вычисления#

Результат вычисления представляет собой одну из конструкций: переменная, константа, арифметическое выражение, вызов функции. Результат вычисления всегда идет после логического выражения и отделяется от логического выражения специальным символом "=>". Пример результата вычисления (выделен жирным):

  • вход.возраст_клиента > 60 => 15.0;

  • вход.возраст_клиент > 60 => получить_базовую_ставку_из_НСИ();

  • вход.возраст_клиент > 60 => получить_базовую_ставку_из_НСИ() + 1.0.

Присвоение с несколькими условиями (блоковое)#

При необходимости группировки условий выбора используется блоковое присвоение переменных с использованием группирующих фигурных скобок "{", "}". Блоковое присвоение переменных имеет вид:

переменная := < первый, все, максимум, минимум> {
	логическое_выражение_1 => результат_1;
	логическое_выражение_2 => результат_2;
    ...
	логическое_выражение_N => результат_N;
}

Если условие не требуется, логическое выражение и символ "=>" опускаются.

Алгоритм и результат выполнения блокового присвоения переменных зависят от ключевого слова перед блоком (первый, все, максимум, минимум). Если ключевое слово не указано, вычисление происходит как в случае "первый".

В случае, когда в блоке все условия являются ложными, значение переменной не изменяется.

Первый#

В этом случае будет выполнено первое "сработавшее" присвоение.

// Пример 1.
переменная := первый {
    1 > 0 => 1;
    2 > 0 => 2;
    3 > 0 => 3;
}

В данном примере, как только будет обнаружено первое истинное условие (1 > 0), Продукт прервёт обработку блока (то есть, оставшиеся два условия не будут проверены) и выполнит присвоение. Таким образом, результат этого присвоения:

переменная = 1

// Пример 2
переменная := первый {
    0 > 0 => 1;
    1 > 0 => 2;
    2 > 0 => 3;
}

В данном примере, первое условие (0 > 0) является ложным. Поэтому обработка блока прервётся только после проверки второго условия. Таким образом, результат этого присвоения:

переменная = 2

Все#

В этом случае будут выполнены все "сработавшие" присвоения.

// Пример 3
переменная := все {
    1 > 0 => 1;
    2 > 0 => 2;
    3 > 0 => 3;
}

В данном примере все три условия (1 > 0, 2 > 0, 3 > 0) являются истинными, значит, последовательно переменной будет присвоено три значения(1, 2, 3). Таким образом, результат этого присвоения:

переменная = 3

// Пример 4
переменная := все {
    1 > 0 => 1;
    1 > 2 => 2;
    1 > 3 => 3;
}

В данном примере первое условие (1 > 0) является истинным, и после его проверки в переменную запишется первое значение (1). Оставшиеся два условия являются ложными и никак не изменят значение переменной. Результат этого присвоения:

переменная = 1

В данном примере результат вычисления зависит от предыдущего вычисления:

// Пример 5
переменная := все {
    1 > 0 => 1;
    2 > 0 => переменная + 2;
    3 > 0 => переменная + 3;
}

В результате вычисления первой строки блока, в переменную будет записано значение 1 (так как 1 > 0). После этого выполнится вторая строка – и результат вычисления будет равен переменная (=1) + 2 = 1 + 2 = 3. После этого выполнится третья строка – и результат вычисления будет равен переменная (=3) +3 = 3 + 3 = 6. Итоговый результат:

переменная = 6

Максимум / Минимум#

В этом случае будут вычислены все "сработавшие" значения, и из них только максимальное (минимальное) значение будет присвоено переменной.

// Пример 6
переменная := максимум {
    1 > 0 => 1;
    2 > 0 => 2;
    3 > 0 => 3;
}

В данном случае все три условия (1 > 0, 2 > 0, 3 > 0) истинны. Однако, выберется только максимальное значение ( = 3). Таким образом, в результате

переменная = 3

Работа с числовыми типами данных#

Описание#

В DSL к числовым типам данных относятся целые и дробные числа. В качестве разделителя дробной части используется точка:

  • 8;

  • 9.05.

С числами можно выполнять арифметические операции: сложение(+), вычитание(-), умножение(*), деление(/), остаток от деления(%). Для изменения порядка действий используются круглые скобки, например, следующим образом: 9*(5+1).

Предопределённые математические функции#

Для работы с числами можно использовать предопределённые функции:

  • целое_от_деления(делимое, делитель): функция возвращает целую часть от частного.

  • возвести_в_степень(основание, показатель): функция возвращает значение аргумента основание, возведённое в степень показатель.

  • округлить(число, количество_знаков): функция возвращает число, округлённое до знака, переданного в качестве аргумента.

  • округлить_вверх(число, количество_знаков), округлить_вниз(число, количество_знаков): функция возвращает число, округлённое в большую/меньшую сторону до знака, переданного в качестве аргумента.

  • число_Пи(), число_е(): функция возвращает значение числа Pi/e.

  • синус(угол), косинус(угол), тангенс(угол), котангенс(угол): функция возвращает синус/ косинус / тангенс / котангенс угла. Угол задан в радианах.

  • градусы_в_радианы(градусы): функция переводит градусы в радианы.

  • радианы_в_градусы(радианы): функция переводит радианы в градусы.

  • модуль(число): функция возвращает модуль числа.

  • натуральный_логарифм(число): функция возвращает натуральный логарифм числа.

  • логарифм(число, основание): функция возвращает натуральный логарифм числа с основанием, переданным в качестве аргумента.

  • случайное_число(): функция возвращает случайное число в диапазоне от 0 до 1.

Работа со строками#

Описание#

Строки задаются внутри двойных кавычек:

"Мужской"

Для объединения нескольких строк используется операция конкатенации строк (+). При попытке конкатенации строки со значением другого типа, оно неявно преобразуется к строке, например, следующим образом.

  • "Клиент с именем " + имя_клиента + " отсутствует.";

  • "Результат = " + ДА;

  • "строка" + 1 +2;

  • 1 + 2 + "строка".

Результатом третьего выражения будет строка «строка12», а четвёртого «3 строка», т.к. операции выполняются слева направо.

Предопределённые функции для работы со строками#

Для работы со строками можно использовать предопределённые функции:

  • строка(строка): функция возвращает строковое представление объекта.

  • верхний_регистр(строка), нижний_регистр(строка): функция возвращает строку, в которой все символы преобразованы в верхний/нижний регистр

  • целое_число(строка), дробное_число(строка): функция преобразовывает строковое представление числа в целое/дробное число*.*

  • подстрока(строка, начало, длина): возвращает подстроку, первый символ и длина которой переданы в аргументах.

  • содержит_подстроку(строка, подстрока): функция проверяет, входит ли подстрока в строку.

  • равны_без_учета_регистра(строка_1, строка_2): функция проверяет, равны ли две строки без учёта регистра.

  • первое_вхождение(строка, подстрока), последнее_ вхождение(строка, подстрока): функция возвращает индекс первого/последнего вхождения подстроки в строку. Если подстрока в строку не входит, то функция возвращает 0.

  • длина(строка): функция возвращает длину строки. Если строка не задана, функция возвращает 0.

  • заменить_все(строка, подстрока, новая_подстрока), заменить_первое_вхождение(строка, подстрока, новая_ подстрока): функция возвращает строку, с которой все подстроки/первое вхождение подстроки заменены на новые, переданные в качестве аргументов.

  • разбить_ строку(строка, разделитель): функция возвращает список подстрок, полученных разделением строки с помощью разделителя, переданного в качестве аргумента.

  • соответствует_шаблону(строка, шаблон): функция проверяет, соответствует ли строка заданному шаблону.

  • получить_ группы(строка, шаблон): функция проверяет, соответствует ли строка заданному шаблону и возвращает список групп. Если строка не соответствует шаблону, возвращается пустой список.

Работа с датами#

Описание#

Даты можно задавать в следующем формате: **<день>.<месяц>.<год>**

Например: 01.10.2015

Предопределённые функции для работы с датами#

Для работы с датами можно использовать предопределённые функции:

  • разница_в_днях(дата_1, дата_2), разница_в_неделях(дата_1, дата_2), разница_в_месяцах(дата_1, дата_2), разница_в_годах(дата_1, дата_2): возвращают разницу между датами в днях, неделях, месяцах и годах соответственно.

  • первая_дата_раньше(дата_1, дата_2): функция сравнивает две даты, и если дата_1 меньше дата_2, то возвращает ДА, в противном случае возвращает НЕТ.

  • число(дата), месяц(дата), год(дата): возвращает число, месяц и год даты соответственно. Возвращаемое значение – число.

  • день_недели(дата): возвращает день недели.

  • прибавить_дни(дата, дни), прибавить_месяцы(дата, месяцы), прибавить_года(дата, года): возвращает дату, которая больше дата на указанное количество дней, месяцев или лет.

  • даты_равны(дата_1, дата_2): функция сравнивает две даты, и если дата_1 равна дата_2, то возвращает ДА, в противном случае возвращает НЕТ.

Работа со списками#

Описание#

Списком является набор однотипных элементов. Значения перечисляются внутри круглых скобок через запятую. Например:

  • список(2.0, 4.0, 10.5);;

  • список("Лучший", "Самый лучший");.

Заполнение списков#

Задавать значения в списке можно либо копированием значений другого списка, либо добавлением новых значений к уже существующим. Как и присвоение, добавление значений может быть с условием и без, однострочным и блоковым.

Копирование списков#

Копирование значений осуществляется с помощью операции присвоения. Копировать можно как значения вновь создаваемых списков, так и списковых переменных. Примеры копирования списков:

// Пример 
список_счетов := список(123, 456);
выход.список_пакетов_клиента := список_пакетов;
список_пакетов := возраст_клиента > 60 => список(“Пенсионный”);
список_пакетов := {
    вход.ТБ = 38 => список_пакетов_для_38;
    список(“Лучший”, “Премьер”, “Пенсионный”);
}

Однострочное добавление элементов к списку#

К списку можно добавлять:

  • константы:

    • к списку список_переменных добавить 10;;

    • к списку список_переменных добавить;

    • список_содержит(список_переменных, "123") => "123";*.

  • переменные:

    • к списку список_переменных добавить вход.ставка;.

  • результат вычисления арифметического выражения или функции:

    • к списку список_ставок добавить получить_ставку();;

    • к списку список_переменных добавить 1 + 0.9 * (переменная_1 + переменная_2);.

Блоковое добавление элементов к списку#

Если добавление значений зависит от нескольких условий, их можно объединить в блок, который имеет следующий вид:

к списку <списковая_переменная> добавить <первый, все, максимум, минимум> {
	<логическое_выражение_1> => <значение_1>;
	<логическое_выражение_2> => <значение_2>;
	...
	<логическое_выражение_N> => <значение_N>;
}
// Пример 7
к списку список_1 добавить все {
    1 > 0 => 1;
    2 > 100 => 2;
    3 > 0 => 3;
} 
к списку список_2 добавить первый {
    1 > 0 => 1;
    2 > 100 => 2;
    3 > 0 => 3;
} 
к списку список_3 добавить максимум {
    1 > 0 => 1;
    2 > 100 => 2;
    3 > 0 => 3;
}

Все три блока содержат одинаковый набор условий, из которых истинны первое (1 > 0) и третье (3 > 0). В результате список_1 будет содержать все подошедшие значения список_2 – первое подошедшее значение, список_3 – максимальное из подошедших.

список_1 = (1, 3)

список_2 = (1)

список_3 = (3)

Перебор элементов списка#

Перебор элементов списка при присвоении#

Для формирования блока присвоений можно использовать список. В этом случае будет выполнен перебор его элементов и будут проверены соответствующие условия. Алгоритм обработки условий зависит от ключевого слова (первый, все, максимум, минимум) и аналогичен алгоритму блокового присвоения. Для того чтобы иметь возможность использовать элементы списка внутри блока, необходимо задать имя элемента. Таким образом, блок присвоения с перебором элементов имеет следующий вид:

<переменная> := <первый, все, максимум, минимум> 
                <списковая_переменная> [<имя_элемента>] {
	<логическое_выражение> => <значение>;
}

<переменная> := <первый, все, максимум, минимум>

<списковая_переменная> х<имя_элемента>] {

<логическое_выражение> => <значение>;

}

// Пример 8
    список_переменных := список(1, 2, 3, 4);
    переменная := первый список_переменных[элемент] {
    элемент > 2 => элемент;
}

В данном примере последовательно берётся каждый элемент списка, и если он больше двух, присваивается в качестве значения переменной. То есть, для первых двух элементов списка (1 и 2) условие будет ложным, следовательно, присвоение не выполнится. Далее берётся третий элемент (3), и т.к. условие выполняется истинно, в переменную записывается значение элемента. На этом перебор заканчивается, т. к. было использовано ключевое слово "первый". В итоге переменная = 3.

Перебор элементов списка при добавлении#

Аналогично можно использовать перебор списка для добавления значений к другому списку при выполнении заданных условий. В этом случае блок принимает вид:

к списку <списковая_переменная_1> добавить 
<первый, все, максимум, минимум>
<списковая_переменная_2> [<имя_элемента>]{
	<логическое_выражение> => <значение>;
}
// Пример 9
список_переменных := список(1, 2, 3, 4);
к списку новый_список добавить все список_переменных[элемент] {
    элемент > 2 => элемент;
}

В данном примере перебираются все элементы списка список_переменных, и если их значение больше двух, то добавляются к списку новый_список. Т. к. используется ключевое слово "все", перебор продолжается до конца. Таким образом, в список добавятся два элемента – новый_список = (3, 4).

Ключевое слово «индекс»#

Внутри блока присвоения, при котором происходит перебор значений списка, для получения номера элемента в списке можно использовать ключевое слово «индекс». Таким образом, значение индекс может принимать значения от 1 до значения, равного длине списка.

// Пример 10
список_переменных := список(2, 3, 4, 5);
переменная := все список_переменных[элемент] {
индекс = 3 => элемент;
}

В данном примере перебираются все элементы списка список_переменных, и если порядковый номер элемента в списке (то есть индекс) равен 3, то значение этого элемента записывается в переменную. Таким образом переменной будет присвоено значение третьего элемента списка – переменная = 4.

Предопределённые функции#

Для работы со списками существуют предопределённые функции:

  • длина_списка(список): возвращает количество элементов в списке.

  • максимальный_элемент(список), минимальный_элемент(список): возвращает максимальный / минимальный элемент в списке.

  • список_содержит(список, значение) проверяет, содержится ли значение в списке. Если содержится, возвращает ДА, в противном случае – НЕТ.

  • сортировать_список(список, направление), сортировать_список_строк(список, направление), сортировать_список_дат(список, направление): возвращает список чисел/строк/дат, отсортированный по убыванию или по возрастанию.

  • объединить_списки(список_1, список_2) возвращает список, в котором содержатся все элементы список_1 и список_2.

  • получить_элемент_по_индексу(список, индекс) возвращает элемент списка с индексом, переданным в качестве аргумента. Если такой элемент не найден, возвращается пустой результат.

// Пример 11
список_переменных := список(1, 2, 6, 3, 4);
длина_списка := длина_списка(список_переменных); 
макс := максимальный_элемент(список_переменных);
мин := минимальный_элемент(список_переменных);
переменная_1 := список_содержит(список_переменных, 1);
переменная_5 := список_содержит(список_переменных, 5); 
сортированный_список := сортировать_список(список_переменных, “по-убыванию”);
объединенный_список := объединить_списки(список_переменных, список_переменных);
третий_элемент := получить_элемент_по_индексу(список_переменных, 3);

Результат вычислений:

длина_списка = 5

макс = 6

мин = 1

переменная_1 = ДА

переменная_5 = НЕТ

сортированный_список = (6, 4, 3, 2, 1)

объединенный_список = (1, 2, 6, 3, 4, 1, 2, 6, 3, 4)

третий _элемент = 6

Работа с наборами полей#

Описание#

Может потребоваться работать не с одиночными переменными, а с наборами полей. Количество и имена полей внутри набора жёстко фиксируется, при этом типы полей не зависят друг от друга. Перед использованием необходимо описать набор:

<имя_набора> это [<имя_1> это <тип_1>, 
    <имя_2> это <тип_2>,  
    ... , 
    <имя_N> это <тип_N>];

Примеры объявление аргументов для разных типов данных:

  • Простой:

    параметр это [код это строка, значение это дробное_число];
    
  • Набор полей.

    клиент это [имя это строка, 
    документ это [тип это строка, номер это целое_число]];
    
  • Встроенный набор полей.

    предложение это [название это строка, пакет_услуг это продукт]; 
    
  • Список.

    предложение это [название это строка, 
        номера_счетов это список(целое_число) ),
        продукт это список([название это строка, номер это целое_число])
    ]; 
    

Присвоение значений#

Присвоение значения набору может происходить несколькими способами:

  • Присвоение значения всему набору:

    <переменная> := х<имя_1>: <значение_1>,

    <имя_2>: <значение_2>,

    … ,

    <имя_N>:< значение_N>];

При этом порядок следования полей не важен. Если присвоение какому-нибудь полю отсутствует, считается, что его значение не задано. Все значения полей, которые содержались в наборе до этого, стираются.

  • Присвоение значения конкретному полю:

    <имя_набора>.<имя_поля>:= <значение>;

Обратиться к значению поля можно по его имени:

<**имя_набора>.<имя_поля>**
// Пример 12
параметр это [код это строка, значение это дробное_число];
параметр := [код: “комиссия”, значение: 11.6];
параметр.значение := параметр.значение > 10 => 10;

Списки наборов полей#

Чаще всего требуется работать со списком наборов. В этом случае объявление переменной будет выглядеть так:

<имя_списка> это список([<имя_1> это <тип_1>, 
    <имя_2> это <тип_2>,  
    ... , 
    <имя_N> это <тип_N>]);

// Пример 
список_фигур это список([название это строка,
число_углов это целое_число]);
список_ фигур := список(
    [название: “Круг”, число_углов: 0],
    [название: “Треугольник”, число_углов: 3], 
    [название: “Квадрат”, число_углов: 4],
    [название: “Ромб”, число_углов: 4]
);	
к списку список_фигур добавить 
    [название: “Трапеция”, число_углов: 4];
к списку список_четырёхугольников добавить 
    все список_фигур[фигура] {
	    фигура.число_углов = 4 => фигура.название;
} 

В данном примере создаётся список наборов. В первой строчке происходит описание полей, во второй списку присваивается список из 4 элементов, а в третьей – добавляется ещё один элемент. В следующем блоке перебираются все элементы из списка, и названия фигур с 4 углами добавляются к списку список_четырёхугольников. В результате, список_четырёхугольников = ("Квадрат", "Ромб", "Трапеция").

Предопределённые функции для работы со списками наборов полей#

  • сортировать_по_полю(список, поле, направление): возвращает список наборов полей, отсортированный по убыванию или по возрастанию значений в поле поле.

  • сортировать_список_структур(список): Функция возвращает новый список, который является отсортированной копией переданного в качестве аргумента списка. Работает только для списков предопределённых наборов полей (11.2), описанных в системе, а не созданных пользователем. (пример: выходная структура договоров функции получить_договоры_на_продукт_и_сервис)

  • максимальный_элемент(список, поле), минимальный_элемент(список, поле): возвращает элемент с максимальным / минимальным значением поля поле.

Предопределённые функции и наборы полей#

Предопределённые функции#

В тексте правил можно вызывать предопределённые функции:

  • Вспомогательные функции: для работы с датами, списками, и наборами полей.

  • Функции, использующие внешние источники данных.

Предопределённые наборы полей#

Предопределённые функции могут возвращать значения или списки значений предопределённого типа. Список предопределенных типов можно найти в редакторе создания/редактирования функций раскрыв список доступных типов данных для входных параметров и возвращаемого значения. Они представляют набор полей, которому сопоставлено определённое имя. Например, предопределённый тип вклад – набор следующих полей:

  • номер_счета: строка;

  • вид: целое число;

  • подвид: целое число;

  • валюта: строка;

  • дата_открытия: дата;

  • остаток: дробное число.

Тип данных вклад имеет возвращаемое значение функции получить_вклад_по_номеру_счета(ид_клиента, регион, номер_счета). Такой же тип будет и у переменной, которой присваивается результат выполнения функции.

// Пример 14
список_вкладов это список([счет это целое_число, вклад это вклад]);
список_счетов := список(123, 456);
к списку список_вкладов добавить все список_счетов[счет] {
    получить_вклад_по_номеру_счета(вход.клиент_ид, вход.ТБ, счет);
}

Если необходимо значение функции записать в поле набор, достаточно указать название предопределённого типа – в данном примере вклад.

Аналогично объявляется тип аргумента пользовательской функции.

Формирование комментариев#

Для пояснения хода расчёта есть возможность формировать комментарий. Комментарий оформляется с помощью конструкции:

( <текст комментария> )

Комментарий может вставляться после операции присвоения либо после результата вычисления внутри блока присвоения.

// Пример 15
переменная := 1; (* переменной присвоено значение 1*)
переменная := первый {
	переменная > 0 => 1; (* вам $возраст лет, поэтому мы уменьшили комиссию до $комиссия 1*)
	0; (* переменной присвоено значение 0 *)
}

При необходимости вставить значение переменной внутрь комментария, перед именем переменной ставится знак доллара – «$»:

( … $<Имя_переменной_1> … $<Имя_переменной_N> … )

Например:

Переменная := 1; ( переменной присвоено значение $Переменная )

В результате сформируется комментарий: переменной присвоено значение 1.

Результат правила#

Каждое правило должно заканчиваться списком выходных переменных, для которых были вычислены значения. В случае возврата одного значения, конструкция имеет вид:

результат выход.<параметр>;

Если необходимо указать несколько выходных параметров, то они объединяются в блок:

результат [выход.<параметр_1>,
    выход. < параметр _2>,
    ... ,
    выход. < параметр _N>];

Генерация ошибок#

Если во время выполнения правила может возникнуть ситуация, когда невозможно вернуть корректное значение или продолжить вычисления, необходимо генерировать ошибку. Для генерации ошибки необходимо проверить условие и указать сообщение о причине ошибки.

В общем виде конструкция выглядит так:

ошибка: <условие> => <сообщение_об_ошибке>;

Например:

ошибка: длина_списка(список_сервисов) = 0 => 
        "Не удалось получить список сервисов";

Блоковая генерация ошибок#

Если в зависимости от условий необходимо формировать несколько разных сообщений, их можно сгруппировать в блок:

ошибка: {
    <условие_1> => <сообщение_об_ошибке_1>;
    <условие_2> => <сообщение_об_ошибке_2>;
    ...
    <условие_N> => <сообщение_об_ошибке_N>;
}

// Пример 16
ошибка: { 
    вход.сумма_по_дб задан и вход.сумма_по_кр задан => 
    “Одновременно заданы суммы по дебету и кредиту”;
    вход.сумма_по_дб не задан и вход.сумма_по_кр не задан => 
    “Не задана ни сумма по дебету ни сумма по кредиту”;
}
Генерация ошибок внутри блока#

Сгенерировать ошибку можно внутри любого блока присвоения. Для этого достаточно добавить одну или несколько строчек вида:

ошибка: <условие> => <сообщение_об_ошибке>;

// Пример 17
переменная := первый список_условий[условие] { 
ошибка: условие.число_дней не задано => “Не задано число дней”;
ошибка: условие.число_дней = 0 => “Число дней равно нулю”;
условие.сумма / условие.число_дней;
}

Пользовательские функции#

Описание#

Для повторного использования одного и того же кода его можно оформлять в виде пользовательских функций и затем вызывать из текста правила. У каждой функции должно быть задано имя и список аргументов. Функция состоит из набора присвоений переменных и результата. Функции бывают двух видов: созданные в виде отдельных правил через интерфейс ДП (глобальные) и описанные внутри правила (встроенные).

В общем виде объявление функции выглядит следующим образом:

/*
<описание_функции>
*/
<имя_функции> это <тип_функции> (<аргумент_1> это <тип_аргумента_1>,
                                <аргумент_2> это <тип_аргумента_2>,
                                ... ,
                                <аргумент_N> это <тип_аргумента_3>) {
	<присвоение_1>; 
	<присвоение_2>;
	…
	<присвоение_N>;
	результат <переменная, константа, арифметическое выражение, вызов функции>;
} 

Примеры объявление аргументов для разных типов данных:

  • Простые:

    код это целое_число

  • Наборы полей.

    условие это [тип_условия это строка, параметр это дробное_число]

  • Встроенные наборы полей.

    *проверяемый_сервис это сервис

  • Списки (8). Тип элементов списка в свою очередь также может быть простым, набором полей или встроенным набором полей.

    номера_счетов это список(целое_число)

Глобальные функции#

Глобальная функция может быть вызвана из любого правила, а также из других глобальных функций. Такие функции описываются в отдельных правилах.

// Пример 18
/* Проверяет наличие договора у клиента
 по переданному сервису (опции) */
проверить_наличие_договора это логическое_выражение (
                               сервис это сервис, 
                               идентификатор_клиента это строка) {
    список_договоров := 
        получить_договора_на_продукт_и_сервис(
            сервис, 
            идентификатор_клиента
        );
    результат длина_списка(список_договоров) > 0;
}

Встроенные функции#

В коде правила можно объявлять собственные вспомогательные функции и использовать их внутри правила (в том числе и внутри других встроенных функций). При этом встроенные функции не доступны внутри других правил. В этом случае описание функции, типы функции и аргументов можно не указывать.

// Пример 19
/* Функция вычисления площади квадрата */
площадь_квадрата это (сторона_квадрата) {
	результат сторона_квадрата * сторона_квадрата;
}

площадь := площадь_квадрата(4);	// Вызов функции в тексте правила 

В данном примере вызывается встроенная функция площадь_квадрата, текст которой находится в том же правиле. В качестве аргумента передаётся число 4. После выполнения функции переменная площадь примет значение 4 * 4:

площадь = 16

Имя встроенной функции должно быть уникальным, то есть в правиле не должно быть двух функций с одним и тем же именем. Также имя встроенной функции не должно совпадать с именами глобальных функций за исключением тех случаев, когда требуется переопределить глобальную функцию.

Переопределение глобальных функций#

Иногда требуется изменить поведение глобальной функции для отдельно взятого правила. Для этого в тексте правила необходимо объявить соответствующую функцию с сохранением её имени, типа, количества, порядка, имён и типов всех аргументов. Теперь при выполнении этого правила всегда будет вызывать переопределённая функция, в то время как другие правила останутся без изменений.

Например, в правиле вызывается глобальная функция проверить_условия, которая в свою очередь вызывает другую глобальную функцию выполняется_условие. Предположим, возникла необходимость для данного конкретного правила изменить логику проверки условия. Но поскольку функция выполняется_условие явно из правила не вызывается, заменить вызов функции на другую возможности нет. Вместо этого можно определить встроенную функцию, которая переопределит глобальную. В этом случае код будет выглядеть следующим образом:

// Пример 20
/* Глобальная функция для проверки условий сервиса */
проверить_условия это логическое_выражение(сервис это сервис) {
    пройдено := ДА;
     
    пройдено := первый сервис.условия[условие] {
            не(выполняется_условие(условие)) => НЕТ;
    }
    результат пройдено;
}

/* Глобальная функция для проверки условия сервиса */
выполняется_условие это логическое_выражение(условие это условие) { 
    // Код для проверки условия
    . . .
}

// Текст правила
/* Переопределённая функция для проверки условия сервиса */
выполняется_условие это логическое_выражение(условие это условие) { 
    // Новый код для проверки условия
    . . .
}
. . . 
к списку проверенный_список_сервисов 
                добавить все список_сервисов[сервис] {
    проверить_условия(сервис) => сервис; 
                    // Вызов функции в тексте правила 
}