付款申请单,如何追查跨级源头采购订单
金蝶云社区-lindsey
lindsey
5人赞赏了该文章 1,256次浏览 未经作者许可,禁止转载编辑于2018年12月02日 18:41:42

案例背景:
采购订单 -> 收料通知单 -> 采购入库单 -> 应付单 -> 付款申请单,采购订单经过多步下推,才生成付款申请单。

现在需要根据付款申请单的单据内码,寻找其源头的采购订单;
打开采购订单列表,展示出来搜索出的源头采购订单;

技术难点:
1. 经过了多步骤下推,如何从付款申请单逐层往上追溯,找到源头采购订单?
2. 如果获取这样的追溯关系?
3. 中间环节应付单,对采购物料明细进行合并,重新归纳了付款计划,在此情景下如何追溯?

实现方案:
1. 通过付款申请单的关联主实体 - 付款明细,根据业务流程实例数据,寻找到源头的应付单
2. 通过应付单的的关联主实体 - 物料明细,读取业务流程实例数据,寻找到源头的采购订单

案例设计:
在付款申请单上,添加一个菜单:联查采购订单(tbSearchSrcPO);
然后挂上本插件,即可验证效果

示例代码:
已经在本地验证通过,代码较长
//*************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

using Kingdee.BOS;
using Kingdee.BOS.Util;
using Kingdee.BOS.BusinessEntity.BusinessFlow;
using Kingdee.BOS.Core;
using Kingdee.BOS.Core.DynamicForm.PlugIn;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Core.Bill;
using Kingdee.BOS.Core.Bill.PlugIn;
using Kingdee.BOS.Core.Bill.PlugIn.Args;
using Kingdee.BOS.Core.BusinessFlow.ServiceArgs;
using Kingdee.BOS.Core.Metadata;
using Kingdee.BOS.Core.Metadata.EntityElement;
using Kingdee.BOS.Core.List;
using Kingdee.BOS.Core.SqlBuilder;
using Kingdee.BOS.ServiceHelper;

namespace JDSample.FormPlugIn.Bill
{
    [Description("搜索付款申请单的源头采购订单")]
    public class S151202SearchSrcBillEdit : AbstractBillPlugIn
    {
        public override void AfterBarItemClick(AfterBarItemClickEventArgs e)
        {
            if (e.BarItemKey.EqualsIgnoreCase("tbSearchSrcPO"))
            {// 搜索源头采购订单
                if (this.View.Model.GetPKValue() == null )
                {
                    this.View.ShowMessage("请先保存单据!");
                    return;
                }
                long payAppBillId = Convert.ToInt64(this.View.Model.GetPKValue());
                this.DoSearchSrcPO(payAppBillId);
            }
        }
        /// <summary>
        /// 搜索源头采购订单,并显示列表
        /// </summary>
        /// <param name="payAppBillId">付款申请单单据内码</param>
        private void DoSearchSrcPO(long payAppBillId)
        {
            // 读取付款申请单.明细内码
            HashSet<long> payAppEntityIds = this.LoadEntityIds(
                "CN_PAYAPPLY", "FPAYAPPLYENTRY", new long[] { payAppBillId });
            if (payAppEntityIds.Count == 0)
            {
                this.View.ShowMessage("付款申请单的明细单据体没有数据,没有源单!");
                return;
            }
            
            // 读取付款申请单的业务流程实例
            BusinessFlowInstanceCollection payAppInsts = this.LoadBFInstances(
                "CN_PAYAPPLY", "FPAYAPPLYENTRY", payAppEntityIds.ToArray());
            if (payAppInsts.Count == 0)
            {
                this.View.ShowMessage("付款申请单的明细单据体是手工创建的,没有源单!");
                return;
            }
            // 取付款申请单源头的应付单.付款计划
            HashSet<long> apPlanEntityIds = this.GetSrcEntityIdsByInsts(payAppInsts,
                "AP_Payable", "FEntityPlan",
                "CN_PAYAPPLY", "FPAYAPPLYENTRY", payAppEntityIds);
            if (apPlanEntityIds.Count == 0)
            {
                this.View.ShowMessage("没有找到源头的应付单,线索中断,不能继续追查采购订单");
                return;
            }
            // 根据应付单.付款计划内码,找到应付单.物料明细内码
            HashSet<long> apBillIds = this.LoadBillIds("AP_Payable", "FEntityPlan", apPlanEntityIds.ToArray());
            HashSet<long> apDetailIds = this.LoadEntityIds("AP_Payable", "FEntityDetail", apBillIds.ToArray());
            // 加载应付单.物料明细的业务流程实例
            BusinessFlowInstanceCollection apDetailInsts = this.LoadBFInstances(
                "AP_Payable", "FEntityDetail", apDetailIds.ToArray());
            
            // 取应付单源头的采购订单物料明细内码
            HashSet<long> poEntityIds = this.GetSrcEntityIdsByInsts(apDetailInsts,
                "PUR_PurchaseOrder", "FPOOrderEntry",
                "AP_Payable", "FEntityDetail", apDetailIds);
            
            if (poEntityIds.Count == 0)
            {
                this.View.ShowMessage("没有找到源头的采购订单!");
                return;
            }
            // 取源头的采购订单内码
            HashSet<long> poBillIds = this.LoadBillIds("PUR_PurchaseOrder", "FPOOrderEntry", poEntityIds.ToArray());
            // 显示出搜索出来的采购订单列表
            this.ShowPOList(poBillIds.ToArray());
        }
        /// <summary>
        /// 根据单据体内码,读取其单据内码
        /// </summary>
        /// <param name="formId"></param>
        /// <param name="entityKey"></param>
        /// <param name="entityIds"></param>
        /// <returns></returns>
        private HashSet<long> LoadBillIds(string formId, string entityKey, long[] entityIds)
        {
            HashSet<long> billIds = new HashSet<long>();
            if (entityKey.EqualsIgnoreCase("FBillHead"))
            {// 需要读取的实体为单据头,直接返回实体内码集合,无需到数据库读取
                foreach (var entityId in entityIds)
                {
                    billIds.Add(entityId);
                }
            }
            else
            {
                // 读取单据的元数据
                FormMetadata meta = MetaDataServiceHelper.Load(this.Context, formId) as FormMetadata;
                Entity entity = meta.BusinessInfo.GetEntity(entityKey);
                // 构建取数服务参数,仅加载单据内码字段
                QueryBuilderParemeter queryParam = new QueryBuilderParemeter();
                queryParam.FormId = formId;
                queryParam.BusinessInfo = meta.BusinessInfo;
                queryParam.SelectItems.Add(new SelectorItemInfo(meta.BusinessInfo.GetForm().PkFieldName));
                queryParam.FilterClauseWihtKey = string.Format("{0}_{1} in ({2})",
                    entityKey, entity.EntryPkFieldName, string.Join(",", entityIds));
                // 读取数据
                var rows = QueryServiceHelper.GetDynamicObjectCollection(this.Context, queryParam);
                
                // 把取到的单据内码返回
                foreach(var row in rows)
                {
                    long billId = Convert.ToInt64(row[0]);
                    if (billIds.Contains(billId) == false)
                    {
                        billIds.Add(billId);
                    }
                }
            }
            return billIds;
        }
        /// <summary>
        /// 读取指定单据的实体内码返回
        /// </summary>
        /// <param name="formId">单据formId</param>
        /// <param name="entityKey">实体Key</param>
        /// <param name="billIds">单据内码集合</param>
        /// <returns></returns>
        private HashSet<long> LoadEntityIds(string formId, string entityKey, long[] billIds)
        {
            HashSet<long> entityIds = new HashSet<long>();
            if (entityKey.EqualsIgnoreCase("FBillHead"))
            {// 需要读取的实体为单据头,直接返回单据内码集合,无需到数据库读取
                foreach (var billId in billIds)
                {
                    entityIds.Add(billId);
                }
            }
            else
            {
                // 读取单据的元数据
                FormMetadata meta = MetaDataServiceHelper.Load(this.Context, formId) as FormMetadata;
                Entity entity = meta.BusinessInfo.GetEntity(entityKey);
                // 构建取数服务参数,仅加载实体内码字段
                QueryBuilderParemeter queryParam = new QueryBuilderParemeter();
                queryParam.FormId = formId;
                queryParam.BusinessInfo = meta.BusinessInfo;
                queryParam.SelectItems.Add(
                    new SelectorItemInfo(string.Format("{0}_{1}", entityKey, entity.EntryPkFieldName)));
                queryParam.FilterClauseWihtKey = string.Format("{0} in ({1})",
                    meta.BusinessInfo.GetForm().PkFieldName, string.Join(",", billIds));
                // 读取数据
                var rows = QueryServiceHelper.GetDynamicObjectCollection(this.Context, queryParam);
                
                // 把取到的实体内码返回
                foreach(var row in rows)
                {
                    entityIds.Add(Convert.ToInt64(row[0]));
                }
            }
            return entityIds;
        }
        /// <summary>
        /// 到业务流程实例中,基于目标单,寻找指定单据类型的源单
        /// </summary>
        /// <param name="insts">业务流程实例</param>
        /// <param name="srcFormId">源单formId</param>
        /// <param name="srcEntityKey">源单实体key</param>
        /// <param name="targetFormId">目标单formId</param>
        /// <param name="targetEntityKey">目标实体key</param>
        /// <param name="targetEntityIds">目标单实体内码集合</param>
        /// <returns></returns>
        private HashSet<long> GetSrcEntityIdsByInsts(BusinessFlowInstanceCollection insts,
            string srcFormId, string srcEntityKey,
            string targetFormId, string targetEntityKey, HashSet<long> targetEntityIds)
        {
            HashSet<long> srcEntityIds = new HashSet<long>();
            if (insts == null || insts.Count == 0)
            {
                return srcEntityIds;
            }
            // 到业务流程实例中,寻找目标单所在的节点
            string targetTableNumber = this.LoadTableNumber(targetFormId, targetEntityKey);
            List<RouteTreeNode> targetNodes = new List<RouteTreeNode>();
            foreach (var inst in insts)
            {
                var nodes = inst.SerarchTargetFormNodes(targetTableNumber);
                foreach(var node in nodes)
                {
                    long targetEntityId = node.Id.EId;
                    if (targetEntityIds.Contains(targetEntityId))
                    {
                        targetNodes.Add(node);
                    }
                }
            }
            // 基于目标单所在的节点,反查指定的源单
            string srcTableNumber = this.LoadTableNumber(srcFormId, srcEntityKey);
            foreach (var targetNode in targetNodes)
            {
                // 不断往上追溯,寻找指定类型的源单
                RouteTreeNode parentNode = targetNode.ParentNode;
                while (parentNode != null)
                {
                    if (parentNode.Id.Tbl.EqualsIgnoreCase(srcTableNumber))
                    {// 此父节点,就是需要的源单类型,取值
                        if (srcEntityIds.Contains(parentNode.Id.EId) == false)
                        {
                            srcEntityIds.Add(parentNode.Id.EId);
                        }
                    }
                    parentNode = parentNode.ParentNode;
                }
            }
            return srcEntityIds;
        }
        /// <summary>
        /// 获取单据关联主实体的业务流程实例:业务流程实例记录了每行单据体的来龙去脉
        /// </summary>
        /// <param name="formId">单据</param>
        /// <param name="entityKey">关联主实体Key</param>
        /// <param name="entityIds">关联主实体内码集合</param>
        /// <returns></returns>
        private BusinessFlowInstanceCollection LoadBFInstances(string formId, string entityKey, long[] entityIds)
        {
            LoadInstancesByEntityIdArgs args = new LoadInstancesByEntityIdArgs(formId, entityKey, entityIds);
            var instances = BusinessFlowDataServiceHelper.LoadInstancesByEntityId(this.Context, args);
            return instances;
        }
        /// <summary>
        /// 获取单据体的表格编码
        /// </summary>
        /// <param name="formId">单据FormId</param>
        /// <param name="entityKey">实体Key;如果关联主实体是单据头,请传入FBillHead</param>
        /// <returns>新建的数据库,从未保存过此业务单据时,表格编码可能为空</returns>
        /// <remarks>
        /// 在业务流程跟踪关系中,每个单据的单据体都有一个唯一的表格编码。
        /// 默认是单据体的物理表格名。
        /// 但在此物料表格被多个单据公用时,会在物理表名后面增加序号以示区分
        /// </remarks>
        private string LoadTableNumber(string formId, string entityKey)
        {
            var tableDefine = BusinessFlowServiceHelper.LoadTableDefine(this.Context, formId, entityKey);
            if (tableDefine == null)
            {
                return string.Empty;
            }
            else
            {
                return tableDefine.TableNumber;
            }
        }
        /// <summary>
        /// 显示采购订单列表
        /// </summary>
        /// <param name="poBillIds"></param>
        private void ShowPOList(long[] poBillIds)
        {
            ListShowParameter showParam = new ListShowParameter();
            showParam.FormId = "PUR_PurchaseOrder";
            showParam.ListFilterParameter.Filter = 
                string.Format(" FID in ({0}) ", string.Join(",", poBillIds));
            this.View.ShowForm(showParam);
        }
    }
}

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