[ELMA3] Создания функции генератора для EQL-запроса

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

Глобальный модуль проверки наличия исключений по договорам лизинга

using System;
using System.Collections.Generic;
using System.Linq;
using EleWise.ELMA.API;
using System.Text;
using EleWise.ELMA.Model.Common;
using EleWise.ELMA.Model.Entities.EntityReferences;
using EleWise.ELMA.Model.Services;
using EleWise.ELMA.Model.Entities;
using EleWise.ELMA.Model.Managers;
using EleWise.ELMA.Model.Types.Settings;
using EleWise.ELMA.Model.Metadata;
using EleWise.ELMA.Services;
using EleWise.ELMA.Exceptions;
using EleWise.ELMA.ConfigurationModel;
using EleWise.ELMA.Templates;

namespace EntityExtension
{
    public static class Extension
    {
        /// <summary>
        /// EQL проверка наличия исключений
        /// </summary>
        /// <param name="elmaEntity - любая сущность для которой необходимо произвести поиск. В данном случае договора лизинга">this IEntity long</param>
        /// <param name="eqlQuery - строка EQL-запроса с пользовательскими функциями">string</param>
        /// <returns>bool</returns>
        public static bool CheckEqlRules(this IEntity<long> elmaEntity, string eqlQuery)
        {
            if (string.IsNullOrWhiteSpace(eqlQuery))
            {
                return true;
            }
			//Форматируем строку поиска с учетом пользовательских функций GetAdvancePermissionLeaseContract и GetLimitBalanceProvider
            TextTemplateGenerator textTemplateGenerator = new TextTemplateGenerator(eqlQuery, null, TemplateRenderMode.Default);
            eqlQuery = textTemplateGenerator.Generate(elmaEntity);
			
            var type = elmaEntity.GetType();
            var manager = ModelHelper.GetEntityManager(type);
            var filterType = ModelHelper.GetEntityFilterType(type);
            var filter = (IEntityFilter)InterfaceActivator.Create(filterType);
            filter.Query = string.Format("({0}) AND Id={1}", eqlQuery, elmaEntity.Id);
            var count = manager.Count(filter);
            if (count > 1)
            {
                throw new EQLException(eqlQuery, string.Format("Ошибка в запросе ({0}) для {1}, результат вернул {2} значений, а должен 1 или 0", eqlQuery, elmaEntity, count));
            }
            return count > 0;
        }
    }
}

Глобальный модуль создания функции генератора для EQL-запроса

namespace EntityExtension
{
    [Component]
    public class EntityTemplateGenerate : ITemplateGeneratorFunctionsContainer
    {
        /// <summary>
        /// Функция EQL для проверки Дата поставки - (Последняя дата аванса +1) меньше количества дней на Авансирование в Договоре лизинга
        /// </summary>
        /// <param name="context">Дата поставки;Блок Авансы</param>
        /// <returns>AdvancePermission</returns>
        public static FormatedValue GetAdvancePermissionLeaseContract(FunctionEvaluationContext context)
        {
            if (context == null || context.Parameters == null || context.Parameters.Count <= 0) return new FormatedValue(AdvancePermission.NotAllowed);
            var ctxParamDeliveryDate = context.Parameters[0];
            if (ctxParamDeliveryDate == null) return new FormatedValue(AdvancePermission.NotAllowed);
            var DeliveryDate = ctxParamDeliveryDate.Value as DateTime?;
            var ctxParamPrepayments = context.Parameters[1];
            if (ctxParamPrepayments == null) return new FormatedValue(AdvancePermission.NotAllowed);
            var Prepayments = ctxParamPrepayments.Value as ISet<LeaseContract_Prepayments>;
            if (DeliveryDate == null || Prepayments == null || !Prepayments.Any())
            {
                return new FormatedValue(AdvancePermission.NotAllowed);
            }
            else
            {
                long countDay = 0;
                AdvancePermission? advpre = null;
                var startDate = Prepayments.OrderBy(pay => pay.PrepaymentDate).LastOrDefault().PrepaymentDate.Value.AddDays(+1);
                var endDate = DeliveryDate;
                if (startDate == null || endDate == null)
                {
                    return new FormatedValue(AdvancePermission.NotAllowed);
                }
                countDay = (long)Math.Abs((startDate - endDate).Value.TotalDays);
                if (countDay > 1 && countDay <= 15)
                {
                    advpre = AdvancePermission.AllowedTo15Days;
                }
                if (countDay > 15 && countDay <= 30)
                {
                    advpre = AdvancePermission.AllowedTo30Days;
                }
                if (countDay > 30 && countDay <= 60)
                {
                    advpre = AdvancePermission.AllowedTo60Days;
                }
                if (countDay > 60 && countDay <= 90)
                {
                    advpre = AdvancePermission.AllowedTo90Days;
                }
                if (advpre == null)
                {
                    return new FormatedValue(AdvancePermission.NotAllowed);
                }
                return new FormatedValue(advpre.ToString());
            }
        }
        /// <summary>
        /// Возвращает сумму на поставщика по Проекту указанному в Предмете лизинга
        /// </summary>
        /// <param name="context">Предмет лизинга</param>
        /// <returns>double</returns>
        public static FormatedValue GetLimitBalanceProvider(FunctionEvaluationContext context)
        {
        	if (context == null || context.Parameters == null || context.Parameters.Count <= 0) return new FormatedValue(0);
            var ctxParam = context.Parameters[0];
            if (ctxParam == null) return new FormatedValue(0);
			var leaseObject = ctxParam.Value as LeaseObject;
            if (leaseObject == null || leaseObject.Provider == null || leaseObject.Project == null || leaseObject.Project.LeasingItems == null || !leaseObject.Project.LeasingItems.Any())
            {
                return new FormatedValue(0);
            }
            else
            {
                return new FormatedValue(leaseObject.Project.LeasingItems.Where(c => c.SubjectOfLeasing.Provider != null && c.SubjectOfLeasing.Provider == leaseObject.Provider).ToList().Select(c => c.MarketPrice.Value).Sum());
            }
        }
	}
}

FormatedValue – это форматируемое значение Вашей функции.

FunctionEvaluationContext – это параметры для вычисления Вашей функции. Количество параметров может быть неограниченно. В функции параметры отделяются друг от друга точкой запятой, например,

{ GetAdvancePermissionLeaseContract({$DeliveryDate};{$Prepayments})} , где {$ DeliveryDate } и {$ Prepayments } являются параметрами.

Для того чтобы получить значение нужного параметра, необходимо ссылаться на номер его позиции. Если обратиться к вышеописанному примеру, то параметр {$ DeliveryDate } имеет порядковый номер, равный 0, {$ Prepayments } является номером 1 и так далее.

Чтобы записать значение параметра необходимо привести его к правильному формату, в этом поможет функция context.GenerationContext.FormatProvider.FormatValue(context.Parameters[Номер_Позиции_Параметра]). Необходимость приведения к правильному формату обусловлена тем, что в значении переменной могут находиться лишние символы, например, при передаче даты окончания пользовательской задачи без времени (дд.мм.гггг) без использования приведения к верному формату в оповещении будет дата начала, равная дд.мм.гггг 23:59:50, а если использовать приведение, то дата запишется в верном формате.

Пример вызова метода проверки исключений по договору лизинга в процессе

/// <summary>
/// Проверка исключений по договору лизинга
/// </summary>
/// <param name="context"></param>
public virtual void CheckExceptions(Context context)
{
	var eqlQuery = "Advancement = '{GetAdvancePermissionLeaseContract({$DeliveryDate};{$Prepayments})}' AND LimitBalance >= {GetLimitBalanceProvider({$LeaseObject})}";
	bool isExceptions = context.LeaseContract.CheckEqlRules(eqlQuery);
}

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

Ссылки на элементы API: https://www.elma-bpm.ru/KB/help_api_developer/version/3.8.0/ru_RU/index.html?topic=html/T_EleWise_ELMA_Templates_ITemplateGeneratorFunctionsContainer.htm&topic=html/T_EleWise_ELMA_Templates_ITemplateGeneratorFunctionsContainer.htm

Ссылки на базу знаний: https://www.elma-bpm.ru/KB/article-6458.html