using System.Diagnostics; using System.Linq.Expressions; using System.Runtime.InteropServices; using JNPF.Common.Core.Manager; using JNPF.Common.Dtos.VisualDev; using JNPF.Common.Extension; using JNPF.Common.Security; using JNPF.EventBus; using JNPF.FriendlyException; using JNPF.Systems.Entitys.Permission; using JNPF.Systems.Interfaces.System; using JNPF.VisualDev; using JNPF.VisualDev.Entitys; using JNPF.VisualDev.Interfaces; using Mapster; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; using SqlSugar; using Tnb.BasicData.Entities; using Tnb.Common.Utils; using Tnb.WarehouseMgr.Entities; using Tnb.WarehouseMgr.Entities.Attributes; using Tnb.WarehouseMgr.Entities.Consts; using Tnb.WarehouseMgr.Entities.Dto; using Tnb.WarehouseMgr.Entities.Dto.Inputs; using Tnb.WarehouseMgr.Entities.Enums; using Tnb.WarehouseMgr.Interfaces; using Tnb.WarehouseMgr.Print; namespace Tnb.WarehouseMgr { /// /// 出库申请业务类 /// [OverideVisualDev(ModuleConsts.MODULE_WMSOUTSTOCK_ID)] [ServiceModule(BizTypeId)] public class WmsOutStockService : ServiceLoggerBase, IWmsOutStockService { private const string BizTypeId = "26191522660645"; private readonly ISqlSugarClient _db; private readonly IDictionaryDataService _dictionaryDataService; private readonly IRunService _runService; private readonly IVisualDevService _visualDevService; private readonly IWareHouseService _wareHouseService; private readonly IUserManager _userManager; private readonly IBillRullService _billRullService; private readonly IWmsCarryMoveInStockService _wmsCarryMoveInStockService; private readonly IWmsCarryService _wareCarryService; public WmsOutStockService( ISqlSugarRepository repository, IDictionaryDataService dictionaryDataService, IRunService runService, IVisualDevService visualDevService, IWareHouseService wareHouseService, IUserManager userManager, IBillRullService billRullService, IWmsCarryMoveInStockService wmsCarryMoveInStockService, IWmsCarryService wareCarryService, IEventPublisher eventPublisher) { _db = repository.AsSugarClient(); _dictionaryDataService = dictionaryDataService; _runService = runService; _visualDevService = visualDevService; _wareHouseService = wareHouseService; _userManager = userManager; _billRullService = billRullService; _wmsCarryMoveInStockService = wmsCarryMoveInStockService; _wareCarryService = wareCarryService; OverideFuncs.CreateAsync = OutStockApplyFor; _ = InitializationTask; } public async Task OutStockApplyFor(VisualDevModelDataCrInput input) { List outStockDList = new(); try { await _db.Ado.BeginTranAsync(); //判断目标库位是否自动签收 BasLocation? loc = null; if (input.data.ContainsKey(nameof(WmsPointH.location_id)) && input.data[nameof(WmsPointH.location_id)].IsNotEmptyOrNull()) { loc = await _db.Queryable().SingleAsync(it => it.id == input.data[nameof(WmsPointH.location_id)].ToString()); } else if (input.data.ContainsKey(nameof(WmsOutstockH.station_id)) && input.data[nameof(WmsOutstockH.station_id)].IsNotEmptyOrNull()) { //多个投料库位 /* * 天益 * 2、不管库位是否为空, 获取到所有库位 A B * 2.1 根据这些库位去查任务执行 目的库位是这些库位的未完成任务数。 A 10 B 9 * 2.2 哪个最少给哪个 */ OrganizeEntity? org = await _db.Queryable().FirstAsync(it => it.Id == input.data[nameof(WmsOutstockH.station_id)].ToString()); if (!org?.FeedingLocationId.IsNullOrWhiteSpace() ?? false) { List fLocIds = JArray.Parse(org.FeedingLocationId).Values().ToList(); var minTaskNumLocs = await _db.Queryable().Where(it => it.status != WmsWareHouseConst.PRETASK_BILL_STATUS_COMPLE_ID && fLocIds.Contains(it.endlocation_id)) .GroupBy(it => it.endlocation_id) .Select(it => new { it.endlocation_id, count = SqlFunc.AggregateCount(it.endlocation_id) }) .MergeTable() .OrderBy(it => it.count) .ToListAsync(); if (minTaskNumLocs?.Count > 0) { List freeLocIds = fLocIds.Except(minTaskNumLocs.Select(x => x.endlocation_id)).ToList(); if (freeLocIds?.Count > 0) { int rIdx = Random.Shared.Next(0, freeLocIds.Count); loc = await _db.Queryable().SingleAsync(it => it.id == freeLocIds[rIdx]); } else { string firstLocId = minTaskNumLocs.FirstOrDefault().endlocation_id; loc = await _db.Queryable().SingleAsync(it => it.id == firstLocId); } } else if (minTaskNumLocs?.Count < 1) { int rIdx = Random.Shared.Next(0, fLocIds.Count); loc = await _db.Queryable().SingleAsync(it => it.id == fLocIds[rIdx]); } input.data[nameof(WmsOutstockH.location_id)] = loc.id; } } List carryIds = new(); var mapKeys = new List { "tablefield120", "details" }; //tablefield120 出库物料明细 if (input.data.Keys.Any(k => mapKeys.Contains(k))) //input.data.ContainsKey("tablefield120") && input.data["tablefield120"].IsNotEmptyOrNull() { if (input.data.ContainsKey("tablefield120") && input.data["tablefield120"].IsNotEmptyOrNull()) { outStockDList = input.data["tablefield120"].ToObject>(); } else { outStockDList = input.data["details"].ToObject>(); } if (outStockDList?.Count > 0) { List carryMats = new(); List carryCodes = new(); List carryCodesPart = new(); foreach (WmsOutstockD os in outStockDList) { OutStockStrategyQuery OutStockStrategyInput = new() { carry_id = input.data[nameof(OutStockStrategyQuery.carry_id)]?.ToString() ?? string.Empty, warehouse_id = input.data[nameof(WmsOutstockH.warehouse_id)]?.ToString() ?? string.Empty, material_id = os.material_id, code_batch = os.code_batch, }; List outStkCarrys = await _wareHouseService.OutStockStrategy(OutStockStrategyInput); Expression> whereExp = input.data.ContainsKey(nameof(WmsOutstockH.carry_id)) && input.data[nameof(WmsOutstockH.carry_id)].IsNotEmptyOrNull() ? (a, b) => a.id == input.data[nameof(WmsOutstockH.carry_id)].ToString() : (a, b) => outStkCarrys.Select(x => x.id).Contains(b.carry_id); carryCodesPart = await _db.Queryable().InnerJoin((a, b) => a.id == b.carry_id).InnerJoin((a, b, c) => a.location_id == c.id) .Where(whereExp) .Select() .MergeTable() .OrderBy(it => it.create_time) .ToListAsync(); if (carryCodesPart?.Count > 0) { decimal codeQty = carryCodesPart.Sum(x => x.codeqty); if (codeQty < os.pr_qty) { throw new AppFriendlyException($"需要出库[{os.pr_qty}],实际库存{codeQty},数量不足", 500); } List curCarryCodes = new(); for (int i = 0; i < carryCodesPart.Count; i++) { if (os.pr_qty > carryCodesPart[i].codeqty) { os.pr_qty -= carryCodesPart[i].codeqty; curCarryCodes.Add(carryCodesPart[i]); } else if (os.pr_qty <= carryCodesPart[i].codeqty) { WmsCarryCode curCarryCode = DeepCopyHelper.DeepCopy(carryCodesPart[i]); curCarryCode.codeqty = os.pr_qty; curCarryCodes.Add(curCarryCode); break; } } carryCodes.AddRange(curCarryCodes); List partCarryMats = curCarryCodes.Adapt>(); for (int i = 0; i < partCarryMats.Count; i++) { partCarryMats[i].need_qty = curCarryCodes[i].codeqty; } carryMats.AddRange(partCarryMats); } } if (carryMats.Count > 0) { carryMats.ForEach(x => x.id = SnowflakeIdHelper.NextId()); carryMats = carryMats.OrderBy(o => o.create_time).GroupBy(g => new { g.carry_id, g.material_id, g.code_batch }) .Select(x => { WmsCarryMat[] arr = x.ToArray(); WmsCarryMat carryMat = arr[^arr.Length]; carryMat.need_qty = x.Sum(d => d.need_qty); return carryMat; }) .ToList(); _ = await _db.Insertable(carryMats).ExecuteCommandAsync(); Dictionary dic = carryMats.DistinctBy(x => x.carry_id).ToDictionary(x => x.carry_id, x => x.need_qty); List allOutIds = new(); List sortingOutIds = new(); foreach (KeyValuePair pair in dic) { List codes = carryCodesPart.FindAll(x => x.carry_id == pair.Key); if (codes?.Count > 0) { if (pair.Value == codes.Sum(d => d.codeqty)) { allOutIds.Add(pair.Key); } else { sortingOutIds.Add(pair.Key); } } } carryIds = allOutIds.Concat(sortingOutIds).ToList(); WmsCarryH carryH = new() { out_status = ((int)EnumOutStatus.全部出).ToString(), source_id = input.data.ContainsKey(nameof(WmsOutstockH.source_id)) ? input.data[nameof(WmsOutstockH.source_id)]?.ToString() ?? string.Empty : string.Empty, source_code = input.data.ContainsKey(nameof(WmsOutstockH.source_code)) ? input.data[nameof(WmsOutstockH.source_code)]?.ToString() ?? string.Empty : string.Empty, }; _ = await _db.Updateable(carryH) .UpdateColumns(it => new { it.out_status, it.source_id, it.source_code }) .Where(it => allOutIds.Contains(it.id)) .ExecuteCommandAsync(); carryH.out_status = ((int)EnumOutStatus.分拣出).ToString(); _ = await _db.Updateable(carryH) .UpdateColumns(it => new { it.out_status, it.source_id, it.source_code }) .Where(it => sortingOutIds.Contains(it.id)) .ExecuteCommandAsync(); } List carrys = await _db.Queryable().Where(it => carryIds.Contains(it.id)).ToListAsync(); if (carrys?.Count > 0) { VisualDevEntity? templateEntity = await _visualDevService.GetInfoById(ModuleConsts.MODULE_WMSOUTSTOCK_ID, true); await _runService.Create(templateEntity, input); List preTasks = new(); List locIds = new(); //当出现多个载具同时出库,可能需要进入电梯时 int ele = 2; foreach (WmsCarryH carry in carrys) { bool isMatch = await IsCarryAndLocationMatchByCarryStd(carry, loc); if (!isMatch) { throw new AppFriendlyException("该载具无法放置到目标库位", 500); } WmsPointH sPoint = null!; WmsPointH ePoint = null!; if (input.data.ContainsKey(nameof(WmsPointH.location_id))) { sPoint = await _db.Queryable().FirstAsync(it => it.location_id == carry.location_id); } if (input.data.ContainsKey(nameof(WmsPointH.location_id)) && input.data[nameof(WmsPointH.location_id)].IsNotEmptyOrNull()) { ePoint = await _db.Queryable().FirstAsync(it => it.location_id == loc.id); } if (sPoint != null && ePoint != null) { List points = await _wareHouseService.PathAlgorithmsEle(sPoint.id, ePoint.id, ele); locIds.AddRange(points.Select(x => x.location_id).ToList()!); //根据获取的路径点生成预任务,生成顺序必须预路径算法返回的起终点的顺序一致(预任务顺序) if (points?.Count > 0) { if (points.Count <= 2) { throw new AppFriendlyException("该路径不存在", 500); } List curPreTasks = points.Where(it => !it.location_id.IsNullOrEmpty()).GroupBy(g => g.area_code).Select(it => { WmsPointH? sPoint = it.FirstOrDefault(); WmsPointH? ePoint = it.LastOrDefault(); WmsPretaskH preTask = new() { org_id = _userManager.User.OrganizeId, startlocation_id = sPoint?.location_id ?? string.Empty, startlocation_code = sPoint?.location_code ?? string.Empty, endlocation_id = ePoint?.location_id ?? string.Empty, endlocation_code = ePoint?.location_code!, start_floor = sPoint?.floor.ToString(), end_floor = ePoint?.floor.ToString(), startpoint_id = sPoint?.id!, startpoint_code = sPoint?.point_code!, endpoint_id = ePoint?.id!, endpoint_code = ePoint?.point_code!, bill_code = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_PRETASK_H_ENCODE).GetAwaiter().GetResult(), status = WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID, biz_type = WmsWareHouseConst.BIZTYPE_WMSOUTSTOCK_ID, task_type = WmsWareHouseConst.WMS_PRETASK_OUTSTOCK_TYPE_ID, carry_id = carry.id, carry_code = carry.carry_code, area_id = sPoint?.area_id ?? string.Empty, area_code = it.Key, require_id = input.data["ReturnIdentity"].ToString(), require_code = input.data[nameof(preTask.bill_code)]?.ToString()!, create_id = _userManager.UserId, create_time = DateTime.Now }; return preTask; }).ToList(); if (loc.is_sign == 0) { curPreTasks[^1].is_sign = 0; // 修改最后一个元素的是否签收值 } preTasks.AddRange(curPreTasks); //判断当前任务中 是否有电梯任务。有的话ele+1 var eleP = curPreTasks.Find(x => x.area_code.Contains("ELE")); if (eleP != null) { ele++; } } } } List pretaskCodes = new(); foreach (WmsPretaskH pt in preTasks) { List partCodes = carryCodes.FindAll(x => x.carry_id == pt.carry_id).Distinct().ToList(); List curPreTaskCodes = partCodes.Adapt>(); curPreTaskCodes.ForEach(x => { x.id = SnowflakeIdHelper.NextId(); x.bill_id = pt.id; x.create_time = DateTime.Now; }); pretaskCodes.AddRange(curPreTaskCodes); } bool isOk = await _wareHouseService.GenPreTask(preTasks, pretaskCodes); if (isOk) { outStockDList.ForEach(x => x.line_status = WmsWareHouseConst.BILLSTATUS_ON_ID); _ = await _db.Updateable(outStockDList).UpdateColumns(it => it.line_status).ExecuteCommandAsync(); _ = await _db.Updateable().SetColumns(it => it.status == WmsWareHouseConst.BILLSTATUS_ON_ID).Where(it => it.id == input.data["ReturnIdentity"].ToString()).ExecuteCommandAsync(); GenPreTaskUpInput genPreTaskAfterUpInput = new() { CarryIds = preTasks.Select(x => x.carry_id).ToList(), LocationIds = new HashSet(locIds).ToList() }; await _wareHouseService.GenInStockTaskHandleAfter(genPreTaskAfterUpInput, it => new WmsCarryH { is_lock = 1 }, it => new BasLocation { is_lock = 1 }); } } else { throw new AppFriendlyException("库存不足", 500); } } else { throw new AppFriendlyException($"请输入物料明细", 500); } } await _db.Ado.CommitTranAsync(); } catch (Exception ex) { JNPF.Logging.Log.Error(ex.ToString()); await _db.Ado.RollbackTranAsync(); throw; } finally { await InvokeGenPretaskExcute(); } return Task.FromResult((input.data["ReturnIdentity"].ToString(), outStockDList)); } public dynamic? PrintTest(List barCodes) { // open port. int nLen, ret, sw; byte[] pbuf = new byte[128]; string strmsg; IntPtr ver; System.Text.Encoding encAscII = System.Text.Encoding.ASCII; _ = System.Text.Encoding.Unicode; // dll version. ver = PPLBUtility.B_Get_DLL_Version(0); // search port. nLen = PPLBUtility.B_GetUSBBufferLen() + 1; strmsg = "DLL "; strmsg += Marshal.PtrToStringAnsi(ver); strmsg += "\r\n"; if (nLen > 1) { byte[] buf1, buf2; int len1 = 128, len2 = 128; buf1 = new byte[len1]; buf2 = new byte[len2]; _ = PPLBUtility.B_EnumUSB(pbuf); _ = PPLBUtility.B_GetUSBDeviceInfo(1, buf1, out len1, buf2, out len2); sw = 1; if (1 == sw) { ret = PPLBUtility.B_CreatePrn(12, encAscII.GetString(buf2, 0, len2));// open usb. } else { ret = PPLBUtility.B_CreateUSBPort(1);// must call B_GetUSBBufferLen() function fisrt. } if (0 != ret) { } else { strmsg += "Open USB:\r\nDevice name: "; strmsg += encAscII.GetString(buf1, 0, len1); strmsg += "\r\nDevice path: "; _ = encAscII.GetString(buf2, 0, len2); //sw = 2; if (2 == sw) { //Immediate Error Report. _ = PPLBUtility.B_WriteData(1, encAscII.GetBytes("^ee\r\n"), 5);//^ee ret = PPLBUtility.B_ReadData(pbuf, 4, 1000); } } } else { _ = System.IO.Directory.CreateDirectory(PPLBUtility.szSavePath); ret = PPLBUtility.B_CreatePrn(0, PPLBUtility.szSaveFile);// open file. strmsg += "Open "; strmsg += PPLBUtility.szSaveFile; if (0 != ret) { } else { } } if (0 != ret) { return null; } // sample setting. //B_Set_DebugDialog(1); //var sznop2 = "测试"; //var sznop1 = "测试2"; _ = PPLBUtility.B_Set_Originpoint(0, 0); _ = PPLBUtility.B_Select_Option(2); _ = PPLBUtility.B_Set_Darkness(8); _ = PPLBUtility.B_Del_Pcx("*");// delete all picture. //PPLBUtility.B_WriteData(0, encAscII.GetBytes(sznop2), sznop2.Length); //PPLBUtility.B_WriteData(1, encAscII.GetBytes(sznop1), sznop1.Length); //When using standard label, and the printer is Intelli Print mode or Smart Print mode, //When calling this function and giving the correct label information, //the immediate print function will be enabled according to the label length setting. _ = PPLBUtility.B_Set_LabelForSmartPrint(254 * 3, 30);//label information: length= 3 * 25.4 mm, gap= 3 mm. //draw box. //PPLBUtility.B_Draw_Box(20, 20, 4, 760, 560); //PPLBUtility.B_Draw_Line('O', 400, 20, 4, 540); //print text, true type text. _ = PPLBUtility.B_Prn_Text(250, 50, 0, 2, 2, 2, 'N', "PPLB Lib Example"); //PPLBUtility.B_Prn_Text_TrueType(30, 100, 30, "Arial", 1, 400, 0, 0, 0, "AA", "TrueType Font");//save in printer. //PPLBUtility.B_Prn_Text_TrueType_W(30, 160, 20, 20, "Times New Roman", 1, 400, 0, 0, 0, "AB", "TT_W: 多字元測試"); //PPLBUtility.B_Prn_Text_TrueType_Uni(30, 220, 30, "Times New Roman", 1, 400, 0, 0, 0, "AC", Encoding.Unicode.GetBytes("TT_Uni: 多字元測試"), 1);//UTF-16 //encUnicode.GetBytes("\xFEFF", 0, 1, pbuf, 0);//UTF-16.//pbuf[0]=0xFF,pbuf[1]=0xFE; //encUnicode.GetBytes("TT_UniB: 多字元測試", 0, 14, pbuf, 2);//copy mutil byte. //encUnicode.GetBytes("\x0000", 0, 1, pbuf, 30);//null.//pbuf[30]=0x00,pbuf[31]=0x00; //PPLBUtility.B_Prn_Text_TrueType_UniB(30, 280, 30, "Times New Roman", 1, 400, 0, 0, 0, "AD", pbuf, 0);//Byte Order Mark. int labelWth = 800; int barcodeWth = 508; _ = (labelWth - barcodeWth) / 2; //barcode. _ = PPLBUtility.B_Prn_Barcode(150, 100, 0, "E80", 8, 500, 70, 'N', "1234<+10>");//have a counter //PPLBUtility.B_Bar2d_QR(420, 200, 1, 3, 'M', 'A', 0, 0, 0, "QR CODE"); //picture. //PPLBUtility.B_Get_Graphic_ColorBMP(420, 280, "bb.bmp");// Color bmp file. //PPLBUtility.B_Get_Graphic_ColorBMPEx(420, 320, 200, 150, 2, "bb1", "bb.bmp");//180 angle. //IntPtr himage = LoadImage(IntPtr.Zero, "bb.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //PPLBUtility.B_Get_Graphic_ColorBMP_HBitmap(630, 280, 250, 80, 1, "bb2", himage);//90 angle. //if (IntPtr.Zero != himage) // DeleteObject(himage); // output. _ = PPLBUtility.B_Print_Out(1);// copy 2. // close port. PPLBUtility.B_ClosePrn(); return "success"; } /// /// 根据出库申请单ID获取申请单明细信息 /// /// /// [HttpGet] public async Task GetInStockDetailsListById([FromRoute] string billId) { Dictionary dic = await _dictionaryDataService.GetDictionaryByTypeId(WmsWareHouseConst.WMS_INSTOCK_D_BILL_STATUS_TYPEID); List items = await _db.Queryable().Where(it => it.bill_id == billId).ToListAsync(); _db.ThenMapper(items, it => it.line_status = dic.ContainsKey(it.line_status) ? dic[it.line_status]?.ToString()! : ""); return items; } /// /// 生产出库接口 /// /// /// [HttpPost] //[NonUnify] public async Task MESCreateOutstock(MESCreateOutstockInput input) { bool isSuccessful = true; try { await _db.Ado.BeginTranAsync(); //出库申请主表 WmsOutstockH outstock = input.outstock.Adapt(); //出库申请明细表 List outstockDs = input.outstockDs.Adapt>(); BasLocation location = await _db.Queryable().SingleAsync(it => it.location_code == input.outstock.location_code && it.is_type != EnumLocationType.存储库位.ToString()); //如果数据不全或有误, if (location.IsNull() || outstockDs?.Count < 1) { //报错, 提示数据不全或有误。 throw new AppFriendlyException("数据不全或有误!", 500); } // 生成出库申请数据,添加其他数据 主表 outstock.id = SnowflakeIdHelper.NextId(); outstock.location_id = location.id; outstock.biz_type = WmsWareHouseConst.BIZTYPE_WMSOUTSTOCK_ID; outstock.bill_code = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_OUTSTOCK_ENCODE).GetAwaiter().GetResult(); outstock.generate_type = "1";// 自动 outstock.sync_status = WmsWareHouseConst.SYNC_STATUS__NOTSYNC;//未同步 outstock.status = WmsWareHouseConst.BILLSTATUS_ADD_ID;// 新增 outstock.create_id = _userManager.UserId; outstock.create_time = DateTime.Now; //明细表 foreach (WmsOutstockD outstockD in outstockDs!) { outstockD.id = SnowflakeIdHelper.NextId(); outstockD.bill_id = outstock.id; outstockD.line_status = WmsWareHouseConst.BILLSTATUS_ADD_ID; outstockD.qty = 0; outstock.create_time = outstock.create_time; outstock.create_id = outstock.create_id; } //var loc = await _db.Queryable().SingleAsync(it => it.id == outstock.location_id.ToString()); List? carryIds = new(); //tablefield120 出库物料明细 List outStockDList = outstockDs.ToObject>(); if (outStockDList?.Count > 0) { List carryMats = new(); List carryCodes = new(); foreach (WmsOutstockD os in outStockDList) { Expressionable whereExp = Expressionable.Create() .And((a, b, c) => a.is_lock == 0) .And((a, b, c) => !string.IsNullOrEmpty(a.location_id)) .And((a, b, c) => a.status == (int)EnumCarryStatus.占用) .And((a, b, c) => c.is_type == ((int)EnumLocationType.存储库位).ToString()) .And((a, b, c) => b.material_id == os.material_id) .AndIF(!string.IsNullOrEmpty(os.code_batch), (a, b, c) => b.code_batch == os.code_batch); List carryCodesPart = await _db.Queryable().InnerJoin((a, b) => a.id == b.carry_id).InnerJoin((a, b, c) => a.location_id == c.id) .Where(whereExp.ToExpression()) .Select() .ToListAsync(); if (carryCodesPart?.Count > 0) { decimal codeQty = carryCodes.Sum(x => x.codeqty); if (codeQty < os.pr_qty) { throw new AppFriendlyException($"需要出库[{os.pr_qty}],实际库存{codeQty},数量不足", 500); } List curCarryCodes = new(); for (int i = 0; i < carryCodesPart.Count; i++) { if (os.pr_qty > carryCodesPart[i].codeqty) { os.pr_qty -= carryCodesPart[i].codeqty; curCarryCodes.Add(carryCodesPart[i]); } else if (os.pr_qty <= carryCodesPart[i].codeqty) { WmsCarryCode tmp = DeepCopyHelper.DeepCopy(carryCodesPart[i]); tmp.codeqty = os.pr_qty; curCarryCodes.Add(tmp); break; } } carryCodes.AddRange(curCarryCodes); List partCarryMats = curCarryCodes.Adapt>(); for (int i = 0; i < partCarryMats.Count; i++) { partCarryMats[i].need_qty = curCarryCodes[i].codeqty; } carryMats.AddRange(partCarryMats); } } if (carryMats.Count > 0) { carryMats.ForEach(x => x.id = SnowflakeIdHelper.NextId()); carryMats = carryMats.OrderBy(o => o.create_time).GroupBy(g => new { g.carry_id, g.material_id, g.code_batch }) .Select(x => { WmsCarryMat[] arr = x.ToArray(); WmsCarryMat carryMat = arr[^arr.Length]; carryMat.need_qty = x.Sum(d => d.need_qty); return carryMat; }) .ToList(); _ = await _db.Insertable(carryMats).ExecuteCommandAsync(); Dictionary dic = carryMats.DistinctBy(x => x.carry_id).ToDictionary(x => x.carry_id, x => x.need_qty); List allOutIds = new(); List sortingOutIds = new(); foreach (KeyValuePair pair in dic) { List codes = carryCodes.FindAll(x => x.carry_id == pair.Key); if (codes?.Count > 0) { if (pair.Value == codes.Sum(d => d.codeqty)) { allOutIds.Add(pair.Key); } else { sortingOutIds.Add(pair.Key); } } } carryIds = allOutIds.Concat(sortingOutIds).ToList(); if (carryIds?.Count > 0) { string carryId = carryIds[^carryIds.Count]; WmsCarryH curCurry = await _db.Queryable().SingleAsync(it => it.id == carryId); bool isMatch = await IsCarryAndLocationMatchByCarryStd(curCurry, location); if (!isMatch) { throw new AppFriendlyException("该载具无法放置到目标库位", 500); } } _ = await _db.Insertable(outstock).ExecuteCommandAsync(); _ = await _db.Insertable(outstockDs).ExecuteCommandAsync(); _ = await _db.Updateable().SetColumns(it => new WmsCarryH { out_status = ((int)EnumOutStatus.全部出).ToString(), source_id = outstock.source_id, source_code = outstock.source_code }).Where(it => allOutIds.Contains(it.id)).ExecuteCommandAsync(); _ = await _db.Updateable().SetColumns(it => new WmsCarryH { out_status = ((int)EnumOutStatus.分拣出).ToString(), source_id = outstock.source_id, source_code = outstock.source_code }).Where(it => sortingOutIds.Contains(it.id)).ExecuteCommandAsync(); } List carrys = await _db.Queryable().Where(it => carryIds.Contains(it.id)).ToListAsync(); if (carrys?.Count > 0) { List preTasks = new(); List locIds = new(); foreach (WmsCarryH carry in carrys) { WmsPointH sPoint = await _db.Queryable().FirstAsync(it => it.location_id == carry.location_id); WmsPointH ePoint = await _db.Queryable().FirstAsync(it => it.location_id == outstock.location_id.ToString()); if (sPoint != null && ePoint != null) { List points = await _wareHouseService.PathAlgorithms(sPoint.id, ePoint.id); locIds.AddRange(points.Select(x => x.location_id).ToList()!); //根据获取的路径点生成预任务,生成顺序必须预路径算法返回的起终点的顺序一致(预任务顺序) if (points?.Count > 0) { if (points.Count <= 2) { throw new AppFriendlyException("该路径不存在", 500); } List curPreTasks = points.Where(it => !it.location_id.IsNullOrEmpty()).GroupBy(g => g.area_code).Select(it => { WmsPointH? sPoint = it.FirstOrDefault(); WmsPointH? ePoint = it.LastOrDefault(); WmsPretaskH preTask = new() { org_id = _userManager.User.OrganizeId, startlocation_id = sPoint?.location_id ?? string.Empty, startlocation_code = sPoint?.location_code ?? string.Empty, endlocation_id = ePoint?.location_id ?? string.Empty, endlocation_code = ePoint?.location_code ?? string.Empty, start_floor = sPoint?.floor.ToString(), end_floor = ePoint?.floor.ToString(), bill_code = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_PRETASK_H_ENCODE).GetAwaiter().GetResult(), status = WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID, biz_type = WmsWareHouseConst.BIZTYPE_WMSOUTSTOCK_ID, task_type = WmsWareHouseConst.WMS_PRETASK_OUTSTOCK_TYPE_ID, carry_id = carry.id, carry_code = carry.carry_code, area_id = sPoint?.area_id ?? string.Empty, area_code = it.Key, require_id = outstock.id.ToString(), require_code = outstock.bill_code?.ToString(), create_id = _userManager.UserId, create_time = DateTime.Now, source_id = outstock.source_id, source_code = outstock.source_code, }; return preTask; }).ToList(); if (location.is_sign == 0) { curPreTasks[^1].is_sign = 0; // 修改最后一个元素的是否签收值 } preTasks.AddRange(curPreTasks); } } } List pretaskCodes = new(); foreach (WmsPretaskH pt in preTasks) { List partCodes = carryCodes.FindAll(x => x.carry_id == pt.carry_id).Distinct().ToList(); List curPreTaskCodes = partCodes.Adapt>(); curPreTaskCodes.ForEach(x => { x.id = SnowflakeIdHelper.NextId(); x.bill_id = pt.id; x.create_time = DateTime.Now; }); pretaskCodes.AddRange(curPreTaskCodes); } bool isOk = await _wareHouseService.GenPreTask(preTasks, pretaskCodes); if (isOk) { outstockDs.ForEach(x => x.line_status = WmsWareHouseConst.BILLSTATUS_ON_ID); _ = await _db.Updateable(outstockDs).UpdateColumns(it => it.line_status).ExecuteCommandAsync(); _ = await _db.Updateable().SetColumns(it => new WmsOutstockH { status = WmsWareHouseConst.BILLSTATUS_ON_ID }).Where(it => it.id == outstock.id).ExecuteCommandAsync(); GenPreTaskUpInput genPreTaskAfterUpInput = new() { CarryIds = preTasks.Select(x => x.carry_id).ToList(), LocationIds = new HashSet(locIds).ToList() }; await _wareHouseService.GenInStockTaskHandleAfter(genPreTaskAfterUpInput, it => new WmsCarryH { is_lock = 1 }, it => new BasLocation { is_lock = 1 }); } } else { throw new AppFriendlyException("库存不足", 500); } } else { throw new AppFriendlyException($"请输入物料明细", 500); } await _db.Ado.CommitTranAsync(); } catch (Exception ex) { isSuccessful = false; JNPF.Logging.Log.Error(ex.Message); await _db.Ado.RollbackTranAsync(); throw; } finally { await InvokeGenPretaskExcute(); } return isSuccessful; } public override async Task ModifyAsync(WareHouseUpInput input) { if (input == null) { throw new ArgumentNullException(nameof(input)); } try { Stopwatch sw = Stopwatch.StartNew(); await _db.Ado.BeginTranAsync(); string carryId = input.carryIds[^input.carryIds.Count]; WmsCarryH carry = await _db.Queryable().SingleAsync(it => it.id == carryId); if (carry != null) { List? otds = await _db.Queryable().Where(it => it.bill_id == input.requireId).ToListAsync(); EnumOutStatus outStatus = carry.out_status.ToEnum(); if (outStatus == EnumOutStatus.全部出) { Stopwatch swAllOut = Stopwatch.StartNew(); //当前载具对应的所有条码插入 List carryCodes = await _db.Queryable().Where(it => it.carry_id == carryId).ToListAsync(); List outStockCodes = carryCodes.Adapt>(); outStockCodes.ForEach(x => { string? billDId = otds?.Find(xx => xx.material_id == x.material_id && xx.code_batch == x.code_batch)?.id; if (billDId.IsNullOrEmpty()) { billDId = otds?.Find(xx => xx.material_id == x.material_id)?.id; } x.id = SnowflakeIdHelper.NextId(); x.bill_id = input.requireId; x.bill_d_id = billDId!; x.org_id = _userManager.User.OrganizeId; x.create_id = _userManager.UserId; x.create_time = DateTime.Now; }); _ = await _db.Insertable(outStockCodes).ExecuteCommandAsync(); List detailIds = outStockCodes.Select(x => x.bill_d_id).ToList(); List curOutstockDetails = otds.FindAll(x => detailIds.Contains(x.id)); Dictionary> dic = outStockCodes.GroupBy(g => g.bill_d_id).ToDictionary(x => x.Key, x => x.Select(x => x.codeqty).ToList()); foreach (WmsOutstockD osd in curOutstockDetails) { if (dic.ContainsKey(osd.id)) { osd.qty += dic[osd.id].Sum(d => d); osd.line_status = osd.qty >= osd.pr_qty ? WmsWareHouseConst.BILLSTATUS_COMPLETE_ID : WmsWareHouseConst.BILLSTATUS_ON_ID; } } _ = await _db.Updateable(curOutstockDetails).ExecuteCommandAsync(); if (otds.All(x => x.line_status == WmsWareHouseConst.BILLSTATUS_COMPLETE_ID)) { _ = await _db.Updateable().SetColumns(it => new WmsOutstockH { status = WmsWareHouseConst.BILLSTATUS_COMPLETE_ID }).Where(it => it.id == input.requireId).ExecuteCommandAsync(); //如果是自动单据,需要回更上层系统 } else { //如果没有完成,修改为工作中 _ = await _db.Updateable().SetColumns(it => new WmsOutstockH { status = WmsWareHouseConst.BILLSTATUS_ON_ID }).Where(it => it.id == input.requireId).ExecuteCommandAsync(); } _ = _wareCarryService.UpdateNullCarry(carry).Unwrap(); swAllOut.Stop(); Logger.Information($"全部出耗时:{swAllOut.ElapsedMilliseconds}ms"); } else if (outStatus == EnumOutStatus.分拣出) { Stopwatch swFJOut = Stopwatch.StartNew(); if (input.distaskCodes?.Count > 0) { List osCodes = input.distaskCodes.Adapt>(); osCodes.ForEach(x => { string? billDId = otds?.Find(xx => xx.material_id == x.material_id && xx.code_batch == x.code_batch)?.id; if (billDId.IsNullOrEmpty()) { billDId = otds?.Find(xx => xx.material_id == x.material_id)?.id; } x.id = SnowflakeIdHelper.NextId(); x.bill_id = input.requireId; x.bill_d_id = billDId!; x.org_id = _userManager.User?.OrganizeId; x.create_id = _userManager.UserId; x.create_time = DateTime.Now; }); _ = await _db.Insertable(osCodes).ExecuteCommandAsync(); // 更新主表 List detailIds = osCodes.Select(x => x.bill_d_id).ToList(); List curOutstockDetails = otds.FindAll(x => detailIds.Contains(x.id)); Dictionary> dic = osCodes.GroupBy(g => g.bill_d_id).ToDictionary(x => x.Key, x => x.Select(x => x.codeqty).ToList()); foreach (WmsOutstockD osd in curOutstockDetails) { if (dic.ContainsKey(osd.id)) { osd.qty += dic[osd.id].Sum(d => d); if (osd.qty >= osd.pr_qty) { osd.qty = osd.pr_qty; osd.line_status = WmsWareHouseConst.BILLSTATUS_COMPLETE_ID; } else { osd.line_status = WmsWareHouseConst.BILLSTATUS_ON_ID; } } } _ = await _db.Updateable(curOutstockDetails).ExecuteCommandAsync(); if (otds.All(x => x.line_status == WmsWareHouseConst.BILLSTATUS_COMPLETE_ID)) { //如果是自动单据,需要回更上层系统 _ = await _db.Updateable().SetColumns(it => new WmsOutstockH { status = WmsWareHouseConst.BILLSTATUS_COMPLETE_ID }).Where(it => it.id == input.requireId).ExecuteCommandAsync(); } else { //如果没有完成,修改为工作中 _ = await _db.Updateable().SetColumns(it => new WmsOutstockH { status = WmsWareHouseConst.BILLSTATUS_ON_ID }).Where(it => it.id == input.requireId).ExecuteCommandAsync(); } List carryCodes = await _db.Queryable().Where(it => input.carryIds.Contains(it.carry_id!)).ToListAsync(); Dictionary dicCodeQty = carryCodes.GroupBy(g => g.barcode).ToDictionary(x => x.Key, x => x.First().codeqty); Dictionary dicUpdate = new(); List delBarcodes = new(); foreach (WmsDistaskCode dtc in input.distaskCodes) { if (dicCodeQty.ContainsKey(dtc.barcode)) { if (dtc.codeqty < dicCodeQty[dtc.barcode]) { dicUpdate[dtc.barcode] = dicCodeQty[dtc.barcode] - dtc.codeqty; } else { delBarcodes.Add(dtc.barcode); } } } if (dicUpdate.Count > 0) { foreach (KeyValuePair pair in dicUpdate) { WmsCarryCode? carryCode = carryCodes.Find(x => x.barcode == pair.Key); if (carryCode != null) { carryCode.codeqty = pair.Value; _ = await _db.Updateable(carryCode).UpdateColumns(it => it.codeqty).ExecuteCommandAsync(); } } } if (delBarcodes.Count > 0) { _ = await _db.Deleteable().Where(it => delBarcodes.Contains(it.barcode)).ExecuteCommandAsync(); } int row = await _db.Updateable().SetColumns(it => new WmsCarryH { out_status = ((int)EnumOutStatus.正常).ToString() }).Where(it => input.carryIds.Contains(it.id)).ExecuteCommandAsync(); _ = await _db.Deleteable().Where(it => input.carryIds.Contains(it.carry_id)).ExecuteCommandAsync(); swFJOut.Stop(); Logger.Information($"出库申请运行耗时{swFJOut.ElapsedMilliseconds}ms"); sw = Stopwatch.StartNew(); //载具移入 WmsOutstockH outStockH = await _db.Queryable().SingleAsync(it => it.id == input.requireId); VisualDevModelDataCrInput visulDevInput = new() { data = new Dictionary { [nameof(InStockStrategyQuery.warehouse_id)] = outStockH.warehouse_id, [nameof(WmsPointH.location_id)] = outStockH.location_id, [nameof(WmsCarryD.carry_id)] = input.carryIds.First(), [nameof(WmsCarryH.carry_code)] = carry.carry_code, [nameof(WmsHandleH.biz_type)] = input.bizTypeId, [nameof(WmsHandleH.create_id)] = _userManager.UserId, [nameof(WmsHandleH.create_time)] = DateTime.Now, [nameof(WmsMoveInstock.status)] = WmsWareHouseConst.BILLSTATUS_ADD_ID, [nameof(WmsHandleH.bill_code)] = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_CARRYMOINSTK_ENCODE).GetAwaiter().GetResult(), } }; _ = _wmsCarryMoveInStockService.CarryMoveIn(visulDevInput); Logger.Information($"载具移入耗时{sw.ElapsedMilliseconds}ms"); await _db.Ado.CommitTranAsync(); } } } } catch (Exception) { await _db.Ado.RollbackTranAsync(); throw; } } } }