[AX2012 R3]关于Alerts

AX2012提供两种类型的Alert,Change-based alert和Due-date-based alert,前者用于在对新建记录、删除记录、记录的某个指定字段被改变的时候发出提醒,后者则是用于监视记录日期类型的某个字段,在超期指定的日期后发出提醒。在记录的编辑form或者list page上右键菜单中我们能找到“Create alert rule”菜单项,通过它打开alert规则创建窗口。规则创建窗口里我们可以选择alert要发给谁,只能是一个特定的用户,不能是一个用户组,如果通知需要发送给多个用户只能创建多个alert规则了。另外在我们可以勾选“Send email”将通知通过邮件的方式发送给用户,要使用这个功能必须设置好Alert邮件模板,首先在Organization administration>Setup>Email-template创建一个Alert的邮件模板(非系统邮件模板),邮件模板中可以添加占位符变量,在生成具体的邮件时指向真实的数据。Alert邮件模板的例子可以在https://technet.microsoft.com/en-us/library/aa834376.aspx找到。创建好邮件模板后,我们还需要到Organization administration>Setup>Alerts>Alert parameter选择这个邮件模板为我们的Alert邮件模板。所有的通知规则我们可以在Organization administration>Setup>Alerts>Alert rules找到,在这里可以管理创建好的通知规则,使能或者删除规则等。通知规则列表有一列叫做Organiztion-wide,这个不是能手工修改的,如果我们对公司无关的表(比如workers、products)添加规则时会自动打勾。

通知规则创建好后不等于在记录改变时你就能收到Alert消息,我们还需要配置两个Batch job,由它们来处理发出通知消息。一个是 System administration>Periodic>Alerts> Change based alerts,由它处理change-based通知。另一个是 System administration>Periodic>Alerts>Due date alerts,由它出来due-date-based通知。

先来看Change-based的批处理任务,它运行的类是EventJobCUD,它只是判断EventCUD表中是否要处理的Alert事件,如果有则交由EventJobCUDTask处理。EventJobCUDTask读取EventCUD需要处理的记录的用户ID,通过runas切换到该用户运行EventJobCUDTask.runCudEventsForUser(),runCudEventsForUser依次读取该用户相关的EventCUD记录,然后交由EventProcessorCUD处理。EventProcessorCUD针对事件的类型(更新、删除、创建、主键更改)做不同的处理,比如更新事件,它从EventCUD的Data字段(Container类型)剥离出被监视记录字段的字段ID、新老值,创建EventTypeCUD对象,保存更新字段的新老值,随后根据通知规则中的通知类型创建相应的EventAction,最后生成相应的事件通知保存在EventInbox表中。如果指定了使用邮件通知,会创建EventActionEmail对象,它负责生成占位符对象数据映射,交由SysEmailTable或者SysEmailSystemTable生成实际的邮件放到SysOutgoingEmailTable等候发送。处理成功后相关记录在EventCUD中被删除。这里只是对处理过程做了简单的陈述,实际的过程要复杂得多,需要处理表继承已经通知规则中指定的记录过滤查询等。

可以看出change-based通知的核心是处理EventCUD的记录,那么这个表中的数据是从哪里来的呢?微软的解释是使用database log来检测记录更改,alert模块在xApplication类的EventInsert、EventUpdate、EventDelete注册自己的回调函数,如果通知规则中涉及到的表发生变化将更改记录到EventCUD表。可以看到的是如果我们添加一个通知规则,在表DatabaseLog中会相应增加一条记录,记录对哪个表做EventXXX类型的log。System administration > Setup > Database > Database log setup看不到这些记录,这个form不显示和EventXXX类型的DatabaseLog。在通知规则监视的表记录修改后,其修改记录在System administration > Inquries > Database > Database log也是看不到的,但是在EventCUD中会增加相关记录。xApplication是系统类,看不到具体是如何实现的。

再来看Due-Date-based的批处理任务,它运行的是EventJobDueDate,它读取EventRule表中Due-date类型的通知规则的用户ID,也是runas切换到该用户执行runDueDateEventsForUser(),在runDueDateEventsForUser中调用EventProcessorDueDate.processRule()来处理当前的EventRule,从EventRule中读取记录过滤Query,读取Query中的记录,调用EventProcessorDueDate.processRecord()处理记录,如果日期字段超期,也是根据通知规则中的通知类型创建相应的EventAction,后面的过程和change-based类似了。如果通知规则已经触发并成功处理,该通知规则会被添加到表EventRuleIgnore中,后续不再触发(在EventTypeDue.canExecute()判断)。

在了解了Alert如何工作的后,我们可以添加自定义的事件类型(EventType)和自定义的事件处理动作(EventAction),可以参见https://msdn.microsoft.com/en-us/library/aa673670.aspx

通知消息是保存在表EventInbox中的,在user options的Notification一节我们可以设置接受通知的时间间隔,客户端按照时间间隔读取通知消息,如果通知设置了需要显示弹窗,AX client会弹出弹窗。可以想见的是直接操作EventInbox来创建用户通知,网上有很多例子,这里截取一个(http://daxldsoft.blogspot.com/2012/10/create-custom-alert-for-ax-with-go-to.html):

 public static void CreateAlert(str message,  
               str subject,  
               UserId userId = curUserId(),  
               NoYes showPopup = NoYes::Yes,  
               NoYes sendEmail = NoYes::No,  
               Common record = null,  
               str dataSourcename = '',  
               MenuFunction menuFunction = null)  
 {  
   EventInbox inbox;  
   DictTable table;  
   EventContextInformation eci;  
   EventInboxData inboxData;  
   Args args = new Args();  
   List list;  
   EventInboxId inboxId = EventInbox::nextEventId();  
   FormRun formRun;  
   WorkflowRecordCaptionGenerator recordCaptionGenerator;  
   UserInfo userInfo;  
   inboxId = EventInbox::nextEventId();  
   inbox.initValue();  
   inbox.ShowPopup      = showPopup;  
   inbox.Subject       = subject;  
   inbox.Message       = message;  
   inbox.SendEmail      = sendEmail;  
   inbox.EmailRecipient    = SysUserInfo::find().Email;  
   inbox.UserId        = userId;  
   inbox.InboxId       = inboxId;  
   inbox.AlertCreatedDateTime = DateTimeUtil::getSystemDateTime();  
   if (record)  
   {  
     table = new DictTable(record.TableId);  
     eci = new EventContextInformation();  
     if (!menuFunction)  
     {  
       menuFunction = new MenuFunction(table.formRef(),MenuItemType::Display);  
       if (!menuFunction)  
         throw error(strFmt("@SYS104114",table.formRef()));  
     }  
     //Build the data to drill down to from the notification  
     args.menuItemName(menuFunction.name());  
     args.menuItemType(MenuItemType::Display);  
     args.name(menuFunction.object());  
     eci.parmPackedArgs(args);  
     eci.parmAlertBuffer(record);  
     eci.parmAlertFormDsName(dataSourceName);  
     //eci.parmDontUseFormRunFromMenuItem(true);  
     inboxData.InboxId = inboxId;  
     inboxData.DataType = EventInboxDataType::Context;  
     inboxData.Data = eci.pack();  
     inboxData.insert();  
     inbox.AlertTableId = table.id();  
     inbox.ParentTableId = table.id();  
     recordCaptionGenerator = WorkflowRecordCaptionGenerator::construct(record);  
     inbox.AlertedFor = recordCaptionGenerator.caption();  
     list = SysDictTable::getUniqueIndexFields(table.id());  
     if (list)  
     {  
       inbox.keyFieldList(list.pack());  
       inbox.keyFieldData(SysDictTable::mapFieldIds2Values(list,record).pack());  
     }  
     inbox.CompanyId = record.company();  
   }  
   inbox.insert();  
 } 

使用上面的函数来创建Alert:

static void Job155(Args _args)  
 {  
   InventTable inventTable;  
   select firstOnly inventTable;  
   DEVUtils::CreateAlert("message", "subject", curUserId(), true, false, inventTable, "InventTable", new MenuFunction(menuitemDisplayStr(EcoResProductDetailsExtended), MenuItemType::Display));  
 }  

上面的CreateAlert函数有个sendEmail参数,对应EventInbox的字段SendEmail,把这个参数设置为true,我们也不会收到邮件,因为我们只是标记了这个字段,我没有找到系统中有比如batch job来将EventInbox通过邮件发送出去。

 

posted @ 2015-03-19 14:04  断水流  阅读(1441)  评论(1编辑  收藏  举报