chengsc
2025-03-13 02abdb2a7821e1eacc2b8ddc91e398cd9906c8cf
Wms/WMS.BLL/BllSoServer/ExportNoticeServer.cs
@@ -812,6 +812,8 @@
        #endregion
        #region JC34
        #region 基础功能
        public async Task<List<ExportNoticeDto>> GetExportNoticeList(GetExportNoticeVm model, RefAsync<int> count)
@@ -839,12 +841,14 @@
                .LeftJoin<SysUserInfor>((a, b, c) => a.CreateUser == c.Id)
                .LeftJoin<SysUserInfor>((a, b, c, d) => a.CreateUser == d.Id)
                .LeftJoin<SysUserInfor>((a, b, c, d, e) => a.CheckUser == e.Id)
                .Select((a, b, c, d, e) => new ExportNoticeDto()
                .LeftJoin<SysWareHouse>((a, b, c, d, e,f) => a.WareHouseNo == f.WareHouseNo)
                .Select((a, b, c, d, e,f) => new ExportNoticeDto()
                {
                    Id = a.Id,
                    SONo = a.SONo,
                    Type = a.Type,
                    Status = a.Status,
                    WareHouseName = f.WareHouseName,
                    Origin = a.Origin,
                    CustomerNo = a.CustomerNo,
                    CustomerName = a.CustomerName,
@@ -873,10 +877,14 @@
        }
        //获取添加/编辑出库单时选择物料明细信息
        public List<ExStockInfoDto> GetStockGroupList(string type, string ownerNo, string msg)
        public List<ExStockInfoDto> GetStockGroupList(string house, string type, string ownerNo, string msg)
        {
            try
            {
                if (string.IsNullOrEmpty(house))
                {
                    throw new Exception("请选择出库仓库");
                }
                if (string.IsNullOrEmpty(type))
                {
                    throw new Exception("请选择出库单类型");
@@ -893,23 +901,52 @@
                switch (type)//0:原料 1:包材 2:成品 3:耗材 4:半成品
                {
                    case "0"://成品出库
                        if (house !="W01")
                        {
                            throw new Exception("成品出库只能选择成品库");
                        }
                        skuType = "(2)";
                        inspectStatus = "1";
                        break;
                    case "1"://领料出库
                        if (house != "W02")
                        {
                            throw new Exception("领料出库只能选择原料库");
                        }
                        skuType = "(0,1,3)";
                        inspectStatus = "1";
                        break;
                    case "2"://抽检出库
                        skuType = "(0,1,2,3)";
                        if (house == "W01")
                        {
                            skuType = "(2)";
                        }
                        if (house == "W02")
                        {
                            skuType = "(0,1,3)";
                        }
                        inspectStatus = "0,1,2";
                        break;
                    case "3"://物料取样出库
                        skuType = "(0,1,2,3)";
                        if (house == "W01")
                        {
                            skuType = "(2)";
                        }
                        if (house == "W02")
                        {
                            skuType = "(0,1,3)";
                        }
                        inspectStatus = "0";
                        break;
                    case "4"://不合格品出库
                        skuType = "(0,1,2,3)";
                        if (house == "W01")
                        {
                            skuType = "(2)";
                        }
                        if (house == "W02")
                        {
                            skuType = "(0,1,3)";
                        }
                        inspectStatus = "2";
                        break;
                    case "5"://中间品出库
@@ -920,14 +957,38 @@
                        skuType = "(2)";
                        inspectStatus = "0,1";
                        break;
                    case "8"://寄存出库
                        skuType = "(0,1,2,3)";
                    case "7"://其它出库
                        if (house == "W01")
                        {
                            skuType = "(2)";
                        }
                        if (house == "W02")
                        {
                            skuType = "(0,1,3,4)";
                        }
                        inspectStatus = "0,1";
                        break;
                    case "8"://寄存出库
                        if (house == "W01")
                        {
                            skuType = "(2)";
                        }
                        if (house == "W02")
                        {
                            skuType = "(0,1,3)";
                        }
                        inspectStatus = "0,1";
                        break;
                    case "9"://生产领料出库
                        if (house != "W02")
                        {
                            throw new Exception("生产领料出库只能选择原料库");
                        }
                        skuType = "(0,1,3)";
                        inspectStatus = "1";
                        break;
                    default: //其它出库
                        skuType = "(0,1,2,3,4)";
                        inspectStatus = "0,1";
                        break;
                        throw new Exception("单据类型错误");
                }
                var skuList = sku.Where(m => skuType.Contains(m.Type)).ToList();
                if (skuList.Count == 0)
@@ -937,9 +998,9 @@
                if (type == "6")
                {
                    skuList = skuList.Where(m=> string.IsNullOrWhiteSpace(m.PackagNo)).ToList();
                    skuList = skuList.Where(m => string.IsNullOrWhiteSpace(m.PackagNo)).ToList();
                }
                if (type!="8")
                if (type != "8")
                {
                    skuList = skuList.Where(m => m.SkuNo != "100088").ToList();
                }
@@ -951,7 +1012,7 @@
                var stockRst = new StockServer();
                var stockDetailRst = new StockDetailServer();
                Expression<Func<DataStockDetail, bool>> item = Expressionable.Create<DataStockDetail>()
                    //.And(it => it.WareHouseNo == wareHouseNo)
                    .And(it => it.WareHouseNo == house)
                    .AndIF(!string.IsNullOrWhiteSpace(inspectStatus), it => inspectStatus.Contains(it.InspectStatus))
                    .And(m => skuStrList.Contains(m.SkuNo))
                    .AndIF(type == "6", m => m.OwnerNo == ownerNo)//代储出库需要关联货主
@@ -1035,6 +1096,23 @@
                var skuNos = model.Detail.Select(a => a.SkuNo).Distinct().ToList();
                //根据物料号获取物料信息、库存明细中获取批次描述供货批次等
                var skuList = Db.Queryable<SysMaterials>().Where(a => skuNos.Contains(a.SkuNo) && a.IsDel == "0").ToList();
                switch (model.WareHouseNo)
                {
                    case "W01"://成品库
                        if (skuList.Any(m=>m.Type !="2"))
                        {
                            throw new Exception("仓库与出库物料不符");
                        }
                        break;
                    case "W02"://原料库
                        if (skuList.Any(m => m.Type == "2"))
                        {
                            throw new Exception("仓库与出库物料不符");
                        }
                        break;
                    default:
                        throw new Exception("仓库号错误");
                }
                var stockList = Db.Queryable<DataStock>().Where(s => skuNos.Contains(s.SkuNo) && (s.Qty - s.FrozenQty - s.LockQty) > 0).ToList();
                //var palletList = contextDb.Queryable<DataStockDetail>().Where(s => skuNos.Contains(s.SkuNo) && (s.Qty - s.FrozenQty - s.LockQty) > 0).ToList();
                //客户信息
@@ -1068,7 +1146,7 @@
                            throw new Exception($"物料信息中未查询到出库物料信息:{d.SkuNo}");
                        }
                        //0:成品出库、1:领料出库、2:抽检出库、4:不合格品出库、6:代储出库、7:其他出库、 ///3:物料取样出库、5:中间品出库、8:寄存出库
                        //0:成品出库、1:领料出库、2:抽检出库、4:不合格品出库、7:其他出库、9生产领料出库 ///3:物料取样出库、5:中间品出库、6:代储出库、8:寄存出库
                        if (model.Type == "0" || model.Type == "2" || model.Type == "4" || model.Type == "7")
                        {
                            if (string.IsNullOrWhiteSpace(d.LotNo))
@@ -1077,20 +1155,20 @@
                            }
                        }
                        if (model.Type == "1")
                        {
                            if (list.Count(m=>m.SkuNo == d.SkuNo)>=2)
                            {
                                throw new Exception("领料出库单同单据同物料不允许超过3个批次混批出库");
                            }
                        }
                        else
                        {
                            if (list.Count(m => m.SkuNo == d.SkuNo) >= 1)
                            {
                                throw new Exception("出库单同单据同物料不允许超过2个批次混批出库");
                            }
                        }
                        //if (model.Type == "1")
                        //{
                        //    if (list.Count(m=>m.SkuNo == d.SkuNo)>=2)
                        //    {
                        //        throw new Exception("领料出库单同单据同物料不允许超过3个批次混批出库");
                        //    }
                        //}
                        //else
                        //{
                        //    if (list.Count(m => m.SkuNo == d.SkuNo) >= 1)
                        //    {
                        //        throw new Exception("出库单同单据同物料不允许超过2个批次混批出库");
                        //    }
                        //}
                        //库存
                        List<DataStock> stocks;
@@ -1141,7 +1219,7 @@
                        };
                        list.Add(item);
                        stocks.First().LockQty += d.Qty;//锁定数量
                        var i = Db.Updateable(stocks.First()).UpdateColumns(it => new { it.LockQty })
                            .ExecuteCommand();
@@ -1218,6 +1296,7 @@
                {
                    SoNo = notify.SONo,
                    Type = notify.Type,
                    WareHouseNo = notify.WareHouseNo,
                    CustomerNo = notify.CustomerNo,
                    LogisticsId = notify.LogisticsId,
                    //ExportWarehouseId = notify.ExportWarehouseId,
@@ -1618,10 +1697,10 @@
                    //查询改后的单据信息 后期接口对接完后需改动回传参数
                    var detail = Db.Queryable<BllExportNoticeDetail>().Where(m => m.IsDel == "0" && m.SONo == notice.SONo).Select(m => new SelectStockSkuDto
                    {
                        LotNo =m.LotNo,
                        LotNo = m.LotNo,
                        SkuNo = m.SkuNo,
                        SkuName = m.SkuName,
                        Qty = m.Qty,
                        Qty = m.Qty,
                        ExQty = decimal.Parse(m.CompleteQty.ToString()),
                        Standard = m.Standard,
                        IsBale = m.IsBale,
@@ -2154,6 +2233,743 @@
        }
        #endregion
        #region 自动分配、取消分配、获取手动分配的数据源、手动分配
        // 判断是否是跨批出库
        public bool IsCrossLotNo(string soNo)
        {
            try
            {
                //方法返回结果:bl
                var bl = true;
                //查询单据信息
                var notice = Db.Queryable<BllExportNotice>().First(m => m.SONo == soNo && m.IsDel == "0");
                //判断单据类型  成品出库、领料出库(其它类型跳出此方法)
                if (notice.Type != "0" && notice.Type != "1")
                {
                    return bl;
                }
                //查询到当前单据下的出库单明细信息
                var noticeDetail = Db.Queryable<BllExportNoticeDetail>().Where(m => m.IsDel == "0" && m.SONo == soNo).ToList();
                //库存总表信息
                var data = Db.Queryable<DataStock>().Where(m => m.IsDel == "0").ToList();
                //库存明细中检验合格批次集合
                var dataDetail = Db.Queryable<DataStockDetail>().Where(m => m.IsDel == "0" && m.InspectStatus == "1").GroupBy(g => g.LotNo).Select(s => s.LotNo).ToList();
                //库存明细表信息
                var dataBoxInfo = Db.Queryable<DataBoxInfo>().Where(m => m.IsDel == "0").ToList();
                //循环单据明细信息
                foreach (var item in noticeDetail)
                {
                    //验证先进先出原则
                    //获取当前物料的所有批次信息(排除单据的批次,防止单据所在批次锁定数量后验证小于等于0)
                    var forData = data.Where(m => m.SkuNo == item.SkuNo
                    && m.LotNo != item.LotNo && (m.Qty - m.FrozenQty - m.LockQty) > 0
                    && dataDetail.Contains(m.LotNo)).Select(m => m.LotNo).ToList();
                    forData.Add(item.LotNo);  //集合添加单据的批次
                    //获取排序后的第一个批次
                    var firstLotNo = forData.OrderBy(m => m).First();
                    if (firstLotNo != item.LotNo)
                    {
                        bl = false;
                        break;
                    }
                    //验证效期优先原则
                    var forDataBox = dataBoxInfo.Where(m => m.SkuNo == item.SkuNo && m.LotNo != item.LotNo && m.Qty > 0).ToList();
                    //获取当前单据批次的最近效期
                    var expirationTimedt = dataBoxInfo.Where(m => m.SkuNo == item.SkuNo
                    && m.LotNo == item.LotNo && m.Qty > 0).ToList();
                    if (expirationTimedt.Count > 0)
                    {
                        var expirationTime = expirationTimedt.OrderBy(m => m.ExpirationTime).Select(m => m.ExpirationTime).First();
                        //获取库存中其它批次效期大于当前单据批次的效期数量
                        var num = forDataBox.Count(m => m.ExpirationTime > expirationTime);
                        //判断是否大于0
                        if (num > 0)
                        {
                            bl = false;
                            break;
                        }
                    }
                    else
                    {
                        var dataDetailtime = Db.Queryable<DataStockDetail>().Where(m => m.IsDel == "0" && m.InspectStatus == "1").
                            Where(m => m.SkuNo == item.SkuNo
                            && m.LotNo == item.LotNo && m.Qty > 0)
                            .OrderBy(m => m.ExpirationTime)
                            .Select(m => m.ExpirationTime).First();
                        var num = forDataBox.Count(m => m.ExpirationTime > dataDetailtime);
                        //判断是否大于0
                        if (num > 0)
                        {
                            bl = false;
                            break;
                        }
                    }
                }
                return bl;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }
        /// <summary>
        /// 自动分配
        /// </summary>
        /// <param name="soNo"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool AutoAllot(string soNo, int userId)
        {
            try
            {
                #region 判断条件(出库单、出库单明细)
                //出库单
                var notice = Db.Queryable<BllExportNotice>().Where(m => m.IsDel == "0" && m.SONo == soNo).ToList().FirstOrDefault();
                if (notice == null)
                {
                    throw new Exception("未查询到出库单据信息");
                }
                if (notice.Status != "0" && notice.Status != "1")
                {
                    throw new Exception("参数异常,请检查状态是否为等待执行或部分分配;");
                }
                //出库单明细
                var detailList = Db.Queryable<BllExportNoticeDetail>().Where(m => m.IsDel == "0" && m.SONo == soNo && (m.AllotQty - m.Qty) <= 0).ToList();
                if (!detailList.Any())
                {
                    throw new Exception("未查询到符合分配条件的出库单据明细信息");
                }
                #endregion
                var exAllotList = new List<BllExportAllot>();
                var assign = new AllotSku();
                Db.BeginTran();
                try
                {
                    List<SoDetailInfo> soDetailList = new List<SoDetailInfo>();
                    foreach (var detail in detailList)
                    {
                        if (detail.AllotQty >= detail.Qty)
                        {
                            continue;
                        }
                        //还需要分配的数量
                        decimal needQty = detail.Qty - (detail.AllotQty == null ? 0 : decimal.Parse(detail.AllotQty.ToString()));
                        //库存明细 Status 0:待分配 1:部分分配  2:已分配
                        var stockDetail = Db.Queryable<DataStockDetail>().Where(m => m.SkuNo == detail.SkuNo && (m.Qty - m.FrozenQty - m.LockQty + m.InspectQty) > 0 && (m.Status == "0" || m.Status == "1") && m.IsDel == "0").ToList();
                        //判断单号是否指定批次
                        if (!string.IsNullOrWhiteSpace(detail.LotNo))
                        {
                            stockDetail = stockDetail.Where(m => m.SkuNo == detail.SkuNo && m.LotNo == detail.LotNo && m.IsDel == "0").ToList();
                        }
                        else
                        {
                            stockDetail = stockDetail.Where(m => m.SkuNo == detail.SkuNo && m.IsDel == "0" && string.IsNullOrWhiteSpace(m.LotNo)).ToList();
                        }
                        if (stockDetail.Count < 1)
                        {
                            throw new Exception("库存不足,无可出库库存");
                        }
                        //0:成品出库、1:领料出库、2:抽检出库、4:不合格品出库、7:其他出库、9:生产领料出库 ///3:取样出库、5:中间品出库、8:寄存出库
                        if (notice.Type == "0" || notice.Type == "1" || notice.Type == "9")//成品、领料出库
                        {
                            stockDetail = stockDetail.Where(m => m.InspectStatus == "1").ToList();
                        }
                        else if (notice.Type == "2")//抽检出库
                        {
                            stockDetail = stockDetail.Where(m => m.InspectStatus == "0" || m.InspectStatus == "1" || m.InspectStatus == "2").ToList();
                        }
                        else if (notice.Type == "4")//不合格出库
                        {
                            stockDetail = stockDetail.Where(m => m.InspectStatus == "2").ToList();
                        }
                        else if (notice.Type == "7") //、其它
                        {
                            stockDetail = stockDetail.Where(m => m.InspectStatus == "0" || m.InspectStatus == "1").ToList();
                        }
                        if (stockDetail.Sum(m => m.Qty - m.LockQty) < needQty)
                        {
                            throw new Exception("库存明细数量不足");
                        }
                        #region 包装信息
                        var pNum = 0;//托盘物品数量
                        var bNum = 0;//箱物品数量
                        //公共方法获取包装数量
                        new Common().GetPackQtyInfo(detail.PackagNo, ref pNum, ref bNum);
                        #endregion
                        //取合适库存商品
                        Dictionary<int, decimal> stockQtyDic = new Dictionary<int, decimal>();//托出库物品数
                        var qty = 0m;
                        var house = "";
                        if (notice.Type == "0")
                        {
                            house = "W01";
                        }
                        //分配货物
                        qty += assign.AllotPallets(stockDetail, needQty, pNum, bNum, stockQtyDic, house);
                        foreach (var sc in stockQtyDic)
                        {
                            var s = stockDetail.FirstOrDefault(m => m.Id == sc.Key);
                            //添加分配表信息
                            var allot = new BllExportAllot
                            {
                                SONo = notice.SONo,
                                WaveNo = "",
                                SODetailNo = detail.Id,
                                StockId = sc.Key,
                                LotNo = s.LotNo,
                                LotText = s.LotText,
                                SupplierLot = s.SupplierLot,
                                SkuNo = s.SkuNo,
                                SkuName = s.SkuName,
                                Standard = s.Standard,
                                PalletNo = s.PalletNo,
                                IsBale = detail.IsBale == "0" ? "0" : s.IsBale == "1" ? "0" : "1", //是否裹包
                                IsBelt = detail.IsBelt == "0" ? "0" : s.IsBelt == "1" ? "0" : "1", //是否打带
                                Qty = sc.Value,
                                CompleteQty = 0,
                                //BoxexQty = s.Qty, //箱内数量
                                Status = "0",
                                LogisticsId = notice.LogisticsId,
                                IsAdvance = "0",
                                OutMode = "",//出库口
                                CreateUser = userId,
                                CreateTime = DateTime.Now
                            };
                            exAllotList.Add(allot);
                            s.LockQty += stockQtyDic[s.Id];
                            if (s.LockQty == s.Qty)
                            {
                                s.Status = "2";
                            }
                            else
                            {
                                s.Status = "1";
                            }
                            var sd = Db.Updateable(s).UpdateColumns(it => new { it.LockQty, it.Status }).ExecuteCommand();
                        }
                        detail.AllotQty += qty;
                        detail.UpdateUser = userId;
                        detail.UpdateTime = DateTime.Now;
                        if (detail.Status == "0")
                        {
                            detail.Status = "1";
                        }
                        if (qty > detail.Qty)
                        {
                            //库存总表
                            var stock = Db.Queryable<DataStock>().First(d => d.IsDel == "0"
                            && d.SkuNo == detail.SkuNo
                            && d.LotNo == detail.LotNo);
                            stock.LockQty += qty - detail.Qty;
                            Db.Updateable(stock).ExecuteCommand();
                            //添加回传上游系统锁定数量更改代码
                            SoDetailInfo soDetail = new SoDetailInfo();
                            soDetail.OrderDetailCode = detail.OrderDetailCode;
                            soDetail.LockQty = qty - detail.Qty;
                            soDetail.LotNo = detail.LotNo;
                            soDetailList.Add(soDetail);
                        }
                    }
                    var mx = Db.Updateable(detailList).ExecuteCommand();
                    var fp = Db.Insertable(exAllotList).ExecuteCommand();
                    //修改分配单据的状态
                    if (notice.Status == "0" || notice.Status == "1")
                    {
                        var bl = 0;
                        var bl2 = 0;
                        foreach (var item in detailList)
                        {
                            if (item.AllotQty <= 0)
                            {
                                continue;
                            }
                            if (item.AllotQty < item.Qty)
                            {
                                bl = 1;
                            }
                            else
                            {
                                bl2 = 1;
                            }
                        }
                        switch (bl2)
                        {
                            case 1 when bl == 1:
                                notice.Status = "1";//证明部分分配数量全部大于等于出库数量  修改为已分配
                                break;
                            case 0 when bl == 1:
                                notice.Status = "1";//证明部分分配数量全部大于等于出库数量  修改为已分配
                                break;
                            case 1 when bl == 0:
                                notice.Status = "2";//证明分配数量全部大于等于出库数量  修改为已分配
                                break;
                            case 0 when bl == 0:
                                //证明所有分配数量全部小于等于出库数量 不做修改
                                break;
                        }
                    }
                    notice.UpdateUser = userId;
                    notice.UpdateTime = DateTime.Now;
                    var zd = Db.Updateable(notice).ExecuteCommand();
                    //添加操作日志记录
                    var k = new OperationSOServer().AddLogOperationSo("出库作业", "出库单据",
                        notice.SONo, "分配", $"自动分配了单据号为{notice.SONo}的单据信息", userId);
                    if (zd > 0 && mx > 0 && fp > 0 && k)
                    {
                        #region 通过接口发送至erp
                        //系统对接后放开
                        /*var jsonData = JsonConvert.SerializeObject(soDetailList);
                        var response = HttpHelper.DoPost(url, jsonData, "库存锁定数量变更", "ERP");
                        var obj = JsonConvert.DeserializeObject<ErpModel>(response);//解析返回数据
                        if (obj.Success != 0)
                        {
                            throw new Exception("上传失败" + obj.Message);
                        }*/
                        #endregion
                        Db.CommitTran();
                        return true;
                    }
                    Db.RollbackTran();
                    return false;
                }
                catch (Exception e)
                {
                    Db.RollbackTran();
                    throw new Exception(e.Message);
                }
            }
            catch (Exception e)
            {
                throw new Exception("自动分配失败:" + e.Message);
            }
        }
        //取消分配
        public bool CancelAllot(string soNo, int userId)
        {
            try
            {
                var notice = Db.Queryable<BllExportNotice>().Where(m => m.IsDel == "0" && m.SONo == soNo).ToList().FirstOrDefault();
                if (notice == null)
                {
                    throw new Exception("未查询到出库单据信息");
                }
                if (notice.Status != "1" && notice.Status != "2")
                {
                    throw new Exception("参数异常,请检查状态是否为已分配或部分分配或来源是否是WMS");
                }
                //该单据的分配信息 Status 0:任务下发 1:待拣货 2:部分拣货 3:待回库 4:已完成
                var allotList = Db.Queryable<BllExportAllot>().Where(o => o.IsDel == "0" && o.SONo == soNo).ToList();
                //有已执行的分配数据不能取消
                if (allotList.Any(o => o.Status != "0"))
                {
                    throw new Exception("当前单据的分配信息已有执行中,不能取消分配");
                }
                List<SoDetailInfo> soDetailList = new List<SoDetailInfo>();
                //开启事务
                Db.BeginTran();
                try
                {
                    //查询分配的明细
                    var detail = Db.Queryable<BllExportNoticeDetail>().Where(d => d.SONo == soNo && d.AllotQty > 0 && d.IsDel == "0").ToList();
                    foreach (var d in detail)
                    {
                        var orders = allotList.Where(o => o.SODetailNo == d.Id).ToList();
                        foreach (var o in orders)
                        {
                            var pq = Db.Queryable<DataStockDetail>().Where(t => t.Id == o.StockId);
                            var pq2 = !string.IsNullOrWhiteSpace(o.LotNo) ? pq.Where(t => t.LotNo == o.LotNo).ToList() : pq.Where(t => string.IsNullOrWhiteSpace(t.LotNo)).ToList();
                            var pallet = pq2.FirstOrDefault();
                            if (pallet != null)
                            {
                                pallet.LockQty -= o.Qty;
                                pallet.Status = pallet.LockQty == 0 ? "0" : "1"; //如果锁定数量是0状态变更为待分配 否则为部分分配
                                Db.Updateable(pallet).ExecuteCommand();
                                //库存总表
                                //var stock = Db.Queryable<DataStock>().First(t => t.SkuNo == pallet.SkuNo && t.IsDel == "0");
                                //stock.LockQty -= o.Qty;
                                //Db.Updateable(stock).ExecuteCommand();
                            }
                        }
                        Db.Deleteable<BllExportAllot>(orders).ExecuteCommand();
                        if (d.AllotQty > d.Qty)
                        {
                            //库存总表
                            var stock = Db.Queryable<DataStock>().First(m => m.IsDel == "0" && m.SkuNo == d.SkuNo && m.LotNo == d.LotNo);
                            stock.LockQty -= (decimal)d.AllotQty - d.Qty;
                            Db.Updateable(stock).ExecuteCommand();
                            //添加回传上游系统锁定数量更改代码
                            SoDetailInfo soDetail = new SoDetailInfo();
                            soDetail.OrderDetailCode = d.OrderDetailCode;
                            soDetail.LockQty = (decimal)(d.Qty - d.AllotQty);
                            soDetail.LotNo = d.LotNo;
                            soDetailList.Add(soDetail);
                        }
                        d.AllotQty = 0;
                        d.Status = "0";
                        d.UpdateUser = userId;
                        d.UpdateTime = DateTime.Now;
                        //if (notice.Type == "1" || notice.Type == "5" || notice.Type == "6" || notice.Type == "7" || notice.Type == "8")//1:领料出库、
                        //{
                        //    if (d.IsIssueLotNo != "1")
                        //    {
                        //        d.LotNo = "";
                        //    }
                        //}
                    }
                    //查询当前单据是否已添加备料任务
                    if (notice.Type == "1")
                    {
                        var task = Db.Queryable<BllExportTimingTask>().First(m => m.IsDel == "0" && m.SoNo == soNo);
                        if (task != null)
                        {
                            task.IsDel = "1";
                            task.UpdateUser = userId;
                            task.UpdateTime = DateTime.Now;
                            Db.Updateable(task).ExecuteCommand();
                        }
                    }
                    notice.Status = "0";
                    notice.UpdateUser = userId;
                    notice.UpdateTime = DateTime.Now;
                    Db.Updateable(detail).ExecuteCommand();
                    Db.Updateable(notice).ExecuteCommand();
                    //系统对接后放开
                    /*var jsonData = JsonConvert.SerializeObject(soDetailList);
                    var response = HttpHelper.DoPost(url, jsonData, "库存锁定数量变更", "ERP");
                    var obj = JsonConvert.DeserializeObject<ErpModel>(response);//解析返回数据
                    if (obj.Success != 0)
                    {
                        throw new Exception("上传失败" + obj.Message);
                    }*/
                    //添加操作日志记录
                    var k = new OperationSOServer().AddLogOperationSo("出库作业", "出库单据", notice.SONo, "取消分配", $"取消分配了单据号为{notice.SONo}的单据信息", userId);
                    Db.CommitTran();
                }
                catch (Exception e)
                {
                    Db.RollbackTran();
                    throw new Exception(e.Message);
                }
                return true;
            }
            catch (Exception e)
            {
                throw new Exception("取消分配失败" + e.Message);
            }
        }
        /// <summary>
        /// 维护出库单备注信息
        /// </summary>
        /// <param name="id"></param>
        /// <param name="demo"></param>
        /// <param name="userId"></param>
        public void EditNoticeDemo(int id, string demo, int userId)
        {
            try
            {
                var notice = Db.Queryable<BllExportNotice>().First(m => m.IsDel == "0" && m.Id == id);
                if (notice == null)
                {
                    throw new Exception("未查询到出库单据信息");
                }
                notice.Demo = demo + "".Trim();
                notice.UpdateUser = userId;
                notice.UpdateTime = DateTime.Now;
                int i = Db.Updateable(notice).ExecuteCommand();
                if (i > 0)
                {
                    //添加操作日志
                    new OperationSOServer().AddLogOperationSo("出库作业", "出库单据", notice.SONo, "编辑", $"编辑了单据号为{notice.SONo}的备注信息", userId);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        //获取库存明细信息(出库单手动分配选择数据源)
        public List<StockDetailDto> GetHandOutList(int detailId, string houseNo, string roadwayNo, string locateNo, string msg, string palletNo)
        {
            try
            {
                var detail = Db.Queryable<BllExportNoticeDetail>().First(d => d.Id == detailId);
                #region 判断条件
                if (detail == null)
                {
                    throw new Exception("获取失败,未找到指定出库单!");
                }
                if (detail.Status != "0" && detail.Status != "1" && detail.AllotQty >= detail.Qty)
                {
                    throw new Exception("获取失败,出库单状态不是等待执行或分配中!");
                }
                if (detail.AllotQty >= detail.Qty)
                {
                    throw new Exception("获取失败,出库单已分配完成!");
                }
                var notice = Db.Queryable<BllExportNotice>().First(a => a.SONo == detail.SONo);
                if (notice == null)
                {
                    throw new Exception("获取失败,未找到指定出库单!");
                }
                if (notice.Status == "3" && detail.AllotQty >= detail.Qty || notice.Status == "4" || notice.Status == "5")
                {
                    throw new Exception("获取失败,出库单状态不允许!");
                }
                #endregion
                Expression<Func<DataStockDetail, bool>> item = Expressionable.Create<DataStockDetail>()
                    .AndIF(!string.IsNullOrWhiteSpace(houseNo), m => m.WareHouseNo == houseNo)
                    .AndIF(!string.IsNullOrWhiteSpace(roadwayNo), m => m.RoadwayNo == roadwayNo)
                    .AndIF(!string.IsNullOrWhiteSpace(locateNo), m => m.LocatNo == locateNo)
                    .AndIF(!string.IsNullOrWhiteSpace(palletNo), m => m.PalletNo == palletNo)
                    .AndIF(!string.IsNullOrWhiteSpace(msg),
                        m => m.SkuNo.Contains(msg.Trim())
                             || m.SkuName.Contains(msg.Trim())
                             || m.LocatNo.Contains(msg.Trim()))
                    .And(m => m.IsDel == "0" && m.SkuNo == detail.SkuNo && m.LotNo == detail.LotNo && (m.Status == "0" || m.Status == "1"))
                    .ToExpression();//注意 这一句 不能少
                var list = Db.Queryable<DataStockDetail>().Where(item).Select(a => new StockDetailDto
                {
                    Id = a.Id,
                    SkuNo = a.SkuNo,
                    SkuName = a.SkuName,
                    Standard = a.Standard,
                    LotNo = a.LotNo,
                    LotText = a.LotText,
                    SupplierLot = a.SupplierLot,
                    Qty = a.Qty - a.LockQty - a.FrozenQty,
                    LocatNo = a.LocatNo,
                    RoadwayNo = a.RoadwayNo,
                    PalletNo = a.PalletNo,
                    Demo = a.Demo,
                }).ToList();
                return list;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }
        //手动分配出库单明细
        public void AddHandOutAllot(AddHandOutVm model, int userId)
        {
            try
            {
                #region 判断条件
                //数据验证
                var detail = Db.Queryable<BllExportNoticeDetail>().First(a => a.IsDel == "0" && a.Id == model.Id);
                if (detail == null)
                {
                    throw new Exception("操作失败,未找到指定出库单详情!");
                }
                if (detail.AllotQty >= detail.Qty || (detail.Status != "0" && detail.Status != "1"))
                {
                    throw new Exception("操作失败,出库单已分配完成!");
                }
                var notice = Db.Queryable<BllExportNotice>().First(a => a.IsDel == "0" && a.SONo == detail.SONo);
                if (notice == null)
                {
                    throw new Exception("操作失败,未找到指定出库单!");
                }
                if (notice.Status == "3" && detail.AllotQty >= detail.Qty || notice.Status == "4" || notice.Status == "5")
                {
                    throw new Exception("操作失败,出库单已分配完成!");
                }
                #endregion
                //单据明细需要的出库数量
                var needQty = detail.Qty - detail.AllotQty;
                //分配的出库数量
                var outQty = model.StockList.Select(s => s.Qty).ToList().Sum();
                if (outQty != needQty)
                {
                    throw new Exception("操作失败,出库数量与计划数量不一致!");
                }
                var stockIds = model.StockList.Select(a => a.StockId).ToList();
                //库存明细
                var stockList = Db.Queryable<DataStockDetail>().Where(a => stockIds.Contains(a.Id)).ToList();
                //分配信息
                var allots = Db.Queryable<BllExportAllot>().Where(m => m.IsDel == "0" && m.SODetailNo == detail.Id && m.Status == "0").ToList();
                //库存总表
                //var stockz = Db.Queryable<DataStock>().First(d => d.IsDel == "0" && d.SkuNo == detail.SkuNo && d.LotNo == detail.LotNo);
                var allotList = new List<BllExportAllot>();
                decimal outQtys = 0;
                foreach (var st in model.StockList)
                {
                    var stock = stockList.First(a => a.Id == st.StockId);
                    if (stock == null)
                    {
                        throw new Exception("操作失败,部分储位库存异常!");
                    }
                    if (st.Qty > (stock.Qty - stock.LockQty - stock.FrozenQty))     // 输入的数量 -  托盘上可用的数量(托盘上数量-锁定的数量-冻结的数量)
                    {
                        throw new Exception("操作失败,出库数量超出库存数量!");
                    }
                    var bl = allots.FirstOrDefault(m => m.StockId == st.StockId);
                    if (bl == null)
                    {
                        //添加分配表信息
                        var allot = new BllExportAllot
                        {
                            SONo = notice.SONo,
                            WaveNo = "",
                            SODetailNo = detail.Id,
                            StockId = st.StockId,
                            LotNo = stock.LotNo,
                            LotText = stock.LotText,
                            SupplierLot = stock.SupplierLot,
                            SkuNo = stock.SkuNo,
                            SkuName = stock.SkuName,
                            Standard = stock.Standard,
                            PalletNo = stock.PalletNo,
                            IsBale = stock.IsBale,
                            IsBelt = stock.IsBelt,
                            Qty = st.Qty,
                            CompleteQty = 0,
                            Status = "0",
                            LogisticsId = notice.LogisticsId,
                            IsAdvance = "0",
                            OutMode = "",//出库口
                            CreateUser = userId,
                            CreateTime = DateTime.Now
                        };
                        allotList.Add(allot);
                    }
                    else
                    {
                        bl.Qty += st.Qty;
                        Db.Updateable(bl).ExecuteCommand();
                    }
                    //库存明细
                    stock.LockQty += st.Qty;
                    stock.Status = stock.LockQty == stock.Qty ? "2" : "1";
                    //库存总表
                    //stockz.LockQty += st.Qty;
                    //Db.Updateable(stockz).ExecuteCommand();
                    Db.Updateable(stock).UpdateColumns(it => new { it.LockQty, it.Status }).ExecuteCommand();
                    outQtys += st.Qty;
                }
                Db.Insertable(allotList).ExecuteCommand();
                //修改单据明细
                detail.AllotQty += outQtys;
                detail.UpdateUser = userId;
                detail.UpdateTime = DateTime.Now;
                if (detail.Status == "0")
                {
                    detail.Status = "1";
                }
                Db.Updateable(detail).ExecuteCommand();
                var detailList = Db.Queryable<BllExportNoticeDetail>()
                    .Where(m => m.IsDel == "0" && m.SONo == notice.SONo).ToList();
                //修改出库单状态
                if (notice.Status == "0" || notice.Status == "1")
                {
                    decimal totalQty = 0;
                    decimal totalAllotQty = 0;
                    foreach (var item in detailList)
                    {
                        totalQty += item.Qty;
                        totalAllotQty += Convert.ToInt32(item.AllotQty);
                    }
                    if (totalAllotQty >= totalQty)
                    {
                        notice.Status = "2";//证明分配数量大于等于出库数量  修改为已分配
                    }
                    else if (totalAllotQty < totalQty && totalAllotQty > 0)
                    {
                        notice.Status = "1";//证明分配数量小于等于出库数量  修改为部分分配
                    }
                    Db.Updateable(notice).ExecuteCommand();
                }
                //添加操作日志记录
                var k = new OperationSOServer().AddLogOperationSo("出库作业", "出库单据", notice.SONo, "分配", $"手动分配了单据号为{notice.SONo}、物料:{detail.SkuNo}、批次:{detail.LotNo}的单据信息", userId);
                Db.CommitTran();
            }
            catch (Exception e)
            {
                Db.RollbackTran();
                throw new Exception(e.Message);
            }
        }
        #endregion
        #endregion
        //------------------------------------------------------------------
@@ -4289,727 +5105,7 @@
        }
        #endregion
        #region 自动分配、取消分配、获取手动分配的数据源、手动分配
        // 判断是否是跨批出库
        public bool IsCrossLotNo(string soNo)
        {
            try
            {
                //方法返回结果:bl
                var bl = true;
                //查询单据信息
                var notice = Db.Queryable<BllExportNotice>().First(m=>m.SONo == soNo && m.IsDel == "0");
                //判断单据类型  成品出库、领料出库(其它类型跳出此方法)
                if (notice.Type!="0" && notice.Type!="1")
                {
                    return bl;
                }
                //查询到当前单据下的出库单明细信息
                var noticeDetail = Db.Queryable<BllExportNoticeDetail>().Where(m => m.IsDel == "0" && m.SONo == soNo).ToList();
                //库存总表信息
                var data = Db.Queryable<DataStock>().Where(m => m.IsDel == "0").ToList();
                //库存明细中检验合格批次集合
                var dataDetail = Db.Queryable<DataStockDetail>().Where(m => m.IsDel == "0" && m.InspectStatus == "1").GroupBy(g => g.LotNo).Select(s => s.LotNo).ToList();
                //库存明细表信息
                var dataBoxInfo = Db.Queryable<DataBoxInfo>().Where(m => m.IsDel == "0").ToList();
                //循环单据明细信息
                foreach (var item in noticeDetail)
                {
                    //验证先进先出原则
                    //获取当前物料的所有批次信息(排除单据的批次,防止单据所在批次锁定数量后验证小于等于0)
                    var forData = data.Where(m => m.SkuNo == item.SkuNo
                    && m.LotNo != item.LotNo && (m.Qty - m.FrozenQty - m.LockQty) > 0
                    && dataDetail.Contains(m.LotNo)).Select(m=>m.LotNo).ToList();
                    forData.Add(item.LotNo);  //集合添加单据的批次
                    //获取排序后的第一个批次
                    var firstLotNo = forData.OrderBy(m => m).First();
                    if (firstLotNo != item.LotNo)
                    {
                        bl = false;
                        break;
                    }
                    //验证效期优先原则
                    var forDataBox = dataBoxInfo.Where(m => m.SkuNo == item.SkuNo && m.LotNo != item.LotNo && m.Qty > 0).ToList();
                    //获取当前单据批次的最近效期
                    var expirationTimedt = dataBoxInfo.Where(m => m.SkuNo == item.SkuNo
                    && m.LotNo == item.LotNo && m.Qty > 0).ToList();
                    if (expirationTimedt.Count > 0)
                    {
                        var expirationTime = expirationTimedt.OrderBy(m => m.ExpirationTime).Select(m => m.ExpirationTime).First();
                        //获取库存中其它批次效期大于当前单据批次的效期数量
                        var num = forDataBox.Count(m => m.ExpirationTime > expirationTime);
                        //判断是否大于0
                        if (num > 0)
                        {
                            bl = false;
                            break;
                        }
                    }
                    else
                    {
                        var dataDetailtime = Db.Queryable<DataStockDetail>().Where(m => m.IsDel == "0" && m.InspectStatus == "1").
                            Where(m => m.SkuNo == item.SkuNo
                            && m.LotNo == item.LotNo && m.Qty > 0)
                            .OrderBy(m => m.ExpirationTime)
                            .Select(m => m.ExpirationTime).First();
                        var num = forDataBox.Count(m => m.ExpirationTime > dataDetailtime);
                        //判断是否大于0
                        if (num > 0)
                        {
                            bl = false;
                            break;
                        }
                    }
                }
                return bl;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }
        //自动分配
        public bool AutoAllot(string soNo, int userId)
        {
            try
            {
                #region 判断条件(出库单、出库单明细)
                //出库单
                var notice = Db.Queryable<BllExportNotice>().Where(m => m.IsDel == "0" && m.SONo == soNo).ToList().FirstOrDefault();
                if (notice == null)
                {
                    throw new Exception("未查询到出库单据信息");
                }
                if (notice.Status != "0" && notice.Status != "1")
                {
                    throw new Exception("参数异常,请检查状态是否为等待执行或部分分配;");
                }
                //出库单明细
                var detailList = Db.Queryable<BllExportNoticeDetail>().Where(m => m.IsDel == "0" && m.SONo == soNo && (m.AllotQty - m.Qty) <= 0).ToList();
                if (!detailList.Any())
                {
                    throw new Exception("未查询到符合分配条件的出库单据明细信息");
                }
                #endregion
                var exAllotList = new List<BllExportAllot>();
                var assign = new AllotSku();
                Db.BeginTran();
                try
                {
                    List<SoDetailInfo> soDetailList = new List<SoDetailInfo>();
                    foreach (var detail in detailList)
                    {
                        if (detail.AllotQty >= detail.Qty)
                        {
                            continue;
                        }
                        //还需要分配的数量
                        decimal needQty = detail.Qty - (detail.AllotQty == null? 0: decimal.Parse(detail.AllotQty.ToString()));
                        //库存明细 Status 0:待分配 1:部分分配  2:已分配
                        var stockDetail = Db.Queryable<DataStockDetail>().Where(m => m.SkuNo == detail.SkuNo && (m.Qty - m.FrozenQty - m.LockQty + m.InspectQty) > 0 && (m.Status == "0" || m.Status == "1") && m.IsDel == "0").ToList();
                        //判断单号是否指定批次
                        if (!string.IsNullOrWhiteSpace(detail.LotNo))
                        {
                            stockDetail = stockDetail.Where(m => m.SkuNo == detail.SkuNo && m.LotNo == detail.LotNo && m.IsDel == "0").ToList();
                        }
                        else
                        {
                            stockDetail = stockDetail.Where(m => m.SkuNo == detail.SkuNo && m.IsDel == "0" && string.IsNullOrWhiteSpace(m.LotNo)).ToList();
                        }
                        if (stockDetail.Count < 1)
                        {
                            throw new Exception("库存不足,无可出库库存");
                        }
                        //0:成品出库、1:领料出库、2:抽检出库、4:不合格品出库、6:代储出库、7:其他出库、 ///3:取样出库、5:中间品出库、8:寄存出库
                        if (notice.Type == "0" || notice.Type == "1" )//成品、领料出库
                        {
                            stockDetail = stockDetail.Where(m => m.InspectStatus == "1").ToList();
                        }
                        else if (notice.Type == "2" )//抽检出库
                        {
                            stockDetail = stockDetail.Where(m => m.InspectStatus == "0" || m.InspectStatus == "1" || m.InspectStatus == "2").ToList();
                        }
                        else if (notice.Type == "6" || notice.Type == "7" ) //代储、其它
                        {
                            stockDetail = stockDetail.Where(m => m.InspectStatus == "0" || m.InspectStatus == "1").ToList();
                        }
                        else if (notice.Type == "4")//不合格出库
                        {
                            stockDetail = stockDetail.Where(m => m.InspectStatus == "2").ToList();
                        }
                        if (stockDetail.Sum(m=>m.Qty-m.LockQty) < needQty)
                        {
                            throw new Exception("库存明细数量不足");
                        }
                        #region 包装信息
                        var pNum = 0;//托盘物品数量
                        var bNum = 0;//箱物品数量
                        //公共方法获取包装数量
                        new Common().GetPackQtyInfo(detail.PackagNo,ref pNum,ref bNum);
                        #endregion
                        //取合适库存商品
                        Dictionary<int, decimal> stockQtyDic = new Dictionary<int, decimal>();//托出库物品数
                        var qty = 0m;
                        //分配货物
                        qty += assign.AllotPallets(stockDetail, needQty, pNum, bNum, stockQtyDic, detail.LotNo, detail.IsMixBox);
                        foreach (var sc in stockQtyDic)
                        {
                            var s = stockDetail.FirstOrDefault(m => m.Id == sc.Key);
                            //添加分配表信息
                            var allot = new BllExportAllot
                            {
                                SONo = notice.SONo,
                                WaveNo = "",
                                SODetailNo = detail.Id,
                                StockId = sc.Key,
                                LotNo = s.LotNo,
                                LotText = s.LotText,
                                SupplierLot = s.SupplierLot,
                                SkuNo = s.SkuNo,
                                SkuName = s.SkuName,
                                Standard = s.Standard,
                                PalletNo = s.PalletNo,
                                IsBale = detail.IsBale == "0" ? "0" : s.IsBale == "1" ? "0" : "1", //是否裹包
                                IsBelt = detail.IsBelt == "0" ? "0" : s.IsBelt == "1" ? "0" : "1", //是否打带
                                Qty = sc.Value,
                                CompleteQty = 0,
                                //BoxexQty = s.Qty, //箱内数量
                                Status = "0",
                                LogisticsId = notice.LogisticsId,
                                IsAdvance = "0",
                                OutMode = "",//出库口
                                CreateUser = userId,
                                CreateTime = DateTime.Now
                            };
                            exAllotList.Add(allot);
                            s.LockQty += stockQtyDic[s.Id];
                            if (s.LockQty == s.Qty)
                            {
                                s.Status = "2";
                            }
                            else
                            {
                                s.Status = "1";
                            }
                            var sd = Db.Updateable(s).UpdateColumns(it => new { it.LockQty, it.Status }).ExecuteCommand();
                        }
                        detail.AllotQty += qty;
                        detail.UpdateUser = userId;
                        detail.UpdateTime = DateTime.Now;
                        if (detail.Status == "0")
                        {
                            detail.Status = "1";
                        }
                        if (qty > detail.Qty)
                        {
                            //库存总表
                            var stock = Db.Queryable<DataStock>().First(d => d.IsDel == "0"
                            && d.SkuNo == detail.SkuNo
                            && d.LotNo == detail.LotNo);
                            stock.LockQty += qty-detail.Qty;
                            Db.Updateable(stock).ExecuteCommand();
                            //添加回传上游系统锁定数量更改代码
                            SoDetailInfo soDetail = new SoDetailInfo();
                            soDetail.OrderDetailCode = detail.OrderDetailCode;
                            soDetail.LockQty = qty - detail.Qty;
                            soDetail.LotNo = detail.LotNo;
                            soDetailList.Add(soDetail);
                        }
                    }
                    var mx = Db.Updateable(detailList).ExecuteCommand();
                    var fp = Db.Insertable(exAllotList).ExecuteCommand();
                    //修改分配单据的状态
                    if (notice.Status == "0" || notice.Status == "1")
                    {
                        var bl = 0;
                        var bl2 = 0;
                        foreach (var item in detailList)
                        {
                            if (item.AllotQty <= 0)
                            {
                                continue;
                            }
                            if (item.AllotQty < item.Qty)
                            {
                                bl = 1;
                            }
                            else
                            {
                                bl2 = 1;
                            }
                        }
                        switch (bl2)
                        {
                            case 1 when bl == 1:
                                notice.Status = "1";//证明部分分配数量全部大于等于出库数量  修改为已分配
                                break;
                            case 0 when bl == 1:
                                notice.Status = "1";//证明部分分配数量全部大于等于出库数量  修改为已分配
                                break;
                            case 1 when bl == 0:
                                notice.Status = "2";//证明分配数量全部大于等于出库数量  修改为已分配
                                break;
                            case 0 when bl == 0:
                                //证明所有分配数量全部小于等于出库数量 不做修改
                                break;
                        }
                    }
                    notice.UpdateUser = userId;
                    notice.UpdateTime = DateTime.Now;
                    var zd = Db.Updateable(notice).ExecuteCommand();
                    //添加操作日志记录
                    var k = new OperationSOServer().AddLogOperationSo("出库作业", "出库单据",
                        notice.SONo, "分配", $"自动分配了单据号为{notice.SONo}的单据信息", userId);
                    if (zd > 0 && mx > 0 && fp > 0 && k)
                    {
                        #region 通过接口发送至erp
                        //系统对接后放开
                        /*var jsonData = JsonConvert.SerializeObject(soDetailList);
                        var response = HttpHelper.DoPost(url, jsonData, "库存锁定数量变更", "ERP");
                        var obj = JsonConvert.DeserializeObject<ErpModel>(response);//解析返回数据
                        if (obj.Success != 0)
                        {
                            throw new Exception("上传失败" + obj.Message);
                        }*/
                        #endregion
                        Db.CommitTran();
                        return true;
                    }
                    Db.RollbackTran();
                    return false;
                }
                catch (Exception e)
                {
                    Db.RollbackTran();
                    throw new Exception(e.Message);
                }
            }
            catch (Exception e)
            {
                throw new Exception("自动分配失败:" + e.Message);
            }
        }
        //取消分配
        public bool CancelAllot(string soNo, int userId)
        {
            try
            {
                var notice = Db.Queryable<BllExportNotice>().Where(m => m.IsDel == "0" && m.SONo == soNo).ToList().FirstOrDefault();
                if (notice == null)
                {
                    throw new Exception("未查询到出库单据信息");
                }
                if (notice.Status != "1" && notice.Status != "2")
                {
                    throw new Exception("参数异常,请检查状态是否为已分配或部分分配或来源是否是WMS");
                }
                //该单据的分配信息 Status 0:任务下发 1:待拣货 2:部分拣货 3:待回库 4:已完成
                var allotList = Db.Queryable<BllExportAllot>().Where(o => o.IsDel == "0" && o.SONo == soNo).ToList();
                //有已执行的分配数据不能取消
                if (allotList.Any(o => o.Status != "0"))
                {
                    throw new Exception("当前单据的分配信息已有执行中,不能取消分配");
                }
                List<SoDetailInfo> soDetailList = new List<SoDetailInfo>();
                //开启事务
                Db.BeginTran();
                try
                {
                    //查询分配的明细
                    var detail = Db.Queryable<BllExportNoticeDetail>().Where(d => d.SONo == soNo && d.AllotQty > 0 && d.IsDel == "0").ToList();
                    foreach (var d in detail)
                    {
                        var orders = allotList.Where(o => o.SODetailNo == d.Id).ToList();
                        foreach (var o in orders)
                        {
                            var pq = Db.Queryable<DataStockDetail>().Where(t => t.Id == o.StockId);
                            var pq2 = !string.IsNullOrWhiteSpace(o.LotNo) ? pq.Where(t => t.LotNo == o.LotNo).ToList() : pq.Where(t => string.IsNullOrWhiteSpace(t.LotNo)).ToList();
                            var pallet = pq2.FirstOrDefault();
                            if (pallet != null)
                            {
                                pallet.LockQty -= o.Qty;
                                pallet.Status = pallet.LockQty == 0 ? "0" : "1"; //如果锁定数量是0状态变更为待分配 否则为部分分配
                                Db.Updateable(pallet).ExecuteCommand();
                                //库存总表
                                //var stock = Db.Queryable<DataStock>().First(t => t.SkuNo == pallet.SkuNo && t.IsDel == "0");
                                //stock.LockQty -= o.Qty;
                                //Db.Updateable(stock).ExecuteCommand();
                            }
                        }
                        Db.Deleteable<BllExportAllot>(orders).ExecuteCommand();
                        if (d.AllotQty > d.Qty)
                        {
                            //库存总表
                            var stock = Db.Queryable<DataStock>().First(m => m.IsDel == "0" && m.SkuNo == d.SkuNo && m.LotNo == d.LotNo);
                            stock.LockQty -= (decimal)d.AllotQty - d.Qty;
                            Db.Updateable(stock).ExecuteCommand();
                            //添加回传上游系统锁定数量更改代码
                            SoDetailInfo soDetail = new SoDetailInfo();
                            soDetail.OrderDetailCode = d.OrderDetailCode;
                            soDetail.LockQty = (decimal)(d.Qty-d.AllotQty);
                            soDetail.LotNo = d.LotNo;
                            soDetailList.Add(soDetail);
                        }
                        d.AllotQty = 0;
                        d.Status = "0";
                        d.UpdateUser = userId;
                        d.UpdateTime = DateTime.Now;
                        //if (notice.Type == "1" || notice.Type == "5" || notice.Type == "6" || notice.Type == "7" || notice.Type == "8")//1:领料出库、
                        //{
                        //    if (d.IsIssueLotNo != "1")
                        //    {
                        //        d.LotNo = "";
                        //    }
                        //}
                    }
                    //查询当前单据是否已添加备料任务
                    if (notice.Type == "1")
                    {
                        var task = Db.Queryable<BllExportTimingTask>().First(m => m.IsDel == "0" && m.SoNo == soNo);
                        if (task!=null)
                        {
                            task.IsDel = "1";
                            task.UpdateUser = userId;
                            task.UpdateTime = DateTime.Now;
                            Db.Updateable(task).ExecuteCommand();
                        }
                    }
                    notice.Status = "0";
                    notice.UpdateUser = userId;
                    notice.UpdateTime = DateTime.Now;
                    Db.Updateable(detail).ExecuteCommand();
                    Db.Updateable(notice).ExecuteCommand();
                    //系统对接后放开
                    /*var jsonData = JsonConvert.SerializeObject(soDetailList);
                    var response = HttpHelper.DoPost(url, jsonData, "库存锁定数量变更", "ERP");
                    var obj = JsonConvert.DeserializeObject<ErpModel>(response);//解析返回数据
                    if (obj.Success != 0)
                    {
                        throw new Exception("上传失败" + obj.Message);
                    }*/
                    //添加操作日志记录
                    var k = new OperationSOServer().AddLogOperationSo("出库作业", "出库单据", notice.SONo, "取消分配", $"取消分配了单据号为{notice.SONo}的单据信息", userId);
                    Db.CommitTran();
                }
                catch (Exception e)
                {
                    Db.RollbackTran();
                    throw new Exception(e.Message);
                }
                return true;
            }
            catch (Exception e)
            {
                throw new Exception("取消分配失败" + e.Message);
            }
        }
        /// <summary>
        /// 维护出库单备注信息
        /// </summary>
        /// <param name="id"></param>
        /// <param name="demo"></param>
        /// <param name="userId"></param>
        public void EditNoticeDemo(int id, string demo, int userId)
        {
            try
            {
                var notice = Db.Queryable<BllExportNotice>().First(m => m.IsDel == "0" && m.Id == id);
                if (notice == null)
                {
                    throw new Exception("未查询到出库单据信息");
                }
                notice.Demo = demo + "".Trim();
                notice.UpdateUser = userId;
                notice.UpdateTime = DateTime.Now;
                int i = Db.Updateable(notice).ExecuteCommand();
                if (i > 0)
                {
                    //添加操作日志
                    new OperationSOServer().AddLogOperationSo("出库作业", "出库单据", notice.SONo, "编辑", $"编辑了单据号为{notice.SONo}的备注信息", userId);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        //获取库存明细信息(出库单手动分配选择数据源)
        public List<StockDetailDto> GetHandOutList(int detailId, string houseNo, string roadwayNo, string locateNo, string msg, string palletNo)
        {
            try
            {
                var detail = Db.Queryable<BllExportNoticeDetail>().First(d => d.Id == detailId);
                #region 判断条件
                if (detail == null)
                {
                    throw new Exception("获取失败,未找到指定出库单!");
                }
                if (detail.Status != "0" && detail.Status != "1" && detail.AllotQty >= detail.Qty)
                {
                    throw new Exception("获取失败,出库单状态不是等待执行或分配中!");
                }
                if (detail.AllotQty >= detail.Qty)
                {
                    throw new Exception("获取失败,出库单已分配完成!");
                }
                var notice = Db.Queryable<BllExportNotice>().First(a => a.SONo == detail.SONo);
                if (notice == null)
                {
                    throw new Exception("获取失败,未找到指定出库单!");
                }
                if (notice.Status == "3" && detail.AllotQty >= detail.Qty || notice.Status == "4" || notice.Status == "5")
                {
                    throw new Exception("获取失败,出库单状态不允许!");
                }
                #endregion
                Expression<Func<DataStockDetail, bool>> item = Expressionable.Create<DataStockDetail>()
                    .AndIF(!string.IsNullOrWhiteSpace(houseNo), m => m.WareHouseNo == houseNo)
                    .AndIF(!string.IsNullOrWhiteSpace(roadwayNo), m => m.RoadwayNo == roadwayNo)
                    .AndIF(!string.IsNullOrWhiteSpace(locateNo), m => m.LocatNo == locateNo)
                    .AndIF(!string.IsNullOrWhiteSpace(palletNo), m => m.PalletNo == palletNo)
                    .AndIF(!string.IsNullOrWhiteSpace(msg),
                        m => m.SkuNo.Contains(msg.Trim())
                             || m.SkuName.Contains(msg.Trim())
                             || m.LocatNo.Contains(msg.Trim()))
                    .And(m => m.IsDel == "0" && m.SkuNo == detail.SkuNo && m.LotNo == detail.LotNo && (m.Status == "0" || m.Status == "1"))
                    .ToExpression();//注意 这一句 不能少
                var list = Db.Queryable<DataStockDetail>().Where(item).Select(a => new StockDetailDto
                {
                    Id = a.Id,
                    SkuNo = a.SkuNo,
                    SkuName = a.SkuName,
                    Standard = a.Standard,
                    LotNo = a.LotNo,
                    LotText = a.LotText,
                    SupplierLot = a.SupplierLot,
                    Qty = a.Qty - a.LockQty - a.FrozenQty,
                    LocatNo = a.LocatNo,
                    RoadwayNo = a.RoadwayNo,
                    PalletNo = a.PalletNo,
                    Demo = a.Demo,
                }).ToList();
                return list;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }
        //手动分配出库单明细
        public void AddHandOutAllot(AddHandOutVm model, int userId)
        {
            try
            {
                #region 判断条件
                //数据验证
                var detail = Db.Queryable<BllExportNoticeDetail>().First(a => a.IsDel == "0" && a.Id == model.Id);
                if (detail == null)
                {
                    throw new Exception("操作失败,未找到指定出库单详情!");
                }
                if (detail.AllotQty >= detail.Qty || (detail.Status != "0" && detail.Status != "1"))
                {
                    throw new Exception("操作失败,出库单已分配完成!");
                }
                var notice = Db.Queryable<BllExportNotice>().First(a => a.IsDel == "0" && a.SONo == detail.SONo);
                if (notice == null)
                {
                    throw new Exception("操作失败,未找到指定出库单!");
                }
                if (notice.Status == "3" && detail.AllotQty >= detail.Qty || notice.Status == "4" || notice.Status == "5")
                {
                    throw new Exception("操作失败,出库单已分配完成!");
                }
                #endregion
                //单据明细需要的出库数量
                var needQty = detail.Qty - detail.AllotQty;
                //分配的出库数量
                var outQty = model.StockList.Select(s => s.Qty).ToList().Sum();
                if (outQty != needQty)
                {
                    throw new Exception("操作失败,出库数量与计划数量不一致!");
                }
                var stockIds = model.StockList.Select(a => a.StockId).ToList();
                //库存明细
                var stockList = Db.Queryable<DataStockDetail>().Where(a => stockIds.Contains(a.Id)).ToList();
                //分配信息
                var allots = Db.Queryable<BllExportAllot>().Where(m => m.IsDel == "0" && m.SODetailNo == detail.Id && m.Status == "0").ToList();
                //库存总表
                //var stockz = Db.Queryable<DataStock>().First(d => d.IsDel == "0" && d.SkuNo == detail.SkuNo && d.LotNo == detail.LotNo);
                var allotList = new List<BllExportAllot>();
                decimal outQtys = 0;
                foreach (var st in model.StockList)
                {
                    var stock = stockList.First(a => a.Id == st.StockId);
                    if (stock == null)
                    {
                        throw new Exception("操作失败,部分储位库存异常!");
                    }
                    if (st.Qty > (stock.Qty - stock.LockQty - stock.FrozenQty))     // 输入的数量 -  托盘上可用的数量(托盘上数量-锁定的数量-冻结的数量)
                    {
                        throw new Exception("操作失败,出库数量超出库存数量!");
                    }
                    var bl = allots.FirstOrDefault(m => m.StockId == st.StockId);
                    if (bl == null)
                    {
                        //添加分配表信息
                        var allot = new BllExportAllot
                        {
                            SONo = notice.SONo,
                            WaveNo = "",
                            SODetailNo = detail.Id,
                            StockId = st.StockId,
                            LotNo = stock.LotNo,
                            LotText = stock.LotText,
                            SupplierLot = stock.SupplierLot,
                            SkuNo = stock.SkuNo,
                            SkuName = stock.SkuName,
                            Standard = stock.Standard,
                            PalletNo = stock.PalletNo,
                            IsBale = stock.IsBale,
                            IsBelt = stock.IsBelt,
                            Qty = st.Qty,
                            CompleteQty = 0,
                            Status = "0",
                            LogisticsId = notice.LogisticsId,
                            IsAdvance = "0",
                            OutMode = "",//出库口
                            CreateUser = userId,
                            CreateTime = DateTime.Now
                        };
                        allotList.Add(allot);
                    }
                    else
                    {
                        bl.Qty += st.Qty;
                        Db.Updateable(bl).ExecuteCommand();
                    }
                    //库存明细
                    stock.LockQty += st.Qty;
                    stock.Status = stock.LockQty == stock.Qty ? "2" : "1";
                    //库存总表
                    //stockz.LockQty += st.Qty;
                    //Db.Updateable(stockz).ExecuteCommand();
                    Db.Updateable(stock).UpdateColumns(it => new { it.LockQty, it.Status }).ExecuteCommand();
                    outQtys += st.Qty;
                }
                Db.Insertable(allotList).ExecuteCommand();
                //修改单据明细
                detail.AllotQty += outQtys;
                detail.UpdateUser = userId;
                detail.UpdateTime = DateTime.Now;
                if (detail.Status == "0")
                {
                    detail.Status = "1";
                }
                Db.Updateable(detail).ExecuteCommand();
                var detailList = Db.Queryable<BllExportNoticeDetail>()
                    .Where(m => m.IsDel == "0" && m.SONo == notice.SONo).ToList();
                //修改出库单状态
                if (notice.Status == "0" || notice.Status == "1")
                {
                    decimal totalQty = 0;
                    decimal totalAllotQty = 0;
                    foreach (var item in detailList)
                    {
                        totalQty += item.Qty;
                        totalAllotQty += Convert.ToInt32(item.AllotQty);
                    }
                    if (totalAllotQty >= totalQty)
                    {
                        notice.Status = "2";//证明分配数量大于等于出库数量  修改为已分配
                    }
                    else if (totalAllotQty < totalQty && totalAllotQty > 0)
                    {
                        notice.Status = "1";//证明分配数量小于等于出库数量  修改为部分分配
                    }
                    Db.Updateable(notice).ExecuteCommand();
                }
                //添加操作日志记录
                var k = new OperationSOServer().AddLogOperationSo("出库作业", "出库单据", notice.SONo, "分配", $"手动分配了单据号为{notice.SONo}、物料:{detail.SkuNo}、批次:{detail.LotNo}的单据信息", userId);
                Db.CommitTran();
            }
            catch (Exception e)
            {
                Db.RollbackTran();
                throw new Exception(e.Message);
            }
        }
        #endregion
        /// <summary>
        /// 判断出库是否需要拆箱