BOTP场景:搜索源单行原创
金蝶云社区-MiLai
MiLai
6人赞赏了该文章 551次浏览 未经作者许可,禁止转载编辑于2021年04月06日 08:38:24

需求背景

业务代码在对下游单据进行处理时,常常需要找到源头某单,并明确下游单据行与源单行之间的对应关系,根据行对应关系进行相关逻辑处理,如检查、或更新源单分录行字段值。

正常情况下,反写源单可以通过配置反写规则实现,不需要自行开发插件。

本章介绍如何使用平台提供的服务接口,搜索源单,并明确下游单据分录行和源单分录行之间的对应关系。

案例设计

假设源头核心单据是demo_botp1,当前处理的下游单据是demo_botp3:

  • demo_botp1 下推生成 demo_botp2;

  • demo_botp2 接着下推生成 demo_botp3;

  • 当前正在审核demo_botp3,需要找到源头的demo_botp1,并确定好行之间的对应关系;

相关知识点

  • TableDefine:单据实体表格定义对象,记录单据、单据体分配的表格编码(即tableId),也可以使用tableId反查到是什么单据的什么单据体;

  • RowId对象:BOTP引擎中,使用RowId对象,包装单据主表编码、单据内码、单据体表格编码、单据体内码,可以通过RowId的属性值,明确的知道其对应什么单据,那个单据体的那一行数据。

  • BFRowLinkUpNode:行关系追溯树,以下游单据单据体行作为根节点,逐层往上记录其直接源单行。通过逐层往上的方式,找到一条分录行全链条的所有源单行,包括直接源单、跨级源单;

示例代码

案例实现思路:

  • 开发单据demo_botp3的审核操作插件,派生自AbstractOperationServicePlugIn;

  • 重写beforeExecuteOperationTransaction方法,此方法在操作校验通过,开启事务保护前触发,可以在此事件中,检查、整理数据;

  • 调用BFTrackerServiceHelper服务方法,传入本单主实体编码和单据内码,搜索本单分录行的源单行追溯树;

  • 从源单行追溯树上,寻找指定的源单行,与当前行关联起来,存储在字典中;

package kd.bos.plugin.sample.bill.billconvert.bizcase;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import kd.bos.entity.EntityMetadataCache;
import kd.bos.entity.ExtendedDataEntity;
import kd.bos.entity.botp.runtime.BFRowLinkUpNode;
import kd.bos.entity.botp.runtime.TableDefine;
import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
import kd.bos.entity.plugin.args.BeforeOperationArgs;
import kd.bos.servicehelper.botp.BFTrackerServiceHelper;
/**
 * 示例:在单据审核时,搜索源头单据,并明确行之间的对应关系
 * @author rd_johnnyding
 *
 */
public class FindSourceRowOpPlugin extends AbstractOperationServicePlugIn {
    /**
     * 开启事务保护前,触发本事件:对数据进行检查、整理
     */
    @Override
    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
        if (e.getValidExtDataEntities().isEmpty()) {
            return;
        }
        // 取出源单主表的表格定义、源单单据体的表格定义
        String botpbill1_EntityNumber = "demo_botp1";
        String botpbill1_EntryKey = "entryentity";
        TableDefine srcMainTable = EntityMetadataCache.loadTableDefine(botpbill1_EntityNumber, botpbill1_EntityNumber);
        TableDefine srcEntryTable = EntityMetadataCache.loadTableDefine(botpbill1_EntityNumber, botpbill1_EntryKey);
        // 获取当前单据(下游单据)的主实体编码、单据内码
        String targetEntityNumber = this.billEntityType.getName();
        String targetEntryKey = "entryentity";
        TableDefine targetEntryTable = EntityMetadataCache.loadTableDefine(targetEntityNumber, targetEntryKey);
        Set<Object> billIds = new HashSet<>();
        for(ExtendedDataEntity dataEntity : e.getValidExtDataEntities()) {
            billIds.add(dataEntity.getBillPkId());
        }
        // 调用平台的服务,获取源单行追溯树:可以从追溯树上,追查每条分录行的来历
        List<BFRowLinkUpNode> linkUpNodes = BFTrackerServiceHelper.loadLinkUpNodes(
                targetEntityNumber,            // 下游单据主实体编码 
                "",                         // 可选参数,如果需要按分录行追溯,此参数传入单据体名称,否则按整单追溯
                billIds.toArray(new Long[0]));    // 如果按分录行追溯,此参数传入分录行内码;按整单追溯,此参数传入单据内码
        // 定义一个字典,记录下游单据行,和源单行之间的对应关系:每行可能对应多行源单,多对一合并生成
        Map<Long, Set<Long>> rowId_SrcRowIds = new HashMap<Long, Set<Long>>(); 
        // 对读取出行追溯树,逐条分析,找出其对应的源单行
        for(BFRowLinkUpNode linkUpNode : linkUpNodes) {
            if (Long.compare(targetEntryTable.getTableId(), linkUpNode.getRowId().getTableId()) != 0) {
                // 其他单据体行,略过不处理
                continue;
            }
            // 获取下游单据体行内码
            Long targetRowId = linkUpNode.getRowId().getEntryId();
            // 把下游单据体行,添加进字典
            if (!rowId_SrcRowIds.containsKey(targetRowId)) {
                rowId_SrcRowIds.put(targetRowId, new HashSet<>());
            }
            // 传入需要查找的源单主表的tableId,搜索出源头上所有主表tableId符合的节点
            List<BFRowLinkUpNode> srcRowNodes = linkUpNode.findSourceNodes(srcMainTable.getTableId());
            // 把各源单行添加到字典中,使用目标单行内码作为key检索
            for(BFRowLinkUpNode srcRowNode : srcRowNodes) {
                if (Long.compare(srcEntryTable.getTableId(), srcRowNode.getRowId().getTableId()) != 0) {
                    // 是源单其他单据体行,略过不处理
                    continue;
                }
                Long srcRowId = srcRowNode.getRowId().getEntryId();
                rowId_SrcRowIds.get(targetRowId).add(srcRowId);
            }
        }
        if (!rowId_SrcRowIds.isEmpty()) {
            // TODO 至此,rowId_SrcRowIds中,已经记录了每行对应的源单行内码,可以进行后续处理
        }
    }
}


赞 6