【Python插件入门】第9篇:单据转换插件原创
金蝶云社区-CQ周玉立
CQ周玉立
155人赞赏了该文章 11816次浏览 未经作者许可,禁止转载编辑于2022年09月06日 17:00:21
封面

往期回顾:

【Python插件入门】第1篇:Python插件入门讲解

【Python插件入门】第2篇:基本开发过程介绍

【Python插件入门】第3篇:插件中如何进行数据操作

【Python插件入门】第4篇:单据表单插件

【Python插件入门】第5篇:单据列表插件

【Python插件入门】第6篇:操作服务插件

【Python插件入门】第7篇:简单账表服务插件

【Python插件入门】第8篇:账表表单插件

      

        前面的篇章讲解了各种类型的插件,这些插件都是在单个业务对象中触发使用,我们同时也了解了对单据数据的操作方法,是不是对这个系统的功能背后的实现逻辑有了更深的理解呢,当不同的业务对象"串联起来"就构成了系统中各种丰富的功能,形成了系统的业务流程,在"串联"各种单据的过程中,必不可少的就是"单据转换",他是业务流程连接过程中的一个重要枢纽,对于单据转换的过程 ,系统也支持了通过插件对这个过程进行干预,今天我们就来看一看如何用Python开发单据转换插件。

一、单据转换概述

  • 单据转换,是指把上游所选单据,按照转换规则,自动生成下游单据数据包的过程;不对下游单据进行保存、提交、审核等处理。

  • 单据转换插件,能够介入到单据转换的各个关键时刻,对转换行为进行控制,从而调整所生成的下游数据包;

  • 单据转换按照发起方不同,可分为下推、选单;

    下推是指在上游单据列表,把所选单据,生成下游单据数据包,并展示出来;

    选单是指在下游单据新增界面,弹出上游单据列表,选择源单返回,然后根据转换规则把源单数据填写到下游单据新增界面上;

  • 选单实际上分为两个独立的过程,一个是选单前过程,根据转换规则,生成源单数据筛选条件,传给源单列表。因此源单列表上显示的,都是允许下推的数据;另外一个就是选单过程,把用户选择返回的源单数据,迁移到目标单据上;

  • 下推与选单,采用相同单据转换规则,进行数据迁移;也采用相同的单据转换插件,但触发的事件略有差异,编写单据转换插件时,需要兼顾这些差异; 

二、单据转换规则简介

以下知识结构截图摘取自@eris 老师分享的学习资料,在此感谢老师的用心制作!

也强烈推荐大家看看老师的讲解文章和课程!金蝶云星空BOS专题中级课-业务流程

    单据转换的核心是单据转换规则基于单据转换规则才会有单据转换插件。单据转换规则配置可以实现我们常见的单据流转需求,我们先来简单回顾一下单据转换规则。

  • 单据关联配置:要实现关键关联,需要先在下游单据中设置单据关联配置

image.png


  • 单据转换规则说明

image.png

  • 单据转换规则策略要点

image.png

  • 可以触发单据转换规则的操作

image.png

三、单据转换插件介绍

    C#开发时,单据转换插件的基类是AbstractConvertPlugIn。Python插件在单据转换规则-插件策略中注册即可。

    单据转换插件开始支持Python插件的版本是PT-146836 [7.5.1.202005],高于此版本,应该都是可以使用的。

    单据转换插件是依赖于单据转换规则来触发的,单据转换从选择单据、指定单据类型、分组合并、过滤携带数据等整个转换过程都提供了插件干预方法,应对一些复杂的单据转换场景需求。所以只要触发了单据转换规则,就可以触发单据转换插件执行过程。

  • 单据下推执行过程

image.png

image.png

  • 单据选单执行过程

image.png

  • 单据转换插件一览图(含事件执行顺序)

image.png


  • 选单时,单据转换插件事件执行顺序:与下推有小部分差异

image.png

这里要说明的是,下推、选单前、选单过程,采用的插件是同一个。 从事件的覆盖度来看,下推过程触发的事件是最全面的,针对下推过程编写的插件,选单过程被自动覆盖。也就是说通常情况下,在不对选单界面做特殊干预的情况下,按照下推的运行过程来开发一个单据转换插件,对选单操作也是通用的。

四、单据转换插件常用事件

        前面的截图中已经将单据转换插件执行过程讲得比较清晰了,总的来说单据转换插件也是按照一条"流水线"执行的,我们只需要在需要干预的环节,去实现对应的事件,加入我们的代码逻辑就可以了。

  • 我们再整体看一下下推运行时序

image.png

  • 新建一个单据转换插件,以采购申请下推采购订单为例

image.png

  • 下面看看单据转换插件中的一些常用事件

#解析字段映射关系,并构建查询参数。
#这里可以加入你想要的额外的字段
def OnQueryBuilderParemeter(e):
    #插件常用全局属性,顺带介绍一下,不一定在此事件中使用  #*************************************************************************************************************
    #this.OperationNumber;#操作编码,如下推,选单操作等,值分别为Push,Draw,
    #paraDIC=this.Option.GetVariables();#获取自定义参数集,字典类型,例如是否整单下推、是否需要校验下游单据新增权限、WebAPI调下推时传入的自定义参数等等
    #if(paraDIC.ContainsKey("参数标识")==True):#判断是否含有某个参数
    #    paraValue=paraDIC["参数标识"];#取出参数标识
    #***********************************************************************************************************
    #e.SourceBusinessInfo;#上游单据的元数据信息,可从这里获取一些上游单据的关键信息
    # secFormId=e.SourceBusinessInfo.GetForm().Id;#上游单据FormId
     billNo=SelectorItemInfo("FBillNo");#加入单据转化规则中没有配置的,但需要额外加载的字段
     e.SelectItems.Add(billNo);

#解析过滤策略中配置的条件,可以在这里加自定义的下推过滤条件
#此事件开始前,刚完成选单条件策略中的过滤条件的解析
#e.FilterPolicySQL来自于:【选单条件策略中设置的过滤条件(JsonSetting)】+【选单条件策略中的附加条件(CustFilter)】+【按目标组织基础资料属性过滤的条件(TargetOrgBDFilterList)】
#此事件结束后,e.FilterPolicySQL将使用AND操作符合并到QueryBuilderParemeter.FilterClauseWihtKey,后续作用于选单列表取数
def OnParseFilter(e):
    filterStr=" FBillNo LIKE 'CGSQ%' "
    e.FilterPolicySQL=StringUtils.JoinFilterString(e.FilterPolicySQL,filterStr);#追加过滤条件,默认用AND连接
    e.PlugFilterDesc = "单据编号必须包含【CGSQ】";#过滤条件描述信息,下推不满足时,会提示出来。

#选单时才有,解析字段映射关系中配置的过滤选项:过滤/仅追加
#此事件开始前,刚完成用于选单列表取数的QueryBuilderParemeter的构建,e.FilterOptionsSQL来自于QueryBuilderParemeter.FilterClauseWihtKey
def OnParseFilterOptions(e):
    #e.TargetData;#目标单的数据包
    #e.SourceBusinessInfo;#源单元数据信息
    #e.TargetBusinessInfo;#目标单元数据信息
    filterStr=" FReqQty > 10 ";
    e.FilterOptionsSQL = StringUtils.JoinFilterString(e.FilterOptionsSQL,filterStr);#追加选单过滤条件

#获取到源单数据之后触发,(下推执行)
#可以在此事件中修改源单下推的数据包,不会真实修改源单,只会影响下推的携带值
#例如,可以动态修改源单分单依据字段的值,以此实现动态分单策略
def OnGetSourceData(e):
    #e.SourceBusinessInfo;#源单元数据信息
    srcData=e.SourceData;#源单数据,本次下推的所有数据行,DynamicObjectCollection类型
    #可循环从数据包集合中取数,只能取到参与转换规则中配置的字段和OnQueryBuilderParemeter额外加入的字段
    for row in srcData:
        srcId=row["Id"];#源单Id
        srcEntryId=str(dr["FEntity_FEntryID"]);#源单分录内码:单据体标识_FEntryId

#获取到源单数据之后触发,(选单执行)
#与OnGetSourceData类似,只不过一个是在下推执行,一个是选单执行
def OnGetDrawSourceData(e):
    #e.SourceBusinessInfo;#源单元数据信息
    srcData=e.SourceData;#源单数据,本次下推的所有数据行,DynamicObjectCollection类型
    #可循环从数据包集合中取数,只能取到参与转换规则中配置的字段和OnQueryBuilderParemeter额外加入的字段
    for row in srcData:
        srcId=row["Id"];#源单Id
        srcEntryId=str(dr["FEntity_FEntryID"]);#源单分录内码:单据体标识_FEntryId

#执行分组前触发,可以在此增加自定义的分组字段
def OnBeforeGroupBy(e):
    srcData= e.SourceData;#源单数据
    HeadGroupKey = e.HeadGroupKey;#分单依据
    e.HeadGroupKey =("{0},{1}").format(e.HeadGroupKey,"FXXX");#追加分组字段
    EntryGroupKey=e.EntryGroupKey;#单据体分组合并字段
    e.EntryGroupKey =("{0},{1}").format(e.EntryGroupKey,"FEXXX");#追加单据体分组合并字段
    SubEntryGroupKey=e.SubEntryGroupKey;#子单据体分组字段
    e.SubEntryGroupKey =("{0},{1}").format(e.SubEntryGroupKey,"FEXXX");#追加单据体分组字段

#下推执行,根据分组策略创建目标单,此时还没根据字段映射赋值目标单的字段,这个事件用得不多
#也没有创建关联数据包,接近一个"空白"的下游单据,当然有默认值的字段还是有值
def OnCreateTarget(e):
    headEntity=e.TargetExtendedDataEntities.FindByEntityKey("FBillHead");#分组之后创建的所有目标单集合
    for entity in headEntity:
        billObj=entity.DataEntity;#下游单据的完整数据包,可以通过实体数据包操作方式处理下游单据字段

#选单执行,与OnCreateTarget类似,此时获取到了选单返回的数据,但是还没赋值到下游单据的字段上
def OnCreateDrawTarget(e):
    headEntity=e.TargetExtendedDataEntities.FindByEntityKey("FBillHead");#获取根据分组创建好的目标单

#目标单赋值之前触发,主要用启动字段赋值事件OnFieldMapping,用于监听字段映射赋值过程,用得很少
def OnBeforeFieldMapping(e):
    e.FireFieldMappingEvent=True;#启动字段赋值
    
#字段赋值事件,每个数据包,每个字段都会触发,易影响性能,慎用!
def OnFieldMapping(e):
    headEntity=e.TargetExtendedDataEntities.FindByEntityKey("FBillHead");#下游单据数据集
    fldKey=e.TargetField.Key.ToUpperInvariant();#赋值字段标识大写

#字段映射所有字段赋值完成之后触发,用得很少    
def OnAfterFieldMapping(e):
    headEntity=e.TargetExtendedDataEntities.FindByEntityKey("FBillHead");#下游单据数据集

#创建关联关系之前触发,可以取消创建,用得很少
def OnCreateLink(e):
    headEntity=e.TargetExtendedDataEntities.FindByEntityKey("FBillHead");#下游单据数据集
    #e.Cancel=True;#取消创建

#单据关联关系创建之后触发,表单服务策略执行之前
#如果需要干预下游单据,再执行表单服务策略,可以在此事件中对下游单据进行处理
#例如,要通过插件修改采购订单单价,之后系统自动执行表单服务策略计算金额
def OnAfterCreateLink(e):
    headEntity = e.Result.FindByEntityKey("FBillHead");#由于可能分单,所以可能有多个下游单据,是一个集合
    for entity in headEntity:#循环处理
        billObj=entity.DataEntity;#下游单据的完整数据包,可以通过实体数据包操作方式处理下游单据字段

#获取到服务策略之前触发,可在此事件中加入自定义的表单服务或者移除服务,用得少
def OnGetConvertBusinessService(e):
    FormSvcList=e.FormBusinessServices;#表单服务集合
    
#单据转换执行完成之后执行,在表单服务策略执行完成之后
#可以获取到下推完成之后,还未保存的下游单据,对下游单据数据包做最后的处理
#应用案例:单据转换插件提示消息
def AfterConvert(e):
    #整个单据转换执行完毕后的下游单据数据集合,相当于用户看到的推出来的下游单据,但是还没保存
    headEntity = e.Result.FindByEntityKey("FBillHead");#由于可能分单,所以可能有多个下游单据,是一个集合
    for entity in headEntity:#循环处理
        billObj=entity.DataEntity;


  • 相关应用案例参考推荐:

单据转换之多选基础资料下推携带

单据转换之多选辅助资料下推携带

单据转换插件提示消息

显示单据转换操作页面事件OnShowConvertOpForm

单据转换插件,动态表单插件,表单插件,服务插件,多单据体应用

案例四:AfterConvert事件

二开案例.单据转换插件.设置选单条件

单据转换实现多单据体到目标单的携带和关联

单据转换插件之携带子单据体

Python单据转换插件示例-自定义单据下推付款单

......

==========================本篇正文结束=====================================

截止到这一篇,Python插件开发各插件类型,已经介绍完了,感谢大家一如既往的关注与支持!

插件示例代码已经上传附件,老规矩,大家按需下载!

大家持续关注,点赞、评论、收藏,您的点赞、评论就是我前进的动力。

下一篇:【Python插件入门】第10篇(完结篇):插件常用工具类分享


发布于 金蝶云星空BOS开发交流圈 社群

赞 155