服务端的TigerApi 框架,基于.NET6 2024 版本
增加工艺路线必须要有结束节点,及其流程相应优化
增加工单批次自动关单
增加判断条码是未投入生产还是已经完工产出
已修改10个文件
382 ■■■■ 文件已修改
Tiger.Api/Language.db 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/BIZ/BIZ_MES_WO.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Common/WorkBatch.cs 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Transaction/CollectNode.cs 95 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Transaction/PackingNode.cs 94 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Transaction/Position.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Business.MES/Transaction/TestNode.cs 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.IBusiness.MES/Core/IWorkBatch.cs 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Model.Net/Entitys/MES/BIZ_MES_WO_SN.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Model.Net/Entitys/MES/ParameterEntity/PositionParameter.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tiger.Api/Language.db
Binary files differ
Tiger.Business.MES/BIZ/BIZ_MES_WO.cs
@@ -207,7 +207,7 @@
                var db = Biz.Db;
                var dbTran = db.UseTran(() =>
                {
                    db.Updateable(_wo, input.UserId).UpdateColumns(x => new { x.STATUS, x.ROUTE_STATUS }).ExecuteCommand();
                    db.Updateable(_wo, input.UserId).UpdateColumns(x => new { x.STATUS, x.ROUTE_STATUS, x.UPDATE_USER, x.UPDATE_TIME }).ExecuteCommand();
                });
                if (!dbTran.IsSuccess)
                {
@@ -270,11 +270,11 @@
                    //如果是下发
                    if (input.Status == BIZ_MES_WO_BATCH.STATUSs.Release.GetValue())
                    {
                        db.Updateable(_woBatch, input.UserId).UpdateColumns(x => new { x.STATUS, x.RELEASE_USER, x.RELEASE_TIME, x.ACT_LINE }).ExecuteCommand();
                        db.Updateable(_woBatch, input.UserId).UpdateColumns(x => new { x.STATUS, x.RELEASE_USER, x.RELEASE_TIME, x.ACT_LINE, x.UPDATE_USER, x.UPDATE_TIME }).ExecuteCommand();
                    }
                    else
                    {
                        db.Updateable(_woBatch, input.UserId).UpdateColumns(x => new { x.STATUS }).ExecuteCommand();
                        db.Updateable(_woBatch, input.UserId).UpdateColumns(x => new { x.STATUS, x.UPDATE_USER, x.UPDATE_TIME }).ExecuteCommand();
                    }
                });
                if (!dbTran.IsSuccess)
Tiger.Business.MES/Common/WorkBatch.cs
@@ -62,20 +62,26 @@
        public void Update()
        {
            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.BATCH_NO == Batch.BATCH_NO).First();
            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())
                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()
@@ -99,7 +105,7 @@
        }
        /// <summary>
        /// 添加节点的下一个行为到工步列表
        /// 递归遍历工序节点,给下级工序节点排序和整理前置工序
        /// </summary>
        /// <param name="parent"></param>
        private void NodeSorting(MES_WO_NODE parent)
@@ -231,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>
@@ -346,7 +394,7 @@
                    }
                    //如果当前工序有必须执行的后续工序,则报错
                    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);
@@ -391,7 +439,7 @@
        }
        /// <summary>
        /// 添加节点的下一个可执行节点
        /// 获取节点的下一个可执行节点(排除有前置工序未完成的节点)
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录信息</param>
@@ -426,7 +474,7 @@
        }
        /// <summary>
        /// 根据传入的条码返回下一站可进入进入的目标工序
        /// 根据传入的条码返回下一站可进入的目标工序
        /// </summary>
        /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录和生产不良记录信息</param>
        /// <returns></returns>
Tiger.Business.MES/Transaction/CollectNode.cs
@@ -66,26 +66,27 @@
                    else
                    {
                        var wosns = Biz.Db.Queryable<BIZ_MES_WO_BATCH, BIZ_MES_WO_SN>((q, s) => new JoinQueryInfos(JoinType.Inner, q.ORDER_NO == s.WORK_ORDER && q.BATCH_NO == s.BATCH_NO))
                                                        .ByAuth(input.AuthOption).Where((q, s) => s.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue())
                                                        .ByAuth(input.AuthOption)//.Where((q, s) => s.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue())
                                                        .Where((q, s) => s.SN == input.SN || s.FLOW_SN == input.SN || s.TRAY_SN == input.SN).Select((q, s) => new { Batch = q, SN = s }).ToList();
                        //查找到条码已绑定的工单
                        if (!wosns.IsNullOrEmpty())
                        if (wosns.Any(q => q.SN.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue()))
                        {
                            if (wosns.First().Batch.ACT_LINE != CurLine.LINE_CODE)
                            var curSNs = wosns.Where(q => q.SN.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue());
                            if (curSNs.First().Batch.ACT_LINE != CurLine.LINE_CODE)
                            {
                                action.Data.ShortMsg = new("产线错误", ShortMessage.Types.Error);
                                action.Data.OperInfo = new();
                                action.IsSuccessed = false;
                                //action.LocaleMsg = new($"条码[{0}]已在产线[{1}]投入生产,请在正确岗位扫描");
                                action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotCorrectLine", input.SN, wosns.First().Batch.ACT_LINE);
                                action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotCorrectLine", input.SN, curSNs.First().Batch.ACT_LINE);
                            }
                            else
                            {
                                if (CurBatch?.Batch?.ORDER_NO != wosns.First().Batch.ORDER_NO)
                                if (CurBatch?.Batch?.ORDER_NO != curSNs.First().Batch.ORDER_NO)
                                {
                                    //条码已绑定的工单不等于当前工单则重新选择工单
                                    var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = wosns.First().Batch.ORDER_NO }, wosns.First().Batch.BATCH_NO);
                                    var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = curSNs.First().Batch.ORDER_NO }, curSNs.First().Batch.BATCH_NO);
                                    if (!result.IsSuccessed)
                                    {
                                        action.Data.ShortMsg = new("工单异常", ShortMessage.Types.Error);
@@ -126,11 +127,23 @@
                            //有当前工单且不是投入,则提示条码未投入生产,请先去首站扫描
                            else
                            {
                                action.Data.ShortMsg = new("未投入生产", ShortMessage.Types.Error);
                                action.Data.OperInfo = new();
                                action.IsSuccessed = false;
                                //action.LocaleMsg = new($"条码[{input.SN}]尚未投入生产,请先去首站扫描", input.SN);
                                action.LocaleMsg = new("MES.Transaction.CollectNode.Submit.NotInputException", input.SN);
                                var lastSn = wosns.OrderByDescending(q => q.SN.UPDATE_TIME).FirstOrDefault();
                                if (!lastSn.IsNullOrEmpty())
                                {
                                    action.Data.ShortMsg = new($"产品{lastSn.SN.STATUS.GetEnumDesc<BIZ_MES_WO_SN.STATUSs>()}", ShortMessage.Types.Error);
                                    action.Data.OperInfo = new();
                                    action.IsSuccessed = false;
                                    //action.LocaleMsg = new($"进站扫描错误,条码[{0}]{1}", input.SN);
                                    action.LocaleMsg = new("MES.Transaction.CollectNode.Submit.NotInputException", input.SN);
                                }
                                else
                                {
                                    action.Data.ShortMsg = new("未投入生产", ShortMessage.Types.Error);
                                    action.Data.OperInfo = new();
                                    action.IsSuccessed = false;
                                    //action.LocaleMsg = new($"条码[{input.SN}]尚未投入生产,请先去首站扫描", input.SN);
                                    action.LocaleMsg = new("MES.Transaction.CollectNode.Submit.NotInputException", input.SN);
                                }
                            }
                        }
                    }
@@ -556,16 +569,70 @@
        /// <returns></returns>
        public ApiAction<SubmitOutput> DoIfFinishAllSteps(ApiAction<SubmitOutput> action, string locale)
        {
            var operInfo = SetOperNodeInfo(CurOperInfo(locale));
            Action endAction = null;
            //如果当前条码已经走到流程终点则记录条码完工
            if (action.Data.OperInfo.IsReachedEndNode)
            {
                //更新工单条码明细信息
                var woSNs = CurBatch.WoSNs.Where(q => CurWipSNs.Any(w => q.WIP_ID == w.ID)).ToList();
                foreach (var woSN in woSNs)
                {
                    woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Finished.GetValue();
                }
                var curNode = CurBatch.Nodes.First(q => q.OPER_CODE == "EndNode");
                //条码完工
                foreach (var wipSN in CurWipSNs)
                {
                    wipSN.STATUS = MES_WIP_DATA.STATUSs.Finished.GetValue();
                    wipSN.NODE_ID = curNode.ID;
                    wipSN.NODE_NAME = curNode.NODE_NAME;
                    wipSN.OPER_CODE = curNode.OPER_CODE;
                    wipSN.SEGMENT = curNode.SEGMENT;
                }
                var wipHiss = new List<MES_WIP_HIS>();
                foreach (var wipSN in CurWipSNs)
                {
                    var his = new MES_WIP_HIS(wipSN, $"工单[{wipSN.WORK_ORDER}]条码[{wipSN.SN}]在岗位[{wipSN.POST_CODE}]过站工序[{wipSN.NODE_NAME}]成功");
                    wipSN.History.Add(his);
                    wipHiss.Add(his);
                }
                //创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值
                var _woSns = woSNs.Clone();
                var _wipSns = CurWipSNs.Clone();
                var _wipHiss = wipHiss.Clone();
                //保存数据
                endAction = () =>
                {
                    //使用统一的事务DB对象
                    var db = GetCommitDB();
                    //数据保存逻辑
                    db.Storageable(_woSns, UserCode).ExecuteCommand();
                    db.Storageable(_wipSns, UserCode).ExecuteCommand();
                    db.Storageable(_wipHiss, UserCode).ExecuteCommand();
                };
            }
            //保存数据库
            SaveStepsCommitActionToDB();
            SaveStepsCommitActionToDB(endAction);
            //保存成功,返回过站消息
            CurOperInfo(locale).InputQty += CurWipSNs.Count;
            action.Data.OperInfo = SetOperNodeInfo(CurOperInfo(locale));
            action.Data.OperInfo = operInfo;
            action.Data.ShortMsg = new(CurWipSNs.Any(q => q.DFT_FLAG == "Y") ? "不良过站" : "良品过站", ShortMessage.Types.Success);
            //action.LocaleMsg = new($"工单[{CurWipSN.WORK_ORDER}]的条码[{CurWipSN.SN}]在岗位[{CurWipSN.POST_CODE}]工序[{CurWipSN.NODE_NAME}]过站成功,状态[{CurWipSN.STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()}]");
            action.LocaleMsg = new("MES.Transaction.CollectNode.ScanSn.PassSuccess", CurWipSNs.First().WORK_ORDER, CurSN, CurWipSNs.First().POST_CODE, CurWipSNs.First().NODE_NAME,CurWipSNs.First().STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>());
            //如果当前条码已经完工,检查当前工单批次和工单是否完工
            if (action.Data.OperInfo.IsReachedEndNode)
            {
                CurBatch.CheckIsComplete(UserCode);
            }
            //重置工序
            ResetNode();
            return action;
Tiger.Business.MES/Transaction/PackingNode.cs
@@ -98,13 +98,14 @@
                        else
                        {
                            var wosns = Biz.Db.Queryable<BIZ_MES_WO_BATCH, BIZ_MES_WO_SN>((q, s) => new JoinQueryInfos(JoinType.Inner, q.ORDER_NO == s.WORK_ORDER && q.BATCH_NO == s.BATCH_NO))
                                                .ByAuth(input.AuthOption).Where((q, s) => s.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue())
                                                .ByAuth(input.AuthOption)//.Where((q, s) => s.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue())
                                                .Where((q, s) => s.SN == input.SN || s.FLOW_SN == input.SN || s.TRAY_SN == input.SN).Select((q, s) => new { Batch = q, SN = s }).ToList();
                            //查找到条码已绑定的工单
                            if (!wosns.IsNullOrEmpty())
                            if (wosns.Any(q => q.SN.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue()))
                            {
                                if (wosns.Count > 1 && wosns.Any(q => q.SN.TRAY_SN != q.SN.OUTER_SN))
                                var curSNs = wosns.Where(q => q.SN.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue());
                                if (curSNs.Count() > 1 && curSNs.Any(q => q.SN.TRAY_SN != q.SN.OUTER_SN))
                                {
                                    action.Data.ShortMsg = new("请扫描产品", ShortMessage.Types.Error);
                                    action.Data.OperInfo = new();
@@ -113,29 +114,29 @@
                                    action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.OnlyMinPackage", input.SN);
                                    return action;
                                }
                                if (Context.ContainsKey("CurPackage") && !Context["CurPackage"].IsNullOrEmpty() && (Context["CurPackage"] as WipPkg).WorkBatch != wosns.First().Batch.BATCH_NO)
                                if (Context.ContainsKey("CurPackage") && !Context["CurPackage"].IsNullOrEmpty() && (Context["CurPackage"] as WipPkg).WorkBatch != curSNs.First().Batch.BATCH_NO)
                                {
                                    action.Data.ShortMsg = new("工单批次错误", ShortMessage.Types.Error);
                                    action.Data.OperInfo = new();
                                    action.IsSuccessed = false;
                                    //action.LocaleMsg = new($"正在包装工单批次[{0}]的产品,请先完成当前包装后再扫描其他批次[{1}]的产品[{2}]");
                                    action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.BatchError", (Context["CurPackage"] as WipPkg).WorkBatch, wosns.First().Batch.BATCH_NO, input.SN);
                                    action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.BatchError", (Context["CurPackage"] as WipPkg).WorkBatch, curSNs.First().Batch.BATCH_NO, input.SN);
                                    return action;
                                }
                                if (wosns.First().Batch.ACT_LINE != CurLine.LINE_CODE)
                                if (curSNs.First().Batch.ACT_LINE != CurLine.LINE_CODE)
                                {
                                    action.Data.ShortMsg = new("产线投产错误", ShortMessage.Types.Error);
                                    action.Data.OperInfo = new();
                                    action.IsSuccessed = false;
                                    //action.LocaleMsg = new($"条码[{0}]已在产线[{1}]投入生产,请在正确岗位扫描");
                                    action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotCorrectLine", input.SN, wosns.First().Batch.ACT_LINE);
                                    action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotCorrectLine", input.SN, curSNs.First().Batch.ACT_LINE);
                                }
                                else
                                {
                                    if (CurBatch?.Batch?.ORDER_NO != wosns.First().Batch.ORDER_NO)
                                    if (CurBatch?.Batch?.ORDER_NO != curSNs.First().Batch.ORDER_NO)
                                    {
                                        //条码已绑定的工单不等于当前工单则重新选择工单
                                        var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = wosns.First().Batch.ORDER_NO }, wosns.First().Batch.BATCH_NO);
                                        var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = curSNs.First().Batch.ORDER_NO }, curSNs.First().Batch.BATCH_NO);
                                        if (!result.IsSuccessed)
                                        {
                                            action.Data.ShortMsg = new("工单异常", ShortMessage.Types.Error);
@@ -176,11 +177,23 @@
                                //有当前工单且不是投入,则提示条码未投入生产,请先去首站扫描
                                else
                                {
                                    action.Data.ShortMsg = new("未投入生产", ShortMessage.Types.Error);
                                    action.Data.OperInfo = new();
                                    action.IsSuccessed = false;
                                    //action.LocaleMsg = new($"条码[{input.SN}]尚未投入生产,请先去首站扫描", input.SN);
                                    action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotInputException", input.SN);
                                    var lastSn = wosns.OrderByDescending(q => q.SN.UPDATE_TIME).FirstOrDefault();
                                    if (!lastSn.IsNullOrEmpty())
                                    {
                                        action.Data.ShortMsg = new($"产品{lastSn.SN.STATUS.GetEnumDesc<BIZ_MES_WO_SN.STATUSs>()}", ShortMessage.Types.Error);
                                        action.Data.OperInfo = new();
                                        action.IsSuccessed = false;
                                        //action.LocaleMsg = new($"进站扫描错误,条码[{0}]{1}", input.SN);
                                        action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotInputException", input.SN);
                                    }
                                    else
                                    {
                                        action.Data.ShortMsg = new("未投入生产", ShortMessage.Types.Error);
                                        action.Data.OperInfo = new();
                                        action.IsSuccessed = false;
                                        //action.LocaleMsg = new($"条码[{input.SN}]尚未投入生产,请先去首站扫描", input.SN);
                                        action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotInputException", input.SN);
                                    }
                                }
                            }
                        }
@@ -572,7 +585,7 @@
                        {
                            case 2:
                                //action.LocaleMsg = new($"请执行第二步");
                                action.LocaleMsg = new("MES.Transaction.CollectNode.第二步操作提示");
                                action.LocaleMsg = new("MES.Transaction.PackingNode.第二步操作提示");
                                break;
                            default:
                                break;
@@ -608,16 +621,63 @@
        /// <returns></returns>
        public ApiAction<SubmitOutput> DoIfFinishAllSteps(ApiAction<SubmitOutput> action, string locale)
        {
            var operInfo = SetOperNodeInfo(CurOperInfo(locale));
            Action endAction = null;
            //如果当前条码已经走到流程终点则记录条码完工
            if (action.Data.OperInfo.IsReachedEndNode)
            {
                //更新工单条码明细信息
                var woSNs = CurBatch.WoSNs.Where(q => CurWipSNs.Any(w => q.WIP_ID == w.ID)).ToList();
                foreach (var woSN in woSNs)
                {
                    woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Finished.GetValue();
                }
                var curNode = CurBatch.Nodes.First(q => q.OPER_CODE == "EndNode");
                //条码完工
                foreach (var wipSN in CurWipSNs)
                {
                    wipSN.STATUS = MES_WIP_DATA.STATUSs.Finished.GetValue();
                    wipSN.NODE_ID = curNode.ID;
                    wipSN.NODE_NAME = curNode.NODE_NAME;
                    wipSN.OPER_CODE = curNode.OPER_CODE;
                    wipSN.SEGMENT = curNode.SEGMENT;
                }
                var wipHiss = new List<MES_WIP_HIS>();
                foreach (var wipSN in CurWipSNs)
                {
                    var his = new MES_WIP_HIS(wipSN, $"工单[{wipSN.WORK_ORDER}]条码[{wipSN.SN}]在岗位[{wipSN.POST_CODE}]过站工序[{wipSN.NODE_NAME}]成功");
                    wipSN.History.Add(his);
                    wipHiss.Add(his);
                }
                //创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值
                var _woSns = woSNs.Clone();
                var _wipSns = CurWipSNs.Clone();
                var _wipHiss = wipHiss.Clone();
                //保存数据
                endAction = () =>
                {
                    //使用统一的事务DB对象
                    var db = GetCommitDB();
                    //数据保存逻辑
                    db.Storageable(_woSns, UserCode).ExecuteCommand();
                    db.Storageable(_wipSns, UserCode).ExecuteCommand();
                    db.Storageable(_wipHiss, UserCode).ExecuteCommand();
                };
            }
            //保存数据库
            SaveStepsCommitActionToDB();
            //保存成功,返回过站消息
            CurOperInfo(locale).InputQty += CurWipSNs.Count;
            action.Data.Data = new PackingActionOutput() { PkgInfo = Context.ContainsKey("CurPackage") ? Context["CurPackage"] as WipPkg : null };
            action.Data.OperInfo = SetOperNodeInfo(CurOperInfo(locale));
            action.Data.OperInfo = operInfo;
            action.Data.ShortMsg = new(CurWipSNs.Any(q => q.DFT_FLAG == "Y") ? "不良过站" : "良品过站", ShortMessage.Types.Success);
            //action.LocaleMsg = new($"工单[{CurWipSN.WORK_ORDER}]的条码[{CurWipSN.SN}]在岗位[{CurWipSN.POST_CODE}]工序[{CurWipSN.NODE_NAME}]过站成功,状态[{CurWipSN.STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()}]");
            action.LocaleMsg = new("MES.Transaction.CollectNode.ScanSn.PassSuccess", CurWipSNs.First().WORK_ORDER, CurSN, CurWipSNs.First().POST_CODE, CurWipSNs.First().NODE_NAME, CurWipSNs.First().STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>());
            action.LocaleMsg = new("MES.Transaction.PackingNode.ScanSn.PassSuccess", CurWipSNs.First().WORK_ORDER, CurSN, CurWipSNs.First().POST_CODE, CurWipSNs.First().NODE_NAME, CurWipSNs.First().STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>());
            //重置工序
            ResetNode();
Tiger.Business.MES/Transaction/Position.cs
@@ -225,7 +225,12 @@
            if (CurWipSNs.Any())
            {
                info.CurNode = CurWipSNs.First().NODE_NAME;
                info.NextNode = string.Join(",", CurBatch.GetNextNodes(CurWipSNs.First()).Select(q => q.NODE_NAME));
                var nextNodes = CurBatch.GetNextNodes(CurWipSNs.First());
                info.NextNode = string.Join(",", nextNodes.Select(q => q.NODE_NAME));
                if (nextNodes.Count == 1 && nextNodes.Single().OPER_CODE == "EndNode")
                {
                    info.IsReachedEndNode = true;
                }
            }
            else
            {
Tiger.Business.MES/Transaction/TestNode.cs
@@ -67,26 +67,27 @@
                    else
                    {
                        var wosns = Biz.Db.Queryable<BIZ_MES_WO_BATCH, BIZ_MES_WO_SN>((q, s) => new JoinQueryInfos(JoinType.Inner, q.ORDER_NO == s.WORK_ORDER && q.BATCH_NO == s.BATCH_NO))
                                                 .ByAuth(input.AuthOption).Where((q, s) => s.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue())
                                                 .Where((q, s) => s.SN == input.SN || s.FLOW_SN == input.SN || s.TRAY_SN == input.SN).Select((q, s) => new { Batch = q, SN = s }).ToList();
                                                        .ByAuth(input.AuthOption)//.Where((q, s) => s.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue())
                                                        .Where((q, s) => s.SN == input.SN || s.FLOW_SN == input.SN || s.TRAY_SN == input.SN).Select((q, s) => new { Batch = q, SN = s }).ToList();
                        //查找到条码已绑定的工单
                        if (!wosns.IsNullOrEmpty())
                        if (wosns.Any(q => q.SN.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue()))
                        {
                            if (wosns.First().Batch.ACT_LINE != CurLine.LINE_CODE)
                            var curSNs = wosns.Where(q => q.SN.STATUS < BIZ_MES_WO_SN.STATUSs.Finished.GetValue());
                            if (curSNs.First().Batch.ACT_LINE != CurLine.LINE_CODE)
                            {
                                action.Data.ShortMsg = new("产线错误", ShortMessage.Types.Error);
                                action.Data.OperInfo = new();
                                action.IsSuccessed = false;
                                //action.LocaleMsg = new($"条码[{0}]已在产线[{1}]投入生产,请在正确岗位扫描");
                                action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotCorrectLine", input.SN, wosns.First().Batch.ACT_LINE);
                                action.LocaleMsg = new("MES.Transaction.PackingNode.Submit.NotCorrectLine", input.SN, curSNs.First().Batch.ACT_LINE);
                            }
                            else
                            {
                                if (CurBatch?.Batch?.ORDER_NO != wosns.First().Batch.ORDER_NO)
                                if (CurBatch?.Batch?.ORDER_NO != curSNs.First().Batch.ORDER_NO)
                                {
                                    //条码已绑定的工单不等于当前工单则重新选择工单
                                    var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = wosns.First().Batch.ORDER_NO }, wosns.First().Batch.BATCH_NO);
                                    var result = await SelectOrder(new() { AuthOption = input.AuthOption, OrderNo = curSNs.First().Batch.ORDER_NO }, curSNs.First().Batch.BATCH_NO);
                                    if (!result.IsSuccessed)
                                    {
                                        action.Data.ShortMsg = new("工单异常", ShortMessage.Types.Error);
@@ -127,11 +128,23 @@
                            //有当前工单且不是投入,则提示条码未投入生产,请先去首站扫描
                            else
                            {
                                action.Data.ShortMsg = new("未投入生产", ShortMessage.Types.Error);
                                action.Data.OperInfo = new();
                                action.IsSuccessed = false;
                                //action.LocaleMsg = new($"条码[{input.SN}]尚未投入生产,请先去首站扫描", input.SN);
                                action.LocaleMsg = new("MES.Transaction.TestNode.Submit.NotInputException", input.SN);
                                var lastSn = wosns.OrderByDescending(q => q.SN.UPDATE_TIME).FirstOrDefault();
                                if (!lastSn.IsNullOrEmpty())
                                {
                                    action.Data.ShortMsg = new($"产品{lastSn.SN.STATUS.GetEnumDesc<BIZ_MES_WO_SN.STATUSs>()}", ShortMessage.Types.Error);
                                    action.Data.OperInfo = new();
                                    action.IsSuccessed = false;
                                    //action.LocaleMsg = new($"进站扫描错误,条码[{0}]{1}", input.SN);
                                    action.LocaleMsg = new("MES.Transaction.TestNode.Submit.NotInputException", input.SN);
                                }
                                else
                                {
                                    action.Data.ShortMsg = new("未投入生产", ShortMessage.Types.Error);
                                    action.Data.OperInfo = new();
                                    action.IsSuccessed = false;
                                    //action.LocaleMsg = new($"条码[{input.SN}]尚未投入生产,请先去首站扫描", input.SN);
                                    action.LocaleMsg = new("MES.Transaction.TestNode.Submit.NotInputException", input.SN);
                                }
                            }
                        }
                    }
@@ -556,15 +569,62 @@
        /// <returns></returns>
        public ApiAction<SubmitOutput> DoIfFinishAllSteps(ApiAction<SubmitOutput> action, string locale)
        {
            var operInfo = SetOperNodeInfo(CurOperInfo(locale));
            Action endAction = null;
            //如果当前条码已经走到流程终点则记录条码完工
            if (action.Data.OperInfo.IsReachedEndNode)
            {
                //更新工单条码明细信息
                var woSNs = CurBatch.WoSNs.Where(q => CurWipSNs.Any(w => q.WIP_ID == w.ID)).ToList();
                foreach (var woSN in woSNs)
                {
                    woSN.STATUS = BIZ_MES_WO_SN.STATUSs.Finished.GetValue();
                }
                var curNode = CurBatch.Nodes.First(q => q.OPER_CODE == "EndNode");
                //条码完工
                foreach (var wipSN in CurWipSNs)
                {
                    wipSN.STATUS = MES_WIP_DATA.STATUSs.Finished.GetValue();
                    wipSN.NODE_ID = curNode.ID;
                    wipSN.NODE_NAME = curNode.NODE_NAME;
                    wipSN.OPER_CODE = curNode.OPER_CODE;
                    wipSN.SEGMENT = curNode.SEGMENT;
                }
                var wipHiss = new List<MES_WIP_HIS>();
                foreach (var wipSN in CurWipSNs)
                {
                    var his = new MES_WIP_HIS(wipSN, $"工单[{wipSN.WORK_ORDER}]条码[{wipSN.SN}]在岗位[{wipSN.POST_CODE}]过站工序[{wipSN.NODE_NAME}]成功");
                    wipSN.History.Add(his);
                    wipHiss.Add(his);
                }
                //创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值
                var _woSns = woSNs.Clone();
                var _wipSns = CurWipSNs.Clone();
                var _wipHiss = wipHiss.Clone();
                //保存数据
                endAction = () =>
                {
                    //使用统一的事务DB对象
                    var db = GetCommitDB();
                    //数据保存逻辑
                    db.Storageable(_woSns, UserCode).ExecuteCommand();
                    db.Storageable(_wipSns, UserCode).ExecuteCommand();
                    db.Storageable(_wipHiss, UserCode).ExecuteCommand();
                };
            }
            //保存数据库
            SaveStepsCommitActionToDB();
            //保存成功,返回过站消息
            CurOperInfo(locale).InputQty += CurWipSNs.Count;
            action.Data.OperInfo = SetOperNodeInfo(CurOperInfo(locale));
            action.Data.OperInfo = operInfo;
            action.Data.ShortMsg = new(CurWipSNs.Any(q => q.DFT_FLAG == "Y") ? "不良过站" : "良品过站", ShortMessage.Types.Success);
            //action.LocaleMsg = new($"工单[{CurWipSN.WORK_ORDER}]的条码[{CurWipSN.SN}]在岗位[{CurWipSN.POST_CODE}]工序[{CurWipSN.NODE_NAME}]过站成功,状态[{CurWipSN.STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>()}]");
            action.LocaleMsg = new("MES.Transaction.CollectNode.ScanSn.PassSuccess", CurWipSNs.First().WORK_ORDER, CurSN, CurWipSNs.First().POST_CODE, CurWipSNs.First().NODE_NAME, CurWipSNs.First().STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>());
            action.LocaleMsg = new("MES.Transaction.TestNode.ScanSn.PassSuccess", CurWipSNs.First().WORK_ORDER, CurSN, CurWipSNs.First().POST_CODE, CurWipSNs.First().NODE_NAME, CurWipSNs.First().STATUS.GetEnumDesc<MES_WIP_DATA.STATUSs>());
            
            //重置工序
            ResetNode();
Tiger.IBusiness.MES/Core/IWorkBatch.cs
@@ -1,4 +1,5 @@
using Newtonsoft.Json;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Rhea.Common;
using System;
using Tiger.Model;
@@ -34,6 +35,12 @@
        /// <returns></returns>
        public bool StartWorking(string user);
        /// <summary>
        /// 检查工单是否完工,已完工则修改相应状态并记录到数据库
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public Task<bool> CheckIsComplete(string user);
        /// <summary>
        /// 根据岗位编码判断是不是首站
        /// </summary>
        /// <param name="postCode"></param>
Tiger.Model.Net/Entitys/MES/BIZ_MES_WO_SN.cs
@@ -64,9 +64,9 @@
        [DisplayName("物料编码")]
        public string ITEM_CODE { get; set; }
        /// <summary>
        /// 状态(0NotInput未投入|1Inputed已投入|2Finished已完工)
        /// 状态(0NotInput未投入|1Inputed已投入|2Finished已完工|3Scrap已报废)
        /// </summary>
        [DisplayName("状态(0NotInput未投入|1Inputed已投入|2Finished已完工)")]
        [DisplayName("状态(0NotInput未投入|1Inputed已投入|2Finished已完工|3Scrap已报废)")]
        public int STATUS { get; set; }
        /// <summary>
        /// 备注
@@ -108,7 +108,7 @@
        */
        /// <summary>
        /// 枚举:状态(0NotInput未投入|1Inputed已投入|2Finished已完工)
        /// 枚举:状态(0NotInput未投入|1Inputed已投入|2Finished已完工|3Scrap已报废)
        /// </summary>
        public enum STATUSs
        {
@@ -118,6 +118,8 @@
            Inputed = 1,
            [Description("已完工")]
            Finished = 2,
            [Description("已报废")]
            Scrap = 3,
        }
        #endregion
Tiger.Model.Net/Entitys/MES/ParameterEntity/PositionParameter.cs
@@ -38,6 +38,10 @@
        /// </summary>
        public string SN { get; set; }
        /// <summary>
        /// 数量
        /// </summary>
        public string Qty { get; set; }
        /// <summary>
        /// 当前操作提交的不良代码,没有则留空
        /// </summary>
        public string DFT_CODE { get; set; }
@@ -45,10 +49,6 @@
        /// 当前操作需要提交的数据
        /// </summary>
        public string Data { get; set; }
        /// <summary>
        /// 数量
        /// </summary>
        public string Qty { get; set; }
    }
    public class SubmitOutput
@@ -247,6 +247,7 @@
    {
        public string NextNode { get; set; } = "   —   ";
        public string CurNode { get; set; } = "   —   ";
        public bool IsReachedEndNode { get; set; } = false;
        public int InputQty { get; set; } = 0;
        public List<WorkStepInfo> StepsInfo { get; set; } = new List<WorkStepInfo>();
    }