【Python插件入门】第5篇:单据列表插件原创
金蝶云社区-CQ周玉立
CQ周玉立
215人赞赏了该文章 2.4万次浏览 未经作者许可,禁止转载编辑于2022年08月17日 10:37:48
summary-icon摘要由AI智能服务提供

本文是对Python插件开发中关于单据列表插件的详细介绍。文章首先简要回顾了表单插件的内容,并指出列表插件的必要性,指出其是Web服务层插件,依赖于单据列表界面触发。接着,文章详细讲解了列表插件的基本概念、重要成员属性(如ListView、ListModel及其相关功能),并提供了新建列表插件的注意事项和注册方法。此外,还介绍了列表插件中常用的事件及其用法,包括过滤事件、菜单点击事件、列表双击事件、单元格超链接点击事件以及列表条件格式化事件等,并给出了具体的事件处理方法和示例代码。

往期回顾:

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

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

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

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


    前面讲了单据表单插件,相信大家对插件事件有了一定的掌握,今天讲一下单据列表插件。

一、列表插件简介

    上一篇讲表单插件时,提到单据列表是不会触发表单插件的,于是我们需要用列表插件来处理列表相关的一些功能。

    列表插件也是Web服务层插件,需要依赖于单据列表界面(ListView)才能触发,可应用于单据列表、基础资料列表中。

    在C#插件开发时,列表插件的基类是AbstractListPlugIn列表插件和表单插件都是界面类的插件,他们都需要依赖于界面触发,他们有一个共同的父类AbstractDynamicFormPlugIn,所以有些表单插件的用法在列表插件中也同样可以用。

二、列表插件重要成员介绍

    列表插件中同样有this.Context,这个在上一篇表单插件中介绍了,用法类似,不再过多介绍,这里主要介绍一下列表插件特殊的一些成员属性。

  • ListView :列表视图层对象,列表插件除了View之外,还有ListView,通过ListView可以提供一些列表特有的功能。

    this.ListView.BillBusinessInfo.GetForm().Id;#当前列表对应的单据标识
    this.ListView .OpenParameter;#列表入口参数
    this.ListView.OpenParameter.HideListMenu = True;#隐藏列表菜单
    this.ListView.OpenParameter.IsShowFilter = True;#打开列表自动弹出过滤框,默认为False
    this.ListView.OpenParameter.IsShowQuickFilter=False;#是否显示快捷过滤
    this.ListView.OpenParameter.FilterSchemeId;#当前过滤方案ID

    this.ListView.OpenParameter.IsTrackBillList();#是否上下查列表

    #获取自定义参数,可获取发布主控台的参数,例如,将不同单据类型发布成不同列表

    this.ListView.OpenParameter.GetCustomParameter("参数标识");

    this.ListView.SelectedRowsInfo;#当前列表上被选中的行记录,复选框勾选的记录集合
    this.ListView.CurrentSelectedRowInfo;#当前列表上当前选择行数据,即焦点行
    this.ListView.CurrentPageRowsInfo;#当前列表页所有单据的行信息

    #******************************************************************************************

    grid=this.View.GetControl[EntryGrid]("FList");#获取列表表格控件对象

    selectedRowIndexs=List[int]();
    selectedRowIndexs.Add(1);
    selectedRowIndexs.Add(3);
    grid.SelectRows(selectedRowIndexs.ToArray());#设置选中行

    grid.SetRowHeight(80);#设置行高

  • ListModel :列表数据模型,从这里可以获取列表的数据

    this.ListModel;#获取列表数据模型
    selectedRowsInfo = this.ListView.SelectedRowsInfo;
    this.ListModel.GetData(selectedRowsInfo);#获取选中的数据

    selectedRowsInfo.GetPrimaryKeyValues();#获取选中行所有单据ID,字符串数组
    selectedRowsInfo.GetEntryPrimaryKeyValues();#获取所有选中行所有明细ID,字符串数组

    this.ListModel.FieldKeyMap;#列表上显示的字段,其字段名 FieldName 和字段标识Key的对应关系
    this.ListModel.FilterParameter;#列表过滤参数对象
    this.ListModel.FilterParameter.CustomFilter;#列表过滤框实体数据包
    this.ListModel.FilterParameter.FilterRows;#列表条件过滤行数据
    this.ListModel.FilterParameter.FilterString;#列表条件过滤数据系统自动转换出的条件表达式
    this.ListModel.GlobalParameter;#单据参数配置中配置的单据全局参数
    this.ListModel.ParameterData;#用户参数实体数据包,选项菜单界面的配置数据
    this.ListModel.Header;#列表表头对象
    this.ListModel.Header.GetChilds();#列表所有的列头集合
    this.ListModel.StartRow;#开始行索引,从0开始

    this.ListModel.Refresh();#刷新列表
    this.ListModel.RefreshByFilter();#根据过滤条件,重新取数,刷新列表

三、新建一个列表插件

  • 注意:这里提供一个引用比较全的Python列表插件示例模板,在附件中下载示例代码,复制到BOS里面注册!

  • Python列表插件注册方法:如下图所示,以采购订单为例,其他单据类似

image.png

四、列表插件常用事件介绍

    Python插件中如何使用事件,看第4篇中的介绍。

  • 列表插件中的常用事件用法介绍:下面介绍一些列表插件中的常用事件

#列表插件过滤事件,加载列表时会触发
#用法1:对列表过条件进行干预,此用法最广
#用法2:修改快捷过滤和过滤框的条件过滤,案例:动态构建下拉列表-年份
def PrepareFilterParameter(e):
    custfilterObj=e.CustomFilter;#过滤框实体数据包
    filterStr=e.FilterString;#过滤框条件过滤表达式
    sortStr=e.SortString#排序字段表达式
    statusFilterStr=e.StatusFilterString;#状态字段过滤表达式
    #msg=("{0}").format(statusFilterStr);
    #this.View.ShowMessage(msg);
    myFilterStr=("FBillNo like '%{0}%' ").format("1");#单据编号包含1
    e.AppendQueryFilter(myFilterStr);#追加过滤条件

#列表菜单点击事件,列表菜单点击开始时触发
#此事件也是很常用的,可以在此事件中取消菜单的点击事件
#使用时一定要判断菜单标识!!!
def BarItemClick(e):
    key=e.BarItemKey.ToUpperInvariant();
    if(key=="TestBtn".ToUpperInvariant()):
        #e.Cancel=True;#取消菜单的点击,可以阻止后续功能的触发,可完成一些简单校验
        #msg=("菜单[{0}]点击被取消啦!").format(key);
        #this.View.ShowMessage(msg);
        return;
    
#列表菜单点击后事件,列表菜单点击完成后触发
#使用时一定要判断菜单标识!!!
#自定义菜单功能实现建议在此事件中完成
#应用案例:列表插件实现单据批改
def AfterBarItemClick(e):
    key=e.BarItemKey.ToUpperInvariant();
    if(key<>"TestBtn".ToUpperInvariant()):
        return;
    msg=("菜单[{0}]点击完成啦!").format(key);
    #this.View.ShowMessage(msg);
    selectedRowsInfo=this.ListView.SelectedRowsInfo;
    billIDs=selectedRowsInfo.GetPrimaryKeyValues();
    entryIDs=selectedRowsInfo.GetEntryPrimaryKeyValues();
    msg=("选中了[{0}]个单据,[{1}]条明细!").format(billIDs.Length,entryIDs.Length);
    this.View.ShowMessage(msg);

#列表双击事件
def ListRowDoubleClick(e):
    row=e.Row;#双击的行号
    colKey=e.ColKey;#双击单元格列标识,这里取到的是列表显示字段名,不能直接用来取单元格的值,可用来做单元格判断
    currentPageRowsInfo = this.ListView.CurrentPageRowsInfo;#列表当前页的数据
    startRow = this.ListModel.StartRow;#当前页开始序号
    num = row - startRow;#双击行所在当前页的行序号
    listSelectedRow = currentPageRowsInfo[num - 1];#当前双击行数据
    FID=listSelectedRow.PrimaryKeyValue;#双击行单据ID
    entryId=listSelectedRow.EntryPrimaryKeyValue;#双击行明细ID
    #通过QueryBuilderParemeter取数,可以获取当前单元格的值,这里顺便演示一下这种取数方法吧*****************
    queryParam=QueryBuilderParemeter();
    queryParam.FormId=this.View.BillBusinessInfo.GetForm().Id;
    queryParam.BusinessInfo=this.View.BillBusinessInfo;
    #billId=SelectorItemInfo(this.View.BillBusinessInfo.GetForm().PkFieldName);#单据内码
    #queryParam.SelectItems.Add(billId);
    #billNo=SelectorItemInfo("FBillNo");#单据编号
    #queryParam.SelectItems.Add(billNo);
    #supplierId=SelectorItemInfo("FSupplierId");#供应商内码
    #queryParam.SelectItems.Add(supplierId);
    #supplierName=SelectorRefItemInfo("FSupplierId.FName");#取基础资料属性字段写法不一样,要注意
    #supplierName.PropertyName="FSupplierId_FName";估计后台是SQL取数,字段名不能有".",要重命名
    #queryParam.SelectItems.Add(supplierName);
    #以上是通过QueryBuilderParemeter取数示例,下面就是取当前双击单元格的*************************************
    fldKey=colKey;
    if("." in colKey):#说明是基础资料的属性
        colItem=SelectorRefItemInfo(colKey);
        fldKey=colKey.Replace(".","_");
        colItem.PropertyName=fldKey;
        queryParam.SelectItems.Add(colItem);
    else:
        queryParam.SelectItems.Add(SelectorItemInfo(colKey));
    entity=this.View.BillBusinessInfo.GetEntity(listSelectedRow.EntryEntityKey);
    if(entity<>None):#列表显示了单据体
        queryParam.FilterClauseWihtKey=("{0}_{1}={2}").format(entity.Key,entity.EntryPkFieldName,entryId);
    else:#列表只显示了单据头
        queryParam.FilterClauseWihtKey=("{0}={1}").format(this.View.BillBusinessInfo.GetForm().PkFieldName,FID);
    dataRows=QueryServiceHelper.GetDynamicObjectCollection(this.Context, queryParam);#获取数据结果数据包
    colValue="";
    if(dataRows.Count>0):
        colValue=("{0}").format(dataRows[0][fldKey]);
    msg=("双击了第{0}行的[{1}]").format(row,colValue);
    this.View.ShowMessage(msg);
    e.Cancel=True;#取消双击事件,否则双击会打开单据,也可以在BOS中取消列表双击事件绑定的操作

#列表单元格超链接点击事件
#该事件中数据处理方式和上面的列表双击事件中几乎一致,这里不再重复讲解
def EntryButtonCellClick(e):
    row=e.Row;#点击超链接所在序号
    fldKey=e.FieldKey;#超链接所在列字段标识
    msg=("点击了第{0}行的[{1}]").format(row,fldKey);
    this.View.ShowMessage(msg);
    e.Cancel=True;#取消事件,单据编号会自动超链接打开单据

#列表条件格式化事件
#可在此事件中根据不同的条件判断设置颜色,BOS中也可以配置,这里可以实现更灵活的条件判断
def OnFormatRowConditions(args):
    if (args.DataRow.ColumnContains("FMaterialId_Ref")):
        #注意!!!这里根据特别演示基础资料属性字段判断设置颜色,因为【列表条件格式化】不支持配置基础资料属性字段作为条件字段
        matObj=args.DataRow.DynamicObject["FMaterialId_Ref"];#物料字段实体数据包,可以取到引用属性中添加了的属性字段
        matNum=("{0}").format(matObj["Number"])
        if(matNum[0:1]=="1"):#物料编码是1开头的显示颜色,这里是实体数据包取值,别忘了是用绑定实体属性标识
            fc=FormatCondition();
            fc.ForeColor="#FFFF9B98";#前景色
            #fc.BackColor="#FFFF9B98";#背景色
            args.FormatConditions.Add(fc);
    if(args.DataRow.ColumnContains("FDate")):
        #这里是普通字段判断设置颜色,首先需要判断列表是否显示了这个字段,否则没显示该字段时会报错,而且只有显示了这个字段才能判断设置颜色
        dateStr=str(args.DataRow["FDate"]);#创建人Id
        date=DateTime.Parse(dateStr).ToString("yyyy-MM-dd");
        #this.View.ShowMessage(date);
        if(date==DateTime.Now.ToString("yyyy-MM-dd")):#单据日期=今天的单据显示颜色
            fc=FormatCondition();
            #fc.ForeColor="#FFFF9B98";#前景色
            fc.BackColor="#0000FF";#背景色
            args.FormatConditions.Add(fc);


#单据界面执行单据操作前触发,例如,保存,提交,审核等,使用时一定要判断操作代码
#与表单插件类似,不同的是,列表里面是批量处理,通常需要获取勾选的所有单据,参考前面事件中的介绍
def BeforeDoOperation(e):
    opCode=e.Operation.FormOperation.Operation.ToUpperInvariant();#触发操作代码大写,例如保存:SAVE
    if(opCode=="DELETE"):
        e.Cancel=True;#可以取消触发操作
        this.View.ShowWarnningMessage("取消删除!");
#单据界面执行单据操作完成后触发,例如,保存,提交,审核等,使用时一定要判断操作代码
#与表单插件类似,不同的是,列表里面是批量处理,通常需要获取勾选的所有单据,参考前面事件中的介绍
def AfterDoOperation(e):
    opCode=e.Operation.Operation.ToUpperInvariant();#触发操作代码大写,例如保存:SAVE
    this.View.ShowMessage(str(opCode));


好了,列表插件的 常用事件就介绍到这里,了解这些事情也基本能满足常见需求了。

五、列表插件读取单据数据

    对于列表插件开发,通常是对单据进行批量处理,列表插件中读取单据数据的方法也不同,这里简单介绍一下。

    在列表插件中是不能直接读取到完整的单据数据包的,需要变通获取,一般来讲是读取勾选的数据,在列表插件的成员中可以读取到勾选的数据行,但是字段信息不全,所以思路都是根据勾选的构建过滤条件,再去查询单据。

  • 首先,我们再来回忆一下列表插件读取数据要用到的几个成员:

    currentSelectedRow=this.ListView.CurrentSelectedRowInfo;#当前列表上当前选择行数据,即焦点行,单行数据

    selectedRowsInfo=this.ListView.SelectedRowsInfo;#列表勾选的数据集合

    billIDs=selectedRowsInfo.GetPrimaryKeyValues();#可以用来构建单据过滤条件查询单据

    entryIDs=selectedRowsInfo.GetEntryPrimaryKeyValues();
    datas this.ListModel.GetData(selectedRowsInfo);#根据勾选的数据集获取数据包,DynamicObjectCollection类型
    if (datas.Count <= 0):
#根据这个数据包集合来判断列表是否有勾选的数据行
        this.View.ShowWarnningMessage("未选择任何行!");
        return;

    #有时候也需要根据列表的分页数据来获取

     currentPageRowsInfo = this.ListView.CurrentPageRowsInfo;#列表当前页的数据
     startRow = this.ListModel.StartRow;#当前页开始序号

  • 在列表插件中,一般都是基于以上成员变量来读取单据数据,读取数据的方法有下面几种

    ①通过QueryBuilderParemeter取数方法来读取单据数据,参考前面列表双击事件中的介绍,效率较高,推荐!

    ②直接从selectedRowsInfo数据包中获取字段值,参考OnFormatRowConditions事件中的示例代码。

        注意!这种方法只能获取当前列表显示出来的字段,以及单据编号、内码等一些关键字段信息。

        可以通过循环的方式逐行获取字段值,示例代码如下:

    for row in selectedRowsInfo:
        dr=row.DataRow;
        billNo=row.BillNo;#单据编号
        billId=row.PrimaryKeyValue;#单据ID
        entryId=row.EntryPrimaryKeyValue;#明细ID,显示了单据体才会有值
        entityKey=str(row.EntryEntityKey);#当前显示的单据体标识,可用来判断entryId是属于哪个单据体的
        #其他字段要先判断是否显示
        if(dr.ColumnContains("FMaterialId_Id")):#物料Id
            matId=dr.DynamicObject["FMaterialId_Id"];
        if(dr.ColumnContains("FMaterialId_Ref")):#物料数据包
            #可以从这里读取基础资料属性字段,基础资料的取值方式可以参考这里
            matObj=dr.DynamicObject["FMaterialId_Ref"];
            matNum=matObj["Number"];#物料编码
            matName=matObj["Name"];#物料名称
        if(dr.ColumnContains("FDate")):#日期,普通字段参考这里
            date=dr.DynamicObject["FDate"];

    ③或者通过数据包datas中获取数据,方法其实与第②种从selectedRowsInfo中取数类似。

    datas是一个DynamicObjectCollection类型的数据集,是不是很熟悉了呢,学到这里,解析这个应该问题不大了!

    datas和selectedRowsInfo中的字段标识一样,也是只能取到列表显示的字段,并且取数方法也类似。

    for row in datas:
        billNo=row["FBILLNO"];#单据编号
        billId=row["FID"];#单据ID
        #其他字段建议先判断是否存在
        if(row.DynamicObjectType.Properties.ContainsKey("FMaterialId_Id")):#物料Id
            matId=row["FMaterialId_Id"];
        if(row.DynamicObjectType.Properties.ContainsKey("FMaterialId_Ref")):#物料数据包
            #可以从这里读取基础资料属性字段,基础资料的取值方式可以参考这里
            matObj=row["FMaterialId_Ref"];
            matNum=matObj["Number"];#物料编码
            matName=matObj["Name"];#物料名称
        if(row.DynamicObjectType.Properties.ContainsKey("FDATE")):#日期,普通字段参考这里
            date=row["FDate"];

    ④基于取到的单据内码ID和单据明细ID,拼接SQL,直接从数据库查询数据。

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

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

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

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



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

图标赞 215
215人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!