在ERP与WMS对接的业务场景中,需要实现WMS出入库时调用星空的源单下推出入库单据。提出了两种API接口方案:调用星空下推接口或带关联关系的保存接口。根据单据转换需求,详细说明了发货通知单下推销售出库单的三种调用层次:完整下推、按行下推并修改、及因批号等原因需拆分行并重建数据包的处理方法,包括请求JSON中的CustomParams参数的使用、单据转换插件的实现逻辑以及如何在下推后修改数据包。
业务场景:ERP与WMS对接时,常会需要实现在WMS出入库,同时调用星空的源单下推出入库单据。
解决方案:API接口有两种方案:一是调用星空下推接口,二是调用星空带关联关系的保存接口。
如果考虑以后有配置单据转换,从源单 带信息到 下游单据,则使用下推接口更合适。
发货通知下推销售出库下推接口的调用有三个层次:
一、按发货通知单信息完整下推,销售出库需要的字段都在发货通知单上保存,则调用标准的下推接口即可。
即:
{
"Ids": "",
"Numbers": [],
"EntryIds": "",
"RuleId": "",
"TargetBillTypeId": "",
"TargetOrgId": 0,
"TargetFormId": "",
"IsEnableDefaultRule": "false",
"IsDraftWhenSaveFail": "false"
}
二、按发货通知单行下推,不存在拆分行,但需要在销售出库单上录入信息。
则需要单据转换插件+请求JSON的 "CustomParams": 包。
{ "Ids": "", "Numbers": [], "EntryIds": "100279,100279", "RuleId": "DeliveryNotice-OutStock", "TargetBillTypeId": "", "TargetOrgId": 0, "TargetFormId": "", "IsEnableDefaultRule": "false", "IsDraftWhenSaveFail": "false", "CustomParams": { "EtyIds": ["100279","100279"], "QmEntryID": ["100173","100174"], "StockId": ["100205","100205"], "Lot": ["PC52121414XJKP201031","PC52121414XJKP201104"], "RealQty": [109,89] } } |
单据转换插件中实现取参数值,并修改下推后的数据包
取参数值 bool Entryid = this.Option.TryGetVariableValue<JSONArray>("EtyIds", out Ids); bool Stock = this.Option.TryGetVariableValue<JSONArray>("StockId", out custStock); bool Lot = this.Option.TryGetVariableValue<JSONArray>("Lot", out custLot); bool Qty = this.Option.TryGetVariableValue<JSONArray>("RealQty", out custQty); bool QmEntryID = this.Option.TryGetVariableValue<JSONArray>("QmEntryID", out qmEntryID);
取下推后数据包 var entityRows = e.TargetExtendedDataEntities.FindByEntityKey("FEntity"); // 销售出库单单据体标识 if (entityRows != null) { foreach (var entityRow in entityRows) { var entityId = (entityRow.DataEntity["FEntity_Link"] as DynamicObjectCollection)[0]["SId"]; //利用关系表获取分录当前行id int i = 0; foreach (var id in Ids) { 修改数据包 if (Qty) { entityRow.DataEntity["RealQty"] = custQty[i]; //修改实收数量 entityRow.DataEntity["PriceUnitQty"] = custQty[i]; //修改计价数量 entityRow.DataEntity["PriceBaseQty"] = custQty[i]; //修改计价基本数量 //entityRow.DataEntity["ExtAuxUnitQty"] = custQty[i]; //修改实收数量(辅单位)数量 entityRow.DataEntity["BaseunitQty"] = custQty[i]; //修改库存基本数量 entityRow.DataEntity["SalUnitQty"] = custQty[i]; //修改销售数量 entityRow.DataEntity["SalBaseQty"] = custQty[i]; //修改销售基本数量 }
三、按发货通知单下推,但因批号、质检等原因,需要拆分行,且标准产品还因库存原因 拣货了
则需要对下推后数据包 重新按 "CustomParams": 包 组织行。
1、先循环标准产生生成的数据包,删除 拆分出来的行,确保每行发货通知单只一行
for(int i = 0 ;i < entityDatas.Count;i++) { DynamicObject entityData = entityDatas[i]; DynamicObjectCollection linkRows = linkEntity.DynamicProperty.GetValue(entityData) as DynamicObjectCollection; string strQmEntryID = entityData["QmEntryID"].ToString(); string srcEntryId = ""; foreach (var linkRow in linkRows) { if (QmEntryID) { srcEntryId = linkRow["SId"].ToString() + strQmEntryID; } else { srcEntryId = linkRow["SId"].ToString(); } if (!srcEntryId.IsNullOrEmptyOrWhiteSpace()) { if (srcEntryIds.Contains(srcEntryId) || QmEntryID && !strqmEntryID.Contains(strQmEntryID)) { //需要删除的行 delEntrys.Add(i, entityData); } else { srcEntryIds.Add(srcEntryId); } } } }
2、删行,并且清除实体
//删除重复行, foreach (var delEntry in delEntrys) { //要删除单据数据包(FBillHead)的表体行 , entityDatas.Remove(delEntry.Value); // 删除 表体数据包 的行 e.TargetExtendedDataEntities.RemoveExtendedDataEntity("FEntity", delEntry.Key); }
3、根据"CustomParams": 包重建实体行,并修改数据
//循环CustomParams数据包,以数据包行数为准 foreach (var id in Ids) { if (QmEntryID) { strQMandSRCEntryID = id.ToString() + qmEntryID[j].ToString(); } else { strQMandSRCEntryID = id.ToString(); } if (!srcEntryIds.Contains(strQMandSRCEntryID)) { throw new Exception($"CustomParams数据包第{j + 1}行数据【"+ id.ToString() + "】找不到对应的上游单据"); } //循环源单表体,找到正确的源单行 for (int i = 0; i < entityLength; i++) { ExtendedDataEntity entityRow = entityRows[i]; var entityId = (entityRow.DataEntity["FEntity_Link"] as DynamicObjectCollection)[0]["SId"]; //利用关系表获取分录当前行id string strRowQMEntryID = entityRow["QmEntryID"].ToString(); if (id.Equals(entityId) && (qmEntryID == null || qmEntryID != null && qmEntryID[j].Equals(strRowQMEntryID))) { //本行已经更新过 if (isUpdateRows.ContainsKey(i)) { //复制行,并更新 DynamicObject newRowObj = (DynamicObject)entityDatas[i].Clone(false, true); //新增数据包行 entityDatas.Insert(entityDatas.Count, newRowObj); //构建实体行 ExtendedDataEntity newEntityRow = new ExtendedDataEntity(newRowObj, entityRow.DataEntityIndex, entityDatas.Count); UpdateEntity(e.TargetBusinessInfo, newEntityRow, Stock, long.Parse(custStock[j].ToString()), Lot, custLot[j].ToString(), Qty, decimal.Parse(custQty[j].ToString()), strOrgID); newEntityRows.Add(newEntityRow); break; } else { isUpdateRows.Add(i, entityDatas[i]); UpdateEntity(e.TargetBusinessInfo, entityRow,Stock, long.Parse( custStock[j].ToString()), Lot,custLot[j].ToString(), Qty, decimal.Parse(custQty[j].ToString()), strOrgID); BaseDataField stockFld = e.TargetBusinessInfo.GetField("FStockId") as BaseDataField; break; } } } j++; }
最后重新添加 目标数据
e.TargetExtendedDataEntities.AddExtendedDataEntities("FEntity", newEntityRows.ToArray());
这样的单据转换插件+带"CustomParams":包 的接口,就能适应好WMS的复杂应用