服务端的TigerApi 框架,基于.NET6 2024 版本
Ben Lin
2024-10-16 e16639d5bee7a844c018ec176c69f5d388b637f8
Tiger.Business.MES/Transaction/Position.cs
@@ -11,6 +11,9 @@
using Tiger.Model;
using static Tiger.Business.Biz;
using Tiger.Model.Entitys.MES.Position;
using System.Globalization;
using Tiger.Business.MES.WorkAction;
using System.Collections;
namespace Tiger.Business.MES.Transaction
{
@@ -48,6 +51,9 @@
        #region Propertys & Variables
        public string UserCode { get; set; }
        public string PostCode { get; set; }
        protected DateTime BeginAt;
        protected DateTime EndAt;
        public TimeSpan ElapsedTime => EndAt - BeginAt;
        public MES_FACTORY CurFactory { get; set; }
        public MES_WORKSHOP CurWorkshop { get; set; }
        public MES_LINE CurLine { get; set; }
@@ -64,7 +70,7 @@
        public WorkStep CurStep { get; set; }
        public List<WorkStep> NextSteps { get; set; } = new();
        public bool IsFinishNodeSteps => !Steps.Any(q => q.NodeType == IWorkStep.NodeTypes.Node && !q.IsFinished);
        public bool IsFinishAllSteps => !Steps.Any() || !Steps.Any(q => !q.IsFinished);
        public bool IsFinishAllSteps => Steps.Any() && !Steps.Any(q => !q.IsFinished);
        //public int CurStep => Steps.Where(q => !q.IsFinished).OrderBy(q => q.Sequence).FirstOrDefault()?.Sequence ?? 0;
        private DbClient CommitDB;
        /// <summary>
@@ -73,8 +79,8 @@
        public bool NeedTemporaryStoreDBCommitAction { get; set; } = false;
        protected Dictionary<string, List<Action>> DBCommitList { get; set; } = new();
        protected List<Position> NodeCommitList { get; set; } = new();
        protected Dictionary<string, OperInfo> OperInfoDic { get; set; } = new();
        //protected OperInfo CurOperInfo { get; set; }
        private Dictionary<string, OperInfo> OperInfoDic = new();
        #endregion Propertys & Variables
        #region Functions
@@ -91,12 +97,12 @@
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<ApiAction> SelectOrder(WoInput input)
        public async Task<ApiAction> SelectOrder(WoInput input, string batchNo = "")
        {
            var action = new ApiAction();
            try
            {
                if (!WoContext.ExistsBatch(input.OrderNo, CurLine.LINE_CODE))
                if (!WoContext.ExistsBatch(input.OrderNo, CurLine.LINE_CODE, batchNo, true))
                {
                    var wo = await Biz.Db.Queryable<BIZ_MES_WO>().ByAuth(input.AuthOption).Where(q => q.ORDER_NO == input.OrderNo).FirstAsync();
                    //验证明细是否正确
@@ -110,11 +116,14 @@
                    if (wo.STATUS != BIZ_MES_WO.STATUSs.Release.GetValue() && wo.STATUS != BIZ_MES_WO.STATUSs.Working.GetValue())
                    {
                        action.IsSuccessed = false;
                        //action.LocaleMsg = new($"工单[{input.OrderNo}]状态[{wo.STATUS.GetEnum<BIZ_MES_WO.STATUSs>().GetName()}]不能生产");
                        action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.StatusException", input.OrderNo, wo.STATUS.GetEnum<BIZ_MES_WO.STATUSs>().GetName());
                        //action.LocaleMsg = new($"工单[{0}]状态[{1}]不能生产");
                        action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.StatusException", input.OrderNo, wo.STATUS.GetEnumDesc<BIZ_MES_WO.STATUSs>());
                        return action;
                    }
                    var batch = await Biz.Db.Queryable<BIZ_MES_WO_BATCH>().ByAuth(input.AuthOption).Where(q => q.ORDER_NO == input.OrderNo && q.ACT_LINE == CurLine.LINE_CODE).FirstAsync();
                    var batch = await Biz.Db.Queryable<BIZ_MES_WO_BATCH>().ByAuth(input.AuthOption)
                        .Where(q => q.ORDER_NO == input.OrderNo && q.ACT_LINE == CurLine.LINE_CODE)
                        .WhereIF(!batchNo.IsNullOrEmpty(), q => q.BATCH_NO == batchNo)
                        .OrderBy(q => q.STATUS).FirstAsync();
                    if (batch.IsNullOrEmpty())
                    {
                        action.IsSuccessed = false;
@@ -122,15 +131,22 @@
                        action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.LineException", input.OrderNo, CurLine.LINE_CODE);
                        return action;
                    }
                    if (batch.STATUS != BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() && batch.STATUS != BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())
                    {
                        action.IsSuccessed = false;
                        //action.LocaleMsg = new($"工单[{0}]批次[1]状态[{2}]不能生产");
                        action.LocaleMsg = new("MES.Transaction.Position.SelectOrder.BatchStatusException", input.OrderNo, batch.BATCH_NO, batch.STATUS.GetEnumDesc<BIZ_MES_WO_BATCH.STATUSs>());
                        return action;
                    }
                    var wb = new WorkBatch(input.OrderNo).Init(CurLine.LINE_CODE);
                    WoContext.WoBatchDic.Add(wb.Batch.BATCH_NO, wb);
                }
                CurBatch = WoContext.GetBatch(input.OrderNo, CurLine.LINE_CODE);
                CurBatch = WoContext.GetBatch(input.OrderNo, CurLine.LINE_CODE, batchNo);
                if (!OperInfoDic.ContainsKey(CurBatch.Batch.BATCH_NO))
                {
                    OperInfoDic.Add(CurBatch.Batch.BATCH_NO, new());
                }
                action.Data = new { WorkOrder = CurBatch.WO, Bacth = CurBatch.Batch };
            }
            catch (Exception ex)
@@ -189,22 +205,37 @@
            CurDefects.Clear();
        }
        //获取当前的工序信息
        protected OperInfo CurOperInfo(string locale)
        {
            OperInfo info = new();
            if (CurBatch?.Batch?.BATCH_NO != null)
            {
                info = OperInfoDic[CurBatch.Batch.BATCH_NO];
                info.StepsInfo = Steps.Select(q => q.GetInfo(locale)).ToList();
            }
            return info;
        }
        /// <summary>
        /// 设置当前条码的工序信息
        /// </summary>
        public OperInfo SetOperNodeInfo(OperInfo info, string locale)
        public OperInfo SetOperNodeInfo(OperInfo info)
        {
            if (CurWipSNs.Any())
            {
                info.CurNode = CurWipSNs.First().NODE_NAME;
                info.NextNode = string.Join(",", CurBatch.GetNextNodes(CurWipSNs.First()).Select(q => q.NODE_NAME));
                info.StepsInfo = Steps.Select(q => q.GetInfo(locale)).ToList();
                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
            {
                info.CurNode = "   —   ";
                info.NextNode = "   —   ";
                info.StepsInfo = new();
            }
            return info;
        }
@@ -334,11 +365,23 @@
                    CurStep = step;
                    //更新后续可执行的工步列表
                    NextSteps = GetNextSteps(CurStep);
                    //返回结果到客户端
                    result = canBegin;
                    result.Data.SetValue(CurBatch, CurStep, CurStep?.ID, IsFinishAllSteps);
                    //如果当前执行工步尝试执行后就已经完成,不需要后续交互,且,则继续开始下一工步
                    if (CurStep.IsFinished && NextSteps.Any())
                    {
                        result = BeginNextActionStep(input);
                    }
                    else
                    {
                        //返回结果到客户端
                        result = canBegin;
                        result.Data.SetValue(CurBatch, CurStep, CurStep?.ID, IsFinishAllSteps);
                    }
                    return result;
                }
                //工序被重置
                else if (Steps.IsNullOrEmpty())
                {
                    return canBegin;
                }
            }
@@ -361,15 +404,45 @@
        /// <summary>
        /// 保存工步的数据库提交操作到数据库
        /// </summary>
        protected void SaveStepsCommitActionToDB()
        /// <param name="appendToSave">提交完工步数据后,提交附加的保存内容</param>
        public void SaveStepsCommitActionToDB(Action appendToSave = null)
        {
            //保存工步的数据库提交操作到提交操作列表
            var commitList = new List<Action>();
            foreach (var step in Steps.OrderBy(q => q.Sequence))
            if (!Steps.IsNullOrEmpty())
            {
                commitList.Add(step.DBSubmitAction);
                var commitList = new List<Action>();
                foreach (var step in Steps.OrderBy(q => q.Sequence))
                {
                    commitList.Add(step.DBSubmitAction);
                }
                //记录工序耗时
                var ids = CurWipSNHiss.Select(q => q.ID).ToList().Clone();
                commitList.Add(() =>
                {
                    EndAt = DateTime.Now;
                    GetCommitDB().Updateable<MES_WIP_HIS>().SetColumns(q => q.ELAPSED_TIME == ElapsedTime.TotalMilliseconds.ToInt64()).Where(q => ids.Contains(q.ID)).ExecuteCommand();
                });
                //增加附加的保存内容
                if (!appendToSave.IsNullOrEmpty())
                {
                    commitList.Add(appendToSave);
                }
                DBCommitList.Add(CurSN, commitList);
            }
            DBCommitList.Add(CurSN, commitList);
            else
            {
                //增加附加的保存内容
                if (!appendToSave.IsNullOrEmpty())
                {
                    if (!DBCommitList.ContainsKey("AppendSaveAction"))
                    {
                        DBCommitList.Add("AppendSaveAction", new List<Action>());
                    }
                    var commitList = DBCommitList["AppendSaveAction"];
                    commitList.Add(appendToSave);
                }
            }
            //如果不需要临时存储数据库提交操作,则把提交操作列表提交到数据库
            if (!NeedTemporaryStoreDBCommitAction)
            {
@@ -379,9 +452,17 @@
                var dbTran = GetCommitDB().UseTran(() =>
                {
                    //在同一个事务中保存所有工步的数据
                    foreach (var wipSn in DBCommitList.Keys)
                    foreach (var wipSn in DBCommitList.Keys.Where(q => q != "AppendSaveAction"))
                    {
                        foreach (var action in DBCommitList[wipSn])
                        {
                            action.Invoke();
                        }
                    }
                    //附加的保存内容
                    if (DBCommitList.ContainsKey("AppendSaveAction"))
                    {
                        foreach (var action in DBCommitList["AppendSaveAction"])
                        {
                            action.Invoke();
                        }
@@ -417,15 +498,16 @@
            }
        }
        #region 打印专用方法
        /// <summary>
        /// 获取打印标签模板过程变量值
        /// </summary>
        /// <param name="labelPVs">过程变量列表</param>
        /// <param name="label">标签模板</param>
        /// <returns></returns>
        public BAS_LABEL_TEMP SetLabelVariables(List<BAS_LABEL_PV> labelPVs, BAS_LABEL_TEMP label)
        public BAS_LABEL_TEMP SetLabelVariables(List<BAS_LABEL_PV> labelPVs, BAS_LABEL_TEMP label, IWorkAction action)
        {
            foreach (var item in label.Variables)
            foreach (var item in label.Variables.OrderBy(q => q.VAR_TYPE == BAS_LABEL_VAR.VAR_TYPEs.BarcodeGenerate.GetValue() ? 0 : 1))
            {
                switch (item.VAR_TYPE.GetEnum<BAS_LABEL_VAR.VAR_TYPEs>())
                {
@@ -433,7 +515,7 @@
                        item.Value = item.VAR_VALUE;
                        break;
                    case BAS_LABEL_VAR.VAR_TYPEs.ProcessVariable:
                        item.Value = GetPrintProcessValue(labelPVs, item);
                        item.Value = GetPrintProcessValue(labelPVs, item, label.Variables, action);
                        break;
                    case BAS_LABEL_VAR.VAR_TYPEs.DateVariable:
                        item.Value = DateTime.Now.ToString(item.VAR_VALUE);
@@ -456,7 +538,7 @@
        /// <param name="labelPVs">过程变量列表</param>
        /// <param name="lv">标签模板变量</param>
        /// <returns></returns>
        public string GetPrintProcessValue(List<BAS_LABEL_PV> labelPVs, BAS_LABEL_VAR lv)
        public string GetPrintProcessValue(List<BAS_LABEL_PV> labelPVs, BAS_LABEL_VAR lv, List<BAS_LABEL_VAR> lvars, IWorkAction action)
        {
            var pv = labelPVs.FirstOrDefault(q => q.VAR_CODE == lv.VAR_VALUE);
            if (!pv.IsNullOrEmpty())
@@ -470,11 +552,87 @@
                                case "GetSN":
                                    return CurSN;
                                case "GetBAS_ITEM":
                                    return WorkBatch.Product.ToJson();
                                    return WorkBatch.Product.CUST_PROD_CODE;
                                case "GetItemName":
                                    return WorkBatch.Product.ITEM_NAME;
                                case "GetCustomer":
                                    return WorkBatch.Batch.ToJson();
                                    return WorkBatch.Batch.Customer.ToJson();
                                case "GetBoxQR":
                                    return action is PackingAction ? GetBoxCode(lvars, action as PackingAction) : "";
                                case "GetCardQR":
                                    return action is PackingAction ? GetCardOrBoxQR(GetCardCode(lvars), lvars, action as PackingAction) : "";
                                case "GetCardSN":
                                    List<string> minPkgList = new List<string>();
                                    var _pkaction = action is PackingAction ? action as PackingAction : null;
                                    if (_pkaction != null)
                                    {
                                        minPkgList = GetMinPkgList(_pkaction);
                                    }
                                    return string.Join("\r\n", minPkgList);
                                case "GetDescription":
                                    return WorkBatch.Product.ITEM_DESC;
                                case "GetFengZ":
                                    return WorkBatch.Product.ExtInfo.Packaging;
                                case "GetPAndO":
                                    return WorkBatch.WO.SALES_CONTRACT;
                                case "GetLTD":
                                    return WorkBatch.Batch.Customer.CUST_NAME_CN;
                                case "GetHWDate":
                                    List<string> list = new List<string>();
                                    var _action = action is PackingAction ? action as PackingAction : null;
                                    if (_action != null)
                                    {
                                        list = GetMinPkgList(_action);
                                    }
                                    return GetHuaWeiWeek(WorkBatch.Batch.ORDER_NO, list);
                                case "GetQty":
                                    List<string> qtylist = new List<string>();
                                    var _qtyAction = action is PackingAction ? action as PackingAction : null;
                                    if (_qtyAction != null)
                                    {
                                        qtylist = GetMinPkgList(_qtyAction);
                                    }
                                    return $"{qtylist.Count}PCS";
                                case "GetModel":
                                    return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Model);
                                case "GetInput":
                                    return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Input);
                                case "GetOutput":
                                    return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.OutPut);
                                case "GetPower":
                                    return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Power);
                                case "GetAccuracy":
                                    return GetLabelVarWo(lv, WorkBatch.Product.ExtInfo?.Accuracy);
                                case "GetVarByWo":
                                    return GetLabelVarWo(lv);
                                    return GetLabelVarWo(lv, "");
                                case "GetLOTNO":
                                    string _lotnos = "";
                                    var _orderAction = action is PackingAction ? action as PackingAction : null;
                                    if (_orderAction != null)
                                    {
                                        var snList = _orderAction.GetMinPackageList(_orderAction.CurPkg.Item).Select(q => q.FLOW_SN).ToList();
                                        if (!snList.Any())
                                        {
                                            snList.Add(_orderAction.CurPkg.CustSN.FLOW_SN);
                                        }
                                        _lotnos = string.Join(",", Biz.Db.Queryable<MES_CUST_SN>()
                                            .Where((q) => snList.Contains(q.FLOW_SN))
                                            .Select((q) => q.WORK_ORDER).Distinct().ToList());
                                        return _lotnos;
                                    }
                                    return "";
                                case "GetNOTES":
                                    return WorkBatch.Product.ExtInfo?.Remark;
                                case "GetXH":
                                    return GetCardCode(lvars);
                                case "GetCardNo":
                                    return "";
                                case "GetCardTotal":
                                    return "";
                                case "GetModelSpec": //Model+空格+SPEC,
                                    return $"{WorkBatch.Product.ExtInfo?.Model} {WorkBatch.Product.SPEC}";
                                case "GetDATE":
                                    return $"{DateTime.Now.ToString("yyyy/MM/dd")}";
                                default:
                                    return "";
                            }
@@ -508,9 +666,9 @@
                            switch (pv.VAR_METHOD)
                            {
                                case "GetCartonGenerate":
                                    return Biz.CodeRule[lv.BARCODE_RULE ?? ""]?.Generate($"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
                                    return Biz.CodeRule[lv.BARCODE_RULE ?? ""]?.Generate("B", $"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
                                case "GetHW21SNGenerate":
                                    return Biz.CodeRule[lv.BARCODE_RULE ?? ""]?.Generate("SN:","05").Data.ToString() ?? "";
                                    return Biz.CodeRule[lv.BARCODE_RULE ?? ""]?.Generate("SN:", "05").Data.ToString() ?? "";
                                default:
                                    return "";
                            }
@@ -527,7 +685,7 @@
        /// </summary>
        /// <param name="labelId"></param>
        /// <returns></returns>
        private string GetLabelVarWo(BAS_LABEL_VAR lv)
        private string GetLabelVarWo(BAS_LABEL_VAR lv, string value)
        {
            string result = "";
            var labelVarwos = Biz.Db.Queryable<BAS_LABEL_VAR_WO>().Where(x => x.LABEL_ID == lv.LABEL_ID && x.VAR_NAME == lv.VAR_NAME).ToList();
@@ -537,11 +695,144 @@
            }
            else
            {
                result = labelVarwos.Count > 0 ? labelVarwos[0].DEFAULT_VALUE : "";
                result = value.IsNullOrEmpty() ? (labelVarwos.Count > 0 ? labelVarwos.First().DEFAULT_VALUE : "") : value;
            }
            return result;
        }
        /// <summary>
        /// 获取最小包装条码列表
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        private List<string> GetMinPkgList(PackingAction action)
        {
            List<string> list = new List<string>();
            if (action.CurPkg.Item.PKG_LEVEL == 1)
            {
                list.Add(CurSN);
            }
            else
            {
                list = action.GetMinPackageList(action.CurPkg.Item).Select(q => q.SN).ToList();
            }
            return list;
        }
        /// <summary>
        /// 获取白盒条码
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        private string GetBoxCode(List<BAS_LABEL_VAR> lvars, PackingAction action)
        {
            var boxCode = Biz.CodeRule["WhiteBoxNo"]?.Generate("W", $"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
            return GetCardOrBoxQR(boxCode, lvars, action);
        }
        /// <summary>
        /// 获取箱条码
        /// </summary>
        /// <param name="lvars"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        private string GetCardCode(List<BAS_LABEL_VAR> lvars)
        {
            var cardCode = Biz.CodeRule["CartonNo"]?.Generate("B", $"{WorkBatch.Batch.BATCH_NO}-{WorkBatch.Batch.PLAN_QTY}-").Data.ToString() ?? "";
            return cardCode;
        }
        /// <summary>
        /// 获取白盒或者箱二维码
        /// </summary>
        /// <param name="code"></param>
        /// <param name="lvars"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        private string GetCardOrBoxQR(string code, List<BAS_LABEL_VAR> lvars, PackingAction action)
        {
            var minPkgList = action.GetMinPackageList(action.CurPkg.Item).Select(q => q.SN);
            var wo = WorkBatch.Batch.ORDER_NO;
            var snList = string.Join("\r\n", minPkgList);
            if (snList.IsNullOrEmpty()) { snList = $"{CurSN}"; }
            var itemCode = action.CurPkg.CustSN?.ITEM_CODE;
            var weight = action.CurPkg.WeightInfo.Weight;
            var unit = action.CurPkg.WeightInfo.Unit.ToUpper();
            var model = WorkBatch.Product.ExtInfo.Model;
            var Input = WorkBatch.Product.ExtInfo.Input;
            var OutPut = WorkBatch.Product.ExtInfo.OutPut;
            var Power = WorkBatch.Product.ExtInfo.Power;
            var Accuracy = WorkBatch.Product.ExtInfo.Accuracy;
            var Display = WorkBatch.Product.ExtInfo.Display;
            var Remark = WorkBatch.Product.ExtInfo.Remark;
            return $"{code}|{itemCode}|{wo}|\r\n{snList}|{weight}{unit}|型号:{model},输入:{Input},显示:{Display},输出:{OutPut},电源:{Power},精度:{Accuracy},备注:{Remark}";
        }
        /// <summary>
        /// 根据包装工单和条码获取华为要求的生产周期
        /// </summary>
        /// <param name="snList"></param>
        /// <returns></returns>
        private string GetHuaWeiWeek(string pkgOrder, List<string> snList)
        {
            Dictionary<string, string> dic = new();
            var snOrder = Biz.Db.Queryable<MES_CUST_SN, BIZ_MES_WO>((q, w) => new JoinQueryInfos(JoinType.Left, q.WORK_ORDER == w.ORDER_NO))
                                            .Where((q, w) => q.PKG_ORDER == pkgOrder && (snList.Contains(q.FLOW_SN) || snList.Contains(q.CUST_SN)))
                                           .Select((q, w) => new { q.PKG_ORDER, q.WORK_ORDER, w.ACT_START_TIME, w.PLAN_START_TIME, q.FLOW_SN, q.CUST_SN }).ToList();
            foreach (var sn in snList)
            {
                var item = snOrder.FirstOrDefault(q => q.FLOW_SN == sn || q.CUST_SN == sn);
                if (!item.IsNullOrEmpty())
                {
                    var date = item.ACT_START_TIME < new DateTime(2000, 1, 1) ? item.PLAN_START_TIME : item.ACT_START_TIME;
                    var firstDay = new DateTime(date.Year, 1, 1);
                    int daysOffset = firstDay.DayOfWeek.GetValue() > 3 ? (firstDay.DayOfWeek.GetValue() - 7) : 0;
                    int year = date.Year + ((date - firstDay).TotalDays + daysOffset < 0 ? -1 : 0);
                    int week = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Sunday);
                    dic.Add(sn, $"{year.ToString("0000").Substring(2, 2)}{week:00}");
                }
                else
                {
                    dic.Add(sn, "");
                }
            }
            var weeks = dic.Select(q => q.Value).Where(q => !q.IsNullOrEmpty()).Distinct().OrderBy(q => q).ToList();
            switch (weeks.Count)
            {
                case 0:
                    return "";
                case 1:
                    return weeks.First();
                case 2:
                    return string.Join("、", weeks.Select(q => q + $"({dic.Count(d => d.Value == q)}PCS)"));
                default:
                    return string.Join("、", weeks.Take(2)) + " (+)";
            }
        }
        #endregion
        #region 包装专用方法
        /// <summary>
        /// 根据传入的包装对象返回所有的包装实体列表
        /// </summary>
        /// <param name="parent"></param>
        /// <returns></returns>
        public List<MES_WIP_PKG> GetPackageList(WipPkgItem parent)
        {
            List<MES_WIP_PKG> list = new();
            if (!parent.Package.IsNullOrEmpty())
            {
                list.Add(parent.Package);
            }
            foreach (var item in parent.Items)
            {
                item.Package.PARENT_SN = parent.Package?.SN;
                list.AddRange(GetPackageList(item));
            }
            return list;
        }
        #endregion
        #endregion Functions
        public override bool Close(bool needSaveHistoryLog = false)
@@ -552,5 +843,6 @@
            this.IsFinished = true;
            return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished;
        }
    }//endClass
}