知识分享 - 付款申请单,如何追查跨级源头采购订单
金蝶云社区-JohnnyDing
JohnnyDing
2人赞赏了该文章 4,202次浏览 未经作者许可,禁止转载编辑于2015年12月02日 10:44:54

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

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

技术难点:
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);
}
}

///


/// 搜索源头采购订单,并显示列表
///

/// 付款申请单单据内码
private void DoSearchSrcPO(long payAppBillId)
{
// 读取付款申请单.明细内码
HashSet 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 apPlanEntityIds = this.GetSrcEntityIdsByInsts(payAppInsts,
"AP_Payable", "FEntityPlan",
"CN_PAYAPPLY", "FPAYAPPLYENTRY", payAppEntityIds);

if (apPlanEntityIds.Count == 0)
{
this.View.ShowMessage("没有找到源头的应付单,线索中断,不能继续追查采购订单");
return;
}

// 根据应付单.付款计划内码,找到应付单.物料明细内码
HashSet apBillIds = this.LoadBillIds("AP_Payable", "FEntityPlan", apPlanEntityIds.ToArray());
HashSet apDetailIds = this.LoadEntityIds("AP_Payable", "FEntityDetail", apBillIds.ToArray());

// 加载应付单.物料明细的业务流程实例
BusinessFlowInstanceCollection apDetailInsts = this.LoadBFInstances(
"AP_Payable", "FEntityDetail", apDetailIds.ToArray());

// 取应付单源头的采购订单物料明细内码
HashSet poEntityIds = this.GetSrcEntityIdsByInsts(apDetailInsts,
"PUR_PurchaseOrder", "FPOOrderEntry",
"AP_Payable", "FEntityDetail", apDetailIds);

if (poEntityIds.Count == 0)
{
this.View.ShowMessage("没有找到源头的采购订单!");
return;
}

// 取源头的采购订单内码
HashSet poBillIds = this.LoadBillIds("PUR_PurchaseOrder", "FPOOrderEntry", poEntityIds.ToArray());

// 显示出搜索出来的采购订单列表
this.ShowPOList(poBillIds.ToArray());
}

///
/// 根据单据体内码,读取其单据内码
///

///
///
///
///
private HashSet LoadBillIds(string formId, string entityKey, long[] entityIds)
{

HashSet billIds = new HashSet();
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;
}

///
/// 读取指定单据的实体内码返回
///

/// 单据formId
/// 实体Key
/// 单据内码集合
///
private HashSet LoadEntityIds(string formId, string entityKey, long[] billIds)
{
HashSet entityIds = new HashSet();
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;
}

///
/// 到业务流程实例中,基于目标单,寻找指定单据类型的源单
///

/// 业务流程实例
/// 源单formId
/// 源单实体key
/// 目标单formId
/// 目标实体key
/// 目标单实体内码集合
///
private HashSet GetSrcEntityIdsByInsts(BusinessFlowInstanceCollection insts,
string srcFormId, string srcEntityKey,
string targetFormId, string targetEntityKey, HashSet targetEntityIds)
{
HashSet srcEntityIds = new HashSet();

if (insts == null || insts.Count == 0)
{
return srcEntityIds;
}

// 到业务流程实例中,寻找目标单所在的节点
string targetTableNumber = this.LoadTableNumber(targetFormId, targetEntityKey);
List targetNodes = new List();
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;
}

///
/// 获取单据关联主实体的业务流程实例:业务流程实例记录了每行单据体的来龙去脉
///

/// 单据
/// 关联主实体Key
/// 关联主实体内码集合
///
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;
}

///
/// 获取单据体的表格编码
///

/// 单据FormId
/// 实体Key;如果关联主实体是单据头,请传入FBillHead
/// 新建的数据库,从未保存过此业务单据时,表格编码可能为空
///
/// 在业务流程跟踪关系中,每个单据的单据体都有一个唯一的表格编码。
/// 默认是单据体的物理表格名。
/// 但在此物料表格被多个单据公用时,会在物理表名后面增加序号以示区分
///

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;
}
}

///
/// 显示采购订单列表
///

///
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);
}
}
}