using HslCommunication; using MailKit.Search; using Microsoft.Data.SqlClient; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using OfficeOpenXml.Style; using Org.BouncyCastle.Asn1.X509; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WIDESEA_Common; using WIDESEA_Common.LocationEnum; using WIDESEA_Common.OrderEnum; using WIDESEA_Common.StockEnum; using WIDESEA_Common.TaskEnum; using WIDESEA_Common.WareHouseEnum; using WIDESEA_Core; using WIDESEA_Core.BaseRepository; using WIDESEA_Core.BaseServices; using WIDESEA_Core.Enums; using WIDESEA_Core.Helper; using WIDESEA_DTO.SquareCabin; using WIDESEA_IBasicService; using WIDESEA_ISquareCabinServices; using WIDESEA_IWMsInfoServices; using WIDESEA_Model.Models; using static WIDESEA_DTO.SquareCabin.OrderDto; namespace WIDESEA_SquareCabinServices { public partial class DeliveryOrderServices : ServiceBase>, IDeliveryOrderServices { private readonly IBasicService _basicService; private readonly IUnitOfWorkManage _unitOfWorkManage; private readonly IInventory_BatchServices _inventory_BatchServices; private readonly IInventoryInfoService _inventoryInfoService; private readonly IDeliveryOrderDetailServices _deliveryOrderDetailServices; private readonly ISupplyTaskService _supplyTaskService; private readonly ICabinOrderServices _cabinOrderServices; private readonly ITacticsService _tacticsService; public IRepository Repository => BaseDal; public DeliveryOrderServices(IRepository BaseDal, IBasicService basicService, IUnitOfWorkManage unitOfWorkManage, IInventory_BatchServices inventory_BatchServices, IInventoryInfoService inventoryInfoService, IDeliveryOrderDetailServices deliveryOrderDetailServices, ISupplyTaskService supplyTaskService, ICabinOrderServices cabinOrderServices, ITacticsService tacticsService) : base(BaseDal) { _basicService = basicService; _unitOfWorkManage = unitOfWorkManage; _deliveryOrderDetailServices = deliveryOrderDetailServices; _supplyTaskService = supplyTaskService; _inventory_BatchServices = inventory_BatchServices; _inventoryInfoService = inventoryInfoService; _cabinOrderServices = cabinOrderServices; _tacticsService = tacticsService; } static string SearchDate = "2025-09-10 00:00:00"; public WebResponseContent GetUpstreamOutOrder() { var responseContent = new WebResponseContent(); try { var url = "http://121.37.118.63:80/GYZ2/95fck/outOrder"; //var url = "http://127.0.0.1:4523/m1/5660322-5340849-default/GYZ2/95fck/outOrder"; if (string.IsNullOrEmpty(SearchDate)) SearchDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); //// 请求参数 var requestData = new { searchDate = SearchDate //searchDate = "2022-10-10 20:45:16" // 正确的格式 }; SearchDate = DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd HH:mm:ss"); var result = HttpHelper.Post(url, requestData.ToJsonString()); var response = JsonConvert.DeserializeObject>(result); if (response.resultCode != "0") { SendErrorToUpstream(3, "", response.resultMsg ?? "上游接口返回失败", ""); return responseContent.Error(response.resultMsg ?? "上游接口返回失败"); } if (response.data == null || !response.data.Any()) { return responseContent.OK("无新出库单数据"); } // 已存在出库单号 var existingOutOrderNos = BaseDal.Db.Queryable() .Select(x => x.Out_no) .ToList(); // 新出库单 var newOutOrders = response.data .Where(outorder => !existingOutOrderNos.Contains(outorder.order_no)) .ToList(); List order_no = newOutOrders.Select(x => x.order_no).ToList(); if (!newOutOrders.Any()) { return responseContent.OK("所有出库单已存在,无需新增"); } Db.Ado.BeginTran(); try { List _DeliveryOrders = new List(); List orderNos = new List(); foreach (var outorder in newOutOrders) { if (outorder.order_type == "1") // 正常出库单 { CreateOutboundOrder(outorder);//创建出库单、处理库存、添加出库任务 } else if (outorder.order_type == "2")//出库退货 { // 创建出库退货单 - 这里需要根据业务逻辑实现 // 暂时跳过或实现退货逻辑 //continue; #region 转换成入库单 UpstreamOrderInfo order = new UpstreamOrderInfo() { order_no = outorder.order_no, order_type = outorder.order_type, warehouse_no = outorder.warehouse_no, details = new List() }; foreach (var item in outorder.details) { UpstreamOrderDetail detail = new UpstreamOrderDetail() { batch_num = item.batch_num, goods_no = item.goods_no, order_qty = item.order_qty, exp_date = item.exp_date, }; order.details.Add(detail); } responseContent = _cabinOrderServices.CreateInboundOrder(order); List? dt_CabinOrders = responseContent.Data as List; if (dt_CabinOrders != null && dt_CabinOrders.Count > 0) BaseDal.Db.InsertNav(dt_CabinOrders).Include(x => x.Details).ExecuteCommand(); #endregion } else if (outorder.order_type == "3")//报损出库 { } } // 批量插入出库单和明细 //if (_DeliveryOrders.Any()) //{ // BaseDal.Db.InsertNav(_DeliveryOrders).Include(x => x.Details).ExecuteCommand(); //} //下发出库单任务给wcs Db.Ado.CommitTran(); //var tex = CreateSupplyTask(order_no); //if (!tex) //{ // return responseContent.Error("创建供应任务失败"); //} return responseContent.OK($"同步出库单成功,共{_DeliveryOrders.Count}条"); } catch (Exception ex) { Db.Ado.RollbackTran(); SendErrorToUpstream(3, "", ex.Message, ""); return responseContent.Error("同步失败: " + ex.Message); } } catch (Exception ex) { SendErrorToUpstream(3, "", ex.Message, ""); return responseContent.Error("同步失败: " + ex.Message); } } /// /// 创建出库单 /// /// /// public WebResponseContent CreateOutboundOrder(UpstramOutOrderInfo outorder) { WebResponseContent webResponseContent = new WebResponseContent(); try { Dt_Tactics tactics = _tacticsService.Repository.QueryFirst(x => x.TacticeName == "出库策略"); if (outorder.warehouse_no == WarehouseEnum.麻精库.ObjToInt().ToString("000") || outorder.warehouse_no == WarehouseEnum.冷冻库.ObjToInt().ToString("000")) { #region 添加出库单 var entityOrder = new Dt_DeliveryOrder { Out_no = outorder.order_no, Out_type = outorder.order_type, Client_no = outorder.client_no, Client_name = outorder.client_name, Account_time = outorder.account_time, Warehouse_no = outorder.warehouse_no, OutStatus = "新建", Details = outorder.details.Select(d => new Dt_DeliveryOrderDetail { Reservoirarea = outorder.warehouse_no, Goods_no = d.goods_no, Order_qty = Math.Abs(d.order_qty), // 出库数量转为正数 Batch_num = d.batch_num, Exp_date = d.exp_date, OotDetailStatus = "新建", Status = 2, // pad平库,无需同步 }).ToList() }; BaseDal.Db.InsertNav(entityOrder).Include(x => x.Details).ExecuteCommand(); #endregion #region 处理库存、库存批次、添加出库任务 foreach (var item in entityOrder.Details) { Dt_Inventory_Batch inventory_Batch = _inventory_BatchServices.Repository.QueryFirst(x => x.MaterielCode == item.Goods_no && x.BatchNo == item.Batch_num); if (inventory_Batch == null) throw new Exception($"未找到出库单号【{entityOrder.Out_no}】中物料编号【{item.Goods_no}】物料批次【{item.Batch_num}】的库存批次信息"); if (inventory_Batch.AvailableQuantity < item.Order_qty) throw new Exception($"出库单号【{entityOrder.Out_no}】中物料编号【{item.Goods_no}】物料批次【{item.Batch_num}】的库存批次信息可用数量不足"); inventory_Batch.AvailableQuantity -= item.Order_qty; inventory_Batch.OutboundQuantity += item.Order_qty; List dt_InventoryInfos = _inventoryInfoService.Repository.QueryData(x => x.MaterielCode == item.Goods_no && x.BatchNo == item.Batch_num && x.StockStatus == StockStatusEmun.入库完成.ObjToInt() && x.StockQuantity > x.OutboundQuantity && x.WarehouseCode == item.Reservoirarea); #region 按出库策略查找库存 if (tactics.SelectTactice == TacticsEnum.ComeOutonFirstTime.ObjToInt()) dt_InventoryInfos = dt_InventoryInfos.OrderBy(x => x.ValidityPeriod).ToList(); else dt_InventoryInfos = dt_InventoryInfos.OrderBy(x => x.InDate).ToList(); #endregion var Order_qty = item.Order_qty;//出库单数量 foreach (var InventoryInfo in dt_InventoryInfos) { if (Order_qty <= 0) break; if (InventoryInfo.AvailableQuantity < Order_qty) { InventoryInfo.AvailableQuantity = 0; Order_qty -= InventoryInfo.AvailableQuantity; InventoryInfo.OutboundQuantity += InventoryInfo.AvailableQuantity; InventoryInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); } else { InventoryInfo.AvailableQuantity -= Order_qty; Order_qty = 0; InventoryInfo.OutboundQuantity += Order_qty; InventoryInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); } } dt_InventoryInfos = dt_InventoryInfos.Where(X => X.StockStatus == StockStatusEmun.出库锁定.ObjToInt()).ToList(); List supplyTasks = new List(); foreach (var inventoryInfo in dt_InventoryInfos) { Dt_SupplyTask supplyTask = new Dt_SupplyTask() { WarehouseCode = inventoryInfo.WarehouseCode, BatchNo = inventoryInfo.BatchNo, MaterielName = inventoryInfo.MaterielName, MaterielCode = inventoryInfo.MaterielCode, MaterielSpec = inventoryInfo.MaterielSpec, TaskStatus = SupplyStatusEnum.NewOut.ObjToInt(), TaskType = TaskTypeEnum.OutPick.ObjToInt(), CreateDate = DateTime.Now, Creater = App.User.UserName ?? "System", LocationCode = inventoryInfo.LocationCode, OrderNo = entityOrder.Out_no, StockQuantity = inventoryInfo.OutboundQuantity, SupplyQuantity = 0, Remark = "出库" }; supplyTasks.Add(supplyTask); } _inventory_BatchServices.Repository.UpdateData(inventory_Batch); _inventoryInfoService.Repository.UpdateData(dt_InventoryInfos); _supplyTaskService.AddData(supplyTasks); } #endregion } else { #region 创建大件库、立库出库头表 var entityOrder = new Dt_DeliveryOrder { Out_no = outorder.order_no, Out_type = outorder.order_type, OutStatus = "新建", Client_name = outorder.client_name, Account_time = outorder.account_time, Client_no = outorder.client_no, Warehouse_no = WarehouseEnum.大件库.ObjToInt().ToString("000"), Details = new List() }; var entityOrderLK = new Dt_DeliveryOrder { Out_no = outorder.order_no, Out_type = outorder.order_type, OutStatus = "新建", Client_name = outorder.client_name, Account_time = outorder.account_time, Client_no = outorder.client_no, Warehouse_no = WarehouseEnum.立库.ObjToInt().ToString("000"), Details = new List() }; #endregion #region 查找库存 foreach (var detail in outorder.details) { #region 查询库存批次和库存 Dt_Inventory_Batch inventory_Batch = _inventory_BatchServices.Repository.QueryFirst(x => x.MaterielCode == detail.goods_no && x.BatchNo == detail.batch_num); if (inventory_Batch == null) throw new Exception($"未找到出库单号【{outorder.order_no}】中物料编号【{detail.goods_no}】物料批次【{detail.batch_num}】的库存批次信息"); if (inventory_Batch.AvailableQuantity < detail.order_qty) throw new Exception($"出库单号【{outorder.order_no}】中物料编号【{detail.goods_no}】物料批次【{detail.batch_num}】的库存批次信息可用数量不足"); inventory_Batch.AvailableQuantity -= detail.order_qty; inventory_Batch.OutboundQuantity += detail.order_qty; List dt_InventoryInfos = _inventoryInfoService.Repository.QueryData(x => x.MaterielCode == inventory_Batch.MaterielCode && x.BatchNo == inventory_Batch.BatchNo && x.StockStatus == StockStatusEmun.入库完成.ObjToInt() && x.StockQuantity > x.OutboundQuantity && (x.WarehouseCode == WarehouseEnum.大件库.ObjToInt().ToString("000") || x.WarehouseCode == WarehouseEnum.立库.ObjToInt().ToString("000"))); #endregion #region 按出库策略查找库存 if (tactics.SelectTactice == TacticsEnum.ComeOutonFirstTime.ObjToInt()) dt_InventoryInfos = dt_InventoryInfos.OrderBy(x => x.ValidityPeriod).ToList(); else dt_InventoryInfos = dt_InventoryInfos.OrderBy(x => x.InDate).ToList(); #endregion var Order_qty = Math.Abs(detail.order_qty);//出库单数量 #region 根据物料编码查询物料信息 Dt_MaterielInfo materielInfo = _basicService.MaterielInfoService.Repository.QueryFirst(x => x.MaterielCode == detail.goods_no); if (materielInfo == null) throw new Exception($"未找到药品编码【{detail.goods_no}】的信息"); if (!Enum.IsDefined(typeof(MaterielSourceTypeEnum), materielInfo.MaterielSourceType)) throw new Exception($"请设置药品编号【{detail.goods_no}】的属性分类"); if (materielInfo.BoxQty < 1) throw new Exception($"请设置药品编号【{detail.goods_no}】的箱规数量"); #endregion #region 大件 if (materielInfo.MaterielSourceType == MaterielSourceTypeEnum.PurchasePart)//如果物料是大件 { #region 添加出库详情 Dt_DeliveryOrderDetail orderDetail = new Dt_DeliveryOrderDetail() { Reservoirarea = entityOrder.Warehouse_no, Goods_no = detail.goods_no, Order_qty = detail.order_qty, Batch_num = detail.batch_num, Exp_date = detail.exp_date, OotDetailStatus = "新建", Order_Outqty = 0, Status = 2 }; entityOrder.Details.Add(orderDetail); #endregion #region 计算库存 foreach (var InventoryInfo in dt_InventoryInfos) { if (Order_qty <= 0) break; if (InventoryInfo.AvailableQuantity < Order_qty) { InventoryInfo.AvailableQuantity = 0; Order_qty -= InventoryInfo.AvailableQuantity; InventoryInfo.OutboundQuantity += InventoryInfo.AvailableQuantity; InventoryInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); } else { InventoryInfo.AvailableQuantity -= Order_qty; Order_qty = 0; InventoryInfo.OutboundQuantity += Order_qty; InventoryInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); } } #endregion #region 生成出库任务 dt_InventoryInfos = dt_InventoryInfos.Where(X => X.StockStatus == StockStatusEmun.出库锁定.ObjToInt()).ToList(); List supplyTasks = new List(); foreach (var inventoryInfo in dt_InventoryInfos) { Dt_SupplyTask supplyTask = new Dt_SupplyTask() { WarehouseCode = inventoryInfo.WarehouseCode, BatchNo = inventoryInfo.BatchNo, MaterielName = inventoryInfo.MaterielName, MaterielCode = inventoryInfo.MaterielCode, MaterielSpec = inventoryInfo.MaterielSpec, TaskStatus = SupplyStatusEnum.NewOut.ObjToInt(), TaskType = TaskTypeEnum.OutPick.ObjToInt(), CreateDate = DateTime.Now, Creater = App.User.UserName ?? "System", LocationCode = inventoryInfo.LocationCode, OrderNo = entityOrder.Out_no, StockQuantity = inventoryInfo.OutboundQuantity, SupplyQuantity = 0, Remark = "出库" }; supplyTasks.Add(supplyTask); } #endregion _inventory_BatchServices.Repository.UpdateData(inventory_Batch); _inventoryInfoService.Repository.UpdateData(dt_InventoryInfos); _supplyTaskService.AddData(supplyTasks); } #endregion else { Dt_DeliveryOrderDetail orderDetail = null; Dt_DeliveryOrderDetail orderDetailLK = null; var ys = Order_qty % materielInfo.BoxQty; //不能整除箱规的散件数 var xs = (int)(Order_qty / materielInfo.BoxQty);//保留整数 #region 散件优先分配立库 if (ys > 0) { orderDetailLK = new Dt_DeliveryOrderDetail() { Reservoirarea = entityOrderLK.Warehouse_no, Goods_no = detail.goods_no, Order_qty = ys, Batch_num = detail.batch_num, Exp_date = detail.exp_date, OotDetailStatus = "新建", Order_Outqty = 0, Status = 0 }; } #endregion #region 整件优先分配大件库 foreach (var item in dt_InventoryInfos.Where(x => x.WarehouseCode == WarehouseEnum.大件库.ObjToInt().ToString("000"))) { if (xs <= 0) break; item.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); while (item.AvailableQuantity > 0 && xs > 0) { xs--; if (orderDetail == null) { orderDetail = new Dt_DeliveryOrderDetail() { Reservoirarea = entityOrder.Warehouse_no, Goods_no = detail.goods_no, Order_qty = materielInfo.BoxQty, Batch_num = detail.batch_num, Exp_date = detail.exp_date, OotDetailStatus = "新建", Order_Outqty = 0, Status = 0 }; item.AvailableQuantity -= materielInfo.BoxQty; item.OutboundQuantity += materielInfo.BoxQty; } else { orderDetail.Order_qty += materielInfo.BoxQty; item.AvailableQuantity -= materielInfo.BoxQty; item.OutboundQuantity += materielInfo.BoxQty; } } } #endregion #region 分配完大件库如果还有箱数,再选择分配立库 if (orderDetailLK == null) { orderDetailLK = new Dt_DeliveryOrderDetail() { Reservoirarea = entityOrderLK.Warehouse_no, Goods_no = detail.goods_no, Order_qty = xs * materielInfo.BoxQty, Batch_num = detail.batch_num, Exp_date = detail.exp_date, OotDetailStatus = "新建", Order_Outqty = 0, Status = 0 }; } else { orderDetailLK.Order_qty += xs * materielInfo.BoxQty; } #endregion List supplyTasks = new List(); if (orderDetailLK != null) { #region 添加出库任务、修改库存信息 Dt_InventoryInfo inventoryInfo = dt_InventoryInfos.Where(x => x.WarehouseCode == WarehouseEnum.立库.ObjToInt().ToString("000")).First(); inventoryInfo.AvailableQuantity -= orderDetailLK.Order_qty; inventoryInfo.OutboundQuantity += orderDetailLK.Order_qty; inventoryInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); _inventoryInfoService.Repository.UpdateData(inventoryInfo); Dt_SupplyTask supplyTask = new Dt_SupplyTask() { WarehouseCode = inventoryInfo.WarehouseCode, BatchNo = inventoryInfo.BatchNo, MaterielName = inventoryInfo.MaterielName, MaterielCode = inventoryInfo.MaterielCode, MaterielSpec = inventoryInfo.MaterielSpec, TaskStatus = SupplyStatusEnum.NewOut.ObjToInt(), TaskType = TaskTypeEnum.OutPick.ObjToInt(), CreateDate = DateTime.Now, Creater = App.User.UserName ?? "System", LocationCode = inventoryInfo.LocationCode, OrderNo = entityOrder.Out_no, StockQuantity = inventoryInfo.OutboundQuantity, SupplyQuantity = 0, Remark = "出库" }; supplyTasks.Add(supplyTask); #endregion entityOrderLK.Details.Add(orderDetailLK); } if (orderDetail != null) { #region 添加出库任务、修改库存信息 dt_InventoryInfos = dt_InventoryInfos.Where(X => X.StockStatus == StockStatusEmun.出库锁定.ObjToInt()).ToList(); foreach (var inventoryInfo in dt_InventoryInfos) { Dt_SupplyTask supplyTask = new Dt_SupplyTask() { WarehouseCode = inventoryInfo.WarehouseCode, BatchNo = inventoryInfo.BatchNo, MaterielName = inventoryInfo.MaterielName, MaterielCode = inventoryInfo.MaterielCode, MaterielSpec = inventoryInfo.MaterielSpec, TaskStatus = SupplyStatusEnum.NewOut.ObjToInt(), TaskType = TaskTypeEnum.OutPick.ObjToInt(), CreateDate = DateTime.Now, Creater = App.User.UserName ?? "System", LocationCode = inventoryInfo.LocationCode, OrderNo = entityOrder.Out_no, StockQuantity = inventoryInfo.OutboundQuantity, SupplyQuantity = 0, Remark = "出库" }; supplyTasks.Add(supplyTask); } #endregion entityOrder.Details.Add(orderDetail); } _inventory_BatchServices.Repository.UpdateData(inventory_Batch); _inventoryInfoService.Repository.UpdateData(dt_InventoryInfos); _supplyTaskService.AddData(supplyTasks); } } if (entityOrder.Details.Count > 0) BaseDal.Db.InsertNav(entityOrder).Include(x => x.Details).ExecuteCommand(); if (entityOrderLK.Details.Count > 0) BaseDal.Db.InsertNav(entityOrderLK).Include(x => x.Details).ExecuteCommand(); #endregion } } catch (Exception ex) { webResponseContent.Error(ex.Message); } return webResponseContent; } /// /// 生成出库单任务 /// /// 出库单号 /// private bool CreateSupplyTask(List order_no) { try { // 先获取符合条件的主表数据 var mainOrders = BaseDal.Db.Queryable() .Where(x => order_no.Contains(x.Out_no)) .ToList(); // 获取所有主表ID var mainOrderIds = mainOrders.Select(x => x.Id).ToList(); // 直接查询子表数据 List filteredDetails = BaseDal.Db.Queryable() .Where(d => mainOrderIds.Contains(d.DeliveryOrderId) && d.OotDetailStatus == "新建") .ToList(); // 获取所有需要查询的物料编码 var materielCodes = filteredDetails.Select(x => x.Goods_no).Distinct().ToList(); // 批量查询物料信息 var materielList = BaseDal.Db.Queryable() .Where(x => materielCodes.Contains(x.MaterielCode)) .ToList(); List supplyTasks = new List(); foreach (var item in filteredDetails) { // 根据子表物料编码查询物料信息 var medication = materielList.FirstOrDefault(x => x.MaterielCode == item.Goods_no); // 查找对应的主表信息 var mainOrder = mainOrders.FirstOrDefault(x => x.Id == item.DeliveryOrderId); //随机生成任务编号加时间戳 if (mainOrder == null) continue; var location = Db.Queryable() .Where(x => x.MaterielCode == ""//materielCode && x.BatchNo == ""//batchNo && x.StockQuantity > 0) .OrderBy(x => x.InDate) // 按入库时间排序 .First(); var supplyTask = new Dt_SupplyTask { TaskNum = GenerateTaskNumber(), PalletCode = "0", BatchNo = item.Batch_num, MaterielCode = item.Goods_no, MaterielName = medication?.MaterielName, // 如果物料信息不存在,使用子表的物料名称 MaterielSpec = medication?.MaterielSpec, // 如果物料信息不存在,使用子表的物料规格 TaskType = 0, TaskStatus = item.Status == 0 ? 0 : 1, WarehouseCode = mainOrder.Warehouse_no, LocationCode = item.Status == 0 ? "立库" : "平库", StockQuantity = item.Order_qty, // 假设子表有Quantity字段 SupplyQuantity = 0, OrderNo = mainOrder.Out_no, Dispatchertime = DateTime.Now, Remark = "" }; supplyTasks.Add(supplyTask); } // 批量插入供应任务 if (supplyTasks.Any()) { return BaseDal.Db.Insertable(supplyTasks).ExecuteCommand() > 0; } return false; } catch (Exception ex) { // 记录日志 Console.WriteLine($"创建供应任务失败:{ex.Message}"); return false; } } /// /// 生成随机任务编号(随机数 + 时间戳) /// /// 任务编号 private int GenerateTaskNumber() { // 生成4位随机数 Random random = new Random(Guid.NewGuid().GetHashCode()); int randomNum = random.Next(1000, 9999); // 1000-9999之间的随机数 // 组合成任务编号:TASK + 时间戳 + 随机数 return randomNum; } /// /// 出库单推送给 WCS /// /// public WebResponseContent EdiOut() { var responseContent = new WebResponseContent(); try { // 1. 查询符合条件的订单(表头=新建 && 包含有效明细) var outOrders = BaseDal.Db.CopyNew().Queryable() .Where(o => o.OutStatus == "新建" && o.Warehouse_no == WarehouseEnum.立库.ObjToInt().ToString("000")) .Includes(o => o.Details, d => d.MedicineGoods) .ToList(); // 2. 再过滤掉不符合条件的明细(只保留 Status=0 ) foreach (var order in outOrders) { order.Details = order.Details.Where(d => d.Status == 0).ToList(); } if (outOrders == null || !outOrders.Any()) { Console.WriteLine("没有符合条件的订单需要推送"); return responseContent.Error("没有符合条件的订单需要推送"); } foreach (var order in outOrders) { try { string materialCode = "YY";//默认值 //获取当前订单的第一个明细项 var firstDetail = order.Details.FirstOrDefault(); if (firstDetail?.MedicineGoods != null && !string.IsNullOrEmpty(firstDetail.MedicineGoods.MaterielErpType)) { //如果条件满足,将物料代码设置为第一个明细项对应的药品物料代码 materialCode = firstDetail.MedicineGoods.MaterielErpType; } // 3. 组装 DTO var ediDto = new TowcsDto.ToediOutInfo { customerCode = "905", materialCode = materialCode, externalOrderNo = order.Out_no, outOrderType = order.Out_type == "1" ? "10" : order.Out_type == "3" ? "20" : "30",//出库单类型是1就是正常的,3就是盘点 priority = 1, Is_cancel = 0, details = order.Details.Select(d => new TowcsDto.ToeOutdiInDetail { batchNo = d.Batch_num, productCode = d.Goods_no, productName = d.MedicineGoods?.MaterielName, productSpecifications = d.MedicineGoods?.MaterielModel, quantity = (int)d.Order_qty, //stocktakingDetails = order.Out_type == "3" // ? new List // { // new ToOutediInStock { palletCode = "FC00001", quantity = d.Order_qty.ToString() } // } // : null }).ToList() }; // 4. 调用接口 var url = "http://172.16.1.2:9357/file-admin/api/out/ediOut"; //var url = "http://127.0.0.1:4523/m2/5660322-5340849-default/363076920"; var result = HttpHelper.Post(url, ediDto.ToJsonString()); var resp = JsonConvert.DeserializeObject>(result); if (resp != null && resp.code == "0") { // 更新表头状态 BaseDal.Db.Updateable() .SetColumns(o => new Dt_DeliveryOrder { OutStatus = "开始" }) .Where(o => o.Id == order.Id) .ExecuteCommand(); // 更新明细状态 BaseDal.Db.Updateable() .SetColumns(d => new Dt_DeliveryOrderDetail { Status = 1, OotDetailStatus = "开始" }) .Where(d => d.DeliveryOrderId == order.Id && d.Status == 0) .ExecuteCommand(); Console.WriteLine($"订单 {order.Out_no} 推送成功"); } else { SendErrorToUpstream(3, order.Out_no, resp?.msg ?? "WCS 推送失败", ""); Console.WriteLine($"订单 {order.Out_no} 推送失败:{resp?.msg}"); } //删除全部状为已完成的明细和表头,移入历史表 } catch (Exception ex) { SendErrorToUpstream(3, order.Out_no, ex.Message, ""); Console.WriteLine($"订单 {order.Out_no} 推送异常:{ex.Message}"); } } return responseContent.OK("出库订单推送完成"); } catch (Exception ex) { Console.WriteLine("EdiOut 异常:" + ex.Message); return responseContent.Error("出库订单推送失败:" + ex.Message); } } //盘点 拿整个批次信息表的商品批号和商品编号来进行盘点 public WebResponseContent InventoryGood(string batchNo, string goodsNo) { var response = new WebResponseContent(); try { // 1️⃣ 查找指定批次与物料的库存信息 var batchInfo = BaseDal.Db.Queryable() .Where(x => x.BatchNo == batchNo && x.MaterielCode == goodsNo) .First(); if (batchInfo == null) return response.Error($"未找到该物料 [{goodsNo}] 批次 [{batchNo}] 的库存信息"); // 2️⃣ 组装请求 DTO(完全符合接口文档结构) var ediDto = new TowcsDto.ToediOutInfo { customerCode = "905", materialCode = "YY", // 物料类型CODE externalOrderNo = $"PDCK-{batchInfo.Id}", // 外部出库单号 outOrderType = "20", // 盘点出库单 priority = 1, Is_cancel = 0, details = new List { new TowcsDto.ToeOutdiInDetail { batchNo = batchInfo.BatchNo, productCode = batchInfo.MaterielCode, productName = batchInfo.MaterielName, productSpecifications = batchInfo.MaterielSpec, quantity = (int)batchInfo.SupplyQuantity, //stocktakingDetails = new List //{ // // 盘点明细可根据实际托盘拆分;此处示例仅1条 // new TowcsDto.ToOutediInStock // { // palletCode = "FC00001", // quantity = batchInfo.SupplyQuantity.ToString() // } //} } } }; // 3️⃣ 调用接口 string url = "http://172.16.1.2:9357/file-admin/api/out/ediOut"; var result = HttpHelper.Post(url, ediDto.ToJsonString()); var resp = JsonConvert.DeserializeObject>(result); // 4️⃣ 响应处理 if (resp == null) return response.Error("WCS 无响应"); if (resp.code != "0") return response.Error($"WCS返回失败: {resp.msg}"); return response.OK("盘点出库下发成功"); } catch (Exception ex) { return response.Error("盘点失败:" + ex.Message); } } /// /// 出库报完成接口 /// /// 出库单号 /// public WebResponseContent CompleteAllOutOrders() { var responseContent = new WebResponseContent(); try { #region 查找所有已完成出库单 var outorders = BaseDal.QueryData(x => x.OutStatus == "已完成").Select(x => x.Out_no).Distinct().ToList(); foreach (var outorder in outorders) { var Orders = BaseDal.Db.Queryable().Where(x => x.Out_no == outorder).Includes(x => x.Details).ToList(); if (!Orders.Where(x => x.OutStatus != "已完成").Any()) { BaseDal.Db.Ado.BeginTran(); BaseDal.DeleteAndMoveIntoHty(Orders, OperateTypeEnum.自动完成); foreach (var item in Orders) { _deliveryOrderDetailServices.Repository.DeleteAndMoveIntoHty(item.Details, OperateTypeEnum.自动完成); } var url = "http://121.37.118.63:80/GYZ2/95fck/outOrderOk"; var requestDate = new { order_no = outorder }; var result = HttpHelper.Post(url, requestDate.ToJsonString()); var response = JsonConvert.DeserializeObject(result); if (response.resultCode == "0") BaseDal.Db.Ado.CommitTran(); else { BaseDal.Db.Ado.RollbackTran(); SendErrorToUpstream(4, "", $"上游接口返回失败: {response.resultMsg}", outorder); } } } #endregion return responseContent.OK(); } catch (Exception ex) { BaseDal.Db.Ado.RollbackTran(); return responseContent.Error("批量处理失败:" + ex.Message); } } /// /// 推送异常信息给上游系统 1.入库单接口;2.入库单报完成接口;3.出库单接口;4.出库报完成接口;5.药品基础信息同步接口;6.供应商信息接口;7.客户信息接口;8.库存查询接口 /// public void SendErrorToUpstream(int type, string code, string message, string remark) { try { var url = "http://121.37.118.63:80/GYZ2/95fck/exceptionLog"; var requestData = new { type = type.ToString(), code = code, message = message, remark = remark }; var result = HttpHelper.Post(url, requestData.ToJsonString()); // 可以反序列化检查 resultCode 是否为0 } catch (Exception e) { // 这里不要再抛异常了,避免死循环 Console.WriteLine("异常接口推送失败:" + e.Message); } } /// /// 查询出库单列表 /// /// /// public WebResponseContent GetDeliveryOrders(SaveModel saveModel) { WebResponseContent content = new WebResponseContent(); try { int pageNo = saveModel.MainData["pageNo"].ObjToInt(); string warehouseCode = saveModel.MainData["warehouseId"].ToString(); string orderNo = saveModel.MainData["orderNo"].ToString(); List dt_ReceiveOrders = new List(); if (string.IsNullOrEmpty(orderNo)) { dt_ReceiveOrders = Db.Queryable().Where(x => (x.OutStatus == "新建" || x.OutStatus == "开始") && x.Warehouse_no == warehouseCode && x.Out_type != "3").Includes(x => x.Details).OrderByDescending(x => x.CreateDate).ToPageList(pageNo, 5); } else { dt_ReceiveOrders = Db.Queryable().Where(x => (x.Out_no.Contains(orderNo) || x.Client_no.Contains(orderNo)) && (x.OutStatus == "新建" || x.OutStatus == "开始") && x.Out_type != "3" && x.Warehouse_no == warehouseCode).OrderByDescending(x => x.CreateDate).Includes(x => x.Details).ToPageList(pageNo, 5); } content.OK(data: dt_ReceiveOrders); } catch (Exception) { throw; } return content; } /// /// 查询出库/盘点单详情 看出库单明细。 /// /// /// /// /// public WebResponseContent GetDeliveryOrderDetail(int pageNo, string orderNo, bool isPick) { WebResponseContent content = new WebResponseContent(); Dt_DeliveryOrder cabinOrder = new Dt_DeliveryOrder(); if (isPick) cabinOrder = Db.Queryable().Includes(x => x.Details).First(x => x.Out_no == orderNo && x.Out_type == "3"); else cabinOrder = Db.Queryable().Includes(x => x.Details).First(x => x.Out_no == orderNo && x.Out_type != "3"); //List? cabinOrderDetails = cabinOrder.Details?.Where(x => x.Reservoirarea == pageNo.ToString()).ToList(); List? cabinOrderDetails = cabinOrder.Details?.Where(x => x.Status == 2).ToList(); content.OK(data: cabinOrderDetails); return content; } public WebResponseContent OutFinish(SaveModel saveModel) { WebResponseContent content = new WebResponseContent(); try { var LocationCode = saveModel.MainData["locationCode"].ToString(); var TaskId = saveModel.MainData["taskId"].ObjToInt(); Dt_SupplyTask supplyTask = _supplyTaskService.Repository.QueryFirst(x => x.TaskId == TaskId && x.TaskStatus == SupplyStatusEnum.NewOut.ObjToInt()); if (supplyTask == null) throw new Exception("当前出库任务已完成"); if (supplyTask.LocationCode != LocationCode) throw new Exception($"当前出库货位【{LocationCode}】与任务分配货位不匹配"); OutTaskFinish(supplyTask); } catch (Exception ex) { content.Error(ex.Message); } return content; } public WebResponseContent OutTaskFinish(Dt_SupplyTask supplyTask) { WebResponseContent content = new WebResponseContent(); try { Dt_DeliveryOrder cabinOrder = BaseDal.Db.Queryable().Where(x => x.Out_no == supplyTask.OrderNo && x.Warehouse_no == supplyTask.WarehouseCode).Includes(x => x.Details).First(); if (cabinOrder == null) return WebResponseContent.Instance.Error($"出库单已完成"); Dt_DeliveryOrderDetail cabinOrderDetail = cabinOrder.Details.Where(x => x.Batch_num == supplyTask.BatchNo && x.Reservoirarea == supplyTask.WarehouseCode && x.Goods_no == supplyTask.MaterielCode).First(); if (cabinOrderDetail == null) return WebResponseContent.Instance.Error($"出库单明细已完成"); Dt_MaterielInfo materielInfo = _basicService.MaterielInfoService.Repository.QueryFirst(x => x.MaterielCode == cabinOrderDetail.Goods_no); if (materielInfo == null) return WebResponseContent.Instance.Error($"请维护物料编号【{cabinOrderDetail.Goods_no}】的物料信息"); cabinOrderDetail.Order_Outqty += supplyTask.StockQuantity; if (cabinOrderDetail.Order_Outqty > cabinOrderDetail.Order_qty) return WebResponseContent.Instance.Error($"出库数量不可超出单据数量"); #region 处理出库单,货位,库存,库存批次信息,出库任务 _unitOfWorkManage.BeginTran(); #region 出库单 cabinOrder.OutStatus = "开始"; cabinOrderDetail.OotDetailStatus = "开始"; if (cabinOrderDetail.Order_Outqty == cabinOrderDetail.Order_qty) { cabinOrderDetail.OotDetailStatus = "已完成"; //_deliveryOrderDetailServices.Repository.DeleteAndMoveIntoHty(cabinOrderDetail, OperateTypeEnum.自动完成); } _deliveryOrderDetailServices.Repository.UpdateData(cabinOrderDetail); var cabinOrder1 = BaseDal.Db.Queryable().Where(x => x.Out_no == cabinOrder.Out_no && x.Warehouse_no == supplyTask.WarehouseCode && x.Out_type != "3").Includes(x => x.Details).First(); if (!cabinOrder1.Details.Where(x => x.OotDetailStatus != "已完成").Any()) cabinOrder.OutStatus = "已完成"; Repository.UpdateData(cabinOrder); #endregion #region 库存 Dt_InventoryInfo inventoryInfo = _inventoryInfoService.Repository.QueryFirst(x => x.BatchNo == cabinOrderDetail.Batch_num && x.MaterielCode == cabinOrderDetail.Goods_no && x.LocationCode == supplyTask.LocationCode); if (inventoryInfo == null) return WebResponseContent.Instance.Error($"未找到货位【{supplyTask.LocationCode}】的库存信息"); inventoryInfo.StockQuantity -= supplyTask.StockQuantity; inventoryInfo.OutboundQuantity -= supplyTask.StockQuantity; if (inventoryInfo.StockQuantity <= 0) _inventoryInfoService.DeleteData(inventoryInfo); else _inventoryInfoService.UpdateData(inventoryInfo); #endregion #region 货位 if (supplyTask.WarehouseCode != WarehouseEnum.立库.ObjToInt().ToString("000")) { var location = _basicService.LocationInfoService.Repository.QueryFirst(x => x.LocationCode == supplyTask.LocationCode); if (location == null) return WebResponseContent.Instance.Error($"请维护货位编号【{supplyTask.LocationCode}】的货位信息"); //if (location.EnableStatus == EnableStatusEnum.Disable.ObjToInt()) // return WebResponseContent.Instance.Error($"货位编号【{supplyTask.LocationCode}】已禁用,请恢复正常再使用"); Dt_InventoryInfo inventoryInfo1 = _inventoryInfoService.Repository.QueryFirst(x => x.LocationCode == supplyTask.LocationCode); if (inventoryInfo1 == null) { location.LocationStatus = LocationStatusEnum.Free.ObjToInt(); _basicService.LocationInfoService.UpdateData(location); } } #endregion //_supplyTaskService.UpdateData(supplyTask); supplyTask.TaskNum = cabinOrderDetail.Id; supplyTask.TaskStatus = SupplyStatusEnum.OutFinish.ObjToInt(); _supplyTaskService.Repository.DeleteAndMoveIntoHty(supplyTask, OperateTypeEnum.人工完成); #region 库存批次 Dt_Inventory_Batch inventory_Batch = _inventory_BatchServices.Repository.QueryFirst(x => x.BatchNo == inventoryInfo.BatchNo && x.MaterielCode == inventoryInfo.MaterielCode); if (inventory_Batch != null) { inventory_Batch.StockQuantity -= supplyTask.StockQuantity; inventory_Batch.OutboundQuantity -= supplyTask.StockQuantity; if (inventory_Batch.StockQuantity <= 0) _inventory_BatchServices.DeleteData(inventory_Batch); else _inventory_BatchServices.UpdateData(inventory_Batch); } #endregion if (supplyTask.WarehouseCode == WarehouseEnum.立库.ObjToInt().ToString("000")) { materielInfo.Business_qty -= supplyTask.StockQuantity; _basicService.MaterielInfoService.Repository.UpdateData(materielInfo); if (materielInfo.Business_qty < materielInfo.MinQty) CreateAllocatInOut(materielInfo);//创建调拨任务 } _unitOfWorkManage.CommitTran(); #endregion content.OK(); } catch (Exception ex) { content.Error(ex.Message); } return content; } /// /// 平库人工拣料出库处理(即人工扫码出库时调用) /// /// /// public WebResponseContent MatPicking(SaveModel saveModel) { WebResponseContent content = new WebResponseContent(); try { var LocationCode = saveModel.MainData["LocationCode"].ToString(); var orderNo = saveModel.MainData["orderNo"].ToString(); var batchNo = saveModel.MainData["batchNo"].ToString(); var Inqty = saveModel.MainData["Inqty"].ObjToInt(); var warehouseCode = saveModel.MainData["warehouseCode"].ToString(); Dt_DeliveryOrder cabinOrder = BaseDal.Db.Queryable().Where(x => x.Out_no == orderNo && x.Warehouse_no == warehouseCode && x.Out_type != "3").Includes(x => x.Details).First(); if (cabinOrder == null) return WebResponseContent.Instance.Error($"出库单已完成"); Dt_DeliveryOrderDetail cabinOrderDetail = cabinOrder.Details.Where(x => x.Batch_num == batchNo && x.Reservoirarea == warehouseCode).First(); if (cabinOrderDetail == null) return WebResponseContent.Instance.Error($"出库单明细已完成"); Dt_MaterielInfo materielInfo = _basicService.MaterielInfoService.Repository.QueryFirst(x => x.MaterielCode == cabinOrderDetail.Goods_no); if (materielInfo == null) return WebResponseContent.Instance.Error($"请维护物料编号【{cabinOrderDetail.Goods_no}】的物料信息"); cabinOrderDetail.Order_Outqty += Inqty; if (cabinOrderDetail.Order_Outqty > cabinOrderDetail.Order_qty) return WebResponseContent.Instance.Error($"出库数量不可超出单据数量"); #region 处理出库单,货位,库存,库存批次信息 _unitOfWorkManage.BeginTran(); #region 出库单 cabinOrder.OutStatus = "开始"; cabinOrderDetail.OotDetailStatus = "开始"; if (cabinOrderDetail.Order_Outqty == cabinOrderDetail.Order_qty) { cabinOrderDetail.OotDetailStatus = "已完成"; _deliveryOrderDetailServices.Repository.DeleteAndMoveIntoHty(cabinOrderDetail, OperateTypeEnum.自动完成); } else { _deliveryOrderDetailServices.Repository.UpdateData(cabinOrderDetail); } var cabinOrder1 = BaseDal.Db.Queryable().Where(x => x.Out_no == cabinOrder.Out_no && x.Out_type != "3").Includes(x => x.Details).First(); if (cabinOrder1.Details == null || cabinOrder1.Details.Count < 1) cabinOrder.OutStatus = "已完成"; Repository.UpdateData(cabinOrder); #endregion #region 库存 Dt_InventoryInfo inventoryInfo = _inventoryInfoService.Repository.QueryFirst(x => x.BatchNo == cabinOrderDetail.Batch_num && x.MaterielCode == cabinOrderDetail.Goods_no && x.LocationCode == LocationCode); if (inventoryInfo == null) return WebResponseContent.Instance.Error($"未找到货位【{LocationCode}】的库存信息"); inventoryInfo.StockQuantity -= Inqty; if (inventoryInfo.StockQuantity <= 0) _inventoryInfoService.DeleteData(inventoryInfo); else _inventoryInfoService.UpdateData(inventoryInfo); #endregion #region 货位 var location = _basicService.LocationInfoService.Repository.QueryFirst(x => x.LocationCode == LocationCode); if (location == null) return WebResponseContent.Instance.Error($"请维护货位编号【{LocationCode}】的货位信息"); if (location.EnableStatus == EnableStatusEnum.Disable.ObjToInt()) return WebResponseContent.Instance.Error($"货位编号【{LocationCode}】已禁用,请恢复正常再使用"); Dt_InventoryInfo inventoryInfo1 = _inventoryInfoService.Repository.QueryFirst(x => x.LocationCode == LocationCode); if (inventoryInfo1 == null) { location.LocationStatus = LocationStatusEnum.Free.ObjToInt(); _basicService.LocationInfoService.UpdateData(location); } #endregion #region 任务记录 Dt_SupplyTask supplyTask = new Dt_SupplyTask() { WarehouseCode = cabinOrderDetail.Reservoirarea, BatchNo = inventoryInfo.BatchNo, MaterielName = inventoryInfo.MaterielName, MaterielCode = inventoryInfo.MaterielCode, MaterielSpec = inventoryInfo.MaterielSpec, TaskStatus = SupplyStatusEnum.OutFinish.ObjToInt(), TaskType = TaskTypeEnum.OutPick.ObjToInt(), CreateDate = DateTime.Now, Creater = App.User.UserName, LocationCode = location.LocationCode, OrderNo = cabinOrder.Out_no, StockQuantity = Inqty, SupplyQuantity = 0, Remark = "出库" }; _supplyTaskService.AddData(supplyTask); #endregion #region 库存批次 Dt_Inventory_Batch inventory_Batch = _inventory_BatchServices.Repository.QueryFirst(x => x.BatchNo == inventoryInfo.BatchNo && x.MaterielCode == inventoryInfo.MaterielCode); if (inventory_Batch != null) { inventory_Batch.StockQuantity -= Inqty; if (inventory_Batch.StockQuantity <= 0) _inventory_BatchServices.DeleteData(inventory_Batch); else _inventory_BatchServices.UpdateData(inventory_Batch); } #endregion _unitOfWorkManage.CommitTran(); #endregion content.OK(cabinOrderDetail.Order_Outqty.ToString()); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); content.Error(ex.Message); } return content; } } }