服务端的TigerApi 框架,基于.NET6 2024 版本
Rodney Chen
2025-03-30 c049523656adcbab2d16df48faa94c672063c0dc
Tiger.Business.WMS/Common/Preparation.cs
@@ -1,304 +1,180 @@
using Rhea.Common;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tiger.Business.WMS.Extensions;
using Tiger.Business.WMS.Transaction;
using Tiger.IBusiness;
using Tiger.Model;
using Tiger.Model.Entitys.MES.Position;
using Tiger.Model.Extensions;
namespace Tiger.Business.WMS.Common
{
    /// <summary>
    /// 备料单发料事务
    /// 备料任务单据
    /// </summary>
    public class Preparation : WMSTransactionBase, IPreparation
    public class Preparation : IPreparation
    {
        public Preparation(DbClient mainDB, string userCode, string orgCode)
        {
            MainDB = mainDB;
            UserCode = userCode;
            OrgCode = orgCode;
        }
        #region Propertys & Variables
        private static object AddOrderLock { get; } = new object();
        public DbClient MainDB { get; set; }
        public string UserCode { get; set; }
        public long UserId { get; set; }
        public string OrgCode { get; set; }
        private DbClient _MainDB;
        public DbClient MainDB => _MainDB;
        private DbClient CommitDB;
        public Inventory CurInvItem { get; set; }
        public ScanShelfInfo CurScanShelf { get; set; }
        /// <summary>
        /// 跟客户端通讯时的指令,有具体功能定义,默认为Normal
        /// </summary>
        public string Command { get; set; } = "Normal";
        /// <summary>
        /// 是否需要临时存储数据库提交操作,待需要的时候再提交
        /// </summary>
        public bool NeedTemporaryStoreDBCommitAction { get; set; } = false;
        protected Dictionary<string, List<Action>> DBCommitList { get; set; } = new();
        public BIZ_WMS_PREP PrepOrder { get; set; }
        #endregion Propertys & Variables
        #region Functions
        public IPreparation Init(string id, string userCode, string apiHost, string orgCode)
        {
            TransID = id;
            UserCode = userCode;
            ApiHost = apiHost;
            OrgCode = orgCode;
            _MainDB = Biz.Db;
            return this;
        }
        /// <summary>
        /// 获取提交数据的DbClient对象
        /// 备料任务添加单据
        /// </summary>
        /// <param name="bizType"></param>
        /// <param name="orderNo"></param>
        /// <returns></returns>
        public DbClient GetCommitDB()
        public ApiAction<ScanOutput> AddOrder(BIZ_WMS_PREP.BIZ_TYPEs bizType, string orderNo)
        {
            return CommitDB ??= Biz.Db;
        }
        /// <summary>
        /// 增加暂存的数据库操作
        /// </summary>
        /// <param name="groupName">暂存的分组</param>
        /// <param name="action">暂存的数据库操作</param>
        public void AddCommitAction(string group, Action action)
        {
            //增加附加的保存内容
            if (!action.IsNullOrEmpty())
            lock(AddOrderLock)
            {
                if (!DBCommitList.ContainsKey(group))
                {
                    DBCommitList.Add(group, new List<Action>());
                }
                var commitList = DBCommitList[group];
                commitList.Add(action);
            }
        }
               var action = new ApiAction<ScanOutput>(new ScanOutput());
               // 查询是否已存在备料任务
               var src = MainDB.Queryable<BIZ_WMS_PREP_SRC>().Where(q => q.SOURCE_ORDER == orderNo &&
                                       SqlFunc.Subqueryable<BIZ_WMS_PREP>().Where(p => p.STATUS < BIZ_WMS_PREP.STATUSs.Sended.GetValue()).Any()).First();
               if (PrepOrder.IsNullOrEmpty())
               {
                   if (!src.IsNullOrEmpty())
                   {
                       PrepOrder = MainDB.Queryable<BIZ_WMS_PREP>().Where(q => q.ORDER_NO == src.ORDER_NO).IncludesAllFirstLayer().First();
                       //action.LocaleMsg = Biz.L("单据[{0}]的备料任务[{1}]加载成功,可以开始备料下架");
                       action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.AddOrder.LoadSuccess", orderNo, PrepOrder.ORDER_NO);
                       return action;
                   }
                   else
                   {
                       PrepOrder = new BIZ_WMS_PREP()
                       {
                            AUTH_ORG = OrgCode,
                            ORDER_NO = $"MP{DateTime.Now:yyMMdd}{(DateTime.Now - DateTime.Today).TotalSeconds:00000}",
                           BIZ_TYPE = bizType.GetValue(),
                           STATUS = BIZ_WMS_PREP.STATUSs.Init.GetValue(),
                       };
                   }
               }
               if (!src.IsNullOrEmpty() && src.ORDER_NO != PrepOrder.ORDER_NO)
               {
                   action.IsSuccessed = false;
                   //action.LocaleMsg = Biz.L("单据[{0}]已属于备料任务[{1}],无法添加到当前备料任务[{2}]中,如需切换备料业务,请退出后重新选择");
                   action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.AddOrder.NoFound", orderNo, src.ORDER_NO, PrepOrder.ORDER_NO);
                   return action;
               }
               // 查询单据明细,并合并到备料单的备料明细中
               var dtls = bizType.GetPickList(orderNo);
               if (dtls.Sum(q => q.QTY_ACT_REQ) <= 0)
               {
                   action.IsSuccessed = false;
                   //action.LocaleMsg = Biz.L("单据[{0}]未找到需要备料的行明细,请扫描正确的单据");
                   action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.AddOrder.NoNeedPrep", orderNo);
                   return action;
               }
        /// <summary>
        /// 暂存的数据库操作提交到数据库
        /// </summary>
        /// <param name="appendToSave">附加的保存内容</param>
        public void SaveCommitListToDB(Action appendToSave = null)
        {
            //增加附加的保存内容
            if (!appendToSave.IsNullOrEmpty())
            {
                if (!DBCommitList.ContainsKey("AppendSaveAction"))
                PrepOrder.SourceOrders = PrepOrder.SourceOrders ?? new();
                PrepOrder.SourceDetails = PrepOrder.SourceDetails ?? new();
                PrepOrder.Details = PrepOrder.Details ?? new();
                PrepOrder.Batchs = PrepOrder.Batchs ?? new();
                PrepOrder.SnList = PrepOrder.SnList ?? new();
                var srcOrder = new BIZ_WMS_PREP_SRC()
                {
                    DBCommitList.Add("AppendSaveAction", new List<Action>());
                }
                var appendList = DBCommitList["AppendSaveAction"];
                appendList.Add(appendToSave);
            }
            //如果不需要临时存储数据库提交操作,则把提交操作列表提交到数据库
            if (!NeedTemporaryStoreDBCommitAction)
            {
                //恢复临时存储标记为false
                NeedTemporaryStoreDBCommitAction = false;
                var dbTran = GetCommitDB().UseTran(() =>
                    AUTH_ORG = OrgCode,
                    ORDER_NO = PrepOrder.ORDER_NO,
                    SOURCE_CODE = bizType.GetSourceCode(),
                    SOURCE_ORDER = orderNo,
                    ADD_TIME = DateTime.Now,
                };
                PrepOrder.SourceOrders.Add(srcOrder);
                foreach (var dtl in dtls)
                {
                    //在同一个事务中保存所有工步的数据
                    foreach (var wipSn in DBCommitList.Keys.Where(q => q != "AppendSaveAction"))
                    dtl.AUTH_ORG = OrgCode;
                    var pdtl = PrepOrder.Details.Where(q => q.ITEM_CODE == dtl.ITEM_CODE).FirstOrDefault();
                    if (pdtl.IsNullOrEmpty())
                    {
                        foreach (var action in DBCommitList[wipSn])
                        pdtl = new ()
                        {
                            action.Invoke();
                        }
                            AUTH_ORG = OrgCode,
                            ORDER_NO = PrepOrder.ORDER_NO,
                            ORDER_LINE = (PrepOrder.Details.Max(q => q.LINE_CODE.ToInt32()) + 1).ToString(),
                            ITEM_CODE = dtl.ITEM_CODE,
                            SUBITEM_CODE = dtl.SUBITEM_CODE,
                            UNIT = dtl.UNIT,
                            PLAN_LOSS_QTY = dtl.PLAN_LOSS_QTY,
                            PLAN_LOSS_RATE = dtl.PLAN_LOSS_RATE,
                            IS_CALC_LOSS = dtl.IS_CALC_LOSS,
                            IS_WHOLE = dtl.IS_WHOLE,
                            SOURCE_CODE = dtl.SOURCE_CODE,
                            SOURCE_ORDER = dtl.SOURCE_ORDER,
                            SOURCE_LINE = dtl.SOURCE_LINE,
                            STATUS = BIZ_WMS_PREP_DTL.STATUSs.UnCalc.GetValue(),
                            FTY_CODE = dtl.FTY_CODE,
                            WS_CODE = dtl.WS_CODE,
                            LINE_CODE = dtl.LINE_CODE,
                            POST_CODE = dtl.POST_CODE,
                            OPER_CODE = dtl.OPER_CODE,
                            SEGMENT = dtl.SEGMENT,
                            SMT_NO = dtl.SMT_NO,
                            STATION_NO = dtl.STATION_NO,
                            FEEDER_NO = dtl.FEEDER_NO,
                            FEEDER_TYPE = dtl.FEEDER_TYPE,
                        };
                        PrepOrder.Details.Add(pdtl);
                    }
                    //附加的保存内容
                    if (DBCommitList.ContainsKey("AppendSaveAction"))
                    {
                        foreach (var action in DBCommitList["AppendSaveAction"])
                        {
                            action.Invoke();
                        }
                    }
                    pdtl.QTY_REQ += dtl.QTY_ACT_REQ;
                    PrepOrder.SourceDetails.Add(dtl);
                }
                var srcInfo = PrepOrder.SourceOrders.Select(q => new { q.SOURCE_CODE, q.SOURCE_ORDER }).DistinctBy(q => q.SOURCE_ORDER).OrderBy(q => q.SOURCE_ORDER);
                PrepOrder.SOURCE_CODE = string.Join(",", srcInfo.Select(q => q.SOURCE_CODE));
                PrepOrder.SOURCE_ORDER = string.Join(",", srcInfo.Select(q => q.SOURCE_ORDER));
                //使用统一的事务DB对象
                var db = Biz.Db;
                var dbTran = db.UseTran(() =>
                {
                    var o = db.Storageable(PrepOrder, UserCode).WhereColumns(t => new { t.ORDER_NO, t.GHOST_ROW }).ToStorage();
                    o.AsInsertable.ExecuteCommand();
                    o.AsUpdateable.IgnoreColumns(q => q.ID).UpdateColumns(q => new { q.SOURCE_CODE, q.SOURCE_ORDER, q.UPDATE_TIME, q.UPDATE_USER }).ExecuteCommand();
                    var so = db.Storageable(PrepOrder.SourceOrders, UserCode).WhereColumns(t => new { t.ORDER_NO, t.SOURCE_ORDER, t.GHOST_ROW }).ToStorage();
                    so.AsInsertable.ExecuteCommand();
                    var sd = db.Storageable(PrepOrder.SourceDetails, UserCode).WhereColumns(t => new { t.ORDER_NO, t.ITEM_CODE, t.GHOST_ROW }).ToStorage();
                    sd.AsInsertable.ExecuteCommand();
                    var d = db.Storageable(PrepOrder.Details, UserCode).WhereColumns(t => new { t.ORDER_NO, t.ITEM_CODE, t.GHOST_ROW }).ToStorage();
                    d.AsInsertable.ExecuteCommand();
                    d.AsUpdateable.IgnoreColumns(q => q.ID).UpdateColumns(q => new { q.QTY_REQ, q.UPDATE_TIME, q.UPDATE_USER }).ExecuteCommand();
                });
                if (dbTran.IsSuccess)
                {
                    //保存成功则清空提交操作列表
                    DBCommitList.Clear();
                    //action.LocaleMsg = Biz.L("备料任务[{0}添加]单据[{1}]成功,可以开始备料下架");
                    action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.AddOrder.LoadSuccess", PrepOrder?.ORDER_NO, orderNo);
                }
                else
                {
                    Logger.Default.Fatal(dbTran.ErrorException, "Database transaction save exception");
                    //抛出异常
                    throw dbTran.ErrorException;
                }
                return action;
            }
        }
        /// <summary>
        /// 上架
        /// </summary>
        /// <param name="inventory">要上架的库存对象</param>
        /// <param name="option">授权查询选项</param>
        /// <param name="targetLocation">要上架的储位代码</param>
        /// <param name="isTransfer">是否移库操作</param>
        /// <returns></returns>
        public Result PutOn(AuthOption option, string targetLocation)
        {
            var result = new Result(Result.Flags.Success);
            try
            {
                var location = Biz.Db.Queryable<V_WH_UNIT>().Where(q => q.LOCATION_CODE == targetLocation && q.AUTH_ORG == option.CurOrg).IncludesAllFirstLayer().First();
                if (!location.IsNullOrEmpty())
                {
                    var isTransfer = CurInvItem.Location.LOCATION_CODE != location.LOCATION_CODE;
                    foreach (var item in CurInvItem.Items)
                    {
                        item.STATUS = WMS_ITEM.STATUSs.InStore.GetValue();
                        item.SUPP_LOTNO = CurInvItem.Barcode.LotNo;
                        item.FIRST_IN_DATE = item.FIRST_IN_DATE <= DateTime.MinValue ? DateTime.Now : item.FIRST_IN_DATE;
                        item.PROD_DATE = item.PROD_DATE <= DateTime.MinValue ? item.FIRST_IN_DATE : item.PROD_DATE;
                        item.WH_ID = location.WH_ID;
                        item.REGION_ID = location.REGION_ID;
                        item.SHELF_ID = location.SHELF_ID;
                        item.LOCATION_ID = location.LOCATION_ID;
                        CurInvItem.History.Add(new WMS_ITEM_HIS(item, $"条码[{CurInvItem.SN}]{(isTransfer ? $"从储位[{CurInvItem?.Location?.LOCATION_CODE}]移库" : "上架")}到储位[{location.LOCATION_CODE}]成功{(isTransfer ? "" : $",操作单据[{item.TRANS_NO}]")}"));
                    }
                    foreach (var item in CurInvItem.Packages)
                    {
                        item.WH_ID = location.WH_ID;
                        item.REGION_ID = location.REGION_ID;
                        item.SHELF_ID = location.SHELF_ID;
                        item.LOCATION_ID = location.LOCATION_ID;
                    }
                    foreach (var item in CurInvItem.ItemsExt)
                    {
                        item.AUTH_ORG = option.OrgCode;
                        item.SN = CurInvItem.Barcode.SN;
                    }
                    //更新储存信息
                    CurInvItem.Warehouse = location.Warehouse;
                    CurInvItem.Region = location.Region;
                    CurInvItem.Shelf = location.Shelf;
                    CurInvItem.Location = location.Location;
                    //创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值
                    var _Items = CurInvItem.Items.Clone();
                    var _Packages = CurInvItem.Packages.Clone();
                    var _History = CurInvItem.History.Clone();
                    var _ItemsExt = CurInvItem.ItemsExt.Clone();
                    AddCommitAction("PutOn", () =>
                    {
                        //使用统一的事务DB对象
                        var db = GetCommitDB();
                        //数据保存逻辑
                        db.Storageable(_Items, UserCode).ExecuteCommand();
                        db.Storageable(_Packages, UserCode).ExecuteCommand();
                        db.Insertable(_History, UserCode).ExecuteCommand();
                        var x = db.Storageable(_ItemsExt, UserCode).ToStorage();
                        x.AsInsertable.ExecuteCommand();//不存在插入
                        x.AsUpdateable.ExecuteCommand();//存在更新
                    });
                }
                else
                {
                    result.Flag = Result.Flags.Failed;
                    result.LocaleMsg = new("WMS.WmsItem.PutOn.LocationNotExists", targetLocation);
                }
            }
            catch (Exception ex)
            {
                result.CatchExceptionWithLog(ex, Biz.L("WMS.WmsItem.PutOn.Exception", CurInvItem.SN, targetLocation));
            }
            return result;
        }
        /// <summary>
        /// 下架
        /// </summary>
        /// <param name="option">授权查询选项</param>
        /// <param name="status">下架后状态</param>
        /// <param name="clearLocation">是否清理储区货架储位信息</param>
        /// <returns></returns>
        public Result TakeDown(AuthOption option, WMS_ITEM.STATUSs status)
        {
            return TakeDown(CurInvItem.Items.ToDictionary(k => k.SN, v => v.QTY), option, status);
        }
        /// <summary>
        /// 下架
        /// </summary>
        /// <param name="inventory">要下架的库存对象</param>
        /// <param name="qtyList">要下架的最小包装数量字典,key:sn,value:qty</param>
        /// <param name="option">授权查询选项</param>
        /// <param name="status">下架后状态</param>
        /// <param name="clearLocation">是否清理储区货架储位信息</param>
        /// <returns></returns>
        public Result TakeDown(Dictionary<string, double> qtyList, AuthOption option, WMS_ITEM.STATUSs status)
        {
            var result = new Result(Result.Flags.Success);
            try
            {
                //如果要下架的数量字典为空则认为是全部下架
                qtyList = qtyList.IsNullOrEmpty(CurInvItem.Items.ToDictionary(k => k.SN, v => v.QTY));
                foreach (var item in CurInvItem.Items)
                {
                    if (qtyList.ContainsKey(item.SN) && qtyList[item.SN] > 0)
                    {
                        var downQty = qtyList[item.SN];
                        if (item.QTY > downQty)
                        {
                            item.QTY -= downQty;
                            var pkg = CurInvItem.Packages.First(q => q.SN == item.SN);
                            pkg.QTY = item.QTY;
                        }
                        else
                        {
                            item.STATUS = status.GetValue();
                            var pkg = CurInvItem.Packages.First(q => q.SN == item.SN);
                            pkg.PARENT_SN = null;
                            pkg.Parent = null;
                            //如果下架数量等于库存数量则清理储区货架储位信息,否则保留仓库信息
                            //item.WH_ID = null;
                            item.REGION_ID = null;
                            item.SHELF_ID = null;
                            item.LOCATION_ID = null;
                            //pkg.WH_ID = null;
                            pkg.REGION_ID = null;
                            pkg.SHELF_ID = null;
                            pkg.LOCATION_ID = null;
                        }
                        CurInvItem.History.Add(new WMS_ITEM_HIS(item, $"条码[{CurInvItem.SN}]从储位[{CurInvItem?.Location?.LOCATION_CODE}]下架数量[{downQty}]成功,状态[{item.STATUS.GetEnumDesc<WMS_ITEM.STATUSs>()}],操作单据[{item.TRANS_NO}]"));
                    }
                }
                CurInvItem.Packages = WMS_ITEM_PKG.UpdateQty(CurInvItem.Packages);
                //创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值
                var _Items = CurInvItem.Items.Clone();
                var _Packages = CurInvItem.Packages.Clone();
                var _History = CurInvItem.History.Clone();
                AddCommitAction("TakeDown", () =>
                {
                    //使用统一的事务DB对象
                    var db = GetCommitDB();
                    //数据保存逻辑
                    db.Updateable(_Items, UserCode).ExecuteCommand();
                    db.Insertable(_History, UserCode).ExecuteCommand();
                    db.Updateable(_Packages, UserCode).ExecuteCommand();
                });
            }
            catch (Exception ex)
            {
                result.CatchExceptionWithLog(ex, Biz.L("WMS.WmsItem.TakeDown.Exception", CurInvItem.SN, CurInvItem?.Location?.LOCATION_CODE));
            }
            return result;
        }
        #endregion Functions
        public override bool Close(bool needSaveHistoryLog = false)
        {
            //needSaveHistoryLog = true;
            //保存操作日志
            this.IsFinished = true;
            return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished;
        }
    }
}