BOTP流程中进行单据转换,下推附件面板数据到目标单原创
金蝶云社区-吴锐雄
吴锐雄
19人赞赏了该文章 2639次浏览 未经作者许可,禁止转载编辑于2023年03月07日 11:35:59

看完文章,点赞收藏,谢谢各位大佬!


一、背景

最近有伙伴在工作流中做下推操作(即进行单据转换),问我能不能在下推时,把附件面板上面的附件,由源单下推到目标单。

经过验证,我写了一个例子,介绍怎么在工作流中进行下推操作,并且下推的同时,把附件面板的数据下推到目标单。


二、实现思路


1 工作流

在工作流中做下推操作,可用工作流插件实现。

在审核节点中,在“同意”时,注册一个工作流插件,然后在工作流插件中使用ConvertServiceHelper(单据转换帮助类)进行单据转换。


2 单据转换规则

无论是在设计器页面中配置下推操作还是用代码调用ConvertServiceHelper,都需要创建一个单据转换规则。

如果是界面化配置的转换规则,只能进行一些单据头字段或者单据体字段的转换。


单据转换插件

为了能让 附件面板从源单下推到目标单,需要用单据转换插件来实现,因此需要编写和注册一个单据转换插件。

在单据转换插件中,从源单中获取到附件信息,然后用AttachmentServiceHelper(附件帮助类),把源单的附件信息绑定给目标单。


三、实现过程

1.准备工作

创建2个单据,用来做工作流和单据转换

image.png

image.png

image.png


2.流程服务云,创建工作流

新建工作流,

image.png

选择刚刚创建好的“源单” 单据,填写一些必填项目

image.png


创建之后如下图,进入工作流设计器

image.png


在审批节点中,在任务处理时执行,注册java插件

image.png


审核同意时,执行这个工作流插件:


image.png


配置节点参与人:

image.png


点击保存和发布之后,工作流就生效了

image.png


3.编写工作流插件代码

构造下推数据:

        Long pkid = execution.getId();// 单据pkid
        /*String billNumber = execution.getEntityNumber();// 单据标识
        String billNo = ((ExecutionEntityImpl) execution).getBillNo();// 单据编码*/
        List<ListSelectedRow> selectedRows = new ArrayList();
        ListSelectedRow selectedRow = new ListSelectedRow(pkid);
        selectedRows.add(selectedRow);


构造PushArgs对象,准备下推

        // 生成下推参数PushArgs
        PushArgs pushArgs = new PushArgs();
        // 必选,源单标识
        pushArgs.setSourceEntityNumber("kdec_wrx_source");
        // 必选,目标单标识
        pushArgs.setTargetEntityNumber("kdec_wrx_target");
        // 是否输出详细错误报告
        pushArgs.setBuildConvReport(false);
        // 必选,设置需要下推的源单及分录内码
        pushArgs.setSelectedRows(selectedRows);


下推,以及下推结束之后,打印下推结果,

注意:这里我调用的方法是pushAndSave,下推并保存。

因为在工作流里面,不能进行图形界面化的操作,所以我选择了下推并保存,

如果是在普通的单据页面上进行下推,开发者可以选择调用push方法,生成缓存的目标单据后,弹出到目标单据里面,让用户自己去保存。

        // 调用单据转换帮助类,下推目标单 并保存
        ConvertOperationResult pushResult = ConvertServiceHelper.pushAndSave(pushArgs);

        if ( pushResult.isSuccess() ) {
            System.out.println("DemoBOTPWorkflowPlugin: kdec_wrx_source 转换kdec_wrx_target 成功");

            Set<Object> targetBillSet = pushResult.getTargetBillIds();
            StringBuilder builder = new StringBuilder();
            for (Object o : targetBillSet) {
                builder.append(o.toString() + ", ");
            }
            System.out.println("下推成功,目标单id:" + builder.toString());
        } else {
            System.out.println("DemoBOTPWorkflowPlugin: kdec_wrx_source 转换kdec_wrx_target 失败");
        }


完整代码如下

package kd.ecos.demo;

import kd.bos.entity.botp.runtime.ConvertOperationResult;
import kd.bos.entity.botp.runtime.PushArgs;
import kd.bos.entity.datamodel.ListSelectedRow;
import kd.bos.servicehelper.botp.ConvertServiceHelper;
import kd.bos.workflow.api.AgentExecution;
import kd.bos.workflow.engine.extitf.IWorkflowPlugin;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

// 工作流插件,任务处理时执行,同意时,产生下推操作
// 流程中产生下推操作,下推操作再进行附件面板下推
public class DemoBOTPWorkflowPlugin implements IWorkflowPlugin {


    @Override
    public void notify(AgentExecution execution) {
        System.out.println("DemoBOTPWorkflowPlugin, notify");

        // 单据pkid
        Long pkid = Long.valueOf(execution.getBusinessKey());
        // 单据标识
        //String billNumber = execution.getEntityNumber();
        // 单据编码
        //String billNo = ((ExecutionEntityImpl) execution).getBillNo();

        List<ListSelectedRow> selectedRows = new ArrayList();
        ListSelectedRow selectedRow = new ListSelectedRow(pkid);
        selectedRows.add(selectedRow);

        // 生成下推参数PushArgs
        PushArgs pushArgs = new PushArgs();
        // 必选,源单标识
        pushArgs.setSourceEntityNumber("kdec_wrx_source");
        // 必选,目标单标识
        pushArgs.setTargetEntityNumber("kdec_wrx_target");
        // 可选,自动保存
        pushArgs.setAutoSave(true);
        // 可选,设置单据转换规则的id,如果没有设置,会自动匹配一个规则进行转换
        pushArgs.setRuleId("1134727974310918144");
        // 是否输出详细错误报告
        pushArgs.setBuildConvReport(false);
        // 必选,设置需要下推的源单及分录内码
        pushArgs.setSelectedRows(selectedRows);

        // 调用下推引擎,下推目标单并保存
        ConvertOperationResult pushResult = ConvertServiceHelper.pushAndSave(pushArgs);

        if ( pushResult.isSuccess() ) {
            System.out.println("DemoBOTPWorkflowPlugin: kdec_wrx_source 转换kdec_wrx_target 成功");

            Set<Object> targetBillSet = pushResult.getTargetBillIds();
            StringBuilder builder = new StringBuilder();
            for (Object o : targetBillSet) {
                builder.append(o.toString() + ", ");
            }
            System.out.println("DemoBOTPWorkflowPlugin:下推成功,目标单id:" + builder.toString());
        } else {
            System.out.println("DemoBOTPWorkflowPlugin: kdec_wrx_source 转换kdec_wrx_target 失败");
        }

    }

}


4.创建单据转换规则

这一步,一共要配置几个地方:

(1)配置源单的单据头,和目标单的单据头进行转换

(2)配置源单的单据编码和组织,直接下推到目标单

(3)配置源单的一个文本字段,使用上 小写函数,把字段值转换为小写,下推到目标单

(4)注册单据转换插件

image.png

image.png


做一个单据头的转换

image.png


配置单据编码转换

image.png

配置普通文本字段,使用大写转换为小写的函数:

image.png


注册单据转换插件

image.png

image.png


5.编写单据转换插件代码

在afterConvert事件中,可以在单据转换之后,获取到目标单和源单信息

@Override
public void afterConvert(AfterConvertEventArgs e) {
    ExtendedDataEntitySet targetExtDataEntitySet = e.getTargetExtDataEntitySet();
    // 获取转换后的目标单
    ExtendedDataEntity[] extendedDataEntities = targetExtDataEntitySet.FindByEntityKey("kdec_wrx_target");
}


必须给目标单设置一个pkid。

注意:如果目标单没有pkid,附件面板的数据是没有办法绑定到目标单据上的。

                long id = DB.genGlobalLongId();
                dynamicEntity.set("id", id);


注意:这个数据对象有没有id,取决于之前在工作流插件中,调用ConvertServiceHelper.push还是ConvertServiceHelper.pushAndSave。

如果是调用pushAndSave方法,由于单据转换结束时,这个单据已经保存了,所以是有id的。

用一个if代码块判断有没有id,如果没有,就设置一个进去,代码如下:

下面这个判断代码可不写,我为了防止流程中出现一些异常情况,还是加了一个判断id的if代码块:

            if (dynamicEntity.get("id") instanceof Long && ((Long) dynamicEntity.get("id")) < 1) {
               ......
            }


遍历目标单,获取对应的源单,如果是一对一的转换,源单的列表长度都是1

            // 获取源单数据
            List<DynamicObject> convertSource = (List<DynamicObject>) extendedDataEntity.getValue("ConvertSource");

            // 遍历源单数据
            for (DynamicObject dynamicObject : convertSource) {
                // 获取源单id,通过源单id获取源单的附件信息
                long sourceId = dynamicObject.getLong("id");
                List<Map<String, Object>> attachments = AttachmentServiceHelper.getAttachments("kdec_wrx_source", sourceId, "attachmentpanel");

                // 把源单的附件信息设置给目标单
                AttachmentUntil.uploadTargetAttachments("kdec_wrx_target", dynamicEntity.get("id"), "attachmentpanel", attachments);
            }


完整代码如下,AttachmentUntil代码见附件

package kd.ecos.demo;

import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.db.DB;
import kd.bos.entity.ExtendedDataEntity;
import kd.bos.entity.ExtendedDataEntitySet;
import kd.bos.entity.botp.plugin.AbstractConvertPlugIn;
import kd.bos.entity.botp.plugin.args.*;
import kd.bos.servicehelper.AttachmentServiceHelper;
import kd.ecos.utils.AttachmentUntil;

import java.util.List;
import java.util.Map;

public class DemoConvertPlugIn extends AbstractConvertPlugIn {

    /**
     * 单据转换后事件,最后执行
     *
     * @param e
     * @remark 插件可以在这个事件中,对生成的目标单数据,进行最后的修改
     */
    @Override
    public void afterConvert(AfterConvertEventArgs e) {
        ExtendedDataEntitySet targetExtDataEntitySet = e.getTargetExtDataEntitySet();
        // 获取转换后的目标单
        ExtendedDataEntity[] extendedDataEntities = targetExtDataEntitySet.FindByEntityKey("kdec_wrx_target");

        // 遍历目标单
        for (ExtendedDataEntity extendedDataEntity : extendedDataEntities) {
            // 目标单数据
            DynamicObject dynamicEntity = extendedDataEntity.getDataEntity();
            // 判断有没有id,如果没有,要设置一个。在附件绑定时,附件面板不能绑定没有id的单据
            // 这个单据转换的是用代码调起的,有没有id取决于调用 ConvertServiceHelper.push还是 ConvertServiceHelper.pushAndSave
            // 如果是调用pushAndSave方法,那么此时目标单已经保存,就不需要设置id,if里面的代码块会跳过
            if (dynamicEntity.get("id") instanceof Long && ((Long) dynamicEntity.get("id")) < 1) {
                long id = DB.genGlobalLongId();
                dynamicEntity.set("id", id);
            }
            // 获取源单数据
            List<DynamicObject> convertSource = (List<DynamicObject>) extendedDataEntity.getValue("ConvertSource");

            // 遍历源单数据
            for (DynamicObject dynamicObject : convertSource) {
                // 获取源单id,通过源单id获取源单的附件信息
                long sourceId = dynamicObject.getLong("id");
                List<Map<String, Object>> attachments = AttachmentServiceHelper.getAttachments("kdec_wrx_source", sourceId, "attachmentpanel");

                // 把源单的附件信息设置给目标单
                AttachmentUntil.uploadTargetAttachments("kdec_wrx_target", dynamicEntity.get("id"), "attachmentpanel", attachments);
            }
        }

    }

}


四、效果展示

新增“源单”单据,填写单据编号,文本写上大写字母,添加一些附件图片。

点击“保存”和“提交”按钮。


image.png


当前用户接收到了审批消息

image.png


进入消息中心,同意这条审核单据

image.png

image.png


查看日志打印,显示下推成功

image.png


查看单据转换结果:

查看源单状态,已经是“已审核”状态

image.png

查看目标单列表,发现已经下推成功

image.png



进入目标单详情,发现源单的两个附件已经绑定在目标单上了

image.png


进入开发平台,查看附件明细表:

image.png

发现两张单据确实已经绑定在了目标单的附件面板上

image.png


五、附件

工作流已上传:Proc_kdec_wrx_source_audit_1.zip

注意:如果参与人配置的人员,在导入环境里面没有,需要重新配置工作流。


源代码已上传,页面的元数据以补丁包的方式上传,都在压缩包code_meta.zip里面。





赞 19