为优化顾问创建规则

本文介绍如何为优化顾问创建新规则。 例如,可创建一个新规则来识别哪些询价单 (RFQ) 案例的标题为空。 可通过案例的标题轻松识别和搜索案例。 此示例非常简单,介绍了通过优化规则可达到哪些目的。

规则是对应用程序数据的检查。 如果满足规则评估的条件,将产生优化流程或改进数据的机会。 可抓住这些机会,也可以选择衡量措施的影响。

若要为优化顾问创建新规则,请添加一个新类,该类用于扩展 SelfHealingRule 抽象类和实施 IDiagnosticsRule 接口,并由 DiagnosticRule 属性装饰。 该类还必须有一个使用 DiagnosticsRuleSubscription 属性装饰的方法。 按照惯例,这通过 opportunityTitle 方法执行,后者将在下文探讨。 这个新类将添加到依赖于 SelfHealingRules 方法的一个自定义模型。 在以下示例中,实施的规则称为 RFQTitleSelfHealingRule

[DiagnosticsRule] 
public final class RFQTitleSelfHealingRule extends SelfHealingRule implements IDiagnosticsRule 
{ 
… 
} 

SelfHealingRule 抽象类具有必须在继承类中实施的抽象方法。 核心是 evaluate 方法,该方法返回由规则标识的商机的列表。 商机可以按法人,也可以适用于整个系统。

protected List evaluate() 
{ 
    List results = new List(Types::Record); 
    
    DataArea dataArea; 

    while select id from dataArea 
        where !dataArea.isVirtual 
    { 
        changecompany(dataArea.id) 
        { 
            container result = this.findRFQCasesWithEmptyTitle(); 

            if (conLen(result) > 0) 
            { 
                SelfHealingOpportunity opportunity = this.getOpportunityForCompany(dataArea.Id); 
                opportunity.EvaluationState = SelfHealingEvaluationState::Evaluated; 
                opportunity.Data = result; 
                opportunity.OpportunityDate = DateTimeUtil::utcNow(); 
                
                results.addEnd(opportunity); 
            } 
        } 
    } 
    
    return results; 
} 

上面显示的方法在公司间循环,并选择 findRFQCasesWithEmptyTitle 方法中带空标题的 RFQ 案例。 如果找到了至少一个这样的类,将使用 getOpportunityForCompany 方法创建公司特定的商机。 请注意,SelfHealingOpportunity 表中字段数据的类型为容器,因此可以包含与特定于该规则的逻辑有关的任何数据。 使用当前时间戳设置 OpportunityDate 将注册商机的最近评估时间。

商机也可以跨公司。 在此情况下,不必执行公司间循环,并且必须使用 getOpportunityAcrossCompanies 方法创建商机。

以下代码显示 findRFQCasesWithEmptyTitle 方法,用于返回具有空标题的 RFQ 案例的 ID。

private container findRFQCasesWithEmptyTitle() 
{ 
    container result; 

    PurchRFQCaseTable rfqCase; 
    while select RFQCaseId from rfqCase 
        where rfqCase.Name == '' 
    { 
        result += rfqCase.RFQCaseId; 
    } 
    
    return result; 
} 

还必须实施的两个方法是 opportunityTitleopportunityDetails。 前者返回商机的短标题,后者返回商机的详细说明(其中也可以包含数据)。

opportunityTitle 返回的标题在优化顾问工作区中的优化商机列下显示。 还显示为侧窗格的标题,用于显示有关商机的详细信息。 按照惯例,此方法使用 DiagnosticRuleSubscription 属性装饰,后者采用以下自变量:

  • 诊断区域 – 类型为 DiagnosticArea 的枚举,用于描述规则所属的应用程序范围,如 DiagnosticArea::SCM

  • 规则名称 – 包含规则名称的字符串。 这将在诊断验证规则窗体中的规则名称列下显示 (DiagnosticsValidationRuleMaintain)。

  • 运行频率 – 类型为 DiagnosticRunFrequency 的枚举,用于描述应运行规则的频率,如 DiagnosticRunFrequency::Daily

  • 规则说明 – 包含规则更详细的说明的字符串。 这将在诊断验证规则窗体中的规则说明列下显示 (DiagnosticsValidationRuleMaintain)。

注释

规则需要 DiagnosticRuleSubscription 属性才能工作。 该属性通常在 opportunityTitle 中使用,但是可以装饰类的任何方法。

下面是一个示例实施。 使用原始字符串是为了简单方便,但是正确的实施需要标签。

[DiagnosticsRuleSubscription(DiagnosticsArea::SCM, 
                             'Assign titles to Request for Quotation cases', 
                             DiagnosticsRunFrequency::Daily,  
                             'This rule detects Requests for Quotation with empty titles.')] 
public str opportunityTitle() 
{ 
    return 'Assign titles to Request for Quotation cases'; 
} 

opportunityDetails 返回的说明在侧窗格中显示,用于显示有关商机的详细信息。 它采用 SelfHealingOpportunity 自变量,后者是可用于提供有关商机的更多详细信息的数据字段。 在该示例中,方法返回具有空标题的 RFQ 案例的 ID。

public str opportunityDetails(SelfHealingOpportunity _opportunity) 
{ 
    str details = ''; 
    container opportunityData = _opportunity.Data; 
    int affectedRFQCasesCount = conLen(opportunityData); 

    if (affectedRFQCasesCount != 0) 
    { 
        details = 'The following Request for Quotation cases have an empty title:\n'; 
        for (int i = 1; i <= affectedRFQCasesCount ; i++) 
        { 
            PurchRFQCaseId rfqCaseId = conPeek(opportunityData, i); 
            details += rfqCaseId + '\n'; 
        } 
    } 

    return details; 
}

其余两个要实施的抽象方法是 provideHealingActionsecurityMenuItem

如果提供了治愈操作,provideHealingAction 将返回 true。否则返回 false。 如果返回 true,则必须实施方法 performAction,否则将引发错误。 performAction 方法采用 SelfHealingOpportunity 自变量,可在其中将数据用于操作。 在该示例中,操作将打开 PurchRFQCaseTableListPage 以执行手动更正。

public boolean providesHealingAction() 
{ 
    return true; 
} 

protected void performAction(SelfHealingOpportunity _opportunity) 
{ 
    new MenuFunction(menuItemDisplayStr(PurchRFQCaseTableListPage), MenuItemType::Display).run(); 
} 

规则可能可以采用商机数据执行自动操作,具体取决于规则的具体内容。 在该示例中,系统可以为 RFQ 案例自动生成标题。

securityMenuItem 返回操作的名称,以便只有可访问操作菜单项的用户可查看规则。 安全设置可能要求只有授权用户可访问特定规则和商机。 在该示例中,只有可访问 PurchRFQCaseTitleAction 的用户可查看商机。 请注意,此操作菜单项是为本示例创建,并作为 PurchRFQCaseTableMaintain 安全权限的进入点添加的。

注释

此菜单项必须是操作菜单项,才能确保安全。 显示菜单项之类其他菜单项类型无法正确工作。

public MenuName securityMenuItem() 
{ 
    return menuItemActionStr(PurchRFQCaseTitleAction); 
}

编译规则后,请执行以下作业,以便使其在用户界面 (UI) 中显示。

class ScanNewRulesJob 
{         
    public static void main(Args _args) 
    {         
        SysExtensionCache::clearAllScopes(); 
        var controller = new DiagnosticsRuleController(); 
        controller.runOperation(); 
    } 
} 

此规则将在诊断验证规则窗体(可从系统管理>定期任务>维护诊断验证规则访问)中显示。 若要评估该规则,请转到系统管理>定期任务>计划诊断验证规则,然后选择规则的频率,如每日。 单击确定。 转到系统管理>优化顾问以查看新商机。

以下示例是带规则摘要的代码段,其中包括所有必需的方法和属性。 可帮助您熟悉如何撰写新规则。 此示例中使用的标签和操作菜单项仅用于演示目的。

[DiagnosticsRuleAttribute]
public final class SkeletonSelfHealingRule extends SelfHealingRule implements IDiagnosticsRule
{
    [DiagnosticsRuleSubscription(DiagnosticsArea::SCM,
                                 "@SkeletonRuleLabels:SkeletonRuleTitle", // Label with the title of the rule
                                 DiagnosticsRunFrequency::Monthly,
                                 "@SkeletonRuleLabels:SkeletonRuleDescription")] // Label with a description of the rule
    public str opportunityTitle()
    {
        // Return a label with the title of the opportunity
        return "@SkeletonRuleLabels:SkeletonOpportunityTitle";
    }

    public str opportunityDetails(SelfHealingOpportunity _opportunity)
    {
        str details = "";

        // Use _opportunity.data to provide details on the opportunity

        return details;
    }

    protected List evaluate()
    {
        List results = new List(Types::Record);

        // Write here the core logic of the rule

        // When creating an opportunity, use:
        //     * this.getOpportunityForCompany() for company specific opportunities
        //     * this.getOpportunityAcrossCompanies() for cross-company opportunities

        return results;
    }

    public boolean providesHealingAction()
    {
        return true;
    }

    protected void performAction(SelfHealingOpportunity _opportunity)
    {
        // Place here the code that performs the healing action

        // To open a form, use the following:
        // new MenuFunction(menuItemDisplayStr(SkeletonRuleDisplayMenuItem), MenuItemType::Display).run();
    }

    public MenuName securityMenuItem()
    {
        return menuItemActionStr(SkeletonRuleActionMenuItem);
    }

}

有关详细信息,请观看以下 YouTube 视频短片:Dynamics 365 Finance 中的优化顾问