引入或使用WebApi导入数据,怎样建立和源单的关联关系

栏目:云苍穹知识作者:金蝶来源:金蝶云社区发布:2024-09-23浏览:1

引入或使用WebApi导入数据,怎样建立和源单的关联关系

需求背景

引入单据,或者使用WebApi导入数据时,需要同步建立和源单的关联关系,以支持联查、反写。

通常,在引入文件(或webapi数据包)中填好关联子实体行数据,填写好源单内码、分录内码,引入保存时,即会自动建立起与源单的关联关系。

但这种方式,需要在整理引入文件时,就能够确定源单内码、分录行内码,内码通常是不可见的,很难做到手工录入。

为了规避难以填写内码的难题,现场可以通过填写一些有意义的字段,如源单单据编号、源单行序号等,在引入过程中,开发引入插件根据这些字段值自动建立起与源单之间的关联关系。

本章介绍如何开发引入插件,根据源单单据编号,单据体源单行序号字段,自动建立起与源单的关联关系。


案例设计

假设下游的单据头,有如下几个字段,需要在引入文件中填写好,以便插件能够据此找到源单行内码:

srcbilltype:单据头字段,基础资料字段类型,记录源单类型,如demo_bill1;

srcbillno:单据头字段,文本字段,记录源单单据编号,如”JD-001”;

srcentrykey:单据头字段,文本字段,记录源单单据体名称,如”entryentity”;

srcrowseq:单据体字段,整数类型,记录源单行序号,如1,2,3…

相关知识点

关联子实体:系统会在目标单的单据体下级,创建一个关联子表,记录源单类型、源单内码、分录行内码,保存时,会基于此建立起与源单的关联关系并反写。关联子实体,通常以单据体标识加上”_lk”后缀命名,又可称为lk子实体、lk表等。如果是单据头的下级关联子实体,标识固定为”billhead_lk”。

实体表格编码:也称为tableId,为了明确的追查到源单行,关联子实体中,需要记录源单类型、源单单据体名称。这两个值是文本类型,直接记录会占用比较多的空间。因此,平台特别为每一种单据、单据体,都分配一个唯一的长整型数字,作为表格编码,即tableId。并改造关联子实体,只记录一个长整数的tableid,不再记录源单类型、单据体名称,节省不少存储空间。联查时,会自动根据表格编码,反查到明确的单据类型及单据体。

TableDefine对象:平台封装的公共对象,包含了主实体编码、单据体名称和表格TableId之间的对应关系。可以通过公共服务接口(EntityMetadataCache.loadTableDefine方法),传入单据主实体编码+单据体名称获取,也可以传入tableId获取;通过这个对象,可以根据单据和单据体追查tableId,也可以根据tableId反查单据和单据体。

示例代码

案例实现思路:

开发单据的表单插件,派生自AbstractBillPlugIn;

重写afterImportData方法,此方法在引入字段值填写完毕,保存之前触发

读取目标上引入的源单类型、源单编号字段值

根据源单编号,加载源单数据包,只需要加载源单内码、分录行内码、分录行序号;

对源单分录行进行逐行遍历,记录源单行号和源单内码、源单行内码之间的对应关系;

对目标单分录行进行逐行遍历,根据引入的源单行号,找到对应的源单内码、分录行内码;

把对应的源单内码、分录内码,填写在关联子实体中;

至此,下游单据中,已经记录好了关联的源单信息;保存时,会根据这些信息自动建立关联关系,支持联查,支持反写;

示例代码:


package kd.bos.plugin.sample.bill.billconvert.bizcase;


import java.util.HashMap;

import java.util.HashSet;

import java.util.Map;

import java.util.Set;


import kd.bos.bill.AbstractBillPlugIn;

import kd.bos.dataentity.entity.DynamicObject;

import kd.bos.dataentity.entity.DynamicObjectCollection;

import kd.bos.dataentity.utils.StringUtils;

import kd.bos.entity.BillEntityType;

import kd.bos.entity.EntityMetadataCache;

import kd.bos.entity.botp.runtime.TableDefine;

import kd.bos.entity.datamodel.events.ImportDataEventArgs;

import kd.bos.orm.query.QCP;

import kd.bos.orm.query.QFilter;

import kd.bos.servicehelper.BusinessDataServiceHelper;


/**

 * 示例代码:开发下游单单据插件,在引入时建立和源单关联信息,以支持联查、反写

 * 

 * @author rd_johnnyding

 */

public class AutoLinkSample extends AbstractBillPlugIn {


    // 下游单据头字段:源单类型字段,记录源单的标识,如"demo_bill1"

    private String KEY_SOURCEBILLTYPE = "srcbilltype";

    // 下游单据头字段:源单单据编号字段,记录源单单据编号,如 "JD-0001"

    private String KEY_SOURCEBILLNO = "srcbillno";

    // 下游单据头字段:源单关联主单据体标识字段,记录源单单据体标识,如 "entryentity"

    private String KEY_SOURCEENTRYKEY = "srcentrykey";

    // 下游单据体字段:源单关联主单据行号字段,记录源单行号,如 1,2,3...

    private String KEY_SOURCEROWSEQ = "srcrowseq";


    /**

     * 引入字段值填写完毕,保存前,触发此事件

     */

    @Override

    public void afterImportData(ImportDataEventArgs e) {

        this.linkSourceRow(this.getModel().getDataEntityType().getName(), "entryentity");

    }


    /**

     * 根据引入的源单编号和物料,自动匹配源单行,在lk子实体记录源单行内码

     */

    private void linkSourceRow(String targetEntityNumber, String targetEntryKey) {


        // 读取单据头源单类型、源单编号字段值

        Object srcEntityFldValue = this.getModel().getValue(KEY_SOURCEBILLTYPE);

        String srcEntityNumber = "";

        if (srcEntityFldValue instanceof DynamicObject) {

            srcEntityNumber = (String)((DynamicObject)srcEntityFldValue).getPkValue();

        }

        else {

            srcEntityNumber = (String)srcEntityFldValue;

        }

        String srcBillNo = (String) this.getModel().getValue(KEY_SOURCEBILLNO);

        String srcEntryKey = (String) this.getModel().getValue(KEY_SOURCEENTRYKEY);


        if (StringUtils.isBlank(srcEntityNumber) || StringUtils.isBlank(srcBillNo) || StringUtils.isBlank(srcEntryKey)) {

            // 没有填写源单类型、源单编号字段值,找不到源单,无法建立与源单的关联

            return;

        }


        // 读取源单数据包

        DynamicObject[] srcObjs = this.loadSourceBill(srcEntityNumber, srcEntryKey, srcBillNo);

        if (srcObjs.length == 0) {

            return;

        }


        // 循环分析源单行,提取源单行号、源单内码、分录行内码对应关系,放在字典中备用: key = 源单行号; value = [源单内码、分录行内码]

        Map<Integer, Object[]> srcRowIds = new HashMap<>();

        for(DynamicObject srcObj : srcObjs) {

            DynamicObjectCollection srcRows = srcObj.getDynamicObjectCollection(srcEntryKey);

            for (DynamicObject srcRow : srcRows) {

                Integer srcRowSeq = srcRow.getInt("seq");

                Object[] srcRowId = new Object[] {srcObj.getPkValue(), srcRow.getPkValue()};

                srcRowIds.put(srcRowSeq, srcRowId);

            }

        }


        // 获取源单单据体的表格编码:传入源单标识、源单单据体,记录关联关系时,需要用此编码,标识源单及单据体

        TableDefine tableDefine = EntityMetadataCache.loadTableDefine(srcEntityNumber, srcEntryKey);

        Long srcTableId = tableDefine.getTableId();


        // 拼接处关联子实体标识:如果是单据头下的lk子表,固定使用billhead_lk;如果是单据体下的lk子表,用单据体标识+lk

        String lkEntryKey = StringUtils.equals(targetEntityNumber, targetEntryKey) ? "billhead_lk" : targetEntryKey + "_lk"; 


        // 循环分析下游单的单据体行,逐一建立起与源单行的关联关系

        DynamicObject targetObj = this.getModel().getDataEntity(true);

        DynamicObjectCollection targetRows = targetObj.getDynamicObjectCollection(targetEntryKey);

        for(DynamicObject targetRow : targetRows) { 

            // 获取下级_lk子实体行

            DynamicObjectCollection linkRows = targetRow.getDynamicObjectCollection(lkEntryKey);

            if (!linkRows.isEmpty()) {

                continue;

            }

            linkRows.clear();


            // 寻找匹配的源单行:提取本行的匹配字段值,和源单行进行比对

            Integer srcRowSeq = targetRow.getInt(KEY_SOURCEROWSEQ);

            if (srcRowIds.containsKey(srcRowSeq)) {

                // 找到了匹配的行,创建一条_lk子实体上数据,记录源单内码

                DynamicObject linkRow = new DynamicObject(linkRows.getDynamicObjectType());

                linkRows.add(linkRow);


                // 在lk行中,记录源单分录表格编码、源单内码、源单分录内码

                Object[] srcRowId = srcRowIds.get(srcRowSeq);

                linkRow.set(lkEntryKey + "_stableid", srcTableId);        // 源单分录表格编码:以此标识源单类型及单据体

                linkRow.set(lkEntryKey + "_sbillid", srcRowId[0]);        // 源单内码

                linkRow.set(lkEntryKey + "_sid", srcRowId[1]);        // 源单分录行内码    

            }

        }


        // 至此:目标单分录行上,已经自动填写源单分录行内码;保存时,会自动创建关联关系及反写

    }


    /**

     * 从目标单中提取源单编号,读取出源单数据包

     * 

     * @param srcEntityNumber 源单主实体编码

     * @param srcEntryKey 源单关联主单据体标识

     * @param srcBillNo 源单单据编号

     * 

     * @return 源单数据包,仅包含源单内码、源单编号、源单行内码、行号

     */

    private DynamicObject[] loadSourceBill(String srcEntityNumber, String srcEntryKey, String srcBillNo) {


        BillEntityType sourceMainType = (BillEntityType)EntityMetadataCache.getDataEntityType(srcEntityNumber);


        // 读取源单:仅读取需要用到的字段:源单内码、源单单据编号、源单行内码、源单行号

        Set<String> selectFields = new HashSet<>();

        selectFields.add("id");

        selectFields.add(sourceMainType.getBillNo());

        selectFields.add(srcEntryKey + ".id");        

        selectFields.add(srcEntryKey + ".seq");


        QFilter filter = new QFilter(sourceMainType.getBillNo(), QCP.equals, srcBillNo);

        DynamicObject[] sourceBillObjs = BusinessDataServiceHelper.load(

                srcEntityNumber, 

                StringUtils.join(selectFields.toArray(), ","), 

                new QFilter[] {filter});

        return sourceBillObjs;

    }

}


引入或使用WebApi导入数据,怎样建立和源单的关联关系

需求背景引入单据,或者使用WebApi导入数据时,需要同步建立和源单的关联关系,以支持联查、反写。通常,在引入文件(或webapi数据包)中填...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息