杨前锦
2025-06-04 d44e3abf0d51cfea1ed7df510974d69458cf516d
HH.WCS.Mobox3/HH.WCS.Mobox3.YNJT_PT/wms/WMSHelper.cs
@@ -1,9 +1,12 @@
using HH.WCS.Mobox3.YNJT_PT.api;
using HH.WCS.Mobox3.YNJT_BZP_PT.models;
using HH.WCS.Mobox3.YNJT_PT.api;
using HH.WCS.Mobox3.YNJT_PT.dispatch;
using HH.WCS.Mobox3.YNJT_PT.models;
using HH.WCS.Mobox3.YNJT_PT.models.other;
using HH.WCS.Mobox3.YNJT_PT.util;
using Newtonsoft.Json;
using NLog.Fluent;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -23,10 +26,10 @@
            var date = DateTime.Now.ToString("yyMMdd");
            return $"OP{date}{id.ToString().PadLeft(4, '0')}";
        }
        internal static string GenerateSortingNo() {
            var id = SYSHelper.GetSerialNumber("分拣单", "SO");
        internal static string GenerateTaskGroupNo() {
            var id = SYSHelper.GetSerialNumber("任务组", "GP");
            var date = DateTime.Now.ToString("yyMMdd");
            return $"SO{date}{id.ToString().PadLeft(4, '0')}";
            return $"GP{date}{id.ToString().PadLeft(4, '0')}";
        }
        internal static List<WMSTask> GetOperationListByState(string state) {
            var db = new SqlHelper<object>().GetInstance();
@@ -77,130 +80,9 @@
            return result;
        }
        internal static bool CreateSortingOrder(List<string> list) {
            var res = false;
            //遍历获取发货单,然后判断库存,如果全都没库存,则不生成分拣单,如果有生成分拣单
            //更新波次单,即使只有一个发货单也更新波次单
            var db = new SqlHelper<object>().GetInstance();
            var sortingOrderNo = "";
            SortingOrder sortingOrder = null;
            try {
                db.BeginTran();
                list.ForEach(a => {
                    var so = db.Queryable<ShippingOrder>().Includes(s => s.Details).Where(s => s.S_NO == a).First();
                    //判断库存,只有已经入库的才可以计算,码盘的不算,入库后出库途中的也要计算,码盘后默认叫待入库,入库后叫正常
                    //生成出库任务,只有托盘位置在立库才需要创建出库任务(先写死立库)
                    //只有全部分拣完成才允许回库(一个托盘可能对应多个分拣明细)
                    //查找仓库量表和库区量表,出库单需要定位仓库和库区了,出库定位物理库区就行,入库可能物料库区还要区分逻辑库区
                    //暂时只查仓库量表(量表如何重置,查找所有查找C_ENABLE=N的,判断如果在仓库中,改成Y,然后统计)
                    //生成分拣单时候增加仓库量表分配量,生成分拣单明细时候,增加库区量表分配量
                    if (so != null && so.Details.Count > 0) {
                        var fail = true;
                        for (int i = 0; i < so.Details.Count; i++) {
                            var whi = db.Queryable<WHInventory>().Where(w => w.S_ITEM_CODE == so.Details[i].S_ITEM_CODE).First();
                            if (whi != null && whi.F_QTY - whi.F_ALLOC_QTY > 0) {
                                //有货就出
                                float qty = whi.F_QTY - whi.F_ALLOC_QTY > so.Details[i].F_QTY ? so.Details[i].F_QTY : whi.F_QTY - whi.F_ALLOC_QTY;
                                fail = false;
                                //有可用库存,生成分拣单
                                if (sortingOrderNo == "") {
                                    sortingOrderNo = GenerateSortingNo();
                                    sortingOrder = new SortingOrder
                                    {
                                        S_NO = sortingOrderNo,
                                        S_SHIPPING_NO = so.S_NO,
                                        Composes = new List<SortingCompose>()
                                    };
                                    //创建分拣单
                                    db.Insertable(sortingOrder).ExecuteCommand();
                                }
                                else {
                                    //获取最新分拣单
                                    sortingOrder = db.Queryable<SortingOrder>().Includes(s => s.Composes).Where(s => s.S_NO == sortingOrderNo).First();
                                    //更新分拣单中的发货单单号
                                    sortingOrder.S_SHIPPING_NO = sortingOrder.S_SHIPPING_NO + ";" + so.S_NO;
                                    sortingOrder.T_MODIFY = DateTime.Now;
                                    db.Updateable(sortingOrder).UpdateColumns(it => new { it.S_SHIPPING_NO, it.T_MODIFY }).ExecuteCommand();
                                }
                                //查询分拣单子表(正常情况还需要增加批次判断)
                                var soc = db.Queryable<SortingCompose>().Where(s => s.S_ITEM_CODE == so.Details[i].S_ITEM_CODE && s.S_SORTING_NO == sortingOrder.S_NO).First();
                                if (soc == null) {
                                    soc = new SortingCompose
                                    {
                                        S_ITEM_CODE = so.Details[i].S_ITEM_CODE,
                                        S_SORTING_NO = sortingOrder.S_NO,
                                        F_QTY = qty,
                                        N_ROW_NO = sortingOrder.Composes.Count() + 1,
                                        S_BATCH_NO = so.Details[i].S_BATCH_NO
                                    };
                                    //创建分拣单子表
                                    db.Insertable(soc).ExecuteCommand();
                                }
                                else {
                                    soc.F_QTY += qty;
                                    soc.T_MODIFY = DateTime.Now;
                                    //更新分拣单子表
                                    db.Updateable(soc).UpdateColumns(it => new { it.F_QTY, it.T_MODIFY }).ExecuteCommand();
                                }
                                //更新仓库量表分配量
                                whi.F_ALLOC_QTY += qty;
                                whi.T_MODIFY = DateTime.Now;
                                db.Updateable(whi).UpdateColumns(it => new { it.F_ALLOC_QTY, it.T_MODIFY }).ExecuteCommand();
                                //跟新发货单子表配货数量
                                so.Details[i].F_ACC_D_QTY += qty;
                                so.Details[i].T_MODIFY = DateTime.Now;
                                db.Updateable(so.Details[i]).UpdateColumns(it => new { it.F_ACC_D_QTY, it.T_MODIFY }).ExecuteCommand();
                            }
                        }
                        //更新发货单状态
                        so.N_B_STATE = fail ? 5 : 1;
                        if (fail) {
                            so.S_NOTE = "没有库存";
                        }
                        so.S_WAVE_CODE = sortingOrderNo;
                        so.T_MODIFY = DateTime.Now;
                        db.Updateable(so).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY, it.S_WAVE_CODE, it.S_NOTE }).ExecuteCommand();
                    }
                    //查找库区内托盘生成托盘明细(包括分拣中)怎么判断库内?查找货位绑定?还是托盘上加标识比较好,如果是整托出的,分拣确认后去掉分拣标识。
                    //1、只有码盘入库后的才需要加标识,分拣回的不用,分拣回的标识还在,没有变。写死码盘入的完成后加标识,整托分拣的去掉标识,防止后面重新码盘脏数据
                    //2、或者关联查询库内的,只给分拣出的加标识,每次分拣回再把标识清除了(如果不清除带标识的会很多,还不如全部加标识),整托的清除。
                    //综合选择还是方案1,这样创建分拣明细,只需要托盘物料两表查询就能定位托盘(如果信息不准,可以重置),方案2需要货位、托盘、物料三表联查
                    //暂时不计库区,标识用容器表 C_ENABLE 来判断,表示能不能出库
                    //执行分拣创建任务,遍历分拣明细中的托盘,如果在仓库就出库,如果不在就不用出库
                });
                //全部分拣单生成之后将分拣单状态设置为开始配货,波次号为发货单
                sortingOrder = db.Queryable<SortingOrder>().Where(s => s.S_NO == sortingOrderNo).First();
                sortingOrder.N_B_STATE = 1;
                sortingOrder.T_MODIFY = DateTime.Now;
                db.Updateable(sortingOrder).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
                db.CommitTran();
                res = true;
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
                db.RollbackTran();
            }
            return res;
        }
        internal static List<SortingDetail> GetSortingDetailByCntr(string cntr) {
            var db = new SqlHelper<object>().GetInstance();
            var result = db.Queryable<SortingDetail>().Where(a => a.S_CNTR_CODE == cntr && a.N_B_STATE != 2).ToList();
            return result;
        }
        internal static bool CreateWmsTask(WMSTask wmsTask) {
            try {
@@ -250,74 +132,7 @@
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<WMSTask>().Where(a => a.S_CODE == code).First();
        }
        /*
        internal static void CreateSortingOrderDetail(string so_no) {
            //分拣单配货
            var db = new SqlHelper<object>().GetInstance();
            var so = db.Queryable<SortingOrder>().Includes(a => a.Composes).Where(a => a.S_NO == so_no && (a.N_B_STATE == 1 || a.N_B_STATE == 20)).First();//
            if (so != null && so.Composes.Count > 0) {
                db.BeginTran();
                try {
                    int rowNo = 1;
                    so.Composes.ForEach(a => {
                        //按分拣单子表去配货,查找可用托盘(先查所有符合的,后面再优化)
                        var cirList = db.Queryable<CntrItemRel>().Includes(c => c.Cntr).Where(c => c.Cntr.C_ENABLE == "Y" && c.S_ITEM_CODE == a.S_ITEM_CODE && (c.F_QTY - c.F_ALLOC_QTY) > 0).OrderBy(c => c.T_CREATE).ToList();
                        for (int i = 0; i < cirList.Count; i++) {
                            var cir = cirList[i];
                            var sd = new SortingDetail
                            {
                                N_ROW_NO = rowNo,
                                S_BATCH_NO = a.S_BATCH_NO,
                                S_ITEM_CODE = a.S_ITEM_CODE,
                                S_CNTR_CODE = cir.S_CNTR_CODE,
                                S_SORTING_NO = a.S_SORTING_NO
                            };
                            bool needBreak = false;
                            if (cir.F_QTY - cir.F_ALLOC_QTY >= (a.F_QTY - a.F_ACC_S_QTY)) {
                                //容器可用数量大于分拣单子表要求的数量,生成分拣明细,然后跳出循环
                                sd.F_QTY = a.F_QTY - a.F_ACC_S_QTY;
                                needBreak = true;
                            }
                            else {
                                //生成分拣明细,继续创建
                                sd.F_QTY = cir.F_QTY - cir.F_ALLOC_QTY;
                            }
                            db.Insertable(sd).ExecuteCommand();
                            rowNo++;
                            //更新容器货品表分配量
                            cir.F_ALLOC_QTY += sd.F_QTY;
                            cir.T_MODIFY = DateTime.Now;
                            db.Updateable(cir).UpdateColumns(it => new { it.F_ALLOC_QTY, it.T_MODIFY }).ExecuteCommand();
                            //更新分拣单子表的量
                            a.F_ACC_S_QTY += sd.F_QTY;
                            a.T_MODIFY = DateTime.Now;
                            db.Updateable(cir).UpdateColumns(it => new { it.F_ALLOC_QTY, it.T_MODIFY }).ExecuteCommand();
                            if (needBreak) {
                                break;
                            }
                        }
                    });
                    //全部分拣单子表生成分拣明细,修改分拣单状态为配货完成(反正仓库的货能配的都配完了,除非数据有异常)
                    so.N_B_STATE = 2;
                    so.T_MODIFY = DateTime.Now;
                    db.Updateable(so).UpdateColumns(it => new { it.N_B_STATE, it.T_MODIFY }).ExecuteCommand();
                    db.CommitTran();
                }
                catch (Exception ex) {
                    Console.WriteLine(ex.Message);
                    db.RollbackTran();
                }
            }
        }
        */
        /// <summary>
        /// 获取开始配货的分拣单,一次性生成分拣明细,避免生成一半再生成,所以创建分拣明细的时候加上事务
        /// </summary>
        /// <returns></returns>
        internal static List<SortingOrder> GetWaitingSortingOrderList() {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<SortingOrder>().Includes(a => a.Composes).Where(a => a.N_B_STATE == 1 || a.N_B_STATE == 20).ToList();
        }
        /// <summary>
        /// 获取配货完成的分拣单,每个分拣单单独创建分拣作业
        /// </summary>
@@ -509,290 +324,447 @@
            return result;
        }
        public static Location GetLocation4InEmptyRow()
        {
            Location result = null;
            try
            {
                var db = new SqlHelper<object>().GetInstance();
                #region 查找所有数量是空的排
                //Console.WriteLine("查找所有数量是空的排");
                //2.0 简化查询只查每一排第一列
                var list = db.Queryable<Location>().OrderBy(a => a.N_COL).Take(1).PartitionBy(a => a.N_ROW).ToList().Where(a => a.N_CURRENT_NUM == 0).ToList();
                //2.1 选一个空排
                for (int i = 0; i < list.Count; i++)
        // ----------------------------------------------- 印尼佳通 - 硫化--------------------------------------------
        /// <summary>
        /// 添加成型机下线记录
        /// </summary>
        /// <param name="record"></param>
        /// <returns></returns>
        public static bool addOffLineRecord(OffLineRecord record)
        {
            bool result = false;
            var db = new SqlHelper<object>().GetInstance();
            var offLineRecord = db.Queryable<OffLineRecord>().Where(a => a.S_RFID == record.S_RFID).First();
            if (offLineRecord != null)
            {
                offLineRecord.S_DEVICE_NO = record.S_DEVICE_NO;
                offLineRecord.N_IS_URGENT = record.N_IS_URGENT;
                offLineRecord.T_OFF_TIME = record.T_OFF_TIME;
                offLineRecord.N_IS_FULL = record.N_IS_FULL;
                result = db.Updateable(offLineRecord).ExecuteCommand() > 0;
            }
            else
            {
                result = db.Insertable(record).ExecuteCommand() > 0;
            }
            return result;
        }
        /// <summary>
        /// 查询成型机下线记录
        /// </summary>
        /// <param name="rfid"></param>
        /// <returns></returns>
        public static OffLineRecord getOffLineRecord(string rfid)
        {
            OffLineRecord offLineRecord = null;
            var db = new SqlHelper<object>().GetInstance();
            if (rfid != null)
            {
                offLineRecord = db.Queryable<OffLineRecord>().Where(a => a.S_RFID == rfid).First();
            }
            return offLineRecord;
        }
        /// <summary>
        /// 查询物料条码信息
        /// </summary>
        /// <param name="barcode"></param>
        /// <returns></returns>
        public static GreenTireInformation GetGreenTireInformation(string barcode)
        {
            GreenTireInformation greenTireInformation = null;
            var db = new SqlHelper<object>().GetInstance();
            if (barcode != null)
            {
                greenTireInformation = db.Queryable<GreenTireInformation>().Where(a => a.BARCODE == barcode).First();
            }
            return greenTireInformation;
        }
        /// <summary>
        /// 查询入库终点货位
        /// </summary>
        /// <param name="locNum">1.单货位 2.双货位</param>
        /// <returns></returns>
        public static EndLocGroup getInStockEndLoc(int locNum)
        {
            var db = new SqlHelper<object>().GetInstance();
            EndLocGroup endLocGroup = new EndLocGroup();
            List<Location> locations = new List<Location>();
            // 1.按容积率从大到小,对巷道进行排序
            var roadwayOrderList = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y")
                .GroupBy(a => a.N_ROADWAY)
                .Select(a => new { roadway = a.N_ROADWAY ,  num = SqlFunc.AggregateCount(a.S_CODE)})
                .OrderByDescending(a => a.num)
                .ToList();
            // 查询单货位
            foreach (var order in roadwayOrderList)
            {
                if (locNum == 1)
                {
                    var other = db.Queryable<Location>().Where(a => a.S_AREA_CODE == list[i].S_AREA_CODE && a.N_ROW == list[i].N_ROW && a.S_LOCK_STATE != "无").First();
                    if (list[i].N_LOCK_STATE == 0 && other == null)
                    /*// 查询所有相同物料货位
                    var sameItemLocList = db.Queryable<Location>()
                       .LeftJoin<LocCntrRel>((a, b) => a.S_CODE == b.S_LOC_CODE)
                       .LeftJoin<CntrItemRel>((a, b, c) => b.S_CNTR_CODE == c.S_CNTR_CODE)
                       .Where((a, b, c) => a.S_AREA_CODE == Settings.stockArea
                                       && a.N_ROADWAY == order.roadway
                                       && a.N_CURRENT_NUM == 1
                                       && a.N_LOCK_STATE == 0
                                       && a.C_ENABLE == "Y"
                                       && b.S_CNTR_CODE != null
                                       && c.S_ITEM_CODE == itemCode
                                       )
                       .OrderBy((a, b, c) => new { a.N_LAYER, a.N_COL })
                       .ToList();
                    // 查询相同物料的左右是否有空货位
                    foreach (var loc in sameItemLocList)
                    {
                        //二次校验当前排所有货位都是空的,防止系统数据错乱
                        var rowSumInfo = db.Queryable<Location>().Where(l => l.S_AREA_CODE == list[i].S_AREA_CODE && l.N_ROW == list[i].N_ROW && l.N_CURRENT_NUM != 0).Count();
                        if (rowSumInfo == 0)
                        var leftLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL - 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                        if (leftLoc != null)
                        {
                            result = list[i];
                            locations.Add(leftLoc);
                            break;
                        }
                    }
                }
                #endregion
                if (result != null && (!string.IsNullOrEmpty(result.C_ENABLE) && (result.C_ENABLE == "禁用" || result.C_ENABLE == "N")))
                {
                    //禁用了选择后面一个货位
                    Console.WriteLine("禁用了选择后面一个货位");
                    result = db.Queryable<Location>().OrderBy(a => a.N_COL).Where(a => a.S_AREA_CODE == result.S_AREA_CODE && a.N_ROW == result.N_ROW && (string.IsNullOrEmpty(a.C_ENABLE) || (a.C_ENABLE != "禁用" && a.C_ENABLE != "N")) && a.N_COL > result.N_COL).First();
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error("GetLocation4InEmptyRow:" + ex.Message, ex);
            }
            return result;
        }
        internal static Location GetInstockEnd(string item)
        {
            var db = new SqlHelper<object>().GetInstance();
            Location end = null;
            // 查询相同物料的排是否有可放货的库位
            var listMaxCol = db.Queryable<Location>()
                 .LeftJoin<LocCntrRel>((l, lc) => l.S_CODE == lc.S_LOC_CODE)
                 .LeftJoin<CntrItemRel>((l, lc, ci) => lc.S_CNTR_CODE == ci.S_CNTR_CODE)
                 .Where((l, lc, ci) => l.N_CURRENT_NUM > 0 && ci.S_ITEM_CODE == item && l.S_AREA_CODE != Settings.BufferArea)
                 .OrderByDescending((l, lc, ci) => l.N_COL).Take(1).PartitionBy((l, lc, ci) => l.N_ROW).ToList();
            foreach (var loc in listMaxCol)
            {
                //排有锁也排除
                var other = db.Queryable<Location>().Where(a => a.S_AREA_CODE == loc.S_AREA_CODE && a.N_ROW == loc.N_ROW && a.S_LOCK_STATE != "无" && a.N_LOCK_STATE != 0).First();
                if (other != null)
                {
                   continue;
                }
                end = db.Queryable<Location>().OrderBy(a => a.N_COL).Where(a => a.S_AREA_CODE == loc.S_AREA_CODE && a.N_ROW == loc.N_ROW && a.N_CURRENT_NUM < a.N_CAPACITY && (string.IsNullOrEmpty(a.C_ENABLE) || (a.C_ENABLE != "禁用" && a.C_ENABLE != "N")) && a.N_COL >= loc.N_COL).First();
                if (end != null) {
                    return end;
                }
            }
            // 如果没有,则随机放空的排
            if (end == null) {
                end = GetLocation4InEmptyRow();
            }
            return end;
        }
        public static Location GetShiftStockStart(string area ,int row)
        {
            Location result = null;
            try
            {
                // 1、查询当前库区-排-物料 有托盘的货位
                var db = new SqlHelper<object>().GetInstance();
                result = db.Queryable<Location>()
                    .Where(l => l.N_CURRENT_NUM > 0 && l.S_AREA_CODE == area && l.N_ROW == row && l.N_LOCK_STATE == 0 && (l.C_ENABLE != "禁用" && l.C_ENABLE != "N"))
                    .Includes(l => l.LocCntrRel, l => l.CntrItemRel)
                    .OrderByDescending(l => l.N_COL)
                    .First();
            }
            catch (Exception ex)
            {
                LogHelper.Error("GetShiftStockStart:" + ex.Message, ex);
            }
            return result;
        }
        public static Location GetShiftStockStart(string area)
        {
            Location result = null;
            try
            {
                // 1、查询当前库区-排-物料 有托盘的货位
                var db = new SqlHelper<object>().GetInstance();
                var listMaxCol = db.Queryable<Location>()
                    .Where(l => l.N_CURRENT_NUM > 0 && l.S_AREA_CODE == area)
                    .Includes(l => l.LocCntrRel, l => l.CntrItemRel)
                    .OrderByDescending(l => l.N_COL)
                    .Take(1)
                    .PartitionBy(l => l.N_ROW)
                    .ToList();
                foreach (var loc in listMaxCol)
                {
                    //排有锁也排除
                    var other = db.Queryable<Location>().Where(a => a.S_AREA_CODE == loc.S_AREA_CODE && a.N_ROW == loc.N_ROW && a.S_LOCK_STATE != "无" && a.N_LOCK_STATE != 0).First();
                    if (other != null)
                    {
                        continue;
                    }
                    return loc;
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error("GetShiftStockStart:" + ex.Message, ex);
            }
            return result;
        }
        public static bool isRowLock(Location location) {
            var db = new SqlHelper<object>().GetInstance();
            return  db.Queryable<Location>().Where(a => a.S_AREA_CODE == location.S_AREA_CODE && a.N_ROW == location.N_ROW && a.S_LOCK_STATE != "无" && a.N_LOCK_STATE != 0).Count() > 0;
        }
        public static Location GetShiftStockEnd(string area, int row)
        {
            Location result = null;
            try
            {
                //1.0 获取每一排最大的列
                //1.1 判断当前数量是不是满的,如果是满的并且是最大列,需要排除
                var listMaxCol = new SqlHelper<Location>().GetInstance().Queryable<Location>().Where(a => a.S_AREA_CODE == area && a.N_ROW == row).OrderByDescending(a => a.N_COL).Take(1).PartitionBy(a => a.N_ROW).ToList();
                //1.1 查到所有有托盘的排
                Console.WriteLine("查到所有有托盘的排 ");
                var db = new SqlHelper<object>().GetInstance();
                var list = db.Queryable<Location>().Where(a => a.N_CURRENT_NUM > 0 && a.S_AREA_CODE == area && a.N_ROW == row).Includes(a => a.LocCntrRel, a => a.CntrItemRel).OrderByDescending(a => a.N_COL).Take(1).PartitionBy(a => a.N_ROW).ToList();
                if (list.Count > 0)
                {
                    //1.2 查找其它尺寸有托盘或者锁定的排
                    for (int i = list.Count - 1; i >= 0; i--)
                    {
                        //排除已经锁定的货位 和 放满了且是最大列的货位
                        if (list[i].N_LOCK_STATE != 0 || (list[i].N_CURRENT_NUM == list[i].N_CAPACITY && listMaxCol.Count(a => a.S_CODE == list[i].S_CODE) > 0))
                        var rightLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL + 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                        if (rightLoc != null)
                        {
                            Console.WriteLine($"排除已经锁定的货位 和 放满了且是最大列的货位 排{list[i].N_ROW}");
                            list.Remove(list[i]);
                            locations.Add(leftLoc);
                            break;
                        }
                        else
                        {
                            //排有锁也排除
                            var other = db.Queryable<Location>().Where(a => a.S_AREA_CODE == area && a.N_ROW == list[i].N_ROW && a.S_LOCK_STATE != "无" && a.N_LOCK_STATE != 0).First();
                            if (other != null)
                            {
                                Console.WriteLine($"排除有锁的排{list[i].N_ROW}");
                                list.Remove(list[i]);
                            }
                        }
                    }
                    Console.WriteLine($"有托盘排数为{list.Count}");
                    if (list.Count > 0)
                    {
                    }*/
                        //1.3 遍历判断物料类型是否相同
                        Console.WriteLine("遍历判断物料类型是否相同");
                        for (int i = 0; i < list.Count; i++)
                    // 查询空货位
                    var emptyLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").OrderBy(a => new { a.N_LAYER, a.N_COL }).First();
                    if (emptyLoc != null)
                    {
                        locations.Add(emptyLoc);
                        endLocGroup.endLocList = locations;
                        return endLocGroup;
                    }
                }
                if (locNum == 2)
                {
                    var emptyLocList = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").OrderBy(a => new { a.N_LAYER, a.N_COL }).ToList();
                    if (emptyLocList.Count > 0)
                    {
                        // 查询双拖空货位
                        if (locations.Count == 0)
                        {
                            //todo 还需要判断锁
                            if (list[i].LocCntrRel != null && list[i].LocCntrRel.CntrItemRel != null)
                            foreach (var loc in emptyLocList)
                            {
                                if (list[i].N_CURRENT_NUM < list[i].N_CAPACITY)
                                var leftLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL - 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                                if (leftLoc != null)
                                {
                                    result = list[i];
                                }
                                else
                                {
                                    Console.WriteLine("选择后面空位");
                                    result = db.Queryable<Location>().Where(a => a.S_AREA_CODE == area && a.N_ROW == list[i].N_ROW && a.N_COL > list[i].N_COL && a.C_ENABLE == "Y").OrderBy(a => a.N_COL).First();
                                    locations.Add(loc);
                                    locations.Add(leftLoc);
                                    break;
                                }
                                if (result != null)
                                var rightLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == order.roadway && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL + 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                                if (rightLoc != null)
                                {
                                    locations.Add(loc);
                                    locations.Add(leftLoc);
                                    break;
                                }
                            }
                            if (locations.Count == locNum)
                            {
                                endLocGroup.groupNo = WMSHelper.GenerateTaskGroupNo();
                                endLocGroup.endLocList = locations;
                                return endLocGroup;
                            }
                        }
                        // 查询单拖货位
                        foreach (var loc in emptyLocList)
                        {
                            locations.Add(loc);
                            if (locations.Count == locNum)
                            {
                                endLocGroup.endLocList = locations;
                                return endLocGroup;
                            }
                        }
                    }
                }
                }
            }
            return endLocGroup;
        }
                if (result == null)
        public class EndLocGroup
        {
            public string groupNo { get; set; }
            public List<Location> endLocList { get; set; }
        }
        /// <summary>
        /// 查询出库开始货位
        /// 计算(1.巷道不报警、2.物料状态OK、3.小于失效时间 大于等于生效时间 4.加急料先出、5.先入先出(生产时间))出库物料
        /// </summary>
        /// <param name="itemCode"></param>
        /// <param name="locNum"></param>
        public static StartLocGroup getOutStockStartLoc(string itemCode, int locNum )
        {
            var db = new SqlHelper<object>().GetInstance();
            StartLocGroup startLocGroup = new StartLocGroup();
            // 1.查询(物料状态OK ,且小于失效时间,大于等于生效时间)出库物料,并按加急料先出,先入先出(生产时间)的原则进行排序
            var query = db.Queryable<Location>()
               .LeftJoin<LocCntrRel>((a, b) => a.S_CODE == b.S_LOC_CODE)
               .LeftJoin<CntrItemRel>((a, b, c) => b.S_CNTR_CODE == c.S_CNTR_CODE)
               .Where((a, b, c) => a.S_AREA_CODE == Settings.stockArea
                               && a.N_CURRENT_NUM == 1
                               && a.N_LOCK_STATE == 0
                               && a.C_ENABLE == "Y"
                               && b.S_CNTR_CODE != null
                               );
            if (itemCode != null)
            {
                query = query.Where((a, b, c) => c.S_ITEM_CODE == itemCode
                               && c.S_ITEM_STATE == "OK"
                               && SqlFunc.ToDate(c.S_EFFECTIVE_TIME) <= SqlFunc.GetDate()  // 生效时间早于当前时间
                               && SqlFunc.ToDate(c.S_EXPIRATION_TIME) >= SqlFunc.GetDate() // 失效时间晚于当前时间
                               && c.S_EFFECTIVE_TIME != null
                               && c.S_EXPIRATION_TIME != null)
                            .OrderByDescending((a, b, c) => c.N_URGENT_FLAG)
                            .OrderBy((a, b, c) => c.S_TXNDATE);
            }
            else
            {
                query = query.Where((a, b, c) => c.S_ITEM_CODE == itemCode);
            }
            var outLocList = query.ToList();
            if (outLocList.Count > 0)
            {
                foreach (var loc in outLocList)
                {
                    #region 查找所有数量是空的排
                    //Console.WriteLine("查找所有数量是空的排");
                    //2.0 简化查询只查每一排第一列
                    list = db.Queryable<Location>().Where(a => a.S_AREA_CODE == area && a.N_ROW == row).OrderBy(a => a.N_COL).Take(1).PartitionBy(a => a.N_ROW).ToList().Where(a => a.N_CURRENT_NUM == 0).ToList();
                    //2.1 选一个空排
                    for (int i = 0; i < list.Count; i++)
                    // 查询巷道是否正常,异常跳过
                    var stackerStates = WCSDispatch.getStackerState(loc.N_ROADWAY);
                    if (stackerStates.Count == 0 || stackerStates[0].roadway_state == "0" || stackerStates[0].roadway_state == "3")
                    {
                        var other = db.Queryable<Location>().Where(a => a.S_AREA_CODE == area && a.N_ROW == list[i].N_ROW && a.S_LOCK_STATE != "无").First();
                        if (list[i].N_LOCK_STATE == 0 && other == null)
                        continue;
                    }
                    var cntrItemRel = db.Queryable<CntrItemRel>().LeftJoin<LocCntrRel>((a, b) => a.S_CNTR_CODE == b.S_CNTR_CODE).Where((a, b) => b.S_LOC_CODE == loc.S_CODE).First();
                    if (cntrItemRel != null)
                    {
                        startLocGroup.startLocList.Add(loc);
                        if (locNum == startLocGroup.startLocList.Count)
                        {
                            //二次校验当前排所有货位都是空的,防止系统数据错乱
                            var rowSumInfo = db.Queryable<Location>().Where(l => l.S_AREA_CODE == area && l.N_ROW == list[i].N_ROW && l.N_CURRENT_NUM != 0).Count();
                            if (rowSumInfo == 0)
                            break;
                        }
                        var leftLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == loc.N_ROADWAY && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL - 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 1 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                        if (leftLoc != null)
                        {
                            var locCntrRel = db.Queryable<LocCntrRel>()
                               .LeftJoin<CntrItemRel>((a, b) => a.S_CNTR_CODE == b.S_CNTR_CODE)
                               .Where((a, b) => a.S_LOC_CODE == leftLoc.S_CODE && b.S_ITEM_CODE == itemCode)
                               .First();
                            if (locCntrRel != null)
                            {
                                result = list[i];
                                startLocGroup.groupNo = WMSHelper.GenerateTaskGroupNo();
                                startLocGroup.startLocList.Add(leftLoc);
                                break;
                            }
                        }
                        var rightLoc = db.Queryable<Location>().Where(a => a.S_AREA_CODE == Settings.stockArea && a.N_ROADWAY == loc.N_ROADWAY && a.N_ROW == loc.N_ROW && a.N_COL == loc.N_COL + 1 && a.N_LAYER == loc.N_LAYER && a.N_CURRENT_NUM == 1 && a.N_LOCK_STATE == 0 && a.C_ENABLE == "Y").First();
                        if (rightLoc != null && startLocGroup.startLocList.Count == 0)
                        {
                            var locCntrRel = db.Queryable<LocCntrRel>()
                                .LeftJoin<CntrItemRel>((a, b) => a.S_CNTR_CODE == b.S_CNTR_CODE)
                                .Where((a, b) => a.S_LOC_CODE == rightLoc.S_CODE && b.S_ITEM_CODE == itemCode)
                                .First();
                            if (locCntrRel != null)
                            {
                                startLocGroup.groupNo = WMSHelper.GenerateTaskGroupNo();
                                startLocGroup.startLocList.Add(rightLoc);
                                break;
                            }
                        }
                    }
                    #endregion
                }
                if (result != null && (!string.IsNullOrEmpty(result.C_ENABLE) && (result.C_ENABLE == "禁用" || result.C_ENABLE == "N")))
                {
                    //禁用了选择后面一个货位
                    //Console.WriteLine("禁用了选择后面一个货位");
                    result = db.Queryable<Location>().OrderBy(a => a.N_COL).Where(a => a.S_AREA_CODE == area && a.N_ROW == result.N_ROW && (string.IsNullOrEmpty(a.C_ENABLE) || (a.C_ENABLE != "禁用" && a.C_ENABLE != "N")) && a.N_COL > result.N_COL).First();
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error("GetLocation4InFinish:" + ex.Message, ex);
            }
            return result;
            return startLocGroup;
        }
        public static List<Location> GetLocationList(string area , int row) {
            List<Location> result = new List<Location> ();
            try {
                var db = new SqlHelper<object>().GetInstance();
                result = db.Queryable<Location>().Where(a => a.S_AREA_CODE == area && a.N_ROW == row).Includes(a => a.LocCntrRel, a => a.CntrItemRel).ToList();
            }
            catch (Exception ex) {
                    LogHelper.Error("GetLocationList:" + ex.Message, ex);
            }
            return result;
        public class StartLocGroup
        {
            public string groupNo {  get; set; }
            public List<Location> startLocList { get; set; }
        }
        public static List<TN_YiKuDetail> getShiftOrderDetail(string orderNo = null) {
        /// <summary>
        /// 查询异常区空货位
        /// </summary>
        /// <param name="areaCode"></param>
        public static Location getAbnormalAreaEmptyLoc(string areaCode)
        {
            Location location = new Location();
            var db = new SqlHelper<object>().GetInstance();
            List<TN_YiKuDetail> details = new List<TN_YiKuDetail>();
            var orderList = db.Queryable<TN_YiKuOrder>().Where(a => a.S_B_STATE == "执行").ToList();
            foreach (var order in orderList)
            {
                var detail = db.Queryable<TN_YiKuDetail>().Where(a => order.S_NO == a.S_NO).First();
                if (detail != null)
                {
                    details.Add(detail);
                }
            }
            return details;
            location = db.Queryable<Location>().Where(a => a.S_AREA_CODE == areaCode && a.N_CURRENT_NUM == 0 && a.C_ENABLE == "Y" && a.N_LOCK_STATE == 0).First();
            return location;
        }
        public static bool updateShiftOrderDetail(TN_YiKuDetail detail) {
        /// <summary>
        /// 查询巷道任务最少的接驳位
        /// </summary>
        /// <returns></returns>
        public static Location getMinTaskMiddleLoc(int type)
        {
            Location location = new Location();
            var db = new SqlHelper<object>().GetInstance();
            var roadwayGroup = db.Queryable<Location>()
                    .LeftJoin<WCSTask>((a, b) => a.S_CODE == b.S_END_LOC && b.N_B_STATE < 3)
                    .Where((a, b) => a.S_AREA_CODE == Settings.stockArea)
                    .GroupBy((a, b) => a.N_ROADWAY)
                    .Select((a, b) => new { roadway = a.N_ROADWAY, num = SqlFunc.AggregateDistinctCount(b.S_CODE != null) })
                    .OrderBy(a => a.num)
                    .ToList();
            foreach (var item in roadwayGroup)
            {
                // 1.查询巷道内的堆垛机状态是否正常
                // 2.查询接驳位
                var connectLoc = Settings.connectLocList.Where(a => a.type == type && a.roadway == item.roadway).FirstOrDefault();
                if (connectLoc != null)
                {
                    location = LocationHelper.GetLoc(connectLoc.locCode);
                    break;
                }
            }
            return location;
        }
        /// <summary>
        /// 查询物料存放时间配置信息
        /// </summary>
        /// <param name="bc_entried"></param>
        /// <returns></returns>
        public static Overage getOverage(string bc_entried)
        {
            var db = new SqlHelper<object>().GetInstance();
            // 直接执行 SQL(参数化查询)
            var sql = "SELECT get_ovg_bar(@barcode, @mcngrp) AS overage_value";
            var sql1 = "SELECT get_minhour_bar(@barcode, @mcngrp) AS overage_value";
            // 使用匿名对象传递参数
            var ovg_bar = db.Ado.SqlQuery<int>(sql, new { barcode = bc_entried, mcngrp = "0" });
            var minhour_bar = db.Ado.SqlQuery<float>(sql1, new { barcode = bc_entried, mcngrp = "0" });
            if (ovg_bar.Count > 0 && minhour_bar.Count > 0)
            {
                Overage overage = new Overage()
                {
                    MINHOUR = minhour_bar[0],
                    OVERAGE = ovg_bar[0]
                };
                return overage;
            }
            return null;
        }
        /// <summary>
        /// 绑定条码物料信息
        /// </summary>
        /// <param name="cntrCode"></param>
        /// <param name="greenTireInformation"></param>
        /// <returns></returns>
        public static bool bindBarcodeItemInfo( string cntrCode,GreenTireInformation greenTireInformation)
        {
            bool result = false;
            var db = new SqlHelper<object>().GetInstance();
            LogHelper.Info("移库完成数量变更参数:" + JsonConvert.SerializeObject(detail), "TSSG");
            result = db.Updateable<TN_YiKuDetail>()
                .SetColumns(a => new TN_YiKuDetail() { N_COMPLETE_NUM = a.N_COMPLETE_NUM + 1 })
                .Where(a => a.S_NO == detail.S_NO
                    && a.S_START_AREA == detail.S_START_AREA
                    && a.S_START_ROW == detail.S_START_ROW
                    && a.S_END_AREA == detail.S_END_AREA
                    && a.S_END_ROW == detail.S_END_ROW
                    )
                .ExecuteCommand() > 0;
            var completeResult = db.Queryable<TN_YiKuDetail>().Where(a => a.S_NO == detail.S_NO && a.N_QTY > a.N_COMPLETE_NUM).Count() == 0;
            if(completeResult) {
                result = db.Updateable<TN_YiKuOrder>().SetColumns(a => new TN_YiKuOrder() { S_B_STATE = "完成" }).Where(a => a.S_NO == detail.S_NO).ExecuteCommand() > 0;
            var cntrItemRel = db.Queryable<CntrItemRel>().Where(a => a.S_CNTR_CODE == cntrCode).First();
            if (cntrItemRel == null)
            {
                var overage = getOverage(greenTireInformation.BARCODE);
                DateTime txndate = DateTime.Parse(greenTireInformation.TXNDATE);
                DateTime minTime = txndate.AddHours(overage.MINHOUR);
                DateTime maxTime = txndate.AddDays(overage.OVERAGE);
                cntrItemRel = new CntrItemRel()
                {
                    S_CG_ID = greenTireInformation.BARCODE,
                    S_ITEM_CODE = greenTireInformation.ITEMCODE,
                    S_CNTR_CODE = cntrCode,
                    S_CELL_NO = greenTireInformation.TIRECODE,
                    F_QTY = greenTireInformation.QTY,
                    S_MCN = greenTireInformation.MCN,
                    S_OPR = greenTireInformation.OPR,
                    S_OPR02 = greenTireInformation.OPR02,
                    S_OPR03 = greenTireInformation.OPR03,
                    S_OPR04 = greenTireInformation.OPR04,
                    S_WINDUP = greenTireInformation.WINDUP,
                    S_TXNDATE = greenTireInformation.TXNDATE,
                    S_DATE_SHIFT = greenTireInformation.DATE_SHIFT,
                    S_EFFECTIVE_TIME = minTime.ToString("yyyy-MM-dd HH:mm:ss"),
                    S_EXPIRATION_TIME = maxTime.ToString("yyyy-MM-dd HH:mm:ss"),
                };
                result = db.Insertable(cntrItemRel).ExecuteCommand() > 0;
            }
            else
            {
                var overage = getOverage(greenTireInformation.BARCODE);
                DateTime txndate = DateTime.Parse(greenTireInformation.TXNDATE);
                DateTime minTime = txndate.AddHours(overage.MINHOUR);
                DateTime maxTime = txndate.AddDays(overage.OVERAGE);
                cntrItemRel.S_CG_ID = greenTireInformation.BARCODE;
                cntrItemRel.S_ITEM_CODE = greenTireInformation.ITEMCODE;
                cntrItemRel.S_CNTR_CODE = cntrCode;
                cntrItemRel.S_CELL_NO = greenTireInformation.TIRECODE;
                cntrItemRel.F_QTY = greenTireInformation.QTY;
                cntrItemRel.S_MCN = greenTireInformation.MCN;
                cntrItemRel.S_OPR = greenTireInformation.OPR;
                cntrItemRel.S_OPR02 = greenTireInformation.OPR02;
                cntrItemRel.S_OPR03 = greenTireInformation.OPR03;
                cntrItemRel.S_OPR04 = greenTireInformation.OPR04;
                cntrItemRel.S_WINDUP = greenTireInformation.WINDUP;
                cntrItemRel.S_TXNDATE = greenTireInformation.TXNDATE;
                cntrItemRel.S_DATE_SHIFT = greenTireInformation.DATE_SHIFT;
                cntrItemRel.S_EFFECTIVE_TIME = minTime.ToString("yyyy-MM-dd HH:mm:ss");
                cntrItemRel.S_EXPIRATION_TIME = maxTime.ToString("yyyy-MM-dd HH:mm:ss");
                result = db.Updateable(cntrItemRel).ExecuteCommand() > 0;
            }
            return result;
        }
        /// <summary>
        /// 查询硫化工单
        /// </summary>
        /// <param name="dateShift"></param>
        /// <param name="mcn"></param>
        /// <param name="shift"></param>
        /// <returns></returns>
        public static ProductionShedule getProductionShedule(string dateShift ,string mcn ,string shift)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<ProductionShedule>().Where(a => a.DATESHIFT == dateShift && a.MCN == mcn && a.SHIFT == shift).First();
        }
        /// <summary>
        /// 查询胚胎完成硫化的数量
        /// </summary>
        /// <returns></returns>
        public static int getEmbryoFinishNum(string dateShift, string mcn, string shift)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<EmbryoFinishRecord>().Where(a => a.DATE_SHIFT == dateShift && a.CUR_MCN == mcn && a.CUR_SHIFT == shift).Count();
        }
    }    
}