服务端的TigerApi 框架,基于.NET6 2024 版本
Tiger.Business.MES/Common/WorkBatch.cs
@@ -53,25 +53,40 @@
        public WorkBatch Init(string lineCode)
        {
            LineCode = lineCode;
            WO = Biz.Db.Queryable<BIZ_MES_WO>().Where(q => q.ORDER_NO == OrderNo).IncludesAllFirstLayer().First();
            Batch = Biz.Db.Queryable<BIZ_MES_WO_BATCH>().Where(q => q.ORDER_NO == OrderNo && q.ACT_LINE == LineCode &&
                                (q.STATUS == BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() || q.STATUS == BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())).First();
            GetBatchInfo();
            return this;
        }
        public void Update()
        {
            if (!WoContext.WoBatchDic.ContainsKey(Batch.BATCH_NO))
            var wo = Biz.Db.Queryable<BIZ_MES_WO>().Where(q => q.ORDER_NO == OrderNo).IncludesAllFirstLayer().First();
            var batch = Biz.Db.Queryable<BIZ_MES_WO_BATCH>().Where(q => q.BATCH_NO == Batch.BATCH_NO).First();
            if (WoContext.WoBatchDic.ContainsKey(batch.BATCH_NO))
            {
                if (wo.STATUS > BIZ_MES_WO.STATUSs.Working.GetValue() || batch.STATUS > BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())
                {
                    WoContext.WoBatchDic.Remove(Batch.BATCH_NO);
                }
                else if(wo.UPDATE_TIME > WO.UPDATE_TIME || batch.UPDATE_TIME > Batch.UPDATE_TIME)
                {
                    GetBatchInfo();
                }
            }
            else
            {
                GetBatchInfo();
                WoContext.WoBatchDic.Add(Batch.BATCH_NO, this);
                WoContext.WoBatchDic.Add(batch.BATCH_NO, this);
            }
            WO = wo;
            Batch = batch;
        }
        private void GetBatchInfo()
        {
            WO = Biz.Db.Queryable<BIZ_MES_WO>().Where(q => q.ORDER_NO == OrderNo).IncludesAllFirstLayer().First();
            Product = Biz.Db.Queryable<BAS_ITEM>().Where(q => q.ITEM_CODE == WO.ITEM_CODE && q.AUTH_ORG == WO.AUTH_ORG).IncludesAllFirstLayer().First();
            Batch = Biz.Db.Queryable<BIZ_MES_WO_BATCH>().Where(q => q.ORDER_NO == OrderNo && q.ACT_LINE == LineCode &&
                                (q.STATUS == BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() || q.STATUS == BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())).First();
            Customer = Biz.Db.Queryable<BAS_CUSTOMER>().Where(q => q.CUST_CODE == WO.CUST_CODE).First();
            WoSNs = Biz.Db.Queryable<BIZ_MES_WO_SN>().Where(q => q.WORK_ORDER == OrderNo).ToList();
            Edges = Biz.Db.Queryable<MES_WO_EDGE>().Where(q => q.WORK_ORDER == OrderNo).ToList();
@@ -90,7 +105,7 @@
        }
        /// <summary>
        /// 添加节点的下一个行为到工步列表
        /// 递归遍历工序节点,给下级工序节点排序和整理前置工序
        /// </summary>
        /// <param name="parent"></param>
        private void NodeSorting(MES_WO_NODE parent)
@@ -122,12 +137,12 @@
        /// </summary>
        /// <param name="curNode"></param>
        /// <returns></returns>
        public ApiAction<SubmitOutput> CheckCanProduce(MES_WO_NODE curNode)
        public ApiAction<SubmitOutput> CheckCanProduce(MES_WO_NODE curNode, MES_WO_OPER curNodeSetting)
        {
            var action = new ApiAction<SubmitOutput>(new SubmitOutput(), true);
            WO = Biz.Db.Queryable<BIZ_MES_WO>().Where(q => q.ORDER_NO == OrderNo).IncludesAllFirstLayer().First();
            Batch = Biz.Db.Queryable<BIZ_MES_WO_BATCH>().Where(q => q.ORDER_NO == OrderNo && q.ACT_LINE == LineCode).First();
            Update();
            //工单批次状态不是已下发或者生产中,则不允许生产
            if (Batch.STATUS != BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() && Batch.STATUS != BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())
            {
@@ -146,7 +161,7 @@
            }
            //工单批次投入数量减去报废数量如果大于等于计划数量,则不允许生产
            //if (curNode.IS_INPUT == "Y" && Batch.INPUT_QTY - Batch.SCRAP_QTY >= Batch.PLAN_QTY)
            if (curNode.IS_INPUT == "Y" && WoSNs.Count(q => q.BATCH_NO == Batch.BATCH_NO) - Batch.SCRAP_QTY >= Batch.PLAN_QTY)
            if (curNodeSetting.IS_INPUT == "Y" && WoSNs.Count(q => q.STATUS > BIZ_MES_WO_SN.STATUSs.NotInput.GetValue() && q.BATCH_NO == Batch.BATCH_NO) - Batch.SCRAP_QTY >= Batch.PLAN_QTY)
            {
                action.IsSuccessed = false;
                action.Data.SetValue(this, null);
@@ -154,7 +169,7 @@
                action.LocaleMsg = new("MES.WorkBatch.BatchInputEnough", Batch.BATCH_NO, WoSNs.Count(q => q.BATCH_NO == Batch.BATCH_NO), Batch.SCRAP_QTY, Batch.PLAN_QTY);
            }
            //工单投入数量减去报废数量如果大于等于计划数量,则不允许生产
            if (curNode.IS_INPUT == "Y" && WoSNs.Count(q => q.WORK_ORDER == WO.ORDER_NO) - WO.SCRAP_QTY >= WO.PLAN_QTY)
            if (curNodeSetting.IS_INPUT == "Y" && WoSNs.Count(q => q.STATUS > BIZ_MES_WO_SN.STATUSs.NotInput.GetValue() && q.WORK_ORDER == WO.ORDER_NO) - WO.SCRAP_QTY >= WO.PLAN_QTY)
            {
                action.IsSuccessed = false;
                action.Data.SetValue(this, null);
@@ -222,6 +237,48 @@
        }
        /// <summary>
        /// 检查工单是否完工,已完工则修改相应状态并记录到数据库
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public async Task<bool> CheckIsComplete(string user)
        {
            var woSNs = Biz.Db.Queryable<BIZ_MES_WO_SN>().Where(q => q.WORK_ORDER == OrderNo).ToList();
            //判断当前工单批次是否已完工
            if (!woSNs.Any(q => q.BATCH_NO == Batch.BATCH_NO && q.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue()) &&
                 woSNs.Count(q => q.BATCH_NO == Batch.BATCH_NO && q.STATUS == BIZ_MES_WO_SN.STATUSs.Finished.GetValue()) == Batch.PLAN_QTY)
            {
                Batch.STATUS = BIZ_MES_WO_BATCH.STATUSs.Closed.GetValue();
                Batch.ACT_END_TIME = DateTime.Now;
            }
            //判断当前工单是否已完工
            if (!woSNs.Any(q => q.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue()) &&
                 woSNs.Count(q => q.STATUS == BIZ_MES_WO_SN.STATUSs.Finished.GetValue()) == WO.PLAN_QTY)
            {
                WO.STATUS = BIZ_MES_WO.STATUSs.Closed.GetValue();
                WO.ACT_END_TIME = DateTime.Now;
            }
            //保存数据库
            var db = Biz.Db;
            var dbTran = db.UseTran(() =>
            {
                db.Updateable(WO, user).UpdateColumns(q => new { q.UPDATE_TIME, q.UPDATE_USER, q.STATUS, q.ACT_END_TIME }).ExecuteCommand();
                db.Updateable(Batch, user).UpdateColumns(q => new { q.UPDATE_TIME, q.UPDATE_USER, q.STATUS, q.ACT_END_TIME }).ExecuteCommand();
            });
            if (!dbTran.IsSuccess)
            {
                //throw dbTran.ErrorException;
                Logger.Default.Fatal(dbTran.ErrorException, $"检查工单批次[{Batch.BATCH_NO}]是否完工异常");
                return false;
            }
            return true;
        }
        /// <summary>
        /// 根据岗位编码判断是不是首站
        /// </summary>
        /// <param name="postCode"></param>
@@ -239,6 +296,16 @@
        public MES_WO_NODE GetNode(string postCode)
        {
            return Nodes.FirstOrDefault(q => NodePosts.Any(p => p.NODE_ID == q.ID && p.POST_CODE == postCode));
        }
        /// <summary>
        /// 根据工序节点返回工序节点设置
        /// </summary>
        /// <param name="postCode"></param>
        /// <returns></returns>
        public MES_WO_OPER GetNodeSetting(MES_WO_NODE node)
        {
            return NodeSets.FirstOrDefault(q => q.NODE_ID == node?.ID);
        }
        /// <summary>
@@ -265,7 +332,7 @@
                    action.IsSuccessed = false;
                    action.Data.SetValue(this, null);
                    var nextList = Nodes.Where(q => q.IS_FIRST_NODE == "Y");
                    action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                    action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (GetNodeSetting(q)?.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                    return action;
                }
            }
@@ -301,7 +368,7 @@
                        action.IsSuccessed = false;
                        action.Data.SetValue(this, null);
                        var nextList = reflowNodes.Where(q => wipSN.REFLOW_NODE.IsNullOrEmpty() || wipSN.REFLOW_NODE == q.NODE_NAME);
                        action.LocaleMsg = new("MES.WorkBatch.ReflowToNodeException", nextNode.NODE_NAME, input.SN, curNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                        action.LocaleMsg = new("MES.WorkBatch.ReflowToNodeException", nextNode.NODE_NAME, input.SN, curNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (GetNodeSetting(q)?.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                        return action;
                    }
                }
@@ -327,12 +394,12 @@
                    }
                    //如果当前工序有必须执行的后续工序,则报错
                    if (nextNodes.Any(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y" && s.CAN_SKIP != "Y")))
                    if (nextNodes.Any(q => q.OPER_CODE != "EndNode" && NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y" && s.CAN_SKIP != "Y")))
                    {
                        action.IsSuccessed = false;
                        action.Data.SetValue(this, null);
                        var nextList = nextNodes.Where(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y" && s.CAN_SKIP != "Y"));
                        action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                        action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (GetNodeSetting(q)?.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                        return action;
                    }
                    //如果当前工序没有必须执行的后续工序,则在前置工序查找还有没有后续工序没完成的工序,有则尝试执行
@@ -361,7 +428,7 @@
                            action.IsSuccessed = false;
                            action.Data.SetValue(this, null);
                            var nextList = nextNodes.Where(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y"));
                            action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                            action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (GetNodeSetting(q)?.CAN_SKIP == "Y" ? $"({Biz.T(Biz.L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                            return action;
                        }
                    }
@@ -372,7 +439,7 @@
        }
        /// <summary>
        /// 添加节点的下一个可执行节点
        /// 获取节点的下一个可执行节点(排除有前置工序未完成的节点)
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录信息</param>
@@ -407,7 +474,7 @@
        }
        /// <summary>
        /// 根据传入的条码返回下一站可进入进入的目标工序
        /// 根据传入的条码返回下一站可进入的目标工序
        /// </summary>
        /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录和生产不良记录信息</param>
        /// <returns></returns>