using Rhea.Common; using Microsoft.AspNetCore.Http; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading; using System.Threading.Tasks; using Tiger.Model; using Apache.NMS; using System.Drawing.Drawing2D; using Tiger.Model.Sharetronic.Shelf; using Newtonsoft.Json.Linq; using Newtonsoft.Json; using Microsoft.CodeAnalysis.Operations; using Tiger.IBusiness; namespace Tiger.Business.WMS.Transaction { /// /// 生产领料事务 /// public class ProductionMaterialReq : WMSTransactionBase, IProductionMaterialReq { public IProductionMaterialReq Init(string id, string userCode, string apiHost, string orgCode, string fty) { TransID = id; UserCode = userCode; ApiHost = apiHost; OrgCode = orgCode; FTY_CODE = fty; Logger.Console.Info($"Start {this.GetType().Name} Transaction[ID: {TransID}]"); return this; } #region Propertys & Variables public string UserCode { get; set; } public long UserId { get; set; } public string OrgCode { get; set; } public string FTY_CODE { get; set; } public AuthOption SelectOrderOption { get; set; } public List Suggests { get; set; } = new(); public List CurPoolList => Suggests.Where(q => !q.poolItem.IsNullOrEmpty()).Select(q => q.poolItem).ToList(); public List LocationHis { get; set; } = new(); public Inventory CurInv { get; set; } public BIZ_ERP_PROD_OUT req { get; set; } public ReqType CurReqType { get; set; } public List dtls { get; set; } = new(); public bool isExceed { get; set; } public ProductionPickToMes toMes { get; set; } public bool his_isComplete { get; set; } public bool isCutting { get; set; } public string ScanAfCut { get; set; } public decimal cutQty { get; set; } public decimal remainQty { get; set; } public string QrCode { get; set; } public BIZ_WMS_TRANSFER transferH = null; public BIZ_WMS_TRANSFER cTransferH = null; public BIZ_WMS_TRANSFER_DTL transferDtl = null; public BIZ_WMS_TRANSFER_SN transferSn = null; #endregion Propertys & Variables #region Functions /// /// 获取生产领料单明细 /// /// /// public async Task>> GetProdMaterialReqDetails(Model.Minsun.ProdMaterialReqInput input) { var action = new ApiAction>(); action.Data = await Biz.Db.Queryable((t, m, w) => new JoinQueryInfos( JoinType.Left, t.MATERIALCODE == m.MCode, JoinType.Left, t.WAREHOUSECODE == w.WHCODE )) .Where((t, m, w) => t.BILLCODE.ToUpper() == input.BillCode.ToUpper() && t.PRQTY > 0) .WhereIF(!string.IsNullOrEmpty(input.Material), (t, m, w) => t.MATERIALCODE.Contains(input.Material) || m.MName.Contains(input.Material)) .Select((t, m, w) => new Model.Minsun.ProdMaterialReqOutput { BillCode = t.BILLCODE, BillLine = t.BILLLINE, MaterialCode = t.MATERIALCODE, MaterialName = m.MName, MaterialStandard = m.MDesc, UnitCode = t.UNITCODE, PRQty = t.PRQTY, Qty = t.QTY, DeliveryDate = t.DELIVERYDATE, WarehouseCode = t.WAREHOUSECODE, WarehouseName = w.WHNAME }) .OrderBy(x => x.BillLine).ToListAsync(); return action; } /// /// 领料单选择事件 /// /// /// public async Task> SelectOrder(ProdReqInput input) { var action = new ApiAction(); try { SelectOrderOption = input.AuthOption; req = await Biz.Db.Queryable().Where(x => x.BILLCODE == input.ReqNo).Includes(q => q.DtlsWithGhost, d => d.ItemInfo).IncludesAllFirstLayer().FirstAsync(); //验证明细是否正确 if (!req.Dtls.Any()) { action.IsSuccessed = false; action.LocaleMsg = new("WMS.ProdMReq.SelectOrder.DtlsException", input.ReqNo); return action; } Biz.Db.Deleteable().Where(q => q.TRANS_CODE == req.BILLCODE).ExecuteCommand(); //如果上一次推荐有数据,则先灭掉亮的灯 if (Suggests.Any()) { await CloseLight(LocationHis); } CurReqType = input.ReqType < 0 ? (!req.SOURCECODE.IsNullOrEmpty() ? ReqType.IsAgv : ReqType.IsFirst) : input.ReqType.GetEnum(); //如果是Agv叫料,只发一盘 if (CurReqType == ReqType.IsAgv) { //推荐物料 Result> result = WMS_ITEM_Biz.WmsItem.Suggest(req.BILLCODE, req.Dtls[0].ITEM_CODE, req.Dtls[0].WAREHOUSECODE, null, null, null, input.AuthOption, 1); action.LocaleMsg = result.LocaleMsg; if (result.IsException) { action.IsSuccessed = false; return action; } Suggests.Clear(); Suggests = result.Data; if (Suggests.Count > 0 && req.Dtls.Count > 0) { var inv = Suggests.First(); var actQty = req.Dtls.First().PRQTY - req.Dtls.First().QTY; if (inv != null && actQty > 0) { inv.poolItem = inv.Item.GetPoolItem(input.AuthOption.OrgCode, nameof(req), req.BILLCODE, req.Dtls[0].BILLLINE, actQty, false); } else { inv.poolItem = null; } } Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty()); } //如果是首套每个料一盘 if (CurReqType == ReqType.IsFirst) { var begin = DateTime.Now; var ElapsedTime = 0.0; //Debug.WriteLine($"Async suggest begin at {begin:T}"); //var dic = new Dictionary>>(); var sumDtls = req.Dtls.Where(q => q.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue()) .GroupBy(x => new { x.ITEM_CODE }).Select(x => new { ItemCode = x.Key.ITEM_CODE.ToString(), WarehouseCode = x.Max(t => t.WAREHOUSECODE), actQty = x.Sum(t => t.PRQTY - t.QTY), lockObj = new object() }); var dic = sumDtls.ToDictionary(k => k, v => new Result>()); foreach (var item in dic) { var key = item.Key; Work.DoAsync(() => { lock (item.Key.lockObj) { dic[item.Key] = WMS_ITEM_Biz.WmsItem.Suggest(req.BILLCODE, item.Key.ItemCode, item.Key.WarehouseCode, null, null, null, input.AuthOption, 1); } }); } while (dic.Any() && dic.Any(q => q.Value.IsNormal)) ; ElapsedTime = (DateTime.Now - begin).TotalSeconds; //Debug.WriteLine($"Async suggest end, elapsed total {ElapsedTime} seconds"); if (dic.Any(q => q.Value.IsException)) { action.IsSuccessed = false; action.LocaleMsg = dic.Where(q => q.Value.IsException).Select(q => q.Value.LocaleMsg).First(); return action; } if (dic.Any(q => q.Value.IsWarning)) { action.IsSuccessed = false; action.LocaleMsg = new("WMS.WmsItem.Suggest.Warning", string.Join(", ", dic.Where(q => q.Value.IsWarning).Select(q => q.Key.ItemCode))); } Suggests.Clear(); dic.Select(x => x.Value.Data).ToList().ForEach((item) => { Suggests.AddRange(item); }); Suggests.ForEach((inv) => { var actQty = sumDtls.Where(x => x.ItemCode == inv.Item.ITEM_CODE).Select(x => x.actQty).First(); if (actQty > 0) { inv.poolItem = inv.Item.GetPoolItem(input.AuthOption.OrgCode, nameof(req), req.BILLCODE, null, actQty, false); } else { inv.poolItem = null; } }); Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty()); } //如果是整单发料 if (CurReqType == ReqType.IsWhole) { var begin = DateTime.Now; var ElapsedTime = 0.0; var sumDtls = req.Dtls.Where(q => q.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue()) .GroupBy(x => new { x.ITEM_CODE }).Select(x => new { ItemCode = x.Key.ITEM_CODE.ToString(), WarehouseCode = x.Max(t => t.WAREHOUSECODE), actQty = x.Sum(t => t.PRQTY - t.QTY), lockObj = new object() }); var dic = sumDtls.ToDictionary(k => k, v => new Result>()); foreach (var item in dic) { var key = item.Key; Work.DoAsync(() => { lock (item.Key.lockObj) { dic[item.Key] = WMS_ITEM_Biz.WmsItem.Suggest(req.BILLCODE, item.Key.ItemCode, item.Key.WarehouseCode, null, null, null, input.AuthOption, item.Key.actQty); } }); } while (dic.Any() && dic.Any(q => q.Value.IsNormal)) ; ElapsedTime = (DateTime.Now - begin).TotalSeconds; //Debug.WriteLine($"Async suggest end, elapsed total {ElapsedTime} seconds"); if (dic.Any(q => q.Value.IsException)) { action.IsSuccessed = false; action.LocaleMsg = dic.Where(q => q.Value.IsException).Select(q => q.Value.LocaleMsg).First(); return action; } if (dic.Any(q => q.Value.IsWarning)) { action.IsSuccessed = false; action.LocaleMsg = new("WMS.WmsItem.Suggest.Warning", string.Join(", ", dic.Where(q => q.Value.IsWarning).Select(q => q.Key.ItemCode))); } Suggests.Clear(); dic.Select(x => x.Value.Data).ToList().ForEach((item) => { Suggests.AddRange(item); }); Suggests.ForEach((inv) => { var actQty = sumDtls.Where(x => x.ItemCode == inv.Item.ITEM_CODE).Select(x => x.actQty).First(); if (actQty > 0) { inv.poolItem = inv.Item.GetPoolItem(input.AuthOption.OrgCode, nameof(req), req.BILLCODE, null, actQty, false); } else { inv.poolItem = null; } }); Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty()); } //保存物料池到数据库 Biz.Db.Insertable(CurPoolList).ExecuteCommand(); action = await LightAll(new() { AuthOption = input.AuthOption, ReqType = CurReqType, Color = LedColor.Blue }); } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"领料单选择异常"); } return action; } /// /// 领料单推荐所有物料亮灯 /// /// /// public async Task> LightAll(LightEntityInput light) { var action = new ApiAction(); try { var list = Suggests.WhereIF(!light.ItemCode.IsNullOrEmpty(), q => q.Item.ITEM_CODE == light.ItemCode); if (list.Any()) { list = list.Where(q => q.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.Smart.GetValue() || q.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.QRCode.GetValue()); if (list.Any()) { list = list.Where(q => q.poolItem.STATUS < WMS_ITEM_POOL.STATUSs.WaitSend.GetValue()); if (list.Any()) { var locs = list.Select(x => x.Location.AddShelf(x.Shelf)).ToList(); LocationHis.AddRange(locs); //亮灯前先灭一遍 foreach (var shelf in list.Select(q => q.Shelf)) { await Share.Shelf.DownAll(shelf); } await Share.Shelf.LightMulti(TransID, light.Color, locs); action.LocaleMsg = Biz.L("亮灯成功,亮灯颜色[{0}]", light.Color.GetDesc()); } else { action.LocaleMsg = Biz.L($"无需亮灯,推荐的物料已全部下架"); } } else { action.LocaleMsg = Biz.L($"无需亮灯,推荐的物料不在智能货架上"); } } else { action.IsSuccessed = false; action.LocaleMsg = Biz.L($"亮灯失败,未找到推荐下架的物料"); } action.Data = new ProdReqOutput() { ReqNo = req.BILLCODE, ReqType = light.ReqType.GetValue() }; } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"亮灯异常"); } return action; } /// /// 领料单发料明细 /// /// public async Task ProdReqDtl(string itemcode) { var action = new ApiAction(); try { //如果是整单发料 if (false && !itemcode.IsNullOrEmpty() && CurReqType == ReqType.IsWhole) { var begin = DateTime.Now; var ElapsedTime = 0.0; //Debug.WriteLine($"Async suggest begin at {begin:T}"); //var dic = new Dictionary>>(); var sumDtls = req.Dtls.Where(q => q.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue()) .GroupBy(x => new { x.ITEM_CODE }).Select(x => new { ItemCode = x.Key.ITEM_CODE.ToString(), actQty = x.Sum(t => t.PRQTY - t.QTY), lockObj = new object() }); var dic = sumDtls.Where(q => q.ItemCode == itemcode).ToDictionary(k => k, v => new Result>()); foreach (var item in dic) { var key = item.Key; Work.DoAsync(() => { lock (item.Key.lockObj) { dic[item.Key] = WMS_ITEM_Biz.WmsItem.Suggest(req.BILLCODE, item.Key.ItemCode, req.Dtls[0].WAREHOUSECODE, null, null, null, SelectOrderOption, item.Key.actQty); } }); } while (dic.Any() && dic.Any(q => q.Value.IsNormal)) ; ElapsedTime = (DateTime.Now - begin).TotalSeconds; //Debug.WriteLine($"Async suggest end, elapsed total {ElapsedTime} seconds"); if (dic.Any(q => q.Value.IsException)) { action.IsSuccessed = false; action.LocaleMsg = dic.Where(q => q.Value.IsException).Select(q => q.Value.LocaleMsg).First(); return action; } if (dic.Any(q => q.Value.IsWarning)) { action.IsSuccessed = false; action.LocaleMsg = new("WMS.WmsItem.Suggest.Warning", string.Join(", ", dic.Where(q => q.Value.IsWarning).Select(q => q.Key.ItemCode))); } Suggests.Clear(); dic.Select(x => x.Value.Data).ToList().ForEach((item) => { Suggests.AddRange(item); }); Suggests.ForEach((inv) => { var actQty = sumDtls.Where(x => x.ItemCode == inv.Item.ITEM_CODE).Select(x => x.actQty).First(); if (actQty > 0) { inv.poolItem = inv.Item.GetPoolItem(OrgCode, nameof(req), req.BILLCODE, null, actQty, false); } else { inv.poolItem = null; } }); Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty()); //保存物料池到数据库 var db = Biz.Db; var dbTran = db.UseTran(() => { db.Deleteable().Where(x => x.TRANS_NO == req.BILLCODE).ExecuteCommand(); db.Insertable(CurPoolList).ExecuteCommand(); }); if (!dbTran.IsSuccess) { Logger.Default.Fatal(dbTran.ErrorException, "Database transaction save exception"); this.Close(!dbTran.IsSuccess); throw dbTran.ErrorException; } } var dtls = req.Dtls.GroupBy(x => new { x.BILLLINE, x.ITEM_CODE, x.ItemInfo.ITEM_NAME }).Select(x => new ProdReqDtl() { BillLine = x.Key.BILLLINE.ToInt32(), ItemCode = x.Key.ITEM_CODE, ItemName = x.Key.ITEM_NAME, Status = "", Items = new List() }).ToList(); foreach (var d in dtls) { d.Items = Suggests.Where(x => x.Item.ITEM_CODE == d.ItemCode).Select(x => new ProdReqDtlItems { WHCode = x.Warehouse.WH_CODE, LocationCode = x.Location.LOCATION_CODE, SN = x.Item.SN, QTY = x.Item.QTY }).ToList(); d.Status = $"{(double)req.Dtls.Where(x => x.ITEM_CODE == d.ItemCode).Sum(x => x.QTY)} / {(double)req.Dtls.Where(x => x.ITEM_CODE == d.ItemCode).Sum(x => x.PRQTY)} {Suggests.FirstOrDefault()?.Item?.UNIT} (还需{d.Items.Count}个)"; } action.Data = itemcode.IsNullOrEmpty() ? dtls.OrderBy(q => q.BillLine) : dtls.Where(x => x.ItemCode == itemcode).FirstOrDefault()?.Items.OrderBy(q => q.SN); } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"获取领料单明细异常"); } return action; } /// /// 发料明细提示信息 /// /// public async Task GetItemTips(string itemcode) { var action = new ApiAction(); try { action.Data = $"物料{itemcode}:已下架[{(double)req.Dtls.Where(x => x.ITEM_CODE == itemcode).Sum(x => x.QTY)}],共{(double)req.Dtls.Where(x => x.ITEM_CODE == itemcode).Sum(x => x.PRQTY)} {Suggests.FirstOrDefault()?.Item?.UNIT}"; } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"获取发料明细提示信息异常"); } return action; } /// /// 灭灯 /// /// public async Task CloseLight(List locations = null) { var action = new ApiAction(); try { if (locations != null) { foreach (var shelf in locations.Select(q => q.Shelf)) { await Share.Shelf.DownAll(shelf); } } //灭灯 await Share.Shelf.DownMulti(TransID, locations.IsNullOrEmpty(Suggests.Where(q => q.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.Smart.GetValue() || q.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.QRCode.GetValue()) .Select(x => x.Location).ToList())); } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"灭灯异常"); } return action; } /// /// 扫描物料领料下架 /// /// /// public async Task> ScanItem(BaseInput input) { var action = new ApiAction(); try { ProcessingSn = input.SN; if (WMSContext.TransactionDic.Where(q => !string.IsNullOrWhiteSpace(q.Value.ProcessingSn)).Any(q => q.Value.ProcessingSn == ProcessingSn && q.Value.TransID != this.TransID)) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("当前条码正在执行中,无法扫描"); return action; } if (input.SN.IsNullOrEmpty()) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("条码不能为空"); return action; } //解析条码 Result result = WMS_ITEM_Biz.WmsItem.Get(input.SN, input.AuthOption, true); if (!result.IsSuccessed) { action.IsSuccessed = false; action.LocaleMsg = result.LocaleMsg; return action; } CurInv = result.Data as Inventory; //验证条码是否正确 if (!CurInv.isNormalStatus || CurInv.Status != WMS_ITEM.STATUSs.InStore) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("WMS.ProdMReq.ScanItem.StatusException", string.Join(',', CurInv.StatusList.Select(x => x.GetDesc()))); return action; } //储位验证 if (CurInv.Location.IsNullOrEmpty()) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("WMS.ProdMReq.ScanItem.LocationIsNull", CurInv.CurPkg.SN, CurInv.CurPkg.LOCATION_ID); return action; } //物料验证 if (CurInv.ItemInfo.IsNullOrEmpty() || CurInv.ItemInfo.IS_ACTIVE == "N") { action.IsSuccessed = false; action.LocaleMsg = Biz.L("WMS.ProdMReq.ScanItem.ItemCodeNotExistsOrNotActive", CurInv.ItemInfo.ITEM_CODE.IsNullOrEmpty(CurInv.Barcode.ItemCode)); return action; } //仓库卡控 if (!req.Dtls.Any(q => q.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE && q.WAREHOUSECODE == CurInv.Warehouse.WH_CODE)) { action.IsSuccessed = false; action.LocaleMsg = Biz.L($"条码[{CurInv.Barcode.SN}]的料号或者仓库跟单据[{req.BILLCODE}]上的不一致"); return action; } QrCode = input.SN; //1.判断FIFO //if (Suggests.Where(x => Barcode.IsReprint(x.Item.SN) && x.Item.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE).Any() && !CurInv.Barcode.IsReprintSn) //{ // action.IsSuccessed = false; // action.LocaleMsg = Biz.L("需要先出拆包的物料"); // return action; //} var MaxDC = CurPoolList.Any(x => x.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE) ? CurPoolList.Where(x => x.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE).Max(t => t.PROD_DATE) : DateTime.MinValue; //判断系统参数是否设置先进先出 if (Cache.SysParam["YesOrNo"].PARAM_VALUE == "Y") { if (CurInv.Items.Max(x => x.PROD_DATE).Date > MaxDC.Date) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("必需先进先出", CurInv.Items.Max(x => x.PROD_DATE).Date, MaxDC.Date); return action; } } //判断物料是否在超期日期范围内 //if (CurInv.ItemInfo.VALIDITY_DAYS>0) //{ // var time = DateTime.Now; // if (CurInv.Items.Count() > 0) // { // foreach (var item in CurInv.Items) // { // //判断是否有生产日期 // if (!item.PROD_DATE.IsNullOrEmpty() && item.PROD_DATE.ToString()!= "1900/1/1 0:00:00") // { // var t = time - item.PROD_DATE; // decimal timeDifference = (decimal)t.TotalDays; // if (timeDifference > CurInv.ItemInfo.VALIDITY_DAYS) // { // action.IsSuccessed = false; // action.LocaleMsg = Biz.L("产品超出有效期范围",timeDifference); // return action; // } // } // //否则以首次入库时间 // else if (!item.FIRST_IN_DATE.IsNullOrEmpty() && item.FIRST_IN_DATE.ToString() != "1900/1/1 0:00:00") // { // var t = time - item.PROD_DATE; // decimal timeDifference = (decimal)t.TotalDays; // if (timeDifference > CurInv.ItemInfo.VALIDITY_DAYS) // { // action.IsSuccessed = false; // action.LocaleMsg = Biz.L("产品超出有效期范围", timeDifference); // return action; // } // } // } // } //} //2.是否超发 isExceed = false; req = await Biz.Db.Queryable().Where(x => x.BILLCODE == req.BILLCODE).Includes(q => q.DtlsWithGhost, d => d.ItemInfo).IncludesAllFirstLayer().FirstAsync(); dtls = req.Dtls.Where(x => x.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE).ToList(); if (dtls.IsNullOrEmpty()) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("领料单明细没有这个物料"); return action; } //如果状态完成 if (dtls.Where(x => x.LINESTATUS == BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue()).Count() == dtls.Count) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("料已经发完"); return action; } //实际已经扫描数量 var actQty = dtls.Sum(x => x.QTY > x.PRQTY ? 0 : x.PRQTY - x.QTY); cutQty = 0; if (actQty < CurInv.CurPkg.QTY) { isExceed = true; cutQty = CurInv.CurPkg.QTY - actQty; Logger.Interface.Info($"生产领料 =>单号[{req.BILLCODE}],条码[{CurInv.CurPkg.SN}],实际已经扫描数量:{actQty},剩余数量:{cutQty},超发提示"); } //3.是否截料,不允许超发的物料,所有的单别都需要做拆包处理,自动调用拆包,允许超发的物料,单别5408或者5409的要拆包 isCutting = false; transferSn = null; var isFirstDtl = true; remainQty = 0; decimal curQty = CurInv.CurPkg.QTY; foreach (var d in dtls) { var actPrQty = d.PRQTY - d.QTY; if (actPrQty > 0 && curQty > 0) { if (actPrQty >= curQty) { d.QTY += curQty; curQty = 0; } else { d.QTY = d.PRQTY; curQty -= actPrQty; } d.LINESTATUS = d.QTY >= d.PRQTY ? BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue() : BIZ_ERP_PROD_OUT.STATUSs.WORKING.GetValue(); //d.WAREHOUSECODE = CurInv.Warehouse.WH_CODE; //如果是Agv叫料 if (CurReqType == ReqType.IsAgv) { d.LINESTATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue(); Logger.Interface.Info($"生产领料 =>叫料发料,单号:[{req.BILLCODE}],Qty:{d.QTY},状态:{d.LINESTATUS},项次:[{d.BILLLINE}]"); } //首套发料且是循环的第一次扣减数量的行,则标记该行完成,后续的行需要再扫一盘才标记完成 if (CurReqType == ReqType.IsFirst && isFirstDtl) { d.LINESTATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue(); } isFirstDtl = false; } } remainQty = curQty; //如果超发就是超发需要调拨的数量 var _isCutting = isCutting; var _isExceed = isExceed; if (isExceed && (CurInv.ItemInfo.DLVY_TYPE == BAS_ITEM.DLVY_TYPEs.ByDemand.GetValue() || req.BILLCODE.Substring(2, 4) == "5408" || req.BILLCODE.Substring(2, 4) == "5409")) { _isCutting = true; _isExceed = false; if (CurInv.Items.Count > 1) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("扫描的条码不是最小包装,不能拆包!"); return action; } } //增加储位操作历史 if (!LocationHis.Any(q => q.SHELF_ID == CurInv.Location.SHELF_ID && q.LOCATION_CODE == CurInv.Location.LOCATION_CODE)) { LocationHis.Add(CurInv.Location); } //action.LocaleMsg = Biz.L($"生产领料 =>料号:[{CurInv.ItemInfo.ITEM_CODE}],状态:{req.STATUS.GetEnumDesc()}"); action.Data = new ProdReqOutput() { SN = CurInv.SN, ItemCode = CurInv.ItemInfo.ITEM_CODE, Qty = CurInv.CurPkg.QTY, CutQty = CurInv.CurPkg.QTY - cutQty, isCutting = _isCutting, isExceed = _isExceed, ReqNo = req.BILLCODE, regionCode = CurInv.Region.REGION_CODE, locationCode = CurInv.Location?.LOCATION_CODE, ScanAfCut = CurInv.Warehouse.SCAN_AF_CUT }; } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"扫描物料[{input.SN}]复核异常"); } return action; } /// /// 确认超发后或者需要截料的 /// /// /// public async Task> ConfirmExceed(ProdReqInput input) { var action = new ApiAction(); try { if (isExceed) { if (CurInv.ItemInfo.DLVY_TYPE == BAS_ITEM.DLVY_TYPEs.ByDemand.GetValue() || req.BILLCODE.Substring(2, 4) == "5408" || req.BILLCODE.Substring(2, 4) == "5409") { isCutting = true; isExceed = false; } else { //最大数量明细行用于调拨,该行发料数量加上应截料数量 var tfdtl = dtls.OrderBy(q => q.BILLLINE.ToDecimal()).Last(); tfdtl.QTY += cutQty; //调拨 transferH = Biz.Db.Queryable().Where(x => x.SOURCECODE == req.BILLCODE).First() ?? new() { BILLCODE = "TF-" + req.BILLCODE, BILLDATE = DateTime.Now, STATUS = BIZ_WMS_TRANSFER.STATUSs.INIT.GetValue(), BIZTYPE = null, OUTWAREHOUSECODE = CurInv.Warehouse.WH_CODE, INWAREHOUSECODE = CurInv.Warehouse.TRANSFER_WH, SOURCETYPE = 0, SOURCECODE = req.BILLCODE, AUTH_ORG = OrgCode }; transferDtl = Biz.Db.Queryable().Where(x => x.BILLCODE == transferH.BILLCODE && x.BILLLINE == tfdtl.BILLLINE).First() ?? new() { BILLCODE = transferH.BILLCODE, BILLLINE = tfdtl.BILLLINE, LINESTATUS = BIZ_WMS_TRANSFER.STATUSs.FINISHED.GetValue(), ITEM_CODE = CurInv.ItemInfo.ITEM_CODE, UNITCODE = "", PRQTY = remainQty, OUTQTY = remainQty, INQTY = remainQty, OUTWAREHOUSECODE = CurInv.Warehouse.WH_CODE, INWAREHOUSECODE = CurInv.Warehouse.TRANSFER_WH, SOURCETYPE = transferH.SOURCETYPE, SOURCECODE = transferH.SOURCECODE, SOURCELINE = tfdtl.BILLLINE, REMARK = "", AUTH_ORG = OrgCode }; Logger.Interface.Info($"生产领料 =>生成调拨明细,单号:[{transferH.BILLCODE}],Qty:{remainQty},状态:{transferDtl.LINESTATUS},项次:[{transferDtl.BILLLINE}]"); transferSn = new() { BILLCODE = transferH.BILLCODE, BILLLINE = tfdtl.BILLLINE, ITEM_CODE = CurInv.ItemInfo.ITEM_CODE, SN = CurInv.CurPkg.SN, QTY = CurInv.CurPkg.QTY, AUTH_ORG = OrgCode }; } } //灭灯 if (CurInv.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.Smart.GetValue() || CurInv.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.QRCode.GetValue()) { await Share.Shelf.DownSingle(TransID, CurInv.Location); } Suggests.Where(q => CurInv.Items.Select(x => x.SN).Contains(q.Item.SN)).ToList().ForEach((i) => { i.poolItem.STATUS = WMS_ITEM_POOL.STATUSs.WaitSend.GetValue(); }); var reqSn = new BIZ_ERP_PROD_OUT_SN() { AUTH_ORG = OrgCode, ORDER_NO = req.BILLCODE, SN = CurInv.CurPkg.SN, ITEM_CODE = CurInv.ItemInfo.ITEM_CODE, QTY = CurInv.CurPkg.QTY, META_SN = CurInv.Barcode.MetaSn, STATUS = WMS_ITEM.STATUSs.Sended.GetValue(), LOCATION_CODE = CurInv.Location?.LOCATION_CODE, ERP_WH = CurInv.Warehouse.WH_CODE, ALLOC_QTY = CurInv.CurPkg.QTY - cutQty, SOURCE_CODE = "WORK_ORDER", SOURCE_ORDER = dtls.First().SOURCECODE, IS_FIRST = CurReqType == ReqType.IsFirst ? "Y" : "N", NEED_CUTTING = isCutting ? "Y" : "N", }; //4.更新业务单据,BIZ_ERP_PROD_OUT、BIZ_ERP_PROD_OUT_DTL、BIZ_ERP_PROD_OUT_SN 判断明细行状态是否完成 //detail未完成的行数 //var isComplete = req.Dtls.Where(x => !(x.LINESTATUS == BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue() && x.PRQTY > 0)).Count() <= 0; var isComplete = !req.Dtls.Any(x => x.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue() && x.PRQTY > 0); his_isComplete = isComplete; if (isComplete) { //detail全部完成了 req.STATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue(); Logger.Interface.Info($"生产领料 =>领料单[{req.BILLCODE}]发料完成,单头状态:{req.STATUS}"); transferH = transferH != null ? transferH : Biz.Db.Queryable().Where(x => x.SOURCECODE == req.BILLCODE).First(); if (transferH != null) { transferH.STATUS = BIZ_WMS_TRANSFER.STATUSs.FINISHED.GetValue(); Logger.Interface.Info($"生产领料 =>更新调拨单[{transferH.BILLCODE}]状态:{transferH.STATUS}"); } } else { req.STATUS = BIZ_ERP_PROD_OUT.STATUSs.WORKING.GetValue(); } //如果是Agv if (CurReqType == ReqType.IsAgv) { req.STATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue(); } //5.出库下架 foreach (var item in CurInv.Items) { item.TRANS_CODE = nameof(BIZ_ERP_PROD_OUT); item.TRANS_NO = req.BILLCODE; item.TRANS_LINE = string.Join(",", dtls.Select(x => x.BILLLINE)); item.SOURCE_CODE = "WORK_ORDER"; item.SOURCE_ORDER = dtls.First().SOURCECODE; } Result downResult = WMS_ITEM_Biz.WmsItem.TakeDown(CurInv, input.AuthOption, WMS_ITEM.STATUSs.Sended, !isCutting); if (!downResult.IsSuccessed) { ProcessingSn = ""; action.IsSuccessed = false; action.LocaleMsg = downResult.LocaleMsg; return action; } TakeDownInfo downInfo = downResult.Data; //物料池中删除已下架物料 var poolItems = CurPoolList.Where(q => downInfo.Items.Select(x => x.SN).Contains(q.SN)).ToList(); //5.保存数据库 var db = Business.Biz.Db; var dbTran = db.UseTran(() => { //入库 db.Updateable(downInfo.Items, UserCode).ExecuteCommand(); db.Insertable(downInfo.History, UserCode).ExecuteCommand(); db.Updateable(downInfo.Packages, UserCode).ExecuteCommand(); db.Deleteable(poolItems).ExecuteCommand(); db.Updateable(req, UserCode).ExecuteCommand(); db.Updateable(dtls, UserCode).ExecuteCommand(); if (transferH != null) { db.Storageable(transferH, UserCode).ExecuteCommand(); } if (transferDtl != null) { db.Storageable(transferDtl, UserCode).ExecuteCommand(); } if (transferSn != null) { db.Insertable(transferSn, UserCode).ExecuteCommand(); } if (reqSn != null) { db.Insertable(reqSn, UserCode).ExecuteCommand(); } db.Deleteable().Where(q => SqlFunc.Subqueryable().Where(p => p.ITEM_CODE == q.ITEM_CODE && p.BILLCODE == q.TRANS_NO && p.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue()).NotAny()).ExecuteCommand(); }); if (!dbTran.IsSuccess) { Logger.Default.Fatal(dbTran.ErrorException, "Database transaction save exception"); this.Close(!dbTran.IsSuccess); throw dbTran.ErrorException; } //6. 对接MES、Agv //通过工厂带出 MES api 和 agv api MES_FACTORY _factory = db.Queryable() .Where(x => x.FTY_CODE == FTY_CODE) .First(); //发送到MES的实体 2022/09/20 var query = db.Queryable().Where(x => x.ORIGINAL_WORKORDER == dtls.First().SOURCECODE).First(); var sctrwo = query != null ? (query.TARGET_WORKORDER.IsNullOrEmpty() ? dtls.First().SOURCECODE : query.TARGET_WORKORDER) : dtls.First().SOURCECODE; toMes = new ProductionPickToMes() { qrcode = QrCode, wo = sctrwo, // nCurrentLines.First().SourceCode, pkgid = CurInv.SN, mfrkp = CurInv.Barcode.OEMItemCode, kpno = CurInv.ItemInfo.ITEM_CODE, dc = CurInv.Barcode.ProdDateStr, lc = CurInv.Barcode.LotNo, qty = CurInv.CurPkg.QTY.ToString(), erpBillCode = req.BILLCODE, //MES接口增加字段,返回备料完成的当前领料单号 2023/05/17 Ben Lin taskid = "" }; //调用agv if (CurReqType == ReqType.IsAgv) { string[] agvData = req.SOURCECODE.Split(','); toMes.taskid = agvData[0]; //MES接口增加字段,返回备料完成的当前任务ID 2023/05/17 Ben Lin AgvMPInput agvMPInput = new AgvMPInput { taskId = agvData[0], stationCode = agvData.Length >= 2 ? agvData[1] : "", materialType = CurInv.ItemInfo.ITEM_CODE, type = "done" }; //JObject resp = PostMesApi(@"http://172.18.8.56:9533/api/WMS/Agv/materialPreparation", JsonConvert.SerializeObject(agvMPInput)); var agvurl = _factory != null ? _factory.AGV_API : @$"{Cache.SysParam["agvUrl", "AgvApiAddress"].PARAM_VALUE}"; ; var agvResult = DI.Resolve().materialPreparation(agvMPInput, agvurl); req.STATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue(); Logger.Interface.Info($"生产领料 =>叫料发料完成,单号:[{req.BILLCODE}],状态:{req.STATUS.GetEnumDesc()}"); } if (isComplete) { //领料单实时过账 MaterialReqToErpNew MReqPostParam mReqPostParam = new() { BillCode = req.BILLCODE, CompanyId = OrgCode }; //异步调用T100过账 DI.Resolve().MaterialReqToErpNew(mReqPostParam); } //发送到MES var isSend = Cache.SysParam["IsSend", "SendToMES"].PARAM_VALUE.ToString() == "Y"; if (isSend && !isCutting) { var mesApi = _factory != null ? _factory.MES_API : Cache.SysParam["apiUrl", "SendToMES"].PARAM_VALUE.ToString(); var response = HttpHelper.PostAsync(mesApi, JsonConvert.SerializeObject(toMes)).Result; var _action = JsonConvert.DeserializeObject(response.Message); Logger.Interface.Info($"生产领料 =>发料完成,单号[{req.BILLCODE}],条码[{CurInv.SN}],状态[{req.STATUS.GetEnumDesc()}],MES传入Json:{JsonConvert.SerializeObject(toMes)}, MES返回:{response.Message}"); } action.LocaleMsg = Biz.L($"生产领料 =>发料完成,单号[{req.BILLCODE}],条码[{CurInv.SN}],状态[{req.STATUS.GetEnumDesc()}]"); action.Data = new ProdReqOutput() { SN = CurInv.SN, ItemCode = CurInv.ItemInfo.ITEM_CODE, Qty = CurInv.CurPkg.QTY, CutQty = CurInv.CurPkg.QTY - cutQty, isCutting = isCutting, isExceed = isExceed, ReqNo = req.BILLCODE, regionCode = CurInv.Region.REGION_CODE, locationCode = CurInv.Location?.LOCATION_CODE, }; } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"亮灯异常"); } ProcessingSn = ""; return action; } /// /// 添加一个ApiAction的历史记录 /// /// public override void AddHistory(Microsoft.AspNetCore.Http.HttpRequest request, ApiAction action) { var his = action.History(); his.TraceDic.Add("CurReqType", CurReqType); his.TraceDic.Add("CurInv", CurInv); his.TraceDic.Add("req", req); his.TraceDic.Add("toMes", toMes); his.TraceDic.Add("CurPoolList", CurPoolList); his.TraceDic.Add("isComplete", his_isComplete); ActionHistoryList.Add($"{request.HttpContext.TraceIdentifier} at {action.Timestamp:yyyy/MM/dd HH:mm:ss.fff}: {request.Path}", his); LastActionTime = DateTime.Now; } #endregion Functions public override bool Close(bool needSaveHistoryLog = false) { //needSaveHistoryLog = true; CloseLight(LocationHis).Wait(); if (!(req?.BILLCODE ?? "").IsNullOrEmpty()) { Biz.Db.Deleteable().Where(x => x.TRANS_NO == req.BILLCODE).ExecuteCommand(); } Biz.Db.Deleteable().Where(q => CurPoolList.Select(q => q.SN).Contains(q.SN)).ExecuteCommand(); //保存操作日志 this.IsFinished = true; return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished; } }//endClass }