案例分享,自定义API进行采购收料单拆分入库
金蝶云社区-罗建荣
罗建荣
3人赞赏了该文章 1,918次浏览 未经作者许可,禁止转载编辑于2017年10月21日 16:14:09

业务背景:仓管员手持移动设备,对采购收料进行实际入库,在手机APP、PDA等移动设备上选择收料单,录入仓库、仓位、数量等信息,移动设备回传数据到K\3 Cloud端,Cloud自动进行收料通知单下推采购入库单,并根据传过来的仓库、仓位、数量信息进行保存、拆分分录等操作。
下面讲解怎么自定义API处理将收料单的一条分录进行下推,并进行修改数据和拆分入库分录。
根据需求,我们定义好客户端(发送端/移动设备app端)需要发送的数据字段:
收料单分录id,入库数量,入库仓库,入库仓位。
因为一个收料单分录可能入到不同的仓库仓位,所以我们这里一个收料单分录要可以对应多个入库数量、入库仓库、仓位。

1.客户端发送数据
//客户端发送的json数据
JObject json_root = new JObject();
JArray json_entrys = new JArray();
json_root.Add("entrys", json_entrys);

//收料单分录id为100016
JObject json_entry = new JObject();
json_entry.Add("recEntryId", 100016);

//第一个入库 为数量5,仓库编码为CK_001 ,仓位(id)为100005
JArray json_entrys_details = new JArray();
JObject json_entrys_detail_item = new JObject();
json_entrys_detail_item.Add("qty",5);
json_entrys_detail_item.Add("stocknumber", "CK_001");
json_entrys_detail_item.Add("stockLocId", 100005);
json_entrys_details.Add(json_entrys_detail_item);

//第一个入库 为数量6,仓库编码为CK_001 ,仓位(id)为100006
json_entrys_detail_item = new JObject();
json_entrys_detail_item.Add("qty",6);
json_entrys_detail_item.Add("stocknumber", "CK_001");
json_entrys_detail_item.Add("stockLocId", 100006);
json_entrys_details.Add(json_entrys_detail_item);

json_entry.Add("details", json_entrys_details);

json_entrys.Add(json_entry);

数据结构格式后结果:

这段json数据结构的意思是要将分录id为100016收料单分录进行下推入库单,并拆分出共两条入库单分录。其中一条仓库为CK_001仓位为100005,数量为5,另外一条仓库为CK_001仓位为100006,数量为6 。
2.服务端接收数据
因为cloud通版的api并不支持这类需求,因此需要自己写自定义接口来处理。
首先我们自己新建一个代码类库项目,引用相关的bos组件,然后新建一个接口,继承BOS相关的接口服务。
InstockService : KDBaseService
2.1在自定义接口中,我们搜集下传入的收料单分录id,然后调用下推服务进行下推采购入库单。
JObject jObj_Root = JObject.Parse(parameter);

List recData = new List();
List srcEntryIds = new List();
JArray jArr_recDataEntrys = jObj_Root["entrys"] as JArray;

foreach (JObject item in jArr_recDataEntrys)
{
long recEntryId = GetNullableLong(item, "recEntryId");
var jObj_entry_detail = item["details"] as JArray;
foreach (JObject detailItem in jObj_entry_detail)
{
decimal qty = GetNullableDecimal(detailItem, "qty");
string stockNumber = Convert.ToString(detailItem["stocknumber"]);
long stockLocId = GetNullableLong(detailItem, "stockLocId");

recData.Add(new RecData()
{
RecEntryId = recEntryId,
Qty = qty,
StockNumber = stockNumber,
StockLocId = stockLocId
});
}
srcEntryIds.Add(recEntryId);
}

srcEntryIds = srcEntryIds.Distinct().ToList();

//通版根据收料单下推入库单单,一行收料单只会生成一行入库单。需要在后面代码中处理关联复制行
DynamicObject[] instockBillDataArr = PushRecBills(this.KDContext.Session.AppContext, srcEntryIds);
if (instockBillDataArr == null || instockBillDataArr.Length <= 0)
{
throw new Exception("下推失败,请检查输入参数");
}

2.2 调用下推后,下推接口会返回下推的入库单的数据包。因为通版的下推功能一行收料单分录只会生成一行入库单,所以我们要处理下推后的数据包,根据客户端发来的json数据的拆分行数量对入库单分录数据进行相当于关联复制行的拆分。即客户端要求拆多少条,我们就关联复制行多少次。
//入库单单据
foreach (var billItem in instockBillDataArr)
{
//入库单分录
var entrys = billItem["InStockEntry"] as DynamicObjectCollection;
List> linkCopyCount = new List>();
int index = 0;
foreach (var entryItem in entrys)
{
DynamicObjectCollection links = entryItem["FInStockEntry_Link"] as DynamicObjectCollection;
//从link数据找到上游单据分录id,不考虑合并情况
long srcEntryId = Convert.ToInt64(links[0]["SID"]);

var recEntryData = jArr_recDataEntrys.Where(p => GetNullableLong((JObject)p, "recEntryId") == srcEntryId).FirstOrDefault();

var jObj_entry_detail = recEntryData["details"] as JArray;
int needCopyCount = jObj_entry_detail.Count - 1;

if (needCopyCount > 0)
{
linkCopyCount.Add(new Tuple(index, needCopyCount));
}
index++;
}

//关联复制数据
foreach (var tupleItem in linkCopyCount)
{
int indexSeq = tupleItem.Item1;
DynamicObject needCopyDy = entrys[indexSeq];

for (int i = 0; i < tupleItem.Item2; i++)
{
DynamicObject copyedDy = (DynamicObject)Kingdee.BOS.Orm.OrmUtils.Clone(needCopyDy);
copyedDy["seq"] = entrys.Count + 1;

entrys.Add(copyedDy);
}
}

}

2.3 暂存下拆分分录后的入库单数据包

//暂存采购入库单,暂存后才会有内码
IOperationResult draftResult = DraftInstockData(instockBillDataArr);

//private IOperationResult DraftInstockData(DynamicObject[] instockBillDataArr)
//{
// //暂存目标单数据
// OperateOption option = OperateOption.Create();
// //option.SetIgnoreWarning(false);
// FormMetadata formMetaData = (FormMetadata)MetaDataServiceHelper.Load(this.KDContext.Session.AppContext, InstockBillFormId);
// return BusinessDataServiceHelper.Draft(this.KDContext.Session.AppContext, formMetaData.BusinessInfo, instockBillDataArr, option);
//}

2.4 调用通版的入库单保存api对入库单分录进行修改(修改数量、仓库、仓位)
if (draftResult.IsSuccess == false)
{
throw new Exception("暂存采购入库单失败!");
}

List lingTracks = new List();
foreach (var billItem in draftResult.SuccessDataEnity)
{
long instockBillId = Convert.ToInt64(billItem["id"]);
var entrys = billItem["InStockEntry"] as DynamicObjectCollection;
foreach (var entryItem in entrys)
{
long instockEntryId = Convert.ToInt64(entryItem["id"]);
DynamicObjectCollection links = entryItem["FInStockEntry_Link"] as DynamicObjectCollection;
foreach (var linkItem in links)
{
long srcEntryId = Convert.ToInt64(linkItem["SID"]);

lingTracks.Add(new InstockData()
{
InstockBillId = instockBillId,
InstockEntryId = instockEntryId,
RecEntryId = srcEntryId,
});
}
}
}

//根据入库单fid分组,有可能生成多个单据
var billGroupDatas = lingTracks.GroupBy(p => p.InstockBillId);

foreach (var item in billGroupDatas)
{
JObject oJobj_Bill = new JObject();
JObject oJobj_Model = new JObject();
JArray entrys = new JArray();

oJobj_Model.Add("FID", item.Key);

foreach (var subItem in item)
{
var recDetalItem = recData.Where(p => p.RecEntryId == subItem.RecEntryId && p.IsUsed == false).FirstOrDefault();
if (recDetalItem == null)
{
continue;
}

recDetalItem.IsUsed = true;

JObject entryRow = new JObject();
entryRow.Add("FEntryId", subItem.InstockEntryId);
entryRow.Add("FRealQty", recDetalItem.Qty);

JObject baseData = new JObject();
baseData.Add("FNumber", recDetalItem.StockNumber);
entryRow.Add("FStockId", baseData);

entryRow.Add("FStockLocId", recDetalItem.StockLocId);

entrys.Add(entryRow);
}

if (entrys.Count > 0)
{
oJobj_Model.Add("FInStockEntry", entrys);

oJobj_Bill.Add("Model", oJobj_Model);

string jsonStr = oJobj_Bill.ToString();
object saveResult = WebApiServiceCall.Save(this.KDContext.Session.AppContext, InstockBillFormId, jsonStr);
}
}

自此,全部处理就完成了。
入库结果如下:

服务端全部代码在附件中,欢迎交流。