using HH.WMS.Common.Algorithm.Out; using HH.WMS.Entitys; using HH.WMS.Entitys.Basic; using HH.WMS.Entitys.Common; using HH.WMS.Entitys.Entitys; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace HH.WMS.Common { public static class CommonExtension { #region 克隆对象 /// /// 克隆对象 /// /// /// /// /// [HanHe(zh)] CREATED 2018/09/16 public static T DeepCloneObject(this T t) where T : class { T model = System.Activator.CreateInstance(); //实例化一个T类型对象 PropertyInfo[] propertyInfos = model.GetType().GetProperties(); //获取T对象的所有公共属性 foreach (PropertyInfo propertyInfo in propertyInfos) { //判断值是否为空,如果空赋值为null见else if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { //如果convertsionType为nullable类,声明一个NullableConverter类,该类提供从Nullable类到基础基元类型的转换 NullableConverter nullableConverter = new NullableConverter(propertyInfo.PropertyType); //将convertsionType转换为nullable对的基础基元类型 propertyInfo.SetValue(model, Convert.ChangeType(propertyInfo.GetValue(t), nullableConverter.UnderlyingType), null); } else { propertyInfo.SetValue(model, Convert.ChangeType(propertyInfo.GetValue(t), propertyInfo.PropertyType), null); } } return model; } public static IList DeepCloneList(this IList tList) where T : class { IList listNew = new List(); foreach (var item in tList) { T model = System.Activator.CreateInstance(); //实例化一个T类型对象 PropertyInfo[] propertyInfos = model.GetType().GetProperties(); //获取T对象的所有公共属性 foreach (PropertyInfo propertyInfo in propertyInfos) { //判断值是否为空,如果空赋值为null见else if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { //如果convertsionType为nullable类,声明一个NullableConverter类,该类提供从Nullable类到基础基元类型的转换 NullableConverter nullableConverter = new NullableConverter(propertyInfo.PropertyType); //将convertsionType转换为nullable对的基础基元类型 propertyInfo.SetValue(model, Convert.ChangeType(propertyInfo.GetValue(item), nullableConverter.UnderlyingType), null); } else { propertyInfo.SetValue(model, Convert.ChangeType(propertyInfo.GetValue(item), propertyInfo.PropertyType), null); } } listNew.Add(model); } return listNew; } #endregion #region 批分仓库量通用方法 /// /// 批分仓库量通用方法 /// /// /// /// /// 是否生成出库单 /// /// public static OperateResult BatchesStockQty(this List tStockList, List beList, BatchesType bType, StockBatchesField sbField, List lstStrate, bool generateOut = false, bool isDropQty = false) { //锁定明细(反批分分配量时记录) BatchesResult bResult = new BatchesResult(); foreach (var b in beList) { //需要生成出库单时,过滤 if (generateOut && b.IsFilter) continue; //匹配五要素 // p.CN_S_OWNER.Trim().Equals(b.Owner.Trim()) && var cis = tStockList.Where(p => p.CN_S_ITEM_CODE.Equals(b.ItemCode) && p.CN_S_ITEM_STATE.Equals(b.ItemState) && (string.IsNullOrEmpty(b.ItemArrivalLot) || b.ItemArrivalLot.Equals(p.CN_S_LOT_NO)) && (string.IsNullOrEmpty(b.ItemProductionLot) || b.ItemProductionLot.Equals(p.CN_S_PRODUCTION_BATCH))); //暂时先倒序生产批次 then 到货批次 后续取策略 var cisList = cis.OrderBy(p => p.CN_S_LOT_NO).ToList(); foreach (string stegy in lstStrate) { //逐个策略进行计算 switch (stegy) { case "FirstInFirstOut": cisList = cisList.OrderBy(o => o.CN_S_LOT_NO).ToList(); break; case "FirstWarrantFirstOut": var lstEmptyPBC = cisList.Where(o => string.IsNullOrEmpty(o.CN_S_PRODUCTION_BATCH)).ToList(); var lstFilterItem = cisList.Where(o => !string.IsNullOrEmpty(o.CN_S_PRODUCTION_BATCH)).OrderBy(o => o.CN_S_PRODUCTION_BATCH).ToList(); lstFilterItem.AddRange(lstEmptyPBC); cisList = lstFilterItem; break; } } if (!cisList.Any()) return OperateResult.Error("库存中未找到物料:" + b.ItemCode); //待批数 var qty = b.Qty; if (qty == 0) continue; switch (bType) { #region 正批分 //正批分 case BatchesType.ForwardBatch: for (int i = 0; i < cisList.Count; i++) { //批分编辑量 if (sbField == StockBatchesField.EditingQty) { //可用量 = 库存量-分配量-编辑量 var availableQty = cisList[i].CN_F_QUANTITY - cisList[i].CN_F_ALLOC_QTY - cisList[i].CN_F_EDITING_QTY; if (availableQty <= 0) continue; if (availableQty >= qty) { cisList[i].CN_F_EDITING_QTY += qty; qty = 0; break; } else { cisList[i].CN_F_EDITING_QTY += availableQty; qty -= availableQty; continue; } } //批分分配量 else if (sbField == StockBatchesField.AllocationQty) { //可用量 = 库存量-分配量 var availableQty = cisList[i].CN_F_QUANTITY - cisList[i].CN_F_ALLOC_QTY;// -cisList[i].CN_F_EDITING_QTY; if (availableQty <= 0) continue; if (isDropQty) { if (availableQty >= qty) { cisList[i].CN_F_QUANTITY -= qty; qty = 0; break; } else { cisList[i].CN_F_QUANTITY -= availableQty; qty -= availableQty; continue; } } else { if (availableQty >= qty) { cisList[i].CN_F_ALLOC_QTY += qty; //记录锁定明细 var outLockingDtlEntity = GetOutLockingDtlEntity(cisList[i], b.OpNo, qty); bResult.OutLockingDtlList.Add(outLockingDtlEntity); qty = 0; break; } else { cisList[i].CN_F_ALLOC_QTY += availableQty; //记录锁定明细 var outLockingDtlEntity = GetOutLockingDtlEntity(cisList[i], b.OpNo, availableQty); bResult.OutLockingDtlList.Add(outLockingDtlEntity); qty -= availableQty; continue; } } } } break; #endregion #region 反批分 //反批分 case BatchesType.ReverseBatch: for (int i = cisList.Count - 1; i >= 0; i--) { //批分编辑量 if (sbField == StockBatchesField.EditingQty) { var editingQty = cisList[i].CN_F_EDITING_QTY; if (editingQty <= 0) continue; if (editingQty >= qty) { cisList[i].CN_F_EDITING_QTY -= qty; qty = 0; break; } else { cisList[i].CN_F_EDITING_QTY = 0; qty -= editingQty; continue; } } //批分分配量 (反批分分配量,相当于批分出库明细了) else if (sbField == StockBatchesField.AllocationQty) { var allocQty = cisList[i].CN_F_ALLOC_QTY; if (allocQty <= 0) continue; if (allocQty >= qty) { //记录锁定明细 var outLockingDtlEntity = GetOutLockingDtlEntity(cisList[i], b.OpNo, qty); bResult.OutLockingDtlList.Add(outLockingDtlEntity); cisList[i].CN_F_ALLOC_QTY -= qty; qty = 0; break; } else { //记录锁定明细 var outLockingDtlEntity = GetOutLockingDtlEntity(cisList[i], b.OpNo, allocQty); bResult.OutLockingDtlList.Add(outLockingDtlEntity); cisList[i].CN_F_ALLOC_QTY = 0; qty -= allocQty; continue; } } } break; #endregion } #region 批分失败 if (qty > 0) { switch (bType) { case BatchesType.ForwardBatch: switch (sbField) { case StockBatchesField.EditingQty: case StockBatchesField.AllocationQty: return OperateResult.Error("物料批分失败:物料编码:" + b.ItemCode + "库存量不足"); } break; case BatchesType.ReverseBatch: switch (sbField) { case StockBatchesField.EditingQty: return OperateResult.Error("物料反批分失败:物料编码:" + b.ItemCode + "编辑量不足"); case StockBatchesField.AllocationQty: return OperateResult.Error("物料反批分失败:物料编码:" + b.ItemCode + "分配量不足"); } break; } } #endregion } #region 生成出库单 if (generateOut) { //若是反批分分配量,生成出库单信息 IEnumerable> beGroup = beList.GroupBy(p => p.OpNo); foreach (var be in beGroup) { //出库订单 TN_WM_OUT_MSTEntity outMst = be.AsEnumerable().ToList()[0].OutMst; //修改出库订单主子表为‘待出库’ outMst.CN_S_STATE = Constants.State_WaitOut; if (isDropQty) outMst.CN_S_STATE = Constants.State_Completed; outMst.OutDtlList.ForEach(p => { p.CN_S_STATE = outMst.CN_S_STATE; }); bResult.OutMstList.Add(outMst); // 生成出库单号 UserRuleEntity user = new UserRuleEntity() { OrgCode = "0", RuleCode = Constants.Rule_Out }; string outNo = user.GenerateNo(); //将锁定库存改成根据出库单来锁 //if (bResult.OutLockingDtlList.FindAll(x => x.CN_S_FROM_NO == be.Key).Any()) //{ // foreach (var _updateStockDtl in bResult.OutLockingDtlList) // { // if (_updateStockDtl.CN_S_FROM_NO.Equals(be.Key)) // _updateStockDtl.CN_S_FROM_NO = outNo; // } //} //非一进一出,生成出库单 var notAutoInOut = outMst.OutDtlList.FindAll(p => !p.CN_C_AUTO_INOUT); if (notAutoInOut.Any()) { var reduceInventoryMstEntity = GetReduceInventoryMst(outNo, Constants.Rule_OutOrder, outMst); reduceInventoryMstEntity.DTLEntity.AddRange(GetReduceInventoryDtl(outNo, notAutoInOut)); bResult.ReduceInventoryMstList.Add(reduceInventoryMstEntity); } //生成一进一出出库单 var autoInOut = outMst.OutDtlList.FindAll(p => p.CN_C_AUTO_INOUT); if (autoInOut.Any()) { var reduceInventoryMstEntity = GetReduceInventoryMst(outNo, Constants.Auto_InOut, outMst); reduceInventoryMstEntity.DTLEntity.AddRange(GetReduceInventoryDtl(outNo, autoInOut)); bResult.ReduceInventoryMstList.Add(reduceInventoryMstEntity); } } } #endregion return OperateResult.Succeed(null, bResult); } #endregion #region 出库单主表 /// /// 出库单主表 /// /// /// /// /// private static TN_WM_REDUCE_INVENTORY_MSTEntity GetReduceInventoryMst(string opNo, string opClass, TN_WM_OUT_MSTEntity outMst) { var mst = new TN_WM_REDUCE_INVENTORY_MSTEntity() { CN_GUID = Guid.NewGuid().ToString(), CN_S_OP_NO = opNo, CN_S_OP_TYPE = outMst.CN_S_OP_TYPE, CN_S_STOCK_CODE = outMst.CN_S_STOCK_CODE, CN_T_OPERATE = outMst.CN_T_OP_DATE, CN_S_OP_FROM = Constants.Rule_OutOrder, CN_S_OP_CLASS = opClass, CN_S_FROM_NO = outMst.CN_S_OP_NO, CN_S_STATE = Constants.State_WaitOut, CN_S_NOTE = outMst.CN_S_NOTE, CN_S_AUDITOR = outMst.CN_S_AUDITOR, CN_T_AUDIT_TIME = outMst.CN_T_AUDIT_TIME, CN_S_AUDIT_REMARK = outMst.CN_S_AUDIT_REMARK, CN_S_CREATOR = outMst.CN_S_CREATOR, CN_S_CREATOR_BY = outMst.CN_S_CREATOR_BY, CN_T_CREATE = DateTime.Now, CN_S_MODIFY = outMst.CN_S_MODIFY, CN_S_MODIFY_BY = outMst.CN_S_MODIFY_BY, CN_T_MODIFY = DateTime.Now, CN_C_PRINT = "N", }; mst.DTLEntity = new List(); return mst; } #endregion #region 出库单子表 /// /// 出库单子表 /// /// /// /// private static List GetReduceInventoryDtl(string opNo, List outDtlList) { var dtlList = new List(); foreach (var outDtl in outDtlList) { var dtl = new TN_WM_REDUCE_INVENTORY_DTLEntity() { CN_GUID = Guid.NewGuid().ToString(), CN_S_OP_NO = opNo, CN_N_ROW_NO = outDtl.CN_N_ROW_NO, CN_S_SERIAL_NO = outDtl.CN_S_SERIAL_NO, CN_S_OWNER = "", CN_S_ITEM_CODE = outDtl.CN_S_ITEM_CODE, CN_S_ITEM_NAME = outDtl.CN_S_ITEM_NAME, CN_S_ITEM_STATE = outDtl.CN_S_ITEM_STATE, CN_S_LOT_CODE = outDtl.CN_S_LOT_CODE, CN_F_QUANTITY = outDtl.CN_F_QUANTITY, CN_S_NOTE = outDtl.CN_S_NOTE, CN_S_STATE = outDtl.CN_S_STATE, CN_S_FIGURE_NO = outDtl.CN_S_FIGURE_NO, CN_S_MODEL = outDtl.CN_S_MODEL, CN_S_MEASURE_UNIT = outDtl.CN_S_MEASURE_UNIT, CN_F_SALE_PRICE = outDtl.CN_F_SALE_PRICE, CN_F_SALE_MONEY = outDtl.CN_F_SALE_MONEY, CN_C_IS_BALANCE = "N", CN_C_IS_SCRAP = "N" }; dtlList.Add(dtl); } return dtlList; } #endregion #region 根据分拣单批分库区(不通用) /// /// 根据分拣单批分库区(不通用) /// /// /// /// public static OperateResult BatchesAreaQty(this List tAreaList, List sortingList, List lstStrate, bool reverseBatch = false) { var sortingDtlList = new List(); sortingList.ForEach(p => { sortingDtlList.AddRange(p.SortingDtlList); }); if (sortingDtlList.Any()) { foreach (var s in sortingDtlList) { var cia = tAreaList.Where(p => p.CN_S_ITEM_CODE.Equals(s.CN_S_ITEM_CODE) && p.CN_S_ITEM_STATE.Equals(s.CN_S_ITEM_STATE) && p.CN_S_OWNER.Trim().Equals(s.CN_S_OWNER.Trim()) && (string.IsNullOrEmpty(s.CN_S_LOT_NO) || s.CN_S_LOT_NO.Equals(p.CN_S_LOT_NO)) && (string.IsNullOrEmpty(s.CN_S_PRODUCTION_BATCH) || s.CN_S_PRODUCTION_BATCH.Equals(p.CN_S_PRODUCTION_BATCH))); //暂时先倒序生产批次 then 到货批次 后续取策略 var ciaList = cia.OrderBy(p => p.CN_S_LOT_NO).ToList(); foreach (string stegy in lstStrate) { //逐个策略进行计算 switch (stegy) { case "FirstInFirstOut": ciaList = ciaList.OrderBy(o => o.CN_S_LOT_NO).ToList(); break; case "FirstWarrantFirstOut": var lstEmptyPBC = ciaList.Where(o => string.IsNullOrEmpty(o.CN_S_PRODUCTION_BATCH)).ToList(); var lstFilterItem = ciaList.Where(o => !string.IsNullOrEmpty(o.CN_S_PRODUCTION_BATCH)).OrderBy(o => o.CN_S_PRODUCTION_BATCH).ToList(); lstFilterItem.AddRange(lstEmptyPBC); ciaList = lstFilterItem; break; } } if (!ciaList.Any()) return OperateResult.Error("库区中未找到物料:" + s.CN_S_ITEM_CODE); //待批数 var qty = s.CN_F_QUANTITY; if (qty == 0) continue; foreach (var area in ciaList) { if (reverseBatch) { var currentCount = area.CN_F_ALLOC_QTY; if (currentCount <= 0) continue; if (currentCount >= qty) { area.CN_F_QUANTITY -= qty; area.CN_F_ALLOC_QTY -= qty; qty = 0; break; } else { area.CN_F_QUANTITY -= currentCount; area.CN_F_ALLOC_QTY = 0; qty -= currentCount; continue; } } else { //当前可用量 var currentCount = area.CN_F_QUANTITY + area.CN_F_PLANNED_QTY - area.CN_F_ALLOC_QTY; if (currentCount <= 0) continue; if (currentCount >= qty) { area.CN_F_ALLOC_QTY += qty; //outLockList.Add(GetOutLockingDtlEntity(area, s.CN_S_SORTING_NO, qty)); qty = 0; break; } else { area.CN_F_ALLOC_QTY += currentCount; //outLockList.Add(GetOutLockingDtlEntity(area, s.CN_S_SORTING_NO, currentCount)); qty -= currentCount; continue; } } } if (qty > 0) { if (reverseBatch) { return OperateResult.Error("物料反批分失败:物料编码:" + s.CN_S_ITEM_CODE + "在库区中分配量不足"); } else { return OperateResult.Error("物料批分失败:物料编码:" + s.CN_S_ITEM_CODE + "在库区中库存量不足"); } } } return OperateResult.Succeed(); } return OperateResult.Error("未找到分拣数据"); } #endregion #region 根据算法返回结果批分库区(不加分配量,直接减库存量) /// /// 根据算法返回结果批分库区(不加分配量,直接减库存量) /// /// /// /// public static OperateResult BatchesAreaQty(this List tAreaList, List outAreaResultList, List lstStrate) { foreach (var item in outAreaResultList) { if (!item.lstItem.Any()) return OperateResult.Error("未找到库区:" + item.areaCode + "中的物料"); foreach (var s in item.lstItem) { var cia = tAreaList.Where(p => p.CN_S_ITEM_CODE.Equals(s.itemCode) && p.CN_S_ITEM_STATE.Equals(item.itemState) && p.CN_S_OWNER.Trim().Equals(item.ownerName.Trim()) && (string.IsNullOrEmpty(item.batchCode) || item.batchCode.Equals(p.CN_S_LOT_NO)) && (string.IsNullOrEmpty(item.prodBatchCode) || item.prodBatchCode.Equals(p.CN_S_PRODUCTION_BATCH))); //暂时先倒序生产批次 then 到货批次 后续取策略 var ciaList = cia.OrderBy(p => p.CN_S_LOT_NO).ToList(); foreach (string stegy in lstStrate) { //逐个策略进行计算 switch (stegy) { case "FirstInFirstOut": ciaList = ciaList.OrderBy(o => o.CN_S_LOT_NO).ToList(); break; case "FirstWarrantFirstOut": var lstEmptyPBC = ciaList.Where(o => string.IsNullOrEmpty(o.CN_S_PRODUCTION_BATCH)).ToList(); var lstFilterItem = ciaList.Where(o => !string.IsNullOrEmpty(o.CN_S_PRODUCTION_BATCH)).OrderBy(o => o.CN_S_PRODUCTION_BATCH).ToList(); lstFilterItem.AddRange(lstEmptyPBC); ciaList = lstFilterItem; break; } } if (!ciaList.Any()) return OperateResult.Error("库区中未找到物料:" + s.itemCode); //待批数 var qty = s.itemQty; if (qty == 0) continue; foreach (var area in ciaList) { //当前可用量 var currentCount = area.CN_F_QUANTITY + area.CN_F_PLANNED_QTY - area.CN_F_ALLOC_QTY; if (currentCount <= 0) continue; if (currentCount >= qty) { //area.CN_F_ALLOC_QTY += qty; area.CN_F_QUANTITY -= qty; qty = 0; break; } else { //area.CN_F_ALLOC_QTY += currentCount; area.CN_F_QUANTITY -= currentCount; qty -= currentCount; continue; } } if (qty > 0) { return OperateResult.Error("物料批分失败:物料编码:" + s.itemCode + "在库区中库存量不足"); } } } return OperateResult.Succeed(); } #endregion //public static OperateResult DropTrayItemQty(this List beList) //{ // var trayItemMstList = BLLCreator>().GetList(new // { // CN_S_TRAY_CODE = sortingResult.Select(x => x.CN_S_TRAY_CODE).ToList(), // CN_S_ITEM_CODE = sortingResult.Select(x => x.CN_S_ITEM_CODE).ToList() // }); //} #region 批分托盘物料分配量通用方法 /// /// 批分托盘物料分配量通用方法 /// /// /// /// /// public static OperateResult BatchesTrayItemQty(this List trayItemList, List beList, BatchesType bType) { foreach (var b in beList) { //匹配托盘和物料编码 var cis = trayItemList.Where(p => p.CN_S_ITEM_CODE.Equals(b.ItemCode) && p.CN_S_TRAY_CODE.Equals(b.TrayCode)); if (!cis.Any()) return OperateResult.Error("批分失败,托盘:" + b.TrayCode + "中未找到物料:" + b.ItemCode); var cisList = cis.OrderBy(p => p.CN_S_LOT_NO).ToList(); //待批数 var qty = b.Qty; if (qty == 0) continue; switch (bType) { #region 正批分 //正批分 case BatchesType.ForwardBatch: for (int i = 0; i < cisList.Count; i++) { //可用量 = 库存量-分配量-编辑量 var availableQty = cisList[i].CN_F_QUANTITY - cisList[i].CN_F_ALLOC_QTY; if (availableQty <= 0) continue; if (availableQty >= qty) { cisList[i].CN_F_ALLOC_QTY += qty; qty = 0; break; } else { cisList[i].CN_F_ALLOC_QTY += availableQty; qty -= availableQty; continue; } } break; #endregion #region 反批分 //反批分 case BatchesType.ReverseBatch: for (int i = cisList.Count - 1; i >= 0; i--) { var allocQty = cisList[i].CN_F_ALLOC_QTY; if (allocQty <= 0) continue; if (allocQty >= qty) { cisList[i].CN_F_ALLOC_QTY -= qty; qty = 0; break; } else { cisList[i].CN_F_ALLOC_QTY = 0; qty -= allocQty; continue; } } break; #endregion } #region 批分失败 //批分失败 if (qty > 0) { switch (bType) { case BatchesType.ForwardBatch: return OperateResult.Error("批分失败,托盘:" + b.TrayCode + "中物料:" + b.ItemCode + "不足"); case BatchesType.ReverseBatch: return OperateResult.Error("反批分失败,托盘:" + b.TrayCode + "中物料:" + b.ItemCode + "分配量不足"); } } #endregion } return OperateResult.Succeed(); } #endregion #region 获取锁定实体 /// /// 获取锁定实体 /// /// 仓库实体 /// 锁定单据号 /// 锁定数量 /// private static TN_WM_B_OUTLOCKING_DTLEntity GetOutLockingDtlEntity(TN_WM_B_STOCK_QTYEntity stockEntity, string opNo, decimal lockNum) { return new TN_WM_B_OUTLOCKING_DTLEntity() { CN_GUID = Guid.NewGuid().ToString(), CN_S_OP_FROM = Constants.Rule_OutOrder, CN_S_FROM_NO = opNo, CN_S_STOCK_CODE = stockEntity.CN_S_STOCK_CODE, CN_S_OWNER = stockEntity.CN_S_OWNER.Trim(), CN_S_ITEM_CODE = stockEntity.CN_S_ITEM_CODE, CN_S_ITEM_NAME = stockEntity.CN_S_ITEM_NAME, CN_S_ITEM_STATE = stockEntity.CN_S_ITEM_STATE, CN_S_LOT_NO = stockEntity.CN_S_LOT_NO, CN_F_QUANTITY = lockNum, CN_T_LOCK = DateTime.Now, CN_S_TABLE_ID = stockEntity.CN_GUID, CN_S_TABLE_TYPE = "1", CN_S_STATE = Constants.State_Executing, CN_F_SUM_USE = 0, CN_T_USE = DateTime.Now, CN_F_SUM_CANCEL = 0, CN_T_CANCEL = DateTime.Now, }; } private static TN_WM_B_OUTLOCKING_DTLEntity GetOutLockingDtlEntity(TN_WM_B_AREA_QTYEntity areaEntity, string opNo, decimal lockNum) { return new TN_WM_B_OUTLOCKING_DTLEntity() { CN_GUID = Guid.NewGuid().ToString(), CN_S_OP_FROM = Constants.Rule_SortingNo, CN_S_FROM_NO = opNo, CN_S_STOCK_CODE = areaEntity.CN_S_STOCK_CODE, CN_S_OWNER = areaEntity.CN_S_OWNER.Trim(), CN_S_ITEM_CODE = areaEntity.CN_S_ITEM_CODE, CN_S_ITEM_NAME = areaEntity.CN_S_ITEM_NAME, CN_S_ITEM_STATE = areaEntity.CN_S_ITEM_STATE, CN_S_LOT_NO = areaEntity.CN_S_LOT_NO, CN_F_QUANTITY = lockNum, CN_T_LOCK = DateTime.Now, CN_S_TABLE_ID = areaEntity.CN_GUID, CN_S_TABLE_TYPE = "2", CN_S_STATE = Constants.State_Executing, CN_F_SUM_USE = 0, CN_T_USE = DateTime.Now, CN_F_SUM_CANCEL = 0, CN_T_CANCEL = DateTime.Now, }; } #endregion #region 生成单号 /// /// 生成单号 /// /// /// public static string GenerateNo(this UserRuleEntity user) { //string postData = "{\"appCode\":\"" + Constants.appCode + "\",\"ruleName\":\"" + user.RuleCode + "\",\"orgId\":\"" + user.OrgCode + "\",\"orgFlag\":\"" + user.OrgFlag + "\"}"; var postData = new { appCode = "AutoWMS", ruleName = user.RuleCode, orgId = 0, orgFlag = "" }; return WebApiManager.HttpAutoBom_Post("api/BillRule/GenBillNo", JsonConvert.SerializeObject(postData)); } #endregion #region 批分分配量 /// /// 批分分配量 /// /// /// /// /// public static OperateResult BatchesStockAllocQty(this List tStockList, List beList, List lstStrate) { try { //锁定明细(反批分分配量时记录) BatchesResult bResult = new BatchesResult(); //记录有缺货的出库单 List shortageList = new List(); foreach (var b in beList) { //匹配五要素 var cis = tStockList.Where(p => p.CN_S_ITEM_CODE.Equals(b.ItemCode) && p.CN_S_ITEM_STATE.Equals(b.ItemState) && p.CN_S_OWNER.Trim().Equals(b.Owner.Trim()) && (string.IsNullOrEmpty(b.ItemArrivalLot) || b.ItemArrivalLot.Equals(p.CN_S_LOT_NO)) && (string.IsNullOrEmpty(b.ItemProductionLot) || b.ItemProductionLot.Equals(p.CN_S_PRODUCTION_BATCH))); //暂时先倒序生产批次 then 到货批次 后续取策略 var cisList = cis.OrderBy(p => p.CN_S_LOT_NO).ToList(); foreach (string stegy in lstStrate) { //逐个策略进行计算 switch (stegy) { case "FirstInFirstOut": cisList = cisList.OrderBy(o => o.CN_S_LOT_NO).ToList(); break; case "FirstWarrantFirstOut": var lstEmptyPBC = cisList.Where(o => string.IsNullOrEmpty(o.CN_S_PRODUCTION_BATCH)).ToList(); var lstFilterItem = cisList.Where(o => !string.IsNullOrEmpty(o.CN_S_PRODUCTION_BATCH)).OrderBy(o => o.CN_S_PRODUCTION_BATCH).ToList(); lstFilterItem.AddRange(lstEmptyPBC); cisList = lstFilterItem; break; } } if (!cisList.Any()) return OperateResult.Error("库存中未找到物料:" + b.ItemCode); //待批数 var qty = b.Qty; if (qty == 0) continue; for (int i = 0; i < cisList.Count; i++) { //可用量 = 库存量-分配量 var availableQty = cisList[i].CN_F_QUANTITY - cisList[i].CN_F_ALLOC_QTY;// -cisList[i].CN_F_EDITING_QTY; if (availableQty <= 0) continue; if (availableQty >= qty) { //记录锁定明细 var outLockingDtlEntity = GetOutLockingDtlEntity(cisList[i], b.OpNo, qty); bResult.OutLockingDtlList.Add(outLockingDtlEntity); cisList[i].CN_F_ALLOC_QTY += qty; qty = 0; break; } else { //记录锁定明细 var outLockingDtlEntity = GetOutLockingDtlEntity(cisList[i], b.OpNo, availableQty); bResult.OutLockingDtlList.Add(outLockingDtlEntity); cisList[i].CN_F_ALLOC_QTY += availableQty; qty -= availableQty; continue; } } #region 批分失败 if (qty > 0) { Log.Info("存在缺货", "出库单:" + b.OpNo + ",缺货物料:" + b.ItemCode + ",缺货数量:" + qty); //如果没记录过该出库单的缺货记录 if (!shortageList.Exists(e => e.CN_S_OP_NO.Equals(b.OpNo))) { shortageList.Add(b.OutMst); } var shortageOutEntity = shortageList.Find(f => f.CN_S_OP_NO == b.OpNo) .OutDtlList.Find(x => x.CN_S_ITEM_CODE == b.ItemCode && x.CN_S_ITEM_STATE == b.ItemState && x.CN_S_OWNER.Trim() == b.Owner.Trim() && x.CN_S_LOT_CODE == b.ItemArrivalLot && x.CN_S_PRODUCTION_BATCH == b.ItemProductionLot); Log.Info("存在缺货 shortageOutEntity", JsonConvert.SerializeObject(shortageOutEntity)); if (shortageOutEntity != null) { //缺货数量 shortageOutEntity.CN_F_SHORTAGE_QTY = qty; } } #endregion } #region 生成出库单 //若是反批分分配量,生成出库单信息 IEnumerable> beGroup = beList.GroupBy(p => p.OpNo); foreach (var be in beGroup) { //出库订单 TN_WM_OUT_MSTEntity outMst = be.AsEnumerable().ToList()[0].OutMst; //当前出库单是否缺货的物料, var shortageCurrentOut = shortageList.Find(f => f.CN_S_OP_NO.Equals(be.Key)); //存在缺货的物料 & 生成新的发货单 if (shortageCurrentOut != null) { outMst.OutDtlList = shortageCurrentOut.OutDtlList.FindAll(p => p.CN_F_SHORTAGE_QTY < p.CN_F_QUANTITY); //GenerateNewOut(outMst, true); //全部缺货 if (!outMst.OutDtlList.Any()) { //当前出库单为全部缺货 shortageCurrentOut.CN_S_STATE = Constants.State_Shortage; continue; } else { //当前出库单设为取消 shortageCurrentOut.CN_B_CANCEL = true; //缺货的部分物料生成新的发货单 var shortageNewOut = GenerateNewOut(outMst, true); //不缺货的部分物料生成新的发货单 outMst = GenerateNewOut(outMst, false); //记录新增的有缺货的发货单 bResult.ShortageOutList.Add(shortageNewOut); bResult.ShortageOutList.Add(outMst); //锁定明细改为新的发货单来锁 var oldOutLocking = bResult.OutLockingDtlList.FindAll(x => x.CN_S_FROM_NO == shortageCurrentOut.CN_S_OP_NO); if (oldOutLocking.Any()) { oldOutLocking.ForEach(f => { f.CN_S_FROM_NO = outMst.CN_S_OP_NO; }); } } //记录之前的出库单,取消 || 全部缺货 bResult.CancelAndAllShortageOut.Add(shortageCurrentOut); } //修改出库订单主子表为‘待出库’ outMst.CN_S_STATE = Constants.State_WaitOut; outMst.OutDtlList.ForEach(p => { p.CN_S_STATE = outMst.CN_S_STATE; }); //if (outMst.CN_S_OUT_TYPE != Constants.OutType_BigOut) //{ // var bigOut = GenerateBigOut(ref outMst, bResult.CancelAndAllShortageOut); // if (bigOut != null) // { // //锁定明细把大件的排除掉 // var bigItems = bResult.OutLockingDtlList.FindAll(f => f.CN_S_FROM_NO == bigOut.CN_S_SPLIT_FROM && bigOut.OutDtlList.Exists(e => e.CN_S_ITEM_CODE == f.CN_S_ITEM_CODE)); // bigItems.ForEach(e => // { // bResult.OutLockingDtlList.Remove(e); // }); // bResult.ShortageOutList.Add(bigOut); // //如果当前单据也是拆分的单据,并且全部是大件时 // if (!string.IsNullOrEmpty(outMst.CN_S_SPLIT_FROM) && !outMst.OutDtlList.Any()) // { // var exists = bResult.ShortageOutList.Find(f => f.CN_S_OP_NO == outMst.CN_S_OP_NO); // if (exists != null) // bResult.ShortageOutList.Remove(exists); // } // } //} if (outMst.OutDtlList.Any()) { bResult.OutMstList.Add(outMst); // 生成出库单号 UserRuleEntity user = new UserRuleEntity() { OrgCode = "0", RuleCode = Constants.Rule_Out }; string outNo = user.GenerateNo(); //发货单子表明细 if (outMst.OutDtlList.Any()) { //减少库存业务主表 var reduceInventoryMstEntity = GetReduceInventoryMst(outNo, Constants.Rule_OutOrder, outMst); //减少库存业务子表 reduceInventoryMstEntity.DTLEntity.AddRange(GetReduceInventoryDtl(outNo, outMst.OutDtlList)); //记录减少库存业务表 bResult.ReduceInventoryMstList.Add(reduceInventoryMstEntity); } } } #endregion return OperateResult.Succeed(null, bResult); } catch (Exception ex) { return OperateResult.Error(ex.Message); } } #endregion private static TN_WM_OUT_MSTEntity GenerateBigOut(ref TN_WM_OUT_MSTEntity currentOutEntity, List cancelList) { List bigDtlList = currentOutEntity.OutDtlList.FindAll(a => a.CN_C_BIG); if (bigDtlList.Any()) { TN_WM_OUT_MSTEntity newOutMst = currentOutEntity.Clone() as TN_WM_OUT_MSTEntity; newOutMst.CN_GUID = Guid.NewGuid().ToString(); string postData = "{\"appCode\":\"" + Constants.appCode + "\",\"ruleName\":\"" + Constants.Rule_OutOrder + "\",\"orgId\":\"0\",\"orgFlag\":\"\"}"; string orderNo = WebApiManager.HttpAutoBom_Post("api/BillRule/GenBillNo", postData); newOutMst.CN_S_OP_NO = orderNo; newOutMst.CN_S_STATE = Constants.State_Audit; bigDtlList.ForEach(e => { e.CN_GUID = Guid.NewGuid().ToString(); e.CN_S_OP_NO = orderNo; e.CN_S_ITEM_STATE = newOutMst.CN_S_STATE; e.CN_S_MSTGUID = newOutMst.CN_GUID; }); newOutMst.CN_S_SPLIT_FROM = currentOutEntity.CN_S_OP_NO; newOutMst.CN_S_OUT_TYPE = Constants.OutType_BigOut; newOutMst.OutDtlList = bigDtlList; if (!string.IsNullOrEmpty(currentOutEntity.CN_S_SPLIT_FROM)) { //之前单据是拆分后的单据 newOutMst.CN_S_SPLIT_FROM = currentOutEntity.CN_S_SPLIT_FROM; } else { //取消之前的单据 currentOutEntity.CN_B_CANCEL = true; cancelList.Add(currentOutEntity); } //全部都是大件 if (currentOutEntity.OutDtlList.Sum(s => s.CN_F_QUANTITY) == bigDtlList.Sum(s => s.CN_F_QUANTITY)) { currentOutEntity.OutDtlList = new List(); } else { //非大件 currentOutEntity.OutDtlList = currentOutEntity.OutDtlList.FindAll(a => !a.CN_C_BIG); } return newOutMst; } return null; } #region 取消当前发货单,返回新的发货单 /// /// 取消当前发货单,返回新的发货单 /// /// /// 是否返回缺货的 /// private static TN_WM_OUT_MSTEntity GenerateNewOut(TN_WM_OUT_MSTEntity oldOutEntity, bool isShortage) { TN_WM_OUT_MSTEntity newOutMst = oldOutEntity.Clone() as TN_WM_OUT_MSTEntity; newOutMst.CN_GUID = Guid.NewGuid().ToString(); string postData = "{\"appCode\":\"" + Constants.appCode + "\",\"ruleName\":\"" + Constants.Rule_OutOrder + "\",\"orgId\":\"0\",\"orgFlag\":\"\"}"; string orderNo = WebApiManager.HttpAutoBom_Post("api/BillRule/GenBillNo", postData); newOutMst.CN_S_OP_NO = orderNo; newOutMst.CN_S_SPLIT_FROM = oldOutEntity.CN_S_OP_NO; newOutMst.CN_S_STATE = isShortage ? Constants.State_Shortage : Constants.State_WaitOut; newOutMst.OutDtlList = new List(); foreach (var dtl in oldOutEntity.OutDtlList) { TN_WM_OUT_DTLEntity newOutDtl = dtl.Clone() as TN_WM_OUT_DTLEntity; newOutDtl.CN_GUID = Guid.NewGuid().ToString(); newOutDtl.CN_S_OP_NO = newOutMst.CN_S_OP_NO; newOutDtl.CN_S_MSTGUID = newOutMst.CN_GUID; newOutDtl.CN_S_STATE = newOutMst.CN_S_STATE; //生成缺货的明细 if (isShortage) { if (dtl.CN_F_SHORTAGE_QTY > 0) { newOutMst.CN_S_OUT_TYPE = Constants.OutType_ShortageOut; newOutDtl.CN_F_QUANTITY = dtl.CN_F_SHORTAGE_QTY; } } else//生成新的明细 { if (dtl.CN_F_QUANTITY > dtl.CN_F_SHORTAGE_QTY) { newOutDtl.CN_F_QUANTITY = dtl.CN_F_QUANTITY - dtl.CN_F_SHORTAGE_QTY; } } if (newOutDtl.CN_F_QUANTITY > 0) newOutMst.OutDtlList.Add(newOutDtl); } return newOutMst; } #endregion } }