【Python插件入门】第6篇:操作服务插件
往期回顾:
【Python插件入门】第1篇:Python插件入门讲解
【Python插件入门】第2篇:基本开发过程介绍
【Python插件入门】第3篇:插件中如何进行数据操作
【Python插件入门】第4篇:单据表单插件
【Python插件入门】第5篇:单据列表插件
前面介绍了表单插件和列表插件,属于Web层插件,那么我们来接触一下APP层插件--操作服务插件,这个插件学习起来更容易理解和掌握 ,因为他的事件相对比较单一,运行机制也没那么复杂,但是他的功能其实很强大,应用场景也非常广泛,是学习插件开发非常必要掌握的一个插件类型。
一、单据操作
在讲解操作服务服务插件之前,我们必须要先来了解一下单据操作,如果说表单插件和列表插件是依赖于单据界面动作触发,那么操作服务插件就是依赖于单据操作触发。所以操作服务插件是绑定到单据操作上的,我们先来了解一下单据操作。系统内置160多种操作类型,支持单据菜单、列表菜单、单据体菜单调用实现功能,另外,还预置了空操作,更方便自定义操作的封装。单据操作可以理解成一个独立运行的任务,对于单据操作,主要还是对实体进行逻辑处理,有针对整个单据的,例如,保存、提交、审核、删除等,也有针对单据体分录实体的,例如新增分录、删除分录、批量填充等等,也有既能针对单据体分录也能针对整单处理的操作,例如状态转换等等。
我们在使用的时候需要根据不同应用场景使用不同的操作类型,单据操作具备下图4个特性:
单据操作列表查看、编辑操作截图:
二、操作服务插件简介
在C#插件开发时,操作服务插件的基类是AbstractOperationServicePlugIn,绑定在操作上,所以操作服务插件针对的对象是操作,用于对于某个操作进行扩展或者对操作执行过程干预,可以与校验器配合使用。操作服务插件可以对标准业务对象没有实现的功能控制进行补充,也可以已有操作和服务未支持的功能进行自定义,甚至可以定义自己的操作类型。开发过程快速,并且复用性很高,一个操作服务插件可以封装成通用的功能,多单据使用,甚至多操作使用,结合前面已经介绍的表单插件和列表插件,我们来看一下操作服务插件的运行机制(参考下图)。
从下图看来,我们把操作服务插件的执行过程理解成一个"流水线作业",会依次经历各个环节,这些环节就是操作服务插件中的事件,某一个事件不会独立触发,这是与表单插件列、表插件在运行过程上的显著区别,在二开操作服务插件中,我们可以在不同的环节进行干预,以此来影响整个执行过程达到我们想要的功能效果。
事务:操作服务插件中有部分环节是以事务机制运行,这也是我们常用来二开的部分,在事务运行过程中,会有时长下限制,默认是10min,所以我们不要在事务部分执行时间过长的任务,另外,事务中执行发生异常,支持回滚。
操作服务插件常用成员介绍:要注意!!!操作服务插件没有View成员了!
插件中同样有this.Context,这个在前面的篇章中介绍了,用法类似,不再过多介绍。
this.BusinessInfo.GetForm().Id;#执行当前操作的单据FormId
this.BusinessInfo.GetBillNoField().FieldName;#单据编号字段名
this.BusinessInfo.GetBillStatusField().FieldName;#单据状态字段名
this.BusinessInfo.GetBillTypeField().FieldName;#单据类型字段名
this.BusinessInfo.MainOrgField.FieldName;#主业务组织字段名
this.BusinessInfo.GetEntity("FBillHead").TableName;#单据头表名
this.BusinessInfo.GetEntity("实体标识").TableName;#实体主表名
this.BusinessInfo.GetEntity("实体标识").SplitTables;#实体所有拆分表
this.BusinessInfo.GetField("字段标识");#获取字段元素
#********************************************************************
this.FormOperation.Operation;#当前执行的操作代码
this.FormOperation.OperationName.GetString(2052);#当前执行的操作中文名称
this.FormOperation.OperationId;#当前执行的操作Id
this.FormOperation.PermissionItemId;#当前操作绑定的权限项Id
this.FormOperation.ServicePlugins;#当前执行操作下所有的操作服务插件集合
#********************************************************************
this.Option;#当前操作执行时的选项参数,代码调用单据操作时,也可以通过此变量传入自定义参数
this.Option.ContainsVariable("参数标识");#判断是否存在某个参数,返回true或false
DIC=this.Option.GetVariables();#获取所有的参数,字典类型,根据参数标识获取
a=DIC["参数标识"];#获取某个参数
IsIgnoreWarning=DIC["IgnoreWarning"];#是否忽略警告提示,返回true或false
selectRows=DIC["_BillOperationSelectedRows_"];#如果是单据体行操作可以从这里获取选中行
for r in selectRows:
billId=r["PrimaryKeyValue"];#单据ID
billId=r["EntryPrimaryKeyValue"];#单据体分录ID,若为整单操作,此值为null
#添加引用,也可以通过如下方法获取选中行:
clr.AddReference('Kingdee.BOS.Core')
from Kingdee.BOS.Core.DynamicForm import *
#获取选中行:
SelectedRows=OperationResultExt.GetBillOperationSelectedRows(this.Option);
使用操作服务插件的优势:
①操作服务插件在单据列表和单据维护界面都可以触发。当开发表单插件和操作服务插件都可以实现的功能时,例如,单据审核之后执行一项任务,这时优先选择操作服务插件。
②代码量低,操作服务插件可在权限控制或者一系列校验之后再执行,而80%+的校验逻辑可以通过BOS配置实现。
③外部调用方便,不管是WebAPI调用还是其他插件调用,都可以很方便的调用。
④插件复用性强,例如,A单据审核之后要完成某个任务,B单据也要此功能,可以快速的复用过去。
三、注册操作服务插件
操作服务插件是依赖于单据操作触发的,所以我们的操作服务插件是注册到单据操作下面的。
这里提供一个引用比较全的Python操作服务插件示例,在附件中下载示例代码,复制到BOS里面注册!
新建一个操作服务插件,以采购订单审核操作为例。
四、操作服务插件事件介绍
操作服务插件中的事件是有严格执行顺序的,看前面的运行时机图可以很清晰的看出事件执行顺序,这里不过多阐述。
操作服务插件常用事件用法介绍:下面我们来看看二开常用的事件
#选项设置,在操作服务初始化时执行
#可以对操作的执行参数进行设置,比如是否是否启动事务,是否支持批量处理等
def OnPrepareOperationServiceOption(e):
e.SupportTransaction=True;#是否启动事务,默认true
e.SurportBatchTransaction=True;#是否支持批量处理,默认true
#字段预加载事件,这是一个非常必要使用的事件
#出于性能考虑,服务插件并不会加载单据完整的数据包,只有默认加载单据编号、单据ID等一些关键字段
#在插件中需要读取的其他字段信息,需要在此事件中先加载,方法也很简单。
#如果在后续事件中取单据字段时,报错提示字段标识不存在时,可以看是否在这里进行预加载
def OnPreparePropertys(e):
e.FieldKeys.Add("FBillTypeID");
e.FieldKeys.Add("字段标识");#这里使用的是字段标识,后面从数据包取值用的是绑定实体属性
#校验器干预,非必要,需要使用的时候可以实现这个事件
#可以这里添加自定义的校验器,或者对已有校验器进行取消干预
#如果校验器执行不通过,将不会继续往后执行,一般配置不能实现的自定义校验逻辑在此实现
#只有在APP层执行的操作才会默认执行校验器,例如,保存、提交、审核、删除等。
#其他在Web层执行界面交互的操作默认不会执行,例如下推、选单、复制等,可通过表单插件强行执行绑定在操作上的校验器。
#校验器需要单独定义一个类,将类的对象加载到此事件中使用,校验器类定义参考
#Python校验器开发案例
def OnAddValidators(e):
e.Validators;#已有校验器
#执行操作事务前事件,通知插件对要处理的数据进行排序等预处理(事务外触发)
#此事件在操作校验之后、操作实现之前执行
#此事件在操作事务之前,即此事件中的数据库处理,不受操作的事务保护
def BeforeExecuteOperationTransaction(e):
e.Cancel = False;#可以再这里做一些强制的校验,可取消操作继续执行
if(e.Cancel==True):
e.CancelMessage=("{0}被取消了!").format(this.FormOperation.OperationName.GetString(2052));
#操作事务前事件(事务内触发),支持回滚
#此事件在操作校验之后,在操作事务开始之后,在操作执行之前
#此事件中的数据库处理,受操作的事务保护,支持回滚
#通常此事件,可以用来做数据准备,在操作之前,获取操作执行前的数据,
#如果是保存操作,可以对单据数据包做修改,但如果发生异常,单据数据包不会回滚,可以在RollbackData事件中处理
#例如,审核操作的话,这里取单据状态字段还是审核中,删除操作的话,这里是最后一次能从数据库取到单据数据的事件
def BeginOperationTransaction(e):
#e.CancelOperation=True;#取消操作
#e.CancelFormService = True;#取消表单服务
#读取执行本次操作的单据实体数据包,这个数据包里面能取到的字段取决于前面OnPreparePropertys预加载的字段
#billObj可以理解成前面篇章讲解的单据头实体数据包,只不过字段不全而已,实体结构还是单据数据包的结构
#为什么要循环呢?
#因为操作是可以批量处理的,所以取到的是一个单据列表的形式,如果只有一张单据触发,e.DataEntitys里面就只有一个单据数据包
for billObj in e.DataEntitys:
billId=billObj["Id"];#单据ID
billNo=billObj["BillNo"];#单据编号
#操作事物后事件(事务内触发)
#此事件在操作执行之后,操作的内部逻辑已经执行完毕,数据库的数据也已经更新成操作后的数据
#所以删除操作,在这里无法通过SQL从数据库查询单据数据
#此事件数据库处理,受操作的事务保护,支持回滚
#通常我们要保证单据操作正常执行完成之后再处理一些任务时,并且需要事务保护时,可以在此事件中完成。
#例如,最常见的,执行一个SQL更新,去更新相关单据的数据
def EndOperationTransaction(e):
#这里获取单据数据包方式和前面一样
for billObj in e.DataEntitys:
billId=billObj["Id"];#单据ID
billNo=billObj["BillNo"];#单据编号
#内部事务执行失败后,调用回滚数据事件(事务外触发)
#此事件只有在操作执行过程中发生异常时才触发
#操作异常时,回滚内存中的数据,在此处理
def RollbackData(e):
for billObj in e.DataEntitys:
billId=billObj["Id"];#单据ID
#执行操作事务后事件,通知插件对象执行其它事务无关的业务逻辑(事务外触发)
#此事件在操作执行后,操作的内部逻辑已经执行完毕,并且在事务提交之后
#事件中的数据库处理,不受操作的事务保护
#通常此事件,也可以做同步数据,但是此同步数据的成功与否,不需影响操作
#尽量不要在此事件中抛出异常。因为前面事务已经提交,后面抛出异常容易导致后续业务不正常。例如单据启用了工作流,提交成功后抛出异常,会导致工作流后续业务处理不正确。
#正确的做法是把异常信息作为提示信息放到OperationResult中,另外,如果是数据不合法校验,应该在事务前或事务中抛出异常。
def AfterExecuteOperationTransaction(e):
#这里获取单据数据包方式和前面一样
for billObj in e.DataEntitys:
billId=billObj["Id"];#单据ID
billNo=billObj["BillNo"];#单据编号
#追加自定义提示信息,参考如下
result=OperateResult();
result.SuccessStatus=True;
#result.PKValue = "单据ID";
#result.Number = "单据编号";
result.Message = "提示信息";
this.OperationResult.OperateResult.Add(result);
==========================本篇正文结束=====================================
操作服务插件示例代码已经上传附件,老规矩,大家按需下载!
服务插件本身事件并不复杂,只是针对具体要实现的功能还需要多掌握一些具体功能实现方法,例如,最常见的单据完成某个操作之后执行一个SQL更新、自动下推一个单据、自动生成某个单据、调用第三方接口...等等。这些要通过实际案例来学习,大家也可以在社区多搜一搜相关的案例,后面的讲解文章也会尽量分享一些常用的示例代码。
感谢大家持续关注,点赞、评论、收藏,您的点赞、评论就是我前进的动力。
往期Python服务插件经典应用案例推荐:
请问用什么方式可以跳出提示信息用于调试
周老师,这个SelectedRows=OperationResultExt.GetBillOperationSelectedRows(this.Option)报错,报错内容 'ListSelectedRow' object is unsubscriptable
周老师为什么反审核就不行
周老师,请教下,BusinessDataWriter(this.Context).Save(e.DataEntitys)这个方法在服务插件中要如何写呢?
《Python插件入门讲解 配套视频课程》可私信作者留言或者加Q群(231541277)联系群主获取
周老师,我的需求是付款单审核时给泛微OA发送单据的部分信息。
老师好,我们这边cloud是6.1版本的。在用python写销售出库单审核服务插件时,先用OnPreparePropertys事件加载了表头的一个自定义字段F_PAEZ_Text,但是在AfterExecuteOperationTransaction事件中还是获取不到自定义字段的值,想请问是python服务插件中只支持一个事件的执行吗?还是因为cloud版本的问题
很好的文章,学习一下
在单据的审核操作里面挂了这个插件,测试了一下,单据还是会审核成功。
老师,单据批量保存,跟基础资料审核自动分配链接有问题。
【Python插件入门】第6篇:操作服务插件
本文2024-09-16 17:17:06发表“云星空知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-k3cloud-14716.html