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 Newtonsoft.Json; using Tiger.IBusiness; namespace Tiger.Business.WMS.Transaction { /// /// 生产领料事务 /// public class OutSplit : WMSTransactionBase, IOutSplit { public IOutSplit 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 string ReqNo { get; set; } public string ExceedTranId { get; set; } public Inventory CurInv { get; set; } public ProductionPickToMes toMes { get; set; } public AuthOption option { get; set; } #endregion #region Functions /// /// 拆批扫描物料条码 /// /// /// public async Task> ScanItem(OutSplitInput input) { var action = new ApiAction(); try { 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 (string.IsNullOrEmpty(CurInv.ItemInfo?.ITEM_CODE)) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("WMS.InReceipt.ScanItem.NotFound", CurInv.ItemInfo?.ITEM_CODE); 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 (!CurInv.isExists || CurInv.Status == WMS_ITEM.STATUSs.WaitIn) { action.IsSuccessed = false; action.LocaleMsg = Biz.L("WMS.InReceipt.ScanItem.NotExist", CurInv.SN); //条码[{0}]不存在于系统中 return action; } ReqNo = input.ReqNo; ExceedTranId = input.ExceedTranId; option = input.AuthOption; action.LocaleMsg = Biz.L($"条码[{CurInv.CurPkg.SN}]扫描成功!"); action.Data = new OutSplitOutput() { MaterialCode = CurInv.ItemInfo.ITEM_CODE, MaterialName = CurInv.ItemInfo.ITEM_NAME, SupplierCode = CurInv.Items[0].SUPP_CODE, Barcode = CurInv.SN, //批次号即为条码 DateCode = CurInv.Items[0].PROD_DATE, LotNo = CurInv.Barcode.LotNo, ScanQty = CurInv.CurPkg.QTY, //批次数量 Unit = CurInv.ItemInfo.UNIT }; } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"扫描物料[{input.SN}]复核异常"); } return action; } /// /// 拆分 /// /// /// public async Task> SplitBarcodeRecord(string qty) { var action = new ApiAction(); try { Logger.Interface.Info($"拆包 => [{CurInv.SN}]正在准备开始执行拆包操作......"); int splitNum = Convert.ToInt32(qty); if (splitNum >= CurInv.CurPkg.QTY || splitNum <= 0) { action.IsSuccessed = false; action.LocaleMsg = Biz.L($"拆批数量有误!"); return action; } Logger.Interface.Info($"拆包 => [{CurInv.SN}]当前数量[{CurInv.CurPkg.QTY}],实际数量[{splitNum}]"); //获取供应商信息 var vender = await Biz.Db.Queryable().Where(t => t.SUPP_CODE == CurInv.Items[0].SUPP_CODE).FirstAsync(); //生成条码 var cutSerial = Convert.ToString(CurInv.Barcode.ReprintNo ?? 0, 2); cutSerial = cutSerial == "0" ? "1" : cutSerial; var orglSn = CurInv.SN.Split("_")[0]; var nSplitSn = $"{orglSn}_CA{Convert.ToString(Convert.ToInt32(cutSerial + "0", 2), 16).ToUpper()}"; var nRemainSn = $"{orglSn}_CB{Convert.ToString(Convert.ToInt32(cutSerial + "1", 2), 16).ToUpper()}"; if (Biz.Db.Queryable().Any(t => t.SN == nRemainSn)) { //throw new UserFriendlyException(1011, "生成的拆批条码已存在!", vender != null ? vender.VenderSName : ""); action.IsSuccessed = false; action.LocaleMsg = Biz.L($"生成的拆批条码已存在!供应商:{vender?.SUPP_NAME_CN}"); return action; } //#region 操作条码信息 //条码一 var nSplitBarcode1 = CurInv.Items[0]; var nSplitBarcode2 = CurInv.Items[0].Clone(); nSplitBarcode1.SN = nSplitSn; //nSplitBarcode1.BarcodeStatus = (int)Domain.InvStorageInfoSides.BarcodeStatus.Outstock; //出库状态 nSplitBarcode1.QTY = splitNum; nSplitBarcode1.TRANS_CODE = nameof(OutSplit); if (!ReqNo.IsNullOrEmpty()) { nSplitBarcode1.STATUS = WMS_ITEM.STATUSs.Sended.GetValue(); nSplitBarcode1.LOCATION_ID = ""; nSplitBarcode1.REGION_ID = ""; nSplitBarcode1.WH_ID = ""; nSplitBarcode1.SHELF_ID = ""; } var nSplitPgk1 = CurInv.CurPkg; var nSplitPgk2 = CurInv.CurPkg.Clone(); nSplitPgk1.SN = nSplitSn; nSplitPgk1.QTY = splitNum; if (!ReqNo.IsNullOrEmpty()) { nSplitPgk1.LOCATION_ID = ""; nSplitPgk1.REGION_ID = ""; nSplitPgk1.WH_ID = ""; nSplitPgk1.SHELF_ID = ""; } //条码二 nSplitBarcode2.SN = nRemainSn; var currentQty = nSplitBarcode2.QTY;//原条码实际数量 nSplitBarcode2.QTY = nSplitBarcode2.QTY - splitNum; //拆分后剩下的数量 nSplitBarcode2.ID = Guid.NewGuid().ToString("N"); nSplitBarcode2.TRANS_CODE = nameof(OutSplit); if (!ReqNo.IsNullOrEmpty()) { nSplitBarcode2.STATUS = WMS_ITEM.STATUSs.InStore.GetValue(); } nSplitPgk2.SN = nRemainSn; nSplitPgk2.QTY = nSplitBarcode2.QTY; nSplitPgk2.ID = Guid.NewGuid().ToString("N"); string msg = ReqNo.IsNullOrEmpty() ? $"手工拆分物料:原条码[{CurInv.SN},条码一[{nSplitBarcode1.SN}],条码二[{nSplitBarcode2.SN}]]" : $"自动拆分物料:单据[{ReqNo}],原条码[{CurInv.SN},发料条码[{nSplitBarcode1.SN}],需要上架条码[{nSplitBarcode2.SN}]]"; if (!ExceedTranId.IsNullOrEmpty()) { var MaterialReqTrans = Biz.Db.Queryable().Where(t => t.BILLCODE == ReqNo).Any(); var OthReqTrans = Biz.Db.Queryable().Where(t => t.BILLCODE == ReqNo).Any(); var SaleReqTrans = Biz.Db.Queryable().Where(t => t.BILLCODE == ReqNo).Any(); var TfTrans = Biz.Db.Queryable().Where(t => t.BILLCODE == ReqNo).Any(); ProductionMaterialReq trans = null; OutWorkOrder owoTrans = null; OutOther othTrans = null; OutSale saleTrans = null; OutTransfer tfTrans = null; if (WMSContext.TransactionDic.ContainsKey(ExceedTranId)) { trans = WMSContext.TransactionDic[ExceedTranId] as ProductionMaterialReq; othTrans = WMSContext.TransactionDic[ExceedTranId] as OutOther; saleTrans = WMSContext.TransactionDic[ExceedTranId] as OutSale; tfTrans = WMSContext.TransactionDic[ExceedTranId] as OutTransfer; //领料单发料或者工单发料 if (MaterialReqTrans) { if (trans == null) { owoTrans = WMSContext.TransactionDic[ExceedTranId] as OutWorkOrder; if (!owoTrans.IsFinished) { lock (owoTrans.TransLock) { Logger.Interface.Info($"自动拆包 => 返回工单发料事务[{ExceedTranId}],开始确认发料并保存数据。"); var response = owoTrans.ConfirmExceed(new() { isExceed = true, AuthOption = option }).Result; action.IsSuccessed = response.IsSuccessed; action.LocaleMsg = response.IsSuccessed ? Biz.L(msg) : Biz.L(response.Message); } } else { action.IsSuccessed = false; action.LocaleMsg = Biz.L($"Transaction Error: 工单领料事务[ID:{ExceedTranId}]已经关闭,请重新打开工单领料功能"); } } else { if (!trans.IsFinished) { lock (trans.TransLock) { Logger.Interface.Info($"自动拆包 => 返回领料单发料事务[{ExceedTranId}],开始确认发料并保存数据。"); var response = trans.ConfirmExceed(new() { isExceed = true, AuthOption = option }).Result; action.IsSuccessed = response.IsSuccessed; action.LocaleMsg = response.IsSuccessed ? Biz.L(msg) : Biz.L(response.Message); } } else { action.IsSuccessed = false; action.LocaleMsg = Biz.L($"Transaction Error: 生产领料事务[ID:{ExceedTranId}]已经关闭,请重新打开生产领料功能"); } } } //其他出库发料 if (OthReqTrans) { if (!othTrans.IsFinished) { lock (othTrans.TransLock) { Logger.Interface.Info($"自动拆包 => 返回其他出库发料事务[{ExceedTranId}],开始确认发料并保存数据。"); var response = othTrans.ConfirmExceed(new() { isExceed = "true", AuthOption = option }).Result; action.IsSuccessed = response.IsSuccessed; action.LocaleMsg = response.IsSuccessed ? Biz.L(msg) : Biz.L(response.Message); } } else { action.IsSuccessed = false; action.LocaleMsg = Biz.L($"Transaction Error: 其他出库事务[ID:{ExceedTranId}]已经关闭,请重新打开其他出库功能"); } } //销售出库发料 //if (SaleReqTrans) //{ // if (!saleTrans.IsFinished) // { // lock (saleTrans.TransLock) // { // Logger.Interface.Info($"自动拆包 => 返回销售出库发料事务[{ExceedTranId}],开始确认发料并保存数据。"); // var response = saleTrans.ConfirmExceed(new() // { // isExceed = true, // AuthOption = option // }).Result; // action.IsSuccessed = response.IsSuccessed; // action.LocaleMsg = response.IsSuccessed ? Biz.L(msg) : Biz.L(response.Message); // } // } // else // { // action.IsSuccessed = false; // action.LocaleMsg = Biz.L($"Transaction Error: 销售出库事务[ID:{ExceedTranId}]已经关闭,请重新打开销售出库功能"); // } //} //调拨出库发料 if (TfTrans) { if (!tfTrans.IsFinished) { lock (tfTrans.TransLock) { Logger.Interface.Info($"自动拆包 => 返回调拨出库发料事务[{ExceedTranId}],开始确认发料并保存数据。"); var response = tfTrans.ConfirmExceed(new() { isExceed = "true", AuthOption = option }).Result; action.IsSuccessed = response.IsSuccessed; action.LocaleMsg = response.IsSuccessed ? Biz.L(msg) : Biz.L(response.Message); } } else { action.IsSuccessed = false; action.LocaleMsg = Biz.L($"Transaction Error: 调拨出库事务[ID:{ExceedTranId}]已经关闭,请重新打开调拨出库功能"); } } } else { action.IsSuccessed = false; action.LocaleMsg = Biz.L($"Transaction Error: 事务[ID:{ExceedTranId}]已经关闭,请重新打开功能"); } action.LocaleMsg = action.IsSuccessed ? Biz.L(msg) : action.LocaleMsg; } if (action.IsSuccessed) { //保存数据库 var db = Business.Biz.Db; var dbTran = db.UseTran(() => {//入库 db.Updateable(nSplitBarcode1, UserCode).ExecuteCommand(); db.Insertable(new WMS_ITEM_HIS(nSplitBarcode1, msg), UserCode).ExecuteCommand(); db.Insertable(nSplitBarcode2, UserCode).ExecuteCommand(); db.Insertable(new WMS_ITEM_HIS(nSplitBarcode2, msg), UserCode).ExecuteCommand(); db.Updateable(nSplitPgk1, UserCode).ExecuteCommand(); db.Insertable(nSplitPgk2, UserCode).ExecuteCommand(); }); if (!dbTran.IsSuccess) { Logger.Default.Fatal(dbTran.ErrorException, "Database transaction save exception"); this.Close(!dbTran.IsSuccess); throw dbTran.ErrorException; } //通过工厂带出 MES api 和 agv api MES_FACTORY _factory = db.Queryable() .Where(x => x.FTY_CODE == FTY_CODE) .First(); //发送到MES if (Cache.SysParam["IsSend", "SendToMES"].PARAM_VALUE.ToString() == "Y" && !ReqNo.IsNullOrEmpty() && Biz.Db.Queryable().Where(x => x.BILLCODE == ReqNo).Any()) { var dtl = Biz.Db.Queryable().Where(x => x.BILLCODE == ReqNo).First(); var sourcecode = dtl != null ? dtl.SOURCECODE : ""; var query = Biz.Db.Queryable().Where(x => x.ORIGINAL_WORKORDER == sourcecode).First(); var sctrwo = query != null ? (query.TARGET_WORKORDER.IsNullOrEmpty() ? sourcecode : query.TARGET_WORKORDER) : sourcecode; toMes = new ProductionPickToMes() { qrcode = WMS_ITEM_Biz.WmsItem.GetQRCode(nSplitBarcode1.SN), wo = sctrwo, // nCurrentLines.First().SourceCode, pkgid = nSplitBarcode1.SN, mfrkp = CurInv.Barcode.OEMItemCode, kpno = CurInv.ItemInfo.ITEM_CODE, dc = CurInv.Barcode.ProdDateStr, lc = CurInv.Barcode.LotNo, qty = nSplitBarcode1.QTY.ToString(), erpBillCode = ReqNo, //MES接口增加字段,返回备料完成的当前领料单号 2023/05/17 Ben Lin taskid = "" }; var mesApi = _factory != null ? _factory.MES_API : Cache.SysParam["apiUrl", "SendToMES"].PARAM_VALUE.ToString(); var res = HttpHelper.PostAsync(mesApi, JsonConvert.SerializeObject(toMes)).Result; var _action = JsonConvert.DeserializeObject(res.Message); Logger.Interface.Info($"生产领料-自动拆分物料 =>拆分物料发料发送MES完成,单号:[{ReqNo}],MES传入Json:{JsonConvert.SerializeObject(toMes)},MES返回:{res.Message}"); } } action.Data = new OutSplitSubmit() { nRemainSn = nRemainSn, nSplitSn = nSplitSn, SupplierCode = vender?.SUPP_CODE, }; } catch (Exception ex) { action.CatchExceptionWithLog(ex, $"系统异常"); } return action; } private ApiAction Confirm(T trans) { ApiAction action = new ApiAction(); 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); ActionHistoryList.Add($"{request.HttpContext.TraceIdentifier} at {action.Timestamp:yyyy/MM/dd HH:mm:ss.fff}: {request.Path}", his); LastActionTime = DateTime.Now; } #endregion public override bool Close(bool needSaveHistoryLog = false) { //needSaveHistoryLog = true; //保存操作日志 this.IsFinished = true; return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished; } }//endClass }