
## 问题描述
缺料分析如何在完成分析后,附加统计计算相关的采购订单的未入库数量
## 实现方案
本插件的目的主要是基于缺料分析的分析功能完成后,按照缺料分析加载的供应表获取采购订单类型的供应,获取相关联的未审核采购入库单统计数量。原理是:在分析完成后自动调用的保存事件中加入二开插件执行客户化逻辑。注:本方案仅支持补丁号 PT-149006([8.1.0.20230608] 发布时间:2023/6/8)以后的版本,往前的历史版本获取不到每个子项分配的供应条目。
## 插件样例
扩展缺料分析单,在保存操作上注册新插件,源码参考以下:
其中,detailItems包含的属性可以参考表单【缺料分析子项明细】业务对象,需要加属性可以扩展该表单进行添加
供应表PrepareMtrlCalcBillView.SupplyData可以参考表单【备料运算单据】业务对象的页签分录【供给数据信息】,判定供应是什么类型按照一下属性
RelationFormId//供应单类型
BaseTotalQty;//该供应条目的总供应数量统计
RelationBillNo;//供应的单据编号
RelationInterId;//供应的单据内码
RelationEntryId;//单据分录内码
RelationFormId_Id;//供应单据的FORMID,如即时库存为STK_INVENTORY,采购订单为PUR_PurchaseOrder...
```csharp
using Kingdee.BOS;
using Kingdee.BOS.Core.DynamicForm.PlugIn;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Core.Metadata;
using Kingdee.BOS.Core.SqlBuilder;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.ServiceHelper;
using Kingdee.BOS.Util;
using Kingdee.K3.Core.MFG;
using Kingdee.K3.Core.MFG.EntityHelper;
using Kingdee.K3.MFG.Common.BusinessEntity.PRD;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SampleAppPlugIn
{
public class LackSaveGetNonAuditStocks : AbstractOperationServicePlugIn
{
//每个子项分配到的供应
Dictionary<string, List<PrepareMtrlCalcBillView.SupplyData>> dicSupplyDatas = new Dictionary<string, List<PrepareMtrlCalcBillView.SupplyData>>();
//每个子项现有的非库存预留关系
Dictionary<string, List<PrepareMtrlCalcBillView.SupplyData>> dicReserveLinkDatas = new Dictionary<string, List<PrepareMtrlCalcBillView.SupplyData>>();
Dictionary<long, DynamicObject> dicPPBOMEntrys = new Dictionary<long, DynamicObject>();
Dictionary<long, DynamicObject> dicSUBPPBOMEntrys = new Dictionary<long, DynamicObject>();
public override void BeginOperationTransaction(BeginOperationTransactionArgs e)
{
if (!this.Option.ContainsVariable("taskId"))
return;
if (!this.Option.ContainsVariable("CalModelData"))
return;
DynamicObject calModelData = this.Option.GetVariableValue<DynamicObject>("CalModelData");
if (calModelData == null)
{
return;
}
//全部自由匹配的供应
DynamicObjectCollection supplyDatas = calModelData.GetDynamicValue<DynamicObjectCollection>("SupplyData");
if (supplyDatas.IsEmpty())
{
return;
}
//按照子项明细分配的供应记录,包含:Item1子项明细数据包,Item2自由匹配的供应记录,Item3当前供应给当前子项明细的分配数量。
var supplyDetails = this.Option.GetVariableValue<List<Tuple<DynamicObject, PrepareMtrlCalcBillView.SupplyData, decimal>>>("SubSupplyDetails");
//还原已经按照标准产品逻辑摊分的供应数量
foreach (var supplyDetail in supplyDetails)
{
supplyDetail.Item2.BaseQty = supplyDetail.Item2.BaseSupplyQty = supplyDetail.Item2.BaseTotalQty;
}
dicSupplyDatas = supplyDetails.GroupBy(x => x.Item1.GetDynamicValue<string>("RowId")).ToDictionary(x => x.Key, v => v.Select(x => x.Item2).OrderBy(x => x.Priority).ToList());
//按照子项明细收集的非库存预留关系,包含:Item1子项明细数据包,Item2当次计算加载的预留关系,Item3当前预留供应给当前子项明细的分配数量
var reserveLinkSupplys = this.Option.GetVariableValue<List<Tuple<DynamicObject, PrepareMtrlCalcBillView.SupplyData, decimal>>>("ReserveLinkSupplys");
dicReserveLinkDatas = reserveLinkSupplys.GroupBy(x => x.Item1.GetDynamicValue<string>("RowId")).ToDictionary(x => x.Key, v => v.Select(x => x.Item2).ToList());
var MoEntrys = e.DataEntitys[0]["Entity"] as DynamicObjectCollection;
var dicMoEntrys = MoEntrys.GroupBy(x => string.Format("{0}_{1}_{2}_{3}", x.GetDynamicValue<string>("MoBillNo"), x.GetDynamicValue<long>("MoEntrySeq"), x.GetDynamicValue<long>("MoId"),
x.GetDynamicValue<long>("MoEntryId"))).ToDictionary(x => x.Key);
long fid = e.DataEntitys[0].GetDynamicValue<long>("Id");
var matchItemMeta = MetaDataServiceHelper.Load(this.Context, "PRD_PMPPBOMENTRY") as FormMetadata;
var qp = new QueryBuilderParemeter()
{
FormId = "PRD_PMPPBOMENTRY",
};
qp.FilterClauseWihtKey = string.Format("FID={0}", fid);
qp.SelectItems = SelectorItemInfo.CreateItems("FENTRYID");
var detailItems = BusinessDataServiceHelper.Load(this.Context, matchItemMeta.BusinessInfo.GetDynamicObjectType(), qp);//缺料子项明细
//收集采购类供应的采购订单内码
List<long> poIds = supplyDatas.Where(x => x.GetDynamicValue<string>("RelationFormId_Id")
== MFGFormIdConst.SubSys_SCM.PUR_PurchaseOrder).Select(x => x.GetDynamicValue<long>("RelationInterId")).ToList();
if (poIds.IsEmpty()) return;
var purchaseStockInDatas = GetPurchaseStockInReference(poIds);//获取相关的未入库数量数据
if (purchaseStockInDatas.IsEmpty()) return;
Dictionary<string, IGrouping<string, DynamicObject>> dicPurStockInDatas = purchaseStockInDatas.GroupBy(x => x.GetDynamicValue<string>("FSID")).ToDictionary(x => x.Key);
foreach (var item in detailItems)//循环缺料分析的子项明细,统计未入库数量
{
string rowId = item.GetDynamicValue<string>("rowId");
List<PrepareMtrlCalcBillView.SupplyData> itemSupplyDatas;
if (dicSupplyDatas.TryGetValue(rowId, out itemSupplyDatas))
{
List<string> poEntryIds = itemSupplyDatas.Where(x => x.RelationFormId_Id == MFGFormIdConst.SubSys_SCM.PUR_PurchaseOrder).Select(x => x.RelationEntryId).Distinct().ToList();
if (poEntryIds.IsEmpty()) continue;
decimal nonAuditInstockQty = 0;
foreach (var poEntryId in poEntryIds)
{
IGrouping<string, DynamicObject> v;
if (dicPurStockInDatas.TryGetValue(poEntryId, out v))
{
nonAuditInstockQty = nonAuditInstockQty + v.Sum(x => x.GetDynamicValue<decimal>("FBASEUNITQTY"));
}
}
item["未入库数量字段属性名"] = nonAuditInstockQty;//须先在缺料分析子项明细表单添加数量字段后,把字段的【动态属性名】填入对应位置进行赋值。
}
}
var result = BusinessDataServiceHelper.Save(this.Context, detailItems);//保存修正后的子项明细
}
protected List<DynamicObject> GetPurchaseStockInReference(List<long> pOIds)
{
if (pOIds.IsEmpty()) return new List<DynamicObject>();
string strSql = @"
SELECT FSID,A.FBASEUNITQTY,'P' AS INSTKTYPE FROM T_STK_INSTOCKENTRY A
INNER JOIN T_STK_INSTOCK C ON A.FID = C.FID
INNER JOIN T_STK_INSTOCKENTRY_LK B ON A.FENTRYID = B.FENTRYID
INNER JOIN TABLE(fn_StrSplit(@poOrderId,',',1)) ts ON B.FSBILLID = ts.FID
WHERE A.FSRCBILLTYPEID = 'PUR_PurchaseOrder' AND C.FDOCUMENTSTATUS IN ('A','B','D') AND C.FCANCELSTATUS = 'A' --通过直接采购入库统计的未审核入库数
UNION ALL
SELECT F.FENTRYID AS FSID,SUM(A.FBASEUNITQTY) AS FBASEUNITQTY,'R' AS INSTKTYPE FROM T_STK_INSTOCKENTRY A
INNER JOIN T_STK_INSTOCK C ON A.FID = C.FID
INNER JOIN T_STK_INSTOCKENTRY_LK B ON A.FENTRYID = B.FENTRYID
INNER JOIN T_PUR_RECEIVEENTRY D ON D.FENTRYID = B.FSID
INNER JOIN T_PUR_RECEIVEENTRY_LK E ON E.FENTRYID = D.FENTRYID
INNER JOIN t_PUR_POOrderEntry F ON F.FENTRYID = E.FSID
INNER JOIN TABLE(fn_StrSplit(@poOrderId1,',',1)) ts ON F.FID = ts.FID
WHERE A.FSRCBILLTYPEID='PUR_ReceiveBill' AND C.FDOCUMENTSTATUS IN ('A','B','D') AND C.FCANCELSTATUS = 'A' --通过收料单间接入库统计的未审核入库数量
group by F.FENTRYID";
SqlParam[] sqlParams = new[] {
new SqlParam("@poOrderId",KDDbType.udt_inttable,pOIds.Distinct().ToArray()),
new SqlParam("@poOrderId1",KDDbType.udt_inttable,pOIds.Distinct().ToArray())
};
var result = DBServiceHelper.ExecuteDynamicObject(Context, strSql, paramList: sqlParams);
return result == null ? new List<DynamicObject>() : result.ToList();
}
}
}
```