heshaofeng
3 天以前 b1a419c2886666934da6499ee552516d0769562e
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -1,5 +1,8 @@
using MailKit.Search;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using SqlSugar;
using WIDESEA_BasicService;
using WIDESEA_Common.CommonEnum;
@@ -15,6 +18,7 @@
using WIDESEA_DTO.Basic;
using WIDESEA_DTO.Inbound;
using WIDESEA_DTO.Outbound;
using WIDESEA_DTO.ReturnMES;
using WIDESEA_IAllocateService;
using WIDESEA_IBasicService;
using WIDESEA_ICheckService;
@@ -26,6 +30,14 @@
using WIDESEA_Model.Models.Basic;
using WIDESEA_Model.Models.Check;
using WIDESEA_Model.Models.Outbound;
using Org.BouncyCastle.Asn1.Ocsp;
using WIDESEA_BasicService.MESOperation;
using WIDESEA_Core.Util;
using WIDESEA_DTO.Allocate;
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
using WIDESEA_IRecordService;
using Microsoft.AspNetCore.Http;
using MailKit.Net.Smtp;
namespace WIDESEA_OutboundService
{
@@ -58,6 +70,12 @@
        private readonly ITask_HtyService _task_HtyService;
        private readonly ILogger<OutboundPickingService> _logger;
        private readonly IRepository<Dt_InterfaceLog> _interfaceLog;
        private readonly IInboundService _inboundService;
        private readonly IFeedbackMesService _feedbackMesService;
        private readonly HttpClientHelper _httpClientHelper;
        private readonly IRepository<Dt_MesReturnRecord> _mesReturnRecord;
        private readonly IStockQuantityChangeRecordService _stockQuantityChangeRecordService;
        private readonly IInboundOrderService _inboundOrderService;
        private Dictionary<string, string> stations = new Dictionary<string, string>
        {
@@ -76,7 +94,7 @@
        public OutboundPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService,
            IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService,
            IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService,
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_InboundOrder> inboundOrderRepository, IInboundOrderDetailService inboundOrderDetailService, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IReCheckOrderService reCheckOrderService, ITask_HtyService task_HtyService, IRepository<Dt_InterfaceLog> interfaceLog) : base(BaseDal)
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_InboundOrder> inboundOrderRepository, IInboundOrderDetailService inboundOrderDetailService, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IReCheckOrderService reCheckOrderService, ITask_HtyService task_HtyService, IRepository<Dt_InterfaceLog> interfaceLog, IInboundService inboundService, IFeedbackMesService feedbackMesService, HttpClientHelper httpClientHelper, IRepository<Dt_MesReturnRecord> mesReturnRecord,IStockQuantityChangeRecordService stockQuantityChangeRecordService,IInboundOrderService inboundOrderService) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _stockInfoService = stockInfoService;
@@ -99,6 +117,13 @@
            _reCheckOrderService = reCheckOrderService;
            _task_HtyService = task_HtyService;
            _interfaceLog = interfaceLog;
            _inboundService = inboundService;
            _feedbackMesService = feedbackMesService;
            _httpClientHelper = httpClientHelper;
            _mesReturnRecord = mesReturnRecord;
            _stockQuantityChangeRecordService = stockQuantityChangeRecordService;
            _inboundOrderService = inboundOrderService;
        }
        #endregion
@@ -1678,29 +1703,29 @@
                //if (moveResult)
                //{
                //  åˆ›å»ºå›žåº“任务
                var essTask = new TaskModel()
                {
                    taskType = "putaway",
                    taskGroupCode = "",
                    groupPriority = 0,
                    tasks = new List<TasksType>{  new() {
                            taskCode = returnTask.TaskNum.ToString(),
                            taskPriority = 0,
                            taskDescribe = new TaskDescribeType
                            {
                                containerCode = palletCode,
                                containerType = "CT_KUBOT_STANDARD",
                                fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
                                toStationCode = "",
                                toLocationCode = returnTask.TargetAddress,
                                deadline = 0,
                                storageTag = ""
                            }
                        } }
                };
                //var essTask = new TaskModel()
                //{
                //    taskType = "putaway",
                //    taskGroupCode = "",
                //    groupPriority = 0,
                //    tasks = new List<TasksType>{  new() {
                //            taskCode = returnTask.TaskNum.ToString(),
                //            taskPriority = 0,
                //            taskDescribe = new TaskDescribeType
                //            {
                //                containerCode = palletCode,
                //                containerType = "CT_KUBOT_STANDARD",
                //                fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
                //                toStationCode = "",
                //                toLocationCode = returnTask.TargetAddress,
                //                deadline = 0,
                //                storageTag = ""
                //            }
                //        } }
                //};
                var resultTask = await _eSSApiService.CreateTaskAsync(essTask);
                _logger.LogInformation($"ReturnRemaining åˆ›å»ºä»»åŠ¡æˆåŠŸ: {resultTask}");
                //var resultTask = await _eSSApiService.CreateTaskAsync(essTask);
                //_logger.LogInformation($"ReturnRemaining åˆ›å»ºä»»åŠ¡æˆåŠŸ: {resultTask}");
                //}
            }
            catch (Exception ex)
@@ -2292,383 +2317,588 @@
            return WebResponseContent.Instance.OK("成功", data: InOderCodes);
        }
        public WebResponseContent GetAvailablePickingOrders()
        public WebResponseContent GetAvailablePickingOrders(string outOrder)
        {
            List<Dt_OutboundOrder> outOders = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().Where(x => x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).ToList();
            List<string> outOderCodes = outOders.Select(x => x.UpperOrderNo).ToList();
            return WebResponseContent.Instance.OK("成功", data: outOderCodes);
            Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == outOrder && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).Includes(x=>x.Details).First();
            if(outboundOrder == null)
            {
                return WebResponseContent.Instance.Error("未找到满足出库条件的出库单");
            }
            if(outboundOrder.IsBatch == 0)
            {
                return WebResponseContent.Instance.Error("该单据不属于分批回传单据,不允许虚拟出入库");
            }
            //先清空单据虚拟出入库数量进行计算
            foreach (var item in outboundOrder.Details)
            {
                item.NoStockOutQty = 0;
                item.documentsNO = "";
            }
            _outboundOrderDetailService.UpdateData(outboundOrder.Details);
            return WebResponseContent.Instance.OK("成功");
        }
        public WebResponseContent BarcodeValidate(NoStockOutModel noStockOut)
    public WebResponseContent BarcodeValidate(NoStockOutModel noStockOut)
    {
        try
        {
            try
            Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>()
                .Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt())
                .Includes(x => x.Details)
                .First();
            if (inboundOrder == null)
            {
                Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt()).Includes(x => x.Details).First();
                if (inboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到采购单:{noStockOut.inOder}");
                }
                var matchedDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == noStockOut.barCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                return WebResponseContent.Instance.Error($"未找到采购单:{noStockOut.inOder}");
            }
            Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>()
                .Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt())
                .Includes(x => x.Details)
                .First();
            if (outboundOrder == null)
            {
                return WebResponseContent.Instance.Error($"未找到出库单:{noStockOut.outOder}");
            }
            var detailLists = new List<Dt_InboundOrderDetail>();
            var matchedDetail = inboundOrder.Details.FirstOrDefault(detail =>
                detail.Barcode == noStockOut.barCode &&
                detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()&& detail.ReceiptQuantity == 0);
            if (matchedDetail == null)
            {
                matchedDetail = inboundOrder.Details.FirstOrDefault(detail =>
                    detail.OutBoxbarcodes == noStockOut.barCode &&
                    detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt() && detail.ReceiptQuantity == 0);
                if (matchedDetail == null)
                {
                    return WebResponseContent.Instance.Error($"在采购单 {noStockOut.inOder} ä¸­æœªæ‰¾åˆ°æ¡ç ä¸º {noStockOut.barCode} çš„æ˜Žç»†ã€‚");
                    return WebResponseContent.Instance.Error($"在采购单 {noStockOut.inOder} ä¸­æœªæ‰¾åˆ°æ¡ç ä¸º {noStockOut.barCode} çš„可出库明细。");
                }
                matchedDetail.NoStockOutQty = 0;
                else
                {
                    // æ·»åŠ æ‰€æœ‰åŒ¹é…å¤–ç®±ç ä¸”éžå®ŒæˆçŠ¶æ€çš„æ˜Žç»†
                    foreach (var detail in inboundOrder.Details)
                    {
                        if (detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt() &&
                            !string.IsNullOrEmpty(detail.Barcode) &&
                            detail.OutBoxbarcodes == noStockOut.barCode && detail.OrderQuantity > detail.NoStockOutQty)
                        {
                            detailLists.Add(detail);
                        }
                    }
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(noStockOut.barCode))
                {
                    detailLists.Add(matchedDetail);
                }
            }
                if (!detailLists.Any())
                {
                    return WebResponseContent.Instance.Error("该条码已经没有可出库数量");
                }
                var outDetails = new List<Dt_OutboundOrderDetail>();
                // éåŽ†æ¯ä¸ªå…¥åº“æ˜Žç»†
                foreach (var item in detailLists)
                {
                    // é‡ç½®å½“前入库明细的无库存出库数量
                    item.NoStockOutQty = 0;
                Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).Includes(x => x.Details).First();
                if (outboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到出库单:{noStockOut.inOder}");
                }
                var matchedCode = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == matchedDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                    // å½“前条码需要出库的总数量(入库单剩余可出数量)
                    decimal remainingBarcodeQty = item.OrderQuantity - item.ReceiptQuantity;
                    if (remainingBarcodeQty <= 0)
                    {
                        return WebResponseContent.Instance.Error($"该采购单中的条码{item.Barcode}对应的可出数量为0");
                    }
                if (matchedCode == null)
                {
                    return WebResponseContent.Instance.Error($"在出库单的物料编码中未找到与采购单中的{matchedDetail.MaterielCode} å¯¹åº”的物料。");
                }
                matchedCode.NoStockOutQty = 0;
                    // ç­›é€‰å‡ºåº“单中符合条件的明细(同物料、非完成、有剩余可出数量)
                    var eligibleOutDetails = outboundOrder.Details.Where(detail =>
                        detail.MaterielCode == item.MaterielCode &&
                        detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt() &&
                        (detail.OrderQuantity - detail.LockQuantity - detail.MoveQty - detail.NoStockOutQty) > 0).ToList();
                //剩余入库数量即虚拟出入库剩余可出数量
                decimal outQuantity = matchedDetail.OrderQuantity - matchedDetail.ReceiptQuantity;
                if (outQuantity == 0)
                {
                    return WebResponseContent.Instance.Error($"该采购单中的条码对应的可出数量为0");
                }
                if (matchedCode.OrderQuantity < outQuantity)
                {
                    return WebResponseContent.Instance.Error($"该采购单中的条码对应的可出数量超出出库单出库数量{matchedDetail.OrderQuantity - matchedCode.OrderQuantity},不满足整包出库");
                }
                //单据出库锁定数量
                matchedDetail.NoStockOutQty += outQuantity;
                matchedCode.NoStockOutQty += outQuantity;
                    if (!eligibleOutDetails.Any())
                    {
                        return WebResponseContent.Instance.Error($"在出库单中未找到物料{item.MaterielCode}的可出库明细");
                    }
                if ((matchedCode.LockQuantity + matchedCode.NoStockOutQty) > matchedCode.OrderQuantity)
                {
                    return WebResponseContent.Instance.Error($"出库单明细数量溢出{matchedCode.LockQuantity - matchedCode.OrderQuantity}");
                    // éåŽ†ç¬¦åˆæ¡ä»¶çš„å‡ºåº“æ˜Žç»†ï¼Œé€è¡Œåˆ†é…æ•°é‡
                    foreach (var outDetail in eligibleOutDetails)
                    {
                        // è®¡ç®—当前出库行的剩余可出数量
                        decimal rowRemainingQty = outDetail.OrderQuantity - outDetail.LockQuantity - outDetail.MoveQty - outDetail.NoStockOutQty;
                        if (rowRemainingQty <= 0) continue;
                        if (!outDetail.BatchNo.IsNullOrEmpty())
                        {
                            if (outDetail.BatchNo != item.BatchNo)
                            {
                                return WebResponseContent.Instance.Error($"出库单行批次{outDetail.BatchNo}与采购单批次{item.BatchNo}不匹配");
                            }
                        }
                        if (!outDetail.SupplyCode.IsNullOrEmpty())
                        {
                            if (outDetail.SupplyCode != item.SupplyCode)
                            {
                                return WebResponseContent.Instance.Error($"出库单行供应商{outDetail.SupplyCode}与采购单供应商{item.SupplyCode}不匹配");
                            }
                        }
                        if (!string.IsNullOrEmpty(outboundOrder.FactoryArea) && !string.IsNullOrEmpty(inboundOrder.FactoryArea))
                        {
                            if (inboundOrder.FactoryArea != outboundOrder.FactoryArea)
                            {
                                return WebResponseContent.Instance.Error($"该条码{item.Barcode}对应的单据厂区与出库单据不一致!不允许出库。");
                            }
                        }
                        if (inboundOrder.BusinessType != "11" && !outDetail.WarehouseCode.IsNullOrEmpty())
                        {
                            if (outDetail.WarehouseCode != item.WarehouseCode)
                            {
                                return WebResponseContent.Instance.Error($"仓库不一致!出库单行仓库{outDetail.WarehouseCode}与采购单仓库{item.WarehouseCode}不匹配");
                            }
                        }
                        else
                        {
                            item.WarehouseCode = outDetail.WarehouseCode;
                        }
                        // è®¡ç®—本次分配的数量(取剩余条码数量和当前行剩余数量的较小值)
                        decimal assignQty = Math.Min(remainingBarcodeQty, rowRemainingQty);
                        // æ›´æ–°å…¥åº“明细和出库明细的无库存出库数量
                        item.NoStockOutQty += assignQty;
                        outDetail.NoStockOutQty += assignQty;
                        // æ›´æ–°å‰©ä½™éœ€è¦åˆ†é…çš„æ¡ç æ•°é‡
                        remainingBarcodeQty -= assignQty;
                        // è®°å½•已更新的出库明细(去重)
                        if (!outDetails.Contains(outDetail))
                        {
                            outDetails.Add(outDetail);
                        }
                        // éªŒè¯å½“前行是否溢出
                        if ((outDetail.LockQuantity + outDetail.NoStockOutQty + outDetail.MoveQty) > outDetail.OrderQuantity)
                        {
                            return WebResponseContent.Instance.Error($"出库单明细{outDetail.Id}数量溢出,超出数量:{(outDetail.LockQuantity + outDetail.NoStockOutQty + outDetail.MoveQty) - outDetail.OrderQuantity}");
                        }
                        // å¤„理MES参数回传:记录当前条码分配到该行的实际数量
                        List<Barcodes> barcodesList = new List<Barcodes>();
                        Barcodes barcodes = new Barcodes
                        {
                            Barcode = item.Barcode,
                            Qty = assignQty,
                            SupplyCode = item?.SupplyCode ?? "",
                            BatchNo = item?.BatchNo ?? "",
                            Unit = item?.Unit ?? ""
                        };
                        // ååºåˆ—化该行已有的条码记录
                        if (!string.IsNullOrEmpty(outDetail.documentsNO))
                        {
                            try
                            {
                                barcodesList = JsonConvert.DeserializeObject<List<Barcodes>>(outDetail.documentsNO) ?? new List<Barcodes>();
                            }
                            catch (JsonException ex)
                            {
                                return WebResponseContent.Instance.Error($"出库单明细{outDetail.Id}的documentsNO字段格式错误:{ex.Message}");
                            }
                        }
                        // æ·»åŠ æœ¬æ¬¡åˆ†é…çš„è®°å½•
                        barcodesList.Add(barcodes);
                        // åºåˆ—化回存
                        JsonSerializerSettings settings = new JsonSerializerSettings
                        {
                            ContractResolver = new CamelCasePropertyNamesContractResolver()
                        };
                        outDetail.documentsNO = JsonConvert.SerializeObject(barcodesList, settings);
                        // æ¡ç æ•°é‡åˆ†é…å®Œæ¯•,退出当前行循环
                        if (remainingBarcodeQty <= 0)
                        {
                            break;
                        }
                    }
                    // æ‰€æœ‰ç¬¦åˆæ¡ä»¶çš„出库行遍历完后,条码数量仍有剩余
                    if (remainingBarcodeQty > 0)
                    {
                        return WebResponseContent.Instance.Error($"条码{item.Barcode}需出库数量{item.OrderQuantity - item.ReceiptQuantity},但出库单中物料{item.MaterielCode}剩余可出总量不足,仍剩余{remainingBarcodeQty}数量未分配");
                    }
                }
                matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.Inbounding.ObjToInt();
                matchedCode.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt();
                _unitOfWorkManage.BeginTran();
                _inboundOrderDetailService.UpdateData(matchedDetail);
                _outboundOrderDetailService.UpdateData(matchedCode);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK();
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
            _inboundOrderDetailService.UpdateData(detailLists);
            _outboundOrderDetailService.UpdateData(outDetails);
            _unitOfWorkManage.CommitTran();
            return WebResponseContent.Instance.OK("成功", data: detailLists);
        }
        public WebResponseContent DeleteBarcode(NoStockOutModel noStockOut)
        catch (Exception ex)
        {
            try
            _unitOfWorkManage.RollbackTran();
            return WebResponseContent.Instance.Error(ex.Message);
        }
    }
    public WebResponseContent DeleteBarcode(NoStockOutModel noStockOut)
    {
        try
        {
            Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>()
                .Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt())
                .Includes(x => x.Details)
                .First();
            if (inboundOrder == null)
            {
                Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt()).Includes(x => x.Details).First();
                if (inboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到采购单:{noStockOut.inOder}");
                }
                var matchedDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == noStockOut.barCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                return WebResponseContent.Instance.Error($"未找到采购单:{noStockOut.inOder}");
            }
                if (matchedDetail == null)
                {
                    return WebResponseContent.Instance.Error($"在采购单 {noStockOut.inOder} ä¸­æœªæ‰¾åˆ°æ¡ç ä¸º {noStockOut.barCode} çš„æ˜Žç»†ã€‚");
                }
                matchedDetail.NoStockOutQty = 0;
            var matchedDetail = inboundOrder.Details.FirstOrDefault(detail =>
                detail.Barcode == noStockOut.barCode &&
                detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                if (matchedDetail.ReceiptQuantity == 0 && matchedDetail.OverInQuantity == 0)
            if (matchedDetail == null)
            {
                return WebResponseContent.Instance.Error($"在采购单 {noStockOut.inOder} ä¸­æœªæ‰¾åˆ°æ¡ç ä¸º {noStockOut.barCode} çš„æ˜Žç»†ã€‚");
            }
            // é‡ç½®å…¥åº“明细的无库存出库数量
            decimal revokedTotalQty = matchedDetail.NoStockOutQty; // è®°å½•需要撤销的总数量(入库单中已分配的数量)
            matchedDetail.NoStockOutQty = 0;
            if(inboundOrder.BusinessType == "11")
            {
                matchedDetail.WarehouseCode ="";
            }
            // é‡ç½®å…¥åº“明细状态
            if (matchedDetail.ReceiptQuantity == 0 && matchedDetail.OverInQuantity == 0)
            {
                matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt();
            }
            Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>()
                .Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt())
                .Includes(x => x.Details)
                .First();
            if (outboundOrder == null)
            {
                return WebResponseContent.Instance.Error($"未找到出库单:{noStockOut.outOder}");
            }
            // æ‰¾åˆ°æ‰€æœ‰å…³è”该条码的出库明细行
            // åŒç‰©æ–™ã€éžå®ŒæˆçŠ¶æ€ã€documentsNO包含该条码
            var matchedCodeList = outboundOrder.Details.Where(detail =>
                detail.MaterielCode == matchedDetail.MaterielCode && // ç¡®ä¿ç‰©æ–™åŒ¹é…
                detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt() &&
                !string.IsNullOrEmpty(detail.documentsNO) &&
                detail.documentsNO.Contains(noStockOut.barCode) // åŒ…含当前条码
            ).ToList();
            if (!matchedCodeList.Any())
            {
                return WebResponseContent.Instance.Error($"在出库单中未找到关联条码{noStockOut.barCode}的物料{matchedDetail.MaterielCode}明细。");
            }
            //逐行处理出库明细的撤销逻辑
            decimal remainingRevokeQty = revokedTotalQty; // å‰©ä½™éœ€è¦æ’¤é”€çš„æ•°é‡
            foreach (var matchedCode in matchedCodeList)
            {
                if (remainingRevokeQty <= 0) break; // æ‰€æœ‰æ•°é‡å·²æ’¤é”€ï¼Œé€€å‡ºå¾ªçޝ
                // å¤„理MES参数撤销
                List<Barcodes> barcodesList = new List<Barcodes>();
                if (!string.IsNullOrEmpty(matchedCode.documentsNO))
                {
                    matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt();
                    try
                    {
                        barcodesList = JsonConvert.DeserializeObject<List<Barcodes>>(matchedCode.documentsNO) ?? new List<Barcodes>();
                    }
                    catch (JsonException ex)
                    {
                        return WebResponseContent.Instance.Error($"出库单明细{matchedCode.Id}的documentsNO字段格式错误:{ex.Message}");
                    }
                }
                Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).Includes(x => x.Details).First();
                if (outboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到出库单:{noStockOut.inOder}");
                }
                var matchedCode = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == matchedDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                // ç­›é€‰å‡ºå½“前条码的所有记录
                var barcodeRecords = barcodesList.Where(b =>
                    string.Equals(b.Barcode, noStockOut.barCode, StringComparison.OrdinalIgnoreCase)).ToList();
                if (!barcodeRecords.Any()) continue;
                if (matchedCode == null)
                // è®¡ç®—该行需要撤销的数量(累加该条码在该行的所有分配数量)
                decimal rowRevokeQty = barcodeRecords.Sum(b => b.Qty);
                // å®žé™…撤销数量:取该行可撤销数量和剩余需要撤销数量的较小值
                decimal actualRevokeQty = Math.Min(rowRevokeQty, remainingRevokeQty);
                // ç§»é™¤è¯¥è¡Œä¸­è¯¥æ¡ç çš„记录(或部分记录,若剩余撤销数量不足)
                if (actualRevokeQty < rowRevokeQty)
                {
                    return WebResponseContent.Instance.Error($"在出库单的物料编码中未找到与采购单中的{matchedDetail.MaterielCode} å¯¹åº”的物料。");
                    // å‰©ä½™æ’¤é”€æ•°é‡ä¸è¶³ï¼Œåªç§»é™¤éƒ¨åˆ†è®°å½•(按数量扣减)
                    decimal tempQty = actualRevokeQty;
                    var removeList = new List<Barcodes>();
                    foreach (var record in barcodeRecords)
                    {
                        if (tempQty <= 0) break;
                        if (record.Qty <= tempQty)
                        {
                            removeList.Add(record);
                            tempQty -= record.Qty;
                        }
                        else
                        {
                            // è®°å½•数量拆分,扣减部分数量
                            record.Qty -= tempQty;
                            tempQty = 0;
                        }
                    }
                    barcodesList.RemoveAll(b => removeList.Contains(b));
                }
                matchedCode.NoStockOutQty = 0;
                else
                {
                    // ç§»é™¤è¯¥è¡Œä¸­è¯¥æ¡ç çš„æ‰€æœ‰è®°å½•
                    barcodesList.RemoveAll(b =>
                        string.Equals(b.Barcode, noStockOut.barCode, StringComparison.OrdinalIgnoreCase));
                }
                //重新序列化MES参数
                JsonSerializerSettings settings = new JsonSerializerSettings
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                };
                matchedCode.documentsNO = JsonConvert.SerializeObject(barcodesList, settings);
                //扣减出库明细的无库存出库数量
                matchedCode.NoStockOutQty = Math.Max(0, matchedCode.NoStockOutQty - actualRevokeQty);
                remainingRevokeQty -= actualRevokeQty;
                //重置出库明细状态
                if (matchedCode.LockQuantity == 0 && matchedCode.OverOutQuantity == 0)
                {
                    matchedCode.OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt();
                }
                _unitOfWorkManage.BeginTran();
                _inboundOrderDetailService.UpdateData(matchedDetail);
                _outboundOrderDetailService.UpdateData(matchedCode);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK();
            }
            catch (Exception ex)
            //若仍有未撤销的数量,说明数据不一致
            if (remainingRevokeQty > 0)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
                return WebResponseContent.Instance.Error($"撤销条码{noStockOut.barCode}时,出库单中可撤销数量不足,仍有{remainingRevokeQty}数量未撤销");
            }
        }
        public async Task<WebResponseContent> NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit)
            _unitOfWorkManage.BeginTran();
            _inboundOrderDetailService.UpdateData(matchedDetail);
            _outboundOrderDetailService.UpdateData(matchedCodeList);
            _unitOfWorkManage.CommitTran();
            return WebResponseContent.Instance.OK("条码撤销成功", data: new { RevokedQty = revokedTotalQty });
        }
        catch (Exception ex)
        {
            _unitOfWorkManage.RollbackTran();
            return WebResponseContent.Instance.Error(ex.Message);
        }
    }
    public async Task<WebResponseContent> NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit)
        {
            try
            {
                Dt_InboundOrder inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOutSubmit.InOderSubmit && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt()).Includes(x => x.Details).First();
                if (inboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到采购单:{noStockOutSubmit.InOderSubmit}");
                }
                Dt_OutboundOrder outboundOrder = _inboundOrderRepository.Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOutSubmit.OutOderSubmit && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).Includes(x => x.Details).First();
                if (outboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到出库单:{noStockOutSubmit.OutOderSubmit}");
                }
                List<Dt_InboundOrderDetail> inboundOrderDetails = new List<Dt_InboundOrderDetail>();
                List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
                foreach (var BarCode in noStockOutSubmit.BarCodeSubmit)
                Dictionary<int, List<string>> orderIdBarCodeDict = new Dictionary<int, List<string>>();
                List<Dt_InboundOrderDetail> updateInboundDetails = new List<Dt_InboundOrderDetail>();
                Dictionary<int, Dt_InboundOrder> updateInboundOrders = new Dictionary<int, Dt_InboundOrder>();
                List<Dt_StockQuantityChangeRecord> changeRecords = new List<Dt_StockQuantityChangeRecord>();
                _unitOfWorkManage.BeginTran();
                List<Dt_InboundOrderDetail> allInboundDetails = _inboundOrderDetailService.Db
                .Queryable<Dt_InboundOrderDetail>()
                .Where(detail => noStockOutSubmit.BarCodeSubmit.Contains(detail.Barcode)
                    && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt())
                .ToList();
                var detailGroupByOrderId = allInboundDetails.GroupBy(d => d.OrderId).ToList();
                foreach (var group in detailGroupByOrderId)
                {
                    var inboundOrderDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == BarCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                    int orderId = group.Key;
                    List<Dt_InboundOrderDetail> groupDetails = group.ToList();
                    List<string> groupBarCodes = groupDetails.Select(d => d.Barcode).ToList();
                    if (inboundOrderDetail == null)
                    orderIdBarCodeDict[orderId] = groupBarCodes;
                    Dt_InboundOrder currentInboundOrder = null;
                    if (!updateInboundOrders.TryGetValue(orderId, out currentInboundOrder))
                    {
                        return WebResponseContent.Instance.Error($"在采购单 {noStockOutSubmit.InOderSubmit} ä¸­æœªæ‰¾åˆ°æ¡ç ä¸º {BarCode} çš„æ˜Žç»†ã€‚");
                        currentInboundOrder = _inboundOrderRepository.Db
                            .Queryable<Dt_InboundOrder>()
                            .Where(x => x.Id == orderId)
                            .Includes(x => x.Details)
                            .First();
                        if (currentInboundOrder == null)
                        {
                            _unitOfWorkManage.RollbackTran();
                            return WebResponseContent.Instance.Error($"未找到入库单ID为 {orderId} çš„单据");
                        }
                        updateInboundOrders[orderId] = currentInboundOrder;
                    }
                    var outboundOrderDetail = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == inboundOrderDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                    if (outboundOrderDetail == null)
                    foreach (var detail in groupDetails)
                    {
                        return WebResponseContent.Instance.Error($"在出库单的物料编码中未找到与采购单中的{inboundOrderDetail.MaterielCode} å¯¹åº”的物料。");
                        detail.ReceiptQuantity = detail.NoStockOutQty;
                        detail.OverInQuantity = detail.NoStockOutQty;
                        if (detail.OrderQuantity == detail.OverInQuantity)
                        {
                            detail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt();
                        }
                        updateInboundDetails.Add(detail);
                        //添加库存变动记录
                        Dt_StockQuantityChangeRecord changeRecord = new Dt_StockQuantityChangeRecord
                        {
                            StockDetailId = detail.Id,
                            PalleCode = DateTime.Now.ToString(),
                            MaterielCode = detail.MaterielCode,
                            MaterielName = detail.MaterielName ?? "",
                            BatchNo = detail.BatchNo ?? "",
                            OriginalSerilNumber = detail.Barcode,
                            NewSerilNumber = "",
                            OrderNo = currentInboundOrder.InboundOrderNo,
                            TaskNum = 0,
                            ChangeType = (int)StockChangeTypeEnum.Inbound,
                            ChangeQuantity = detail.NoStockOutQty,
                            BeforeQuantity = detail.OverInQuantity - detail.NoStockOutQty,
                            AfterQuantity = detail.OverInQuantity,
                            SupplyCode = detail.SupplyCode ?? "",
                            WarehouseCode = detail.WarehouseCode ?? "",
                            Remark = $"虚拟入库"
                        };
                        changeRecords.Add(changeRecord);
                    }
                    inboundOrderDetail.ReceiptQuantity += inboundOrderDetail.NoStockOutQty;
                    inboundOrderDetail.OverInQuantity = inboundOrderDetail.ReceiptQuantity;
                    inboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt();
                    inboundOrderDetails.Add(inboundOrderDetail);
                    outboundOrderDetail.LockQuantity += outboundOrderDetail.NoStockOutQty;
                    outboundOrderDetail.OverOutQuantity = outboundOrderDetail.LockQuantity;
                    if (outboundOrderDetail.OrderQuantity == outboundOrderDetail.OverOutQuantity)
                    var inboundOrder = updateInboundOrders[orderId];
                    int totalDetailCount = inboundOrder.Details.Count;
                    int finishedDetailCount = inboundOrder.Details.Count(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt())
                                             + groupDetails.Count(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt());
                    inboundOrder.OrderStatus = totalDetailCount == finishedDetailCount
                        ? InOrderStatusEnum.入库完成.ObjToInt()
                        : InOrderStatusEnum.入库中.ObjToInt();
                }
                if (updateInboundDetails.Any())
                {
                    _inboundOrderDetailService.Db.Updateable(updateInboundDetails).ExecuteCommand();
                }
                if (updateInboundOrders.Any())
                {
                    // æ‰¹é‡æ›´æ–°å…¥åº“主单
                    _inboundOrderService.Db.Updateable(updateInboundOrders.Values.ToList()).ExecuteCommand();
                }
                // 7. å¾ªçŽ¯åˆ†ç»„ç»“æžœï¼Œè°ƒç”¨MES回传方法(按入库单分组回传)
                foreach (var kvp in orderIdBarCodeDict)
                {
                    int orderId = kvp.Key;
                    List<string> barCodeList = kvp.Value;
                    //入库回传MES
                    NoStockOutBatchInOrderFeedbackToMes(orderId, barCodeList);
                }
                //只对出库条码的出库单明细进行计算回传
                List<Dt_OutboundOrderDetail> outboundOrderDetail = outboundOrder.Details
                .Where(x => !string.IsNullOrWhiteSpace(x.documentsNO)
                    && noStockOutSubmit.BarCodeSubmit.Any(barcode =>
                        x.documentsNO.IndexOf(barcode, StringComparison.OrdinalIgnoreCase) >= 0))
                .ToList();
                foreach (var item in outboundOrderDetail)
                {
                    item.LockQuantity += item.NoStockOutQty;
                    item.OverOutQuantity += item.NoStockOutQty;
                    item.CurrentDeliveryQty += item.NoStockOutQty;
                    //添加回传MES参数
                    List<Barcodes> barcodesList = new List<Barcodes>();
                    List<Barcodes> documentsNOList = new List<Barcodes>();
                    if (!string.IsNullOrEmpty(item.ReturnJsonData))
                    {
                        outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt();
                        barcodesList = JsonConvert.DeserializeObject<List<Barcodes>>(item.ReturnJsonData) ?? new List<Barcodes>();
                    }
                    outboundOrderDetails.Add(outboundOrderDetail);
                    var newLockInfo = new Dt_OutStockLockInfo
                    if (!string.IsNullOrEmpty(item.documentsNO) && item.documentsNO!="")
                    {
                        OrderNo = outboundOrder.UpperOrderNo,
                        OrderDetailId = outboundOrderDetail.Id,
                        OutboundBatchNo = outboundOrderDetail.BatchNo,
                        MaterielCode = outboundOrderDetail.MaterielCode,
                        MaterielName = outboundOrderDetail.MaterielName,
                        StockId = 0,
                        OrderQuantity = outboundOrderDetail.OrderQuantity,
                        AssignQuantity = outboundOrderDetail.OverOutQuantity,
                        PickedQty = outboundOrderDetail.NoStockOutQty,
                        LocationCode = "空",
                        PalletCode = "空",
                        TaskNum = 0,
                        Status = (int)OutLockStockStatusEnum.拣选完成,
                        Unit = outboundOrderDetail.Unit,
                        SupplyCode = outboundOrderDetail.SupplyCode ?? "无",
                        OrderType = outboundOrder.OrderType,
                        CurrentBarcode = inboundOrderDetail.Barcode,
                        IsSplitted = 1,
                        Operator = App.User.UserName,
                        lineNo = outboundOrderDetail.lineNo,
                        WarehouseCode = outboundOrderDetail.WarehouseCode ?? "无",
                        BarcodeQty = outboundOrderDetail.NoStockOutQty,
                        BarcodeUnit = outboundOrderDetail.BarcodeUnit,
                        BatchNo = outboundOrderDetail.BatchNo
                        documentsNOList = JsonConvert.DeserializeObject<List<Barcodes>>(item.documentsNO) ?? new List<Barcodes>();
                    }
                    foreach (var documentsNO in documentsNOList)
                    {
                        barcodesList.Add(documentsNO);
                    }
                    JsonSerializerSettings settings = new JsonSerializerSettings
                    {
                        ContractResolver = new CamelCasePropertyNamesContractResolver()
                    };
                    _outStockLockInfoService.AddData(newLockInfo);
                    item.ReturnJsonData = JsonConvert.SerializeObject(barcodesList, settings);
                    //添加库存变动记录
                    Dt_StockQuantityChangeRecord changeRecord = new Dt_StockQuantityChangeRecord
                    {
                        StockDetailId = item.Id,
                        PalleCode = DateTime.Now.ToString(),
                        MaterielCode = item.MaterielCode,
                        MaterielName = item.MaterielName ?? "",
                        BatchNo = item.BatchNo ?? "",
                        OriginalSerilNumber = item.ReturnJsonData,
                        NewSerilNumber = "",
                        OrderNo = outboundOrder.OrderNo,
                        TaskNum = 0,
                        ChangeType = (int)StockChangeTypeEnum.Inbound,
                        ChangeQuantity = -item.NoStockOutQty,
                        BeforeQuantity = item.OrderQuantity,
                        AfterQuantity = item.OrderQuantity - item.OverOutQuantity,
                        SupplyCode = item.SupplyCode ?? "",
                        WarehouseCode = item.WarehouseCode ?? "",
                        Remark = $"虚拟出库"
                    };
                    changeRecords.Add(changeRecord);
                    outboundOrderDetails.Add(item);
                }
                //判断入库单据明细是否全部是完成状态
                int e = inboundOrder.Details.Count();
                int w = inboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                bool inoderOver = inboundOrder.Details.Count() == inboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                if (inoderOver)
                _outboundOrderDetailService.UpdateData(outboundOrderDetails);
                //批量添加库存变动记录
                if (changeRecords.Any())
                {
                    inboundOrder.OrderStatus = InOrderStatusEnum.入库完成.ObjToInt();
                    _stockQuantityChangeRecordService.Db.Insertable(changeRecords).ExecuteCommand();
                }
                else
                {
                    inboundOrder.OrderStatus = InOrderStatusEnum.入库中.ObjToInt();
                }
                //判断出库单据明细是否全部是完成状态
                bool outOderOver = outboundOrder.Details.Count() == outboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                if (outOderOver)
                // æ£€æŸ¥å‡ºåº“单是否完成
                if (CheckOutboundOrderCompleted(outboundOrder.OrderNo))
                {
                    outboundOrder.OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt();
                }
                else
                {
                    outboundOrder.OrderStatus = OutOrderStatusEnum.出库中.ObjToInt();
                }
                //数据处理
                _unitOfWorkManage.BeginTran();
                _inboundOrderDetailService.UpdateData(inboundOrderDetails);
                _outboundOrderDetailService.UpdateData(outboundOrderDetails);
                _inboundOrderRepository.UpdateData(inboundOrder);
                _outboundOrderService.UpdateData(outboundOrder);
                _unitOfWorkManage.CommitTran();
                //入库回传MES
                var infeedmodel = new FeedbackInboundRequestModel
                {
                    reqCode = Guid.NewGuid().ToString(),
                    reqTime = DateTime.Now.ToString(),
                    business_type = inboundOrder.BusinessType,
                    factoryArea = inboundOrder.FactoryArea,
                    operationType = 1,
                    Operator = App.User.UserName,
                    orderNo = inboundOrder.UpperOrderNo,
                    status = inboundOrder.OrderStatus,
                    details = new List<FeedbackInboundDetailsModel>()
                };
                var groupedData = inboundOrderDetails.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.lineNo, item.BarcodeUnit, item.WarehouseCode })
                    .Select(group => new FeedbackInboundDetailsModel
                    {
                        materialCode = group.Key.MaterielCode,
                        supplyCode = group.Key.SupplyCode,
                        batchNo = group.Key.BatchNo,
                        lineNo = group.Key.lineNo,
                        warehouseCode = group.Key.WarehouseCode,
                        qty = group.Sum(x => x.BarcodeQty),
                        // warehouseCode= "1072",
                        unit = group.Key.BarcodeUnit,
                        barcodes = group.Select(row => new FeedbackBarcodesModel
                        {
                            barcode = row.Barcode,
                            qty = row.BarcodeQty
                        }).ToList()
                    }).ToList();
                infeedmodel.details = groupedData;
                var result1 = await _invokeMESService.FeedbackInbound(infeedmodel);
                if (result1 != null && result1.code == 200)
                {
                    var orderIds = inboundOrderDetails.Select(x => x.Id).Distinct().ToList();
                    if (inboundOrder.OrderStatus == InOrderStatusEnum.入库完成.ObjToInt())
                    {
                        _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1, Remark = "" })
                        .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                    }
                    _inboundOrderDetailService.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 })
                    .Where(it => orderIds.Contains(it.Id)).ExecuteCommand();
                }
                else
                {
                    _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 2, Remark = result1.message })
                    .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                    _inboundOrderDetailService.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 2 })
                   .Where(it => it.OrderId == inboundOrder.Id).ExecuteCommand();
                }
                //出库回传MES
                _feedbackMesService.OutboundFeedback(outboundOrder.OrderNo);
                var documentNo = UniqueValueGenerator.Generate();
                var outfeedmodel = new FeedbackOutboundRequestModel
                {
                    reqCode = Guid.NewGuid().ToString(),
                    reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                    business_type = outboundOrder.BusinessType,
                    factoryArea = outboundOrder.FactoryArea,
                    operationType = 1,
                    Operator = App.User.UserName,
                    orderNo = outboundOrder.UpperOrderNo,
                    documentsNO = documentNo,
                    status = outboundOrder.OrderStatus,
                    details = new List<FeedbackOutboundDetailsModel>()
                };
                foreach (var detail in outboundOrderDetails)
                {
                    // èŽ·å–è¯¥æ˜Žç»†å¯¹åº”çš„æ¡ç ä¿¡æ¯ï¼ˆä»Žé”å®šè®°å½•ï¼‰
                    var detailLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(x => x.OrderNo == outboundOrder.UpperOrderNo &&
                                    x.OrderDetailId == detail.Id &&
                                        (x.Status == (int)OutLockStockStatusEnum.拣选完成 || x.Status == (int)OutLockStockStatusEnum.已回库))
                        .ToListAsync();
                    var groupdata = detailLocks.GroupBy(item => new { item.MaterielCode, item.lineNo, item.BarcodeUnit, item.WarehouseCode })
                          .Select(group => new FeedbackOutboundDetailsModel
                          {
                              materialCode = group.Key.MaterielCode,
                              lineNo = group.Key.lineNo,
                              warehouseCode = group.Key.WarehouseCode,
                              qty = group.Sum(x => x.PickedQty),
                              currentDeliveryQty = group.Sum(x => x.PickedQty),
                              unit = group.Key.BarcodeUnit,
                              barcodes = group.Select(lockInfo => new WIDESEA_DTO.Outbound.BarcodesModel
                              {
                                  barcode = lockInfo.CurrentBarcode,
                                  supplyCode = lockInfo.SupplyCode,
                                  batchNo = lockInfo.BatchNo,
                                  unit = lockInfo.BarcodeUnit,
                                  qty = lockInfo.PickedQty
                              }).ToList()
                          }).ToList();
                    outfeedmodel.details.AddRange(groupdata);
                }
                //存储回传参数,保证异常手动回传
                Dt_InterfaceLog interfaceLog = new Dt_InterfaceLog
                {
                    OrderNo = outboundOrder.UpperOrderNo,
                    DocumentNo = documentNo,
                    OrderType = "虚拟出入库",
                    Content = outfeedmodel.ToJson(),
                    ReturnToMESStatus = 0,
                    IsDeleted = false
                };
                _interfaceLog.AddData(interfaceLog);
                var result = await _invokeMESService.FeedbackOutbound(outfeedmodel);
                if (result != null && result.code == 200)
                {
                    var orderIds = outboundOrderDetails.Select(x => x.Id).Distinct().ToList();
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(x => x.ReturnToMESStatus == 1)
                        .Where(x => orderIds.Contains(x.Id))
                        .ExecuteCommandAsync();
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(it => new Dt_OutboundOrder { ReturnToMESStatus = 1, Remark = "" })
                        .Where(x => x.Id == outboundOrder.Id)
                        .ExecuteCommandAsync();
                    await _interfaceLog.Db.Updateable<Dt_InterfaceLog>()
                        .SetColumns(x => x.ReturnToMESStatus == 1)
                        .Where(x => x.DocumentNo == documentNo)
                        .ExecuteCommandAsync();
                }
                else
                {
                    var uporderIds = outboundOrderDetails.Select(x => x.Id).Distinct().ToList();
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                      .SetColumns(x => x.ReturnToMESStatus == 2)
                       .Where(x => uporderIds.Contains(x.Id))
                      .ExecuteCommandAsync();
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(it => new Dt_OutboundOrder { ReturnToMESStatus = 2, Remark = result.message })
                         .Where(x => x.Id == outboundOrder.Id)
                        .ExecuteCommandAsync();
                    await _interfaceLog.Db.Updateable<Dt_InterfaceLog>()
                        .SetColumns(x => x.ReturnToMESStatus == 2)
                        .Where(x => x.DocumentNo == documentNo)
                        .ExecuteCommandAsync();
                }
                return WebResponseContent.Instance.OK();
            }
            catch (Exception ex)
@@ -2676,6 +2906,284 @@
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        public WebResponseContent NoStockOutBatchInOrderFeedbackToMes(int id,List<string> barCodeList)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>()
                                    .Where(x => x.Id == id)
                                    .First();
                List<Dt_MesReturnRecord> returnRecords = _mesReturnRecord.QueryData(x => x.OrderNo == inboundOrder.InboundOrderNo && x.OrderId == inboundOrder.Id && x.ReturnStatus == 2);
                foreach (var item in returnRecords)
                {
                    HttpResponseResult<MesResponseDTO> httpResponse = _httpClientHelper.Post<MesResponseDTO>(item.ApiUrl, item.RequestData);
                    item.ReturnCount += 1;
                    bool success = httpResponse.IsSuccess && httpResponse.Data.Code == "200";
                    item.ReturnStatus = success ? 1 : 2;
                    item.HttpStatusCode = httpResponse.StatusCode.ObjToInt();
                    item.LastReturnTime = DateTime.Now;
                    item.ResponseData = httpResponse.Content;
                    item.SuccessTime = httpResponse.IsSuccess ? DateTime.Now : null;
                    //List<Dt_InboundOrderDetail> details=new List<Dt_InboundOrderDetail>();
                    //foreach (var y in item.DetailsId.Split(','))
                    //{
                    //    details.Add( _inboundOrderDetailRepository.QueryFirst(x => x.Id == Convert.ToInt32(y)));
                    //}
                }
                _mesReturnRecord.UpdateData(returnRecords);
                var inboundOrderDetail = _inboundOrderRepository.Db.Queryable<Dt_InboundOrderDetail>()
                                        .Where(x => x.OrderId == inboundOrder.Id && x.OrderDetailStatus == (int)OrderDetailStatusEnum.Over && x.ReturnToMESStatus == 0)
                                        .ToList();
                var detail = inboundOrderDetail.Where(x => barCodeList.Contains(x.Barcode)).ToList();
                if (inboundOrder.OrderType == (int)InOrderTypeEnum.AllocatInbound)//调拨入库
                {
                    var allocate = _inboundOrderRepository.Db.Queryable<Dt_AllocateOrder>().Where(x => x.OrderNo == inboundOrder.InboundOrderNo).First();
                    var allocatefeedmodel = new AllocateDto
                    {
                        ReqCode = Guid.NewGuid().ToString(),
                        ReqTime = DateTime.Now.ToString(),
                        BusinessType = "3",
                        FactoryArea = inboundOrder.FactoryArea,
                        OperationType = 1,
                        Operator = App.User.UserName,
                        OrderNo = inboundOrder.UpperOrderNo,
                        fromWarehouse = allocate?.FromWarehouse ?? "",
                        toWarehouse = allocate?.ToWarehouse ?? "",
                        Details = NoStockOutGetAllocateDtoDetails(detail)
                    };
                    if (allocatefeedmodel.Details.Count <= 0)
                    {
                        throw new Exception("未找到需要回传的数据");
                    }
                    var response = NoStockOutresponseModel(inboundOrder, 3, null, allocatefeedmodel);
                    if (response != null && response.IsSuccess && response.Data.Code == "200")
                    {
                        _inboundOrderRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 })
                            .Where(it => it.OrderId == inboundOrder.Id && barCodeList.Contains(it.Barcode)).ExecuteCommand();
                        if (inboundOrder.OrderStatus == InOrderStatusEnum.入库完成.ObjToInt())
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        else if (inboundOrder.OrderStatus == InOrderStatusEnum.入库中.ObjToInt())
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 3 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        else
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 0 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                    }
                    else
                    {
                        _inboundOrderRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 2 })
                            .Where(it => it.OrderId == inboundOrder.Id && barCodeList.Contains(it.Barcode)).ExecuteCommand();
                        if (inboundOrder.OrderStatus == InOrderStatusEnum.入库完成.ObjToInt())
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 2 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        else if (inboundOrder.OrderStatus == InOrderStatusEnum.入库中.ObjToInt())
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 4 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        else
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 0 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        return content.Error("回传MES失败");
                    }
                }
                else
                {
                    var feedmodel = new FeedbackInboundRequestModel
                    {
                        reqCode = Guid.NewGuid().ToString(),
                        reqTime = DateTime.Now.ToString(),
                        business_type = inboundOrder.BusinessType,
                        factoryArea = inboundOrder.FactoryArea,
                        operationType = 1,
                        Operator = App.User.UserName,
                        orderNo = inboundOrder.UpperOrderNo,
                        status = inboundOrder.OrderStatus,
                        details = NoStockOutFeedbackInboundDetailsModelDto(detail)
                    };
                    if (feedmodel.details.Count <= 0)
                    {
                        throw new Exception("未找到需要回传的数据");
                    }
                    var response = NoStockOutresponseModel(inboundOrder, 3, feedmodel);
                    if (response != null && response.IsSuccess && response.Data.Code == "200")
                    {
                        _inboundOrderRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 })
                            .Where(it => it.OrderId == inboundOrder.Id && barCodeList.Contains(it.Barcode)).ExecuteCommand();
                        if (inboundOrder.OrderStatus == InOrderStatusEnum.入库完成.ObjToInt())
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        else if (inboundOrder.OrderStatus == InOrderStatusEnum.入库中.ObjToInt())
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 3 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        else
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 0 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                    }
                    else
                    {
                        _inboundOrderRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 2 }).Where(it => it.OrderId == inboundOrder.Id && barCodeList.Contains(it.Barcode)).ExecuteCommand();
                        if (inboundOrder.OrderStatus == InOrderStatusEnum.入库完成.ObjToInt())
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 2 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        else if (inboundOrder.OrderStatus == InOrderStatusEnum.入库中.ObjToInt())
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 4 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        else
                        {
                            _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 0 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                        }
                        return content.Error("回传MES失败");
                    }
                }
                return content.OK("回传MES成功");
            }
            catch (Exception ex)
            {
                return content.Error(ex.Message);
            }
        }
        public List<AllocateDtoDetail> NoStockOutGetAllocateDtoDetails(List<Dt_InboundOrderDetail> inboundOrderDetails)
        {
            var groupedData = inboundOrderDetails.GroupBy(item => new { item.MaterielCode, item.lineNo, item.BarcodeUnit, item.WarehouseCode })
               .Select(group => new AllocateDtoDetail
               {
                   MaterialCode = group.Key.MaterielCode,
                   LineNo = group.Key.lineNo,
                   WarehouseCode = group.Key.WarehouseCode,
                   Qty = group.Sum(x => x.BarcodeQty),
                   Unit = group.Key.BarcodeUnit,
                   Barcodes = group.Select(row => new BarcodeInfo
                   {
                       Barcode = row.Barcode,
                       Qty = row.BarcodeQty,
                       BatchNo = row.BatchNo,
                       SupplyCode = row.SupplyCode,
                       Unit = row.BarcodeUnit
                   }).ToList()
               }).ToList();
            return groupedData;
        }
        public List<FeedbackInboundDetailsModel> NoStockOutFeedbackInboundDetailsModelDto(List<Dt_InboundOrderDetail> inboundOrderDetails )
        {
            var groupedData = inboundOrderDetails.GroupBy(item => new { item.MaterielCode, item.lineNo, item.BarcodeUnit, item.WarehouseCode })
               .Select(group => new FeedbackInboundDetailsModel
               {
                   materialCode = group.Key.MaterielCode,
                   lineNo = group.Key.lineNo,
                   warehouseCode = group.Key.WarehouseCode,
                   qty = group.Sum(x => x.BarcodeQty),
                   unit = group.Key.BarcodeUnit,
                   barcodes = group.Select(row => new FeedbackBarcodesModel
                   {
                       barcode = row.Barcode,
                       qty = row.BarcodeQty
                   }).ToList()
               }).ToList();
            return groupedData;
        }
        public HttpResponseResult<MesResponseDTO> NoStockOutresponseModel(Dt_InboundOrder order, int InterfaceType, FeedbackInboundRequestModel model = null, AllocateDto allocateDto = null)
        {
            HttpResponseResult<MesResponseDTO> httpResponseResult = new HttpResponseResult<MesResponseDTO>();
            string reqCode = Guid.NewGuid().ToString();
            string reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            string requestData = string.Empty;
            string apiUrl = string.Empty;
            if (model != null)
            {
                apiUrl = AppSettings.GetValue("AldMaterialWarehousing");
                httpResponseResult = _httpClientHelper.Post<MesResponseDTO>(apiUrl, model.Serialize());
                requestData = model.Serialize();
            }
            else
            {
                apiUrl = AppSettings.GetValue("AldAllocationOperation");
                httpResponseResult = _httpClientHelper.Post<MesResponseDTO>(apiUrl, allocateDto.Serialize());
                requestData = allocateDto.Serialize();
            }
            httpResponseResult.ApiUrl = apiUrl;
            bool isSuccess = httpResponseResult.IsSuccess && httpResponseResult.Data.Code == "200";
            string message = "成功";
            if (!isSuccess)
            {
                if (!httpResponseResult.IsSuccess)
                {
                    message = $"MES接口返回错误,HTTP代码:{httpResponseResult.StatusCode},信息:{httpResponseResult.ErrorMessage}";
                }
                else if (httpResponseResult?.Data?.Code != "200")
                {
                    message = $"调用MES接口失败,代码:{httpResponseResult?.Data?.Code},信息:{httpResponseResult?.Data?.Message}";
                }
            }
            Dt_MesReturnRecord mesReturnRecord = new Dt_MesReturnRecord()
            {
                ApiUrl = httpResponseResult.ApiUrl,
                InterfaceType = InterfaceType,
                OrderId = order.Id,
                OrderNo = order.InboundOrderNo,
                RequestCode = reqCode,
                RequestData = requestData,
                FailureReason = message,
                LastReturnTime = DateTime.Now,
                HttpStatusCode = httpResponseResult.StatusCode.ObjToInt(),
                ResponseData = httpResponseResult.Content,
                ReturnType = 0,
                ReturnCount = 1,
                ReturnStatus = httpResponseResult.IsSuccess ? 1 : 2,
                SuccessTime = httpResponseResult.IsSuccess ? DateTime.Now : null
            };
            _unitOfWorkManage.Db.Insertable(mesReturnRecord).ExecuteCommand();
            return httpResponseResult;
        }
        public bool CheckOutboundOrderCompleted(string orderNo)
        {
            Dt_OutboundOrder outboundOrder = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().Where(x => x.OrderNo == orderNo).First();
            if (outboundOrder == null) return false;
            List<Dt_OutboundOrderDetail> details = _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().Where(x => x.OrderId == outboundOrder.Id).ToList();
            // æ£€æŸ¥æ‰€æœ‰æ˜Žç»†çš„已出数量是否都等于单据数量
            return details.All(x => x.OverOutQuantity >= x.OrderQuantity - x.MoveQty);
        }
        public WebResponseContent GetPurchaseOrderByBarcode(string barcode)
@@ -2685,7 +3193,12 @@
                Dt_InboundOrderDetail inboundOrderDetail = _stockInfoDetailService.Db.Queryable<Dt_InboundOrderDetail>().Where(x => x.Barcode == barcode && x.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()).First();
                if (inboundOrderDetail == null)
                {
                    return WebResponseContent.Instance.Error($"未找到该条码{barcode}的入库明细或者明细状态已入智仓完成");
                    inboundOrderDetail = _stockInfoDetailService.Db.Queryable<Dt_InboundOrderDetail>().Where(x => x.OutBoxbarcodes == barcode && x.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()).First();
                    if(inboundOrderDetail == null)
                    {
                        return WebResponseContent.Instance.Error($"未找到该条码{barcode}的入库明细或者明细状态已入智仓完成");
                    }
                }
                Dt_InboundOrder inboundOrder = _inboundOrderRepository.QueryFirst(x => x.Id == inboundOrderDetail.OrderId && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt());
                if (inboundOrder == null)
@@ -2844,7 +3357,8 @@
                        FactoryArea = item.FactoryArea,
                        Status = 0,
                        OrderNo = item.OrderNo,
                        BusinessType = materielGroupDTO.orderTypes.ToString()
                        BusinessType = materielGroupDTO.orderTypes.ToString(),
                    });
                    item.WarehouseCode = item.WarehouseCode;