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