using Kingdee.CDP.WebApi.SDK;
using Kingdee.CDP.WebApi.SDK.DataEntity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPOI.POIFS.FileSystem;
using Org.BouncyCastle.Asn1.Cmp;
using RB_MES_API.Context;
using RB_MES_API.Controllers.Cloud;
using RB_MES_API.Models;
using RB_MES_API.Models.Cloud;
using RB_MES_API.Models.Pangu;
using RB_MES_APICore.Controllers.Interface;
using RB_MES_APICore.Models;
using RB_MES_APICore.Models.Cloud;
using RB_MES_APICore.Models.Pangu;
using System.Data;
using System.Data.Entity;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace RB_MES_API.Controllers
{
    /// <summary>
    /// 封装金蝶Cloud的API接口
    /// </summary>
    [ApiController]
    [Route("[controller]")]
    public class EnpowerApiController : ControllerBase
    {
        private readonly RBContext _context;
        private readonly IShareController _shareController;
        private readonly IKDCloudHttpClient _KdhttpClient;
        private readonly IChiledSelect _chiledselect;
        private readonly IKDSqlHelper _kdsqlhelper;
        private delegate Task<string> _PugAsync(Dictionary<string, object> pairs, CloudBillQuery billquery);
        private delegate Task<BreakData> _SaveAsync<T>(string formid, T inputdata) where T : class;
        public delegate Task<int> _SaveReciveDataBackAsync(List<ReciveDataBack> dataBacks);
        //private IMesApiSTKTransferDirectsService _STKTransferDirectsService;
        ///// <summary>
        ///// 需要查询的请求列表
        ///// </summary>
        //private IList<CloudBillQuery> queryJsons = new List<CloudBillQuery>();
        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="context"></param>
        /// <param name="shareController"></param>
        /// <param name="kdhttpClient"></param>
        /// <param name="chiledSelect"></param>
        /// <param name="kdsqlhelper"></param>
        public EnpowerApiController(RBContext context, IShareController shareController, IKDCloudHttpClient kdhttpClient,
            IChiledSelect chiledSelect, IKDSqlHelper kdsqlhelper )
        {
            _context = context;
            _shareController = shareController;
            _KdhttpClient = kdhttpClient;
            _chiledselect = chiledSelect;
            _kdsqlhelper = kdsqlhelper;
            //_STKTransferDirectsService = STKTransferDirectsService;
            //_pushmesinterface = pushmesinterface;
        }

        /// <summary>
        /// 编号为A、B、C的枚举，保存值为0、1、2
        /// </summary>
        public enum DropDownText
        {
            A = 0,      //按系统默认
            B = 1,      //良品退料
            C = 2,      //来料不良退料
            D = 3      //作业不良退料
        }
        /// <summary>
        /// 整数枚举，标号为小数点位数
        /// </summary>
        private long IntNumber(int v)
        {
            long val = 0;
            switch (v)
            {
                case 1:
                    {
                        val = 10;
                        break;
                    }
                case 2:
                    {
                        val = 100;
                        break;
                    }
                case 3:
                    {
                        val = 1000;
                        break;
                    }
                case 4:
                    {
                        val = 10000;
                        break;
                    }
                case 5:
                    {
                        val = 100000;
                        break;
                    }
                case 6:
                    {
                        val = 1000000;
                        break;
                    }
                case 7:
                    {
                        val = 10000000;
                        break;
                    }
                case 8:
                    {
                        val = 100000000;
                        break;
                    }
                case 9:
                    {
                        val = 1000000000;
                        break;
                    }
                case 10:
                    {
                        val = 10000000000;
                        break;
                    }
            }
            return val;
        }
        public class RemoveAPIModel
        {
            public string formid { get; set; }
            public RemoveAPIData data { get; set; } = new RemoveAPIData();
        }
        public class RemoveAPIData
        {
            public int CreateOrgId { get; set; } = 0;
            public string[] Numbers { get; set; }
            public string Ids { get; set; } = string.Empty;
            public bool NetworkCtrl { get; set; } = false;
        }

        /// <summary>
        /// MES从ERP里查询的JSON结构，逻辑判断符都是‘=’
        /// </summary>
        public class GetErpParameters
        {
            /// <summary>
            /// 查询类型
            /// </summary>
            public string DocType { get; set; } = string.Empty;
            /// <summary>
            /// 查询参数
            /// </summary>
            public Dictionary<string, object> KeyValues { get; set; }
        }


        /// <summary>
        /// 根据SelectClumnConvert创建一个新的空表，准备装返回数据。暂时不用............
        /// </summary>
        /// <param name="clumnConvert">SelectClumnConvert</param>
        /// <returns></returns>
        private DataTable CreateDatatable(List<SelectClumnConvert> clumnConvert)
        {
            DataTable dataTable = new DataTable();
            var selectconvert = clumnConvert
                .Select(s => new { s.FIndexID, s.FDesTableName })
                .DistinctBy(s => new { s.FIndexID, s.FDesTableName })
                .OrderBy(s => s.FIndexID)
                .ToList();
            foreach (var selectClumnConvert in selectconvert)
            {
                dataTable.Columns.Add(selectClumnConvert.FDesTableName, typeof(string));
            }
            return dataTable;
        }
        private string UpdateDataRow_Unit(BD_UNIT unit, DataRow row)
        {
            string result = "/*dialect*/UPDATE kdCloud_BD_UNIT SET ";
            string usql = "";
            Type type = typeof(BD_UNIT);
            PropertyInfo[] propertyInfos = type.GetProperties();
            for (int i = 0; i < propertyInfos.Length; i++)
            {
                PropertyInfo propertyInfo = propertyInfos[i];
                string colname = propertyInfo.Name;
                if (row.Table.Columns.Contains(colname))
                {
                    object v = propertyInfo.GetValue(unit, null);
                    if (row[colname].Equals(v))
                    {
                        string sform = string.Format(colname + "='{0}'", v);
                        usql += usql == "" ? sform : "," + sform;
                    }
                    ;
                }
            }
            if (usql != "")
            {
                return result + usql + string.Format(" where FUNITID={0}", unit.FUNITID);
            }
            return "";
        }
        private void AddDataRow_Unit(BD_UNIT unit, DataTable table)
        {
            DataRow row = table.NewRow();
            Type type = typeof(BD_UNIT);
            PropertyInfo[] propertyInfos = type.GetProperties();
            for (int i = 0; i < propertyInfos.Length; i++)
            {
                PropertyInfo propertyInfo = propertyInfos[i];
                string colname = propertyInfo.Name;
                if (table.Columns.Contains(colname))
                {
                    row[colname] = propertyInfo.GetValue(unit, null);
                }
            }
            table.Rows.Add(row);
            table.AcceptChanges();
        }
        /// <summary>
        /// 外部系统要溜个弯，已弃用！
        /// </summary>
        /// <param name="formtypeid">沃比系统配置的可查询接口ID</param>
        /// <returns></returns>
        [HttpGet]
        [Route("GetCustomRequestTBAsync")]
        public async Task<string> GetCustomRequestTBAsync(int formtypeid)
        {
            return await _chiledselect.GetCustomRequestTBAsync(formtypeid);
        }
        /// <summary>
        /// 云星空计量单位变动反向同步到沃比系统_初始化
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("UnitChangedinfo")]
        public async Task<string> UnitChangedinfo()
        {
            string messstr = string.Empty;
            string fileds = "FUNITID,FNumber,FName,FIsBaseUnit,FPrecision,FDocumentStatus,FForbidStatus,FConvertDenominator,FConvertNumerator,FRoundType";
            //从云星空中把所有计量单位获取过来
            BillQuery queryJson = new BillQuery()
            {
                FieldKeys = fileds,
                FormId = "BD_UNIT"
            };
            //从云星空查询
            string result = _KdhttpClient.SDKBillQuery(queryJson);
            //如果访问正常
            if (_KdhttpClient.SDKBillQueryStatus(result))
            {
                //将获取到的结果转为List<BD_UNIT>
                List<BD_UNIT> bDs = _shareController.GetNewObjForChild<BD_UNIT>(fileds, result, ref messstr);
                //访问接口
                UnitChanged(JsonConvert.SerializeObject(bDs));
            }
            if (string.IsNullOrEmpty(messstr))
            {
                //从沃比系统查一下，如果成功了就把所有数据展示出来
                DataTable table = GetAllUnit();
                //把table转为JSON
                messstr = JsonConvert.SerializeObject(table);
            }

            return messstr;
        }
        /// <summary>
        /// 云星空计量单位变动反向同步到沃比系统
        /// 前提：云监察使用沃比系统插件，通过插件来调用
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpGet]
        [Route("UnitChanged")]
        public BreakData UnitChanged(string data)
        {
            BreakData breakData = new BreakData() { Status = false };
            string reason = string.Empty;
            List<BD_UNIT> listunit = _shareController.DataConvertClass<BD_UNIT>(data, ref reason);
            if (listunit == null)
            {
                breakData.Message = reason;
                return breakData;
            }
            DataTable table = GetAllUnit();
            DataTable newtb = table.Clone();
            StringBuilder stringBuilder = new StringBuilder();  //更新SQL
            //包裹一个可能产生错误的代码块，以防止将错误返回给了Cloud
            try
            {
                //对比数据，产生增加、更新操作
                if (table.Rows.Count > 0)
                {
                    foreach (BD_UNIT uNIT in listunit)
                    {
                        long id = uNIT.FUNITID;
                        DataRow[] rows = table.Select(string.Format("FUnitID={0}", id));
                        if (rows.Length > 0)
                        {
                            string usql = UpdateDataRow_Unit(uNIT, rows.FirstOrDefault()!);
                            if (!string.IsNullOrEmpty(usql)) { stringBuilder.Append(usql); }
                        }
                        else
                        {
                            AddDataRow_Unit(uNIT, newtb);
                        }
                    }
                }
                else//全是新增
                {
                    foreach (BD_UNIT unit in listunit)
                    {
                        AddDataRow_Unit(unit, newtb);
                    }
                }
                if (newtb != null) { _kdsqlhelper.InsertBill(newtb, "kdCloud_BD_UNIT", ref reason, false); }
                if (string.IsNullOrEmpty(reason))
                {
                    //确保，就算没法有新行，也会执行
                    if (stringBuilder.Length > 0) { reason = _kdsqlhelper.ExecuteNonQueryAsync(stringBuilder.ToString(), CommandType.Text, null, false).Result; }
                }
                if (string.IsNullOrEmpty(reason))
                {
                    //重新加入缓存
                    Thread uthread = new Thread(_shareController.AddUnitCache);
                    uthread.IsBackground = true;
                    uthread.Start();
                    breakData.Status = true;
                }
            }
            catch (Exception ex)
            {
                reason = ex.Message;
                //写入错误日志
                LogHelper.WriteLog(string.Format("UnitChanged方法错误：\"{0}\"接收指令的数据是：\"{1}", reason, data));
                //throw;
            }
            //不需要返回数据包
            breakData.Message = reason;
            return breakData;
        }
        private DataTable GetAllUnit()
        {
            DataTable table = new DataTable();
            string sql = "/*dialect*/SELECT * FROM kdCloud_BD_UNIT";
            string reason = string.Empty;
            string costomsql = ApiSettingsHelper.GetConfig("CustomRequestRemoteSql");
            if (costomsql.ToUpper() == "Y")
            {
                string result = _chiledselect.GetCustomReaderAsync(sql, "ExecuteDataSet").Result;
                try
                {
                    if (!string.IsNullOrWhiteSpace(result) && result != "[]")
                    {
                        //先将返回数据格式化
                        JObject jobj = (JObject)JsonConvert.DeserializeObject(result);
                        if (jobj.Count > 0)
                        {
                            string json = JsonConvert.SerializeObject(jobj["Table"]);
                            table = _shareController.JsonToDatatable(json, ref reason);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.WriteLog(string.Format("GetCustomRequestBillAsync发生错误：\r\n{0}\r\n{1}", ex.Message, result));
                }
            }
            else
            {
                DataSet dataSet = _kdsqlhelper.GetDataSet(CommandType.Text, sql, null, ref reason);
                if (dataSet != null)
                {
                    if (dataSet.Tables.Count > 0)
                    {
                        table = dataSet.Tables[0];
                    }
                }
            }
            return table;
        }

        ///// <summary>
        ///// MES下达日计划后，同步开工状态到erp测试
        ///// </summary>
        ///// <param name="parm"></param>
        ///// <returns></returns>
        //[HttpPost]
        //[Route("UpdatePRDMO_Test")]
        //public async Task<string> UpdateMOStatus_Test()
        //{
        //    List<PRDMO> mos = new List<PRDMO>()
        //    {
        //        new PRDMO()
        //        {
        //            ERP_MO="Test_WO2302070003",
        //            MO_SEQ=1
        //        }
        //        //,new PRDMO()
        //        //{
        //        //    ERP_MO="MO000034",
        //        //    MO_SEQ=2
        //        //}
        //        //,new PRDMO()
        //        //{
        //        //    ERP_MO="MO000027",
        //        //    MO_SEQ=1
        //        //}
        //    };

        //    RequestParm parm = new RequestParm() { DocType = "MOToStart" };
        //    parm.data = JsonConvert.SerializeObject(mos);
        //    string result = await UpdateMOStatus(parm);
        //    return result;
        //}
        /// <summary>
        /// MES下达日计划后，同步开工状态到erp
        /// </summary>
        /// <param name="parm">请提供FromBody</param>
        /// <returns></returns>
        [HttpPost]
        [Route("UpdatePRDMO")]
        public async Task<string> UpdateMOStatus([FromBody] RequestParm parm)
        {
            BreakData breakData = new BreakData() { Status = false };
            string DocType = parm.DocType;
            if (DocType.ToLower() != "motostart")
            {
                breakData.Message = "接口类型不正确";
                return JsonConvert.SerializeObject(breakData);
            }
            string result = string.Empty;
            string data = parm.data.ToString();
            string reason = string.Empty;
            //先固定好时间
            string guid = Guid.NewGuid().ToString("B");
            _shareController._SqlloggerInvoke(2, DocType, "从接口UpdatePRDMO收到...", data, true, guid);
            try
            {
                List<PRDMO> mos = _shareController.DataConvertClass<PRDMO>(data, ref reason);
                if (mos == null)
                {
                    breakData.Message = reason;
                    goto E;
                }
                //准备校验星空是否有这些数据
                string fieldkeys = "FBillNo,FID,FTreeEntity_FEntryID,FTreeEntity_FSeq";
                System.Collections.ArrayList filterstr = new System.Collections.ArrayList();
                foreach (PRDMO mo in mos)
                {
                    filterstr.Add(string.Format("FBillNo='{0}' AND FTreeEntity_FSeq={1}", mo.ERP_MO, mo.MO_SEQ));
                }
                //组装云星空查询
                BillQuery queryJson = new BillQuery
                {
                    FilterString = string.Format("({0})", string.Join(") or (", filterstr.ToArray())),
                    FieldKeys = fieldkeys,
                    FormId = "PRD_MO"
                };
                //从云星空查询
                result = _KdhttpClient.SDKBillQuery(queryJson);
                //如果访问正常
                if (_KdhttpClient.SDKBillQueryStatus(result))
                {
                    DataTable table = _shareController.JsonConvertDatatable("FBillNo,FID,FEntryID,FSeq", result, ref reason);
                    if (table != null)
                    {
                        result = await ExecuteOperation(table);
                        //检测是否成功
                        if (_shareController.CloudExecuteSaveStatus(result, ref reason))
                        {
                            breakData.Status = true;
                            breakData.Message = result;
                        }
                        else
                        {
                            breakData.Status = false;
                            breakData.Message = reason;
                        }
                    }
                    else
                    {
                        breakData.Status = false;
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Status = false;
                    breakData.Message = reason;
                }

                //如果有错误发生，就会跑到catch中去...
            }
            catch (Exception ex)
            {
                reason = ex.Message;
                breakData.Message = reason;
                //同时写入日志
                //LogHelper.WriteLog(string.Format("UpdatePRDMO接口处理发生错误：\"{0}\"接收到的数据是：\"{1}", reason, data));
                //throw;
            }
            E:

            result = JsonConvert.SerializeObject(breakData);
            _shareController._SqlloggerInvoke(2, DocType, !breakData.Status ? result : "", data, breakData.Status, guid);
            return result;
        }

        private async Task<string> ExecuteOperation(DataTable table)
        {
            string result = string.Empty;
            var pslist = (from a in table.AsEnumerable()
                          select new
                          {
                              FID = a.Field<string>("FID")
                          }).Distinct().ToList();
            List<int> ints = new List<int>();
            List<PkEntryId> pklist = new List<PkEntryId>();
            foreach (var id in pslist)
            {
                int moid = int.Parse(id.FID);
                ints.Add(moid);
                var pk = (from b in table.AsEnumerable().Where(s => int.Parse(s.Field<string>("FID")) == moid)
                          select new
                          {
                              RowID = b.Field<string>("FEntryID")
                          }).ToList();
                string stringBuilder = string.Empty;
                foreach (var str in pk)
                {
                    stringBuilder += stringBuilder == string.Empty ? str.RowID : "," + str.RowID;
                }
                pklist.Add(new PkEntryId()
                {
                    Id = moid.ToString(),
                    EntryIds = stringBuilder
                });
            }
            RequestData data = new RequestData()
            {
                Ids = string.Join(",", ints.ToArray()),
                PkEntryIds = pklist
            };

            if (!LocalStaticRequest.Islogin)
            {
                await _KdhttpClient.LoginErp();
            }
            if (!LocalStaticRequest.Islogin)
            {
                return result;
            }
            //提交给云星空
            string jsonstr = JsonConvert.SerializeObject(data);
            List<object> Parameters = new List<object>
            {
                "PRD_MO",
                "ToStart",
                jsonstr
            };
            //string content = JsonConvert.SerializeObject(Parameters);
            //string url = LocalStaticRequest.GetSystemProfile(4, "TokenUrl");
            //string myurl = url + "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExcuteOperation.common.kdsvc";
            //result = _KdhttpClient.AsyncRequest(myurl, content);

            return result;
        }

        /// <summary>
        /// MES下达日计划后，将工单号提交给沃比
        /// 目的是沃比插件使用,当前已弃用...
        /// </summary>
        /// <param name="parm">请提供FromBody</param>
        /// <returns></returns>
        [HttpPost]
        [Route("BackPRDMO")]
        public async Task<string> UpdatePRDMO([FromBody] RequestParm parm)
        {
            BreakData breakData = new BreakData() { Status = false };
            string DocType = parm.DocType;
            if (parm.DocType.ToLower() != "update_wplan_mn")
            {
                breakData.Message = "接口类型不正确";
                return JsonConvert.SerializeObject(breakData);
            }
            string result = string.Empty;
            string data = parm.data.ToString();
            string reason = string.Empty;
            //先固定好时间
            string guid = Guid.NewGuid().ToString("B");
            _shareController._SqlloggerInvoke(2, DocType, "从接口BackPRDMO收到...", data, true, guid);
            //包裹一个可能产生错误的代码块
            try
            {
                List<PRDMO> mos = _shareController.DataConvertClass<PRDMO>(data, ref reason);
                if (mos == null)
                {
                    breakData.Message = reason;
                    goto E;
                }
                //准备校验星空是否有这些数据
                string fieldkeys = "FBillNo,FID,FTreeEntity_FEntryID,FTreeEntity_FSeq";
                System.Collections.ArrayList filterstr = new System.Collections.ArrayList();
                foreach (PRDMO mo in mos)
                {
                    filterstr.Add(string.Format("FBillNo='{0}' AND FTreeEntity_FSeq={1}", mo.ERP_MO, mo.MO_SEQ));
                }
                //组装云星空查询
                BillQuery queryJson = new BillQuery
                {
                    FilterString = string.Format("({0})", string.Join(") or (", filterstr.ToArray())),
                    FieldKeys = fieldkeys,
                    FormId = "PRD_MO"
                };
                //从云星空查询
                result = _KdhttpClient.SDKBillQuery(queryJson);
                //如果访问正常
                if (_KdhttpClient.SDKBillQueryStatus(result))
                {
                    DataTable table = _shareController.JsonConvertDatatable("FBillNo,FID,FEntryID,FSeq", result, ref reason);
                    if (table != null)
                    {
                        //产生新的日计划记录
                        List<LockPRD_MO> locks = (from a in table.AsEnumerable()
                                                  select new LockPRD_MO
                                                  {
                                                      ERP_MO = a.Field<string>("FBillNo")!,
                                                      MO_SEQ = a.Field<string>("FSeq"),
                                                      MOID = a.Field<string>("FID"),
                                                      MOEntryID = a.Field<string>("FEntryID"),
                                                      TRX_DATE = DateTime.Now.ToString()
                                                  }).ToList();

                        //写入沃比系统
                        _context.r_LockPRD_MO!.AddRange(locks);
                        int q = await _context.SaveChangesAsync();
                        if (q > 0)
                        {
                            breakData.Status = true;
                            breakData.DataSet = locks;
                        }
                    }
                    else
                    {
                        breakData.Status = false;
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Status = false;
                    breakData.Message = reason;
                }

                //如果有错误发生，就会跑到catch中去...
            }
            catch (Exception ex)
            {
                reason = ex.Message;
                breakData.Message = reason;
                //同时写入日志
                //LogHelper.WriteLog(string.Format("UpdatePRDMO接口处理发生错误：\"{0}\"接收到的数据是：\"{1}", reason, data));
                //throw;
            }
            E:
            result = JsonConvert.SerializeObject(breakData);
            _shareController._SqlloggerInvoke(2, DocType, !breakData.Status ? result : "", data, breakData.Status, guid);
            return result;
            //return JsonConvert.SerializeObject(breakData);
        }
        /// <summary>
        /// 获取MES系统日计划工单
        /// 可能部分插件还有使用，暂不取消
        /// </summary>
        /// <param name="data">LockPRD_MO列表的JSON字符串</param>
        /// <returns></returns>
        [HttpGet]
        [Route("GetLockMOsAsync")]
        public async Task<string> GetLockMOsAsync(string data)
        {
            BreakData breakData = new BreakData() { Status = false };
            string reason = string.Empty;
            //包裹一个可能产生错误的代码块，以防止将错误返回给了Cloud
            try
            {
                List<LockPRD_MO> mos = _shareController.DataConvertClass<LockPRD_MO>(data, ref reason);
                if (mos == null)
                {
                    breakData.Message = reason;
                    return JsonConvert.SerializeObject(breakData);
                }
                //EF处理起来更慢，使用直接SQL
                System.Collections.ArrayList stringBuilder = new System.Collections.ArrayList();
                foreach (LockPRD_MO po in mos)
                {
                    string number = po.ERP_MO;
                    int seq = int.Parse(po.MO_SEQ);
                    int moid = int.Parse(po.MOID);
                    int moentryid = int.Parse(po.MOEntryID);
                    System.Collections.ArrayList arrayList = new System.Collections.ArrayList();
                    if (number != "") { arrayList.Add(string.Format("ERP_MO='{0}'", number)); }
                    if (seq != 0) { arrayList.Add(string.Format("MO_SEQ={0}", seq)); }
                    if (moid != 0) { arrayList.Add(string.Format("MOID={0}", moid)); }
                    if (moentryid != 0) { arrayList.Add(string.Format("MOEntryID={0}", moentryid)); }
                    stringBuilder.Add(string.Join(" AND ", arrayList.ToArray()));
                }
                string sql = string.Format(@"/*dialect*/SELECT * FROM r_LockPRD_MO WHERE ({0})", string.Join(") OR (", stringBuilder.ToArray()));
                DataSet dataSet = _kdsqlhelper.GetDataSet(CommandType.Text, sql, null, ref reason, false);
                if (dataSet == null) { return JsonConvert.SerializeObject(breakData); }
                if (dataSet.Tables.Count == 0) { return JsonConvert.SerializeObject(breakData); }
                if (dataSet.Tables[0].Rows.Count == 0) { return JsonConvert.SerializeObject(breakData); }

                List<LockPRD_MO> locks = (from a in dataSet.Tables[0].AsEnumerable()
                                          select new LockPRD_MO
                                          {
                                              FID = a.Field<int>("FID"),
                                              ERP_MO = a.Field<string>("ERP_MO")!,
                                              MO_SEQ = a.Field<string>("MO_SEQ"),
                                              MOID = a.Field<string>("MOID"),
                                              MOEntryID = a.Field<string>("MOEntryID"),
                                              TRX_DATE = a.Field<string>("TRX_DATE")
                                          }).ToList();
                breakData.DataSet = locks;
                breakData.Status = true;
            }
            catch (Exception ex)
            {
                reason = ex.Message;
                breakData.Message = reason;
                //同时写入日志
                LogHelper.WriteLog(string.Format("GetLockMOsAsync接口处理发生错误：\"{0}\"接收到的数据是：\"{1}", reason, data));
            }

            //测试序列化与反序列化
            string result = JsonConvert.SerializeObject(breakData);
            return result;
        }

        /// <summary>
        /// 从ERP中查询数据
        /// </summary>
        /// <param name="data">查询条件数据包</param>
        /// <returns></returns>
        [HttpGet]
        [Route("GetERPData")]
        public async Task<string> GeterpdataAsync(string data)
        {
            List<FunctionList> functions = await _shareController.GetAPIList<FunctionList>();
            int fid = functions.Where(s => s.FUsing && s.FFunctionName == "GetERPData")
                .Select(s => s.FID).FirstOrDefault();
            string DocType = string.Empty;
            BreakData breakData = new BreakData();
            string result = string.Empty;
            string reason = string.Empty;
            try
            {
                if (!string.IsNullOrEmpty(data))
                {
                    GetErpParameters erpParameters = JsonConvert.DeserializeObject<GetErpParameters>(data)!;
                    if (erpParameters != null)
                    {
                        DocType = erpParameters.DocType;
                        if (!string.IsNullOrEmpty(DocType))
                        {
                            //先固定好时间
                            string guid = Guid.NewGuid().ToString("B");
                            _shareController._SqlloggerInvoke(2, DocType, "从接口GetERPData收到...", data, true, guid);

                            List<CloudBillQuery> querjson = await _shareController.GetAPIList<CloudBillQuery>();
                            querjson = querjson.Where(s => s.GetFormID.FFunctionID == fid && s.GetFormID.FDocType == DocType).ToList();
                            if (querjson.Any())
                            {
                                CloudBillQuery billquery = querjson.FirstOrDefault()!;
                                string actname = billquery.GetFormID.FActionName;
                                if (!actname.Contains("GeterpdataAsync"))
                                {
                                    //找到这个方法，并另起线程去执行它...
                                    string fk = billquery.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                                    string fs = billquery.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                                    result = await _chiledselect.GetCustomRequestBillAsync(fk, fs, billquery.FOrderString, erpParameters.KeyValues);
                                    if (result == null)
                                    {
                                        breakData.Status = false;
                                        breakData.Message = "查询有误，参见沃比错误日志";
                                    }
                                    else
                                    {
                                        breakData.Status = true;
                                        breakData.Message = reason;
                                        if (result == "[]")
                                        {
                                            breakData.DataSet = "";
                                        }
                                        else
                                        {
                                            //JSON格式化!!!
                                            breakData.DataSet = new JsonSerializer().Deserialize(new JsonTextReader(new StringReader(result)));
                                        }
                                    }
                                }
                                else
                                {
                                    result = await PugAsync(erpParameters.KeyValues, billquery);
                                    //判断是对还是错
                                    if (_KdhttpClient.SDKBillQueryStatus(result))
                                    {
                                        //转换为客户端需要的格式
                                        switch (DocType)
                                        {
                                            case "XCL":
                                                //先将无标题的返回数据转为tb，然后使用类型转换功能
                                                DataTable table = _shareController.JsonConvertDatatable(billquery.FFieldKeys, result, ref reason);
                                                if (string.IsNullOrEmpty(reason))
                                                {
                                                    string xcl = JsonConvert.SerializeObject(table);
                                                    breakData.DataSet = _shareController.GetNewObj<XCL>(fid, DocType, xcl, ref reason);
                                                }
                                                break;
                                            case "Material":
                                                DataTable mtable = _shareController.GetInputDataTable(fid, DocType, result, ref reason);
                                                if (string.IsNullOrEmpty(reason))
                                                {
                                                    string mxcl = JsonConvert.SerializeObject(mtable);
                                                    breakData.DataSet = _shareController.GetNewObj<TB_BS_MTRL>(fid, DocType, mxcl, ref reason);
                                                }
                                                break;
                                        }
                                        if (reason == "")
                                        {
                                            breakData.Status = true;
                                        }
                                        else
                                        {
                                            breakData.Status = false;
                                            breakData.Message = reason;
                                        }
                                    }
                                    else
                                    {
                                        breakData.Status = false;
                                        breakData.Message = reason;
                                    }
                                }
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "CloudBillQuery还未配置";
                            }
                            _shareController._SqlloggerInvoke(2, DocType, !breakData.Status ? JsonConvert.SerializeObject(breakData) : "", data, breakData.Status, guid);
                        }
                        else
                        {
                            breakData.Status = false;
                            breakData.Message = "提供的参数中必须包含DocType";
                        }
                    }
                }
                else
                {
                    breakData.Status = false;
                    breakData.Message = "不接受空的数据包";
                }
            }
            catch (Exception e)
            {
                breakData.Status = false;
                breakData.Message = e.Message;
                //throw;
                //LogHelper.WriteLog(string.Format("GetERPData接口发生错误：{0}", e.Message));
            }
            return JsonConvert.SerializeObject(breakData);
        }

        /// <summary>
        /// 匹配合适的参数，从金蝶Cloud中查询数据
        /// </summary>
        /// <param name="pairs">输入参数</param>
        /// <param name="billquery"></param>
        /// <returns></returns>
        private async Task<string> PugAsync(Dictionary<string, object> pairs, CloudBillQuery billquery)
        {
            string filterstr = billquery.FFiledString;
            //解析KeyValues
            string FilterString = await GetErpDataPar(billquery.GetFormID.FFunctionID, pairs, billquery.GetFormID.FDocType, filterstr);
            BillQuery queryJson = new BillQuery
            {
                FilterString = FilterString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg),
                FieldKeys = billquery.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg),
                FormId = billquery.GetFormID.FDBName,
                Limit = billquery.FLimit,
                OrderString = billquery.FOrderString,
                StartRow = billquery.FStartRow,
                SubSystemId = billquery.FSubSystemID,
                TopRowCount = billquery.FTopRowCount
            };
            //从云星空查询
            return _KdhttpClient.SDKBillQuery(queryJson);
        }

        /// <summary>
        /// MES传过来的参数转义
        /// </summary>
        /// <param name="fid">FunctionID</param>
        /// <param name="inputKeyValues">输入的参数和值</param>
        /// <param name="DocType">查询类别</param>
        /// <param name="filterstr"></param>
        /// <returns></returns>
        private async Task<string> GetErpDataPar(int fid, Dictionary<string, object> inputKeyValues, string DocType, string filterstr)
        {
            string result = filterstr;
            //根据SelectJoinID获取参数列表
            var sj1 = await _shareController.GetAPIList<SelectJoinID>();
            var sj = sj1.Where(s => s.FFunctionID == fid && s.FDocType.Equals(DocType)).ToList();
            if (sj != null)
            {
                foreach (var s in sj)
                {
                    string pars = s.FOptcode;
                    string[] strings = pars.Split('|');
                    if (strings.Length > 0)
                    {
                        foreach (string s2 in strings)
                        {
                            string[] strings1 = s2.Split('=');
                            if (strings1.Length == 2)
                            {
                                result = result.Replace(strings1[0], inputKeyValues[strings1[0]].ToString());
                                //result += string.IsNullOrEmpty(result) ? strings1[1] + "='" + inputKeyValues[strings1[0]] + "'" : 
                                //    " AND " + strings1[1] + "='" + inputKeyValues[strings1[0]] + "'";
                            }
                            else
                            {
                                result = result.Replace(strings1[0], inputKeyValues[strings1[0]].ToString());
                                //result += string.IsNullOrEmpty(result) ? s2 + "='" + inputKeyValues[s2] + "'":
                                //    " AND " + s2 + "='" + inputKeyValues[s2] + "'";
                            }
                        }
                    }

                }
            }
            return result;
        }
        /// <summary>
        /// 批量提交单据输入参数校验
        /// </summary>
        /// <param name="DocType">接口类型,云星空的FormId,传入时不必区分大小写</param>
        /// <param name="data">单据数据包</param>
        /// <param name="reason">当有错误时返回的消息</param>
        /// <returns></returns>
        private bool PostAsyncCheck(string DocType, string data, ref string reason)
        {
            reason = "";
            bool result = true;
            List<DataSaveBatch> list = _shareController.GetAPIList<DataSaveBatch>().Result;
            if (!list.Where(s => s.GetForm.FDocType == DocType).Any())
            {
                result = false;
                reason += "未配置JSON格式数据";
            }
            if (string.IsNullOrEmpty(DocType))
            {
                result = false;
                reason += reason == "" ? "必须指定接口类型" : "\n" + "必须指定接口类型";
            }
            if (string.IsNullOrEmpty(data))
            {
                result = false;
                reason += reason == "" ? "不接受空的数据包" : "\n" + "不接受空的数据包";
            }
            return result;
        }
        /// <summary>
        /// 根据接口类型，返回关于它的定义项
        /// </summary>
        /// <param name="DocType">接口类型</param>
        /// <returns></returns>
        private List<FormIDType> GetAllFormIDTypeAsync(string DocType)
        {
            List<FormIDType> result = _shareController.GetAPIList<FormIDType>().Result
                    .Where(s => s.FDocType.Equals(DocType)).ToList();
            return result;
        }
        /// <summary>
        /// 单个单据的保存接口
        /// </summary>
        /// <typeparam name="T">要提交保存的类</typeparam>
        /// <param name="formid">云星空的FormId</param>
        /// <param name="inputdata">整理好的表单</param>
        /// <returns></returns>
        private async Task<BreakData> SaveAsync<T>(string formid, T inputdata) where T : class
        {
            BreakData data = new BreakData() { Status = false };
            string messstr = string.Empty;
            DataSaveJson batchJson = new DataSaveJson { Model = inputdata };
            //从云星空查询
            string jsonstr = JsonConvert.SerializeObject(batchJson);
            List<object> Parameters = new List<object>() { formid, jsonstr };
            string content = JsonConvert.SerializeObject(Parameters);
            string result = LocalStaticRequest.cloudApi.Save(formid, content);
            //如果访问正常
            data.Status = _shareController.CloudExecuteSaveStatus(result, ref messstr);
            data.Message = messstr;
            return data;
        }

        /// <summary>
        /// 批量提交给Cloud的单据
        /// </summary>
        /// <typeparam name="T">泛型，实体类</typeparam>
        /// <param name="batchObj">批量提交参数</param>
        /// <param name="formid"></param>
        /// <param name="inputdata">泛型，需要提交的实体类</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchAsync<T>(DataSaveBatch? batchObj, string formid, List<T> inputdata) where T : class
        {
            string messstr = string.Empty;
            BreakData data = new BreakData() { Status = false };
            try
            {
                if (batchObj != null)
                {
                    string breakfileds = batchObj.FNeedReturnFields;
                    string updatefields = batchObj.FNeedUpDateFields;
                    DataSaveBatchJson batchJson = new DataSaveBatchJson()
                    {
                        Model = inputdata,
                        IgnoreInterationFlag = batchObj.FIgnoreInterationFlag,
                        InterationFlags = batchObj.FInterationFlags,
                        IsAutoAdjustField = batchObj.FIsAutoAdjustField,
                        //IsAutoSubmitAndAudit = false,
                        IsAutoSubmitAndAudit = batchObj.FIsAutoSubmitAndAudit,
                        IsDeleteEntry = batchObj.FIsDeleteEntry,
                        IsEntryBatchFill = batchObj.FIsEntryBatchFill,
                        IsVerifyBaseDataField = batchObj.FIsVerifyBaseDataField,
                        SubSystemId = batchObj.FSubSystemId,
                        BatchCount = batchObj.FBatchCount,
                        NeedReturnFields = breakfileds == "" ? null : breakfileds.Split(","),
                        NeedUpDateFields = updatefields == "" ? null : updatefields.Split(","),
                        NumberSearch = batchObj.FNumberSearch,
                        ValidateFlag = batchObj.FValidateFlag,
                        IsControlPrecision = batchObj.FIsControlPrecision,
                        ValidateRepeatJson = batchObj.FValidateRepeatJson
                    };
                    //数据包创建完成
                    string jsonstr = JsonConvert.SerializeObject(batchJson);
                    Stopwatch stopwatch = new Stopwatch();
                    // 开始计时
                    stopwatch.Start();

                    string result = "";

                    string setPort = ApiSettingsHelper.GetConfig($"{formid}_Port");
                    if (!setPort.IsNullOrEmpty())
                    {
                        string acctid = ApiSettingsHelper.GetConfig("X-KDApi-AcctID");
                        string appid = ApiSettingsHelper.GetConfig("X-KDApi-AppID");
                        string sppsec = ApiSettingsHelper.GetConfig("X-KDApi-AppSec");
                        string usname = ApiSettingsHelper.GetConfig("X-KDApi-UserName");
                        int lcid = 0;
                        int.TryParse(ApiSettingsHelper.GetConfig("X-KDApi-LCID"), out lcid);
                        string serverUrl = ApiSettingsHelper.GetConfig("X-KDApi-ServerUrl");
                        string orgno = LocalStaticRequest.GetSystemProfile(2, "DefaultORG");

                        serverUrl = serverUrl.Replace("k3cloud/", "");
                        serverUrl = ReplaceOrAddPortAdvanced(serverUrl, setPort);
                        K3CloudApi k3CloudApi = new K3CloudApi(serverUrl);
                        LogHelper.WriteLog($"云星空API地址  {formid} : {serverUrl}");

                        k3CloudApi.InitClient(acctid, appid, sppsec, usname, lcid, orgno, serverUrl);
                        result = k3CloudApi.BatchSave(formid, jsonstr);
                    }
                    else
                    {
                        result = LocalStaticRequest.cloudApi.BatchSave(formid, jsonstr);
                    }

                    // 停止计时
                    stopwatch.Stop();
                    // 获取执行时间
                    TimeSpan elapsed = stopwatch.Elapsed;
                    LogHelper.WriteLog($"云星空API执行时间  {formid} : {elapsed.TotalMilliseconds} 毫秒，ResultData :{result}");

                    //如果访问正常
                    List<Dictionary<string, string>> billnos = new List<Dictionary<string, string>>();
                    int errcode = 0;
                    data.Status = _shareController.KDCloudExecuteBatchSaveStatus(result, breakfileds, ref errcode, ref billnos);
                    if (data.Status)
                    {//当errcode = 1 时代表会话超时
                        if (errcode != 1) { data.DataSet = billnos; }
                    }
                    else
                    {
                        messstr = billnos[0].FirstOrDefault().Value;
                    }
                }
                else
                {
                    messstr = "请检查批量提交配置...";
                }
                data.Message = messstr;
            }
            catch (Exception ex)
            {
                data.Message = messstr = ex.Message;
            }
            return data;
        }

        /// <summary>
        /// 替换或添加URL中的端口号（支持指定协议）
        /// </summary>
        /// <param name="input"></param>
        /// <param name="newPort"></param>
        /// <param name="protocol"></param>
        /// <returns></returns>
        public static string ReplaceOrAddPortAdvanced(string input, string newPort, string protocol = "http")
        {
            // 构建协议特定的模式
            string protocolPattern = string.IsNullOrEmpty(protocol) ? @"(https?|ftp)://" : $"{protocol}://";

            // 匹配已有端口号
            string patternWithPort = $@"({protocolPattern}[^/]+?):\d+";
            // 匹配没有端口号
            string patternWithoutPort = $@"({protocolPattern}[^/:/]+)(?=/|$)";

            // 先尝试替换已有端口号
            string result = Regex.Replace(input, patternWithPort, $"$1:{newPort}");

            // 如果没有端口号被替换，尝试添加端口号
            if (result == input)
                result = Regex.Replace(input, patternWithoutPort, $"$1:{newPort}");

            return result;
        }

        /// <summary>
        /// 批量提交已保存的单据
        /// </summary>
        /// <param name="billlist">单据编号列表</param>
        /// <param name="formid">Cloud的FormId</param>
        /// <param name="submiturl"></param>
        /// <returns></returns>
        private async Task<BreakData> SubmitCloudAsync(List<string> billlist, string formid, string submiturl)
        {
            if (!LocalStaticRequest.Islogin)
            {
                await _KdhttpClient.LoginErp();
            }
            BreakData data = new BreakData() { Status = false };
            if (!LocalStaticRequest.Islogin)
            {
                data.Message = "KdhttpClient登录失败";
                return data;
            }
            string messstr = string.Empty;
            SubmitJson submitJson = new SubmitJson() { Numbers = billlist.ToArray() };
            //await _KdhttpClient.LoginErp();
            //从云星空查询
            string jsonstr = JsonConvert.SerializeObject(submitJson);
            string result = LocalStaticRequest.cloudApi.Submit(formid, jsonstr);
            //如果访问正常
            //List<string> billnos = new List<string>();
            data.Status = _shareController.CloudExecuteSaveStatus(result, ref messstr);
            if (!data.Status)
            {
                data.Message = messstr;
            }
            data.DataSet = billlist;    //此时为单据编号
            return data;
        }
        /// <summary>
        /// 批量审核已提交的单据
        /// </summary>
        /// <param name="billlist">单据编号列表</param>
        /// <param name="formid">Cloud的FormId</param>
        /// <param name="aduiturl">审核的Url地址</param>
        /// <returns></returns>
        private async Task<BreakData> AuditCloudAsync(List<string> billlist, string formid, string aduiturl)
        {
            if (!LocalStaticRequest.Islogin)
            {
                await _KdhttpClient.LoginErp();
            }
            BreakData data = new BreakData() { Status = false };
            if (!LocalStaticRequest.Islogin)
            {
                data.Message = "KdhttpClient登录失败";
                return data;
            }
            string messstr = string.Empty;
            AuditJson submitJson = new AuditJson() { Numbers = billlist.ToArray() };
            //await _KdhttpClient.LoginErp();
            //从云星空查询
            string jsonstr = JsonConvert.SerializeObject(submitJson);
            string result = LocalStaticRequest.cloudApi.Audit(formid, jsonstr);
            //如果访问正常
            //List<string> billnos = new List<string>();
            data.Status = _shareController.CloudExecuteSaveStatus(result, ref messstr);
            if (!data.Status)
            {
                data.Message = "提交成功，未审核：" + messstr;
            }
            data.DataSet = billlist;    //此时为单据编号
            return data;
        }

        /// <summary>
        /// 更新普通采购订单，以目标表的单据类型编码命名
        /// </summary>
        /// <param name="inStocks">外购入库明细</param>
        /// <returns></returns>
        private async Task<BreakData> UpdateCGDD01_SYSAsync(List<STK_InStock> inStocks)
        {
            BreakData breakData = new BreakData() { Status = false };
            var entries = from a in inStocks
                          select a.FInStockEntry;
            List<STK_InStock.InStockEntry> instockentries = new List<STK_InStock.InStockEntry>();
            foreach (var entry in entries)
            {
                foreach (var t in entry)
                {
                    instockentries.Add(t);
                }
            }
            //取出已入库汇总、未入库汇总
            var polist = from a in instockentries
                         group a by new
                         {
                             A = a.FPOOrderNo,
                             B = a.FPOORDERENTRYID,
                             C = a.FInStockEntry_Link!.FirstOrDefault()!.FInStockEntry_Link_FSBillId,
                             D = a.FUnitID,
                             E = a.FRemainInStockUnitId,
                             F = a.FMaterialId,
                             G = a.FPriceUnitID
                         }
                       into b
                         select new
                         {
                             OrderNo = b.Key.A,
                             OrderEntryID = b.Key.B,
                             SBillId = b.Key.C,
                             UnitID = b.Key.D,
                             RemainInStockUnitId = b.Key.E,
                             MaterialId = b.Key.F,
                             PriceUnitID = b.Key.G,
                             FMustQty = b.Sum(s => s.FMustQty),
                             FRealQty = b.Sum(s => s.FRealQty)
                         };
            if (instockentries.Count > 0)
            {
                List<PUR_PurchaseOrder> purchaseorders = new List<PUR_PurchaseOrder>();
                List<string> pogroup = polist.Select(s => s.OrderNo).Distinct().ToList();
                foreach (string po in pogroup)
                {
                    var poentry = polist.Where(s => s.OrderNo == po).ToList();
                    //为采购订单赋值
                    PUR_PurchaseOrder order = new PUR_PurchaseOrder()
                    {
                        FBillNo = po,
                        FID = int.Parse(poentry.FirstOrDefault()!.SBillId)
                    };
                    List<PUR_PurchaseOrder.POOrderEntry> entrylist = new List<PUR_PurchaseOrder.POOrderEntry>();
                    foreach (var entry in poentry)
                    {
                        PUR_PurchaseOrder.POOrderEntry orderEntry = new PUR_PurchaseOrder.POOrderEntry()
                        {
                            FEntryID = int.Parse(entry.OrderEntryID),
                            FMaterialId = entry.MaterialId,
                            FPriceUnitID = entry.PriceUnitID,
                            FStockUnitID = entry.UnitID,
                            FUnitID = entry.RemainInStockUnitId,
                            FStockInQty = entry.FMustQty + entry.FRealQty,
                            FRemainStockINQty = entry.FMustQty - entry.FRealQty,
                            FEntryDeliveryPlan = new List<PUR_PurchaseOrder.POOrderEntry.EntryDeliveryPlan>
                            {
                                new PUR_PurchaseOrder.POOrderEntry.EntryDeliveryPlan()
                                {
                                    FBaseDeliCommitQty=entry.FMustQty + entry.FRealQty ,
                                    FBaseDeliRemainQty=entry.FMustQty - entry.FRealQty ,
                                    FDeliCommitQty=entry.FMustQty + entry.FRealQty ,
                                    FDeliRemainQty=entry.FMustQty - entry.FRealQty
                                }
                            }
                        };
                        entrylist.Add(orderEntry);
                        order.FPOOrderEntry = entrylist;
                    }
                    purchaseorders.Add(order);
                }
                if (purchaseorders.Count > 0)
                {
                    string formid = "PUR_PurchaseOrder";
                    //推送数据
                    DataSaveBatch? dataSave = new DataSaveBatch()
                    {
                        FNeedUpDateFields = "FStockInQty,FRemainStockINQty,FBaseDeliCommitQty,FBaseDeliRemainQty,FDeliCommitQty,FDeliRemainQty",
                    };
                    //调用批量保存功能
                    breakData = await SaveBatchAsync(dataSave, formid, purchaseorders);
                }
            }
            else
            {
                breakData.Message = "没有需要反写采购订单";
            }
            return breakData;
        }

        ///// <summary>
        ///// 批量提交生产用料清单/变更单到云星空：测试
        ///// </summary>
        ///// <param name="parm">RequestParm</param>
        //[HttpPost]
        //[Route("MERGE_WPLAN_Test")]
        //public async Task<string> TestMERGE_WPLANAsync()
        //{
        //    RequestParm parm = new RequestParm() { 
        //        DocType = "M_MO" ,
        //        data= new List<PRDMO>()
        //        {
        //            new PRDMO()
        //            {
        //                ERP_MO="test_WO2302080001",
        //                MO_SEQ=1
        //            }
        //            ,new PRDMO()
        //            {
        //                ERP_MO="test_WO2302080001",
        //                MO_SEQ=2
        //            }
        //        }
        //    };
        //    string json = JsonConvert.SerializeObject(parm);
        //    return await MergePPBOMAsync(parm);
        //}

        /// <summary>
        /// 根据生产/委外订单编号、行号，自动合并需求数量大于0的主料（替代料不参与）
        /// </summary>
        /// <param name="parm">请提供FromBody</param>
        /// <returns></returns>
        [HttpPost]
        [Route("MERGE_WPLAN")]
        public async Task<string> MergePPBOMAsync([FromBody] RequestParm parm)
        {
            BreakData breakData = new BreakData() { Status = false };
            if (parm == null)
            {
                breakData.Message = "输入的格式不正确";
                return JsonConvert.SerializeObject(breakData);
            }
            string result = string.Empty;
            string reason = string.Empty;
            string DocType = parm.DocType;
            if (string.IsNullOrEmpty(parm.data.ToString()) || string.IsNullOrEmpty(DocType))
            {
                breakData.Message = "传入的参数不正确";
                return JsonConvert.SerializeObject(breakData);
            }
            if (DocType != "M_VW" && DocType != "M_MO")
            {
                breakData.Message = "传入的DocType不正确";
                return JsonConvert.SerializeObject(breakData);
            }
            string data = parm.data.ToString();
            string guid = Guid.NewGuid().ToString("B");
            if (!PostAsyncCheck(DocType, data, ref reason))
            {
                breakData.Message = reason;
                goto E;
            }
            //var pbomform = await _context.r_FormIDType.Where(s => s.FFunctionID == 2 && s.FDocType == DocType).ToListAsync();
            var pbomform = await _shareController.GetAPIList<FormIDType>();
            pbomform = pbomform.Where(s => s.FFunctionID == 2 && s.FDocType == DocType).ToList();
            if (pbomform == null || pbomform.Count == 0)
            {
                breakData.Message = "接口类型不存在或未配置...";
                goto E;
            }
            _shareController._SqlloggerInvoke(2, DocType, "从接口MERGE_WPLAN收到...", data, true, guid);
            //将传过来的数据读出来 
            StringBuilder mess = new StringBuilder();
            bool isok = false;
            List<PRDMO> mesmo = _shareController.DataConvertClass<PRDMO>(data, ref reason);
            if (string.IsNullOrEmpty(reason))
            {
                int postmid = pbomform.FirstOrDefault().FID;
                switch (DocType.ToUpper())
                {
                    case "M_MO":
                        //查询相同项
                        string sql = "/*dialect*/exec rb_GetMergePPBOMKeyMartQty '{0}',{1},1,{2}";
                        foreach (PRDMO mo in mesmo)
                        {
                            //string errstr = await SaveBatchMergePPBOMChangeAsync(string.Format(sql, mo.ERP_MO, mo.MO_SEQ, LocalStaticRequest.DefaultOrgID), postmid);

                            //20241128更新
                            string errstr = await SaveBatchMergePPBOMChangeAsync2(string.Format(sql, mo.ERP_MO, mo.MO_SEQ, LocalStaticRequest.DefaultOrgID), postmid);
                            if (!string.IsNullOrEmpty(errstr))
                            {
                                isok = false;
                                mess.AppendLine(errstr);
                            }
                        }
                        break;
                    case "M_VW":
                        //查询相同项
                        string subsql = "/*dialect*/exec rb_GetMergeSubPPBOMKeyMartQty '{0}',{1},1,{2}";
                        foreach (PRDMO mo in mesmo)
                        {
                            string errstr = await SaveBatchMergeSUBPPBOMChangeAsync(string.Format(subsql, mo.ERP_MO, mo.MO_SEQ, LocalStaticRequest.DefaultOrgID), postmid);
                            if (!string.IsNullOrEmpty(errstr))
                            {
                                isok = false;
                                mess.AppendLine(errstr);
                            }
                        }
                        break;
                }

                isok = !mess.ToString().IsNullOrEmpty();
            }
            else { mess.AppendLine(reason); }
            breakData.Status = isok;
            breakData.Message = isok ? "" : mess.ToString();
            E:
            result = JsonConvert.SerializeObject(breakData);
            _shareController._SqlloggerInvoke(2, DocType, !breakData.Status ? result : "", data, breakData.Status, guid);
            return result;
        }

        /// <summary>
        /// 按工单、工单行号合并主料相同的生产用料清单
        /// </summary>
        /// <param name="sql">查询需要合并的用料清单SQL语句</param>
        /// <returns></returns>
        private async Task<string> SaveBatchMergePPBOMChangeAsync(string sql, int postmid)
        {
            string reason = string.Empty;
            DataTable dataTable = new DataTable();

            string headtb = _chiledselect.GetCustomRequestBillAsync(sql, "", "", null).Result;
            if (!string.IsNullOrWhiteSpace(headtb) && headtb != "[]")
            {
                string fileds = "FParentMaterial,FBOMID,FMoId,FMOBILLNO,FMOEntrySeq,FMOEntryID,FBaseQty,FMOType,FBaseUnitID,FUnitID,FParentOwnerTypeId,FMaterialID2,FChangeType,FUnitID2,FMUSTQTY,FProduceQty,FPPBOMNo,FPPBOMId,FPPBOMEntryId,FPPBOMEntrySeq,FWORKSHOPID,FOWNERID,FSUPPLYORG,FReplaceGroup,FPositionNO";
                dataTable = _shareController.JsonConvertDatatable(fileds, headtb, ref reason);
            }
            if (dataTable == null || dataTable.Rows.Count == 0)
            {
                reason = "没有找到符合条件的记录";
                return null;
            }
            //创建单据头
            PRD_PPBOMChange change = new PRD_PPBOMChange();
            var head = (from a in dataTable.AsEnumerable()
                        select new PRD_PPBOMChange
                        {
                            //FBillNo=b.FPPBOMNo,
                            FMaterialID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FParentMaterial") } },
                            FWorkshopID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FWORKSHOPID") } },
                            FBOMID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FBOMID") } },
                            FQty = decimal.Parse(a.Field<string>("FProduceQty")),
                            FMOBillNO = a.Field<string>("FMOBILLNO"),
                            FMOEntrySeq = int.Parse(a.Field<string>("FMOEntrySeq")),
                            FMOEntryID = int.Parse(a.Field<string>("FMOEntryID")),
                            FBASEQTY = decimal.Parse(a.Field<string>("FBaseQty")),
                            FMoId = int.Parse(a.Field<string>("FMoId")),
                            FBaseUnitID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FBaseUnitID") } },
                            FMOType = new Dictionary<string, string> { { "FNumber", a.Field<string>("FMOType") } },
                            FUnitID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FUnitID") } },
                            FPrdOrgId = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } },
                            FParentOwnerTypeId = a.Field<string>("FParentOwnerTypeId"),
                            FParentOwnerId = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } }
                        }).Distinct().ToList();
            if (head.Any())
            {
                change = head.FirstOrDefault();
            }
            else
            {
                return reason;
            }
            //创建单据体
            List<PRD_PPBOMChange.Entity> entities = (from a in dataTable.AsEnumerable()
                                                     select new PRD_PPBOMChange.Entity
                                                     {
                                                         //FSeq = a.FPPBOMEntrySeq,
                                                         FMaterialID2 = new Dictionary<string, string> { { "FNumber", a.Field<string>("FMaterialID2") } },
                                                         FChangeType = a.Field<string>("FChangeType"),
                                                         FUnitID2 = new Dictionary<string, string> { { "FNumber", a.Field<string>("FUnitID2") } },
                                                         FNeedQty2 = decimal.Parse(a.Field<string>("FMUSTQTY")),
                                                         FMustQty = decimal.Parse(a.Field<string>("FMUSTQTY")),
                                                         //FBaseNeedQty = decimal.Parse(a.Field<string>("FBASENEEDQTY")),
                                                         //FSTDQTY = decimal.Parse(a.Field<string>("FBASESTDQTY")),
                                                         //FBASESTDQTY = decimal.Parse(a.Field<string>("FBASESTDQTY")),
                                                         FProduceQty = decimal.Parse(a.Field<string>("FProduceQty")),
                                                         FMoBillNo1 = a.Field<string>("FMOBILLNO"),
                                                         FPPBOMNo = a.Field<string>("FPPBOMNo"),
                                                         FPPBOMEntryId = int.Parse(a.Field<string>("FPPBOMEntryId")),
                                                         FPPBOMEntrySeq = int.Parse(a.Field<string>("FPPBOMEntrySeq")),
                                                         FMoEntrySeq1 = int.Parse(a.Field<string>("FMOEntrySeq")),
                                                         FPPBOMId = int.Parse(a.Field<string>("FPPBOMId")),
                                                         FWorkshopID1 = new Dictionary<string, string> { { "FNumber", a.Field<string>("FWORKSHOPID") } },
                                                         FOwnerID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } },
                                                         FSupplyOrg = new Dictionary<string, string> { { "FNumber", a.Field<string>("FSUPPLYORG") } },
                                                         FReplaceGroup = int.Parse(a.Field<string>("FReplaceGroup")),
                                                         FPositionNO = a.Field<string>("FPositionNO"),
                                                         //FMaterialType = a.Field<string>("FMaterialType"),
                                                         FEntity_Link = new List<PRD_PPBOMChange.Entity.Entity_Link>()
                                                            {new PRD_PPBOMChange.Entity.Entity_Link
                                                                    {
                                                                        FEntity_Link_FSBillId =int.Parse(a.Field<string>("FPPBOMId")),
                                                                        FEntity_Link_FSId = int.Parse(a.Field<string>("FPPBOMEntryId"))
                                                                    }
                                                            }
                                                     }).ToList();
            //重新获取分子、分母和使用比例
            //MergeUseRate(entities);
            UpdateUseRate(entities);
            change.FEntity.AddRange(entities);
            //批量推送数据模型
            var dsb = await _shareController.GetAPIList<DataSaveBatch>();
            DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == 2 && s.FDocTypeID == postmid).FirstOrDefault();
            //提交新的单据
            List<PRD_PPBOMChange> list = new List<PRD_PPBOMChange>()
            {
                change
            };
            BreakData breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, list);
            if (!breakData.Status)
            {
                reason = (string)breakData.Message;
            }
            //只返回有错误的单据，正确的单据不用管它
            return reason;
        }

        /// <summary>
        /// 按工单、工单行号合并主料相同的生产用料清单 2024/11/28回归备份
        /// </summary>
        /// <param name="sql">查询需要合并的用料清单SQL语句</param>
        /// <returns></returns>
        private async Task<string> SaveBatchMergePPBOMChangeAsync2(string sql, int postmid)
        {
            string reason = string.Empty;
            DataTable dataTable = new DataTable();

            string headtb = _chiledselect.GetCustomRequestBillAsync(sql, "", "", null).Result;
            if (!string.IsNullOrWhiteSpace(headtb) && headtb != "[]")
            {
                string fileds = "FParentMaterial,FBOMID,FMoId,FMOBILLNO,FMOEntrySeq,FMOEntryID,FBaseQty,FMOType,FBaseUnitID,FUnitID,FParentOwnerTypeId,FMaterialID2,FChangeType,FUnitID2,FMUSTQTY,FProduceQty,FPPBOMNo,FPPBOMId,FPPBOMEntryId,FPPBOMEntrySeq,FWORKSHOPID,FOWNERID,FSUPPLYORG,FReplaceGroup,FPositionNO,FMaterialType,FBASENEEDQTY,FBASESTDQTY";
                dataTable = _shareController.JsonConvertDatatable(fileds, headtb, ref reason);
            }
            if (dataTable == null || dataTable.Rows.Count == 0)
            {
                reason = "没有找到符合条件的记录";
                return null;
            }
            //创建单据头
            PRD_PPBOMMerge change = new PRD_PPBOMMerge();
            var head = (from a in dataTable.AsEnumerable()
                        select new PRD_PPBOMMerge
                        {
                            //FBillNo=b.FPPBOMNo,
                            FMaterialID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FParentMaterial") } },
                            FWorkshopID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FWORKSHOPID") } },
                            FBOMID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FBOMID") } },
                            FQty = decimal.Parse(a.Field<string>("FProduceQty")),
                            FMOBillNO = a.Field<string>("FMOBILLNO"),
                            FMOEntrySeq = int.Parse(a.Field<string>("FMOEntrySeq")),
                            FMOEntryID = int.Parse(a.Field<string>("FMOEntryID")),
                            FBASEQTY = decimal.Parse(a.Field<string>("FBaseQty")),
                            FMoId = int.Parse(a.Field<string>("FMoId")),
                            FBaseUnitID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FBaseUnitID") } },
                            FMOType = new Dictionary<string, string> { { "FNumber", a.Field<string>("FMOType") } },
                            FUnitID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FUnitID") } },
                            FPrdOrgId = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } },
                            FParentOwnerTypeId = a.Field<string>("FParentOwnerTypeId"),
                            FParentOwnerId = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } }
                        }).Distinct().ToList();
            if (head.Any())
            {
                change = head.FirstOrDefault();
            }
            else
            {
                return reason;
            }
            //创建单据体
            List<PRD_PPBOMMerge.MergeEntity> entities = (from a in dataTable.AsEnumerable()
                                                         select new PRD_PPBOMMerge.MergeEntity
                                                         {
                                                             //FSeq = a.FPPBOMEntrySeq,
                                                             FMaterialID2 = new Dictionary<string, string> { { "FNumber", a.Field<string>("FMaterialID2") } },
                                                             FChangeType = a.Field<string>("FChangeType"),
                                                             FUnitID2 = new Dictionary<string, string> { { "FNumber", a.Field<string>("FUnitID2") } },
                                                             FNeedQty2 = decimal.Parse(a.Field<string>("FMUSTQTY")),
                                                             FMustQty = decimal.Parse(a.Field<string>("FMUSTQTY")),
                                                             //FBaseNeedQty = decimal.Parse(a.Field<string>("FBASENEEDQTY")),
                                                             FSTDQTY = decimal.Parse(a.Field<string>("FBASESTDQTY")),
                                                             FBASESTDQTY = decimal.Parse(a.Field<string>("FBASESTDQTY")),
                                                             FProduceQty = decimal.Parse(a.Field<string>("FProduceQty")),
                                                             FMoBillNo1 = a.Field<string>("FMOBILLNO"),
                                                             FPPBOMNo = a.Field<string>("FPPBOMNo"),
                                                             FPPBOMEntryId = int.Parse(a.Field<string>("FPPBOMEntryId")),
                                                             FPPBOMEntrySeq = int.Parse(a.Field<string>("FPPBOMEntrySeq")),
                                                             FMoEntrySeq1 = int.Parse(a.Field<string>("FMOEntrySeq")),
                                                             FPPBOMId = int.Parse(a.Field<string>("FPPBOMId")),
                                                             FWorkshopID1 = new Dictionary<string, string> { { "FNumber", a.Field<string>("FWORKSHOPID") } },
                                                             FOwnerID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } },
                                                             FSupplyOrg = new Dictionary<string, string> { { "FNumber", a.Field<string>("FSUPPLYORG") } },
                                                             FReplaceGroup = int.Parse(a.Field<string>("FReplaceGroup")),
                                                             FPositionNO = a.Field<string>("FPositionNO"),
                                                             FMaterialType = a.Field<string>("FMaterialType"),
                                                             FEntity_Link = new List<PRD_PPBOMChange.Entity.Entity_Link>()
                                                            {new PRD_PPBOMChange.Entity.Entity_Link
                                                                    {
                                                                        FEntity_Link_FSBillId =int.Parse(a.Field<string>("FPPBOMId")),
                                                                        FEntity_Link_FSId = int.Parse(a.Field<string>("FPPBOMEntryId"))
                                                                    }
                                                            }
                                                         }).ToList();
            //重新获取分子、分母和使用比例
            //MergeUseRate(entities);
            UpdateUseRate(entities);
            change.FEntity.AddRange(entities);
            //批量推送数据模型
            var dsb = await _shareController.GetAPIList<DataSaveBatch>();
            DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == 2 && s.FDocTypeID == postmid).FirstOrDefault();
            //提交新的单据
            List<PRD_PPBOMMerge> list = new List<PRD_PPBOMMerge>()
            {
                change
            };
            BreakData breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, list);
            if (!breakData.Status)
            {
                reason = (string)breakData.Message;
            }
            //只返回有错误的单据，正确的单据不用管它
            return reason;
        }
        /// <summary>
        /// 已弃用
        /// </summary>
        /// <param name="entities"></param>
        private void MergeUseRate(List<PRD_PPBOMChange.Entity> entities)
        {
            var groupid = (from a in entities.Where(s => s.FChangeType != "2")
                           select new
                           {
                               FReplaceGroup = a.FReplaceGroup
                           }).ToList().Distinct();
            foreach (var item in groupid)
            {
                int id = item.FReplaceGroup;
                List<PRD_PPBOMChange.Entity> itemgroup = entities.Where(s => s.FReplaceGroup == id && s.FChangeType != "2").ToList();
                foreach (PRD_PPBOMChange.Entity entity in itemgroup)
                {
                    decimal itemqty = entity.FMustQty;
                    if (itemqty != 0)
                    {
                        //使用比例=应发数量/总需求*100
                        decimal userate = Math.Round(itemqty / entity.FProduceQty, 4);
                        if (userate != 0)
                        {
                            decimal m = itemqty;
                            decimal d = entity.FProduceQty;
                            GetNewfraction(ref m, ref d);
                            entity.FNumerator = m;
                            entity.FDenominator = d;
                        }
                        //当取消使用百分比控制时可以不用管它
                        entity.FUseRate = 100;
                    }
                    else
                    {
                        if (entity.FMaterialType == "1" && entity.FIsKeyItem == 0)
                        {
                            entity.FUseRate = 100;
                            entity.FNumerator = 0;
                        }
                        else
                        {
                            //当取消使用百分比控制时可以不用管它
                            entity.FUseRate = 0;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 按工单、工单行号合并主料相同的委外用料清单
        /// </summary>
        /// <param name="sql">查询需要合并的用料清单SQL语句</param>
        /// <returns></returns>
        private async Task<string> SaveBatchMergeSUBPPBOMChangeAsync(string sql, int postmid)
        {
            string reason = string.Empty;
            DataTable dataTable = new DataTable();
            string headtb = _chiledselect.GetCustomRequestBillAsync(sql, "", "", null).Result;
            if (!string.IsNullOrWhiteSpace(headtb) && headtb != "[]")
            {
                string fileds = "FParentMaterial,FBOMID,FMoId,FMOBILLNO,FMOEntrySeq,FMOEntryID,FBaseQty,FMOType,FBaseUnitID,FUnitID,FParentOwnerTypeId,FMaterialID2,FChangeType,FUnitID2,FMUSTQTY,FProduceQty,FPPBOMNo,FPPBOMId,FPPBOMEntryId,FPPBOMEntrySeq,FSupplierId,FOWNERID,FSUPPLYORG,FReplaceGroup,FPositionNO";
                dataTable = _shareController.JsonConvertDatatable(fileds, headtb, ref reason);
            }
            if (dataTable == null || dataTable.Rows.Count == 0)
            {
                return null;
            }
            //创建单据头
            SUB_PPBOMChange change = new SUB_PPBOMChange();
            var head = (from a in dataTable.AsEnumerable()
                        select new SUB_PPBOMChange
                        {
                            //FBillNo=b.FPPBOMNo,
                            FMaterialID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FParentMaterial") } },
                            FQty = decimal.Parse(a.Field<string>("FProduceQty")),
                            FBOMID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FBOMID") } },
                            FSupplierId = new Dictionary<string, string> { { "FNumber", a.Field<string>("FSupplierId") } },
                            FSubReqBillNO = a.Field<string>("FMOBILLNO"),
                            FSubReqEntrySeq = int.Parse(a.Field<string>("FMOEntrySeq")),
                            FSubReqEntryId = int.Parse(a.Field<string>("FMOEntryID")),
                            FBASEQTY = decimal.Parse(a.Field<string>("FBaseQty")),
                            FSubReqId = int.Parse(a.Field<string>("FMoId")),
                            FBaseUnitID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FBaseUnitID") } },
                            FSubReqType = new Dictionary<string, string> { { "FNumber", a.Field<string>("FMOType") } },
                            FUnitID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FUnitID") } },
                            FSubOrgId = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } },
                            FParentOwnerTypeId = a.Field<string>("FParentOwnerTypeId"),
                            FParentOwnerId = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } }
                        }
                                    ).Distinct().ToList();
            if (head.Any())
            {
                change = head.FirstOrDefault();
            }
            else
            {
                return reason;
            }
            //创建单据体
            List<SUB_PPBOMChange.Entity> entities = (from a in dataTable.AsEnumerable()
                                                     select new SUB_PPBOMChange.Entity
                                                     {
                                                         //FSeq = a.FPPBOMEntrySeq,
                                                         FMaterialID2 = new Dictionary<string, string> { { "FNumber", a.Field<string>("FMaterialID2") } },
                                                         FChangeType = a.Field<string>("FChangeType"),
                                                         FUnitID2 = new Dictionary<string, string> { { "FNumber", a.Field<string>("FUnitID2") } },
                                                         FNeedQty2 = decimal.Parse(a.Field<string>("FMUSTQTY")),
                                                         FMustQty = decimal.Parse(a.Field<string>("FMUSTQTY")),
                                                         //FSTDQTY = decimal.Parse(a.Field<string>("DOSAGE")),
                                                         FProduceQty = decimal.Parse(a.Field<string>("FProduceQty")),
                                                         FSubReqBillNO1 = a.Field<string>("FMOBILLNO"),
                                                         FSUBPPBOMNo = a.Field<string>("FPPBOMNo"),
                                                         FSUBPPBOMEntryId = int.Parse(a.Field<string>("FPPBOMEntryId")),
                                                         FSUBPPBOMEntrySeq = int.Parse(a.Field<string>("FPPBOMEntrySeq")),
                                                         FSubReqEntrySeq1 = int.Parse(a.Field<string>("FMOEntrySeq")),
                                                         FSubReqId1 = int.Parse(a.Field<string>("FPPBOMId")),
                                                         //= new Dictionary<string, string> { { "FNumber", a.Field<string>("FWORKSHOPID") } },
                                                         FOwnerID = new Dictionary<string, string> { { "FNumber", a.Field<string>("FOWNERID") } },
                                                         FSupplyOrg = new Dictionary<string, string> { { "FNumber", a.Field<string>("FSUPPLYORG") } },
                                                         FReplaceGroup = int.Parse(a.Field<string>("FReplaceGroup")),
                                                         FPositionNO = a.Field<string>("FPositionNO"),
                                                         FEntity_Link = new List<SUB_PPBOMChange.Entity.Entity_Link>()
                                      {new SUB_PPBOMChange.Entity.Entity_Link
                                             {
                                                 FEntity_Link_FSBillId =int.Parse( a.Field<string>("FPPBOMId")),
                                                 FEntity_Link_FSId = int.Parse(a.Field<string>("FPPBOMEntryId"))
                                             }
                                      }
                                                     }).ToList();
            //重新获取分子、分母和使用比例
            //MergeSUBUseRate(entities);
            UpdateSUBUseRate(entities);
            change.FEntity.AddRange(entities);
            //批量推送数据模型
            var dsb = await _shareController.GetAPIList<DataSaveBatch>();
            DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == 2 && s.FDocTypeID == postmid).FirstOrDefault();
            //提交新的单据
            List<SUB_PPBOMChange> list = new List<SUB_PPBOMChange>()
            {
                change
            };
            BreakData breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, list);
            if (!breakData.Status)
            {
                reason = (string)breakData.Message;
            }
            //只返回有错误的单据，正确的单据不用管它
            return reason;
        }
        /// <summary>
        /// 已弃用...
        /// </summary>
        /// <param name="entities"></param>
        private void MergeSUBUseRate(List<SUB_PPBOMChange.Entity> entities)
        {
            var groupid = (from a in entities.Where(s => s.FChangeType != "2")
                           select new
                           {
                               FReplaceGroup = a.FReplaceGroup
                           }).ToList().Distinct();
            foreach (var item in groupid)
            {
                int id = item.FReplaceGroup;
                List<SUB_PPBOMChange.Entity> itemgroup = entities.Where(s => s.FReplaceGroup == id && s.FChangeType != "3").ToList();
                foreach (SUB_PPBOMChange.Entity entity in itemgroup)
                {
                    decimal itemqty = entity.FMustQty;
                    if (itemqty != 0)
                    {
                        //使用比例=应发数量/总需求*100
                        decimal userate = Math.Round(itemqty / entity.FProduceQty, 4);
                        if (userate != 0)
                        {
                            decimal m = itemqty;
                            decimal d = entity.FProduceQty;
                            GetNewfraction(ref m, ref d);
                            entity.FNumerator = m;
                            entity.FDenominator = d;
                        }
                        //当取消使用百分比控制时可以不用管它
                        entity.FUseRate = 100;
                    }
                    else
                    {
                        if (entity.FMaterialType == "1")
                        {
                            entity.FNumerator = 0;
                        }
                        else if (entity.FMaterialType == "3")
                        {
                            //当取消使用百分比控制时可以不用管它
                            entity.FUseRate = 0;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 批量提交生产订单、生产用料清单/变更单到云星空
        /// </summary>
        /// <param name="parm">请提供FromBody</param>
        [HttpPost]
        [Route("UPDATE_WPLAN")]
        public async Task<string> UPDATEPPBOMAsync([FromBody] RequestParm parm)
        {
            //LogHelper.WriteLog(string.Format("收到MES数据：{0}", parm.DocType));
            BreakData breakData = new BreakData() { Status = false };
            if (parm == null)
            {
                breakData.Message = "输入的格式不正确";
                return JsonConvert.SerializeObject(breakData);
                //goto E;
            }
            string result = string.Empty;
            string reason = string.Empty;
            string DocType = parm.DocType;
            if (string.IsNullOrEmpty(parm.data.ToString()) || string.IsNullOrEmpty(DocType))
            {
                breakData.Message = "传入的参数不正确";
                return JsonConvert.SerializeObject(breakData);
            }
            string data = parm.data.ToString();
            if (!PostAsyncCheck(DocType, data, ref reason))
            {
                breakData.Message = reason;
                return JsonConvert.SerializeObject(breakData);
            }
            //var pbomform = await _context.r_FormIDType.Where(s => s.FFunctionID == 2 && s.FDocType == DocType).ToListAsync();
            var pbomform = await _shareController.GetAPIList<FormIDType>();
            pbomform = pbomform.Where(s => s.FFunctionID == 2 && s.FDocType == DocType).ToList();
            if (pbomform == null || pbomform.Count == 0)
            {
                breakData.Message = "接口类型不存在或未配置...";
                return JsonConvert.SerializeObject(breakData);
            }
            string guid = Guid.NewGuid().ToString("B");
            _shareController._SqlloggerInvoke(2, DocType, "从接口UPDATE_WPLAN收到...", data, true, guid);
            switch (DocType.ToUpper())
            {
                //生产订单变更
                case "WPLAN_CHANGE":
                    breakData = await SaveBatchMOCHANGEAsync(data, pbomform.FirstOrDefault().FFunctionID, pbomform.FirstOrDefault().FID);
                    break;
                //委外订单变更
                case "WW_CHANGE":
                    breakData = await SaveBatchSUBCHANGEAsync(data, pbomform.FirstOrDefault().FFunctionID, pbomform.FirstOrDefault().FID);
                    break;
                //生产用料清单
                case "UPDATE_WPLAN_MN":
                    breakData = await SaveBatchPPBOMAsync(data, pbomform.FirstOrDefault().FFunctionID, pbomform.FirstOrDefault().FID);
                    break;
                //委外用料清单
                case "UPDATE_WPLAN_WW":
                    breakData = await SaveBatchSUBPPBOMAsync(data, pbomform.FirstOrDefault().FFunctionID, pbomform.FirstOrDefault().FID);
                    break;
                //生产用料清单变更单
                case "WPLAN_MN_CHANGE":
                    breakData = await SaveBatchPPBOMChangeAsync(data, pbomform.FirstOrDefault().FFunctionID, pbomform.FirstOrDefault().FID);
                    break;
                //委外用料清单变更单
                case "WPLAN_WW_CHANGE":
                    breakData = await SaveBatchSubPPBOMChangeAsync(data, pbomform.FirstOrDefault().FFunctionID, pbomform.FirstOrDefault().FID);
                    break;
            }

            //LogHelper.WriteLog(string.Format("完成推送：{0}", parm.DocType));

            result = JsonConvert.SerializeObject(breakData);
            _shareController._SqlloggerInvoke(2, DocType, !breakData.Status ? result : "", data, breakData.Status, guid);
            return result;
        }

        //尽量让子程序变得简单一点，通过反射出方法来执行
        /// <summary>
        /// 批量提交MES单据到云星空
        /// </summary>
        /// <param name="parm">请提供FromBody</param>
        [HttpPost]
        [Route("DataSave_Batch")]
        public async Task<string> PostAsync([FromBody] RequestParm parm)
        {
            Stopwatch stopwatchAll = new Stopwatch();
            // 开始计时
            stopwatchAll.Start();


            //LogHelper.WriteLog(string.Format("收到MES数据：{0}", parm.DocType));
            BreakData breakData = new BreakData() { Status = false };
            if (parm == null)
            {
                breakData.Message = "输入的格式不正确";
                return JsonConvert.SerializeObject(breakData);
            }
            string reason = string.Empty;
            string DocType = parm.DocType;
            if (string.IsNullOrEmpty(parm.data.ToString()) || string.IsNullOrEmpty(DocType))
            {
                breakData.Message = "传入的参数不正确";
                return JsonConvert.SerializeObject(breakData);
            }
            string data = parm.data.ToString(); /*JsonConvert.SerializeObject(parm.data);*/
            if (!PostAsyncCheck(DocType, data, ref reason))
            {
                breakData.Message = reason;
                return JsonConvert.SerializeObject(breakData);
            }

            Stopwatch checkQueueIdHasFlagWatch = new Stopwatch();
            // 开始计时
            checkQueueIdHasFlagWatch.Start();

            var checkQueueIdHasFlag = !CheckQUEUEIDHasPut(DocType, data, ref reason);

            // 停止计时
            checkQueueIdHasFlagWatch.Stop();
            // 获取执行时间
            LogHelper.WriteLog($"{DocType}:检测是否已经推送过或者正在推送中 执行时间: {checkQueueIdHasFlagWatch.Elapsed.TotalMilliseconds} 毫秒");

            if (checkQueueIdHasFlag)
            {
                breakData.Message = reason;
                return JsonConvert.SerializeObject(breakData);
            }

            //先固定好时间
            string guid = Guid.NewGuid().ToString("B");
            _shareController._SqlloggerInvoke(2, DocType, "从接口DataSave_Batch收到...", data, true, guid);
            try
            {
                //查FFunctionID、查FormId
                List<FormIDType> fm_all = GetAllFormIDTypeAsync(DocType);
                if (!fm_all.Any())
                {
                    breakData.Message = "无法匹配要传入的对象，请检查r_FormIDType配置是否正确";
                    goto E;
                }
                var fm_post = fm_all.Where(f => f.functions.FUsing && f.FActionName == "PostAsync");
                if (!fm_post.Any())
                {
                    breakData.Message = "没有PostAsync的配置项，请检查r_FormIDType配置是否正确";
                    goto E;
                }
                //注意：这里要用的是批量参数
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                if (dsb.Any(s => s.GetForm.FFunctionID == fm_post!.FirstOrDefault()!.FFunctionID && s.FDocTypeID == fm_post.FirstOrDefault()!.FID))
                {
                    string kdformid = fm_post!.FirstOrDefault()!.FDocType;
                    //如果有子查询...
                    //是否存在子查询都要单独写方法...
                    IEnumerable<FormIDType> fm_child = new List<FormIDType>();
                    fm_child = fm_all.Where(f => f.functions.FUsing && f.FActionName == "ChiledSelect");
                    if (fm_child.Any())
                    {
                        int fchildid = fm_child.FirstOrDefault()!.FFunctionID;
                        int fchildmid = fm_child.FirstOrDefault()!.FID;
                        //首先，将data转为DataTable待用于内码转换
                        DataTable dataTable = _shareController.GetInputDataTable(fchildid, fm_child.FirstOrDefault()!.FDBName, data, ref reason);
                        if (!string.IsNullOrEmpty(reason))
                        {
                            breakData.Message = reason;
                            goto E;
                        }
                        if (dataTable.Rows.Count != 0)
                        {
                            string newrate = LocalStaticRequest.GetSystemProfile(5, "NewRate");
                            bool nrate = string.IsNullOrEmpty(newrate) ? false : newrate == "1" ? true : false;
                            //查找并配置Model,根据字段转换规则，匹配对应的类
                            switch (kdformid.ToUpper())
                            {
                                //源单为采购订单的退料:PUR_MRB 
                                case "R006":
                                    breakData = await SaveBatchPURMRBAsync(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;
                                //源单为采购订单的入库:STK_INSTOCK ,包含委外入库
                                case "R170C":
                                    if (nrate)
                                    {
                                        fm_child = fm_all.Where(f => f.functions.FUsing && f.FActionName == "NewRateSelect");
                                    }
                                    if (fm_child.Any())
                                    {
                                        fchildmid = fm_child.FirstOrDefault()!.FID;
                                    }
                                    else
                                    {
                                        breakData.Message = "系统参数要求汇率来源为最新汇率，但没有相关配置";
                                        goto E;
                                    }
                                    breakData = await SaveBatchInStockAsync(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;
                                //源单为采购订单的入库:STK_INSTOCK ,包含委外入库
                                case "RO-VMI":
                                    if (nrate)
                                    {
                                        fm_child = fm_all.Where(f => f.functions.FUsing && f.FActionName == "NewRateSelect");
                                    }
                                    if (fm_child.Any())
                                    {
                                        fchildmid = fm_child.FirstOrDefault()!.FID;
                                    }
                                    else
                                    {
                                        breakData.Message = "系统参数要求汇率来源为最新汇率，但没有相关配置";
                                        goto E;
                                    }
                                    breakData = await SaveBatchInStockAsync(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;

                                //生产入库单:PRD_INSTOCK 
                                case "R102":
                                    breakData = await SaveBatchINSTOCKAsync(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;
                                //生产汇报单:PRD_MORPT 
                                case "R202":
                                    breakData = await SaveBatchMORPTAsync(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;

                                //直接调拨单:STK_TRANSFERDIRECT 
                                case "R010":
                                    breakData = await SaveBatchSTKTransferDirectAsync(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;
                                //（二次）直接调拨单:STK_TRANSFERDIRECT 
                                case "R012":
                                    breakData = await SaveBatchSTKTransferDirect_2Async(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;
                                //销售出库:SAL_OUTSTOCK 
                                case "R104":
                                    breakData = await SaveBatchSALOUTSTOCKAsync(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;

                                //销售退货:SAL_RETURNSTOCK 
                                case "R172C":
                                    breakData = await SaveBatchSALRETURNSTOCKAsync(dataTable,
                                        fm_post!.FirstOrDefault()!.FFunctionID,
                                        fm_post.FirstOrDefault()!.FID,
                                        fchildmid,
                                        data);
                                    break;
                            }
                        }
                        else
                        {
                            switch (kdformid.ToUpper())
                            {
                                //生产补料单:PRD_FEEDMTRL 
                                case "R062":
                                    breakData = await SaveBatchPRDFeedMtrlAsync(fm_child.FirstOrDefault(), data,
                                        fm_post!.FirstOrDefault()!.FFunctionID, fm_post.FirstOrDefault()!.FID);
                                    break;
                                //普通生产领料：PRD_PICKMTRL 
                                case "R014":
                                    breakData = await SaveBatchPickMtrlAsync(fm_child.FirstOrDefault(), data,
                                        fm_post!.FirstOrDefault()!.FFunctionID, fm_post.FirstOrDefault()!.FID);
                                    break;
                                //生产退料单：PRD_RETURNMTRL 
                                case "R019":
                                    breakData = await SaveBatchPRDReturnMtrlAsync(fm_child.FirstOrDefault(), data,
                                        fm_post!.FirstOrDefault()!.FFunctionID, fm_post.FirstOrDefault()!.FID);
                                    break;
                                //委外领料单:SUB_PICKMTRL 
                                case "R014-1":
                                    breakData = await SaveBatchSUBPickMtrlAsync(fm_child.FirstOrDefault(), data,
                                        fm_post!.FirstOrDefault()!.FFunctionID, fm_post.FirstOrDefault()!.FID);
                                    break;
                                //委外退料单DocType:SUB_RETURNMTRL 
                                case "R019-1":
                                    breakData = await SaveBatchSubReturnMtrlAsync(fm_child.FirstOrDefault(), data,
                                        fm_post!.FirstOrDefault()!.FFunctionID, fm_post.FirstOrDefault()!.FID);
                                    //breakData = await SaveBatchSUBRETURNMTRLAsync(dataTable,
                                    //    fm_post!.FirstOrDefault()!.FFunctionID,
                                    //    fm_post.FirstOrDefault()!.FID,
                                    //    fchildmid,
                                    //    data);
                                    break;
                                //委外补料单:SUB_FEEDMTRL 
                                case "R062-1":
                                    breakData = await SaveBatchSUBFeedMtrlAsync(fm_child.FirstOrDefault(), data,
                                        fm_post!.FirstOrDefault()!.FFunctionID, fm_post.FirstOrDefault()!.FID);
                                    break;
                                default:
                                    breakData.Message = "未配置子查询的ClumnConvert";
                                    goto E;

                            }
                        }
                    }
                    else
                    {
                        //查找并配置Model,根据字段转换规则，匹配对应的类
                        int fchildid = fm_all.FirstOrDefault()!.FFunctionID;
                        int fchildmid = fm_all.FirstOrDefault()!.FID;
                        //首先，将data转为DataTable待用于内码转换
                        DataTable dataTable = _shareController.GetInputDataTable(fchildid, fm_all.FirstOrDefault()!.FDBName, data, ref reason);
                        if (!string.IsNullOrEmpty(reason))
                        {
                            breakData.Message = reason;
                            goto E;
                        }
                        switch (kdformid.ToUpper())
                        {
                            //其他入库单DocType:STK_MISCELLANEOUS 
                            case "R175C":
                                breakData = await SaveBatchSTKMISCELLANEOUSAsync(dataTable,
                                    fm_post!.FirstOrDefault()!.FFunctionID,
                                    fm_post.FirstOrDefault()!.FID,
                                    data);
                                break;
                            //其他出库单DocType:STK_MISDELIVERY 
                            case "R100":
                                breakData = await SaveBatchSTKMisDeliveryAsync(dataTable,
                                    fm_post!.FirstOrDefault()!.FFunctionID,
                                    fm_post.FirstOrDefault()!.FID,
                                    data);
                                break;
                            //盘盈单DocType:STK_STOCKCOUNTGAIN 
                            case "R023":
                                breakData = await SaveBatchSTKStockCountGainAsync(dataTable,
                                    fm_post!.FirstOrDefault()!.FFunctionID,
                                    fm_post.FirstOrDefault()!.FID,
                                    data);
                                break;
                            //盘亏单DocType:STK_STOCKCOUNTLOSS 
                            case "R024":
                                breakData = await SaveBatchSTKStockCountLossAsync(dataTable,
                                    fm_post!.FirstOrDefault()!.FFunctionID,
                                    fm_post.FirstOrDefault()!.FID,
                                    data);
                                break;
                            //受托加工材料入库单:STK_OEMINSTOCK 
                            case "R171C":
                                breakData = await SaveBatchSTKOEMInStockAsync(dataTable,
                                    fm_post!.FirstOrDefault()!.FFunctionID,
                                    fm_post.FirstOrDefault()!.FID,
                                    data);
                                break;
                            //受托加工材料退料:STK_OEMInStockRETURN 
                            case "R006-CUST":
                                breakData = await SaveBatchSTKOEMInStockRETURNAsync(dataTable,
                                    fm_post!.FirstOrDefault()!.FFunctionID,
                                    fm_post.FirstOrDefault()!.FID,
                                    data);
                                break;
                            //直接调拨单:STK_TRANSFERDIRECT 
                            case "R011":
                                breakData = await SaveBatchSTKTransferDirectsAsync(dataTable,
                                    fm_post!.FirstOrDefault()!.FFunctionID,
                                    fm_post.FirstOrDefault()!.FID,
                                    data);
                                break;
                            //VMI直接调拨单:STK_TRANSFERDIRECT 
                            case "R011-VMI":
                                breakData = await SaveBatchSTKTransferVMIDirectsAsync(dataTable,
                                    fm_post!.FirstOrDefault()!.FFunctionID,
                                    fm_post.FirstOrDefault()!.FID,
                                    data);
                                break;
                        }
                        //switch (kdformid.ToUpper())
                        //{
                        //    case "SSS":
                        //        //这里执行
                        //        break;
                        //}
                    }
                }
                else
                {
                    breakData.Message = "未配置属于【" + DocType + "】的JSON提交参数";
                    goto E;
                }
            }
            catch (Exception ex)
            {
                string line = ex.StackTrace.Substring(ex.StackTrace.IndexOf("行号"), ex.StackTrace.Length - ex.StackTrace.IndexOf("行号"));
                breakData.Message = string.Format("错误在PostAsync{0}发生：{1}", line, ex.Message);
            }
            //LogHelper.WriteLog(string.Format("完成ERP推送：{0}", parm.DocType));

            E:
            string result = JsonConvert.SerializeObject(breakData);
            _shareController._SqlloggerInvoke(2, DocType, !breakData.Status ? result : "", data, breakData.Status, guid);

            // 停止计时
            stopwatchAll.Stop();
            // 获取执行时间
            TimeSpan elapsed = stopwatchAll.Elapsed;
            LogHelper.WriteLog($"{DocType}:接口总执行时间: {elapsed.TotalMilliseconds} 毫秒");

            return result;
        }

        /// <summary>
        /// 生产退料单：提供批量保存、提交并审核普通生产退料单
        /// </summary>
        /// <param name="formIDType">接口定义</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchPRDReturnMtrlAsync(FormIDType formIDType, string data, int fid, int childtypeid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            var questobj = await _shareController.GetAPIList<CloudBillQuery>();
            questobj = questobj.Where(s => s.FFormIDTypeID == formIDType.FID).ToList();
            if (questobj.Count == 0)
            {
                breakData.Message = "此接口的请求条件未配置";
                return breakData;
            }
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            //List<PRDReturnMtrl> purchaseOrders = _chiledselect.GetSourceBillEntry<PRDReturnMtrl>(dataTable, fchildmid, ref reason);
            List<R019>? mes_POInStocks = _shareController.DataConvertClass<R019>(data, ref reason);
            if (mes_POInStocks != null)
            {
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, formIDType.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                //通过输入信息得到子查询的条件
                List<string> monumberes = (from a in mes_POInStocks
                                           select a.ERP_MO).Distinct().ToList();
                //准备输入参数
                List<Dictionary<string, object>> keyValues = new List<Dictionary<string, object>>();
                foreach (string key in monumberes)
                {
                    keyValues.Add(new Dictionary<string, object>()
                    {
                        {"@ERP_MO",key}
                    });
                }
                //把源单信息都取出来
                CloudBillQuery billQuery = questobj.FirstOrDefault();
                string fk = billQuery.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                string fs = billQuery.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                //string sourcebills = await _chiledselect.GetCustomRequestTableAsync(billQuery.FFieldKeys, billQuery.FFiledString, billQuery.FOrderString, keyValues);
                string sourcebills = GetCustomRequestTableAsync(fk, fs, billQuery.FOrderString, keyValues, ref reason);
                if (!string.IsNullOrWhiteSpace(reason))
                {
                    breakData.Message = reason;
                }
                else
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == childtypeid)
                        .FirstOrDefault();
                    List<PRDReturnMtrl> purchaseOrders = _shareController.DataConvertClass<PRDReturnMtrl>(sourcebills, ref reason);
                    if (!string.IsNullOrWhiteSpace(reason))
                    {
                        breakData.Message = reason;
                    }
                    else
                    {
                        //把主料为空的转为新的主料
                        List<PRDReturnMtrl> nullmain = purchaseOrders.Where(s => s.FMainMaterial == null && s.FMaterial != null).ToList();
                        foreach (PRDReturnMtrl s_change in nullmain)
                        {
                            //将mesbom中的主料拿过来自己用
                            var item = mes_POInStocks.Find(s => s.MTRL_ID == s_change.FMaterial);
                            if (item != null)
                            {
                                string main = mes_POInStocks.Find(s => s.MTRL_ID == s_change.FMaterial).MAIN_ID;
                                s_change.FMainMaterial = main;
                            }
                        }

                        List<PRD_ReturnMtrl> inStocks = CreateBatchPRDReturnMtrl(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                        if (inStocks.Count() > 0 && reason == "")
                        {
                            breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                            if (breakData.Status)
                            {
                                List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                                if (breakData.DataSet != null)
                                {
                                    //提取出创建好的单据编号
                                    List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                    List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                    Thread thread = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread.Start(dataBacks);
                                }
                                else
                                {
                                    dataBacks = await DataBacks(childtypeid, mes_POInStocks);
                                    if (dataBacks.Count != 0)
                                    {
                                        Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                        //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                        thread_1.Start(dataBacks);
                                    }
                                    else
                                    {
                                        breakData.Status = false;
                                        breakData.Message = "不可控因素，请稍后再次提交";
                                    }
                                }
                                breakData.DataSet = dataBacks;
                            }
                        }
                        else
                        {
                            breakData.Message = reason;
                        }
                    }
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }

        private async Task<List<ReciveDataBack>> DataBacks<T>(int doctypeid, List<T> mes)
        {
            List<ReciveDataBack> reciveDataBacks = new List<ReciveDataBack>();
            Type type = typeof(T);
            var questobj = await _shareController.GetAPIList<FormIDType>();
            FormIDType queries = questobj.Find(s => s.FFunctionID == 2 && s.FID == doctypeid);
            string colname = queries.FGuidColName;
            PropertyInfo[] propertyInfos = type.GetProperties();
            //PropertyInfo info = propertyInfos.Find(s => s.Name.Equals(colname));
            var infos = propertyInfos.Where(s => s.Name.Equals("QUEUE_ID"));
            if (infos != null)
            {
                PropertyInfo info = infos.FirstOrDefault();
                //从ERP里再交查询这个QUEUE_ID
                if (queries != null)
                {
                    string doctype = queries.FDocType;
                    string sql = queries.FNeedBreakSQL;
                    if (!string.IsNullOrWhiteSpace(sql))
                    {
                        List<SqlParameter> parameters = new List<SqlParameter>() { new SqlParameter("@defaultorgid", LocalStaticRequest.DefaultOrgID) };    //后台配置默认组织必须是这个参数
                        List<string> sqlParams = new List<string>();    //已写入单据的值
                        foreach (T item in mes)
                        {
                            string v = info.GetValue(item).ToString();
                            if (!string.IsNullOrWhiteSpace(v))
                            {
                                string parname = string.Format("@{0}", doctype);

                                //避免传入的参数名字一样，导致报错
                                var paraHasCount = sqlParams.Count(w => w.Contains(parname));
                                if (paraHasCount > 0)
                                    parname = $"{parname}_{paraHasCount}";

                                string parval = string.Format("{0}:{1}", doctype, v);
                                parameters.Add(new SqlParameter(parname, parval));
                                sqlParams.Add(parname);
                            }
                        }
                        //仅从数据库直接查询
                        string reason = string.Empty;
                        sql = string.Format(sql, string.Join(",", sqlParams.ToArray()));
                        DataSet dataSet = _kdsqlhelper.GetDataSet(CommandType.Text, sql, parameters.ToArray(), ref reason, true);
                        if (dataSet != null)
                        {
                            if (dataSet.Tables.Count > 0)
                            {
                                DataTable dataTable = dataSet.Tables[0];
                                foreach (DataRow row in dataTable.Rows)
                                {
                                    string queue_id_str = row[colname].ToString();
                                    string[] str = queue_id_str.Split(":");    //如果有返回，前面一定是接口类型，后面一定是QUEUE_ID
                                    if (str.Length > 1)
                                    {
                                        reciveDataBacks.Add(new ReciveDataBack()
                                        {
                                            FDocType = doctype,
                                            FBillNo = row["FBillNo"].ToString(),
                                            FErpID = int.Parse(row["FID"].ToString()),
                                            FSeq = int.Parse(row["FSEQ"].ToString()),
                                            QUEUE_ID = str[1],
                                            FJson = ""
                                        });
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return reciveDataBacks;
        }
        /// <summary>
        /// 委外退料单：提供批量保存、提交并审核普通委外退料单
        /// </summary>
        /// <param name="formIDType">接口定义</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSubReturnMtrlAsync(FormIDType formIDType, string data, int fid, int childtypeid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            var questobj = await _shareController.GetAPIList<CloudBillQuery>();
            questobj = questobj.Where(s => s.FFormIDTypeID == formIDType.FID).ToList();
            if (questobj.Count == 0)
            {
                breakData.Message = "此接口的请求条件未配置";
                return breakData;
            }
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            //List<PRDReturnMtrl> purchaseOrders = _chiledselect.GetSourceBillEntry<PRDReturnMtrl>(dataTable, fchildmid, ref reason);
            List<R019_1>? mes_POInStocks = _shareController.DataConvertClass<R019_1>(data, ref reason);
            if (mes_POInStocks != null)
            {
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, formIDType.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                //通过输入信息得到子查询的条件
                List<string> monumberes = (from a in mes_POInStocks
                                           select a.ERP_MO).Distinct().ToList();
                //准备输入参数
                List<Dictionary<string, object>> keyValues = new List<Dictionary<string, object>>();
                foreach (string key in monumberes)
                {
                    keyValues.Add(new Dictionary<string, object>()
                    {
                        {"@ERP_MO",key}
                    });
                }
                //把源单信息都取出来
                CloudBillQuery billQuery = questobj.FirstOrDefault();
                string fk = billQuery.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                string fs = billQuery.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                string sourcebills = GetCustomRequestTableAsync(fk, fs, billQuery.FOrderString, keyValues, ref reason);
                if (!string.IsNullOrWhiteSpace(reason))
                {
                    breakData.Message = reason;
                }
                else
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == childtypeid)
                        .FirstOrDefault();
                    List<SUBRETURNMTRL> purchaseOrders = _shareController.DataConvertClass<SUBRETURNMTRL>(sourcebills, ref reason);
                    if (!string.IsNullOrWhiteSpace(reason))
                    {
                        breakData.Message = reason;
                    }
                    else
                    {
                        //把主料为空的转为新的主料
                        List<SUBRETURNMTRL> nullmain = purchaseOrders.Where(s => s.FMainMaterial == null && s.FMaterial != null).ToList();
                        foreach (SUBRETURNMTRL s_change in nullmain)
                        {
                            //将mesbom中的主料拿过来自己用
                            var item = mes_POInStocks.Find(s => s.MTRL_ID == s_change.FMaterial);
                            if (item != null)
                            {
                                string main = mes_POInStocks.Find(s => s.MTRL_ID == s_change.FMaterial).MAIN_ID;
                                s_change.FMainMaterial = main;
                            }
                        }

                        List<SUB_RETURNMTRL> inStocks = CreateBatchSUBRETURNMTRL(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                        if (inStocks.Count() > 0 && reason == "")
                        {
                            breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                            if (breakData.Status)
                            {
                                List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                                if (breakData.DataSet != null)
                                {
                                    //提取出创建好的单据编号
                                    List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                    List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                    Thread thread = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread.Start(dataBacks);
                                }
                                else
                                {
                                    dataBacks = await DataBacks(childtypeid, mes_POInStocks);
                                    if (dataBacks.Count != 0)
                                    {
                                        Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                        //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                        thread_1.Start(dataBacks);
                                    }
                                    else
                                    {
                                        breakData.Status = false;
                                        breakData.Message = "不可控因素，请稍后再次提交";
                                    }
                                }
                                breakData.DataSet = dataBacks;
                            }
                        }
                        else
                        {
                            breakData.Message = reason;
                        }
                    }
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }

        /// <summary>
        /// 源单使用API结构查询，提供批量保存、提交并审核普通委外退料单。
        /// 已弃用...
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSUBRETURNMTRLAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            List<SUBRETURNMTRL> purchaseOrders = _chiledselect.GetSourceBillEntry<SUBRETURNMTRL>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                List<R019_1>? mes_POInStocks = _shareController.DataConvertClass<R019_1>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    //string dectype = _shareController.GetDesFormID(dataSave.GetForm.FDocType, purchaseOrders.FirstOrDefault().FBillTypeID);
                    List<SUB_RETURNMTRL> inStocks = CreateBatchSUBRETURNMTRL(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);

                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通盘盈单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKStockCountGainAsync(DataTable dataTable, int fid, int postmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R023>? mes_POInStocks = _shareController.DataConvertClass<R023>(data, ref reason);
            if (mes_POInStocks != null)
            {
                //推送数据
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                List<STK_StockCountGain> inStocks = CreateBatchSTKStockCountGain(mes_POInStocks, dataSave.FErpBillType, ref reason);

                if (inStocks.Count() > 0 && reason == "")
                {
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                    if (breakData.Status)
                    {
                        List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                        if (breakData.DataSet != null)
                        {
                            //提取出创建好的单据编号
                            List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                            List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                            Thread thread = new Thread(_shareController.SaveReciveData);
                            //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                            thread.Start(dataBacks);
                        }
                        else
                        {
                            dataBacks = await DataBacks(postmid, mes_POInStocks);
                            if (dataBacks.Count != 0)
                            {
                                Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread_1.Start(dataBacks);
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "不可控因素，请稍后再次提交";
                            }
                        }
                        breakData.DataSet = dataBacks;
                    }
                }
                else
                {
                    breakData.Message = reason;
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }
        /// <summary>
        /// 提供批量保存、提交并审核普通盘亏单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKStockCountLossAsync(DataTable dataTable, int fid, int postmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R024>? mes_POInStocks = _shareController.DataConvertClass<R024>(data, ref reason);
            if (mes_POInStocks != null)
            {
                //推送数据
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                List<STK_StockCountLoss> inStocks = CreateBatchSTKStockCountLoss(mes_POInStocks, dataSave.FErpBillType, ref reason);

                if (inStocks.Count() > 0 && reason == "")
                {
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                    if (breakData.Status)
                    {
                        List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                        if (breakData.DataSet != null)
                        {
                            //提取出创建好的单据编号
                            List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                            List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                            Thread thread = new Thread(_shareController.SaveReciveData);
                            //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                            thread.Start(dataBacks);
                        }
                        else
                        {
                            dataBacks = await DataBacks(postmid, mes_POInStocks);
                            if (dataBacks.Count != 0)
                            {
                                Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread_1.Start(dataBacks);
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "不可控因素，请稍后再次提交";
                            }
                        }
                        breakData.DataSet = dataBacks;
                    }
                }
                else
                {
                    breakData.Message = reason;
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }
        /// <summary>
        /// 提供批量保存、提交并审核普通受托加工材料入库单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKOEMInStockAsync(DataTable dataTable, int fid, int postmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R171C>? mes_POInStocks = _shareController.DataConvertClass<R171C>(data, ref reason);
            if (mes_POInStocks != null)
            {
                //推送数据 
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                List<STK_OEMInStock> inStocks = CreateBatchSTKOEMInStock(mes_POInStocks, dataSave.FErpBillType, ref reason);

                if (inStocks.Count() > 0 && reason == "")
                {
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                    if (breakData.Status)
                    {
                        List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                        if (breakData.DataSet != null)
                        {
                            //提取出创建好的单据编号
                            List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                            List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                            Thread thread = new Thread(_shareController.SaveReciveData);
                            //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                            thread.Start(dataBacks);
                        }
                        else
                        {
                            dataBacks = await DataBacks(postmid, mes_POInStocks);
                            if (dataBacks.Count != 0)
                            {
                                Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread_1.Start(dataBacks);
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "不可控因素，请稍后再次提交";
                            }
                        }
                        breakData.DataSet = dataBacks;
                    }
                }
                else
                {
                    breakData.Message = reason;
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }
        /// <summary>
        /// 提供批量保存、提交并审核普通受托加工材料退料单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKOEMInStockRETURNAsync(DataTable dataTable, int fid, int postmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R006_CUST>? mes_POInStocks = _shareController.DataConvertClass<R006_CUST>(data, ref reason);
            if (mes_POInStocks != null)
            {
                //推送数据
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                List<STK_OEMInStockRETURN> inStocks = CreateBatchSTKOEMInStockRETURN(mes_POInStocks, dataSave.FErpBillType, ref reason);

                if (inStocks.Count() > 0 && reason == "")
                {
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                    if (breakData.Status)
                    {
                        List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                        if (breakData.DataSet != null)
                        {
                            //提取出创建好的单据编号
                            List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                            List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                            Thread thread = new Thread(_shareController.SaveReciveData);
                            //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                            thread.Start(dataBacks);
                        }
                        else
                        {
                            dataBacks = await DataBacks(postmid, mes_POInStocks);
                            if (dataBacks.Count != 0)
                            {
                                Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread_1.Start(dataBacks);
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "不可控因素，请稍后再次提交";
                            }
                        }
                        breakData.DataSet = dataBacks;
                    }
                }
                else
                {
                    breakData.Message = reason;
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }
        /// <summary>
        /// 提供批量保存、提交并审核普通销售退货单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSALRETURNSTOCKAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            List<SALRETURNSTOCK> purchaseOrders = _chiledselect.GetSourceBillEntry<SALRETURNSTOCK>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                List<R172C>? mes_POInStocks = _shareController.DataConvertClass<R172C>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    List<SAL_RETURNSTOCK> inStocks = CreateBatchSALRETURNSTOCK(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);

                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通销售出库单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSALOUTSTOCKAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            List<SALOUTSTOCK> purchaseOrders = _chiledselect.GetSourceBillEntry<SALOUTSTOCK>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                List<R104>? mes_POInStocks = _shareController.DataConvertClass<R104>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    List<SAL_OUTSTOCK> inStocks = CreateBatchSALOUTSTOCK(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);

                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通直接调拨单(没有源单)
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKTransferDirectsAsync(DataTable dataTable, int fid, int postmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R011>? mes_POInStocks = _shareController.DataConvertClass<R011>(data, ref reason);
            if (mes_POInStocks != null)
            {
                //推送数据
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                List<STK_TransferDirect> inStocks = CreateBatchSTKTransferDirects(mes_POInStocks, dataSave.FErpBillType, ref reason);

                if (inStocks.Count() > 0 && reason == "")
                {
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                    if (breakData.Status)
                    {
                        List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                        if (breakData.DataSet != null)
                        {
                            //提取出创建好的单据编号
                            List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                            List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                            Thread thread = new Thread(_shareController.SaveReciveData);
                            //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                            thread.Start(dataBacks);
                        }
                        else
                        {
                            dataBacks = await DataBacks(postmid, mes_POInStocks);
                            if (dataBacks.Count != 0)
                            {
                                Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread_1.Start(dataBacks);
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "不可控因素，请稍后再次提交";
                            }
                        }
                        breakData.DataSet = dataBacks;
                    }
                }
                else
                {
                    breakData.Message = reason;
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }

            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通直接调拨单(没有源单)
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKTransferVMIDirectsAsync(DataTable dataTable, int fid, int postmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R011VMI>? vmi_POInStocks = _shareController.DataConvertClass<R011VMI>(data, ref reason);
            if (vmi_POInStocks != null)
            {
                //推送数据
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(vmi_POInStocks, dataSave.GetForm.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                List<STK_TransferDirect> inStocks = CreateBatchSTKTransferVMIDirects(vmi_POInStocks, dataSave.FErpBillType, ref reason);

                if (inStocks.Count() > 0 && reason == "")
                {
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                    if (breakData.Status)
                    {
                        List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                        if (breakData.DataSet != null)
                        {
                            //提取出创建好的单据编号
                            List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                            List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, vmi_POInStocks, billlist, ref dataBacks);
                            Thread thread = new Thread(_shareController.SaveReciveData);
                            //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                            thread.Start(dataBacks);
                        }
                        else
                        {
                            dataBacks = await DataBacks(postmid, vmi_POInStocks);
                            if (dataBacks.Count != 0)
                            {
                                Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread_1.Start(dataBacks);
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "不可控因素，请稍后再次提交";
                            }
                        }
                        breakData.DataSet = dataBacks;
                    }
                }
                else
                {
                    breakData.Message = reason;
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }

            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通其他入库单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKMISCELLANEOUSAsync(DataTable dataTable, int fid, int postmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R175C>? mes_POInStocks = _shareController.DataConvertClass<R175C>(data, ref reason);
            if (mes_POInStocks != null)
            {
                //推送数据
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                List<STK_MISCELLANEOUS> inStocks = CreateBatchSTKMISCELLANEOUS(mes_POInStocks, ref reason);

                if (inStocks.Count() > 0 && reason == "")
                {
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                    if (breakData.Status)
                    {
                        List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                        if (breakData.DataSet != null)
                        {
                            //提取出创建好的单据编号
                            List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                            List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                            Thread thread = new Thread(_shareController.SaveReciveData);
                            //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                            thread.Start(dataBacks);
                        }
                        else
                        {
                            dataBacks = await DataBacks(postmid, mes_POInStocks);
                            if (dataBacks.Count != 0)
                            {
                                Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread_1.Start(dataBacks);
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "不可控因素，请稍后再次提交";
                            }
                        }
                        breakData.DataSet = dataBacks;
                    }
                }
                else
                {
                    breakData.Message = reason;
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }

            return breakData;
        }
        /// <summary>
        /// 提供批量保存、提交并审核普通其他出库单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKMisDeliveryAsync(DataTable dataTable, int fid, int postmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R100>? mes_POInStocks = _shareController.DataConvertClass<R100>(data, ref reason);
            if (mes_POInStocks != null)
            {
                //推送数据
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                List<STK_MisDelivery> inStocks = CreateBatchSTKMisDelivery(mes_POInStocks, ref reason);

                if (inStocks.Count() > 0 && reason == "")
                {
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                    if (breakData.Status)
                    {
                        List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                        if (breakData.DataSet != null)
                        {
                            //提取出创建好的单据编号
                            List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                            List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                            Thread thread = new Thread(_shareController.SaveReciveData);
                            //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                            thread.Start(dataBacks);
                        }
                        else
                        {
                            dataBacks = await DataBacks(postmid, mes_POInStocks);
                            if (dataBacks.Count != 0)
                            {
                                Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread_1.Start(dataBacks);
                            }
                            else
                            {
                                breakData.Status = false;
                                breakData.Message = "不可控因素，请稍后再次提交";
                            }
                        }
                        breakData.DataSet = dataBacks;
                    }
                }
                else
                {
                    breakData.Message = reason;
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }

            return breakData;
        }

        ///// <summary>
        ///// 将转入data转为云星空需要的类
        ///// </summary>
        ///// <param name="spacename">命名空间名</param>
        ///// <param name="clssname">类名</param>
        ///// <param name="data">转入的参数</param>
        ///// <param name="reason"></param>
        ///// <returns></returns>
        //private List<object>? DataConvertClass(string spacename, string clssname, string data, ref string reason)
        //{
        //    reason = "";
        //    List<object> list = new List<object>();
        //    //获得Models下所有模型
        //    var typelist = Assembly.GetExecutingAssembly().GetTypes()
        //        .Where(s => s.Namespace == spacename)
        //        .Where(p => p.Name == clssname);
        //    if (typelist.Any())
        //    {
        //        Type? type = typelist.FirstOrDefault();
        //        if (type != null)
        //        {
        //            try
        //            {
        //                JArray jArray = (JArray)JsonConvert.DeserializeObject(data)!;
        //                //List<object> list = JsonConvert.DeserializeObject<List<object>>(data)!;       //如果直接这样使用，则有些字段不相同的不会报错...
        //                foreach (var item in jArray)
        //                {
        //                    if (item != null)
        //                    {
        //                        list.Add(item.ToObject(type));
        //                    }
        //                }
        //                return list;
        //            }
        //            catch (Exception ex)
        //            {
        //                reason = "将转入data转为云星空需要的类时，发生错误：" + ex.Message;
        //                //throw;
        //            }
        //        }
        //        {
        //            reason = "将转入data转为云星空需要的类时，指定的类型不存在...";
        //        }
        //    }
        //    return list;
        //}

        /// <summary>
        /// 入库接口与采购订单信息对比验证
        /// </summary>
        /// <param name="mes">MES推过来的数据类</param>
        /// <param name="indata">ERP的采购订单信息</param>
        /// <returns></returns>
        private string PONoInStockQtyCheck(List<R170C> mes, List<PurchaseOrder> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var R170 = from a in mes.AsEnumerable()
            //           group a by new { a.PO_ID, a.PO_SEQ } into b
            //           select new
            //           {
            //               PO_ID = b.Key.PO_ID,
            //               PO_SEQ = b.Key.PO_SEQ,
            //               Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //           };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.PO_ID, a.PO_SEQ }
                  ).Except(
                from b in indata select new { PO_ID = b.FBillNo, PO_SEQ = b.FPOOrderEntry_FSeq }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},行号{1}。", p.PO_ID, p.PO_SEQ);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var SR170 = from a in R170
            //            join b in indata on new { A = a.PO_ID, B = a.PO_SEQ } equals new { A = b.FBillNo, B = b.FPOOrderEntry_FSeq }
            //            where a.Qty > _shareController.DecimalPar(b.FRemainInStockQty)
            //            select new
            //            {
            //                PO_ID = a.PO_ID,
            //                PO_SEQ = a.PO_SEQ,
            //                Qty = _shareController.DecimalPar(b.FRemainInStockQty) - a.Qty
            //            };
            //if (SR170 != null)
            //{
            //    foreach (var s in SR170)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超过未入库数{2}。", s.PO_ID, s.PO_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }

        /// <summary>
        /// 生产领料接口与生产用料清单信息对比验证
        /// </summary>
        /// <param name="mes">MES推过来的数据类</param>
        /// <param name="indata">ERP的生产用料清单信息</param>
        /// <returns></returns>
        private string PONoPickMtrlQtyCheck(List<R014> mes, List<PrdPpbomEntry> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.ERP_MO, a.MAIN_ID,a.MTRL_ID } into b
            //         select new
            //         {
            //             ERP_NO = b.Key.ERP_MO,
            //             MAIN_ID = b.Key.MAIN_ID,
            //             MTRL_ID = b.Key.MTRL_ID,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在

            var poExcept = (from a in mes
                            select new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID }
                  ).Except(
                from b in indata select new { ERP_MO = b.FBillNo, MAIN_ID = b.FMainMaterial, MTRL_ID = b.FMaterial }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},主料{1}、替代料{2}。", p.ERP_MO, p.MAIN_ID, p.MTRL_ID);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes =( from a in rs
            //             join b in indata on new { A = a.ERP_NO, B = a.MAIN_ID,C=a.MTRL_ID } equals new { A = b.FBillNo, B = b.FMainMaterial,C=b.FMaterial }
            //             where a.Qty > (decimal.Parse(b.FNeedQty2) - decimal.Parse(b.FPickedQty))
            //             select new
            //             {
            //                 ERP_NO = a.ERP_NO,
            //                 MAIN_ID = a.MAIN_ID,
            //                 MTRL_ID=a.MTRL_ID,
            //                 Qty = (decimal.Parse(b.FNeedQty2) - decimal.Parse(b.FPickedQty) - a.Qty)
            //             }).ToList();
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},主料{1}、替代料{2}，超过未入库数{3}。", s.ERP_NO, s.MAIN_ID,s.MTRL_ID, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        private string PONoSUBPickMtrltyCheck(List<R014_1> mes, List<SUBPickMtrl> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID } into b
            //         select new
            //         {
            //             ERP_NO = b.Key.ERP_MO,
            //             MAIN_ID = b.Key.MAIN_ID,
            //             MTRL_ID = b.Key.MTRL_ID,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在

            var poExcept = (from a in mes
                            select new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID }
                  ).Except(
                from b in indata select new { ERP_MO = b.FBillNo, MAIN_ID = b.FMainMaterial, MTRL_ID = b.FMaterial }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},主料{1}、替代料{2}。", p.ERP_MO, p.MAIN_ID, p.MTRL_ID);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes =( from a in rs
            //             join b in indata on new { A = a.ERP_NO, B = a.MAIN_ID,C=a.MTRL_ID } equals new { A = b.FBillNo, B = b.FMainMaterial,C=b.FMaterial }
            //             where a.Qty > (decimal.Parse(b.FNeedQty2) - decimal.Parse(b.FPickedQty))
            //             select new
            //             {
            //                 ERP_NO = a.ERP_NO,
            //                 MAIN_ID = a.MAIN_ID,
            //                 MTRL_ID=a.MTRL_ID,
            //                 Qty = (decimal.Parse(b.FNeedQty2) - decimal.Parse(b.FPickedQty) - a.Qty)
            //             }).ToList();
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},主料{1}、替代料{2}，超过未入库数{3}。", s.ERP_NO, s.MAIN_ID,s.MTRL_ID, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        /// <summary>
        /// 生产订单汇报接口原单信息对比验证
        /// </summary>
        /// <param name="mes">MES推过来的数据类</param>
        /// <param name="indata">ERP的生产入库单信息</param>
        /// <returns></returns>
        private string PONoMORPTCheck(List<R202> mes, List<PRDMORPT> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.ERP_MO, a.MO_SEQ } into b
            //         select new
            //         {
            //             ERP_NO = b.Key.ERP_MO,
            //             ERP_SEQ = b.Key.MO_SEQ,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.ERP_MO, a.MO_SEQ }
                  ).Except(
                from b in indata select new { ERP_MO = b.FBillNo, MO_SEQ = b.FSeq.ToString() }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},行号{1}。", p.ERP_MO, p.MO_SEQ);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.ERP_NO, B = a.ERP_SEQ } equals new { A = b.FBillNo, B = b.FSeq.ToString() }
            //             where a.Qty > b.FNoStockInQty
            //             select new
            //             {
            //                 ERP_NO = a.ERP_NO,
            //                 ERP_SEQ = a.ERP_SEQ,
            //                 Qty = a.Qty - b.FNoStockInQty
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超了未入库数{2}。", s.ERP_NO, s.ERP_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        /// <summary>
        /// 生产订单接口与生产用入库单信息对比验证
        /// </summary>
        /// <param name="mes">MES推过来的数据类</param>
        /// <param name="indata">ERP的生产入库单信息</param>
        /// <returns></returns>
        private string PONoPRDINSTOCKQtyCheck(List<R102> mes, List<PRDINSTOCK> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.ERP_MO, a.MO_SEQ } into b
            //         select new
            //         {
            //             ERP_NO = b.Key.ERP_MO,
            //             ERP_SEQ = b.Key.MO_SEQ,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.ERP_MO, a.MO_SEQ }
                  ).Except(
                from b in indata select new { ERP_MO = b.FBillNo, MO_SEQ = b.FSeq.ToString() }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},行号{1}。", p.ERP_MO, p.MO_SEQ);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.ERP_NO, B = a.ERP_SEQ } equals new { A = b.FBillNo, B = b.FSeq.ToString() }
            //             where a.Qty > b.FNoStockInQty
            //             select new
            //             {
            //                 ERP_NO = a.ERP_NO,
            //                 ERP_SEQ = a.ERP_SEQ,
            //                 Qty = a.Qty - b.FNoStockInQty
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超了未入库数{2}。", s.ERP_NO, s.ERP_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }

        private string PONoSUBRETURNMTRLCheck(List<R019_1> mes, List<SUBRETURNMTRL> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            var poExcept = (from a in mes
                            select new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID }
                  ).Except(
                from b in indata select new { ERP_MO = b.FBillNo, MAIN_ID = b.FMainMaterial, MTRL_ID = b.FMaterial }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0}，主料{1}、替代料{2}。", p.ERP_MO, p.MAIN_ID, p.MTRL_ID);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.ERP_NO, B = a.ERP_SEQ } equals new { A = b.FBillNo, B = b.FSeq.ToString() }
            //             where a.Qty > b.FWipQty
            //             select new
            //             {
            //                 ERP_NO = a.ERP_NO,
            //                 ERP_SEQ = a.ERP_SEQ,
            //                 Qty = a.Qty - b.FWipQty
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超了未入库数{2}。", s.ERP_NO, s.ERP_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        private string PONoPRDReturnMtrlCheck(List<R019> mes, List<PRDReturnMtrl> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.ERP_MO, a.MAIN_ID,a.MTRL_ID } into b
            //         select new
            //         {
            //             ERP_NO = b.Key.ERP_MO,
            //             MAIN_ID = b.Key.MAIN_ID,
            //             MTRL_ID=b.Key.MTRL_ID,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID }
                  ).Except(
                from b in indata select new { ERP_MO = b.FBillNo, MAIN_ID = b.FMainMaterial, MTRL_ID = b.FMaterial }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},主料{1}、辅料{2}。", p.ERP_MO, p.MAIN_ID, p.MTRL_ID);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.ERP_NO, B = a.MAIN_ID,C=a.MTRL_ID } equals new { A = b.FBillNo, B = b.FMainMaterial,C=b.FMaterial }
            //             where a.Qty > b.FWipQty
            //             select new
            //             {
            //                 ERP_NO = a.ERP_NO,
            //                 MAIN_ID = a.MAIN_ID,
            //                 MTRL_ID=a.MTRL_ID,
            //                 Qty = a.Qty - b.FWipQty
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},主料{1}、辅料{2}，超了未入库数{3}。", s.ERP_NO, s.MAIN_ID,s.MTRL_ID, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        private string PONoSTKTransferDirectCheck(List<R010> mes, List<STKTransferDirect> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.AUDIT_ID, a.AUDIT_SEQ } into b
            //         select new
            //         {
            //             AUDIT_ID = b.Key.AUDIT_ID,
            //             AUDIT_SEQ = b.Key.AUDIT_SEQ,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.AUDIT_ID, a.AUDIT_SEQ }
                  ).Except(
                from b in indata select new { AUDIT_ID = b.FBillNo, AUDIT_SEQ = b.FSeq.ToString() }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},行号{1}。", p.AUDIT_ID, p.AUDIT_SEQ);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.AUDIT_ID, B = a.AUDIT_SEQ } equals new { A = b.FBillNo, B = b.FSeq.ToString() }
            //             where a.Qty > b.FRemainOutQty
            //             select new
            //             {
            //                 AUDIT_ID = a.AUDIT_ID,
            //                 AUDIT_SEQ = a.AUDIT_SEQ,
            //                 Qty = a.Qty - b.FRemainOutQty
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超了未入库数{2}。", s.AUDIT_ID, s.AUDIT_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }

        private string PONoSTKTransferDirectCheck_2(List<R010> mes, List<STKTransferDirect_2> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.AUDIT_ID, a.AUDIT_SEQ } into b
            //         select new
            //         {
            //             PO_ID = b.Key.AUDIT_ID,
            //             PO_SEQ = b.Key.AUDIT_SEQ,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.AUDIT_ID, a.AUDIT_SEQ }
                  ).Except(
                from b in indata select new { AUDIT_ID = b.FBillNo, AUDIT_SEQ = b.FSeq.ToString() }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},行号{1}。", p.AUDIT_ID, p.AUDIT_SEQ);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.PO_ID, B = a.PO_SEQ } equals new { A = b.FBillNo, B = b.FSeq.ToString() }
            //             where a.Qty > b.FQty
            //             select new
            //             {
            //                 PO_ID = a.PO_ID,
            //                 PO_SEQ = a.PO_SEQ,
            //                 Qty = a.Qty - b.FQty
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超了原始调拨数{2}。", s.PO_ID, s.PO_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }

        private string PONoSALRETURNSTOCKCheck(List<R172C> mes, List<SALRETURNSTOCK> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.AUDIT_ID, a.AUDIT_SEQ } into b
            //         select new
            //         {
            //             AUDIT_ID = b.Key.AUDIT_ID,
            //             AUDIT_SEQ = b.Key.AUDIT_SEQ,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.AUDIT_ID, a.AUDIT_SEQ }
                  ).Except(
                from b in indata select new { AUDIT_ID = b.FBillNo, AUDIT_SEQ = b.FSeq.ToString() }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},行号{1}。", p.AUDIT_ID, p.AUDIT_SEQ);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.AUDIT_ID, B = a.AUDIT_SEQ } equals new { A = b.FBillNo, B = b.FSeq.ToString() }
            //             where a.Qty > b.FRemainOutQty
            //             select new
            //             {
            //                 PO_ID = a.AUDIT_ID,
            //                 PO_SEQ = a.AUDIT_SEQ,
            //                 Qty = a.Qty - b.FRemainOutQty
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超过了未出库数{2}。", s.PO_ID, s.PO_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        private string PONoSALOUTSTOCKCheck(List<R104> mes, List<SALOUTSTOCK> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.AUDIT_ID, a.AUDIT_SEQ } into b
            //         select new
            //         {
            //             AUDIT_ID = b.Key.AUDIT_ID,
            //             AUDIT_SEQ = b.Key.AUDIT_SEQ,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.AUDIT_ID, a.AUDIT_SEQ }
                  ).Except(
                from b in indata select new { AUDIT_ID = b.FBillNo, AUDIT_SEQ = b.FSeq.ToString() }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},行号{1}。", p.AUDIT_ID, p.AUDIT_SEQ);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.AUDIT_ID, B = a.AUDIT_SEQ } equals new { A = b.FBillNo, B = b.FSeq.ToString() }
            //             where a.Qty > b.FRemainOutQty
            //             select new
            //             {
            //                 PO_ID = a.AUDIT_ID,
            //                 PO_SEQ = a.AUDIT_SEQ,
            //                 Qty = a.Qty - b.FRemainOutQty
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超过了未出库数{2}。", s.PO_ID, s.PO_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        private string PONoPRDFeedMtrCheck(List<R062> mes, List<PrdPpbomEntry> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID } into b
            //         select new
            //         {
            //             ERP_NO = b.Key.ERP_MO,
            //             MAIN_ID = b.Key.MAIN_ID,
            //             MTRL_ID = b.Key.MTRL_ID,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID }
                  ).Except(
                from b in indata select new { ERP_MO = b.FBillNo, MAIN_ID = b.FMainMaterial, MTRL_ID = b.FMaterial }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},主料{1}、替代料{2}。", p.ERP_MO, p.MAIN_ID, p.MTRL_ID);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = (from a in rs
            //              join b in indata on new { A = a.ERP_NO, B = a.MAIN_ID, C = a.MTRL_ID } equals new { A = b.FBillNo, B = b.FMainMaterial, C = b.FMaterial }
            //              where a.Qty > (decimal.Parse(b.FNeedQty2))
            //              select new
            //              {
            //                  ERP_NO = a.ERP_NO,
            //                  MAIN_ID = a.MAIN_ID,
            //                  MTRL_ID = a.MTRL_ID,
            //                  Qty = decimal.Parse(b.FNeedQty2)
            //              }).ToList();
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},主料{1}、替代料{2}，超过需求数量：{3}。", s.ERP_NO, s.MAIN_ID, s.MTRL_ID, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        private string PONoSUBFeedMtrCheck(List<R062_1> mes, List<SUBPickMtrl> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID } into b
            //         select new
            //         {
            //             ERP_NO = b.Key.ERP_MO,
            //             MAIN_ID = b.Key.MAIN_ID,
            //             MTRL_ID = b.Key.MTRL_ID,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.ERP_MO, a.MAIN_ID, a.MTRL_ID }
                  ).Except(
                from b in indata select new { ERP_MO = b.FBillNo, MAIN_ID = b.FMainMaterial, MTRL_ID = b.FMaterial }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},主料{1}、替代料{2}。", p.ERP_MO, p.MAIN_ID, p.MTRL_ID);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = (from a in rs
            //              join b in indata on new { A = a.ERP_NO, B = a.MAIN_ID, C = a.MTRL_ID } equals new { A = b.FBillNo, B = b.FMainMaterial, C = b.FMaterial }
            //              where a.Qty > (decimal.Parse(b.FNeedQty2))
            //              select new
            //              {
            //                  ERP_NO = a.ERP_NO,
            //                  MAIN_ID = a.MAIN_ID,
            //                  MTRL_ID = a.MTRL_ID,
            //                  Qty = decimal.Parse(b.FNeedQty2)
            //              }).ToList();
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},主料{1}、替代料{2}，超过需求数量：{3}。", s.ERP_NO, s.MAIN_ID, s.MTRL_ID, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }
        private string PONoPURMRBCheck(List<R006> mes, List<PurchaseOrder> indata)
        {
            //检测源单是否存在
            if (!indata.Any()) { return "沃比提示：必须有源单..."; }
            StringBuilder stringBuilder = new StringBuilder();
            //var rs = from a in mes.AsEnumerable()
            //         group a by new { a.PO_ID, a.PO_SEQ } into b
            //         select new
            //         {
            //             PO_ID = b.Key.PO_ID,
            //             PO_SEQ = b.Key.PO_SEQ,
            //             Qty = b.Sum(s => decimal.Parse(s.QUANTITY))
            //         };
            //检测是否所有源单都存在
            var poExcept = (from a in mes
                            select new { a.PO_ID, a.PO_SEQ }
                  ).Except(
                from b in indata select new { PO_ID = b.FBillNo, PO_SEQ = b.FPOOrderEntry_FSeq.ToString() }
                );
            if (poExcept.Any())
            {
                foreach (var p in poExcept)
                {
                    string messtr = string.Format("没找到符合条件的订单：{0},行号{1}。", p.PO_ID, p.PO_SEQ);
                    stringBuilder.Append(messtr);
                }
                return stringBuilder.ToString();
            }
            ////与源单剩余入库数量对比
            //var rs_mes = from a in rs
            //             join b in indata on new { A = a.PO_ID, B = a.PO_SEQ } equals new { A = b.FBillNo, B = b.FPOOrderEntry_FSeq.ToString() }
            //             where a.Qty > (decimal.Parse(b.FRemainInStockQty) - decimal.Parse(b.FRemainStockINQty))
            //             select new
            //             {
            //                 PO_ID = a.PO_ID,
            //                 PO_SEQ = a.PO_SEQ,
            //                 Qty = a.Qty - (decimal.Parse(b.FRemainInStockQty) - decimal.Parse(b.FRemainStockINQty))
            //             };
            //if (rs_mes != null)
            //{
            //    foreach (var s in rs_mes)
            //    {
            //        string messtr = string.Format("订单：{0},行号{1}，超了已入库数{2}。", s.PO_ID, s.PO_SEQ, s.Qty);
            //        stringBuilder.Append(messtr);
            //    }
            //}
            return stringBuilder.ToString();
        }

        /// <summary>
        /// 在RB系统校验生产补料的源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R062cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<PRD_FeedMtrl> CreateBatchPRDFeedMtrl(List<R062> R062cs, List<PrdPpbomEntry> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<PRD_FeedMtrl> list = new List<PRD_FeedMtrl>();
            //源单校验
            string pocheck = PONoPRDFeedMtrCheck(R062cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }

            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R062cs where a != null select new { ERP_No = a.ERP_MO, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.ERP_No);
                var order = purchase.FirstOrDefault();
                string monumber = order!.FMoBillNo1;
                int parseq = int.Parse(order!.FMoEntrySeq1);
                int moid = int.Parse(order!.FMoId1);
                int moentryid = int.Parse(order!.FMoEntryId1);
                //string parunit = order!.FUnitID;
                string supplynumber = order!.FWorkshopNumber;
                string parnumber = order!.FParentMaterial;
                string pbomnumber = order!.FBillNo;
                int pid = int.Parse(order!.FID);
                //decimal sqty = decimal.Parse(order!.FNeedQty2);
                string newtype = _shareController.GetDesFormID("R062", order!.FMOType);

                //创建单据头
                PRD_FeedMtrl inStock = new PRD_FeedMtrl()
                {
                    FBillType = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FDate = DateTime.Now.ToShortDateString(),
                    FWorkShopId = new Dictionary<string, string>() { { "FNUMBER", supplynumber } },
                    F_SBMI_QUEUE_ID = string.Format("R062:{0}", R062cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<PRD_FeedMtrl.Entity> entries = new List<PRD_FeedMtrl.Entity>();
                //工单行明细...按生产用料清单号
                List<R062> entrygroup = R062cs.Where(s => s.ERP_MO == head.ERP_No).ToList();
                foreach (var entry in entrygroup)
                {
                    //校验仓库是否正确
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrWhiteSpace(stocknumber))
                    {
                        string mestr = string.Format("{0}的主料{1}、辅料{2}", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID, "辅料行【仓库】不能为空");
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = string.Format("{0}【物料编码】不能为空", entry.ERP_MO);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = string.Format("{0}{1}格式不正确", entry.ERP_MO, entry.QUANTITY);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息
                    var sourentry = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FMainMaterial == entry.MAIN_ID && s.FMaterial == entry.MTRL_ID);
                    if (!sourentry.Any())
                    {
                        string mestr = string.Format("{0}主料{1}、辅料{2}【数量】没有源单信息", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    else
                    {
                        PrdPpbomEntry pRDPPBOM = sourentry.FirstOrDefault()!;
                        int pentryid = int.Parse(pRDPPBOM.FEntryID);
                        int pseq = int.Parse(pRDPPBOM.FEntity_FSeq);
                        //当前单位编号
                        string unitnumber = pRDPPBOM.FUnitNumber;
                        //当前单位实体
                        var unit = units.Find(s => s.FNumber == unitnumber);
                        //当前单位的换算率
                        decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                        //当前单位的精度，通过接口参数自动校验
                        int jd = unit.FPrecision;
                        if (qty != Math.Round(qty, jd))
                        {
                            //2023-07-19更新6月补丁之后添加校验
                            //因为如果提供的QTY小数点精度大于jd时，在BOS中由主库存单位数量（FSTOCKACTUALQTY）引发值更新事件
                            //会导致字段FBaseStockActualQty与FBaseActualQty不相等。
                            string mestr = string.Format("{0}主料{1}的小数点精度只能是{2}位", entry.ERP_MO, entry.MAIN_ID, jd);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        string baseunitnumber = pRDPPBOM.FBaseUnitNumber;
                        PRD_FeedMtrl.Entity stockEntry = new PRD_FeedMtrl.Entity()
                        {
                            FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                            FUnitID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                            FStockUnitId = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                            FStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                            FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                            FMoBillNo = pRDPPBOM.FMoBillNo1,
                            FMoEntryId = int.Parse(pRDPPBOM.FMoEntryId1),
                            FMoId = int.Parse(pRDPPBOM.FMoId1),
                            FMoEntrySeq = int.Parse(pRDPPBOM.FMoEntrySeq1),
                            FPPBomBillNo = pRDPPBOM.FBillNo,
                            FPPBomEntryId = int.Parse(pRDPPBOM.FEntryID),
                            FAppQty = qty,
                            FActualQty = qty,
                            FStockAppQty = qty,
                            FStockActualQty = qty,
                            FBaseAppQty = Math.Round(qty / unithsl, jd),
                            FBaseActualQty = Math.Round(qty / unithsl, jd),
                            FBaseStockActualQty = Math.Round(qty / unithsl, jd),
                            FParentMaterialId = new Dictionary<string, string>() { { "FNumber", pRDPPBOM.FParentMaterial } },
                            FBaseUnitId = new Dictionary<string, string>() { { "FNumber", baseunitnumber } },
                            FEntryWorkShopId = new Dictionary<string, string>() { { "FNumber", supplynumber } },
                            FEntrySrcEntrySeq = int.Parse(pRDPPBOM.FEntity_FSeq),
                            FEntrySrcBillNo = pRDPPBOM.FBillNo,
                            FEntrySrcEnteryId = int.Parse(pRDPPBOM.FID),
                            FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new PRD_FeedMtrl.Entity.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", entry.MTRL_VER } } },
                            FEntity_Link = int.Parse(pRDPPBOM.FID) == 0 ? new List<PRD_FeedMtrl.Entity.Entity_Link>() : new List<PRD_FeedMtrl.Entity.Entity_Link>()
                            {
                                new PRD_FeedMtrl.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =int.Parse(pRDPPBOM.FID),
                                    FEntity_Link_FSId = pentryid,
                                    FEntity_Link_FBaseActualQtyOld = Math.Round(qty / unithsl, jd),
                                    FEntity_Link_FBaseActualQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                        };
                        entries.Add(stockEntry);
                    }

                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }
        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的委外补料单实体类
        /// </summary>
        /// <param name="R014_1cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<SUB_FeedMtrl> CreateBatchSUBFeedMtrl(List<R062_1> R014_1cs, List<SUBPickMtrl> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<SUB_FeedMtrl> list = new List<SUB_FeedMtrl>();
            //源单校验
            string pocheck = PONoSUBFeedMtrCheck(R014_1cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R014_1cs where a != null select new { ERP_No = a.ERP_MO, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.ERP_No);
                if (!purchase.Any())
                {
                    string mestr = head.ERP_No + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);
                string newtype = _shareController.GetDesFormID("R062_1", order!.FBillTypeID);

                //创建单据头
                SUB_FeedMtrl inStock = new()
                {
                    FBillType = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FStockOrgId = new Dictionary<string, string>() { { "FNumber", order.FSupplyOrg! } },
                    FSubOrgId = new Dictionary<string, string>() { { "FNumber", order.FSubOrgId! } },
                    FSubSupplierId = new Dictionary<string, string>() { { "FNumber", order.FSupplierId! } },
                    F_SBMI_QUEUE_ID = string.Format("R062_1:{0}", R014_1cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<SUB_FeedMtrl.Entity> entries = new List<SUB_FeedMtrl.Entity>();
                //工单行明细...按生产用料清单号
                List<R062_1> entrygroup = R014_1cs.Where(s => s.ERP_MO == head.ERP_No).ToList();
                foreach (var entry in entrygroup)
                {

                    //var sourentry = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FSeq == entry.SEQ).FirstOrDefault();
                    var sourline = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FMainMaterial == entry.MAIN_ID && s.FMaterial == entry.MTRL_ID);
                    if (!sourline.Any())
                    {
                        string mestr = string.Format("{0}主料{1}、辅料{2}没有可用数据", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    else
                    {
                        var sourentry = sourline.FirstOrDefault();
                        //应入库数量，来自原单
                        decimal sqty = _shareController.DecimalPar(entry.QUANTITY);
                        if (sqty <= 0)
                        {
                            string mestr = entry.ERP_MO + "【数量】不正确";
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        //校验仓库是否正确
                        string stocknumber = entry.STORE_ID;
                        if (string.IsNullOrWhiteSpace(stocknumber))
                        {
                            string mestr = string.Format("{0}主料{1}、辅料{2}【仓库】不能为空", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        string martnumber = entry.MTRL_ID;
                        if (string.IsNullOrWhiteSpace(martnumber))
                        {
                            string mestr = entry.ERP_MO + "【物料编码】不能为空";
                            reason += reason == "" ? mestr : "；" + mestr;
                        }

                        if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                                                             //对应这一行属于源单的信息                 
                        int pentryid = sourentry.FEntryID;
                        //int pseq = int.Parse(entry.SEQ);
                        //当前单位编号
                        string unitnumber = sourentry.FUnitID2;
                        int poseq = 0;
                        int.TryParse(sourentry.FPurOrderEntrySeq, out poseq);
                        //当前单位实体
                        var unit = units.Find(s => s.FNumber == unitnumber);
                        //当前单位的换算率
                        decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                        //当前单位的精度
                        int jd = unit.FPrecision;
                        if (sqty != Math.Round(sqty, jd))
                        {
                            //2023-07-19更新6月补丁之后添加校验
                            //因为如果提供的QTY小数点精度大于jd时，在BOS中由主库存单位数量（FSTOCKACTUALQTY）引发值更新事件
                            //会导致字段FBaseStockActualQty与FBaseActualQty不相等。
                            string mestr = string.Format("{0}主料{1}的小数点精度只能是{2}位", entry.ERP_MO, entry.MAIN_ID, jd);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        string baseunitnumber = sourentry.FBaseUnitID1;
                        SUB_FeedMtrl.Entity stockEntry = new SUB_FeedMtrl.Entity()
                        {
                            FSrcBillNo = sourentry.FBillNo,
                            FSrcEntryId = pentryid,
                            FSrcEntrySeq = int.Parse(sourentry.FSeq),
                            FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                            FUnitID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                            FAppQty = sqty > 0 ? sqty : 0,
                            FActualQty = sqty,
                            FStockAppQty = sqty > 0 ? sqty : 0,
                            FStockActualQty = sqty,
                            FBaseAppQty = sqty > 0 ? Math.Round(sqty / unithsl, jd) : 0,
                            FBaseActualQty = Math.Round(sqty / unithsl, jd),       //确定基本单位和常用单位之间的换算关系
                            FBaseStockActualQty = Math.Round(sqty / unithsl, jd),
                            FStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                            FPOOrderBillNo = sourentry.FPurOrderNo,
                            FPOOrderSeq = poseq,
                            FSupplierId = new Dictionary<string, string>() { { "FNumber", sourentry.FSupplierId } },
                            //StockLocId = new SUB_PickMtrl.Entity.FStockLocId()
                            //{
                            //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } },
                            //    FSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                            //},
                            //FLot = new Dictionary<string, string>() { { "FNumber", entry.ERP_LOTNO } },
                            FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                            FParentMaterialId = new Dictionary<string, string>() { { "FNumber", sourentry.FProductNumber } },
                            FSrcBillId = sourentry.FID,
                            FSUBReqId = sourentry.FSubReqId,
                            FSUBReqBillNo = sourentry.FSubReqBillNo,
                            FReserveType = sourentry.FReserveType,
                            FSettleOrgId = new Dictionary<string, string>() { { "FNumber", sourentry.FSettleOrgId } },
                            FPPBomBillNo = sourentry.FBillNo,
                            FPPBomEntryId = pentryid,
                            FSubReqEntryId = int.Parse(sourentry.FSubReqEntryId),
                            FSUBReqEntrySeq = sourentry.FSubReqEntrySeq,
                            FBaseUnitId = new Dictionary<string, string>() { { "FNumber", baseunitnumber } },
                            FStockUnitId = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                            //FSrcPickEntryId = int.Parse(sourentry.FID),
                            //FPOOrderSeq = int.Parse(sourentry.FSeq),
                            FStockStatusId = new Dictionary<string, string>() { { "FNumber", sourentry.FStockStatusId } },
                            FOwnerTypeId = sourentry.FOwnerTypeId,
                            FOwnerId = !string.IsNullOrEmpty(sourentry.FParentOwnerId) ? new Dictionary<string, string>() { { "FNumber", sourentry.FParentOwnerId } } : new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                            FParentOwnerTypeId = sourentry.FParentOwnerTypeId,
                            FParentOwnerId = !string.IsNullOrEmpty(sourentry.FParentOwnerId) ? new Dictionary<string, string>() { { "FNumber", sourentry.FParentOwnerId } } : new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                            FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new SUB_FeedMtrl.Entity.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", entry.MTRL_VER } } },
                            FEntity_Link = sourentry.FID == 0 ? new List<SUB_FeedMtrl.Entity.Entity_Link>() : new List<SUB_FeedMtrl.Entity.Entity_Link>()
                            {
                                new SUB_FeedMtrl.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =sourentry.FID,
                                    FEntity_Link_FSId = pentryid
                                }
                            }
                        };
                        entries.Add(stockEntry);
                    }
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }
        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R006s">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<PUR_MRB> CreateBatchPURMRB(List<R006> R006s, List<PurchaseOrder> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<PUR_MRB> list = new List<PUR_MRB>();
            //源单校验
            string pocheck = PONoPURMRBCheck(R006s, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组
            var headgroup = (from a in R006s where a != null select new { PO_ID = a.PO_ID, SUP_ID = a.SUP_ID }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.PO_ID).ToList();
                if (purchase.Count == 0)
                {
                    string mestr = head.PO_ID + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                var order = purchase.FirstOrDefault();
                string supplynumber = order!.FSupplierId;
                string curnumber = order!.FSettleCurrId;
                string ecchangetype = order!.FExchangeTypeId;
                decimal ecchange = decimal.Parse(order!.FExchangeRate);
                decimal purqty = _shareController.DecimalPar(order!.FRemainStockINQty);
                decimal price = _shareController.DecimalPar(order!.FPrice);
                decimal taxprice = _shareController.DecimalPar(order!.FTaxPrice);
                decimal taxret = decimal.Parse(order!.FExchangeRate);
                string newtype = _shareController.GetDesFormID("R006", order!.FBillTypeID);
                //创建单据头
                PUR_MRB inStock = new PUR_MRB()
                {
                    FBillTypeID = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    //FOwnerTypeIdHead = ,     //货主类型、货主跟【退料类型】有关，当退料类型为“检验退料”时取供应商，否则为采购方！！！
                    FOwnerIdHead = new Dictionary<string, string>() { { "FNUMBER", LocalStaticRequest.DefaultOrg } },//其他字段已有默认
                    //FBusinessType = order.FBillTypeID == "CGDD07_SYS" ? "VMICG" : order.FBillTypeID == "CGDD02_SYS" ? "WW" : "CG",
                    FDate = DateTime.Now.ToShortDateString(),
                    FSettleId = new Dictionary<string, string>() { { "FNUMBER", supplynumber } },
                    FCHARGEID = new Dictionary<string, string>() { { "FNUMBER", supplynumber } },
                    FSupplierID = new Dictionary<string, string>() { { "FNumber", supplynumber } },
                    FACCEPTORID = new Dictionary<string, string>() { { "FNumber", supplynumber } },
                    F_SBMI_QUEUE_ID = string.Format("R006:{0}", R006s.FirstOrDefault().QUEUE_ID), //如果不是盘古，建议放在单据体！！！
                    F_Return_instructions = R006s.FirstOrDefault().REMARK,
                    FPURCHASERID = new Dictionary<string, string>() { { "FNumber", order.FPurchaserNumber } }
                };
                bool giveaway = order.FGiveAway;
                //财务信息
                PUR_MRB.PURMRBFIN purmrbfin = new PUR_MRB.PURMRBFIN()
                {
                    FPAYCONDITIONID = new Dictionary<string, string>() { { "FNUMBER", order.FPayConditionId } },
                    FSettleCurrId = new Dictionary<string, string>() { { "FNUMBER", order.FSettleCurrId } },
                    FIsIncludedTax = order.FIsIncludedTax,
                    FISPRICEEXCLUDETAX = order.FISPRICEEXCLUDETAX,
                    FPRICETIMEPOINT = order.FPriceTimePoint,
                    FLOCALCURRID = new Dictionary<string, string>() { { "FNUMBER", order.FLocalCurrId } },
                    FEXCHANGETYPEID = new Dictionary<string, string>() { { "FNUMBER", order.FExchangeTypeId } },
                    FEXCHANGERATE = decimal.Parse(order.FExchangeRate)
                };
                inStock.FPURMRBFIN = purmrbfin;

                //添加单据体，按订单号找到它的明细数据                
                List<PUR_MRB.PURMRBENTRY> entries = new List<PUR_MRB.PURMRBENTRY>();
                var entrygroup = R006s.Where(s => s.PO_ID == head.PO_ID && s.SUP_ID == head.SUP_ID).ToList();
                foreach (var entry in entrygroup)
                {
                    //源单内码
                    int sid = 0;
                    //源单行号
                    int sentryid = 0;
                    //采购数量
                    decimal dqty = 0;
                    //剩余未入库数量
                    decimal sqty = 0;
                    //最大退料数量数量
                    decimal yqty = 0;
                    //单位编号
                    string unitnumber = string.Empty;
                    string baseunitnumber = string.Empty;
                    int jd = 0;
                    decimal unithsl = 0;
                    var sourceentry = purchaseOrders.Where(s => s.FBillNo == entry.PO_ID && s.FPOOrderEntry_FSeq == entry.PO_SEQ);
                    if (sourceentry != null)
                    {
                        var source = sourceentry.FirstOrDefault();
                        //源单内码
                        sid = int.Parse(source.FID);
                        //源单行号
                        sentryid = int.Parse(source.FPOOrderEntry_FEntryID);
                        //采购数量
                        dqty = _shareController.DecimalPar(source.FRemainInStockQty);
                        //剩余未入库数量
                        sqty = _shareController.DecimalPar(source.FRemainStockINQty);
                        //最大退料数量数量
                        yqty = dqty - sqty;
                        //单位编号
                        unitnumber = source.UnitNumber;
                        //当前单位实体
                        var unit = units.Find(s => s.FNumber == unitnumber);
                        //当前单位的换算率
                        unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                        //当前单位的精度
                        jd = unit.FPrecision;
                    }
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrEmpty(stocknumber))
                    {
                        string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【仓库】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrEmpty(martnumber))
                    {
                        string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    if (string.IsNullOrEmpty(unitnumber))
                    {
                        string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【单位编码】不存在";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > yqty)
                    //{
                    //    string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【数量】不允许大于累计入库数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    PUR_MRB.PURMRBENTRY stockEntry = new PUR_MRB.PURMRBENTRY()
                    {
                        FOWNERTYPEID = order.FBillTypeID == "CGDD07_SYS" ? "BD_Supplier" : "BD_OwnerOrg",
                        FOWNERID = new Dictionary<string, string>() { { "FNUMBER", order.FBillTypeID == "CGDD07_SYS" ? supplynumber : LocalStaticRequest.DefaultOrg } },
                        FORDERNO = order!.FBillNo,
                        FSRCBillNo = order!.FBillNo,
                        FPOORDERENTRYID = sentryid,
                        FSRCSeq = order.FPOOrderEntry_FSeq,
                        FMATERIALID = new Dictionary<string, string>() { { "FNumber", martnumber } },
                        FUnitID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                        FSTOCKID = new Dictionary<string, string>() { { "FNumber", stocknumber } },
                        FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } }, //必须填写编号
                        FNOTE = entry.REMARK,
                        FRMREALQTY = qty,
                        FKEAPAMTQTY = qty,
                        FBASEREPLAYQTY = Math.Round(qty / unithsl, jd),
                        FPriceBaseQty = Math.Round(qty / unithsl, jd),
                        FPrice = price,
                        FTAXPRICE = taxprice,
                        FGiveAway = giveaway,
                        FBASEUNITPRICE = Math.Round(price / unithsl, jd),
                        FSetPriceUnitID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                        FPRICEUNITID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                        FCarryUnitId = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                        FVERSIONNO = entry.MTRL_VER,
                        FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new PUR_MRB.PURMRBENTRY.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", entry.MTRL_VER } } },
                        FPURMRBENTRY_Link = sid == 0 ? new List<PUR_MRB.PURMRBENTRY.PURMRBENTRY_Link>() : new List<PUR_MRB.PURMRBENTRY.PURMRBENTRY_Link>()
                            {
                                new PUR_MRB.PURMRBENTRY.PURMRBENTRY_Link()
                                {
                                    FPURMRBENTRY_Link_FSBillId =sid.ToString(),
                                    FPURMRBENTRY_Link_FSId = sentryid.ToString(),
                                    //FPURMRBENTRY_Link_FRemainInStockBaseQtyOld = yqty,
                                    //FPURMRBENTRY_Link_FRemainInStockBaseQty = qty,
                                    FPURMRBENTRY_Link_FBaseUnitQtyOld = Math.Round(yqty/unithsl,jd),
                                    FPURMRBENTRY_Link_FBaseUnitQty = Math.Round(qty/unithsl,jd)
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }
                inStock.FPURMRBENTRY = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }
        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="r170cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_InStock> CreateBatchSTKInStock(List<R170C> r170cs, List<PurchaseOrder> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<STK_InStock> list = new List<STK_InStock>();
            //源单校验
            string pocheck = PONoInStockQtyCheck(r170cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            ////基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;

            //var mary = from a in r170cs select a.MTRL_ID;
            //List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;

            //将inStocks按源单分组，形成单据头组
            var headgroup = (from a in r170cs where a != null select new { PO_ID = a.PO_ID, SUP_ID = a.SUP_ID, QUEUE_ID = a.QUEUE_ID, ASN_ID = a.ASN_ID }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.PO_ID && s.FSupplierId == head.SUP_ID).ToList();
                if (purchase.Count == 0)
                {
                    string mestr = head.PO_ID + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                var order = purchase.FirstOrDefault();
                string supplynumber = order!.FSupplierId;
                string curnumber = order!.FSettleCurrId;
                string ecchangetype = order!.FExchangeTypeId;
                decimal ecchange = decimal.Parse(order!.FExchangeRate);
                decimal purqty = _shareController.DecimalPar(order!.FRemainStockINQty);
                decimal price = _shareController.DecimalPar(order!.FPrice);
                decimal taxprice = _shareController.DecimalPar(order!.FTaxPrice);
                decimal taxnetprice = _shareController.DecimalPar(order!.FTaxNetPrice);
                string newtype = _shareController.GetDesFormID("R170C", order!.FBillTypeID);
                //创建单据头
                STK_InStock inStock = new STK_InStock()
                {
                    //FBillTypeID = new Dictionary<string, string>() { 
                    //    { "FNUMBER", order.FBillTypeID == "CGDD07_SYS" ? "RKD07_SYS" :order.FBillTypeID == "CGDD02_SYS" ? "RKD03_SYS" : billtype } 
                    //},
                    FBillTypeID = new Dictionary<string, string>() {
                        { "FNUMBER", string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FDate = DateTime.Now.ToShortDateString(),
                    FSupplyId = new Dictionary<string, string>() { { "FNUMBER", supplynumber } },
                    FSupplierId = new Dictionary<string, string>() { { "FNUMBER", supplynumber } },
                    FSettleId = new Dictionary<string, string>() { { "FNUMBER", supplynumber } },
                    FChargeId = new Dictionary<string, string>() { { "FNumber", supplynumber } },
                    //FBusinessType = order.FBillTypeID == "CGDD07_SYS" ? "VMICG" : order.FBillTypeID == "CGDD02_SYS" ? "WW" : "CG",        //这3个字段能通过单据转换规则自动添加！！！
                    //FOwnerTypeIdHead = order.FBillTypeID != "RKD07_SYS" ? "BD_Supplier" : "BD_OwnerOrg",
                    //FOwnerIdHead = new Dictionary<string, string>() { { "FNUMBER", order.FBillTypeID == "CGDD07_SYS" ? supplynumber : LocalStaticRequest.DefaultOrg } },
                    F_NOTE = "由IMS自动生成:" + r170cs.FirstOrDefault().REMARK + r170cs.FirstOrDefault().RO_ID,
                    F_SBMI_QUEUE_ID = string.Format("R170C:{0}", head.QUEUE_ID), //MES推单标识！！！
                    F_DELIVERY_NOTE_NUMBER = head.ASN_ID
                };
                bool giveaway = order.FGiveAway;
                //财务信息
                STK_InStock.InStockFin stockFin = new STK_InStock.InStockFin()
                {
                    FSettleOrgId = new Dictionary<string, string>() { { "FNUMBER", LocalStaticRequest.DefaultOrg } },
                    FSettleCurrId = new Dictionary<string, string>() { { "FNumber", curnumber } },
                    FExchangeTypeId = new Dictionary<string, string>() { { "FNumber", ecchangetype } },
                    FExchangeRate = ecchange,
                    FPriceTimePoint = order.FPriceTimePoint,
                    FPayConditionId = new Dictionary<string, string>() { { "FNumber", order.FPayConditionId } },
                    FLocalCurrId = new Dictionary<string, string>() { { "FNumber", order.FLocalCurrId } },
                    FIsIncludedTax = order.FIsIncludedTax,
                    FISPRICEEXCLUDETAX = order.FISPRICEEXCLUDETAX
                };
                inStock.FInStockFin = stockFin;
                //添加单据体，按订单号找到它的明细数据                
                List<STK_InStock.InStockEntry> entries = new List<STK_InStock.InStockEntry>();
                var entrygroup = r170cs.Where(s => s.PO_ID == head.PO_ID).ToList();
                foreach (var entry in entrygroup)
                {
                    //源单内码
                    int sid = 0;
                    //源单行号
                    int sentryid = 0;
                    //剩余未入库数量
                    decimal sqty = 0;
                    //单位编号
                    string unitnumber = string.Empty;
                    string baseunitnumber = string.Empty;
                    //int jd = 0;
                    //decimal unithsl = 0;
                    //var sourceentry = purchaseOrders.Where(s => s.FBillNo == entry.PO_ID && s.FK3EntryID == int.Parse(entry.PO_SEQ));
                    //物料版本号
                    //string itemversion = marylist.Find(s => s.FNumber == entry.MTRL_ID).F_PKGR_ITEMVERSION;
                    var sourceentry = purchaseOrders.Where(s => s.FBillNo == entry.PO_ID &&
                    s.FPOOrderEntry_FSeq == entry.PO_SEQ && s.Mtrl == entry.MTRL_ID);
                    if (sourceentry.Count() != 0)
                    {
                        var source = sourceentry.FirstOrDefault();
                        //源单内码
                        sid = int.Parse(source.FID);
                        //源单行号
                        sentryid = int.Parse(source.FPOOrderEntry_FEntryID);
                        //剩余未入库数量
                        sqty = _shareController.DecimalPar(source.FRemainStockINQty);
                        //单位编号
                        unitnumber = source.UnitNumber;
                        baseunitnumber = source.FBaseUnitNumber;
                    }
                    else
                    {
                        string mestr = entry.PO_ID + "行号【" + entry.PO_SEQ + "】、物料编码【" + entry.MTRL_ID + "】与ERP不匹配";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrEmpty(stocknumber))
                    {
                        string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【仓库】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrEmpty(martnumber))
                    {
                        string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    if (string.IsNullOrEmpty(unitnumber))
                    {
                        string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【单位编码】不存在";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    decimal taxrate = decimal.Parse(order.FEntryTaxRate);
                    //当前单位实体
                    var unit = units.Find(s => s.FNumber == unitnumber);
                    //当前单位的换算率
                    decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                    //当前单位的精度
                    int jd = unit.FPrecision;
                    //else if (qty > sqty)
                    //{
                    //    string mestr = entry.PO_ID + "第" + entry.PO_SEQ + "行【数量】不允许大于未入库数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    STK_InStock.InStockEntry stockEntry = new STK_InStock.InStockEntry()
                    {
                        FOWNERTYPEID = order.FBillTypeID == "CGDD07_SYS" ? "BD_Supplier" : "BD_OwnerOrg",
                        FOWNERID = new Dictionary<string, string>() { { "FNUMBER", order.FBillTypeID == "CGDD07_SYS" ? supplynumber : LocalStaticRequest.DefaultOrg } },
                        FPOOrderNo = order!.FBillNo,
                        FSRCBillNo = order!.FBillNo,
                        FPOORDERENTRYID = order!.FPOOrderEntry_FEntryID,
                        FMaterialId = new Dictionary<string, string>() { { "FNumber", martnumber } },
                        FUnitID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                        FBaseUnitID = new Dictionary<string, string>() { { "FNumber", baseunitnumber } },
                        FMustQty = sqty,
                        FEntryTaxRate = taxrate,
                        FPrice = price,
                        FTaxPrice = taxprice,
                        FTaxNetPrice = taxnetprice,
                        FGiveAway = giveaway,
                        //FPRICELISTENTRY = new Dictionary<string, string>() { { "FNumber", order.FPriceListEntry } },//由于20230831与廖工确认，采购并不一定完全按价目表执行，此字段取消
                        //税额，金额、价税合计全给系统自己计算
                        FPriceUnitID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                        FRemainInStockUnitId = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                        FRemainInStockQty = qty,//经实测，FRemainInStockQty应当与FRealQty、FPriceUnitQty相等
                        FPriceUnitQty = qty,
                        FRemainInStockBaseQty = Math.Round(qty / unithsl, jd),
                        FPriceBaseQty = Math.Round(qty / unithsl, jd),
                        FRealQty = qty,
                        FStockId = new Dictionary<string, string>() { { "FNumber", stocknumber } },
                        FNote = entry.REMARK,
                        FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } }, //必须填写编号
                        FVERSIONNO = entry.MTRL_VER,
                        FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new STK_InStock.InStockEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", entry.MTRL_VER } } },
                        FInStockEntry_Link = sid == 0 ? new List<STK_InStock.InStockEntry.InStockEntry_Link>() : new List<STK_InStock.InStockEntry.InStockEntry_Link>()
                            {
                                new STK_InStock.InStockEntry.InStockEntry_Link()
                                {
                                    FInStockEntry_Link_FSBillId =sid.ToString(),
                                    FInStockEntry_Link_FSId = sentryid.ToString(),
                                    FInStockEntry_Link_FRemainInStockBaseQtyOld = Math.Round(qty / unithsl, jd),
                                    FInStockEntry_Link_FRemainInStockBaseQty = qty,
                                    FInStockEntry_Link_FBaseUnitQtyOld = Math.Round(qty / unithsl, jd),
                                    FInStockEntry_Link_FBaseUnitQty = qty
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }
                inStock.FInStockEntry = entries;
                //财务费用，与明细数据同级
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 生产领料单：在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="r014cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<PRD_PickMtrl> CreateBatchPRDPickMtrl(List<R014> r014cs, List<PrdPpbomEntry> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<PRD_PickMtrl> list = new List<PRD_PickMtrl>();
            //源单校验
            string pocheck = PONoPickMtrlQtyCheck(r014cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in r014cs where a != null select new { ERP_No = a.ERP_MO, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.ERP_No);
                //PONoPickMtrlQtyCheck中已校验，多此一举！！！
                //if (!purchase.Any())
                //{
                //    string mestr = head.ERP_No + "不存在";
                //    reason += reason == "" ? mestr : "；" + mestr;
                //    break;
                //}
                var order = purchase.FirstOrDefault();
                string monumber = order!.FMoBillNo1;
                int parseq = int.Parse(order!.FMoEntrySeq1);
                int moid = int.Parse(order!.FMoId1);
                int moentryid = int.Parse(order!.FMoEntryId1);
                //string parunit = order!.FUnitID;
                string supplynumber = order!.FWorkshopNumber;
                string parnumber = order!.FParentMaterial;
                string pbomnumber = order!.FBillNo;
                int pid = int.Parse(order!.FID);
                decimal sqty = decimal.Parse(order!.FNeedQty2);
                decimal pickqty = sqty - decimal.Parse(order!.FPickedQty);
                decimal basesqty = decimal.Parse(order!.FBaseNeedQty);
                string newtype = _shareController.GetDesFormID("R014", order!.FMOType);

                //创建单据头
                PRD_PickMtrl inStock = new PRD_PickMtrl()
                {
                    FBillType = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FDate = DateTime.Now.ToShortDateString(),
                    F_SBMI_QUEUE_ID = string.Format("R014:{0}", r014cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                    //FWorkShopId = new Dictionary<string, string>() { { "FNUMBER", supplynumber } }
                };
                //添加单据体，按订单号找到它的明细数据                
                List<PRD_PickMtrl.BillEntry> entries = new List<PRD_PickMtrl.BillEntry>();
                //工单行明细...按生产用料清单号
                List<R014> entrygroup = r014cs.Where(s => s.ERP_MO == head.ERP_No).ToList();
                foreach (var entry in entrygroup)
                {
                    //校验仓库是否正确
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrWhiteSpace(stocknumber))
                    {
                        string mestr = string.Format("{0}的主料{1}、辅料{2}", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID, "辅料行【仓库】不能为空");
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = string.Format("{0}【物料编码】不能为空", entry.ERP_MO);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = string.Format("{0}{1}格式不正确", entry.ERP_MO, entry.QUANTITY);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > sqty)
                    //{
                    //    string mestr =string.Format("{0}主料{1}、辅料{2}【数量】不允许大于未领料数", entry.ERP_MO ,entry.MAIN_ID, entry.MTRL_ID);
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息
                    var sourentry = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FMainMaterial == entry.MAIN_ID && s.FMaterial == entry.MTRL_ID);
                    if (!sourentry.Any())
                    {
                        string mestr = string.Format("{0}主料{1}、辅料{2}没有可用数据", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    else
                    {
                        int pentryid = int.Parse(sourentry.FirstOrDefault()!.FEntryID);
                        int pseq = int.Parse(sourentry.FirstOrDefault().FEntity_FSeq);
                        //当前单位编号
                        string unitnumber = sourentry.FirstOrDefault()!.FUnitNumber;
                        //当前单位实体
                        var unit = units.Find(s => s.FNumber == unitnumber);
                        //当前单位的换算率
                        decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                        //当前单位的精度
                        int jd = unit.FPrecision;
                        if (qty != Math.Round(qty, jd))
                        {
                            //2023-07-19更新6月补丁之后添加校验
                            //因为如果提供的QTY小数点精度大于jd时，在BOS中由主库存单位数量（FSTOCKACTUALQTY）引发值更新事件
                            //会导致字段FBaseStockActualQty与FBaseActualQty不相等。
                            string mestr = string.Format("{0}主料{1}的小数点精度只能是{2}位", entry.ERP_MO, entry.MAIN_ID, jd);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        //基本单位
                        string baseunitnumber = sourentry.FirstOrDefault()!.FBaseUnitNumber;
                        PRD_PickMtrl.BillEntry stockEntry = new PRD_PickMtrl.BillEntry()
                        {
                            FParentMaterialId = new Dictionary<string, string> { { "FNumber", parnumber } },
                            //FBaseStockActualQty = Math.Round(qty / unithsl, jd),
                            FBaseActualQty = Math.Round(qty / unithsl, jd),       //确定基本单位和常用单位之间的换算关系
                            FMaterialId = new Dictionary<string, string> { { "FNumber", martnumber } },
                            FUnitID = new Dictionary<string, string> { { "FNumber", unitnumber } },
                            FAppQty = pickqty > 0 ? pickqty : 0,
                            FActualQty = qty,
                            FSTOCKAPPQTY = pickqty > 0 ? pickqty : 0,
                            FSTOCKACTUALQTY = qty,
                            FBaseAppQty = pickqty > 0 ? Math.Round(pickqty / unithsl, jd) : 0,
                            FBaseStockActualQty = Math.Round(qty / unithsl, jd),

                            FStockId = new Dictionary<string, string> { { "FNumber", stocknumber } },
                            //FStockLocId = new PRD_PickMtrl.BillEntry.StockLocId()
                            //{
                            //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } },
                            //    FSTOCKLOCID__FF100002 = new Dictionary<string, string>()
                            //},
                            FEntrtyMemo = entry.REMARK,
                            FMoBillNo = monumber,
                            FMoEntryId = moentryid,
                            FPPBomEntryId = pentryid,
                            FMoId = moid,
                            FMoEntrySeq = parseq,
                            FPPBomBillNo = pbomnumber,

                            //FStockAppQty = basesqty,
                            //FStockActualQty = sqty,
                            FBaseUnitId = new Dictionary<string, string> { { "FNumber", baseunitnumber } },
                            FStockUnitId = new Dictionary<string, string> { { "FNumber", unitnumber } },
                            FEntryWorkShopId = new Dictionary<string, string>() { { "FNumber", supplynumber } },
                            FSrcBillNo = pbomnumber,
                            FEntrySrcInterId = pid,
                            FEntrySrcEnteryId = pentryid,
                            FEntrySrcEntrySeq = pseq,
                            FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                            FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new PRD_PickMtrl.BillEntry.AuxPropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.MTRL_VER } } },
                            FEntity_Link = pid == 0 ? new List<PRD_PickMtrl.BillEntry.Entity_Link>() : new List<PRD_PickMtrl.BillEntry.Entity_Link>()
                            {
                                new PRD_PickMtrl.BillEntry.Entity_Link()
                                {

                                    FEntity_Link_FSBillId =pid,
                                    FEntity_Link_FSId = pentryid,
                                    //FEntity_Link_FBaseActualQtyOld = Math.Round(qty / unithsl, jd),
                                    //FEntity_Link_FBaseActualQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                        };
                        entries.Add(stockEntry);
                    }
                }
                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="r102cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<PRD_INSTOCK> CreateBatchPRDINSTOCK(List<R102> r102cs, List<PRDINSTOCK> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<PRD_INSTOCK> list = new List<PRD_INSTOCK>();
            //源单校验
            string pocheck = PONoPRDINSTOCKQtyCheck(r102cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //var mary = from a in r102cs select a.MTRL_ID;
            //List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;

            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in r102cs where a != null select new { ERP_No = a.ERP_MO, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.ERP_No);
                if (!purchase.Any())
                {
                    string mestr = head.ERP_No + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);
                string newtype = _shareController.GetDesFormID("R102", order!.FBillTypeID);

                //创建单据头
                PRD_INSTOCK inStock = new()
                {
                    FBillType = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FDate = DateTime.Now.ToShortDateString(),
                    FStockOrgId = new Dictionary<string, string>() { { "FNumber", order.FPrdOrgId! } },
                    FPrdOrgId = new Dictionary<string, string>() { { "FNumber", order.FPrdOrgId! } },
                    FWorkShopId = new Dictionary<string, string>() { { "FNUMBER", order.FWorkShopID! } },
                    FOwnerTypeId0 = order.FOwnerTypeId,
                    FOwnerId0 = !string.IsNullOrWhiteSpace(order.FInStockOwnerId) ? new Dictionary<string, string>() { { "FNumber", order.FInStockOwnerId } } : new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                    F_SBMI_QUEUE_ID = string.Format("R102:{0}", r102cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<PRD_INSTOCK.Entity> entries = new List<PRD_INSTOCK.Entity>();
                //工单行明细...按生产用料清单号
                List<R102> entrygroup = r102cs.Where(s => s.ERP_MO == head.ERP_No).ToList();
                foreach (var entry in entrygroup)
                {
                    var sourentry = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FSeq == entry.MO_SEQ).FirstOrDefault();
                    //应入库数量，来自原单
                    decimal sqty = sourentry.FNoStockInQty;
                    //校验仓库是否正确
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrWhiteSpace(stocknumber))
                    {
                        string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【仓库】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > sqty)
                    //{
                    //    string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【数量】不允许大于未领料数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息                 
                    int pentryid = int.Parse(sourentry.FEntryId);
                    int pseq = int.Parse(entry.MO_SEQ);
                    //当前单位编号
                    string unitnumber = sourentry.FUnitID;
                    //当前单位实体
                    var unit = units.Find(s => s.FNumber == unitnumber);
                    //当前单位的换算率
                    decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                    //当前单位的精度
                    int jd = unit.FPrecision;
                    string baseunitnumber = sourentry.FBaseUnitId;
                    //物料版本号
                    //string itemversion = marylist.Find(s => s.FNumber == entry.MTRL_ID).F_PKGR_ITEMVERSION;
                    PRD_INSTOCK.Entity stockEntry = new PRD_INSTOCK.Entity()
                    {
                        FSrcEntryId = int.Parse(sourentry.FEntryId),
                        FMaterialId = new Dictionary<string, string>() { { "FNumber", sourentry.FMaterialId } },
                        FProductType = sourentry.FProductType,
                        FInStockType = sourentry.FInStockType,
                        FUnitID = new Dictionary<string, string>() { { "FNumber", sourentry.FUnitID } },
                        FMustQty = sourentry.FNoStockInQty,
                        FRealQty = decimal.Parse(entry.QUANTITY),
                        FBaseUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FBaseUnitId } },
                        FBaseMustQty = sourentry.FBaseNoStockInQty,
                        FBaseRealQty = sourentry.FBaseNoStockInQty,
                        FOwnerTypeId0 = sourentry.FInStockOwnerTypeId,
                        FOwnerId = !string.IsNullOrEmpty(sourentry.FInStockOwnerId) ? new Dictionary<string, string>() { { "FNumber", sourentry.FInStockOwnerId } } : new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                        FStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                        //FStockLocId = new PRD_INSTOCK.Entity.StockLocId()
                        //{
                        //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } },
                        //    FSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                        //},
                        FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                        FWorkShopId1 = new Dictionary<string, string>() { { "FNumber", sourentry.FWorkShopID } },
                        FMoBillNo = sourentry.FBillNo,
                        FMoId = int.Parse(sourentry.FID),
                        FMoEntryId = int.Parse(sourentry.FEntryId),
                        FMoEntrySeq = int.Parse(sourentry.FSeq),
                        FMOMAINENTRYID = int.Parse(sourentry.FEntryId),
                        FMemo = entry.REMARK,
                        FStockUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FUnitID } },
                        FStockRealQty = qty,
                        FSrcInterId = int.Parse(sourentry.FID),
                        FSrcBillNo = sourentry.FBillNo,
                        FBasePrdRealQty = Math.Round(qty / unithsl, jd),
                        FSrcEntrySeq = int.Parse(sourentry.FSeq),
                        FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new PRD_INSTOCK.Entity.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", entry.MTRL_VER } } },
                        F_PNDR_BASE_QTR = new Dictionary<string, string> { { "FNumber", sourentry.FBillTypeID } },
                        FSrcBillType = "PRD_MO",
                        FEntity_Link = int.Parse(sourentry.FID) == 0 ? new List<PRD_INSTOCK.Entity.Entity_Link>() : new List<PRD_INSTOCK.Entity.Entity_Link>()
                            {
                                new PRD_INSTOCK.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =int.Parse(sourentry.FID),
                                    FEntity_Link_FSId = pentryid,
                                    FEntity_Link_FBaseActualQtyOld = Math.Round(sqty / unithsl, jd),
                                    FEntity_Link_FBaseActualQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="r102cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<PRD_MORPT> CreateBatchPRDMO(List<R202> r102cs, List<PRDMORPT> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<PRD_MORPT> list = new List<PRD_MORPT>();
            //源单校验
            string pocheck = PONoMORPTCheck(r102cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in r102cs where a != null select new { ERP_No = a.ERP_MO, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.ERP_No);
                if (!purchase.Any())
                {
                    string mestr = head.ERP_No + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);

                //创建单据头
                PRD_MORPT inStock = new()
                {
                    FBillType = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = DateTime.Now.ToShortDateString(),
                    FPrdOrgId = new Dictionary<string, string>() { { "FNumber", order.FPrdOrgId! } },
                    FWorkshipIdH = new Dictionary<string, string>() { { "FNUMBER", order.FWorkShopID! } }
                    //QUEUE_ID = string.Format("R202:{0}", r102cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<PRD_MORPT.Entity> entries = new List<PRD_MORPT.Entity>();
                //工单行明细...按生产用料清单号
                List<R202> entrygroup = r102cs.Where(s => s.ERP_MO == head.ERP_No).ToList();
                foreach (var entry in entrygroup)
                {
                    var sourentry = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FSeq == entry.MO_SEQ).FirstOrDefault();
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > sqty)
                    //{
                    //    string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【数量】不允许大于未领料数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息                 
                    int pentryid = int.Parse(sourentry.FEntryId);
                    int pseq = int.Parse(entry.MO_SEQ);
                    //当前单位编号
                    string unitnumber = sourentry.FUnitId;
                    //当前单位实体
                    var unit = units.Find(s => s.FNumber == unitnumber);
                    //当前单位的换算率
                    decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                    //当前单位的精度
                    int jd = unit.FPrecision;
                    string baseunitnumber = sourentry.FBaseUnitId;
                    PRD_MORPT.Entity stockEntry = new PRD_MORPT.Entity()
                    {
                        FMaterialId = new Dictionary<string, string>() { { "FNumber", sourentry.FMaterialId } },
                        FProductType = sourentry.FProductType,
                        FUnitID = new Dictionary<string, string>() { { "FNumber", sourentry.FUnitId } },
                        FBaseUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FBaseUnitId } },
                        FOwnerId = !string.IsNullOrEmpty(sourentry.FInStockOwnerId) ? new Dictionary<string, string>() { { "FNumber", sourentry.FInStockOwnerId } } : new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                        FWorkshipId = new Dictionary<string, string>() { { "FNumber", sourentry.FWorkShopID } },
                        FMoBillNo = sourentry.FBillNo,
                        FMoId = int.Parse(sourentry.FID),
                        FMoEntryId = int.Parse(sourentry.FEntryId),
                        FMoEntrySeq = int.Parse(sourentry.FSeq),
                        FDescriptionE = entry.REMARK,
                        FSrcInterId = int.Parse(sourentry.FID),
                        FSrcBillNo = sourentry.FBillNo,
                        FSrcEntrySeq = int.Parse(sourentry.FSeq),
                        FFinishQty = qty,

                        FEntity_Link = int.Parse(sourentry.FID) == 0 ? new List<PRD_MORPT.Entity.Entity_Link>() : new List<PRD_MORPT.Entity.Entity_Link>()
                            {
                                new PRD_MORPT.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =int.Parse(sourentry.FID),
                                    FEntity_Link_FSId = pentryid,
                                    FEntity_Link_FBaseActualQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的委外领料单实体类
        /// </summary>
        /// <param name="R014_1cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<SUB_PickMtrl> CreateBatchSUBPickMtrl(List<R014_1> R014_1cs, List<SUBPickMtrl> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<SUB_PickMtrl> list = new List<SUB_PickMtrl>();
            //源单校验
            string pocheck = PONoSUBPickMtrltyCheck(R014_1cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R014_1cs where a != null select new { ERP_No = a.ERP_MO, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.ERP_No);
                if (!purchase.Any())
                {
                    string mestr = head.ERP_No + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);
                string newtype = _shareController.GetDesFormID("R014_1", order!.FBillTypeID);

                //创建单据头
                SUB_PickMtrl inStock = new()
                {
                    FBillType = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FStockOrgId = new Dictionary<string, string>() { { "FNumber", order.FSupplyOrg! } },
                    FSubOrgId = new Dictionary<string, string>() { { "FNumber", order.FSubOrgId! } },
                    FSupplierId = new Dictionary<string, string>() { { "FNumber", order.FSupplierId! } },
                    F_SBMI_QUEUE_ID = string.Format("R014_1:{0}", R014_1cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<SUB_PickMtrl.Entity> entries = new List<SUB_PickMtrl.Entity>();
                //工单行明细...按生产用料清单号
                List<R014_1> entrygroup = R014_1cs.Where(s => s.ERP_MO == head.ERP_No).ToList();
                foreach (var entry in entrygroup)
                {

                    //var sourentry = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FSeq == entry.SEQ).FirstOrDefault();
                    var sourline = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FMainMaterial == entry.MAIN_ID && s.FMaterial == entry.MTRL_ID);
                    if (!sourline.Any())
                    {
                        string mestr = string.Format("{0}主料{1}、辅料{2}没有可用数据", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    else
                    {
                        var sourentry = sourline.FirstOrDefault();
                        //应入库数量，来自原单
                        decimal sqty = sourentry.FNoPickedQty;
                        //校验仓库是否正确
                        string stocknumber = entry.STORE_ID;
                        if (string.IsNullOrWhiteSpace(stocknumber))
                        {
                            string mestr = string.Format("{0}主料{1}、辅料{2}【仓库】不能为空", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        string martnumber = entry.MTRL_ID;
                        if (string.IsNullOrWhiteSpace(martnumber))
                        {
                            string mestr = entry.ERP_MO + "【物料编码】不能为空";
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                        if (qty <= 0)
                        {
                            string mestr = entry.ERP_MO + "【数量】不正确";
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        //else if (qty > sqty)
                        //{
                        //    string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【数量】不允许大于未领料数";
                        //    reason += reason == "" ? mestr : "；" + mestr;
                        //}
                        if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                                                             //对应这一行属于源单的信息                 
                        int pentryid = sourentry.FEntryID;
                        //int pseq = int.Parse(entry.SEQ);
                        //当前单位编号
                        string unitnumber = sourentry.FUnitID2;
                        int poseq = 0;
                        int.TryParse(sourentry.FPurOrderEntrySeq, out poseq);
                        //当前单位实体
                        var unit = units.Find(s => s.FNumber == unitnumber);
                        //当前单位的换算率
                        decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                        //当前单位的精度
                        int jd = unit.FPrecision;
                        if (qty != Math.Round(qty, jd))
                        {
                            //2023-07-19更新6月补丁之后添加校验
                            //因为如果提供的QTY小数点精度大于jd时，在BOS中由主库存单位数量（FSTOCKACTUALQTY）引发值更新事件
                            //会导致字段FBaseStockActualQty与FBaseActualQty不相等。
                            string mestr = string.Format("{0}主料{1}的小数点精度只能是{2}位", entry.ERP_MO, entry.MAIN_ID, jd);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        string baseunitnumber = sourentry.FBaseUnitID1;
                        SUB_PickMtrl.Entity stockEntry = new SUB_PickMtrl.Entity()
                        {
                            FSrcBillNo = sourentry.FBillNo,
                            FSrcEntryId = pentryid,
                            FSrcEntrySeq = int.Parse(sourentry.FSeq),
                            FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                            FUnitID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                            FAppQty = sqty > 0 ? sqty : 0,
                            FActualQty = qty,
                            FStockAppQty = sqty > 0 ? sqty : 0,
                            //FStockActualQty = qty,
                            FBaseAppQty = sqty > 0 ? Math.Round(sqty / unithsl, jd) : 0,
                            FBaseActualQty = Math.Round(qty / unithsl, jd),       //确定基本单位和常用单位之间的换算关系
                            FBaseStockActualQty = Math.Round(qty / unithsl, jd),
                            FStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                            FPOOrderBillNo = sourentry.FPurOrderNo,
                            FPOOrderSeq = poseq,
                            FSupplierId0 = new Dictionary<string, string>() { { "FNumber", sourentry.FSupplierId } },
                            //StockLocId = new SUB_PickMtrl.Entity.FStockLocId()
                            //{
                            //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } },
                            //    FSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                            //},
                            //FLot = new Dictionary<string, string>() { { "FNumber", entry.ERP_LOTNO } },
                            FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                            FParentMaterialId = new Dictionary<string, string>() { { "FNumber", sourentry.FProductNumber } },
                            FSrcInterId = sourentry.FID,
                            FSubReqId = sourentry.FSubReqId,
                            FSubReqBillNo = sourentry.FSubReqBillNo,
                            FReserveType = sourentry.FReserveType,
                            FSettleOrgId = new Dictionary<string, string>() { { "FNumber", sourentry.FSettleOrgId } },
                            FPPbomBillNo = sourentry.FBillNo,
                            FPPbomEntryId = pentryid,
                            FSubReqEntryId = int.Parse(sourentry.FSubReqEntryId),
                            FSubReqEntrySeq = sourentry.FSubReqEntrySeq,
                            FBaseUnitId = new Dictionary<string, string>() { { "FNumber", baseunitnumber } },
                            FStockUnitId = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                            //FSrcPickEntryId = int.Parse(sourentry.FID),
                            //FPOOrderSeq = int.Parse(sourentry.FSeq),
                            //FStockStatusId = new Dictionary<string, string>() { { "FNumber", sourentry.FStockStatusId } },
                            FOwnerTypeId = "BD_OwnerOrg",
                            //FOwnerId = !string.IsNullOrEmpty(sourentry.FParentOwnerId) ? new Dictionary<string, string>() { { "FNumber", sourentry.FParentOwnerId } } : new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                            FOwnerId = new Dictionary<string, string>() { { "FNUMBER", LocalStaticRequest.DefaultOrg } },
                            FParentOwnerTypeId = sourentry.FParentOwnerTypeId,
                            FParentOwnerId = !string.IsNullOrEmpty(sourentry.FParentOwnerId) ? new Dictionary<string, string>() { { "FNumber", sourentry.FParentOwnerId } } : new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                            FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new SUB_PickMtrl.Entity.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", entry.MTRL_VER } } },
                            FEntity_Link = sourentry.FID == 0 ? new List<SUB_PickMtrl.Entity.Entity_Link>() : new List<SUB_PickMtrl.Entity.Entity_Link>()
                            {
                                new SUB_PickMtrl.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =sourentry.FID,
                                    FEntity_Link_FSId = pentryid,
                                    FEntity_Link_FBaseActualQtyOld=Math.Round(sqty / unithsl, jd),
                                    FEntity_Link_FBaseActualQty=Math.Round(qty / unithsl, jd)
                                }
                            }
                        };
                        entries.Add(stockEntry);
                    }
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 组装Cloud的委外退料单实体类
        /// </summary>
        /// <param name="R019_1cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<SUB_RETURNMTRL> CreateBatchSUBRETURNMTRL(List<R019_1> R019_1cs, List<SUBRETURNMTRL> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<SUB_RETURNMTRL> list = new List<SUB_RETURNMTRL>();
            //源单校验
            string pocheck = PONoSUBRETURNMTRLCheck(R019_1cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R019_1cs where a != null select new { ERP_No = a.ERP_MO, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.ERP_No);
                if (!purchase.Any())
                {
                    string mestr = head.ERP_No + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();
                string newtype = _shareController.GetDesFormID("R019_1", order!.FSubReqType);

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);

                //创建单据头
                SUB_RETURNMTRL inStock = new()
                {
                    FBillType = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FDate = DateTime.Now.ToShortDateString(),
                    FStockOrgId = new Dictionary<string, string>() { { "FNumber", order.FSupplyOrg! } },
                    //FStockId = new Dictionary<string, string>() { { "FNumber", order.FStockID! } },
                    FSubOrgId = new Dictionary<string, string>() { { "FNumber", order.FSubOrgId! } },
                    FSubSupplierId = new Dictionary<string, string>() { { "FNumber", order.FSupplierId! } },
                    FOwnerTypeId0 = order.FOwnerTypeId,
                    FOwnerId0 = new Dictionary<string, string>() { { "FNumber", order.FParentOwnerId! } },
                    F_SBMI_QUEUE_ID = string.Format("R019_1:{0}", R019_1cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<SUB_RETURNMTRL.Entity> entries = new List<SUB_RETURNMTRL.Entity>();
                //工单行明细...按生产用料清单号
                List<R019_1> entrygroup = R019_1cs.Where(s => s.ERP_MO == head.ERP_No).ToList();
                foreach (var entry in entrygroup)
                {
                    var linedata = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FMainMaterial == entry.MAIN_ID && s.FMaterial == entry.MTRL_ID);
                    if (!linedata.Any())
                    {
                        string mestr = string.Format("{0}主料{1}、辅料{2}没有可用数据", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                        reason += reason == "" ? mestr : "；" + mestr;
                        break;
                    }
                    var sourentry = linedata.FirstOrDefault();
                    //应退数量，不应该是已领数量，因为有些可能已经入库了...
                    decimal sqty = sourentry.FWipQty;
                    //校验仓库是否正确
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrWhiteSpace(stocknumber))
                    {
                        string mestr = entry.ERP_MO + "第" + entry.SEQ + "行【仓库】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = entry.ERP_MO + "第" + entry.SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.ERP_MO + "第" + entry.SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > sqty)
                    //{
                    //    string mestr = entry.ERP_MO + "第" + entry.MO_SEQ + "行【数量】不允许大于未领料数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息                 
                    int pentryid = int.Parse(sourentry.FEntryID);
                    //int pseq = int.Parse(entry.MO_SEQ);
                    //当前单位编号
                    string unitnumber = sourentry.FUnitID;
                    //当前单位实体
                    var unit = units.Find(s => s.FNumber == unitnumber);
                    //当前单位的换算率
                    decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                    //当前单位的精度
                    int jd = unit.FPrecision;
                    if (qty != Math.Round(qty, jd))
                    {
                        //2023-07-19更新6月补丁之后添加校验
                        //因为如果提供的QTY小数点精度大于jd时，在BOS中由主库存单位数量（FSTOCKACTUALQTY）引发值更新事件
                        //会导致字段FBaseStockQty与FBaseQty不相等。
                        string mestr = string.Format("{0}主料{1}的小数点精度只能是{2}位", entry.ERP_MO, entry.MAIN_ID, jd);
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string baseunitnumber = sourentry.FUnitID;
                    SUB_RETURNMTRL.Entity stockEntry = new SUB_RETURNMTRL.Entity()
                    {
                        FSrcBillNo = sourentry.FBillNo,
                        FSrcBillId = int.Parse(sourentry.FID),
                        FSrcEntryId = pentryid,
                        FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                        FUnitID = new Dictionary<string, string>() { { "FNumber", sourentry.FUnitID } },
                        FAPPQty = sqty > 0 ? sqty : 0,
                        FQty = qty,
                        FStockAppQty = sqty > 0 ? sqty : 0,
                        FStockQty = qty,
                        FBaseStockQty = Math.Round(qty / unithsl, jd),
                        FBaseAppQty = sqty > 0 ? Math.Round(sqty / unithsl, jd) : 0,
                        FBaseQty = Math.Round(qty / unithsl, jd),
                        //FBaseActualQty = Math.Round(qty / unithsl, jd),       //确定基本单位和常用单位之间的换算关系
                        FStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                        //FStockLocId = new SUB_RETURNMTRL.Entity.StockLocId()
                        //{
                        //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } },
                        //    FSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                        //},
                        FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                        FSUBReqBillNo = sourentry.FSubReqBillNo,
                        FSrcEntrySeq = int.Parse(sourentry.FSeq),
                        FParentMaterialId = new Dictionary<string, string>() { { "FNumber", sourentry.FProductNumber } },
                        FSUBReqId = int.Parse(sourentry.FSubReqId),
                        FSUBReqEntrySeq = int.Parse(sourentry.FSubReqEntrySeq),
                        FPPBOMEntryId = pentryid,
                        FSubReqEntryId = int.Parse(sourentry.FSubReqEntryId),
                        FPOOrderBillNo = sourentry.FPurOrderNo,
                        FPOOrderSeq = sourentry.FPurOrderEntrySeq,
                        FPPBomBillNo = sourentry.FBillNo,
                        FOwnerTypeId = sourentry.FOwnerTypeId,
                        FSupplierId = new Dictionary<string, string>() { { "FNumber", sourentry.FSupplierId! } },
                        FOwnerId = new Dictionary<string, string>() { { "FNumber", sourentry.FParentOwnerId } },
                        FBaseUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FBaseUnitId } },
                        FReserveType = sourentry.FReserveType,
                        FSettleOrgId = new Dictionary<string, string>() { { "FNumber", sourentry.FSettleOrgId } },
                        FStockUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FUnitID } },
                        //FSecUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FUnitID } },
                        FMtoNo = sourentry.FMtoNo,
                        FProjectNo = sourentry.FProjectNo,
                        FStockStatusId = new Dictionary<string, string>() { { "FNumber", sourentry.FStockStatusId } },
                        FKeeperId = new Dictionary<string, string>() { { "FNumber", sourentry.FSupplyOrg } },
                        //FProcessId = new Dictionary<string, string>() { { "FNumber", sourentry.FProcessID } },
                        //FBomId = new Dictionary<string, string>() { { "FNumber", sourentry.FBomId } },
                        FReturnType = String.IsNullOrWhiteSpace(entry.TL_TYPE) ? "0" : entry.TL_TYPE,
                        FParentOwnerTypeId = sourentry.FParentOwnerTypeId,
                        FParentOwnerId = new Dictionary<string, string>() { { "FNumber", sourentry.FParentOwnerId } },
                        FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new SUB_RETURNMTRL.Entity.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", entry.MTRL_VER } } },
                        FEntity_Link = int.Parse(sourentry.FID) == 0 ? new List<SUB_RETURNMTRL.Entity.Entity_Link>() : new List<SUB_RETURNMTRL.Entity.Entity_Link>()
                            {
                                new SUB_RETURNMTRL.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =int.Parse(sourentry.FID),
                                    FEntity_Link_FSId = pentryid,
                                    //FEntity_Link_FBaseActualQtyOld = Math.Round(sqty / unithsl, jd),
                                    //FEntity_Link_FBaseActualQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R019cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<PRD_ReturnMtrl> CreateBatchPRDReturnMtrl(List<R019> R019cs, List<PRDReturnMtrl> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<PRD_ReturnMtrl> list = new List<PRD_ReturnMtrl>();
            //源单校验
            string pocheck = PONoPRDReturnMtrlCheck(R019cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R019cs where a != null select new { ERP_No = a.ERP_MO, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.ERP_No);
                if (!purchase.Any())
                {
                    string mestr = head.ERP_No + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);
                string newtype = _shareController.GetDesFormID("R019", order!.FMOType);

                //创建单据头
                PRD_ReturnMtrl inStock = new()
                {
                    FBillType = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = DateTime.Now.ToShortDateString(),
                    F_SBMI_QUEUE_ID = string.Format("R019:{0}", R019cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<PRD_ReturnMtrl.Entity> entries = new List<PRD_ReturnMtrl.Entity>();
                //工单行明细...按生产用料清单号
                List<R019> entrygroup = R019cs.Where(s => s.ERP_MO == head.ERP_No).ToList();
                foreach (var entry in entrygroup)
                {
                    var sourentry = purchase.Where(s => s.FBillNo == entry.ERP_MO && s.FMainMaterial == entry.MAIN_ID && s.FMaterial == entry.MTRL_ID && s.FMaterialType == (entry.IS_BACK == "Y" ? "2" : entry.MAIN_ID == entry.MTRL_ID ? "1" : "3"));
                    if (!sourentry.Any())
                    {
                        string mestr = string.Format("{0}主料{1}、辅料{2}不存在源单信息", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                        reason += reason == "" ? mestr : "；" + mestr;
                        break;
                    }
                    else
                    {
                        PRDReturnMtrl returnMtrl = sourentry.FirstOrDefault();
                        //应入库数量，来自原单
                        decimal sqty = returnMtrl.FWipQty;
                        //校验仓库是否正确
                        string stocknumber = entry.STORE_ID;
                        if (string.IsNullOrWhiteSpace(stocknumber))
                        {
                            string mestr = string.Format("{0}主料{1}、辅料{2}【仓库】不能为空", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        string martnumber = entry.MTRL_ID;
                        if (string.IsNullOrWhiteSpace(martnumber))
                        {
                            string mestr = string.Format("{0}主料{1}【辅料】不能为空", entry.ERP_MO, entry.MAIN_ID);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                        if (qty <= 0)
                        {
                            string mestr = string.Format("{0}主料{1}、辅料{2}【数量】不正确", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        //else if (qty > sqty)
                        //{
                        //    string mestr = string.Format("{0}主料{1}、辅料{2}【数量】不允许大于未领料数", entry.ERP_MO, entry.MAIN_ID, entry.MTRL_ID);
                        //    reason += reason == "" ? mestr : "；" + mestr;
                        //}
                        if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...

                        int pentryid = int.Parse(returnMtrl.FEntryID);
                        int pseq = int.Parse(returnMtrl.FSeq);
                        //当前单位编号
                        string unitnumber = returnMtrl.FUnitNumber;
                        //当前单位实体
                        var unit = units.Find(s => s.FNumber == unitnumber);
                        //当前单位的换算率
                        decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                        //当前单位的精度
                        int jd = unit.FPrecision;
                        if (qty != Math.Round(qty, jd))
                        {
                            //2023-07-19更新6月补丁之后添加校验
                            //因为如果提供的QTY小数点精度大于jd时，在BOS中由主库存单位数量（FSTOCKACTUALQTY）引发值更新事件
                            //会导致字段FBaseStockActualQty与FBaseActualQty不相等。
                            string mestr = string.Format("{0}主料{1}的小数点精度只能是{2}位", entry.ERP_MO, entry.MAIN_ID, jd);
                            reason += reason == "" ? mestr : "；" + mestr;
                        }
                        string baseunitnumber = returnMtrl.FBaseUnitNumber;
                        //int enumid = string.IsNullOrEmpty(entry.TL_TYPE)? 0 : int.Parse(entry.TL_TYPE);
                        PRD_ReturnMtrl.Entity stockEntry = new PRD_ReturnMtrl.Entity()
                        {
                            //FEntryID = int.Parse(sourentry.FEntryID),
                            FReturnType = entry.TL_TYPE,
                            FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                            FUnitID = new Dictionary<string, string>() { { "FNumber", unitnumber } },
                            FAPPQty = qty,
                            FStockAppQty = qty,
                            FQty = qty,
                            FStockQty = qty,
                            FStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                            //StockLocId = new PRD_ReturnMtrl.Entity.FStockLocId()
                            //{
                            //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } },
                            //    FSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                            //},
                            FSrcBillNo = returnMtrl.FBillNo,
                            FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                            FEntrySrcEntrySeq = int.Parse(returnMtrl.FSeq),
                            FPPBomBillNo = returnMtrl.FBillNo,
                            FParentMaterialId = new Dictionary<string, string>() { { "FNumber", returnMtrl.FParentMaterial } },
                            FPPBOMEntryId = int.Parse(returnMtrl.FEntryID),
                            FMoId = int.Parse(returnMtrl.FMoId1),
                            FMoBillNo = returnMtrl.FMoBillNo1,
                            FMoEntryId = int.Parse(returnMtrl.FMoEntryId1),
                            FMoEntrySeq = int.Parse(returnMtrl.FMoEntrySeq1),
                            FStockUnitId = new Dictionary<string, string>() { { "FNumber", returnMtrl.FUnitNumber } },
                            FBaseUnitId = new Dictionary<string, string>() { { "FNumber", returnMtrl.FBaseUnitNumber } },
                            FBaseAppQty = Math.Round(qty / unithsl, jd),
                            FBaseQty = Math.Round(qty / unithsl, jd),
                            FBASESTOCKQTY = Math.Round(qty / unithsl, jd),
                            FEntrySrcEnteryId = int.Parse(returnMtrl.FEntryID),
                            FEntrySrcInterId = int.Parse(returnMtrl.FID),
                            FWorkShopId1 = new Dictionary<string, string>() { { "FNumber", returnMtrl.FWorkshopNumber } },
                            FParentOwnerId = new Dictionary<string, string>() { { "FNumber", returnMtrl.FPrdOrgFNumber } },
                            FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new PRD_ReturnMtrl.Entity.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", entry.MTRL_VER } } },
                            FEntity_Link = int.Parse(returnMtrl.FID) == 0 ? new List<PRD_ReturnMtrl.Entity.Entity_Link>() : new List<PRD_ReturnMtrl.Entity.Entity_Link>()
                            {
                                new PRD_ReturnMtrl.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =int.Parse(returnMtrl.FID),
                                    FEntity_Link_FSId = pentryid,
                                    FEntity_Link_FBaseActualQtyOld = Math.Round(sqty / unithsl, jd),
                                    FEntity_Link_FBaseActualQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                        };
                        entries.Add(stockEntry);
                    }
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }
        /// <summary>
        /// 在RB系统校验发货通知作为源单生成调拨单的信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R010cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_TransferDirect> CreateBatchSTKTransferDirect(List<R010> R010cs, List<STKTransferDirect> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<STK_TransferDirect> list = new List<STK_TransferDirect>();
            //源单校验
            string pocheck = PONoSTKTransferDirectCheck(R010cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R010cs where a != null select new { PO_ID = a.AUDIT_ID, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.PO_ID);
                if (!purchase.Any())
                {
                    string mestr = head.PO_ID + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);
                string newtype = _shareController.GetDesFormID("R010", order.FBillTypeID);
                //创建单据头
                STK_TransferDirect inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FDate = DateTime.Now.ToShortDateString(),
                    //FBizType = order.FBussinessType,
                    FStockOutOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FDeliveryOrgID } },
                    //FSaleOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleOrgId } },
                    FStockerId = new Dictionary<string, string>() { { "FNUMBER", order.FStockerID } },
                    FStockerGroupId = new Dictionary<string, string>() { { "FNUMBER", order.FStockerGroupID } },
                    //FRecConditionId = new Dictionary<string, string>() { { "FNUMBER", order.FRECEIPTCONDITIONID } },
                    FSaleDeptId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleDeptID } },
                    FSaleGroupId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleGroupID } },
                    FSalerId = new Dictionary<string, string>() { { "FNUMBER", order.FSalesManID } },
                    F_PKGR_CustomerID = new Dictionary<string, string>() { { "FNUMBER", order.FCustomerID } },
                    F_SBMI_QUEUE_ID = string.Format("R010:{0}", R010cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<STK_TransferDirect.BillEntry> entries = new List<STK_TransferDirect.BillEntry>();
                //工单行明细...按生产用料清单号
                List<R010> entrygroup = R010cs.Where(s => s.AUDIT_ID == head.PO_ID).ToList();
                foreach (var entry in entrygroup)
                {
                    var sourentry = purchase.Where(s => s.FBillNo == entry.AUDIT_ID && s.FSeq == entry.AUDIT_SEQ).FirstOrDefault();
                    //应入库数量，来自原单
                    decimal sqty = sourentry.FRemainOutQty;
                    //校验仓库是否正确
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrWhiteSpace(stocknumber))
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【仓库】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > sqty)
                    //{
                    //    string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【数量】不允许大于未领料数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息                 
                    int pentryid = int.Parse(sourentry.FEntryID);
                    int pseq = int.Parse(entry.AUDIT_SEQ);
                    //当前单位编号
                    string unitnumber = sourentry.FStockUnitID;
                    //当前单位实体
                    var unit = units.Find(s => s.FNumber == unitnumber);
                    //当前单位的换算率
                    decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                    //当前单位的精度
                    int jd = unit.FPrecision;
                    string baseunitnumber = sourentry.FStockUnitID;
                    STK_TransferDirect.BillEntry stockEntry = new STK_TransferDirect.BillEntry()
                    {
                        FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                        FNoteEntry = entry.REMARK,
                        FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                        FDestLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                        FQty = string.IsNullOrEmpty(entry.QUANTITY) ? 0 : decimal.Parse(entry.QUANTITY),
                        FDestStockId = new Dictionary<string, string>() { { "FNumber", entry.TOSTORE_ID } },
                        FUnitID = new Dictionary<string, string>() { { "FNumber", sourentry.FStockUnitID } },
                        FBaseUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FStockUnitID } },
                        FSrcStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                        FOwnerTypeId = sourentry.FOwnerTypeID,
                        FOwnerId = new Dictionary<string, string>() { { "FNumber", sourentry.FOwnerID } },
                        //FRowType = sourentry.FRowType,
                        //FStockLocId = new STK_TransferDirect.BillEntry.StockLocId()
                        //{
                        //    FSRCSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } },
                        //    FSRCSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                        //},
                        FSaleQty = qty,
                        FActQty = qty,
                        FBaseQty = Math.Round(qty / unithsl, jd),
                        FSalBaseQty = Math.Round(qty / unithsl, jd),
                        FOwnerOutId = new Dictionary<string, string>() { { "FNumber", sourentry.FOwnerID } },
                        //FISFREE = bool.Parse(sourentry.FIsFree),
                        FDiscountRate = int.Parse(sourentry.FDiscountRate),
                        FSaleUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FUnitID } },
                        //FSOEntryId = int.Parse(sourentry.FSOEntryId),
                        FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new STK_TransferDirect.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.MTRL_VER } } },
                        FBillEntry_Link = int.Parse(sourentry.FID) == 0 ? new List<STK_TransferDirect.BillEntry.BillEntry_Link>() : new List<STK_TransferDirect.BillEntry.BillEntry_Link>()
                            {
                                new STK_TransferDirect.BillEntry.BillEntry_Link()
                                {
                                    FBillEntry_Link_FRuleId="DeliveryNotice-StkTransferDirect",
                                    FBillEntry_Link_FSTableName="T_SAL_DELIVERYNOTICEENTRY",
                                    FBillEntry_Link_FSBillId =int.Parse(sourentry.FID),
                                    FBillEntry_Link_FSId = pentryid,
                                    FBillEntry_Link_FBaseQtyOld = Math.Round(sqty / unithsl, jd),
                                    FBillEntry_Link_FBaseQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }

                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R010cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_TransferDirect> CreateBatchSTKTransferDirect_2(List<R010> R010cs, List<STKTransferDirect_2> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<STK_TransferDirect> list = new List<STK_TransferDirect>();
            //源单校验
            string pocheck = PONoSTKTransferDirectCheck_2(R010cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R010cs where a != null select new { PO_ID = a.AUDIT_ID, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.PO_ID);
                if (!purchase.Any())
                {
                    string mestr = head.PO_ID + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);

                //创建单据头
                STK_TransferDirect inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = DateTime.Now.ToShortDateString(),
                    //FBizType = order.FBizType,
                    FStockOutOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FStockOrgId } },
                    FStockerId = new Dictionary<string, string>() { { "FNUMBER", order.FStockerId } },
                    FStockerGroupId = new Dictionary<string, string>() { { "FNUMBER", order.FStockerGroupId } },
                    //FSaleOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleOrgId } },
                    FSaleDeptId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleDeptId } },
                    FSalerId = new Dictionary<string, string>() { { "FNUMBER", order.FSalerId } },
                    //F_PKGR_CustomerID = new Dictionary<string, string>() { { "FNUMBER", order.FCustomerID } },
                    F_SBMI_QUEUE_ID = string.Format("R010:{0}", R010cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<STK_TransferDirect.BillEntry> entries = new List<STK_TransferDirect.BillEntry>();
                //工单行明细...按生产用料清单号
                List<R010> entrygroup = R010cs.Where(s => s.AUDIT_ID == head.PO_ID).ToList();
                foreach (var entry in entrygroup)
                {
                    var sourentry = purchase.Where(s => s.FBillNo == entry.AUDIT_ID && s.FSeq == int.Parse(entry.AUDIT_SEQ)).FirstOrDefault();
                    //应入库数量，来自原单
                    decimal sqty = sourentry.FQty;
                    //校验仓库是否正确
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrWhiteSpace(stocknumber))
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【仓库】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > sqty)
                    //{
                    //    string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【数量】不允许大于首次调拨数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息                 
                    int pentryid = sourentry.FBillEntry_FEntryID;
                    int pseq = int.Parse(entry.AUDIT_SEQ);
                    //当前单位编号
                    string unitnumber = sourentry.FUnitID;
                    //当前单位实体
                    var unit = units.Find(s => s.FNumber == unitnumber);
                    //当前单位的换算率
                    decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                    //当前单位的精度
                    int jd = unit.FPrecision;
                    string baseunitnumber = sourentry.FBaseUnitId;
                    STK_TransferDirect.BillEntry stockEntry = new STK_TransferDirect.BillEntry()
                    {
                        FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                        FUnitID = new Dictionary<string, string>() { { "FNumber", sourentry.FUnitID } },
                        //FStockLocId = new STK_TransferDirect.BillEntry.StockLocId()
                        //{
                        //    FSRCSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } },
                        //    FSRCSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                        //},
                        //注意：个别字段原单的调入就是当前的调出！！！
                        FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                        FSrcStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                        FDestStockId = new Dictionary<string, string>() { { "FNumber", entry.TOSTORE_ID } },
                        FOwnerTypeOutId = sourentry.FOwnerTypeIdHead,
                        FOwnerOutId = new Dictionary<string, string>() { { "FNumber", sourentry.FOwnerIdHead } },
                        FBaseUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FBaseUnitId } },
                        //FISFREE = sourentry.FISFREE,
                        FKeeperOutId = new Dictionary<string, string>() { { "FNumber", sourentry.FKeeperId } },
                        FDiscountRate = int.Parse(sourentry.FDiscountRate),
                        FDestLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                        //FDestMaterialId = new Dictionary<string, string>() { { "FNumber", sourentry.FMaterialID } },
                        //FSaleUnitId = new Dictionary<string, string>() { { "FNumber", sourentry.FSaleUnitId } },
                        //FPriceUnitID = new Dictionary<string, string>() { { "FNumber", sourentry.FPriceUnitID } },
                        //FSOEntryId = int.Parse(sourentry.FSOEntryId),
                        FAuxPropId = string.IsNullOrEmpty(entry.MTRL_VER) ? null : new STK_TransferDirect.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.MTRL_VER } } },
                        //FAuxPropId = new STK_TransferDirect.BillEntry.AuxpropId() { FAUXPROPID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.MTRL_VER } } },
                        FBillEntry_Link = int.Parse(sourentry.FID) == 0 ? new List<STK_TransferDirect.BillEntry.BillEntry_Link>() : new List<STK_TransferDirect.BillEntry.BillEntry_Link>()
                            {
                                new STK_TransferDirect.BillEntry.BillEntry_Link()
                                {
                                    FLinkId="BILLFLOW_SCM_(TransferDirect)To(TransferDirect)",
                                    FBillEntry_Link_FRuleId="TransferDirect-TransferDirect",
                                    FBillEntry_Link_FSTableName="T_STK_STKTRANSFERINENTRY_LK",
                                    FBillEntry_Link_FSBillId =int.Parse(sourentry.FID),
                                    FBillEntry_Link_FSId = pentryid,
                                    FBillEntry_Link_FBaseQtyOld = Math.Round(sqty / unithsl, jd),
                                    FBillEntry_Link_FBaseQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }

                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 创建调拨单信息，并转换成Cloud的实体类 
        /// </summary>
        /// <param name="R011cs">接收到的数据</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_TransferDirect> CreateBatchSTKTransferDirectsOLD(List<R011> R011cs, string billtype, ref string reason)
        {
            reason = "";
            List<STK_TransferDirect> list = new List<STK_TransferDirect>();
            var R011 = (from a in R011cs
                        group a by a.TRX_DATE into b
                        select b).Distinct().ToList();

            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            foreach (var head in R011)
            {
                //创建单据头
                STK_TransferDirect inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = head.Key,
                    FOwnerOutIdHead = new Dictionary<string, string>() { { "FNUMBER", LocalStaticRequest.DefaultOrg } },
                    F_SBMI_QUEUE_ID = string.Format("R011:{0}", R011cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };


                //收集物料清单，一起查询
                var mary = from a in R011cs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                if (marylist.Count == 0)
                {
                    reason = "输入的物料编码不存在";
                    break;
                }
                //添加单据体，按订单号找到它的明细数据                
                List<STK_TransferDirect.BillEntry> entries = (from a in head
                                                              join b in R011cs on a.TRX_DATE equals b.TRX_DATE
                                                              join c in marylist on b.MTRL_ID equals c.FNumber
                                                              join d in units on c.FStoreUnitID equals d.FUNITID
                                                              join e in units on c.FBaseUnitId equals e.FUNITID
                                                              select new STK_TransferDirect.BillEntry
                                                              {
                                                                  FMaterialId = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                                                                  FNoteEntry = b.REMARK,
                                                                  FLot = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                                  FDestLot = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                                  FQty = decimal.Parse(b.QUANTITY),
                                                                  FDestStockId = new Dictionary<string, string>() { { "FNumber", b.TOSTORE_ID } },
                                                                  FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                                  FBaseUnitId = new Dictionary<string, string>() { { "FNumber", c.FBaseUnitNumber } },
                                                                  FSrcStockId = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                                                                  FSaleQty = decimal.Parse(b.QUANTITY),
                                                                  FSaleUnitId = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                                  FActQty = decimal.Parse(b.QUANTITY),
                                                                  FSalBaseQty = Math.Round(decimal.Parse(b.QUANTITY) / (d.FConvertNumerator / d.FConvertDenominator), d.FPrecision),
                                                                  FBaseQty = Math.Round(decimal.Parse(b.QUANTITY) / (d.FConvertNumerator / d.FConvertDenominator), d.FPrecision),
                                                                  FOwnerOutId = new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                                                                  FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_TransferDirect.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNumber", b.MTRL_VER } } }
                                                              }
                                                         ).Distinct().ToList()
                                                         ;
                var q = entries.Where(s => s.FQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "入库单数量必须大于0";
                    break;
                }
                string s = JsonConvert.SerializeObject(entries);
                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            return list;
        }


        /// <summary>
        /// 创建VMI调拨单信息，并转换成Cloud的实体类 
        /// </summary>
        /// <param name="R011cs">接收到的数据</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_TransferDirect> CreateBatchSTKTransferVMIDirects(List<R011VMI> R011cs, string billtype, ref string reason)
        {
            reason = "";
            List<STK_TransferDirect> list = new List<STK_TransferDirect>();
            var R011 = (from a in R011cs
                        group a by new { a.SUP_ID } into b
                        select b).Distinct().ToList();

            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            foreach (var head in R011)
            {
                //创建单据头
                STK_TransferDirect inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = DateTime.Now.ToShortDateString(),
                    FOwnerTypeOutIdHead = "BD_Supplier",
                    FOwnerOutIdHead = new Dictionary<string, string>() { { "FNUMBER", head.Key.SUP_ID } },
                    FOwnerTypeIdHead = "BD_OwnerOrg",
                    FOwnerIdHead = new Dictionary<string, string>() { { "FNUMBER", LocalStaticRequest.DefaultOrg } },
                    FBizType = "VMI",
                    F_SBMI_QUEUE_ID = string.Format("R011VMI:{0}", R011cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！    
                };


                //收集物料清单，一起查询
                var mary = from a in R011cs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                if (marylist.Count == 0)
                {
                    reason = "输入的物料编码不存在";
                    break;
                }
                //添加单据体，按订单号找到它的明细数据                
                List<STK_TransferDirect.BillEntry> entries = (from b in R011cs.Where(s => s.SUP_ID == head.Key.SUP_ID) //on new { A = a.TRX_DATE ,B= a.SUP_ID } equals new {A= b.TRX_DATE ,B= b.SUP_ID }
                                                              join c in marylist on b.MTRL_ID equals c.FNumber
                                                              join d in units on c.FStoreUnitID equals d.FUNITID
                                                              join e in units on c.FBaseUnitId equals e.FUNITID
                                                              select new STK_TransferDirect.BillEntry
                                                              {
                                                                  FMaterialId = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                                                                  FNoteEntry = b.REMARK,
                                                                  FLot = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                                  FDestLot = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                                  FQty = decimal.Parse(b.QUANTITY),
                                                                  FDestStockId = new Dictionary<string, string>() { { "FNumber", b.TOSTORE_ID } },
                                                                  FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                                  FBaseUnitId = new Dictionary<string, string>() { { "FNumber", c.FBaseUnitNumber } },
                                                                  FSrcStockId = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                                                                  FSaleQty = decimal.Parse(b.QUANTITY),
                                                                  FSaleUnitId = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                                  FActQty = decimal.Parse(b.QUANTITY),
                                                                  FSalBaseQty = Math.Round(decimal.Parse(b.QUANTITY) / (d.FConvertNumerator / d.FConvertDenominator), d.FPrecision),
                                                                  FBaseQty = Math.Round(decimal.Parse(b.QUANTITY) / (d.FConvertNumerator / d.FConvertDenominator), d.FPrecision),
                                                                  FOwnerTypeOutId = "BD_Supplier",
                                                                  FOwnerOutId = new Dictionary<string, string>() { { "FNumber", b.SUP_ID } },
                                                                  FOwnerTypeId = "BD_OwnerOrg",
                                                                  FOwnerId = new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                                                                  FSettleOrgId = new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                                                                  FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_TransferDirect.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNumber", b.MTRL_VER } } }
                                                                  //FAuxPropId = new STK_TransferDirect.BillEntry.AuxpropId() { FAUXPROPID__FF100002 = new Dictionary<string, string>() { { "FNumber", b.MTRL_VER } } }
                                                              }
                                                         ).Distinct().ToList()
                                                         ;
                var q = entries.Where(s => s.FQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "入库单数量必须大于0";
                    break;
                }

                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            return list;
        }
        /// <summary>
        /// 销售出库的源单信息校验，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R172cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<SAL_RETURNSTOCK> CreateBatchSALRETURNSTOCK(List<R172C> R172cs, List<SALRETURNSTOCK> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<SAL_RETURNSTOCK> list = new List<SAL_RETURNSTOCK>();
            //源单校验
            string pocheck = PONoSALRETURNSTOCKCheck(R172cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R172cs where a != null select new { AUDIT_ID = a.AUDIT_ID, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.AUDIT_ID);
                if (!purchase.Any())
                {
                    string mestr = head.AUDIT_ID + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);

                //创建单据头
                SAL_RETURNSTOCK inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = DateTime.Now.ToShortDateString(),
                    FSaleOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleOrgId } },
                    FRetcustId = new Dictionary<string, string>() { { "FNUMBER", order.FCustomerID } },
                    FSaledeptid = new Dictionary<string, string>() { { "FNUMBER", order.FSaleDeptID } },
                    FCorrespondOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FCorrespondOrgId } },
                    FSalesManId = new Dictionary<string, string>() { { "FNUMBER", order.FSalesManID } },
                    FStockOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleOrgId } },
                    FReceiveCustId = new Dictionary<string, string>() { { "FNUMBER", order.FReceiverID } },
                    FReceiveAddress = order.FReceiveAddress,
                    FSettleCustId = new Dictionary<string, string>() { { "FNUMBER", order.FSettleID } },
                    FReceiveCusContact = new Dictionary<string, string>() { { "FNUMBER", order.FReceiverContactID } },
                    FPayCustId = new Dictionary<string, string>() { { "FNUMBER", order.FPayerID } },
                    FOwnerTypeIdHead = order.FOwnerTypeIdHead,
                    FOwnerIdHead = new Dictionary<string, string>() { { "FNUMBER", order.FOwnerIdHead } },
                    FLinkMan = order.FLinkMan,
                    FLinkPhone = order.FLinkPhone,
                    F_SBMI_QUEUE_ID = string.Format("R172C:{0}", R172cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };
                //添加单据体，按订单号找到它的明细数据                
                List<SAL_RETURNSTOCK.Entity> entries = new List<SAL_RETURNSTOCK.Entity>();
                //工单行明细...按生产用料清单号
                List<R172C> entrygroup = R172cs.Where(s => s.AUDIT_ID == head.AUDIT_ID).ToList();
                foreach (var entry in entrygroup)
                {
                    var sourentry = purchase.Where(s => s.FBillNo == entry.AUDIT_ID && s.FSeq == entry.AUDIT_SEQ);
                    if (!sourentry.Any())
                    {
                        string mestr = string.Format("未找到源单{0}，第{1}行", entry.AUDIT_ID, entry.AUDIT_SEQ);
                        reason += reason == "" ? mestr : "；" + mestr;
                        break;
                    }
                    SALRETURNSTOCK salreturnstock = sourentry.FirstOrDefault();
                    //应出库数量，来自原单
                    decimal sqty = salreturnstock.FRemainOutQty;
                    //校验仓库是否正确
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrWhiteSpace(stocknumber))
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【仓库】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > sqty)
                    //{
                    //    string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【数量】不允许大于未出库数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息                 
                    int pentryid = int.Parse(salreturnstock.FEntryID);
                    int pseq = int.Parse(salreturnstock.FSeq);
                    //当前单位编号
                    string unitnumber = salreturnstock.FUnitID;
                    //当前单位实体
                    var unit = units.Find(s => s.FNumber == unitnumber);
                    //当前单位的换算率
                    decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                    //当前单位的精度
                    int jd = unit.FPrecision;
                    string baseunitnumber = salreturnstock.FBaseUnitID;
                    SAL_RETURNSTOCK.Entity stockEntry = new SAL_RETURNSTOCK.Entity()
                    {
                        FENTRYID = int.Parse(salreturnstock.FEntryID),
                        FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                        FUnitID = new Dictionary<string, string>() { { "FNumber", salreturnstock.FUnitID } },
                        FPrice = decimal.Parse(salreturnstock.FPrice),
                        FTaxPrice = decimal.Parse(salreturnstock.FTaxPrice),
                        FIsFree = bool.Parse(salreturnstock.FIsFree),
                        FEntryTaxRate = int.Parse(salreturnstock.FTaxRate),
                        FOwnerTypeId = salreturnstock.FOwnerTypeIdHead,
                        FOwnerId = new Dictionary<string, string>() { { "FNumber", salreturnstock.FOwnerIdHead } },
                        FStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                        //FStocklocId = new SAL_RETURNSTOCK.Entity.StocklocId()
                        //{
                        //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                        //    //FSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                        //},
                        FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                        FNote = entry.REMARK,
                        FDiscountRate = decimal.Parse(salreturnstock.FDiscountRate),
                        FSalUnitID = new Dictionary<string, string>() { { "FNumber", salreturnstock.FUnitID } },
                        FSalBaseQty = Math.Round(qty / unithsl, jd),
                        FSOEntryId = salreturnstock.FSOEntryId,
                        FAuxpropId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_VER } },
                        FEntity_Link = new List<SAL_RETURNSTOCK.Entity.Entity_Link>()
                            {
                                new SAL_RETURNSTOCK.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =int.Parse(salreturnstock.FID),
                                    FEntity_Link_FSId = pentryid,
                                    FEntity_Link_FBaseActualQtyOld = Math.Round(sqty / unithsl, jd),
                                    FEntity_Link_FBaseActualQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 销售出库的源单信息校验，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R104cs">接收到的数据</param>
        /// <param name="purchaseOrders">源单信息</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<SAL_OUTSTOCK> CreateBatchSALOUTSTOCK(List<R104> R104cs, List<SALOUTSTOCK> purchaseOrders, string billtype, ref string reason)
        {
            reason = "";
            List<SAL_OUTSTOCK> list = new List<SAL_OUTSTOCK>();
            //源单校验
            string pocheck = PONoSALOUTSTOCKCheck(R104cs, purchaseOrders);
            if (!string.IsNullOrEmpty(pocheck))
            {
                reason += reason == "" ? pocheck : "；" + pocheck;
                return list;
            }
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            //将inStocks按源单分组，形成单据头组:按生产用料清单号分组
            var headgroup = (from a in R104cs where a != null select new { AUDIT_ID = a.AUDIT_ID, TRX_DATE = a.TRX_DATE }).Distinct().ToList();
            foreach (var head in headgroup)
            {
                //源单信息
                var purchase = purchaseOrders.Where(s => s.FBillNo == head.AUDIT_ID);
                if (!purchase.Any())
                {
                    string mestr = head.AUDIT_ID + "不存在";
                    reason += reason == "" ? mestr : "；" + mestr;
                    break;
                }
                //原单的单据头
                var order = purchase.FirstOrDefault();
                string newtype = _shareController.GetDesFormID("R104", order!.FBillTypeID);

                //decimal sqty = int.Parse(order!.FMaterialId) - int.Parse(order!.FPickedQty);
                //decimal basesqty = int.Parse(order!.FBaseNeedQty) - int.Parse(order!.FBasePickedQty);

                //创建单据头
                SAL_OUTSTOCK inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() {
                        { "FNUMBER",string.IsNullOrWhiteSpace(newtype) ? billtype : newtype }
                    },
                    FDate = DateTime.Now.ToShortDateString(),
                    FSaleOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleOrgId } },
                    FCustomerID = new Dictionary<string, string>() { { "FNUMBER", order.FCustomerID } },
                    FSaledeptid = new Dictionary<string, string>() { { "FNUMBER", order.FSaleDeptID } },
                    FCorrespondOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FCorrespondOrgId } },
                    FSalesManId = new Dictionary<string, string>() { { "FNUMBER", order.FSalesManID } },
                    FStockOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FSaleOrgId } },
                    FReceiveAddress = order.FReceiveAddress,
                    FOwnerTypeIdHead = order.FOwnerTypeIdHead,
                    FOwnerIdHead = new Dictionary<string, string>() { { "FNUMBER", string.IsNullOrWhiteSpace(order.FOwnerIdHead) ? LocalStaticRequest.DefaultOrg : order.FOwnerIdHead } },
                    FLinkMan = order.FLinkMan,
                    FLinkPhone = order.FLinkPhone,
                    F_SBMI_QUEUE_ID = string.Format("R104:{0}", R104cs.FirstOrDefault().QUEUE_ID), //如果不是盘古，建议放在单据体！！！
                    SubHeadEntity = new SAL_OUTSTOCK.FSubHeadEntity()
                    {
                        FSettleCurrId = new Dictionary<string, string>() { { "FNUMBER", order.FSettleCurrID } },
                        FSettleOrgId = new Dictionary<string, string>() { { "FNUMBER", order.FSettleOrgID } },
                        FSettleTypeId = new Dictionary<string, string>() { { "FNUMBER", order.FSettleTypeID } },
                        FLocalCurrId = new Dictionary<string, string>() { { "FNUMBER", order.FLocalCurrID } },
                        FExchangeTypeId = new Dictionary<string, string>() { { "FNUMBER", order.FExchangeTypeID } },
                        FExchangeRate = order.FExchangeRate
                    }
                };
                //添加单据体，按订单号找到它的明细数据                
                List<SAL_OUTSTOCK.Entity> entries = new List<SAL_OUTSTOCK.Entity>();
                //工单行明细...按生产用料清单号
                List<R104> entrygroup = R104cs.Where(s => s.AUDIT_ID == head.AUDIT_ID).ToList();
                foreach (var entry in entrygroup)
                {
                    var sourentry = purchase.Where(s => s.FBillNo == entry.AUDIT_ID && s.FSeq == entry.AUDIT_SEQ);
                    if (!sourentry.Any())
                    {
                        string mestr = string.Format("未找到源单{0}，第{1}行", entry.AUDIT_ID, entry.AUDIT_SEQ);
                        reason += reason == "" ? mestr : "；" + mestr;
                        break;
                    }
                    SALOUTSTOCK salreturnstock = sourentry.FirstOrDefault();
                    //应出库数量，来自原单
                    decimal sqty = salreturnstock.FRemainOutQty;
                    //校验仓库是否正确
                    string stocknumber = entry.STORE_ID;
                    if (string.IsNullOrWhiteSpace(stocknumber))
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【仓库】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    string martnumber = entry.MTRL_ID;
                    if (string.IsNullOrWhiteSpace(martnumber))
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【物料编码】不能为空";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    decimal qty = _shareController.DecimalPar(entry.QUANTITY);
                    if (qty <= 0)
                    {
                        string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【数量】不正确";
                        reason += reason == "" ? mestr : "；" + mestr;
                    }
                    //else if (qty > sqty)
                    //{
                    //    string mestr = entry.AUDIT_ID + "第" + entry.AUDIT_SEQ + "行【数量】不允许大于未出库数";
                    //    reason += reason == "" ? mestr : "；" + mestr;
                    //}
                    if (reason.Length > 0) { break; }    //遇到错误直接退出，不用浪费时间了...
                    //对应这一行属于源单的信息                 
                    int pentryid = int.Parse(salreturnstock.FEntryID);
                    int pseq = int.Parse(salreturnstock.FSeq);
                    //当前单位编号
                    string unitnumber = salreturnstock.FUnitID;
                    //当前单位实体
                    var unit = units.Find(s => s.FNumber == unitnumber);
                    //当前单位的换算率
                    decimal unithsl = unit.FConvertNumerator / unit.FConvertDenominator;
                    //当前单位的精度
                    int jd = unit.FPrecision;
                    string baseunitnumber = salreturnstock.FBaseUnitID;
                    SAL_OUTSTOCK.Entity stockEntry = new SAL_OUTSTOCK.Entity()
                    {
                        FSrcBillNo = salreturnstock.FBillNo,
                        FMaterialId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_ID } },
                        FUnitID = new Dictionary<string, string>() { { "FNumber", salreturnstock.FUnitID } },
                        FBaseUnitID = new Dictionary<string, string>() { { "FNumber", salreturnstock.FBaseUnitID } },
                        FMustQty = sqty,
                        FBaseMustQty = Math.Round(sqty / unithsl, jd),
                        FRealQty = qty,
                        FPriceUnitQty = qty,
                        FPriceBaseQty = Math.Round(sqty / unithsl, jd),
                        FSalUnitQty = qty,
                        FSalBaseQty = Math.Round(sqty / unithsl, jd),
                        FPrice = !bool.Parse(salreturnstock.FIsFree) ? decimal.Parse(salreturnstock.FPrice) : 0,
                        FTaxPrice = !bool.Parse(salreturnstock.FIsFree) ? decimal.Parse(salreturnstock.FTaxPrice) : 0,
                        FIsFree = bool.Parse(salreturnstock.FIsFree),
                        FEntryTaxRate = int.Parse(salreturnstock.FTaxRate),
                        FOwnerTypeId = salreturnstock.FOwnerTypeIdHead,
                        FOwnerId = new Dictionary<string, string>() { { "FNumber", string.IsNullOrWhiteSpace(salreturnstock.FOwnerIdHead) ? LocalStaticRequest.DefaultOrg : salreturnstock.FOwnerIdHead } },
                        FStockId = new Dictionary<string, string>() { { "FNumber", entry.STORE_ID } },
                        //FStocklocId = new SAL_RETURNSTOCK.Entity.StocklocId()
                        //{
                        //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                        //    //FSTOCKLOCID__FF100002 = new Dictionary<string, string>() { { "FNumber", entry.STORELOC_ID } }
                        //},
                        FLot = new Dictionary<string, string>() { { "FNumber", string.IsNullOrEmpty(entry.ERP_LOTNO) ? "" : entry.ERP_LOTNO } },
                        FNote = entry.REMARK,
                        FDiscountRate = decimal.Parse(salreturnstock.FDiscountRate),
                        FSalUnitID = new Dictionary<string, string>() { { "FNumber", salreturnstock.FUnitID } },
                        FSOEntryId = salreturnstock.FSOEntryId,
                        FAuxpropId = new Dictionary<string, string>() { { "FNumber", entry.MTRL_VER } },
                        FEntity_Link = new List<SAL_OUTSTOCK.Entity.Entity_Link>()
                            {
                                new SAL_OUTSTOCK.Entity.Entity_Link()
                                {
                                    FEntity_Link_FSBillId =int.Parse(salreturnstock.FID),
                                    FEntity_Link_FSId = pentryid,
                                    FEntity_Link_FBaseActualQtyOld = Math.Round(sqty / unithsl, jd),
                                    FEntity_Link_FBaseActualQty = Math.Round(qty / unithsl, jd)
                                }
                            }
                    };
                    entries.Add(stockEntry);
                }
                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);
            }
            return list;
        }

        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R175Ccs">接收到的数据</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_MISCELLANEOUS> CreateBatchSTKMISCELLANEOUS(List<R175C> R175Ccs, ref string reason)
        {
            reason = "";
            List<STK_MISCELLANEOUS> list = new List<STK_MISCELLANEOUS>();
            var R175C = (from a in R175Ccs
                         group a by new { TRX_DATE = a.TRX_DATE, DEPT_ID = a.DEPT_ID, SRTYPE_ID = a.SRTYPE_ID } into b
                         select b).Distinct().ToList();
            foreach (var head in R175C)
            {
                //创建单据头
                STK_MISCELLANEOUS inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", head.Key.SRTYPE_ID } },
                    FDate = DateTime.Now.ToShortDateString(),
                    FDEPTID = new Dictionary<string, string>() { { "FNUMBER", head.Key.DEPT_ID } },
                    F_SBMI_QUEUE_ID = string.Format("R175C:{0}", head.FirstOrDefault().QUEUE_ID), //如果不是盘古，建议放在单据体！！！
                    F_PNDR_Text_qtr = head.FirstOrDefault().ORDER_USER,
                    F_PNDR_Text_qtr2 = head.FirstOrDefault().OA_ID,
                };

                //收集物料清单，一起查询
                var mary = from a in R175Ccs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                //添加单据体，按订单号找到它的明细数据                
                List<STK_MISCELLANEOUS.Entity> entries = (from a in head
                                                          join b in R175Ccs on a.TRX_DATE equals b.TRX_DATE
                                                          join c in marylist on b.MTRL_ID equals c.FNumber
                                                          select new STK_MISCELLANEOUS.Entity
                                                          {
                                                              FMATERIALID = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                                                              FEntryNote = b.REMARK,
                                                              FLOT = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                              FQty = string.IsNullOrEmpty(b.QUANTITY) ? 0 : decimal.Parse(b.QUANTITY),
                                                              FSTOCKID = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                                                              FPrice = head.Key.SRTYPE_ID == "ZPRK" ? decimal.Parse("0.01") : 0,
                                                              FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                              FSubBaseUnitId = new Dictionary<string, string>() { { "FNumber", c.FBaseUnitNumber } },
                                                              FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_MISCELLANEOUS.Entity.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", b.MTRL_VER } } },
                                                              F_PNDR_LargeText_qtr = b.PM_ID,
                                                              F_PNDR_LargeText_83g = b.PM_NAME,
                                                              F_PNDR_Text_re5 = b.PM_M,
                                                              F_PNDR_LargeText_apv = b.PM_STATUS,
                                                          }
                                                         ).Distinct().ToList()
                                                         ;
                var q = entries.Where(s => s.FQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "入库单数量必须大于0";
                    break;
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            return list;
        }

        /// <summary>
        /// 创建其他出库单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R100cs">接收到的数据</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_MisDelivery> CreateBatchSTKMisDelivery(List<R100> R100cs, ref string reason)
        {
            reason = "";
            List<STK_MisDelivery> list = new List<STK_MisDelivery>();

            var R100 = (from a in R100cs
                        group a by new { TRX_DATE = a.TRX_DATE, SRTYPE_ID = a.SRTYPE_ID, DEPT_ID = a.DEPT_ID, StaffNumber = a.StaffNumber, a.CPA } into b
                        select b).Distinct().ToList();
            foreach (var head in R100)
            {
                //创建单据头
                STK_MisDelivery inStock = new()
                {
                    FPickerId = new Dictionary<string, string>() { { "FStaffNumber", head.Key.StaffNumber } },
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", head.Key.SRTYPE_ID } },
                    FDeptId = new Dictionary<string, string>() { { "FNUMBER", head.Key.DEPT_ID } },
                    FDate = DateTime.Now.ToShortDateString(),
                    F_SBMI_QUEUE_ID = string.Format("R100:{0}", head.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                    ,
                    F_PNDR_Assistant_HTXM = new Dictionary<string, string>() { { "FNUMBER", head.Key.CPA } }
                };

                //收集物料清单，一起查询
                var mary = from a in R100cs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                if (marylist.Count == 0)
                {
                    reason = "输入的物料编码不正确";
                    break;
                }
                //添加单据体，按订单号找到它的明细数据                
                List<STK_MisDelivery.Entity> entries = (from a in head
                                                        join b in R100cs on new { A = a.TRX_DATE, B = a.SRTYPE_ID, C = a.DEPT_ID } equals new { A = b.TRX_DATE, B = b.SRTYPE_ID, C = b.DEPT_ID }
                                                        join c in marylist on b.MTRL_ID equals c.FNumber
                                                        select new STK_MisDelivery.Entity
                                                        {
                                                            FMaterialId = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                                                            FEntryNote = b.REMARK,
                                                            FLOT = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                            FQty = string.IsNullOrEmpty(b.QUANTITY) ? 0 : decimal.Parse(b.QUANTITY),
                                                            FStockId = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                                                            //FStockLocId = new STK_MisDelivery.Entity.StockLocId()
                                                            //{
                                                            //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", b.STORELOC_ID } }
                                                            //},
                                                            FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                            FBaseUnitId = new Dictionary<string, string>() { { "FNumber", c.FBaseUnitNumber } },
                                                            FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_MisDelivery.Entity.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", b.MTRL_VER } } },
                                                            F_PNDR_Remarks_YFBH = b.PM_ID,
                                                            F_PNDR_Remarks_YFMC = b.PM_NAME,
                                                            F_PNDR_Text_XMJD = b.PM_STATUS,
                                                            F_PNDR_Text_OALCH = b.OA_ID
                                                        }
                                                         ).Distinct().ToList()
                                                         ;
                var q = entries.Where(s => s.FQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "出库单数量必须大于0";
                    break;
                }

                inStock.FEntity = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            return list;
        }
        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R023cs">接收到的数据</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>

        private List<STK_StockCountGain> CreateBatchSTKStockCountGain(List<R023> R023cs, string billtype, ref string reason)
        {
            reason = "";
            List<STK_StockCountGain> list = new List<STK_StockCountGain>();

            var R023 = (from a in R023cs
                        group a by a.TRX_DATE into b
                        select b).ToList();
            foreach (var head in R023)
            {
                //创建单据头
                STK_StockCountGain inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = head.Key,
                    F_SBMI_QUEUE_ID = string.Format("R023:{0}", head.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };

                //收集物料清单，一起查询
                var mary = from a in R023cs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                if (marylist.Count == 0)
                {
                    reason = "输入的物料编码不正确";
                    break;
                }
                //添加单据体，按订单号找到它的明细数据                
                List<STK_StockCountGain.BillEntry> entries = (from a in head
                                                              join b in R023cs on a.TRX_DATE equals b.TRX_DATE
                                                              join c in marylist on b.MTRL_ID equals c.FNumber
                                                              select new STK_StockCountGain.BillEntry
                                                              {
                                                                  FMaterialId = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                                                                  Fnote = b.REMARK,
                                                                  FLOT = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                                  FCountQty = string.IsNullOrEmpty(b.QUANTITY) ? 0 : decimal.Parse(b.QUANTITY),
                                                                  FStockId = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                                                                  //FStockLocId = new STK_StockCountGain.BillEntry.StockLocId()
                                                                  //{
                                                                  //    FSTOCKLOCID__FF100001 = new Dictionary<string, string>() { { "FNumber", b.STORELOC_ID } }
                                                                  //},                                                                                                      
                                                                  FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                                  FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_StockCountGain.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", b.MTRL_VER } } },

                                                              }
                                                         ).Distinct().ToList()
                                                         ;
                var q = entries.Where(s => s.FCountQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "入库单数量必须大于0";
                    break;
                }

                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            return list;
        }
        /// <summary>
        /// 在RB系统校验源单信息，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R024cs">接收到的数据</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_StockCountLoss> CreateBatchSTKStockCountLoss(List<R024> R024cs, string billtype, ref string reason)
        {
            reason = "";
            List<STK_StockCountLoss> list = new List<STK_StockCountLoss>();
            var R024 = (from a in R024cs
                        group a by a.TRX_DATE into b
                        select b).Distinct().ToList();
            foreach (var head in R024)
            {
                //创建单据头
                STK_StockCountLoss inStock = new()
                {

                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = head.Key,
                    F_SBMI_QUEUE_ID = string.Format("R024:{0}", head.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };

                //收集物料清单，一起查询
                var mary = from a in R024cs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                if (marylist.Count == 0)
                {
                    reason = "传入的物料编码不正确...";
                    break;
                }
                //添加单据体，按订单号找到它的明细数据                
                List<STK_StockCountLoss.BillEntry> entries = (from a in head
                                                              join b in R024cs on a.TRX_DATE equals b.TRX_DATE
                                                              join c in marylist on b.MTRL_ID equals c.FNumber
                                                              select new STK_StockCountLoss.BillEntry
                                                              {
                                                                  FMaterialId = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                                                                  Fnote = b.REMARK,
                                                                  FLOT = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                                  FCountQty = string.IsNullOrEmpty(b.QUANTITY) ? 0 : decimal.Parse(b.QUANTITY),
                                                                  FStockId = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                                                                  FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                                  FSubBaseUnitId = new Dictionary<string, string>() { { "FNumber", c.FBaseUnitNumber } },
                                                                  FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_StockCountLoss.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", b.MTRL_VER } } },
                                                              }
                                                         ).Distinct().ToList()
                                                         ;
                var q = entries.Where(s => s.FCountQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "入库单数量必须大于0";
                    break;
                }


                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            return list;
        }
        /// <summary>
        /// 受托加工材料入库单，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R171Ccs">接收到的数据</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_OEMInStock> CreateBatchSTKOEMInStock(List<R171C> R171Ccs, string billtype, ref string reason)
        {
            reason = "";
            List<STK_OEMInStock> list = new List<STK_OEMInStock>();
            var R171C = (from a in R171Ccs
                         group a by new { a.TRX_DATE, a.CUST_ID } into b
                         select b).Distinct().ToList();
            foreach (var head in R171C)
            {
                //创建单据头
                STK_OEMInStock inStock = new()
                {

                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = DateTime.Now.ToShortDateString(),
                    FCustId = new Dictionary<string, string>() { { "FNumber", head.Key.CUST_ID } },
                    F_SBMI_QUEUE_ID = string.Format("R171C:{0}", head.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };

                //收集物料清单，一起查询
                var mary = from a in R171Ccs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                if (marylist.Count == 0)
                {
                    reason = "传入的物料编码不正确...";
                    break;
                }
                //添加单据体，按订单号找到它的明细数据                
                List<STK_OEMInStock.BillEntry> entries = (from a in head
                                                          join b in R171Ccs on new { A = a.TRX_DATE, B = a.CUST_ID } equals new { A = b.TRX_DATE, B = b.CUST_ID }
                                                          join c in marylist on b.MTRL_ID equals c.FNumber
                                                          select new STK_OEMInStock.BillEntry
                                                          {
                                                              FMaterialId = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                                                              FNoteEntry = b.REMARK,
                                                              FLot = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                              FQty = string.IsNullOrEmpty(b.QUANTITY) ? 0 : decimal.Parse(b.QUANTITY),
                                                              FStockId = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                                                              FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                              FBaseUnitId = new Dictionary<string, string>() { { "FNumber", c.FBaseUnitNumber } },
                                                              FOwnerId = new Dictionary<string, string>() { { "FNumber", a.CUST_ID } },
                                                              FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_OEMInStock.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", b.MTRL_VER } } },
                                                          }
                                                         ).Distinct().ToList()
                                                         ;
                var q = entries.Where(s => s.FQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "入库单数量必须大于0";
                    break;
                }


                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            return list;
        }
        /// <summary>
        /// 受托加工材料入库单，并转换成Cloud的实体类
        /// </summary>
        /// <param name="R006_CUSTcs">接收到的数据</param>
        /// <param name="billtype">FBillTypeID的FNumber属性</param>
        /// <param name="reason">如果错误，则返回消息</param>
        /// <returns></returns>
        private List<STK_OEMInStockRETURN> CreateBatchSTKOEMInStockRETURN(List<R006_CUST> R006_CUSTcs, string billtype, ref string reason)
        {
            reason = "";
            List<STK_OEMInStockRETURN> list = new List<STK_OEMInStockRETURN>();
            var R006_CUST = (from a in R006_CUSTcs
                             group a by new { a.TRX_DATE, a.CUST_ID } into b
                             select b).Distinct().ToList();
            foreach (var head in R006_CUST)
            {
                //创建单据头
                STK_OEMInStockRETURN inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = DateTime.Now.ToShortDateString(),
                    FCustId = new Dictionary<string, string>() { { "FNumber", head.Key.CUST_ID } },
                    F_SBMI_QUEUE_ID = string.Format("R006_CUST:{0}", head.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };

                //收集物料清单，一起查询
                var mary = from a in R006_CUSTcs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                if (marylist.Count == 0)
                {
                    reason = "传入的物料编码不正确...";
                    break;
                }
                //添加单据体，按订单号找到它的明细数据                
                List<STK_OEMInStockRETURN.BillEntry> entries = (from a in head
                                                                join b in R006_CUSTcs on new { A = a.TRX_DATE, B = a.CUST_ID } equals new { A = b.TRX_DATE, B = b.CUST_ID }
                                                                join c in marylist on b.MTRL_ID equals c.FNumber
                                                                select new STK_OEMInStockRETURN.BillEntry
                                                                {
                                                                    FMaterialId = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                                                                    FNoteEntry = b.REMARK,
                                                                    FLot = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                                                                    FQty = string.IsNullOrEmpty(b.QUANTITY) ? 0 : decimal.Parse(b.QUANTITY),
                                                                    FStockId = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                                                                    FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                                                                    FBaseUnitId = new Dictionary<string, string>() { { "FNumber", c.FBaseUnitNumber } },
                                                                    FOwnerId = new Dictionary<string, string>() { { "FNumber", a.CUST_ID } },
                                                                    FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_OEMInStockRETURN.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNUMBER", b.MTRL_VER } } }
                                                                }
                                                         ).Distinct().ToList()
                                                         ;
                var q = entries.Where(s => s.FQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "入库单数量必须大于0";
                    break;
                }


                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            return list;
        }
        private BreakData ExistsDoubleQueid<T>(List<T> inputdata, string DocType) where T : class
        {
            BreakData breakData = new BreakData() { Status = false };
            //return breakData;
            Type type = typeof(T);
            PropertyInfo[] propertyInfos = type.GetProperties();
            var propertyInfo = propertyInfos.Where(s => s.Name == "QUEUE_ID");
            if (propertyInfo.Any())
            {
                try
                {
                    List<string> strings = new List<string>();
                    PropertyInfo infos = propertyInfo.FirstOrDefault()!;
                    foreach (var obj in inputdata)
                    {
                        object v = infos.GetValue(obj)!;
                        strings.Add(string.Format("'{0}'", v.ToString()));
                    }
                    string sql = string.Format("select * from r_ReciveDataBack where FDocType='{0}' and QUEUE_ID in ({1})", DocType, string.Join(',', strings.ToArray()));
                    //直接SQL查询更快！！！
                    string messstr = string.Empty;
                    DataSet? dataSet = _kdsqlhelper.GetDataSet(CommandType.Text, sql, null, ref messstr!, false);
                    if (dataSet != null || string.IsNullOrEmpty(messstr))
                    {
                        if (dataSet.Tables.Count != 0)
                        {
                            DataTable dataTable = dataSet.Tables[0];
                            if (dataTable.Rows.Count != 0)
                            {
                                breakData.Status = true;
                                breakData.Message = "不可重复推单";
                                var dt = (from a in dataTable.AsEnumerable()
                                          select new
                                          {
                                              QUEUE_ID = a.Field<string>("QUEUE_ID").ToString(),
                                              FDocType = a.Field<string>("FDocType")!,
                                              FBillNo = a.Field<string>("FBillNo")!,
                                              FErpID = a.Field<int>("FErpID"),
                                              FSeq = a.Field<int>("FSeq")
                                          }).ToList();
                                breakData.DataSet = dt;
                            }
                        }
                    }
                    else
                    {
                        breakData.Message = messstr;
                    }
                }
                catch (Exception ex)
                {
                    breakData.Message = "错误在ExistsDoubleQueid中发生：" + ex.Message;
                    //throw;
                }
            }
            else
            {
                breakData.Message = "传入的数据中必须有字段【QUEUE_ID】";
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通生产补料单
        /// </summary>
        /// <param name="fid">FFunctionID</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchPRDFeedMtrlAsync(FormIDType formIDType, string data, int fid, int childtypeid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            //string sourcedata =await _chiledselect.GetCustomRequestBillAsync(fchildmid);
            //List<PrdPpbomEntry> purchaseOrders = _shareController.DataConvertClass<PrdPpbomEntry>(sourcedata,ref reason);
            //List<PrdPpbomEntry> purchaseOrders = _chiledselect.GetSourceBillEntry<PrdPpbomEntry>(dataTable, fchildmid, ref reason);

            List<R062>? mes_POInStocks = _shareController.DataConvertClass<R062>(data, ref reason);
            if (mes_POInStocks != null)
            {
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, formIDType.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                //通过输入信息得到子查询的条件
                List<string> monumberes = (from a in mes_POInStocks
                                           select a.ERP_MO).Distinct().ToList();
                //准备输入参数
                List<Dictionary<string, object>> keyValues = new List<Dictionary<string, object>>();
                foreach (string key in monumberes)
                {
                    keyValues.Add(new Dictionary<string, object>()
                    {
                        {"@ERP_MO",key}
                    });
                }
                var questobj = await _shareController.GetAPIList<CloudBillQuery>();
                questobj = questobj.Where(s => s.FFormIDTypeID == formIDType.FID).ToList();
                if (questobj.Count == 0)
                {
                    breakData.Message = "此接口的请求条件未配置";
                    return breakData;
                }
                //把源单信息都取出来
                CloudBillQuery billQuery = questobj.FirstOrDefault();
                string fk = billQuery.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                string fs = billQuery.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                //string sourcebills = await _chiledselect.GetCustomRequestTableAsync(billQuery.FFieldKeys, billQuery.FFiledString, billQuery.FOrderString, keyValues);
                string sourcebills = GetCustomRequestTableAsync(fk, fs, billQuery.FOrderString, keyValues, ref reason);
                if (!string.IsNullOrWhiteSpace(reason))
                {
                    breakData.Message = reason;
                }
                else
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == childtypeid)
                        .FirstOrDefault();
                    List<PrdPpbomEntry> purchaseOrders = _shareController.DataConvertClass<PrdPpbomEntry>(sourcebills, ref reason);
                    if (!string.IsNullOrWhiteSpace(reason))
                    {
                        breakData.Message = reason;
                    }
                    else
                    {
                        List<PRD_FeedMtrl> inStocks = CreateBatchPRDFeedMtrl(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                        if (inStocks.Count() > 0 && reason == "")
                        {
                            breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                            if (breakData.Status)
                            {
                                List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                                if (breakData.DataSet != null)
                                {
                                    //提取出创建好的单据编号
                                    List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                    List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                    Thread thread = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread.Start(dataBacks);
                                }
                                else
                                {
                                    dataBacks = await DataBacks(childtypeid, mes_POInStocks);
                                    if (dataBacks.Count != 0)
                                    {
                                        Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                        //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                        thread_1.Start(dataBacks);
                                    }
                                    else
                                    {
                                        breakData.Status = false;
                                        breakData.Message = "不可控因素，请稍后再次提交";
                                    }
                                }
                                breakData.DataSet = dataBacks;
                            }
                        }
                        else
                        {
                            breakData.Message = reason;
                        }
                    }
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通委外加工补料单
        /// </summary>
        /// <param name="fid">FFunctionID</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSUBFeedMtrlAsync(FormIDType formIDType, string data, int fid, int childtypeid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            //string sourcedata =await _chiledselect.GetCustomRequestBillAsync(fchildmid);
            //List<PrdPpbomEntry> purchaseOrders = _shareController.DataConvertClass<PrdPpbomEntry>(sourcedata,ref reason);
            //List<PrdPpbomEntry> purchaseOrders = _chiledselect.GetSourceBillEntry<PrdPpbomEntry>(dataTable, fchildmid, ref reason);

            List<R062_1>? mes_POInStocks = _shareController.DataConvertClass<R062_1>(data, ref reason);
            if (mes_POInStocks != null)
            {
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, formIDType.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                //通过输入信息得到子查询的条件
                List<string> monumberes = (from a in mes_POInStocks
                                           select a.ERP_MO).Distinct().ToList();
                //准备输入参数
                List<Dictionary<string, object>> keyValues = new List<Dictionary<string, object>>();
                foreach (string key in monumberes)
                {
                    keyValues.Add(new Dictionary<string, object>()
                    {
                        {"@ERP_MO",key}
                    });
                }
                var questobj = await _shareController.GetAPIList<CloudBillQuery>();
                questobj = questobj.Where(s => s.FFormIDTypeID == formIDType.FID).ToList();
                if (questobj.Count == 0)
                {
                    breakData.Message = "此接口的请求条件未配置";
                    return breakData;
                }
                //把源单信息都取出来
                CloudBillQuery billQuery = questobj.FirstOrDefault();
                string fk = billQuery.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                string fs = billQuery.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                //string sourcebills = await _chiledselect.GetCustomRequestTableAsync(billQuery.FFieldKeys, billQuery.FFiledString, billQuery.FOrderString, keyValues);
                string sourcebills = GetCustomRequestTableAsync(fk, fs, billQuery.FOrderString, keyValues, ref reason);
                if (!string.IsNullOrWhiteSpace(reason))
                {
                    breakData.Message = reason;
                }
                else
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == childtypeid)
                        .FirstOrDefault();
                    List<SUBPickMtrl> purchaseOrders = _shareController.DataConvertClass<SUBPickMtrl>(sourcebills, ref reason);
                    if (!string.IsNullOrWhiteSpace(reason))
                    {
                        breakData.Message = reason;
                    }
                    else
                    {
                        List<SUB_FeedMtrl> inStocks = CreateBatchSUBFeedMtrl(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                        if (inStocks.Count() > 0 && reason == "")
                        {
                            breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                            if (breakData.Status)
                            {
                                List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                                if (breakData.DataSet != null)
                                {
                                    //提取出创建好的单据编号
                                    List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                    List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                    Thread thread = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread.Start(dataBacks);
                                }
                                else
                                {
                                    dataBacks = await DataBacks(childtypeid, mes_POInStocks);
                                    if (dataBacks.Count != 0)
                                    {
                                        Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                        //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                        thread_1.Start(dataBacks);
                                    }
                                    else
                                    {
                                        breakData.Status = false;
                                        breakData.Message = "不可控因素，请稍后再次提交";
                                    }
                                }
                                breakData.DataSet = dataBacks;
                            }
                        }
                        else
                        {
                            breakData.Message = reason;
                        }
                    }
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通采购退料单(含VMI退料、委外退回加工)
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchPURMRBAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            List<PurchaseOrder> purchaseOrders = _chiledselect.GetSourceBillEntry<PurchaseOrder>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                List<R006>? mes_POInStocks = _shareController.DataConvertClass<R006>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    //string dectype = _shareController.GetDesFormID(dataSave.GetForm.FDocType, purchaseOrders.FirstOrDefault().FBillTypeID);
                    List<PUR_MRB> inStocks = CreateBatchPURMRB(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);

                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核采购入库单方法，包含VMI和委外入库
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchInStockAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //GetCustomRequestTableAsync

            List<R170C>? mes_POInStocks = _shareController.DataConvertClass<R170C>(data, ref reason);
            //应当就采购订单号，查询源单并校验 
            List<PurchaseOrder> purchaseOrders = new List<PurchaseOrder>();
            string newrate = LocalStaticRequest.GetSystemProfile(5, "NewRate");
            bool nrate = string.IsNullOrEmpty(newrate) ? false : newrate == "1" ? true : false;
            if (nrate)
            {
                var questobj = await _shareController.GetAPIList<CloudBillQuery>();
                var po = questobj.Find(s => s.FFormIDTypeID == fchildmid);
                if (po != null)
                {
                    //准备输入参数
                    List<Dictionary<string, object>> keyValues = new List<Dictionary<string, object>>();
                    foreach (var key in mes_POInStocks)
                    {
                        keyValues.Add(new Dictionary<string, object>()
                    {
                        {"@PO_ID",key.PO_ID},
                        {"@PO_SEQ",key.PO_SEQ}
                    });
                    }
                    string fk = po.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                    string fs = po.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                    string orderes = GetCustomRequestTableAsync(fk, fs, po.FOrderString, keyValues, ref reason);
                    if (!string.IsNullOrEmpty(reason))
                    {
                        breakData.Message = reason;
                    }
                    else { purchaseOrders = _shareController.DataConvertClass<PurchaseOrder>(orderes, ref reason); }
                }
                else
                {
                    breakData.Message = string.Format("CloudBillQuery参数未配置FFormIDTypeID={0}的项目", fchildmid);
                    return breakData;
                }
            }
            else
            {
                //取采购订单上的汇率（使用云星空API查询接口，注意要符合表r_CloudBillQuery的配置！！！）
                purchaseOrders = _chiledselect.GetSourceBillEntry<PurchaseOrder>(dataTable, fchildmid, ref reason);
            }

            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    //为采购入库类赋值
                    //string dectype = _shareController.GetDesFormID(dataSave.GetForm.FDocType, purchaseOrders.FirstOrDefault().FBillTypeID);
                    List<STK_InStock> inStocks = CreateBatchSTKInStock(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                    if (inStocks.Count() > 0 && reason == "")
                    {

                        string json = JsonConvert.SerializeObject(inStocks);
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 根据金蝶系统返回字段，整理成list并返回给推送方
        /// </summary>
        /// <param name="doctype">接口类型</param>
        /// <param name="mesobj">推送的数据</param>
        /// <param name="billlist">ERP返回的集合</param>
        /// <param name="dataBacks">返回给推送方的文本</param>
        /// <returns></returns>
        private List<string> GetNewBillNoList<T>(string doctype, List<T>? mesobj,
            List<Dictionary<string, string>> billlist, ref List<ReciveDataBack> dataBacks) where T : class
        {
            List<string> billnolist = new List<string>();
            Type type = typeof(T);
            PropertyInfo[] propertyInfos = type.GetProperties();
            var Q = propertyInfos.Where(s => s.Name == "QUEUE_ID");
            if (!Q.Any())
            {
                return billnolist;
            }
            PropertyInfo propertyInfo = Q.FirstOrDefault()!;
            dataBacks = new List<ReciveDataBack>();
            if (billlist != null)
            {
                foreach (var mes in mesobj)
                {
                    string billno = string.Empty;
                    int FID = 0;
                    int FSeq = 0;
                    foreach (var bill in billlist)
                    {
                        if (bill.ContainsKey("FBillNo"))
                        {
                            billno = bill["FBillNo"].ToString();
                            if (!billnolist.Contains(billno)) { billnolist.Add(billno); }    //注意？后台配置r_DataSaveBatch时，FID,FBillNo,FSeq是必须的...
                        }
                        if (bill.ContainsKey("FID")) { FID = int.Parse(bill["FID"].ToString()); }
                        if (bill.ContainsKey("FSeq")) { FSeq = int.Parse(bill["FSeq"].ToString()); }
                    }
                    if (!string.IsNullOrEmpty(billno))
                    {
                        dataBacks.Add(
                            new ReciveDataBack()
                            {
                                FBillNo = billno,
                                FDocType = doctype,
                                FErpID = FID,
                                FSeq = FSeq,
                                QUEUE_ID = (string)propertyInfo.GetValue(mes),
                                FJson = JsonConvert.SerializeObject(mes)
                            }
                        );
                    }
                }
            }
            return billnolist;
        }

        /// <summary>
        /// 成功保存到ERP之后备份主键信息到沃比数据库
        /// </summary>
        /// <param name="dataBacks">需要保存的数据集</param>
        private async Task<int> SaveReciveDataBackAsync(List<ReciveDataBack> dataBacks)
        {
            int num = -1;
            try
            {
                if (dataBacks.Count > 0)
                {
                    _context.r_ReciveDataBack!.AddRange(dataBacks);
                    num = await _context.SaveChangesAsync();
                }
            }
            catch (Exception ex)
            {
                string bak = JsonConvert.SerializeObject(dataBacks);
                LogHelper.WriteLog(string.Format("备份ReciveDataBack时发生错误：{0}\n" + ex.Message, bak));
                //throw;
            }
            return num;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通生产领料单
        /// </summary>
        /// <param name="formIDType">接口类</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchPickMtrlAsync(FormIDType formIDType, string data, int fid, int childtypeid)
        {
            Stopwatch stopwatchAll = new Stopwatch();
            // 开始计时
            stopwatchAll.Start();

            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            //string sourcedata =await _chiledselect.GetCustomRequestBillAsync(fchildmid);
            //List<PrdPpbomEntry> purchaseOrders = _shareController.DataConvertClass<PrdPpbomEntry>(sourcedata,ref reason);
            //List<PrdPpbomEntry> purchaseOrders = _chiledselect.GetSourceBillEntry<PrdPpbomEntry>(dataTable, fchildmid, ref reason);

            List<R014>? mes_POInStocks = _shareController.DataConvertClass<R014>(data, ref reason);
            if (mes_POInStocks != null)
            {
                breakData.Message = string.Empty;

                Stopwatch ExistsDoubleQueidWatch = new Stopwatch();
                // 开始计时
                ExistsDoubleQueidWatch.Start();
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, formIDType.FDocType);
                // 停止计时
                ExistsDoubleQueidWatch.Stop();
                // 获取执行时间
                LogHelper.WriteLog($"生产领料单_校验是否有重复推单执行时间: {ExistsDoubleQueidWatch.Elapsed.TotalMilliseconds} 毫秒");

                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                //通过输入信息得到子查询的条件
                List<string> monumberes = (from a in mes_POInStocks
                                           select a.ERP_MO).Distinct().ToList();
                //准备输入参数
                List<Dictionary<string, object>> keyValues = new List<Dictionary<string, object>>();
                foreach (string key in monumberes)
                {
                    keyValues.Add(new Dictionary<string, object>()
                    {
                        {"@ERP_MO",key}
                    });
                }
                List<CloudBillQuery> questobj = await _shareController.GetAPIList<CloudBillQuery>();
                questobj = questobj.Where(s => s.FFormIDTypeID == formIDType.FID).ToList();
                if (questobj.Count == 0)
                {
                    breakData.Message = "此接口的请求条件未配置";
                    return breakData;
                }

                Stopwatch sourcebillsWatch = new Stopwatch();
                // 开始计时
                sourcebillsWatch.Start();

                //把源单信息都取出来
                CloudBillQuery billQuery = questobj.FirstOrDefault();
                string fk = billQuery.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                string fs = billQuery.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                //string sourcebills = await _chiledselect.GetCustomRequestTableAsync(billQuery.FFieldKeys, billQuery.FFiledString, billQuery.FOrderString, keyValues);
                string sourcebills = GetCustomRequestTableAsync(fk, fs, billQuery.FOrderString, keyValues, ref reason);

                // 停止计时
                sourcebillsWatch.Stop();
                // 获取执行时间
                LogHelper.WriteLog($"生产领料单_获取源单执行时间: {sourcebillsWatch.Elapsed.TotalMilliseconds} 毫秒");

                if (!string.IsNullOrWhiteSpace(reason))
                {
                    breakData.Message = reason;
                }
                else
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == childtypeid).FirstOrDefault();

                    Stopwatch dataConvertWatch = new Stopwatch();
                    // 开始计时
                    dataConvertWatch.Start();

                    List<PrdPpbomEntry> purchaseOrders = _shareController.DataConvertClass<PrdPpbomEntry>(sourcebills, ref reason);

                    // 停止计时
                    dataConvertWatch.Stop();
                    // 获取执行时间
                    LogHelper.WriteLog($"生产领料单_数据转换执行时间: {dataConvertWatch.Elapsed.TotalMilliseconds} 毫秒");

                    if (!string.IsNullOrWhiteSpace(reason))
                    {
                        breakData.Message = reason;
                    }
                    else
                    {
                        //把主料为空的转为新的主料
                        List<PrdPpbomEntry> nullmain = purchaseOrders.Where(s => s.FMainMaterial == null && s.FMaterial != null).ToList();
                        foreach (PrdPpbomEntry s_change in nullmain)
                        {
                            //将mesbom中的主料拿过来自己用
                            var item = mes_POInStocks.Find(s => s.MTRL_ID == s_change.FMaterial);
                            if (item != null)
                            {
                                string main = mes_POInStocks.Find(s => s.MTRL_ID == s_change.FMaterial).MAIN_ID;
                                s_change.FMainMaterial = main;
                            }
                        }

                        List<PRD_PickMtrl> inStocks = CreateBatchPRDPickMtrl(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                        if (inStocks.Count() > 0 && string.IsNullOrWhiteSpace(reason))
                        {
                            breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                            if (breakData.Status)
                            {
                                List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                                if (breakData.DataSet != null)
                                {
                                    //提取出创建好的单据编号
                                    List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                    List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                    Thread thread = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread.Start(dataBacks);
                                }
                                else
                                {
                                    dataBacks = await DataBacks(childtypeid, mes_POInStocks);
                                    if (dataBacks.Count != 0)
                                    {
                                        Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                        //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                        thread_1.Start(dataBacks);
                                    }
                                    else
                                    {
                                        breakData.Status = false;
                                        breakData.Message = "不可控因素，请稍后再次提交";
                                    }
                                }
                                breakData.DataSet = dataBacks;
                            }
                        }
                        else
                        {
                            breakData.Message = reason;
                        }
                    }
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }

            // 停止计时
            stopwatchAll.Stop();
            // 获取执行时间
            TimeSpan elapsed = stopwatchAll.Elapsed;
            LogHelper.WriteLog($"生产领料单_总执行时间: {elapsed.TotalMilliseconds} 毫秒");

            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通生产入库单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchINSTOCKAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            List<PRDINSTOCK> purchaseOrders = _chiledselect.GetSourceBillEntry<PRDINSTOCK>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                List<R102>? mes_POInStocks = _shareController.DataConvertClass<R102>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    //string dectype = _shareController.GetDesFormID(dataSave.GetForm.FDocType, purchaseOrders.FirstOrDefault().FBillTypeID);
                    List<PRD_INSTOCK> inStocks = CreateBatchPRDINSTOCK(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通生产汇报单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchMORPTAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            List<PRDMORPT> purchaseOrders = _chiledselect.GetSourceBillEntry<PRDMORPT>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                List<R202>? mes_POInStocks = _shareController.DataConvertClass<R202>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    //string dectype = _shareController.GetDesFormID(dataSave.GetForm.FDocType, purchaseOrders.FirstOrDefault().FBillTypeID);
                    List<PRD_MORPT> inStocks = CreateBatchPRDMO(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通直接调拨单
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKTransferDirectAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP单号、行号，查询源单，并校验
            List<STKTransferDirect> purchaseOrders = _chiledselect.GetSourceBillEntry<STKTransferDirect>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                List<R010>? mes_POInStocks = _shareController.DataConvertClass<R010>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    List<STK_TransferDirect> inStocks = CreateBatchSTKTransferDirect(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通直接调拨单(二次调拨)
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSTKTransferDirect_2Async(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            List<STKTransferDirect_2> purchaseOrders = _chiledselect.GetSourceBillEntry<STKTransferDirect_2>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                //与一次调拨使用相同的接收类...
                List<R010>? mes_POInStocks = _shareController.DataConvertClass<R010>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    List<STK_TransferDirect> inStocks = CreateBatchSTKTransferDirect_2(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通委外领料单，已弃用
        /// </summary>
        /// <param name="dataTable">接收到的数据，已转换为table</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="postmid">主查询doctypeid</param>
        /// <param name="fchildmid">子查询doctypeid</param>
        /// <param name="data">接收到的直接数据</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSUBPickMtrlAsync(DataTable dataTable, int fid, int postmid, int fchildmid, string data)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            //应当就ERP生产用料清单号、行号，查询源单，并校验
            List<SUBPickMtrl> purchaseOrders = _chiledselect.GetSourceBillEntry<SUBPickMtrl>(dataTable, fchildmid, ref reason);
            if (!string.IsNullOrEmpty(reason))
            {
                breakData.Message = reason;
            }
            else
            {
                List<R014_1>? mes_POInStocks = _shareController.DataConvertClass<R014_1>(data, ref reason);
                if (mes_POInStocks != null)
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                        .FirstOrDefault();
                    breakData.Message = string.Empty;
                    //校验是否有重复推单
                    breakData = ExistsDoubleQueid(mes_POInStocks, dataSave.GetForm.FDocType);
                    if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                    {
                        return breakData;
                    }
                    //string dectype = _shareController.GetDesFormID(dataSave.GetForm.FDocType, purchaseOrders.FirstOrDefault().FBillTypeID);
                    List<SUB_PickMtrl> inStocks = CreateBatchSUBPickMtrl(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                    if (inStocks.Count() > 0 && reason == "")
                    {
                        breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                        if (breakData.Status)
                        {
                            List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                            if (breakData.DataSet != null)
                            {
                                //提取出创建好的单据编号
                                List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                Thread thread = new Thread(_shareController.SaveReciveData);
                                //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                thread.Start(dataBacks);
                            }
                            else
                            {
                                dataBacks = await DataBacks(postmid, mes_POInStocks);
                                if (dataBacks.Count != 0)
                                {
                                    Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread_1.Start(dataBacks);
                                }
                                else
                                {
                                    breakData.Status = false;
                                    breakData.Message = "不可控因素，请稍后再次提交";
                                }
                            }
                            breakData.DataSet = dataBacks;
                        }
                    }
                    else
                    {
                        breakData.Message = reason;
                    }
                }
                else
                {
                    breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
                }
            }
            return breakData;
        }

        /// <summary>
        /// 提供批量保存、提交并审核普通委外领料单
        /// 自定义SQL查询源单
        /// </summary>
        /// <param name="formIDType">接口类</param>
        /// <param name="data">接收到的直接数据</param>
        /// <param name="fid">FFunctionID</param>
        /// <param name="childtypeid">childtypeid</param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSUBPickMtrlAsync(FormIDType formIDType, string data, int fid, int childtypeid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<R014_1>? mes_POInStocks = _shareController.DataConvertClass<R014_1>(data, ref reason);
            if (mes_POInStocks != null)
            {
                breakData.Message = string.Empty;
                //校验是否有重复推单
                breakData = ExistsDoubleQueid(mes_POInStocks, formIDType.FDocType);
                if (breakData.Status || !string.IsNullOrEmpty(breakData.Message.ToString()))
                {
                    return breakData;
                }
                //通过输入信息得到子查询的条件
                List<string> monumberes = (from a in mes_POInStocks
                                           select a.ERP_MO).Distinct().ToList();
                //准备输入参数
                List<Dictionary<string, object>> keyValues = new List<Dictionary<string, object>>();
                foreach (string key in monumberes)
                {
                    keyValues.Add(new Dictionary<string, object>()
                    {
                        {"@ERP_MO",key}
                    });
                }
                var questobj = await _shareController.GetAPIList<CloudBillQuery>();
                questobj = questobj.Where(s => s.FFormIDTypeID == formIDType.FID).ToList();
                if (questobj.Count == 0)
                {
                    breakData.Message = "此接口的请求条件未配置";
                    return breakData;
                }
                //把源单信息都取出来
                CloudBillQuery billQuery = questobj.FirstOrDefault();
                string fk = billQuery.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                string fs = billQuery.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                //string sourcebills = await _chiledselect.GetCustomRequestTableAsync(billQuery.FFieldKeys, billQuery.FFiledString, billQuery.FOrderString, keyValues);
                string sourcebills = GetCustomRequestTableAsync(fk, fs, billQuery.FOrderString, keyValues, ref reason);
                if (!string.IsNullOrWhiteSpace(reason))
                {
                    breakData.Message = reason;
                }
                else
                {
                    //推送数据
                    var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                    DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == childtypeid)
                        .FirstOrDefault();
                    List<SUBPickMtrl> purchaseOrders = _shareController.DataConvertClass<SUBPickMtrl>(sourcebills, ref reason);
                    if (!string.IsNullOrWhiteSpace(reason))
                    {
                        breakData.Message = reason;
                    }
                    else
                    {
                        //把主料为空的转为新的主料
                        List<SUBPickMtrl> nullmain = purchaseOrders.Where(s => s.FMainMaterial == null && s.FMaterial != null).ToList();
                        foreach (SUBPickMtrl s_change in nullmain)
                        {
                            //将mesbom中的主料拿过来自己用
                            var item = mes_POInStocks.Find(s => s.MTRL_ID == s_change.FMaterial);
                            if (item != null)
                            {
                                string main = mes_POInStocks.Find(s => s.MTRL_ID == s_change.FMaterial).MAIN_ID;
                                s_change.FMainMaterial = main;
                            }
                        }

                        List<SUB_PickMtrl> inStocks = CreateBatchSUBPickMtrl(mes_POInStocks, purchaseOrders, dataSave.FErpBillType, ref reason);
                        //string json = JsonConvert.SerializeObject(inStocks);
                        if (inStocks.Count() > 0 && string.IsNullOrWhiteSpace(reason))
                        {
                            breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, inStocks);
                            if (breakData.Status)
                            {
                                List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                                if (breakData.DataSet != null)
                                {
                                    //提取出创建好的单据编号
                                    List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet;
                                    List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mes_POInStocks, billlist, ref dataBacks);
                                    Thread thread = new Thread(_shareController.SaveReciveData);
                                    //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                    thread.Start(dataBacks);
                                }
                                else
                                {
                                    dataBacks = await DataBacks(childtypeid, mes_POInStocks);
                                    if (dataBacks.Count != 0)
                                    {
                                        Thread thread_1 = new Thread(_shareController.SaveReciveData);
                                        //thread_1.IsBackground = true; //这意味着主程序可能不等它保存了...
                                        thread_1.Start(dataBacks);
                                    }
                                    else
                                    {
                                        breakData.Status = false;
                                        breakData.Message = "不可控因素，请稍后再次提交";
                                    }
                                }
                                breakData.DataSet = dataBacks;
                            }
                        }
                        else
                        {
                            breakData.Message = reason;
                        }
                    }
                }
            }
            else
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "输入数据转换出错" : reason;
            }
            return breakData;
        }

        /// <summary>
        /// 为了减少一些操作，从数据库里读出来就转为需要的类
        /// </summary>
        /// <param name="mobuilder"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private List<PPBOMHeadStr>? GetPPBOMHead(List<string> mobuilder, ref string reason)
        {
            List<PPBOMHeadStr> list = new List<PPBOMHeadStr>();
            StringBuilder sql = new StringBuilder();
            sql.AppendLine("/*dialect*/SELECT DISTINCT M1.FNUMBER FMaterialID,D1.FNUMBER FWorkshopID,B1.FNUMBER FBOMID,P1.FQTY FQty,MO.FBILLNO FMOBillNO,P1.FSEQ FMOEntrySeq,P1.FENTRYID FMOEntryID");
            sql.AppendLine("	,P1.FBASEUNITQTY FBaseQty,MO.FID FMoId,U1.FNUMBER FBaseUnitID,B2.FNUMBER FMOType,U2.FNUMBER FUnitID,Org.FNUMBER FPrdOrgId");
            sql.AppendLine("	,MO.FOwnerTypeId FParentOwnerTypeId,Org.FNUMBER FParentOwnerId ");
            sql.AppendLine("FROM T_PRD_MO MO");
            sql.AppendLine("	INNER JOIN T_PRD_MOENTRY P1 ON P1.FID=MO.FID");
            sql.AppendLine("	INNER JOIN T_BD_DEPARTMENT D1 ON D1.FDEPTID=P1.FWORKSHOPID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIAL M1 ON M1.FMATERIALID=P1.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_ENG_BOM B1 ON B1.FID=P1.FBOMID");
            sql.AppendLine("	INNER JOIN T_BAS_BILLTYPE B2 ON B2.FBILLTYPEID=MO.FBILLTYPE");
            sql.AppendLine("	INNER JOIN T_BD_MATERIAL M2 ON M2.FMATERIALID=P1.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIALBASE M3 ON M3.FMATERIALID=M2.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U1 ON U1.FUNITID=M3.FBASEUNITID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIALPRODUCE M4 ON M4.FMATERIALID=M1.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U2 ON U2.FUNITID=M4.FPRODUCEUNITID");
            sql.AppendLine("	INNER JOIN T_ORG_Organizations Org ON Org.FORGID=MO.FPrdOrgId");
            sql.AppendLine("WHERE 1=1 AND ({0})");
            string headsql = string.Format(sql.ToString(), string.Join(" OR ", mobuilder.ToArray()));
            string head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "MO.FBILLNO,P1.FSEQ", null).Result;
            if (!string.IsNullOrWhiteSpace(head)) { list = _shareController.DataConvertClass<PPBOMHeadStr>(head, ref reason); }
            return list;
        }

        /// <summary>
        /// 为了减少一些操作，从数据库里读出来就转为需要的类（委外订单）
        /// </summary>
        /// <param name="mobuilder">委外订单分录查询条件</param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private List<SUBPPBOMHeadStr>? GetSUBPPBOMHead(List<string> mobuilder, ref string reason)
        {
            List<SUBPPBOMHeadStr> list = new List<SUBPPBOMHeadStr>();
            StringBuilder sql = new StringBuilder();
            sql.AppendLine("/*dialect*/SELECT DISTINCT M1.FNUMBER FMaterialID,S1.FNUMBER FSupplierId,B1.FNUMBER FBOMID,MY.FQTY FQty,MO.FBILLNO FMOBillNO,MY.FSEQ FMOEntrySeq,MY.FENTRYID FMOEntryID");
            sql.AppendLine("	,MY.FBaseYieldQty FBaseQty,MO.FID FMoId,U1.FNUMBER FBaseUnitID,B2.FNUMBER FMOType,U2.FNUMBER FUnitID,MO.FOwnerTypeId FParentOwnerTypeId,Org.FNUMBER FParentOwnerId,Org.FNUMBER FSubOrgId");
            sql.AppendLine("FROM T_SUB_REQORDER MO");
            sql.AppendLine("	INNER JOIN T_SUB_REQORDERENTRY MY ON MY.FID=MO.FID");
            sql.AppendLine("	INNER JOIN T_SUB_REQORDERENTRY_A MA ON MA.FID=MY.FID AND MA.FENTRYID=MY.FENTRYID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIAL M1 ON M1.FMATERIALID=MY.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_ENG_BOM B1 ON B1.FID=MY.FBOMID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U1 ON U1.FUNITID=MA.FBaseUnitId");
            sql.AppendLine("	INNER JOIN T_BAS_BILLTYPE B2 ON B2.FBILLTYPEID=MO.FBILLTYPE");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U2 ON U2.FUNITID=MY.FUnitID");
            sql.AppendLine("	INNER JOIN T_ORG_Organizations Org ON Org.FORGID=MA.FINSTOCKOWNERID");
            sql.AppendLine("	LEFT JOIN T_BD_SUPPLIER S1 ON S1.FSUPPLIERID=MY.FSUPPLIERID");
            sql.AppendLine("WHERE 1=1 AND ({0})");
            string head = _chiledselect.GetCustomRequestBillAsync(sql.ToString(), "", "", null).Result;
            if (!string.IsNullOrWhiteSpace(head)) { list = _shareController.DataConvertClass<SUBPPBOMHeadStr>(head, ref reason); }
            return list;
        }
        /// <summary>
        /// 为了减少一些操作，从数据库里读出来就转为需要的类
        /// </summary>
        /// <param name="mobuilder"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private List<PPBOMEntryStr>? GetPPBOMEntry(List<string> mobuilder, List<string> billnolist, ref string reason)
        {
            List<PPBOMEntryStr> list = new List<PPBOMEntryStr>();
            StringBuilder sql = new StringBuilder();
            sql.AppendLine("/*dialect*/exec GetPPBOMEntry '{0}','{1}'");
            string headsql = string.Format(sql.ToString(), string.Join(" OR ", billnolist.ToArray()), string.Join(" OR ", mobuilder.ToArray()));
            string head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "", null).Result;
            if (head.Length > 2 && head != "[]") { list = _shareController.DataConvertClass<PPBOMEntryStr>(head, ref reason); }
            if (string.IsNullOrWhiteSpace(head))
            {
                reason = "调用沃比子查询服务（GetPPBOMEntry）出错，请查询接口日志";
                goto E;
            }
            if (head == "[]")
            {
                sql = new StringBuilder();
                sql.Append("/*dialect*/EXEC rb_GetPPBOMHead '{0}'");

                headsql = String.Format(sql.ToString(), string.Join(" OR ", billnolist.ToArray()));
                head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "", null).Result;
                if (head.Length > 2 && head != "[]") { list = _shareController.DataConvertClass<PPBOMEntryStr>(head, ref reason); }
                if (string.IsNullOrWhiteSpace(head))
                {
                    reason = "调用沃比子查询服务（rb_GetPPBOMHead）出错，请查询接口日志";
                    goto E;
                }
                list = _shareController.DataConvertClass<PPBOMEntryStr>(head, ref reason);
            }
            E:
            return list;
        }/// <summary>

         /// <summary>
         /// 为了减少一些操作，从数据库里读出来就转为需要的类
         /// </summary>
         /// <param name="mobuilder"></param>
         /// <param name="reason"></param>
         /// <returns></returns>
        private List<PPBOMEntryStr>? GetSubPPBOMEntry(List<string> mobuilder, List<string> billnolist, ref string reason)
        {
            List<PPBOMEntryStr> list = new List<PPBOMEntryStr>();
            StringBuilder sql = new StringBuilder();
            sql.AppendLine("/*dialect*/exec GetSubPPBOMEntry '{0}','{1}'");
            string headsql = string.Format(sql.ToString(), string.Join(" OR ", billnolist.ToArray()), string.Join(" OR ", mobuilder.ToArray()));
            string head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "", null).Result;
            if (head.Length > 2 && head != "[]") { list = _shareController.DataConvertClass<PPBOMEntryStr>(head, ref reason); }
            if (string.IsNullOrWhiteSpace(head))
            {
                reason = "调用沃比子查询服务（GetSubPPBOMEntry）出错，请查询接口日志";
                goto E;
            }
            if (head == "[]")
            {
                sql = new StringBuilder();
                sql.AppendLine("/*dialect*/DECLARE @SQL VARCHAR(MAX)='");
                sql.AppendLine("SELECT DISTINCT M1.FNUMBER FParentMaterial,'''' FMainMaterial,'''' FMaterial,D1.FNUMBER FWorkshopID,B1.FNUMBER FBOMID,P.FQTY FQty");
                sql.AppendLine("	,P.FSUBBILLNO FMOBillNO,P.FSUBREQENTRYSEQ FMOEntrySeq,P.FSUBREQENTRYID FMOEntryID,P.FBaseQty,P.FMODIFIERID FMoId,U1.FNUMBER FBaseUnitID");
                sql.AppendLine("	,B2.FNUMBER FMOType,U2.FNUMBER FUnitID,Org.FNUMBER FPrdOrgId,1 FNumerator,1 FDenominator ,P.FParentOwnerTypeId FParentOwnerTypeId");
                sql.AppendLine("    ,Org.FNUMBER FParentOwnerId,P.FBILLNO FPPBOMNo,0 FPPBOMEntrySeq,P.FID FPPBOMId,0 FPPBOMEntryId");
                sql.AppendLine("FROM T_SUB_PPBOM P");
                sql.AppendLine("	LEFT JOIN T_BD_SUPPLIER D1 ON D1.FSUPPLIERID=P.FSUPPLIERID");
                sql.AppendLine("	INNER JOIN T_BD_MATERIAL M1 ON M1.FMATERIALID=P.FMATERIALID");
                sql.AppendLine("	INNER JOIN T_ENG_BOM B1 ON B1.FID=P.FBOMID");
                sql.AppendLine("	INNER JOIN T_BAS_BILLTYPE B2 ON B2.FBILLTYPEID=P.FSUBREQTYPE");
                sql.AppendLine("	INNER JOIN T_BD_UNIT U1 ON U1.FUNITID=P.FBASEUNITID");
                sql.AppendLine("	INNER JOIN T_BD_MATERIALPRODUCE M4 ON M4.FMATERIALID=M1.FMATERIALID");
                sql.AppendLine("	INNER JOIN T_BD_UNIT U2 ON U2.FUNITID=P.FUNITID");
                sql.AppendLine("	INNER JOIN T_ORG_Organizations Org ON Org.FORGID=P.FSUBORGID");
                sql.AppendLine("WHERE 1=1 AND ({0})'");
                sql.AppendLine("EXEC(@SQL)");
                headsql = String.Format(sql.ToString(), string.Join(" OR ", billnolist.ToArray()));
                head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "", null).Result;
                if (head.Length > 2 && head != "[]") { list = _shareController.DataConvertClass<PPBOMEntryStr>(head, ref reason); }
                if (string.IsNullOrWhiteSpace(head))
                {
                    reason = "调用沃比子查询服务（GetSubPPBOMEntry）出错，请查询接口日志";
                    goto E;
                }
                list = _shareController.DataConvertClass<PPBOMEntryStr>(head, ref reason);
            }
            E:
            return list;
        }
        /// <summary>
        /// 为了减少一些操作，从数据库里读出来就转为需要的类
        /// </summary>
        /// <param name="mobuilder"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private List<PPBOMEntryStr>? GetNewPPBOMEntry(List<string> mobuilder, ref string reason)
        {

            List<PPBOMEntryStr> list = new List<PPBOMEntryStr>();
            StringBuilder sql = new StringBuilder();
            sql.AppendLine("/*dialect*/SELECT DISTINCT M1.FNUMBER FProductID,D1.FNUMBER FWorkshopID,B1.FNUMBER FBOMID,P.FQTY FQty,P.FMOBillNO,P.FMOEntrySeq,P.FMOEntryID");
            sql.AppendLine("	,P.FBaseQty,P.FMODIFIERID FMoId,U1.FNUMBER FBaseUnitID,B2.FNUMBER FMOType,U2.FNUMBER FUnitID,Org.FNUMBER FPrdOrgId");
            sql.AppendLine("	,P.FParentOwnerTypeId FParentOwnerTypeId,Org.FNUMBER FParentOwnerId,P.FBILLNO FPPBOMNo,P.FID FPPBOMId");
            sql.AppendLine("FROM T_PRD_PPBOM P");
            sql.AppendLine("	INNER JOIN T_BD_DEPARTMENT D1 ON D1.FDEPTID=P.FWORKSHOPID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIAL M1 ON M1.FMATERIALID=P.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_ENG_BOM B1 ON B1.FID=P.FBOMID");
            sql.AppendLine("	INNER JOIN T_BAS_BILLTYPE B2 ON B2.FBILLTYPEID=P.FMOTYPE");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U1 ON U1.FUNITID=P.FBASEUNITID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIALPRODUCE M4 ON M4.FMATERIALID=M1.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U2 ON U2.FUNITID=M4.FPRODUCEUNITID");
            sql.AppendLine("	INNER JOIN T_ORG_Organizations Org ON Org.FORGID=P.FPrdOrgId");
            sql.AppendLine("WHERE 1=1 AND ({0})");
            string headsql = string.Format(sql.ToString(), string.Join(" OR ", mobuilder.ToArray()));

            string head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "P.FBILLNO", null).Result;
            if (!string.IsNullOrWhiteSpace(head))
            {
                list = _shareController.DataConvertClass<PPBOMEntryStr>(head, ref reason);
            }
            else
            {
                reason = "调用沃比子查询服务（GetNewPPBOMEntry）出错，请查询接口日志";
            }
            return list;
        }
        /// <summary>
        /// 为了减少一些操作，从数据库里读出来就转为需要的类
        /// </summary>
        /// <param name="mobuilder"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private List<PPBOMEntryStr>? GetNewSUBPPBOMEntry(List<string> mobuilder, ref string reason)
        {

            List<PPBOMEntryStr> list = new List<PPBOMEntryStr>();
            StringBuilder sql = new StringBuilder();
            sql.AppendLine("/*dialect*/SELECT DISTINCT M1.FNUMBER FProductID,D1.FNUMBER FWorkshopID,B1.FNUMBER FBOMID,P.FQTY FQty,P.FSUBBILLNO FMOBillNO,P.FSUBREQENTRYSEQ FMOEntrySeq,P.FSUBREQENTRYID FMOEntryID");
            sql.AppendLine("	,P.FBaseQty,P.FMODIFIERID FMoId,U1.FNUMBER FBaseUnitID,B2.FNUMBER FMOType,U2.FNUMBER FUnitID,Org.FNUMBER FPrdOrgId");
            sql.AppendLine("	,P.FParentOwnerTypeId FParentOwnerTypeId,Org.FNUMBER FParentOwnerId,P.FBILLNO FPPBOMNo,P.FID FPPBOMId");
            sql.AppendLine("FROM T_SUB_PPBOM P");
            sql.AppendLine("	LEFT JOIN T_BD_SUPPLIER D1 ON D1.fsupplierid=P.fsupplierid");
            sql.AppendLine("	INNER JOIN T_BD_MATERIAL M1 ON M1.FMATERIALID=P.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_ENG_BOM B1 ON B1.FID=P.FBOMID");
            sql.AppendLine("	INNER JOIN T_BAS_BILLTYPE B2 ON B2.FBILLTYPEID=P.FSUBREQTYPE");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U1 ON U1.FUNITID=P.FBASEUNITID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIALPRODUCE M4 ON M4.FMATERIALID=M1.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U2 ON U2.FUNITID=M4.FPRODUCEUNITID");
            sql.AppendLine("	INNER JOIN T_ORG_Organizations Org ON Org.FORGID=P.FSubOrgId");
            sql.AppendLine("WHERE 1=1 AND ({0})");
            string headsql = string.Format(sql.ToString(), string.Join(" OR ", mobuilder.ToArray()));
            string head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "P.FBILLNO", null).Result;
            if (!string.IsNullOrWhiteSpace(head))
            {
                list = _shareController.DataConvertClass<PPBOMEntryStr>(head, ref reason);
            }
            else
            {
                reason = "调用沃比子查询服务（GetNewPPBOMEntry）出错，请查询接口日志";
            }
            return list;
        }

        /// <summary>
        /// 生产订单变更时为了减少一些操作，从数据库里读出来就转为需要的类
        /// </summary>
        /// <param name="mobuilder"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private List<PRDMOChanges>? GetMOEntries(List<string> mobuilder, ref string reason)
        {
            List<PRDMOChanges> list = new List<PRDMOChanges>();
            StringBuilder sql = new StringBuilder();
            sql.AppendLine("/*dialect*/SELECT DISTINCT D.FNUMBER FWorkShopID,M.FNUMBER FMaterialId,MO.FBILLNO FSrcBillNo,MO.FID FSrcBillId,P1.FENTRYID FSrcBillEntryId,P1.FSEQ FSrcBillEntrySeq");
            sql.AppendLine("	,U1.FNUMBER FUnitId,U2.FNUMBER FBaseUnitId,P1.FQty,P1.FBaseUnitQty FBaseQty,FPlanStartDate,FPlanFinishDate,BOM.FNUMBER FBomId");
            sql.AppendLine("    ,U.FConvertNumerator,U.FConvertDenominator,PA.FStatus");
            sql.AppendLine("FROM T_PRD_MO MO");
            sql.AppendLine("	INNER JOIN T_PRD_MOENTRY P1 ON P1.FID=MO.FID");
            sql.AppendLine("	INNER join T_PRD_MOENTRY_A PA ON PA.FID=P1.FID AND PA.FENTRYID=P1.FENTRYID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIAL M ON M.FMATERIALID=P1.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U1 ON U1.FUNITID=P1.FUNITID");
            sql.AppendLine("	INNER JOIN T_BD_UNITCONVERTRATE U ON U.FUNITID=U1.FUNITID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U2 ON U2.FUNITID=P1.FBASEUNITID");
            sql.AppendLine("	INNER JOIN T_BD_DEPARTMENT D ON D.FDEPTID=P1.FWORKSHOPID");
            sql.AppendLine("	INNER JOIN T_ENG_BOM BOM ON BOM.FID=P1.FBomId");
            sql.AppendLine("WHERE PA.FSTATUS BETWEEN 3 AND 5 AND ({0})");
            string headsql = string.Format(sql.ToString(), string.Join(" OR ", mobuilder.ToArray()));
            string head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "MO.FBILLNO,P1.FSEQ", null).Result;
            if (!string.IsNullOrWhiteSpace(head)) { list = _shareController.DataConvertClass<PRDMOChanges>(head, ref reason); }
            return list;
        }
        /// <summary>
        /// 生产订单变更时为了减少一些操作，从数据库里读出来就转为需要的类
        /// 与生产订单变更共用来源类PRDMOChanges
        /// </summary>
        /// <param name="mobuilder"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private List<PRDMOChanges>? GetSUBEntries(List<string> mobuilder, ref string reason)
        {
            List<PRDMOChanges> list = new List<PRDMOChanges>();
            StringBuilder sql = new StringBuilder();
            sql.AppendLine("/*dialect*/SELECT DISTINCT D.FNUMBER FWorkShopID,M.FNUMBER FMaterialId,MO.FBILLNO FSrcBillNo,MO.FID FSrcBillId,P1.FENTRYID FSrcBillEntryId,P1.FSEQ FSrcBillEntrySeq");
            sql.AppendLine("	,U1.FNUMBER FUnitId,U2.FNUMBER FBaseUnitId,P1.FQty,PA.FBaseUnitQty FBaseQty,FPlanStartDate,FPlanFinishDate,BOM.FNUMBER FBomId");
            sql.AppendLine("    ,U.FConvertNumerator,U.FConvertDenominator,P1.FStatus");
            sql.AppendLine("FROM T_SUB_REQORDER MO");
            sql.AppendLine("	INNER JOIN T_SUB_REQORDERENTRY P1 ON P1.FID=MO.FID");
            sql.AppendLine("	INNER join T_SUB_REQORDERENTRY_A PA ON PA.FID=P1.FID AND PA.FENTRYID=P1.FENTRYID");
            sql.AppendLine("	INNER JOIN T_BD_MATERIAL M ON M.FMATERIALID=P1.FMATERIALID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U1 ON U1.FUNITID=P1.FUNITID");
            sql.AppendLine("	INNER JOIN T_BD_UNITCONVERTRATE U ON U.FUNITID=U1.FUNITID");
            sql.AppendLine("	INNER JOIN T_BD_UNIT U2 ON U2.FUNITID=PA.FBaseUnitId");
            sql.AppendLine("	INNER JOIN T_BD_SUPPLIER D ON D.FSupplierId=P1.FSupplierId");
            sql.AppendLine("	INNER JOIN T_ENG_BOM BOM ON BOM.FID=P1.FBomId");
            sql.AppendLine("WHERE P1.FSTATUS BETWEEN 3 AND 5 AND ({0})");
            string headsql = string.Format(sql.ToString(), string.Join(" OR ", mobuilder.ToArray()));
            string head = _chiledselect.GetCustomRequestBillAsync(headsql, "", "MO.FBILLNO,P1.FSEQ", null).Result;
            if (!string.IsNullOrWhiteSpace(head)) { list = _shareController.DataConvertClass<PRDMOChanges>(head, ref reason); }
            return list;
        }
        private async Task<BreakData> SaveBatchMOCHANGEAsync(string data, int fid, int postmid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<WPLAN_CHANGE> mes = _shareController.DataConvertClass<WPLAN_CHANGE>(data, ref reason);
            if (mes.Count == 0)
            {
                breakData.Message = reason;
                return breakData;
            }
            var mos = (from a in mes
                       select new
                       {
                           a.ERP_MO,
                           a.MO_SEQ
                       }).Distinct().ToList();
            List<string> mobuilder = new List<string>();
            foreach (var mo in mos)
            {
                mobuilder.Add(string.Format("(MO.FBILLNO='{0}' AND P1.FSEQ={1})", mo.ERP_MO, mo.MO_SEQ));
            }
            //查询源单信息
            List<PRDMOChanges> mochanges = GetMOEntries(mobuilder, ref reason);
            if (mochanges.Count == 0)
            {
                breakData.Message = string.IsNullOrWhiteSpace(reason) ? "提供的工单或工单行号全部不正确" : reason;
                return breakData;
            }
            List<PRD_MOCHANGE> changes = GetMoChanges(mes, mochanges);
            //批量推送数据模型
            var dsb = await _shareController.GetAPIList<DataSaveBatch>();
            DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                .FirstOrDefault();

            //提交新的单据
            breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, changes);

            return breakData;
        }
        private async Task<BreakData> SaveBatchSUBCHANGEAsync(string data, int fid, int postmid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<WPLAN_CHANGE> mes = _shareController.DataConvertClass<WPLAN_CHANGE>(data, ref reason);
            if (mes.Count == 0)
            {
                breakData.Message = reason;
                return breakData;
            }
            var mos = (from a in mes
                       select new
                       {
                           a.ERP_MO,
                           a.MO_SEQ
                       }).Distinct().ToList();
            List<string> mobuilder = new List<string>();
            foreach (var mo in mos)
            {
                mobuilder.Add(string.Format("(MO.FBILLNO='{0}' AND P1.FSEQ={1})", mo.ERP_MO, mo.MO_SEQ));
            }
            //查询源单信息
            List<PRDMOChanges> mochanges = GetSUBEntries(mobuilder, ref reason);
            if (mochanges == null)
            {
                breakData.Message = string.IsNullOrWhiteSpace(reason) ? "提供的工单或工单行号全部不正确" : reason;
                return breakData;
            }
            List<SUB_REQCHANGE> changes = GetSUBChanges(mes, mochanges);
            //批量推送数据模型
            var dsb = await _shareController.GetAPIList<DataSaveBatch>();
            DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                .FirstOrDefault();

            //提交新的单据
            breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, changes);

            return breakData;
        }

        /// <summary>
        /// 计算生产订单变更内容
        /// </summary>
        /// <param name="mes">MES传入的数据</param>
        /// <param name="mochanges">源单数据</param>
        /// <returns></returns>
        private List<PRD_MOCHANGE> GetMoChanges(List<WPLAN_CHANGE> mes, List<PRDMOChanges> mochanges)
        {
            List<PRD_MOCHANGE> changes = (from a in mochanges
                                          select new PRD_MOCHANGE
                                          {
                                              FWorkShopID = new Dictionary<string, string>() { { "FNumber", a.FWorkShopID } }
                                          }).DistinctBy(s => s.FWorkShopID).ToList();
            foreach (PRD_MOCHANGE mo in changes)
            {
                string workshopid = mo.FWorkShopID.GetValueOrDefault("FNumber");
                List<PRDMOChanges> moentries = mochanges.Where(s => s.FWorkShopID == workshopid).ToList();
                List<PRD_MOCHANGE.Entity> entities1 = (from a in moentries
                                                       select new PRD_MOCHANGE.Entity
                                                       {
                                                           FChangeType = "2",
                                                           FMaterialId = new Dictionary<string, string>() { { "FNUMBER", a.FMaterialId } },
                                                           FWorkShopId1 = mo.FWorkShopID,
                                                           FSrcBillNo = a.FSrcBillNo,
                                                           FSrcBillId = a.FSrcBillId,
                                                           FSrcBillEntryId = a.FSrcBillEntryId,
                                                           FSrcBillEntrySeq = a.FSrcBillEntrySeq,
                                                           FMoNo = a.FSrcBillNo,
                                                           FMoId = a.FSrcBillId,
                                                           FMoEntryId = a.FSrcBillEntryId,
                                                           FMoEntrySeq = a.FSrcBillEntrySeq,
                                                           FStatus = a.FStatus,
                                                           FUnitId = new Dictionary<string, string>() { { "FNUMBER", a.FUnitId } },
                                                           FBaseUnitId = new Dictionary<string, string>() { { "FNUMBER", a.FBaseUnitId } },
                                                           FQty = a.FQty,
                                                           FBaseQty = a.FBaseQty,
                                                           FPlanStartDate = a.FPlanStartDate,
                                                           FPlanFinishDate = a.FPlanFinishDate,
                                                           FBomId = new Dictionary<string, string>() { { "FNUMBER", a.FBomId } },
                                                           FEntity_Link = new List<PRD_MOCHANGE.Entity.Entity_Link>()
                                                           {
                                                               new PRD_MOCHANGE.Entity.Entity_Link()
                                                               {
                                                                   FEntity_Link_FSBillId=a.FSrcBillId,
                                                                   FEntity_Link_FSId=a.FSrcBillEntryId
                                                               }
                                                           }
                                                       }).ToList();
                List<PRD_MOCHANGE.Entity> entities2 = (from a in moentries
                                                       join b in mes on new { A = a.FSrcBillNo, B = a.FSrcBillEntrySeq } equals new { A = b.ERP_MO, B = int.Parse(b.MO_SEQ) }
                                                       select new PRD_MOCHANGE.Entity
                                                       {
                                                           FChangeType = "3",
                                                           FMaterialId = new Dictionary<string, string>() { { "FNUMBER", a.FMaterialId } },
                                                           FWorkShopId1 = mo.FWorkShopID,
                                                           FSrcBillNo = a.FSrcBillNo,
                                                           FSrcBillId = a.FSrcBillId,
                                                           FSrcBillEntryId = a.FSrcBillEntryId,
                                                           FSrcBillEntrySeq = a.FSrcBillEntrySeq,
                                                           FMoNo = a.FSrcBillNo,
                                                           FMoId = a.FSrcBillId,
                                                           FMoEntryId = a.FSrcBillEntryId,
                                                           FMoEntrySeq = a.FSrcBillEntrySeq,
                                                           FStatus = a.FStatus,
                                                           FUnitId = new Dictionary<string, string>() { { "FNUMBER", a.FUnitId } },
                                                           FBaseUnitId = new Dictionary<string, string>() { { "FNUMBER", a.FBaseUnitId } },
                                                           FQty = b.NEW_P_QTY,
                                                           FBaseQty = b.NEW_P_QTY * (a.FConvertNumerator / a.FConvertDenominator),
                                                           FPlanStartDate = a.FPlanStartDate,
                                                           FPlanFinishDate = a.FPlanFinishDate,

                                                           //F_PNDR_Remarks_uky2 = b.RI_NAME,
                                                           //F_PNDR_Remarks_dvn2 = b.OA_NUM,
                                                           //F_PNDR_Remarks_w5c2 = b.TECH,

                                                           FBomId = new Dictionary<string, string>() { { "FNUMBER", a.FBomId } },
                                                           FEntity_Link = new List<PRD_MOCHANGE.Entity.Entity_Link>()
                                                           {
                                                               new PRD_MOCHANGE.Entity.Entity_Link()
                                                               {
                                                                   FEntity_Link_FSBillId=a.FSrcBillId,
                                                                   FEntity_Link_FSId=a.FSrcBillEntryId
                                                               }
                                                           }
                                                       }).ToList();
                mo.FEntity.AddRange(entities1);
                mo.FEntity.AddRange(entities2);
                mo.FEntity.OrderBy(s => new { s.FMaterialId, s.FChangeType });
            }
            return changes;
        }
        /// <summary>
        /// 计算委外订单变更内容
        /// </summary>
        /// <param name="mes">MES传入的数据</param>
        /// <param name="mochanges">源单数据</param>
        /// <returns></returns>
        private List<SUB_REQCHANGE> GetSUBChanges(List<WPLAN_CHANGE> mes, List<PRDMOChanges> mochanges)
        {
            List<SUB_REQCHANGE> changes = (from a in mochanges
                                           select new SUB_REQCHANGE
                                           {
                                               FSupplierId = new Dictionary<string, string>() { { "FNumber", a.FWorkShopID } }
                                           }).DistinctBy(s => s.FSupplierId).ToList();
            foreach (SUB_REQCHANGE mo in changes)
            {
                string workshopid = mo.FSupplierId.GetValueOrDefault("FNumber");
                List<PRDMOChanges> moentries = mochanges.Where(s => s.FWorkShopID == workshopid).ToList();
                List<SUB_REQCHANGE.Entity> entities1 = (from a in moentries
                                                        select new SUB_REQCHANGE.Entity
                                                        {
                                                            FChangeType = "2",
                                                            FMaterialId = new Dictionary<string, string>() { { "FNUMBER", a.FMaterialId } },
                                                            FSrcBillNo = a.FSrcBillNo,
                                                            FSrcBillId = a.FSrcBillId,
                                                            FSrcBillEntryId = a.FSrcBillEntryId,
                                                            FSrcBillEntrySeq = a.FSrcBillEntrySeq,
                                                            FSubReqBillNO = a.FSrcBillNo,
                                                            FSubReqId = a.FSrcBillId,
                                                            FSubReqEntryId = a.FSrcBillEntryId,
                                                            FSubReqEntrySeq = a.FSrcBillEntrySeq,
                                                            FStatus = a.FStatus,
                                                            FUnitId = new Dictionary<string, string>() { { "FNUMBER", a.FUnitId } },
                                                            FBaseUnitId = new Dictionary<string, string>() { { "FNUMBER", a.FBaseUnitId } },
                                                            FQty = a.FQty,
                                                            FBaseQty = a.FBaseQty,
                                                            FPlanStartDate = a.FPlanStartDate,
                                                            FPlanFinishDate = a.FPlanFinishDate,
                                                            FBomId = new Dictionary<string, string>() { { "FNUMBER", a.FBomId } },
                                                            FEntity_Link = new List<SUB_REQCHANGE.Entity.Entity_Link>()
                                                           {
                                                               new SUB_REQCHANGE.Entity.Entity_Link()
                                                               {
                                                                   FEntity_Link_FSBillId=a.FSrcBillId,
                                                                   FEntity_Link_FSId=a.FSrcBillEntryId
                                                               }
                                                           }
                                                        }).ToList();
                List<SUB_REQCHANGE.Entity> entities2 = (from a in moentries
                                                        join b in mes on new { A = a.FSrcBillNo, B = a.FSrcBillEntrySeq } equals new { A = b.ERP_MO, B = int.Parse(b.MO_SEQ) }
                                                        select new SUB_REQCHANGE.Entity
                                                        {
                                                            FChangeType = "3",
                                                            FMaterialId = new Dictionary<string, string>() { { "FNUMBER", a.FMaterialId } },
                                                            FSrcBillNo = a.FSrcBillNo,
                                                            FSrcBillId = a.FSrcBillId,
                                                            FSrcBillEntryId = a.FSrcBillEntryId,
                                                            FSrcBillEntrySeq = a.FSrcBillEntrySeq,
                                                            FSubReqBillNO = a.FSrcBillNo,
                                                            FSubReqId = a.FSrcBillId,
                                                            FSubReqEntryId = a.FSrcBillEntryId,
                                                            FSubReqEntrySeq = a.FSrcBillEntrySeq,
                                                            FStatus = a.FStatus,
                                                            FUnitId = new Dictionary<string, string>() { { "FNUMBER", a.FUnitId } },
                                                            FBaseUnitId = new Dictionary<string, string>() { { "FNUMBER", a.FBaseUnitId } },
                                                            FQty = b.NEW_P_QTY,
                                                            FBaseQty = b.NEW_P_QTY * (a.FConvertNumerator / a.FConvertDenominator),
                                                            FPlanStartDate = a.FPlanStartDate,
                                                            FPlanFinishDate = a.FPlanFinishDate,
                                                            FBomId = new Dictionary<string, string>() { { "FNUMBER", a.FBomId } },
                                                            FEntity_Link = new List<SUB_REQCHANGE.Entity.Entity_Link>()
                                                           {
                                                               new SUB_REQCHANGE.Entity.Entity_Link()
                                                               {
                                                                   FEntity_Link_FSBillId=a.FSrcBillId,
                                                                   FEntity_Link_FSId=a.FSrcBillEntryId
                                                               }
                                                           }
                                                        }).ToList();
                mo.FEntity.AddRange(entities1);
                mo.FEntity.AddRange(entities2);
                mo.FEntity.OrderBy(s => new { s.FMaterialId, s.FChangeType });
            }
            return changes;
        }

        private async Task<BreakData> SaveBatchPPBOMAsync(string data, int fid, int postmid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<UPDATE_WPLAN_MN> mesbom = _shareController.DataConvertClass<UPDATE_WPLAN_MN>(data, ref reason);
            if (mesbom.Count == 0)
            {
                breakData.Message = reason;
                return breakData;
            }
            var mos = (from a in mesbom
                       select new
                       {
                           a.ERP_MO,
                           a.MO_SEQ
                       }).Distinct().ToList();
            List<string> mobuilder = new List<string>();
            foreach (var mo in mos)
            {
                mobuilder.Add(string.Format("(MO.FBILLNO='{0}' AND P1.FSEQ={1})", mo.ERP_MO, mo.MO_SEQ));
            }
            //分析有多少工单，拼接成查询语句
            List<PPBOMHeadStr>? ppbom = GetPPBOMHead(mobuilder, ref reason);
            if (ppbom == null)
            {
                breakData.Message = string.IsNullOrWhiteSpace(reason) ? "提供的工单或工单行号全部不正确" : reason;
                return breakData;
            }

            //没有成功传入的清单，如果存在要返回给推送方
            List<UPDATE_WPLAN_MN> unpost = new List<UPDATE_WPLAN_MN>();
            //表头转换，并与表体结合...
            List<PRD_PPBOM> pPBOMs = GetPPBOMEntities(ppbom, mesbom, ref unpost);
            if (pPBOMs.Count() > 0 && reason == "")
            {
                List<string> billnolist = (from a in pPBOMs.Where(s => s.FBillNo != null)
                                           select a.FBillNo).Distinct().ToList();

                //批量推送数据模型
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                if (billnolist.Count > 0)
                {
                    //先删除
                    breakData = await RemoveBillsAsync(dataSave.GetForm.FDBName, billnolist);
                }
                if (breakData.Status)
                {
                    //提交新的单据
                    breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, pPBOMs);
                }

                //if (breakData.Status)
                //{
                //    List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                //    //提取出创建好的单据编号
                //    List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet!;
                //    //List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mesbom, billlist, ref dataBacks);

                //    if (dataSave.FIsAutoSubmitAndAudit && breakData.Status) //自动提交、审核
                //    {
                //        //提交单据
                //        breakData = await SubmitCloudAsync(billnolist!, dataSave!.GetForm.FDBName, dataSave.FSubmitUrl);
                //        if (breakData.Status)
                //        {
                //            //继续审核单据
                //            breakData = await AuditCloudAsync(billnolist!, dataSave!.GetForm.FDBName, dataSave.FAuditUrl);
                //        }
                //    }
                //    if (breakData.Status)
                //    {
                //        if (unpost.Count != 0)
                //        {
                //            breakData.Status = false;
                //            breakData.Message = "批量处理时未记录详细错误，部分数据未能导入，请检查相关信息是否存在。";
                //            breakData.DataSet = unpost;
                //        }
                //    }
                //}
            }
            else
            {
                breakData.Message = reason;
            }
            return breakData;
        }

        private async Task<BreakData> SaveBatchSUBPPBOMAsync(string data, int fid, int postmid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<UPDATE_WPLAN_MN> mesbom = _shareController.DataConvertClass<UPDATE_WPLAN_MN>(data, ref reason);
            if (mesbom.Count == 0)
            {
                breakData.Message = reason;
                return breakData;
            }
            var mos = (from a in mesbom
                       select new
                       {
                           a.ERP_MO,
                           a.MO_SEQ
                       }).Distinct().ToList();
            List<string> mobuilder = new List<string>();
            foreach (var mo in mos)
            {
                mobuilder.Add(string.Format("(MO.FBILLNO='{0}' AND MY.FSEQ={1})", mo.ERP_MO, mo.MO_SEQ));
            }
            //分析有多少工单，拼接成查询语句
            List<SUBPPBOMHeadStr>? ppbom = GetSUBPPBOMHead(mobuilder, ref reason);
            if (ppbom == null)
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "提供的工单或工单行号全部不正确" : reason;
                return breakData;
            }

            //没有成功传入的清单，如果存在要返回给推送方
            List<UPDATE_WPLAN_MN> unpost = new List<UPDATE_WPLAN_MN>();
            //表头转换，并与表体结合...
            List<SUB_PPBOM> pPBOMs = GetSUBPPBOMEntities(ppbom, mesbom, ref unpost);
            if (pPBOMs.Count() > 0 && reason == "")
            {
                List<string> billnolist = (from a in pPBOMs.Where(s => s.FBillNo != null)
                                           select a.FBillNo).Distinct().ToList();

                //批量推送数据模型
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                if (billnolist.Count > 0)
                {
                    //先删除
                    await RemoveBillsAsync(dataSave.GetForm.FDBName, billnolist);
                }
                //提交新的单据
                breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, pPBOMs);
                //if (breakData.Status)
                //{
                //    List<ReciveDataBack> dataBacks = new List<ReciveDataBack>();
                //    //提取出创建好的单据编号
                //    List<Dictionary<string, string>> billlist = (List<Dictionary<string, string>>)breakData.DataSet!;
                //    //List<string> billnolist = GetNewBillNoList(dataSave.GetForm.FDocType, mesbom, billlist, ref dataBacks);

                //    if (dataSave.FIsAutoSubmitAndAudit && breakData.Status) //自动提交、审核
                //    {
                //        //提交单据
                //        breakData = await SubmitCloudAsync(billnolist!, dataSave!.GetForm.FDBName, dataSave.FSubmitUrl);
                //        if (breakData.Status)
                //        {
                //            //继续审核单据
                //            breakData = await AuditCloudAsync(billnolist!, dataSave!.GetForm.FDBName, dataSave.FAuditUrl);
                //        }
                //    }
                //    if (breakData.Status)
                //    {
                //        if (unpost.Count != 0)
                //        {
                //            breakData.Status = false;
                //            breakData.Message = "批量处理时未记录详细错误，部分数据未能导入，请检查相关信息是否存在。";
                //            breakData.DataSet = unpost;
                //        }
                //    }
                //}
            }
            else
            {
                breakData.Message = reason;
            }
            return breakData;
        }

        private List<PRD_PPBOM> GetPPBOMEntities(List<PPBOMHeadStr> ppbom, List<UPDATE_WPLAN_MN> mesbom, ref List<UPDATE_WPLAN_MN> notexists)
        {
            List<PRD_PPBOM> bills = new List<PRD_PPBOM>();
            try
            {
                //基本单位和单位转换率。
                List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
                List<string> numbers = (from a in mesbom
                                        where !string.IsNullOrEmpty(a.MTRL_ID) && a.MTRL_ID != ""
                                        select a.MTRL_ID).Distinct().ToList();
                List<BD_Material> materials = _chiledselect.materialsAsync(numbers).Result;

                bills = (from b in ppbom //on new { A = a.WPLAN_MO, B = a.WPLAN_MO_LINE } equals new { A = b.FMOBillNO, B = b.FMOEntrySeq.ToString() }
                         select new PRD_PPBOM
                         {
                             FBillNo = mesbom.FindLast(s => s.ERP_MO == b.FMOBillNO && s.MO_SEQ == b.FMOEntrySeq.ToString()).ERP_ID,
                             FMaterialID = new Dictionary<string, string> { { "FNumber", b.FMaterialID } },
                             //FAuxPropIDHead = new PRD_PPBOM.AuxPropIDHead() { FAUXPROPIDHEAD__FF100001 = new Dictionary<string, string> { { "FNumber", "00" } } },
                             FPrdOrgId = new Dictionary<string, object> { { "FNumber", b.FPrdOrgId } },
                             FUnitID = new Dictionary<string, string> { { "FNumber", b.FUnitID } },
                             FMOType = new Dictionary<string, string> { { "FNumber", b.FMOType } },
                             FMOBillNO = b.FMOBillNO,
                             FBaseQty = b.FBaseQty,
                             FMoId = b.FMoId,
                             FBaseUnitID = new Dictionary<string, string> { { "FNumber", b.FBaseUnitID } },
                             FParentOwnerTypeId = b.FParentOwnerTypeId,
                             FParentOwnerId = new Dictionary<string, string> { { "FNumber", b.FParentOwnerId } },
                             FWorkshopID = new Dictionary<string, string> { { "FNumber", b.FWorkshopID } },
                             FBOMID = new Dictionary<string, string> { { "FNumber", b.FBOMID } },
                             FQty = b.FQty,
                             FMOEntrySeq = b.FMOEntrySeq,
                             FMOEntryID = b.FMOEntryID,
                             FEntity = (from a in mesbom
                                        join c in materials on a.MTRL_ID equals c.FNumber
                                        join d in units on c.FStoreUnitID equals d.FUNITID
                                        where a.ERP_MO == b.FMOBillNO && a.MO_SEQ == b.FMOEntrySeq.ToString()
                                        select new PRD_PPBOM.Entity
                                        {
                                            FSeq = (int)a.SEQ,
                                            FMaterialID2 = new Dictionary<string, string> { { "FNumber", c.FNumber } },
                                            FNumerator = a.NN_QTY == 0 ? 0 : GetNumerator(a.NN_QTY, b.FQty, true),
                                            FDenominator = a.NN_QTY == 0 ? 1 : GetNumerator(a.NN_QTY, b.FQty, false),
                                            FUnitID2 = new Dictionary<string, string> { { "FNumber", d.FNumber } },
                                            FNeedDate2 = DateTime.Now.ToShortDateString(),
                                            FSupplyOrg = new Dictionary<string, string> { { "FNumber", b.FPrdOrgId } },
                                            //FBomId2 = new Dictionary<string, string> { { "FNumber", b.FBOMID } },
                                            FBaseNumerator = GetNumerator(a.NN_QTY, b.FQty, true),
                                            FBASEDENOMINATOR = GetNumerator(a.NN_QTY, b.FQty, false),
                                            FNoPickedQty = a.TN_QTY,
                                            FBaseNoPickedQty = a.TN_QTY,
                                            FBaseUnitID1 = new Dictionary<string, string> { { "FNumber", d.FNumber } },
                                            FMoType1 = new Dictionary<string, string> { { "FNumber", b.FMOType } },
                                            FOwnerID = new Dictionary<string, string> { { "FNumber", b.FPrdOrgId } },
                                            //FALLOCATEQTY = a.TN_QTY,
                                            FMustQty = a.TN_QTY,
                                            FNeedQty2 = a.TN_QTY,
                                            FPositionNO = a.POINT_STR,
                                            FMaterialType = a.IS_BACK == "Y" ? "2" : a.IS_MAIN == "N" ? "3" : "1",
                                            FIsKeyItem = a.IS_MAIN == "Y" ? IsKeyItem(mesbom, a.MAIN_ID, 1, a.MTRL_ID) : 1,
                                            FReplacePriority = a.IS_MAIN == "Y" ? 0 : 1,
                                            FReplaceType = a.IS_MAIN == "Y" ? IsKeyItem(mesbom, a.MAIN_ID).ToString() : "1",
                                            FReplacePolicy = a.IS_MAIN == "Y" ? IsKeyItem(mesbom, a.MAIN_ID).ToString() : "3",
                                            FUseRate = a.IS_MAIN == "N" ? 0 : 100,
                                            FRowExpandType = a.IS_MAIN == "N" ? 0 : 16
                                        }
                                      ).OrderBy(s => s.FSeq).ToList()
                         }).Distinct().ToList();
            }
            catch (Exception ex)
            {
                string msg = ex.Message;
                throw;
            }

            //notexists = (from a in mesbom
            //             join b in bills on new { A = a.ERP_MO, B = a.MO_SEQ } equals new { A = b.FMOBillNO, B = b.FMOEntrySeq.ToString() } into c
            //             from d in c.DefaultIfEmpty()
            //             where d == null
            //             select a
            //            ).ToList();
            //再次更新替代的上级行号
            foreach (PRD_PPBOM prd in bills)
            {
                int n = 1;
                foreach (PRD_PPBOM.Entity entity in prd.FEntity)
                {
                    if (entity.FMaterialType == "3" && entity.FIsKeyItem == 1)
                    {
                        entity.FReplaceGroup = GetReplaceGroup(prd.FEntity, entity.FMaterialID2.GetValueOrDefault("FNumber"), mesbom);
                    }
                    else
                    {
                        entity.FReplaceGroup = n;
                        n++;
                    }
                }
            }

            return bills;
        }
        /// <summary>
        /// 计算新增物料或替代子项的ReplaceGroup
        /// 如果是替代子项，则同时更新上级行ID
        /// </summary>
        /// <param name="entity">新的子项对象</param>
        /// <param name="martnumber">物料编码</param>
        /// <param name="mesbom">MES提供的变更实体</param>
        /// <param name="reason">当有错误时的返回消息</param>
        /// <param name="parrowid">上级行ID</param>
        /// <returns></returns>
        private int GetChangeReplaceGroup(PRD_PPBOMChange.Entity entity, string martnumber,
            WPLAN_MN_CHANGE mesbom, ref string reason, ref string parrowid)
        {
            reason = string.Empty;
            parrowid = string.Empty;
            int newgroup = 0;
            //var item = mesbom.Find(s => s.MTRL_ID == martnumber); 
            //从数据库查询
            StringBuilder stringBuilder = new StringBuilder();
            string main = mesbom.MAIN_ID;
            string jquest = string.Empty;
            string costomsql = ApiSettingsHelper.GetConfig("CustomRequestRemoteSql");
            ////var paritem = entity.Find(s => s.FChangeType == "2" && s.FMaterialID2.GetValueOrDefault("FNumber") == main);
            //if (entity.FChangeType == "2")//说明主料一起被放在JSON中了
            //{
            //    newgroup = entity.FReplaceGroup;
            //    //replacetype = paritem.FReplaceType;
            //    //从数据库查询
            //    stringBuilder.AppendLine("/*dialect*/SELECT DISTINCT PC.FROWID FROM T_PRD_PPBOMENTRY P1 INNER JOIN T_BD_MATERIAL M ON M.FMATERIALID=P1.FMATERIALID AND M.FUSEORGID={3}");
            //    stringBuilder.AppendLine("	INNER JOIN T_PRD_PPBOMENTRY_C PC ON PC.FID=P1.FID AND PC.FENTRYID=P1.FENTRYID");
            //    stringBuilder.AppendLine("WHERE M.FNUMBER='{0}' AND P1.FID={1} AND FMATERIALTYPE=1 and FReplaceGroup={2} AND F_POSH_MESMERGE=0");
            //    string sql = string.Format(stringBuilder.ToString(), main, entity.FPPBOMId, newgroup,LocalStaticRequest.DefaultOrgID);
            //    if (costomsql.ToUpper() == "Y")
            //    {
            //        jquest = _chiledselect.GetCustomReaderAsync(sql, "ExecuteScalar").Result;
            //        if (!string.IsNullOrWhiteSpace(jquest))
            //        {
            //            if (string.IsNullOrWhiteSpace(reason))
            //            {
            //                parrowid = jquest;
            //            }
            //        }
            //    }
            //    else
            //    {
            //        parrowid = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true).ToString();
            //    }
            //}
            //else 
            if (entity.FPPBOMEntrySeq != 0)   //仅仅是JSON行标识，跟ERP无关！！！
            {
                stringBuilder.AppendLine("/*dialect*/SELECT DISTINCT FReplaceGroup,PC.FROWID FROM T_PRD_PPBOMENTRY P1 INNER JOIN T_BD_MATERIAL M ON M.FMATERIALID=P1.FMATERIALID");
                stringBuilder.AppendLine("	INNER JOIN T_PRD_PPBOMENTRY_C PC ON PC.FID=P1.FID AND PC.FENTRYID=P1.FENTRYID");
                stringBuilder.AppendLine("WHERE M.FNUMBER='{0}' AND P1.FID={1} AND FMATERIALTYPE=1 AND F_POSH_MESMERGE=0 and P1.FSeq={2} AND M.FUSEORGID={3}");
                //stringBuilder.AppendLine("and ((p1.fmustqty>0 and not exists(select 1 from T_PRD_PPBOMENTRY_C where fid=p1.fid and FPARENTROWID=pc.frowid))");  //第1次变更主料的用量必须大于0，因为可能存在合并物料
                //stringBuilder.AppendLine("or (exists(select 1 from T_PRD_PPBOMENTRY_C where fid=p1.fid and FPARENTROWID=pc.frowid)))");  //第n+1次变更的时候，可能主料不变，只增加替代料
                string sql = string.Format(stringBuilder.ToString(), main, entity.FPPBOMId, entity.FPPBOMEntrySeq, LocalStaticRequest.DefaultOrgID);
                if (costomsql.ToUpper() == "Y")
                {
                    //string jquest = _chiledselect.GetCustomReaderAsync(sql, "ExecuteScalar").Result;
                    jquest = _chiledselect.GetCustomRequestTableAsync(sql, "", "", null).Result;
                    if (!string.IsNullOrWhiteSpace(jquest))
                    {
                        if (string.IsNullOrWhiteSpace(reason))
                        {
                            DataTable table = _shareController.JsonConvertDatatable("FReplaceGroup,FROWID", jquest, ref reason);
                            if (table != null)
                            {
                                if (table.Rows.Count > 0)
                                {
                                    int.TryParse(table.Rows[0][0].ToString(), out newgroup);
                                    parrowid = table.Rows[0][1].ToString();
                                }
                            }
                        }
                    }
                }
                else    //本地SQL查询
                {
                    DataSet dataSet = _kdsqlhelper.GetDataSet(CommandType.Text, sql, null, ref reason, true);
                    if (dataSet != null)
                    {
                        DataTable table = dataSet.Tables[0];
                        if (table.Rows.Count > 0)
                        {
                            int.TryParse(table.Rows[0][0].ToString(), out newgroup);
                            parrowid = table.Rows[0][1].ToString();
                        }
                    }
                }
            }
            else if (costomsql.ToUpper() == "Y")
            {
                stringBuilder.Clear();
                stringBuilder.AppendLine("/*dialect*/SELECT MAX(FREPLACEGROUP)+1 FREPLACEGROUP FROM T_PRD_PPBOMENTRY");
                stringBuilder.AppendLine("WHERE FID={0}");
                string sql = string.Format(stringBuilder.ToString(), entity.FPPBOMId);

                jquest = _chiledselect.GetCustomReaderAsync(sql, "ExecuteScalar").Result;
                if (!string.IsNullOrWhiteSpace(jquest))
                {
                    if (string.IsNullOrWhiteSpace(reason)) { int.TryParse(jquest.ToString(), out newgroup); }
                }
                //obj = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true);
            }
            else
            {
                stringBuilder.Clear();
                stringBuilder.AppendLine("/*dialect*/SELECT MAX(FREPLACEGROUP)+1 FREPLACEGROUP FROM T_PRD_PPBOMENTRY");
                stringBuilder.AppendLine("WHERE FID={0}");
                string sql = string.Format(stringBuilder.ToString(), entity.FPPBOMId);
                jquest = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true).ToString();
                int.TryParse(jquest, out newgroup);
            }
            return newgroup;
        }
        private int GetSUBChangeReplaceGroup(List<SUB_PPBOMChange.Entity> entity, string martnumber,
            List<WPLAN_MN_CHANGE> mesbom, ref string reason, ref string parrowid)
        {
            reason = string.Empty;
            parrowid = string.Empty;
            int newgroup = 0;
            var item = mesbom.Find(s => s.MTRL_ID == martnumber); ;
            string main = item.MAIN_ID;
            string jquest = string.Empty;
            string costomsql = ApiSettingsHelper.GetConfig("CustomRequestRemoteSql");
            var paritem = entity.Find(s => s.FChangeType == "2" && s.FMaterialID2.GetValueOrDefault("FNumber") == main);
            if (paritem != null)//说明主料一起被放在JSON中了
            {
                newgroup = paritem.FReplaceGroup;
                //replacetype = paritem.FReplaceType;
                //从数据库查询
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.AppendLine("/*dialect*/SELECT DISTINCT PC.FROWID FROM T_SUB_PPBOMENTRY P1 INNER JOIN T_BD_MATERIAL M ON M.FMATERIALID=P1.FMATERIALID");
                stringBuilder.AppendLine("	INNER JOIN T_SUB_PPBOMENTRY_C PC ON PC.FID=P1.FID AND PC.FENTRYID=P1.FENTRYID");
                stringBuilder.AppendLine("WHERE M.FNUMBER='{0}' AND P1.FID={1} AND FMATERIALTYPE=1 and FReplaceGroup={2} AND F_POSH_MESMERGE=0");
                string sql = string.Format(stringBuilder.ToString(), main, entity[0].FSUBPPBOMId, newgroup);
                if (costomsql.ToUpper() == "Y")
                {
                    jquest = _chiledselect.GetCustomReaderAsync(sql, "ExecuteScalar").Result;
                    if (!string.IsNullOrWhiteSpace(jquest))
                    {
                        if (string.IsNullOrWhiteSpace(reason))
                        {
                            parrowid = jquest;
                        }
                    }
                }
                else
                {
                    parrowid = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true).ToString();
                }
            }
            else
            {
                //从数据库查询
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.AppendLine("/*dialect*/SELECT DISTINCT FReplaceGroup,PC.FROWID FROM T_SUB_PPBOMENTRY P1 INNER JOIN T_BD_MATERIAL M ON M.FMATERIALID=P1.FMATERIALID");
                stringBuilder.AppendLine("	INNER JOIN T_SUB_PPBOMENTRY_C PC ON PC.FID=P1.FID AND PC.FENTRYID=P1.FENTRYID");
                stringBuilder.AppendLine("WHERE M.FNUMBER='{0}' AND P1.FID={1} AND FMATERIALTYPE=1 AND F_POSH_MESMERGE=0 ");
                stringBuilder.AppendLine("and ((p1.fmustqty>0 and not exists(select 1 from T_SUB_PPBOMENTRY_C where fid=p1.fid and FPARENTROWID=pc.frowid))");  //第1次变更主料的用量必须大于0，因为可能存在合并物料
                stringBuilder.AppendLine("\r or (exists(select 1 from T_SUB_PPBOMENTRY_C where fid=p1.fid and FPARENTROWID=pc.frowid)))");  //第n+1次变更的时候，可能主料不变，只增加替代料
                string sql = string.Format(stringBuilder.ToString(), main, entity[0].FSUBPPBOMId);
                if (costomsql.ToUpper() == "Y")
                {
                    //string jquest = _chiledselect.GetCustomReaderAsync(sql, "ExecuteScalar").Result;
                    jquest = _chiledselect.GetCustomRequestTableAsync(sql, "", "", null).Result;
                    if (!string.IsNullOrWhiteSpace(jquest))
                    {
                        if (string.IsNullOrWhiteSpace(reason))
                        {
                            DataTable table = _shareController.JsonConvertDatatable("FReplaceGroup,FROWID", jquest, ref reason);
                            if (table != null)
                            {
                                if (table.Rows.Count > 0)
                                {
                                    int.TryParse(table.Rows[0][0].ToString(), out newgroup);
                                    parrowid = table.Rows[0][1].ToString();
                                }
                            }
                        }
                    }
                    else
                    {
                        stringBuilder.Clear();
                        stringBuilder.AppendLine("/*dialect*/SELECT MAX(FREPLACEGROUP) FREPLACEGROUP FROM T_SUB_PPBOMENTRY");
                        stringBuilder.AppendLine("WHERE FID={0}");
                        sql = string.Format(stringBuilder.ToString(), entity[0].FSUBPPBOMId);
                        //obj = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true);
                        jquest = _chiledselect.GetCustomReaderAsync(sql, "ExecuteScalar").Result;
                        if (!string.IsNullOrWhiteSpace(jquest))
                        {
                            if (string.IsNullOrWhiteSpace(reason)) { int.TryParse(jquest.ToString(), out newgroup); }
                        }
                    }
                }
                else
                {
                    DataSet dataSet = _kdsqlhelper.GetDataSet(CommandType.Text, sql, null, ref reason, true);
                    if (dataSet != null)
                    {
                        DataTable table = dataSet.Tables[0];
                        if (table != null)
                        {
                            if (table.Rows.Count > 0)
                            {
                                int.TryParse(table.Rows[0][0].ToString(), out newgroup);
                                parrowid = table.Rows[0][1].ToString();
                            }
                            else
                            {
                                stringBuilder.Clear();
                                stringBuilder.AppendLine("/*dialect*/SELECT MAX(FREPLACEGROUP) FREPLACEGROUP FROM T_SUB_PPBOMENTRY");
                                stringBuilder.AppendLine("WHERE FID={0}");
                                sql = string.Format(stringBuilder.ToString(), entity[0].FSUBPPBOMId);
                                jquest = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true).ToString();
                                if (string.IsNullOrWhiteSpace(reason)) { int.TryParse(jquest.ToString(), out newgroup); }
                            }
                        }
                    }
                }
            }
            return newgroup;
        }
        /// <summary>
        /// 取替代组序号，错误的方法。若要使用请参考GetChangeReplaceGroup重写
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="martnumber"></param>
        /// <param name="mesbom"></param>
        /// <returns></returns>
        private int GetSUBReplaceGroup(List<SUB_PPBOM.Entity> entity, string martnumber, List<UPDATE_WPLAN_MN> mesbom)
        {
            int newgroup = 0;
            var item = mesbom.Find(s => s.MTRL_ID == martnumber); ;
            string main = item.MAIN_ID;
            newgroup = entity.Find(s => s.FMaterialID2.GetValueOrDefault("FNumber") == main).FReplaceGroup;
            return newgroup;
        }
        /// <summary>
        /// 取替代组序号，错误的方法。若要使用请参考GetChangeReplaceGroup重写
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="martnumber"></param>
        /// <param name="mesbom"></param>
        /// <returns></returns>
        private int GetReplaceGroup(List<PRD_PPBOM.Entity> entity, string martnumber, List<UPDATE_WPLAN_MN> mesbom)
        {
            int newgroup = 0;
            var item = mesbom.Find(s => s.MTRL_ID == martnumber); ;
            string main = item.MAIN_ID;
            newgroup = entity.Find(s => s.FMaterialID2.GetValueOrDefault("FNumber") == main).FReplaceGroup;
            return newgroup;
        }

        private decimal GetSubUseRate(string martnumber, List<WPLAN_MN_CHANGE> mesbom)
        {
            decimal rate = 1;
            var item = (from a in mesbom
                        where a.MTRL_ID == martnumber
                        select new
                        {
                            MAIN_ID = a.MAIN_ID,
                            TN_QTY = a.TN_QTY
                        }).ToList();
            if (item.Count > 0)
            {
                string mainnumber = item.FirstOrDefault().MAIN_ID;
                decimal qty = item.FirstOrDefault().TN_QTY;
                var mainseq = from a in mesbom.Where(s => s.MAIN_ID == mainnumber)
                              group a by new { K = a.MAIN_ID }
                             into b
                              select new
                              {
                                  Value = b.Sum(s => s.TN_QTY)
                              };
                if (mainseq.FirstOrDefault().Value > 0)
                {
                    rate = Math.Round(qty / mainseq.FirstOrDefault().Value, 2);
                }
                else { rate = 0; }
            }

            return rate;
        }

        private int ReplacePriority(List<WPLAN_MN_CHANGE> mesbom, string mainitem, string mart)
        {
            int pri = 0;
            var items = mesbom.Where(s => s.MAIN_ID == mainitem).ToList();
            if (items.Count > 1)
            {
                foreach (var item in items)
                {
                    pri += 1;
                    if (item.MAIN_ID == mart && item.MAIN_ID == mainitem)
                    {
                        break;
                    }
                }
            }
            return pri;
        }
        private int KeyItem(List<WPLAN_MN_CHANGE> mesbom, string mainitem, int IS_REPLACE = 0, string marId = "")
        {
            if (IS_REPLACE == 0)
            {
                var items = mesbom.Where(s => s.MAIN_ID == mainitem).ToList();
                if (items.Count >= 2)
                    return 1;
                else
                    return 0;
            }
            else
            {
                //var itemList = mesbom.Where(s => s.MAIN_ID == mainitem).ToList();
                //var itemList2 = itemList.OrderByDescending(x => new { x.WPLAN_MO, x.WPLAN_MO_LINE, x.ERP_ID, x.SEQ }).ToList();
                //var items = itemList2.FirstOrDefault();

                var items = (from t0 in mesbom.Where(s => s.MAIN_ID == mainitem)
                             orderby t0.WPLAN_MO, t0.WPLAN_MO_LINE, t0.ERP_ID, t0.SEQ
                             select t0).FirstOrDefault();
                if (items != null && items.MTRL_ID == marId)
                    return 1;

                return 0;
            }
        }
        private int GetReplacePriority(List<UPDATE_WPLAN_MN> mesbom, string mainitem, string mart)
        {
            int pri = 0;
            var items = mesbom.Where(s => s.MAIN_ID == mainitem).ToList();
            if (items.Count > 1)
            {
                foreach (var item in items)
                {
                    pri += 1;
                    if (item.MAIN_ID == mart && item.MAIN_ID == mainitem)
                    {
                        break;
                    }
                }
            }
            return pri;
        }
        private int IsKeyItem(List<UPDATE_WPLAN_MN> mesbom, string mainitem, int IS_REPLACE = 0, string marId = "")
        {
            if (IS_REPLACE == 0)
            {
                var items = mesbom.Where(s => s.MAIN_ID == mainitem).ToList();
                if (items.Count >= 2)
                    return 1;
                else
                    return 0;
            }
            else
            {
                var items = mesbom.Where(s => s.MAIN_ID == mainitem)
                    .OrderByDescending(x => new { x.ERP_MO, x.MO_SEQ, x.ERP_ID, x.SEQ })
                    .FirstOrDefault();
                if (items != null && items.MTRL_ID == marId)
                    return 1;

                return 0;
            }
        }
        private List<SUB_PPBOM> GetSUBPPBOMEntities(List<SUBPPBOMHeadStr> ppbom, List<UPDATE_WPLAN_MN> mesbom, ref List<UPDATE_WPLAN_MN> notexists)
        {
            List<SUB_PPBOM> bills = new List<SUB_PPBOM>();
            try
            {
                //基本单位和单位转换率。
                List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
                List<string> numbers = (from a in mesbom
                                        where !string.IsNullOrEmpty(a.MTRL_ID) && a.MTRL_ID != ""
                                        select a.MTRL_ID).Distinct().ToList();
                List<BD_Material> materials = _chiledselect.materialsAsync(numbers).Result;

                bills = (from b in ppbom //on new { A = a.WPLAN_MO, B = a.WPLAN_MO_LINE } equals new { A = b.FMOBillNO, B = b.FMOEntrySeq.ToString() }
                         select new SUB_PPBOM
                         {
                             FBillNo = mesbom.FindLast(s => s.ERP_MO == b.FMOBillNO && s.MO_SEQ == b.FMOEntrySeq.ToString()).ERP_ID,
                             FMaterialID = new Dictionary<string, string> { { "FNumber", b.FMaterialID } },
                             //FAuxPropIDHead = new PRD_PPBOM.AuxPropIDHead() { FAUXPROPIDHEAD__FF100001 = new Dictionary<string, string> { { "FNumber", "00" } } },
                             FSubOrgId = new Dictionary<string, object> { { "FNumber", b.FSubOrgId } },
                             FUnitID = new Dictionary<string, string> { { "FNumber", b.FUnitID } },
                             FSubReqType = new Dictionary<string, string> { { "FNumber", b.FMOType } },
                             FSubReqBillNO = b.FMOBillNO,
                             FBaseQty = b.FBaseQty,
                             FSubReqId = b.FMoId,
                             FBaseUnitID = new Dictionary<string, string> { { "FNumber", b.FBaseUnitID } },
                             FParentOwnerTypeId = b.FParentOwnerTypeId,
                             FParentOwnerId = new Dictionary<string, string> { { "FNumber", b.FParentOwnerId } },
                             FSupplierId = new Dictionary<string, object> { { "FNumber", b.FSupplierId } },
                             FBOMID = new Dictionary<string, string> { { "FNumber", b.FBOMID } },
                             FQty = b.FQty,
                             FSubReqEntrySeq = b.FMOEntrySeq,
                             FSubReqEntryId = b.FMOEntryID,
                             FEntity = (from a in mesbom
                                        join c in materials on a.MTRL_ID equals c.FNumber
                                        join d in units on c.FStoreUnitID equals d.FUNITID
                                        where a.ERP_MO == b.FMOBillNO && a.MO_SEQ == b.FMOEntrySeq.ToString()
                                        select new SUB_PPBOM.Entity
                                        {
                                            FSeq = (int)a.SEQ,
                                            FMaterialID2 = new Dictionary<string, string> { { "FNumber", c.FNumber } },
                                            FNumerator = a.NN_QTY == 0 ? 0 : GetNumerator(a.NN_QTY, b.FQty, true),
                                            FDenominator = a.NN_QTY == 0 ? 1 : GetNumerator(a.NN_QTY, b.FQty, false),
                                            FUnitID2 = new Dictionary<string, string> { { "FNumber", d.FNumber } },
                                            FNeedDate2 = DateTime.Now.ToShortDateString(),
                                            FSupplyOrg = new Dictionary<string, string> { { "FNumber", b.FSubOrgId } },
                                            //FBomId2 = new Dictionary<string, string> { { "FNumber", b.FBOMID } },
                                            FBaseNumerator = GetNumerator(a.NN_QTY, b.FQty, true),
                                            FBASEDENOMINATOR = GetNumerator(a.NN_QTY, b.FQty, false),
                                            FNoPickedQty = a.TN_QTY,
                                            FBaseNoPickedQty = a.TN_QTY,
                                            FBaseUnitID1 = new Dictionary<string, string> { { "FNumber", d.FNumber } },
                                            FSubReqType1 = new Dictionary<string, string> { { "FNumber", b.FMOType } },
                                            FOwnerID = new Dictionary<string, string> { { "FNumber", b.FSubOrgId } },
                                            //FALLOCATEQTY = a.TN_QTY,
                                            FMustQty = a.TN_QTY,
                                            FNeedQty2 = a.TN_QTY,
                                            FPositionNO = a.POINT_STR,
                                            FMaterialType = a.IS_BACK == "Y" ? "2" : a.IS_MAIN == "N" ? "3" : "1",
                                            FIsKeyItem = a.IS_MAIN == "Y" ? IsKeyItem(mesbom, a.MAIN_ID, 1, a.MTRL_ID) : 1,
                                            FReplacePriority = a.IS_MAIN == "Y" ? 0 : 1,
                                            FReplaceType = a.IS_MAIN == "Y" ? IsKeyItem(mesbom, a.MAIN_ID).ToString() : "1",
                                            FReplacePolicy = a.IS_MAIN == "Y" ? IsKeyItem(mesbom, a.MAIN_ID).ToString() : "3",
                                            FUseRate = a.IS_MAIN == "N" ? 0 : 100,
                                            FRowExpandType = a.IS_MAIN == "N" ? 0 : 16
                                        }
                                      ).OrderBy(s => s.FSeq).ToList()
                         }
                            ).Distinct().ToList();
            }
            catch (Exception ex)
            {
                string msg = ex.Message;
                throw;
            }

            //notexists = (from a in mesbom
            //             join b in bills on new { A = a.ERP_MO, B = a.MO_SEQ } equals new { A = b.FSubReqBillNO, B = b.FSubReqEntrySeq.ToString() } into c
            //             from d in c.DefaultIfEmpty()
            //             where d == null
            //             select a
            //            ).ToList();

            //再次更新替代的上级行号
            foreach (SUB_PPBOM prd in bills)
            {
                int n = 1;
                foreach (SUB_PPBOM.Entity entity in prd.FEntity)
                {
                    if (entity.FMaterialType == "3" && entity.FIsKeyItem == 1)
                    {
                        entity.FReplaceGroup = GetSUBReplaceGroup(prd.FEntity, entity.FMaterialID2.GetValueOrDefault("FNumber"), mesbom);
                    }
                    else
                    {
                        entity.FReplaceGroup = n;
                        n++;
                    }
                }
            }
            return bills;
        }
        private List<PRD_PPBOMChange.Entity> GetEntitiesForOld(List<PPBOMEntryStr> ppbom, List<WPLAN_MN_CHANGE> mesbom
            , List<BD_Material> materials, List<BD_UNIT> units, PRD_PPBOMChange change)
        {
            return (from a in ppbom.Where(s => s.FMoId == change.FMoId && s.FMOEntryID == change.FMOEntryID).DistinctBy(s => new { s.FPPBOMNo, s.FPPBOMEntrySeq })
                        //join b in mesbom on new { A = a.FPPBOMNo, B = a.FMainMaterial, C = a.FMaterial } equals new { A = b.ERP_ID, B = b.MAIN_ID, C = b.MTRL_ID }//2023-11-23被注释
                    join b in mesbom on new { A = a.FPPBOMNo, B = a.FMainMaterial, C = a.FMaterial, D = a.FMaterialType } equals
                    new { A = b.ERP_ID, B = b.MAIN_ID, C = b.MTRL_ID, D = b.IS_BACK == "Y" ? 2 : b.IS_MAIN == "N" ? 3 : 1 }
                    join c in materials on b.MTRL_ID equals c.FNumber
                    join d in units on c.FStoreUnitID equals d.FUNITID
                    where a.FPPBOMEntryId != 0
                    select new PRD_PPBOMChange.Entity
                    {
                        FParentRowId = a.FParentRowId,
                        //FSeq=a.FPPBOMEntrySeq,
                        FMaterialID2 = new Dictionary<string, string> { { "FNumber", c.FNumber } },
                        FChangeType = "2",
                        FUnitID2 = new Dictionary<string, string> { { "FNumber", d.FNumber } },
                        FMustQty = a.ChildQty,
                        FNeedQty2 = a.ChildQty,
                        //FSTDQTY=a.ChildQty,
                        FProduceQty = a.FQty,
                        FMoBillNo1 = a.FMOBillNO,
                        FPPBOMNo = a.FPPBOMNo,
                        FPPBOMEntryId = a.FPPBOMEntryId,
                        FPPBOMEntrySeq = a.FPPBOMEntrySeq,
                        FMoEntrySeq1 = a.FMOEntrySeq,
                        FPPBOMId = a.FPPBOMId,
                        FOwnerID = change.FPrdOrgId,
                        FSupplyOrg = change.FPrdOrgId,
                        //FUseRate=b.IS_MAIN=="N" ? 0 : 100,
                        //FWorkshopID1 = new Dictionary<string, string> { { "FNumber", a.FWorkshopID } },
                        FNeedDate2 = b.N_DATE,
                        FNumerator = a.FNumerator,
                        FDenominator = a.FDenominator,
                        FReplaceGroup = a.FReplaceGroup,
                        FReplacePriority = a.FReplacePriority,
                        FEntity_Link = new List<PRD_PPBOMChange.Entity.Entity_Link>()
                                      {
                                          new PRD_PPBOMChange.Entity.Entity_Link()
                                          {FEntity_Link_FSBillId = a.FPPBOMId,
                                          FEntity_Link_FSId=a.FPPBOMEntryId
                                          }
                                      }
                    }
                        //).DistinctBy(s => new { s.FMaterialID2, s.FChangeType, s.FUnitID2, s.FProduceQty, s.FMoBillNo1, s.FPPBOMNo, s.FPPBOMEntryId, s.FMoEntrySeq1, s.FPPBOMId, s.FWorkshopID1, s.FMaterialType, s.FNeedDate2, s.FOwnerID, s.FSupplyOrg, s.FNumerator, s.FDenominator }).ToList();
                        ).Distinct().ToList();
        }

        private List<SUB_PPBOMChange.Entity> GetSUBEntitiesForOld(List<PPBOMEntryStr> ppbom, List<WPLAN_MN_CHANGE> mesbom
            , List<BD_Material> materials, List<BD_UNIT> units, SUB_PPBOMChange change)
        {
            return (from a in ppbom.Where(s => s.FMoId == change.FSubReqId && s.FMOEntryID == change.FSubReqEntryId).DistinctBy(s => new { s.FPPBOMNo, s.FPPBOMEntrySeq })
                        //join b in mesbom on new { A = a.FPPBOMNo, B = a.FMainMaterial,C=a.FMaterial } equals new { A = b.ERP_ID, B = b.MAIN_ID,C=b.MTRL_ID }//2023-11-23被注释
                    join b in mesbom on new { A = a.FPPBOMNo, B = a.FMaterial, C = a.FMainMaterial, D = a.FMaterialType } equals
                    new { A = b.ERP_ID, B = b.MTRL_ID, C = b.MAIN_ID, D = b.IS_BACK == "Y" ? 2 : b.IS_MAIN == "N" ? 3 : 1 }
                    join c in materials on b.MTRL_ID equals c.FNumber
                    join d in units on c.FStoreUnitID equals d.FUNITID
                    where a.FPPBOMEntryId != 0
                    select new SUB_PPBOMChange.Entity
                    {
                        //FSeq=a.FPPBOMEntrySeq,
                        FMaterialID2 = new Dictionary<string, string> { { "FNumber", c.FNumber } },
                        FChangeType = "2",
                        FUnitID2 = new Dictionary<string, string> { { "FNumber", d.FNumber } },
                        FMustQty = a.ChildQty,
                        FNeedQty2 = a.ChildQty,
                        FProduceQty = a.FQty,
                        FSubReqBillNO1 = a.FMOBillNO,
                        FSUBPPBOMNo = a.FPPBOMNo,
                        FSUBPPBOMEntryId = a.FPPBOMEntryId,
                        FSUBPPBOMEntrySeq = a.FPPBOMEntrySeq,
                        FSubReqEntrySeq1 = a.FMOEntrySeq,
                        FSUBPPBOMId = a.FPPBOMId,
                        FOwnerID = change.FSubOrgId,
                        FSupplyOrg = change.FSubOrgId,
                        //FUseRate=b.IS_MAIN=="N" ? 0 : 100,
                        //FWorkshopID1 = new Dictionary<string, string> { { "FNumber", a.FWorkshopID } },
                        FNeedDate2 = b.N_DATE,
                        FNumerator = a.FNumerator,
                        FDenominator = a.FDenominator,
                        FReplaceGroup = a.FReplaceGroup,
                        //FSeq=a.FPPBOMEntrySeq,
                        //FSTDQTY=a.ChildQty,
                        //FUseRate=b.IS_MAIN=="N" ? 0 : 100,
                        //FWorkshopID1 = new Dictionary<string, string> { { "FNumber", a.FWorkshopID } },
                        FEntity_Link = new List<SUB_PPBOMChange.Entity.Entity_Link>()
                                      {
                                          new SUB_PPBOMChange.Entity.Entity_Link()
                                          {FEntity_Link_FSBillId = a.FPPBOMId,
                                          FEntity_Link_FSId=a.FPPBOMEntryId
                                          }
                                      }
                    }
                        //).DistinctBy(s => new { s.FMaterialID2, s.FChangeType, s.FUnitID2, s.FProduceQty, s.FMoBillNo1, s.FPPBOMNo, s.FPPBOMEntryId, s.FMoEntrySeq1, s.FPPBOMId, s.FWorkshopID1, s.FMaterialType, s.FNeedDate2, s.FOwnerID, s.FSupplyOrg, s.FNumerator, s.FDenominator }).ToList();
                        ).Distinct().ToList();
        }
        /// <summary>
        /// 取分子或分母
        /// </summary>
        /// <param name="numerator">应发数量</param>
        /// <param name="denominator">成品生产数量</param>
        /// <param name="num">true为返回分子反之分母</param>
        /// <returns></returns>
        private long GetNumerator(decimal numerator, decimal denominator, bool num)
        {
            if (denominator == 0 || numerator == 0) { return num ? 0 : 1; }
            decimal d = Math.Round(numerator / denominator, 4);
            int index = d.ToString(CultureInfo.InvariantCulture).IndexOf(".");
            if (index <= 0) { return (long)(num ? d : 1); }
            int count = d.ToString(CultureInfo.InvariantCulture).Substring(index + 1).Length;
            if (num)
            {
                if (count > 0)
                    return (long)numerator;
                else
                    return (long)d;
                //return (long)(d * IntNumber(count));
            }
            else
            {
                if (count > 0)
                    return (long)denominator;
                else
                    return 1;
                //return IntNumber(count);
            }
        }
        private List<PRD_PPBOMChange.Entity> GetEntitiesForChange(List<PPBOMEntryStr> ppbom, List<WPLAN_MN_CHANGE> mesbom
            , List<BD_Material> materials, List<BD_UNIT> units, PRD_PPBOMChange change)
        {
            List<PRD_PPBOMChange.Entity> entities = (from a in ppbom.Where(s => s.FMoId == change.FMoId && s.FMOEntryID == change.FMOEntryID).DistinctBy(s => new { s.FPPBOMNo, s.FPPBOMEntrySeq })
                                                         //join b in mesbom on new { A = a.FPPBOMNo, B = a.FMaterial } equals new { A = b.ERP_ID, B = b.MTRL_ID }//2023-11-23被注释
                                                     join b in mesbom on new { A = a.FPPBOMNo, B = a.FMainMaterial, C = a.FMaterial, D = a.FMaterialType } equals
                                                     new { A = b.ERP_ID, B = b.MAIN_ID, C = b.MTRL_ID, D = b.IS_BACK == "Y" ? 2 : b.IS_MAIN == "N" ? 3 : 1 }
                                                     join c in materials on b.MTRL_ID equals c.FNumber
                                                     join d in units on c.FStoreUnitID equals d.FUNITID
                                                     where a.FPPBOMEntryId != 0
                                                     select new PRD_PPBOMChange.Entity
                                                     {
                                                         FParentRowId = a.FParentRowId,
                                                         //FSeq = a.FPPBOMEntrySeq,
                                                         FMaterialID2 = new Dictionary<string, string> { { "FNumber", b.MTRL_ID } },
                                                         FChangeType = "3",
                                                         FUnitID2 = new Dictionary<string, string> { { "FNumber", d.FNumber } },
                                                         FProduceQty = a.FQty,
                                                         FMoBillNo1 = a.FMOBillNO,
                                                         FPPBOMNo = a.FPPBOMNo,
                                                         FPPBOMEntryId = a.FPPBOMEntryId,
                                                         FPPBOMEntrySeq = a.FPPBOMEntrySeq,
                                                         FMoEntrySeq1 = a.FMOEntrySeq,
                                                         FPPBOMId = a.FPPBOMId,
                                                         FWorkshopID1 = new Dictionary<string, string> { { "FNumber", a.FWorkshopID } },
                                                         FMaterialType = b.IS_BACK == "Y" ? "2" : b.IS_MAIN == "N" ? "3" : "1",
                                                         FIsKeyItem = b.IS_MAIN == "Y" ? KeyItem(mesbom, b.MAIN_ID, 1, b.MTRL_ID) : 1,
                                                         //FReplacePriority = b.IS_MAIN == "Y" ? 0 : 1,
                                                         FReplacePriority = a.FReplacePriority,
                                                         FReplaceType = b.IS_MAIN == "Y" ? KeyItem(mesbom, b.MAIN_ID).ToString() : "1",
                                                         FReplacePolicy = b.IS_MAIN == "Y" ? KeyItem(mesbom, b.MAIN_ID) == 1 ? "3" : "" : "3",
                                                         FReplaceGroup = a.FReplaceGroup,
                                                         FRowExpandType = b.IS_MAIN == "N" ? 0 : 16,
                                                         FNeedDate2 = b.N_DATE,
                                                         FOwnerID = change.FPrdOrgId,
                                                         FSupplyOrg = change.FPrdOrgId,
                                                         //FNumerator = b.IS_BACK == "Y" ? GetNumerator(b.TN_QTY, a.FQty, true) : a.FNumerator,
                                                         //FDenominator = b.IS_BACK == "Y" ? GetNumerator(b.TN_QTY, a.FQty, false) : a.FDenominator,
                                                         FNumerator = (b.IS_BACK == "Y" ? b.TN_QTY : (b.TN_QTY == 0 ? 0 : GetNumerator(b.TN_QTY, a.FQty, true))),//20250108
                                                         FDenominator = (b.IS_BACK == "Y" ? a.FQty : (b.TN_QTY == 0 ? 1 : GetNumerator(b.TN_QTY, a.FQty, true))),//20250108
                                                         //FUseRate= b.TN_QTY==0 ? 0 : 100,
                                                         FPositionNO = b.POINT_STR,
                                                         FNeedQty2 = b.IS_BACK == "Y" ? 0 : b.TN_QTY,
                                                         FMustQty = b.IS_BACK == "Y" ? 0 : b.TN_QTY,
                                                         //FSTDQTY= a.TN_QTY
                                                         FIssueType = b.IS_BACK == "Y" ? 0 : 1,
                                                         FEntity_Link = new List<PRD_PPBOMChange.Entity.Entity_Link>()
                                                              {new PRD_PPBOMChange.Entity.Entity_Link
                                                                     {
                                                                         FEntity_Link_FSBillId = a.FPPBOMId,
                                                                         FEntity_Link_FSId = a.FPPBOMEntryId
                                                                     }
                                                              }
                                                     }
            //).DistinctBy(s=>new { s.FMaterialID2, s.FChangeType, s.FUnitID2, s.FProduceQty, s.FMoBillNo1, s.FPPBOMNo, s.FPPBOMEntryId, s.FMoEntrySeq1, s.FPPBOMId, s.FWorkshopID1, s.FMaterialType, s.FNeedDate2, s.FOwnerID, s.FSupplyOrg, s.FNumerator, s.FDenominator }).ToList();
            ).Distinct().OrderBy(s => s.FSeq).ToList();
            return entities;
        }
        private List<SUB_PPBOMChange.Entity> GetSUBEntitiesForChange(List<PPBOMEntryStr> ppbom, List<WPLAN_MN_CHANGE> mesbom
            , List<BD_Material> materials, List<BD_UNIT> units, SUB_PPBOMChange change)
        {
            List<SUB_PPBOMChange.Entity> entities = (from a in ppbom.Where(s => s.FMoId == change.FSubReqId && s.FMOEntryID == change.FSubReqEntryId).DistinctBy(s => new { s.FPPBOMNo, s.FPPBOMEntrySeq })
                                                         //join b in mesbom on new { A = a.FPPBOMNo, B = a.FMaterial } equals new { A = b.ERP_ID, B = b.MTRL_ID }//2023-11-23被注释
                                                     join b in mesbom on new { A = a.FPPBOMNo, B = a.FMaterial, C = a.FMainMaterial, D = a.FMaterialType } equals
                                                     new { A = b.ERP_ID, B = b.MTRL_ID, C = b.MAIN_ID, D = b.IS_BACK == "Y" ? 2 : b.IS_MAIN == "N" ? 3 : 1 }
                                                     join c in materials on b.MTRL_ID equals c.FNumber
                                                     join d in units on c.FStoreUnitID equals d.FUNITID
                                                     select new SUB_PPBOMChange.Entity
                                                     {
                                                         //FSeq = a.FPPBOMEntrySeq,
                                                         FIssueType = b.IS_BACK == "Y" ? 0 : 1,
                                                         FMaterialID2 = new Dictionary<string, string> { { "FNumber", b.MTRL_ID } },
                                                         FChangeType = "3",
                                                         FUnitID2 = new Dictionary<string, string> { { "FNumber", d.FNumber } },
                                                         //FMustQty = b.TN_QTY,
                                                         //FNeedQty2=b.TN_QTY,
                                                         //FSTDQTY = b.TN_QTY,
                                                         FProduceQty = a.FQty,
                                                         FSubReqBillNO1 = a.FMOBillNO,
                                                         FSUBPPBOMNo = a.FPPBOMNo,
                                                         FSUBPPBOMEntryId = a.FPPBOMEntryId,
                                                         FSUBPPBOMEntrySeq = a.FPPBOMEntrySeq,
                                                         FSubReqEntrySeq1 = a.FMOEntrySeq,
                                                         FSUBPPBOMId = a.FPPBOMId,
                                                         //FUseRate = GetUseRate(),
                                                         //FSupplierId = new Dictionary<string, string> { { "FNumber", a.FWorkshopID } },
                                                         FMaterialType = b.IS_BACK == "Y" ? "2" : b.IS_MAIN == "N" ? "3" : "1",
                                                         FIsKeyItem = b.IS_MAIN == "Y" ? KeyItem(mesbom, b.MAIN_ID, 1, b.MTRL_ID) : 1,
                                                         FReplacePriority = b.IS_MAIN == "Y" ? 0 : 1,
                                                         FReplaceType = b.IS_MAIN == "Y" ? KeyItem(mesbom, b.MAIN_ID).ToString() : "1",
                                                         FReplacePolicy = b.IS_MAIN == "Y" ? KeyItem(mesbom, b.MAIN_ID) == 1 ? "3" : "" : "3",
                                                         FReplaceGroup = a.FReplaceGroup,
                                                         FRowExpandType = b.IS_MAIN == "N" ? 0 : 16,
                                                         FNeedDate2 = b.N_DATE,
                                                         FOwnerID = change.FSubOrgId,
                                                         FSupplyOrg = change.FSubOrgId,
                                                         FNumerator = b.IS_BACK == "Y" ? GetNumerator(b.TN_QTY, a.FQty, true) : a.FNumerator,
                                                         FDenominator = b.IS_BACK == "Y" ? GetNumerator(b.TN_QTY, a.FQty, false) : a.FDenominator,
                                                         //FUseRate= b.TN_QTY==0 ? 0 : 100,
                                                         FPositionNO = b.POINT_STR,
                                                         FNeedQty2 = b.IS_BACK == "Y" ? 0 : b.TN_QTY,
                                                         FMustQty = b.IS_BACK == "Y" ? 0 : b.TN_QTY,
                                                         //FSTDQTY= a.TN_QTY
                                                         FEntity_Link = new List<SUB_PPBOMChange.Entity.Entity_Link>()
                                                              {new SUB_PPBOMChange.Entity.Entity_Link
                                                                     {
                                                                         FEntity_Link_FSBillId = a.FPPBOMId,
                                                                         FEntity_Link_FSId = a.FPPBOMEntryId
                                                                     }
                                                              }
                                                     }
            //).DistinctBy(s=>new { s.FMaterialID2, s.FChangeType, s.FUnitID2, s.FProduceQty, s.FMoBillNo1, s.FPPBOMNo, s.FPPBOMEntryId, s.FMoEntrySeq1, s.FPPBOMId, s.FWorkshopID1, s.FMaterialType, s.FNeedDate2, s.FOwnerID, s.FSupplyOrg, s.FNumerator, s.FDenominator }).ToList();
            ).Distinct().OrderBy(s => s.FSeq).ToList();
            return entities;
        }
        private void UpdateSUBUseRate(List<SUB_PPBOMChange.Entity> entities)
        {
            var groupid = (from a in entities.Where(s => s.FChangeType != "2")
                           group a by new
                           {
                               a.FReplaceGroup
                           } into b
                           select new
                           {
                               FReplaceGroup = b.Key.FReplaceGroup,
                               TotalQty = b.Sum(s => s.FMustQty)
                           }).ToList().Distinct();
            foreach (var item in groupid)
            {
                int id = item.FReplaceGroup;
                decimal itemqty = item.TotalQty;
                List<SUB_PPBOMChange.Entity> itemgroup = entities.Where(s => s.FReplaceGroup == id && s.FChangeType != "2").ToList();
                foreach (SUB_PPBOMChange.Entity entity in itemgroup)
                {
                    if (itemqty != 0)
                    {
                        //使用比例=应发数量/总需求*100
                        decimal userate = Math.Round(entity.FMustQty / itemqty * 100, 4);
                        if (userate != 0)
                        {
                            entity.FUseRate = userate;
                            //decimal pqty = entity.FProduceQty;
                            decimal m = entity.FMustQty;
                            decimal d = entity.FProduceQty;
                            GetNewfraction(ref m, ref d);
                            entity.FNumerator = m;
                            entity.FDenominator = d;
                            ////2-2总部给逻辑之前这样做是正常的，传到系统后应发数量不正确
                            ////分子=应发数量/(生产订单数量*使用比例/100)
                            //decimal md = Math.Round(entity.FMustQty / (pqty * userate / 100), 4);
                            //string desmd = md.ToString();
                            //int mdlen = desmd.Length;
                            //string mdstr = "";
                            //for (int i = mdlen; i >= 1; i--)
                            //{
                            //    string lstr = desmd.Substring(i - 1, 1);
                            //    if (lstr != "0" || lstr == ".")
                            //    {
                            //        mdstr = desmd.Substring(0, i);
                            //        break;
                            //    }
                            //}
                            //decimal desdeci = decimal.Parse(mdstr);
                            //entity.FNumerator = GetChangeNumerator(desdeci, true);
                            //entity.FDenominator = GetChangeNumerator(desdeci, false);


                            ////分子=应发数量/生产订单数量)
                            //decimal md = Math.Round(entity.FMustQty / pqty, 4);
                            //entity.FNumerator = md;
                            ////entity.FDenominator = 1;        //使用默认值

                        }
                        else
                        {
                            //主料料用量为0时，存在替代料的使用比例为0，否则分子为0
                            if (entity.FMaterialType == "1" && entity.FIsKeyItem == 1)
                            {
                                entity.FNumerator = 1;        //使用默认值
                                entity.FUseRate = 0;
                                //20230202遇到：原有主料A，替代料B，均取消领用，同时增加替代料C，如果A、B料的使用比例为0，C为100，API接口返回主替代料合计比例不为100%错误
                                //系统本身的逻辑是分子、分母均不能为0，此处为了让应发数量为0
                            }
                            else if (entity.FMaterialType == "1" && entity.FIsKeyItem == 0)
                            {
                                entity.FNumerator = 0;
                                entity.FUseRate = 100;
                            }
                            else if (entity.FMaterialType == "3")
                            {
                                entity.FNumerator = 1;        //使用默认值
                                entity.FUseRate = 0;
                            }
                            entity.FDenominator = 1;        //使用默认值
                        }
                        //一定要取消百分比控制
                        //entity.FUseRate = Math.Round(userate, 4);
                    }
                    else
                    {
                        //主料料用量为0时，存在替代料的使用比例应当为0，否则分子为0
                        if (entity.FMaterialType == "1" && entity.FIsKeyItem == 1)
                        {
                            entity.FNumerator = 1;        //使用默认值
                            entity.FUseRate = 0;
                            //20230202遇到：原有主料A，替代料B，均取消领用，同时增加替代料C，如果A、B料的使用比例为0，C为100，API接口返回主替代料合计比例不为100%错误
                            //系统本身的逻辑是分子、分母均不能为0，此处为了让应发数量为0
                        }
                        else if (entity.FMaterialType == "1" && entity.FIsKeyItem == 0)
                        {
                            entity.FNumerator = 0;
                            entity.FUseRate = 100;
                        }
                        else if (entity.FMaterialType == "3")
                        {
                            entity.FNumerator = 1;        //使用默认值
                            entity.FUseRate = 0;
                        }
                    }
                    //if((entity.FNumerator==0 || entity.FUseRate == 0) && entity.FSTDQTY != 0)
                    //{
                    //    entity.FSTDQTY = 0;
                    //}
                }
            }
        }
        /// <summary>
        /// 针对变更后的物料，更新它的使用比例、分子、分母
        /// </summary>
        /// <param name="entities"></param>
        private void UpdateUseRate(List<PRD_PPBOMChange.Entity> entities)
        {
            var groupid = (from a in entities.Where(s => s.FChangeType != "2")
                           group a by new
                           {
                               a.FReplaceGroup
                           } into b
                           select new
                           {
                               FReplaceGroup = b.Key.FReplaceGroup,
                               TotalQty = b.Sum(s => s.FMustQty)
                           }).ToList().Distinct();

            foreach (var item in groupid)
            {
                int id = item.FReplaceGroup;
                decimal itemqty = item.TotalQty;
                List<PRD_PPBOMChange.Entity> itemgroup = entities.Where(s => s.FReplaceGroup == id && s.FChangeType != "2").ToList();

                var count = itemgroup.Count();
                var index = 0;
                foreach (PRD_PPBOMChange.Entity entity in itemgroup)
                {
                    index++;
                    if (itemqty != 0)
                    {
                        //使用比例=应发数量/总需求*100
                        decimal userate = Math.Round(entity.FMustQty / itemqty * 100, 4);

                        if (userate != 0)
                        {
                            entity.FUseRate = userate;
                            //decimal pqty = entity.FProduceQty;
                            decimal m = entity.FMustQty;
                            decimal d = entity.FProduceQty;
                            GetNewfraction(ref m, ref d);
                            entity.FNumerator = m;
                            entity.FDenominator = d;


                        }
                        else
                        {
                            //主料料用量为0时，存在替代料的使用比例为0，否则分子为0
                            if (entity.FMaterialType == "1" && entity.FIsKeyItem == 1)
                            {
                                entity.FNumerator = 1;        //使用默认值
                                entity.FUseRate = 0;
                                //20230202遇到：原有主料A，替代料B，均取消领用，同时增加替代料C，如果A、B料的使用比例为0，C为100，API接口返回主替代料合计比例不为100%错误
                                //系统本身的逻辑是分子、分母均不能为0，此处为了让应发数量为0
                            }
                            else if (entity.FMaterialType == "1" && entity.FIsKeyItem == 0)
                            {
                                entity.FNumerator = 0;
                                entity.FUseRate = 100;
                            }
                            else if (entity.FMaterialType == "3")
                            {
                                entity.FNumerator = 1;        //使用默认值
                                entity.FUseRate = 0;
                            }
                            entity.FDenominator = 1;        //使用默认值
                        }
                        //一定要取消百分比控制
                        //entity.FUseRate = Math.Round(userate, 4);
                    }
                    else
                    {
                        //主料料用量为0时，存在替代料的使用比例应当为0，否则分子为0
                        if (entity.FMaterialType == "1" && entity.FIsKeyItem == 1)
                        {
                            entity.FNumerator = 1;        //使用默认值
                            entity.FUseRate = 0;
                            //20230202遇到：原有主料A，替代料B，均取消领用，同时增加替代料C，如果A、B料的使用比例为0，C为100，API接口返回主替代料合计比例不为100%错误
                            //系统本身的逻辑是分子、分母均不能为0，此处为了让应发数量为0
                        }
                        else if (entity.FMaterialType == "1" && entity.FIsKeyItem == 0)
                        {
                            entity.FNumerator = 0;
                            entity.FUseRate = 100;
                        }
                        else if (entity.FMaterialType == "3")
                        {
                            entity.FNumerator = 1;        //使用默认值
                            entity.FUseRate = 0;
                        }
                        else if (entity.FMaterialType == "2")
                        {
                            entity.FUseRate = 100;
                        }
                    }
                    //if ((entity.FNumerator == 0 || entity.FUseRate == 0) && entity.FSTDQTY != 0)
                    //{
                    //    entity.FSTDQTY = 0;
                    //}
                }
            }
        }

        private void UpdateUseRate(List<PRD_PPBOMMerge.MergeEntity> entities)
        {
            var groupid = (from a in entities.Where(s => s.FChangeType != "2")
                           group a by new
                           {
                               a.FReplaceGroup
                           } into b
                           select new
                           {
                               FReplaceGroup = b.Key.FReplaceGroup,
                               TotalQty = b.Sum(s => s.FMustQty)
                           }).ToList().Distinct();

            foreach (var item in groupid)
            {
                int id = item.FReplaceGroup;
                decimal itemqty = item.TotalQty;
                List<PRD_PPBOMMerge.MergeEntity> itemgroup = entities.Where(s => s.FReplaceGroup == id && s.FChangeType != "2").ToList();

                var count = itemgroup.Count();
                var index = 0;
                foreach (var entity in itemgroup)
                {
                    index++;
                    if (itemqty != 0)
                    {
                        //使用比例=应发数量/总需求*100
                        decimal userate = Math.Round(entity.FMustQty / itemqty * 100, 4);

                        if (userate != 0)
                        {
                            entity.FUseRate = userate;
                            //decimal pqty = entity.FProduceQty;
                            decimal m = entity.FMustQty;
                            decimal d = entity.FProduceQty;
                            GetNewfraction(ref m, ref d);
                            entity.FNumerator = m;
                            entity.FDenominator = d;
                        }
                        else
                        {
                            //主料料用量为0时，存在替代料的使用比例为0，否则分子为0
                            if (entity.FMaterialType == "1" && entity.FIsKeyItem == 1)
                            {
                                entity.FNumerator = 1;        //使用默认值
                                entity.FUseRate = 0;
                                //20230202遇到：原有主料A，替代料B，均取消领用，同时增加替代料C，如果A、B料的使用比例为0，C为100，API接口返回主替代料合计比例不为100%错误
                                //系统本身的逻辑是分子、分母均不能为0，此处为了让应发数量为0
                            }
                            else if (entity.FMaterialType == "1" && entity.FIsKeyItem == 0)
                            {
                                entity.FNumerator = 0;
                                entity.FUseRate = 100;
                            }
                            else if (entity.FMaterialType == "3")
                            {
                                entity.FNumerator = 1;        //使用默认值
                                //if (entity.FBASESTDQTY == 0)
                                //    entity.FNumerator = 0;
                                entity.FUseRate = 0;
                            }
                            entity.FDenominator = 1;        //使用默认值
                        }
                        //一定要取消百分比控制
                        //entity.FUseRate = Math.Round(userate, 4);
                    }
                    else
                    {
                        //主料料用量为0时，存在替代料的使用比例应当为0，否则分子为0
                        if (entity.FMaterialType == "1" && entity.FIsKeyItem == 1)
                        {
                            entity.FNumerator = 1;        //使用默认值
                            entity.FUseRate = 0;
                            //20230202遇到：原有主料A，替代料B，均取消领用，同时增加替代料C，如果A、B料的使用比例为0，C为100，API接口返回主替代料合计比例不为100%错误
                            //系统本身的逻辑是分子、分母均不能为0，此处为了让应发数量为0
                        }
                        else if (entity.FMaterialType == "1" && entity.FIsKeyItem == 0)
                        {
                            entity.FNumerator = 0;
                            entity.FUseRate = 100;
                        }
                        else if (entity.FMaterialType == "3")
                        {
                            entity.FNumerator = 1;        //使用默认值
                            entity.FUseRate = 0;
                        }
                    }
                    //if ((entity.FNumerator == 0 || entity.FUseRate == 0) && entity.FSTDQTY != 0)
                    //{
                    //    entity.FSTDQTY = 0;
                    //}
                }
            }
        }

        private void GetNewfraction(ref decimal m, ref decimal d)
        {
            decimal md = m / d;
            int index = md.ToString(CultureInfo.InvariantCulture).IndexOf(".");
            if (index <= 0) { return; }
            int count = md.ToString(CultureInfo.InvariantCulture).Substring((index + 1)).Length;
            if (count > 0)
            {
                return;
            }
            else
            {
                m = md;
                d = 1;
            }
        }
        private decimal GetChangeNumerator(decimal m, bool isnumerator)
        {
            if (m == 0) { return isnumerator ? 0 : 1; }
            int index = m.ToString(CultureInfo.InvariantCulture).IndexOf(".");
            if (index <= 0) { return isnumerator ? m : 1; }
            int count = m.ToString(CultureInfo.InvariantCulture).Substring((index + 1)).Length;
            if (isnumerator)
            {
                if (count > 0)
                {
                    return m * IntNumber(count);
                }
                else
                {
                    return m;
                }
            }
            else
            {
                if (count > 0) { return IntNumber(count); } else { return 1; }
            }
        }
        private List<PRD_PPBOMChange.Entity> GetEntitiesForNew(List<PPBOMEntryStr> ppbomlist, List<WPLAN_MN_CHANGE> newpbom
            , List<BD_Material> materials, List<BD_UNIT> units, PRD_PPBOMChange change)
        {
            List<PRD_PPBOMChange.Entity> entities = (from a in newpbom
                                                     join b in materials on a.MTRL_ID equals b.FNumber
                                                     join c in units on b.FStoreUnitID equals c.FUNITID
                                                     join d in ppbomlist on a.ERP_ID equals d.FPPBOMNo
                                                     select new PRD_PPBOMChange.Entity
                                                     {
                                                         //FSeq=(int)a.SEQ,
                                                         FMaterialID2 = new Dictionary<string, string> { { "FNumber", a.MTRL_ID } },
                                                         FChangeType = "1",
                                                         FUnitID2 = new Dictionary<string, string> { { "FNumber", c.FNumber } },
                                                         FProduceQty = d.FQty,
                                                         FMoBillNo1 = a.WPLAN_MO,
                                                         FMoEntrySeq1 = int.Parse(a.WPLAN_MO_LINE),
                                                         FPPBOMNo = a.ERP_ID,
                                                         FMaterialType = a.IS_BACK == "Y" ? "2" : a.IS_MAIN == "N" ? "3" : "1",
                                                         FPPBOMId = d.FPPBOMId,
                                                         FNeedDate2 = a.N_DATE,
                                                         FSupplyOrg = change.FPrdOrgId,
                                                         FIsKeyItem = a.IS_MAIN == "N" ? 1 : KeyItem(newpbom, a.MTRL_ID),
                                                         FReplacePriority = a.IS_MAIN == "N" ? 1 : 0,
                                                         FReplacePolicy = a.IS_MAIN == "N" ? "3" : "",
                                                         FReplaceType = a.IS_MAIN == "N" ? "1" : "",
                                                         //FParentRowId = "",
                                                         FUseRate = 100,
                                                         FRowExpandType = a.IS_MAIN == "N" ? 0 : 16,
                                                         //FNumerator = a.TN_QTY == 0 ? 0 : GetNumerator(a.TN_QTY, d.FQty, true),
                                                         //FDenominator = a.TN_QTY == 0 ? 1 : GetNumerator(a.TN_QTY, d.FQty, false),
                                                         FNumerator = (a.IS_BACK == "Y" ? a.TN_QTY : (a.TN_QTY == 0 ? 0 : GetNumerator(a.TN_QTY, d.FQty, true))),//20250108
                                                         FDenominator = (a.IS_BACK == "Y" ? d.FQty : (a.TN_QTY == 0 ? 1 : GetNumerator(a.TN_QTY, d.FQty, false))),//20250108
                                                         //FNumerator = (a.IS_BACK == "Y"? false: a.TN_QTY == 0) ? 0 : GetNumerator(a.TN_QTY, d.FQty, true),//20250108
                                                         //FDenominator = (a.IS_BACK == "Y" ? false : a.TN_QTY == 0) ? 1 : GetNumerator(a.TN_QTY, d.FQty, true),//20250108
                                                         FPositionNO = a.POINT_STR,
                                                         FNeedQty2 = a.IS_BACK == "Y" ? 0 : a.TN_QTY,
                                                         FMustQty = a.IS_BACK == "Y" ? 0 : a.TN_QTY,
                                                         //FSTDQTY= a.TN_QTY
                                                         FIssueType = a.IS_BACK == "Y" ? 0 : 1,
                                                         FPPBOMEntrySeq = int.Parse(a.SEQ.ToString())       //对应MES提供的JSON中的唯一项
                                                     }).ToList();
            return entities;
        }
        private List<SUB_PPBOMChange.Entity> GetSUBEntitiesForNew(List<PPBOMEntryStr> ppbomlist, List<WPLAN_MN_CHANGE> newpbom
            , List<BD_Material> materials, List<BD_UNIT> units, SUB_PPBOMChange change)
        {
            List<SUB_PPBOMChange.Entity> entities = (from a in newpbom
                                                     join b in materials on a.MTRL_ID equals b.FNumber
                                                     join c in units on b.FStoreUnitID equals c.FUNITID
                                                     join d in ppbomlist on a.ERP_ID equals d.FPPBOMNo
                                                     select new SUB_PPBOMChange.Entity
                                                     {
                                                         //FSeq=(int)a.SEQ,
                                                         FMaterialID2 = new Dictionary<string, string> { { "FNumber", a.MTRL_ID } },
                                                         FChangeType = "1",
                                                         FUnitID2 = new Dictionary<string, string> { { "FNumber", c.FNumber } },
                                                         //FSTDQTY = a.TN_QTY,
                                                         FProduceQty = d.FQty,
                                                         FSubReqBillNO1 = a.WPLAN_MO,
                                                         FSubReqEntrySeq1 = int.Parse(a.WPLAN_MO_LINE),
                                                         FSUBPPBOMNo = a.ERP_ID,
                                                         FMaterialType = a.IS_BACK == "Y" ? "2" : a.IS_MAIN == "N" ? "3" : "1",
                                                         FSUBPPBOMId = d.FPPBOMId,
                                                         FSUBPPBOMEntryId = d.FPPBOMEntryId,
                                                         FNeedDate2 = a.N_DATE,
                                                         FSupplyOrg = change.FSubOrgId,
                                                         FIsKeyItem = a.IS_MAIN == "N" ? 1 : KeyItem(newpbom, a.MTRL_ID),
                                                         FReplacePriority = a.IS_MAIN == "N" ? 1 : 0,
                                                         FReplacePolicy = a.IS_MAIN == "N" ? "3" : "",
                                                         FReplaceType = a.IS_MAIN == "N" ? "1" : "",
                                                         //FParentRowId = "",
                                                         FUseRate = 100,
                                                         FRowExpandType = a.IS_MAIN == "N" ? 0 : 16,
                                                         FNumerator = a.TN_QTY == 0 ? 0 : GetNumerator(a.TN_QTY, d.FQty, true),
                                                         FDenominator = a.TN_QTY == 0 ? 1 : GetNumerator(a.TN_QTY, d.FQty, false),
                                                         FPositionNO = a.POINT_STR,
                                                         FNeedQty2 = a.IS_BACK == "Y" ? 0 : a.TN_QTY,
                                                         FMustQty = a.IS_BACK == "Y" ? 0 : a.TN_QTY,
                                                         //FSTDQTY= a.TN_QTY
                                                         FIssueType = a.IS_BACK == "Y" ? 0 : 1,
                                                         FSUBPPBOMEntrySeq = int.Parse(a.SEQ.ToString())       //对应MES提供的JSON中的唯一项

                                                     }).ToList();
            return entities;
        }
        private List<WPLAN_MN_CHANGE> GetPpbomData(List<PPBOMEntryStr> ppbom, List<WPLAN_MN_CHANGE> mesbom, PRD_PPBOMChange change)
        {
            List<WPLAN_MN_CHANGE> wnc = new List<WPLAN_MN_CHANGE>();
            var ppbomentry = (from a in ppbom.Where(s => s.FMoId == change.FMoId && s.FMOEntryID == change.FMOEntryID).DistinctBy(s => new { s.FPPBOMNo, s.FPPBOMEntrySeq })
                              join b in mesbom.Where(s => s.IS_MAIN == "N") on new { A = a.FPPBOMNo, B = a.FMaterial } equals new { A = b.ERP_ID, B = b.MTRL_ID }
                              select new
                              {
                                  a.FReplaceGroup
                              }
                            ).ToList().Distinct();
            return wnc;
        }
        private List<PRD_PPBOMChange> GetPPBOMChangeEntities(List<PPBOMEntryStr> ppbom, List<WPLAN_MN_CHANGE> mesbom
            , ref List<WPLAN_MN_CHANGE> notexists, ref List<PRD_PPBOMChange.Entity> newentities, ref string reason)
        {
            reason = string.Empty;
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            List<string> billnolist = (from a in mesbom.Where(s => !string.IsNullOrWhiteSpace(s.ERP_ID)).DistinctBy(s => s.ERP_ID)
                                       select string.Format("P.FBillNo='{0}'", a.ERP_ID)).ToList();
            //按生产用料清单查询，准备给新添加的子项使用
            if (!string.IsNullOrEmpty(reason) || billnolist.Count == 0)
            {
                reason = "ERP生产用料需求单号不能为空...";
                return null;
            }
            List<string> numbers = (from a in mesbom
                                    where !string.IsNullOrEmpty(a.MTRL_ID) && a.MTRL_ID != ""
                                    select a.MTRL_ID).Distinct().ToList();
            if (numbers.Count == 0)
            {
                reason = "物料不能为空...";
                return null;
            }
            //把主料为空的转为新的主料
            List<PPBOMEntryStr> nullmain = ppbom.Where(s => string.IsNullOrWhiteSpace(s.FMainMaterial) && !string.IsNullOrWhiteSpace(s.FMaterial)).ToList();
            foreach (PPBOMEntryStr s_change in nullmain)
            {
                //将mesbom中的主料拿过来自己用
                string main = mesbom.Find(s => s.MTRL_ID == s_change.FMaterial).MAIN_ID;
                s_change.FMainMaterial = main;
            }
            List<string> errmo = (from a in mesbom
                                  join b in ppbom on
                                  new { A = a.WPLAN_MO, B = a.WPLAN_MO_LINE, C = a.MAIN_ID, D = a.MTRL_ID }
                                  equals
                                  new { A = b.FMOBillNO, B = b.FMOEntrySeq.ToString(), C = b.FMainMaterial, D = b.FMaterial }
                                  where a.P_QTY != b.FQty
                                  select a.MTRL_ID).Distinct().ToList();
            if (errmo.Count > 0)
            {
                reason = "IMS计划数量与ERP生产数量不相同...";
                return null;
            }
            List<BD_Material> materials = _chiledselect.materialsAsync(numbers).Result;
            //先添加主表
            List<PRD_PPBOMChange> bills = (from b in ppbom
                                           select new PRD_PPBOMChange
                                           {
                                               //FBillNo=b.FPPBOMNo,
                                               FMaterialID = new Dictionary<string, string> { { "FNumber", b.FParentMaterial } },
                                               FWorkshopID = new Dictionary<string, string> { { "FNumber", b.FWorkshopID } },
                                               FBOMID = new Dictionary<string, string> { { "FNumber", b.FBOMID } },
                                               FQty = b.FQty,
                                               FMOBillNO = b.FMOBillNO,
                                               FMOEntrySeq = b.FMOEntrySeq,
                                               FMOEntryID = b.FMOEntryID,
                                               FBASEQTY = b.FBaseQty,
                                               FMoId = b.FMoId,
                                               FBaseUnitID = new Dictionary<string, string> { { "FNumber", b.FBaseUnitID } },
                                               FMOType = new Dictionary<string, string> { { "FNumber", b.FMOType } },
                                               FUnitID = new Dictionary<string, string> { { "FNumber", b.FUnitID } },
                                               FPrdOrgId = new Dictionary<string, string> { { "FNumber", b.FPrdOrgId } },
                                               FParentOwnerTypeId = b.FParentOwnerTypeId,
                                               FParentOwnerId = new Dictionary<string, string> { { "FNumber", b.FParentOwnerId } }
                                           }
                        ).DistinctBy(s => new { s.FMOEntryID, s.FMoId }).ToList();
            foreach (PRD_PPBOMChange change in bills)
            {
                List<PRD_PPBOMChange.Entity> ppbomchanges = new List<PRD_PPBOMChange.Entity>();
                //添加变更前子项
                ppbomchanges.AddRange(GetEntitiesForOld(ppbom, mesbom, materials, units, change));
                //添加变更后子项
                ppbomchanges.AddRange(GetEntitiesForChange(ppbom, mesbom, materials, units, change));
                //添加新增子项
                List<WPLAN_MN_CHANGE> newpbom = (from a in mesbom
                                                     //join b in ppbom on new { A = a.ERP_ID, B = a.MAIN_ID,C=a.MTRL_ID } equals new { A = b.FPPBOMNo, B = b.FMainMaterial,C=b.FMaterial } into c//2023-11-23被注释
                                                 join b in ppbom on new { A = a.ERP_ID, B = a.MAIN_ID, C = a.MTRL_ID, D = a.IS_BACK == "Y" ? 2 : a.IS_MAIN == "N" ? 3 : 1 } equals
                                                 new { A = b.FPPBOMNo, B = b.FMainMaterial, C = b.FMaterial, D = b.FMaterialType } into c
                                                 from d in c.DefaultIfEmpty()
                                                 where d == null //&& a.ERP_MO == change.FMOBillNO && a.MO_SEQ == change.FMOEntrySeq.ToString()
                                                 select a).ToList();
                if (newpbom.Count != 0)
                {
                    List<PPBOMEntryStr> ppbomlist = GetNewPPBOMEntry(billnolist, ref reason);
                    newentities = GetEntitiesForNew(ppbomlist, newpbom, materials, units, change);
                    if (newentities.Count != 0)
                    {
                        //var changees = newentities.Where(s => int.Parse(s.FChangeType) != 3);
                        foreach (PRD_PPBOMChange.Entity entity in newentities)
                        {
                            //从源单信息中把主料找出来
                            int seq = entity.FPPBOMEntrySeq;
                            WPLAN_MN_CHANGE mes = newpbom.Find(s => s.SEQ == seq);
                            string mainitem = mes.MAIN_ID;
                            string item = mes.MTRL_ID;
                            //定位主料,即使子料既有标准件也有返还件，它的主、替关系是固定的！！！
                            var mainrow = ppbom.Where(s => s.FMainMaterial == mainitem && s.FMaterial == mainitem && s.FMaterialType == 1);
                            if (mainrow.Any())
                            {
                                entity.FReplacePriority = ppbomchanges.Count(w =>
                                    w.FReplaceGroup == mainrow.FirstOrDefault().FReplaceGroup
                                    && (w.FChangeType == "3" || w.FChangeType == "1")
                                );
                                entity.FParentRowId = mainrow.FirstOrDefault().FRowId;
                                entity.FReplaceGroup = mainrow.FirstOrDefault().FReplaceGroup;  //实际上是多余的，提交给ERP之后，会被覆盖...
                            }
                            ////entity.FSeq = maxseq;       //对于新的子项，这里的赋值没起作用...
                            //WPLAN_MN_CHANGE _CHANGE = newpbom.Find(s=>s.MTRL_ID==entity.FMaterialID2.FirstOrDefault().Value && s.SEQ==entity.FPPBOMEntrySeq);
                            //string parrowid = string.Empty;
                            //int groupno = GetChangeReplaceGroup(entity, entity.FMaterialID2.FirstOrDefault().Value, _CHANGE, ref reason, ref parrowid);
                            //if (!string.IsNullOrWhiteSpace(reason)) { break; }
                            ////if (groupno == 0 && entity.FMaterialType == "3")
                            ////{
                            ////    reason = "替代子项的同时主料也得变更";
                            ////    return null;
                            ////}
                            //entity.FReplaceGroup = groupno;
                            //entity.FParentRowId = parrowid;
                            //注意：FPPBOMEntrySeq在此之前属于主料的SEQ，用完既应该恢复为0
                            entity.FPPBOMEntrySeq = 0;
                        }
                        ppbomchanges.AddRange(newentities);
                    }
                }

                //int maxseq = GetPPBOMEntries(change.FMOBillNO, change.FMOEntrySeq,ref reason);
                if (string.IsNullOrEmpty(reason))
                {
                    UpdateUseRate(ppbomchanges);
                    var hp = ppbomchanges.OrderBy(s => s.FReplaceGroup).ThenBy(s => s.FReplaceType).ToList();
                    change.FEntity.AddRange(hp);
                }
            }

            return bills;
        }
        private List<SUB_PPBOMChange> GetSUBPPBOMChangeEntities(List<PPBOMEntryStr> ppbom, List<WPLAN_MN_CHANGE> mesbom
            , ref List<WPLAN_MN_CHANGE> notexists, ref List<SUB_PPBOMChange.Entity> newentities, ref string reason)
        {
            reason = string.Empty;
            //基本单位和单位转换率。
            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            List<string> billnolist = (from a in mesbom.Where(s => !string.IsNullOrWhiteSpace(s.ERP_ID)).DistinctBy(s => s.ERP_ID)
                                       select string.Format("P.FBillNo='{0}'", a.ERP_ID)).ToList();
            if (!string.IsNullOrEmpty(reason) || billnolist.Count == 0) { return null; }
            List<string> numbers = (from a in mesbom
                                    where !string.IsNullOrEmpty(a.MTRL_ID) && a.MTRL_ID != ""
                                    select a.MTRL_ID).Distinct().ToList();
            if (numbers.Count == 0) { return null; }
            List<BD_Material> materials = _chiledselect.materialsAsync(numbers).Result;
            //先添加主表
            List<SUB_PPBOMChange> bills = (from b in ppbom
                                           select new SUB_PPBOMChange
                                           {
                                               //FBillNo=b.FPPBOMNo,
                                               FMaterialID = new Dictionary<string, string> { { "FNumber", b.FParentMaterial } },
                                               FSupplierId = new Dictionary<string, string> { { "FNumber", string.IsNullOrEmpty(b.FWorkshopID) ? "" : b.FWorkshopID } },
                                               FBOMID = new Dictionary<string, string> { { "FNumber", b.FBOMID } },
                                               FQty = b.FQty,
                                               FSubReqBillNO = b.FMOBillNO,
                                               FSubReqEntrySeq = b.FMOEntrySeq,
                                               FSubReqEntryId = b.FMOEntryID,
                                               FBASEQTY = b.FBaseQty,
                                               FSubReqId = b.FMoId,
                                               FBaseUnitID = new Dictionary<string, string> { { "FNumber", b.FBaseUnitID } },
                                               FSubReqType = new Dictionary<string, string> { { "FNumber", b.FMOType } },
                                               FUnitID = new Dictionary<string, string> { { "FNumber", b.FUnitID } },
                                               FSubOrgId = new Dictionary<string, string> { { "FNumber", b.FPrdOrgId } },
                                               FParentOwnerTypeId = b.FParentOwnerTypeId,
                                               FParentOwnerId = new Dictionary<string, string> { { "FNumber", b.FParentOwnerId } }
                                           }
                        ).DistinctBy(s => new { s.FSubReqEntryId, s.FSubReqId }).ToList();
            foreach (SUB_PPBOMChange change in bills)
            {
                List<SUB_PPBOMChange.Entity> ppbomchanges = new List<SUB_PPBOMChange.Entity>();
                //添加变更前子项
                ppbomchanges.AddRange(GetSUBEntitiesForOld(ppbom, mesbom, materials, units, change));
                //添加变更后子项
                ppbomchanges.AddRange(GetSUBEntitiesForChange(ppbom, mesbom, materials, units, change));
                //添加新增子项
                List<WPLAN_MN_CHANGE> newpbom = (from a in mesbom
                                                     //join b in ppbom on new { A = a.ERP_ID, B = a.MAIN_ID, C = a.MTRL_ID } equals new { A = b.FPPBOMNo, B = b.FMainMaterial, C = b.FMaterial } into c//2023-11-23被注释
                                                 join b in ppbom on new { A = a.ERP_ID, B = a.MAIN_ID, C = a.MTRL_ID, D = a.IS_BACK == "Y" ? 2 : a.IS_MAIN == "N" ? 3 : 1 } equals
                                                 new { A = b.FPPBOMNo, B = b.FMainMaterial, C = b.FMaterial, D = b.FMaterialType } into c
                                                 from d in c.DefaultIfEmpty()
                                                 where d == null //&& a.ERP_MO == change.FMOBillNO && a.MO_SEQ == change.FMOEntrySeq.ToString()
                                                 select a).ToList();
                if (newpbom.Count != 0)
                {
                    //按生产用料清单查询，准备给新添加的子项使用
                    List<PPBOMEntryStr> ppbomlist = GetNewSUBPPBOMEntry(billnolist, ref reason);
                    newentities = GetSUBEntitiesForNew(ppbomlist, newpbom, materials, units, change);
                    if (newentities.Count != 0)
                    {
                        foreach (SUB_PPBOMChange.Entity entity in newentities)
                        {
                            //从源单信息中把主料找出来
                            int seq = entity.FSUBPPBOMEntrySeq;
                            WPLAN_MN_CHANGE mes = newpbom.Find(s => s.SEQ == seq);
                            string mainitem = mes.MAIN_ID;
                            string item = mes.MTRL_ID;
                            //定位主料,即使子料既有标准件也有返还件，它的主、替关系是固定的！！！
                            var mainrow = ppbom.Where(s => s.FMainMaterial == mainitem && s.FMaterial == mainitem && s.FMaterialType == 1);
                            if (mainrow.Any())
                            {
                                entity.FParentRowId = mainrow.FirstOrDefault().FRowId;
                                entity.FReplaceGroup = mainrow.FirstOrDefault().FReplaceGroup;  //实际上是多余的，提交给ERP之后，会被覆盖...
                            }
                            ////maxseq++;
                            ////entity.FSeq = maxseq;       //对于新的子项，这里的赋值没起作用...
                            //string parrowid = string.Empty;
                            //entity.FReplaceGroup = GetSUBChangeReplaceGroup(newentities, entity.FMaterialID2.FirstOrDefault().Value, newpbom, ref reason, ref parrowid);
                            //if (!string.IsNullOrWhiteSpace(reason)) { break; }
                            //entity.FParentRowId = parrowid;
                            entity.FSUBPPBOMEntrySeq = 0;
                        }
                        ppbomchanges.AddRange(newentities);
                    }
                }

                if (string.IsNullOrEmpty(reason))
                {
                    UpdateSUBUseRate(ppbomchanges);
                    var hp = ppbomchanges.OrderBy(s => s.FReplaceGroup).ThenBy(s => s.FReplaceType).ToList();
                    change.FEntity.AddRange(hp);
                }
                //string json=JsonConvert.SerializeObject(ppbomchanges.OrderBy(s => s.FReplaceGroup)
                //    .ThenBy(s => s.FChangeType));
            }


            return bills;
        }

        private int GetPPBOMEntries(string monnumber, int moseq, ref string reason)
        {
            int result = 0;
            reason = string.Empty;
            string sql = string.Format("select (select max(fseq) fseq from t_prd_ppbomentry where fid=t_prd_ppbom.fid) seq from t_prd_ppbom where fmobillno='{0}' and fmoentryseq={1}", monnumber, moseq);
            //string head = _chiledselect.GetCustomRequestBillAsync(sql, "","", null).Result;
            //object entryid = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true);
            string jquest = string.Empty;
            string costomsql = ApiSettingsHelper.GetConfig("CustomRequestRemoteSql");
            if (costomsql.ToUpper() == "Y")
            {
                jquest = _chiledselect.GetCustomReaderAsync(sql, "ExecuteScalar").Result;
            }
            else
            {
                jquest = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true).ToString();
            }
            if (!string.IsNullOrWhiteSpace(jquest))
            {
                if (string.IsNullOrWhiteSpace(reason)) { result = int.Parse(jquest.ToString()); }
            }
            else
            {
                reason = "调用沃比子查询服务（GetPPBOMEntries）出错，请查询接口日志";
            }

            //if (head != null)
            //{
            //    JArray array = (JArray)JsonConvert.DeserializeObject(head);
            //    List<Dictionary<string, string>> pairs = _shareController.GetClassList(array);
            //    if (pairs.Count > 0)
            //    {
            //        foreach (Dictionary<string, string> pair in pairs)
            //        {
            //            int.TryParse(pair.GetValueOrDefault("seq"), out result);
            //        }
            //    }
            //}
            return result;
        }

        private int GetSUBPPBOMEntries(string monnumber, int moseq, ref string reason)
        {
            int result = 0;
            string sql = string.Format("select (select max(FEntryID) seq from t_sub_ppbomentry where fid=t_sub_ppbom.fid) seq from t_sub_ppbom where FSUBBILLNO='{0}' and FSUBREQENTRYSEQ={1}", monnumber, moseq);
            reason = string.Empty;
            //object obj = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true);

            string jquest = string.Empty;
            string costomsql = ApiSettingsHelper.GetConfig("CustomRequestRemoteSql");
            if (costomsql.ToUpper() == "Y")
            {
                jquest = _chiledselect.GetCustomReaderAsync(sql, "ExecuteScalar").Result;
            }
            else
            {
                jquest = _kdsqlhelper.ExecuteScalar(CommandType.Text, sql, null, ref reason, true).ToString();
            }
            if (!string.IsNullOrWhiteSpace(jquest))
            {
                if (string.IsNullOrWhiteSpace(reason)) { result = int.Parse(jquest.ToString()); }
            }
            else
            {
                reason = "调用沃比子查询服务（GetSUBPPBOMEntries）出错，请查询接口日志";
            }
            //string head = _chiledselect.GetCustomRequestBillAsync(sql, "", "", null).Result;
            //if (head != null)
            //{
            //    JArray array = (JArray)JsonConvert.DeserializeObject(head);
            //    List<Dictionary<string, string>> pairs = _shareController.GetClassList(array);
            //    if (pairs.Count > 0)
            //    {
            //        foreach (Dictionary<string, string> pair in pairs)
            //        {
            //            int.TryParse(pair.GetValueOrDefault("seq"), out result);
            //        }
            //    }
            //}
            return result;
        }
        /// <summary>
        /// 处理新增的替代关系
        /// </summary>
        /// <param name="ppbom">原始生产用料清单</param>
        /// <param name="mesbom">传入的完整变更信息</param>
        private void ReplaceChild(List<PPBOMEntryStr> ppbom, List<WPLAN_MN_CHANGE> mesbom, ref bool again)
        {
            again = false;
            foreach (WPLAN_MN_CHANGE wPLAN_MN_CHANGE in mesbom)
            {
                if (wPLAN_MN_CHANGE.IS_MAIN == "N")
                {
                    string knumber = wPLAN_MN_CHANGE.MAIN_ID.ToString();
                    string mart = wPLAN_MN_CHANGE.MTRL_ID.ToString();
                    var ppbomchild = ppbom.Where(s => s.FMainMaterial.Equals(knumber) && s.FMaterial.Equals(mart)).ToList();
                    if (ppbomchild.Count == 0)
                    {
                        var child = ppbom.Where(s => s.FMainMaterial.Equals(knumber) && s.FMainMaterial.Equals(s.FMaterial)).ToList();
                        if (child.Count != 0)//找到主料了
                        {
                            PPBOMEntryStr entryStr = child.FirstOrDefault();
                            again = true;
                            //如果ERP中已经是主料了就不处理主料
                            UpdatePPBOM(entryStr.FPPBOMId, entryStr.FPPBOMEntryId, mart);
                        }

                    }
                }
            }
        }

        private void UpdatePPBOM(int billid, int entryid, string childmart)
        {
            string result = string.Empty;
            string sql = string.Format("exec AddChildPPBOM @kid={0},@entryid={1},@childnumber='{2}'", billid, entryid, childmart);
            sql = string.Format(sql, billid, entryid);
            string costomsql = ApiSettingsHelper.GetConfig("CustomRequestRemoteSql");
            if (costomsql.ToUpper() == "Y")
            {
                result = _chiledselect.GetCustomReaderAsync(sql, "Execute").Result;
            }
            else
            {
                result = _kdsqlhelper.ExecuteNonQueryAsync(sql, CommandType.Text, null, true).Result;
            }
        }
        /// <summary>
        /// 工单需求变更单
        /// </summary>
        /// <param name="data"></param>
        /// <param name="fid"></param>
        /// <param name="postmid"></param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchPPBOMChangeAsync(string data, int fid, int postmid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<WPLAN_MN_CHANGE> mesbom = _shareController.DataConvertClass<WPLAN_MN_CHANGE>(data, ref reason);
            if (mesbom.Count == 0)
            {
                breakData.Message = reason;
                return breakData;
            }
            var errmes = mesbom.Where(s => s.IS_BACK == "Y" && (s.IS_MAIN == "N" || s.MTRL_ID != s.MAIN_ID)).ToList();
            if (errmes.Count != 0)
            {
                breakData.Message = "返还件不能作替代...";
                return breakData;
            }
            var mos = (from a in mesbom
                       select new
                       {
                           a.ERP_ID
                       }).Distinct().ToList();
            List<string> listwhere = new List<string>();
            List<string> billnos = new List<string>();
            foreach (var mo in mos)
            {
                string key = mo.ERP_ID;
                List<string> wherebuilder = new List<string>();
                var martes = (from a in mesbom.Where(s => s.ERP_ID == key)
                              select new
                              {
                                  MAIN_ID = a.MAIN_ID,
                                  MTRL_ID = a.MTRL_ID,
                                  MTRL_Type = a.IS_BACK == "Y" ? 2 : a.IS_MAIN == "Y" ? 1 : 3 //返还件不能做替代，只有标准件才有替代。所以，先判断是否为返还件。
                              }).Distinct().ToList();
                foreach (var marte in martes)
                {
                    string s1 = string.Format("((FMainMaterial=''{0}'' and FMATERIALTYPE=1) OR (FMainMaterial=''{0}''  and FMaterial=''{1}'' and FMATERIALTYPE={2}))", marte.MAIN_ID, marte.MTRL_ID, marte.MTRL_Type);
                    if (!wherebuilder.Contains(s1))
                    {
                        wherebuilder.Add(s1);
                    }
                }
                listwhere.Add(string.Format("(FPPBOMNo=''{0}'' AND ({1}))", key, string.Join(" OR ", wherebuilder.ToArray())));
                billnos.Add(string.Format("(P.FBILLNO=''{0}'')", key));
            }
            //分析有多少工单，拼接成查询语句
            List<PPBOMEntryStr>? ppbom = GetPPBOMEntry(listwhere, billnos, ref reason);
            if (ppbom == null || ppbom.Count() == 0 || !string.IsNullOrWhiteSpace(reason))
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "提供的工单或工单行号全部不正确" : reason;
                return breakData;
            }
            //bool again = false;
            //ReplaceChild(ppbom, mesbom, ref again);
            //if (again)
            //{
            //     ppbom = GetPPBOMEntry(mobuilder, ref reason);
            //}
            //没有成功传入的清单，如果存在要返回给推送方
            List<WPLAN_MN_CHANGE> unpost = new List<WPLAN_MN_CHANGE>();
            //表头转换，并与表体结合...
            List<PRD_PPBOMChange.Entity> newentities = new List<PRD_PPBOMChange.Entity>();
            List<PRD_PPBOMChange> pPBOMs = new List<PRD_PPBOMChange>();
            try
            {
                pPBOMs = GetPPBOMChangeEntities(ppbom, mesbom, ref unpost, ref newentities, ref reason);
            }
            catch (Exception ex)
            {
                breakData.Message = ex.Message;
                return breakData;
            }


            if (pPBOMs != null && string.IsNullOrWhiteSpace(reason))
            {
                //string json=JsonConvert.SerializeObject(pPBOMs);
                //LogHelper.WriteLog(json);
                //批量推送数据模型
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                //提交新的单据
                breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, pPBOMs);
                if (breakData.Status)
                {
                    //传了项次之后ERP不生效，只能直接后台更新
                    if (newentities.Count > 0)
                    {
                        try
                        {
                            await Task.Run(() =>
                            {
                                UpdateReplaceItemRow(newentities);
                            });
                        }
                        catch
                        {
                            throw;
                        }
                    }
                }
            }
            else
            {
                breakData.Message = !string.IsNullOrWhiteSpace(reason) ? reason : "没有找到可变更的需求单据...";
            }
            return breakData;
        }

        /// <summary>
        /// 委外工单需求变更单
        /// </summary>
        /// <param name="data"></param>
        /// <param name="fid"></param>
        /// <param name="postmid"></param>
        /// <returns></returns>
        private async Task<BreakData> SaveBatchSubPPBOMChangeAsync(string data, int fid, int postmid)
        {
            BreakData breakData = new BreakData() { Status = false, DataSet = JsonConvert.DeserializeObject(data) };
            string reason = string.Empty;
            List<WPLAN_MN_CHANGE> mesbom = _shareController.DataConvertClass<WPLAN_MN_CHANGE>(data, ref reason);
            if (mesbom.Count == 0)
            {
                breakData.Message = reason;
                return breakData;
            }
            var errmes = mesbom.Where(s => s.IS_BACK == "Y" && (s.IS_MAIN == "N" || s.MTRL_ID != s.MAIN_ID)).ToList();
            if (errmes.Count != 0)
            {
                breakData.Message = "返还件不能作替代...";
                return breakData;
            }
            var mos = (from a in mesbom
                       select new
                       {
                           a.ERP_ID
                       }).Distinct().ToList();
            List<string> listwhere = new List<string>();
            List<string> billnos = new List<string>();
            foreach (var mo in mos)
            {
                string key = mo.ERP_ID;
                List<string> wherebuilder = new List<string>();
                var martes = (from a in mesbom.Where(s => s.ERP_ID == key)
                              select new
                              {
                                  MAIN_ID = a.MAIN_ID,
                                  MTRL_ID = a.MTRL_ID,
                                  MTRL_Type = a.IS_BACK == "Y" ? 2 : a.IS_MAIN == "Y" ? 1 : 3 //返还件不能做替代，只有标准件才有替代。所以，先判断是否为返还件。
                              }).Distinct().ToList();
                foreach (var marte in martes)
                {
                    string s1 = string.Format("((FMainMaterial=''{0}'' and FMATERIALTYPE=1) OR (FMainMaterial=''{0}''  and FMaterial=''{1}'' and FMATERIALTYPE={2}))", marte.MAIN_ID, marte.MTRL_ID, marte.MTRL_Type);
                    if (!wherebuilder.Contains(s1))
                    {
                        wherebuilder.Add(s1);
                    }
                }
                listwhere.Add(string.Format("(FPPBOMNo=''{0}'' AND ({1}))", key, string.Join(" OR ", wherebuilder.ToArray())));
                billnos.Add(string.Format("(P.FBILLNO=''{0}'')", key));
            }
            //分析有多少工单，拼接成查询语句
            List<PPBOMEntryStr>? ppbom = GetSubPPBOMEntry(listwhere, billnos, ref reason);
            if (ppbom == null || ppbom.Count() == 0)
            {
                breakData.Message = string.IsNullOrEmpty(reason) ? "提供的工单或工单行号全部不正确" : reason;
                return breakData;
            }
            //bool again = false;
            //ReplaceChild(ppbom, mesbom, ref again);
            //if (again)
            //{
            //     ppbom = GetPPBOMEntry(mobuilder, ref reason);
            //}
            //没有成功传入的清单，如果存在要返回给推送方
            List<WPLAN_MN_CHANGE> unpost = new List<WPLAN_MN_CHANGE>();
            //表头转换，并与表体结合...
            List<SUB_PPBOMChange.Entity> newentities = new List<SUB_PPBOMChange.Entity>();
            List<SUB_PPBOMChange> pPBOMs = new List<SUB_PPBOMChange>();
            try
            {
                pPBOMs = GetSUBPPBOMChangeEntities(ppbom, mesbom, ref unpost, ref newentities, ref reason);
            }
            catch (Exception ex)
            {
                breakData.Message = ex.Message;
                return breakData;
            }


            if (pPBOMs.Count() > 0 && string.IsNullOrWhiteSpace(reason))
            {
                //批量推送数据模型
                var dsb = await _shareController.GetAPIList<DataSaveBatch>();
                DataSaveBatch? dataSave = dsb.Where(s => s.GetForm.FFunctionID == fid && s.FDocTypeID == postmid)
                    .FirstOrDefault();
                //提交新的单据
                breakData = await SaveBatchAsync(dataSave, dataSave.GetForm.FDBName, pPBOMs);
                //if (breakData.Status)
                //{
                //直接后台更新替代料相关信息
                if (newentities.Count > 0)
                {
                    try
                    {
                        //Thread.Sleep(1000);
                        await Task.Run(() =>
                        {
                            UpdateSUBReplaceItemRow(newentities, mesbom);
                        });
                    }
                    catch
                    {
                        throw;
                    }

                }
                //LogHelper.WriteLog(string.Format("变更完成，输入JSON：{0}", data));
                //}
            }
            else
            {
                breakData.Message = pPBOMs.Count() > 0 ? reason : "没有找到可变更的需求单据...";
            }
            return breakData;
        }

        private async void UpdateReplaceItemRow(List<PRD_PPBOMChange.Entity> newentities)
        {
            try
            {
                var pnumbers = from a in newentities.Where(s => int.Parse(s.FChangeType) == 1 && !string.IsNullOrWhiteSpace(s.FParentRowId))
                               select new
                               {
                                   FPPBOMNo = a.FPPBOMNo,
                                   FParentRowId = a.FParentRowId,
                                   FReplaceGroup = a.FReplaceGroup
                               };
                foreach (var item in pnumbers)
                {
                    string sql = string.Format("/*dialect*/EXEC rb_UpdatePPBOMEntryReplaceItem '{0}','{1}','{2}','PRD'", item.FPPBOMNo, item.FParentRowId, item.FReplaceGroup);
                    string costomsql = ApiSettingsHelper.GetConfig("CustomRequestRemoteSql");
                    if (costomsql.ToUpper() == "Y")
                    {
                        await _chiledselect.GetCustomRequestTableAsync(sql, "", "", null);
                    }
                    else
                    {
                        string reason = await _kdsqlhelper.ExecuteNonQueryAsync(sql, CommandType.Text, null, true);
                    }
                }
            }
            catch (Exception ex)
            {
                //写入本地日志
                string entry = JsonConvert.SerializeObject(newentities);
                LogHelper.WriteLog(string.Format("变更单：{0}，更新主替代关系时发生错误：{1}", entry, ex.Message));
                //throw;
            }

        }
        private async void UpdateSUBReplaceItemRow(List<SUB_PPBOMChange.Entity> newentities, List<WPLAN_MN_CHANGE> mesbom)
        {
            try
            {
                var pnumbers = from a in newentities.Where(s => int.Parse(s.FChangeType) == 1 && !string.IsNullOrWhiteSpace(s.FParentRowId))
                               select new
                               {
                                   FPPBOMNo = a.FSUBPPBOMNo,
                                   FParentRowId = a.FParentRowId,
                                   FReplaceGroup = a.FReplaceGroup
                               };
                foreach (var item in pnumbers)
                {
                    string sql = string.Format("/*dialect*/EXEC rb_UpdatePPBOMEntryReplaceItem '{0}','{1}','{2}','SUB'", item.FPPBOMNo, item.FParentRowId, item.FReplaceGroup);
                    string costomsql = ApiSettingsHelper.GetConfig("CustomRequestRemoteSql");
                    if (costomsql.ToUpper() == "Y")
                    {
                        await _chiledselect.GetCustomRequestTableAsync(sql, "", "", null);
                    }
                    else
                    {
                        string reason = await _kdsqlhelper.ExecuteNonQueryAsync(sql, CommandType.Text, null, true);
                    }
                }
            }
            catch (Exception ex)
            {
                //写入本地日志
                string entry = JsonConvert.SerializeObject(newentities);
                LogHelper.WriteLog(string.Format("变更单：{0}，更新主替代关系时发生错误：{1}", entry, ex.Message));
                //throw;
            }
        }
        private async Task<BreakData> RemoveBillsAsync(string formid, List<string> billNolist)
        {
            if (!LocalStaticRequest.Islogin)
            {
                await _KdhttpClient.LoginErp();
            }
            BreakData breakData = new BreakData() { Status = false };
            if (!LocalStaticRequest.Islogin)
            {
                breakData.Status = false;
                breakData.Message = "KdhttpClien登录失败...";
                return breakData;
            }
            //从云星空查询
            RemoveAPIData data = new RemoveAPIData() { Numbers = billNolist.ToArray() };
            string jsonstr = JsonConvert.SerializeObject(data);
            string result = LocalStaticRequest.cloudApi.Delete(formid, jsonstr);
            //对返回结果进行解析和校验，
            var resultJObject = JObject.Parse(result);
            var queryNode = resultJObject.SelectToken("$..IsSuccess");
            //判断并记录结果
            if (queryNode == null)
            {
                breakData.Status = false;
                breakData.Message = "发生异常，但未提供任何有用的消息...";
            }
            else
            {
                bool isSuccess = queryNode.Value<bool>();
                breakData.Status = isSuccess;
                //如果访问正常
                if (!isSuccess)
                {
                    JToken reErrors = resultJObject.SelectToken("$..Errors");
                    var remess = JsonConvert.SerializeObject(reErrors);
                    breakData.Message = (isSuccess ? "" : remess);
                }
            }

            return breakData;
        }

        //通过查询服务执行金蝶数据库查询
        private string GetCustomRequestTableAsync(string sql, string filestr, string ordstr,
            List<Dictionary<string, object>> inputdic, ref string reason)
        {
            reason = string.Empty;
            string tbjson = string.Empty;
            List<string> wheres = new List<string>();
            filestr = filestr.Replace("GETDATE", DateTime.Now.ToShortDateString());
            if (inputdic != null)
            {
                foreach (Dictionary<string, object> dic in inputdic)
                {
                    string wherestr = filestr;
                    foreach (KeyValuePair<string, object> keyValue in dic)
                    {
                        string key = keyValue.Key;
                        string value = keyValue.Value.ToString();
                        //在SQL查询脚本中必须有key，并且必须以@开头，否则传入的参数无效
                        wherestr = wherestr.Replace(key, value);
                    }
                    wheres.Add(wherestr);
                }
            }
            if (wheres.Count != 0) { sql += string.Format(" AND (({0}))", string.Join(") or (", wheres.ToArray())); }      //前面一定要有WHERE 
            if (!string.IsNullOrWhiteSpace(ordstr) && ordstr != "") { sql += string.Format("\r\n ORDER BY {0}", ordstr); }

            string head = _chiledselect.GetCustomRequestBillAsync(sql, "", "", null).Result;
            if (!string.IsNullOrWhiteSpace(head))
            {
                tbjson = head;
            }
            else
            {
                reason = "调用沃比子查询服务（GetCustomRequestTableAsync）出错，请查询接口日志";
            }
            return tbjson;
        }

        #region 推送操作检测
        /// <summary>
        /// 用于检测是否已经推送过或者正在推送中
        /// </summary>
        /// <param name="docType"></param>
        /// <param name="data"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private bool CheckQUEUEIDHasPut(string docType, string data, ref string reason)
        {
            var resFlag = true;
            try
            {
                var fm_all = GetAllFormIDTypeAsync(docType);
                var fm_post = fm_all.FirstOrDefault(f => f.functions.FUsing && f.FActionName == "PostAsync" && f.FDocType == docType);

                var key = "QUEUE_ID";
                string pattern = $"\"{key}\"\\s*:\\s*\"(.*?)\"";
                Match match = Regex.Match(data, pattern, RegexOptions.IgnoreCase);
                //int matchCount = 0;
                List<string> queueIds = new List<string>();
                while (match.Success)
                {
                    var queueId = match.Groups[1].Value.Trim();
                    if (queueId.IsNullOrEmpty())
                    {
                        throw new Exception("QUEUE_ID取不到值，请检查数据结构是否正确!");
                    }
                    else if (!queueIds.Contains(queueId))
                    {
                        queueIds.Add(queueId);
                    }

                    match = match.NextMatch();
                }

                if (queueIds.Any() && fm_post != null)
                {
                    var dataList = _context.r_ERPPutOperationAnchor.Where(w => w.FDocType == docType && queueIds.Contains(w.QUEUE_ID)).ToList();

                    if (dataList != null && dataList.Any())
                    {
                        //已推送过
                        var data1 = dataList.Where(w => w.FOperation == 1).ToList();
                        if (data1.Any())
                        {
                            resFlag = false;
                            foreach (var item in data1)
                            {
                                reason += $"类型：{docType}，MES队列ID:{item.QUEUE_ID},不可重复推送！\r\n ";
                            }
                        }
                        //已推送未获取最终结果
                        var data0 = dataList.Where(w => w.FOperation == 0).ToList();
                        if (data0.Any())
                        {
                            List<SqlParameter> parameters = new List<SqlParameter>() { new SqlParameter("@defaultorgid", LocalStaticRequest.DefaultOrgID) };    //后台配置默认组织必须是这个参数
                            var sqlL = fm_post.FNeedBreakSQL;
                            var colname = fm_post.FGuidColName;
                            var queueIdParams = new List<string>();
                            foreach (var item in data0)
                            {
                                queueIdParams.Add($"{docType}:{item.QUEUE_ID}");
                            }
                            sqlL = string.Format(sqlL, $"'{string.Join("','", queueIdParams)}'");
                            DataSet dataSet = _kdsqlhelper.GetDataSet(CommandType.Text, sqlL, parameters.ToArray(), ref reason, true);

                            if (dataSet != null)
                            {
                                if (dataSet.Tables.Count > 0)
                                {
                                    DataTable dataTable = dataSet.Tables[0];
                                    if (dataTable.Rows.Count > 0)
                                    {
                                        var toUpdQueueIds = new List<string>();
                                        foreach (DataRow row in dataTable.Rows)
                                        {
                                            var queue_id_str = row[colname].ToString();
                                            var queueId = queue_id_str.Split(":")[1];
                                            var item = data0.FirstOrDefault(w => w.QUEUE_ID == queueId);
                                            if (item != null)
                                            {
                                                resFlag = false;
                                                item.FErpBillNo = row["FBILLNO"].ToString();
                                                item.FOperation = 1;
                                                reason += $"类型：{docType}，MES队列ID:{item.QUEUE_ID},重复推送！\r\n ";
                                            }
                                        }

                                        _context.r_ERPPutOperationAnchor.UpdateRange(data0);
                                        _context.SaveChanges();
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        _context.r_ERPPutOperationAnchor.AddRange(queueIds.Select(x => new ERPPutOperationAnchor
                        {
                            QUEUE_ID = x,
                            FErpBillNo = "",
                            FDocType = docType,
                            FOperation = 0,
                            FActionName = "DataSave_Batch",
                        }));
                        _context.SaveChanges();
                    }
                }
            }
            catch (Exception ex)
            {
                reason = ex.Message;
                resFlag = false;
            }

            return resFlag;
        }
        #endregion 推送操作检测

        private List<STK_TransferDirect> CreateBatchSTKTransferDirects(List<R011> R011cs, string billtype, ref string reason)
        {
            reason = "";
            List<STK_TransferDirect> list = new List<STK_TransferDirect>();
            var R011 = (from a in R011cs
                        group a by a.TRX_DATE into b
                        select b).Distinct().ToList();

            List<BD_UNIT> units = _shareController.GetAPIList<BD_UNIT>().Result;
            foreach (var head in R011)
            {
                //创建单据头
                STK_TransferDirect inStock = new()
                {
                    FBillTypeID = new Dictionary<string, string>() { { "FNUMBER", billtype } },
                    FDate = head.Key,
                    FOwnerOutIdHead = new Dictionary<string, string>() { { "FNUMBER", LocalStaticRequest.DefaultOrg } },
                    F_SBMI_QUEUE_ID = string.Format("R011:{0}", R011cs.FirstOrDefault().QUEUE_ID) //如果不是盘古，建议放在单据体！！！
                };


                //收集物料清单，一起查询
                var mary = from a in R011cs select a.MTRL_ID;
                List<BD_Material> marylist = _chiledselect.materialsAsync(mary.ToList()).Result;
                if (marylist.Count == 0)
                {
                    reason = "输入的物料编码不存在";
                    break;
                }

                // 先进行分组汇总，再构建单据体
                var groupedData = (from b in R011cs
                                   join c in marylist on b.MTRL_ID equals c.FNumber
                                   join d in units on c.FStoreUnitID equals d.FUNITID
                                   join e in units on c.FBaseUnitId equals e.FUNITID
                                   group new { b, c, d, e } by new
                                   {
                                       b.MTRL_ID,
                                       b.ERP_LOTNO,
                                       b.STORE_ID,
                                       b.TOSTORE_ID,
                                       c.FUnitNumber,
                                       c.FBaseUnitNumber,
                                       b.REMARK,
                                       b.MTRL_VER,
                                       d.FConvertNumerator,
                                       d.FConvertDenominator,
                                       d.FPrecision
                                   } into g
                                   select new
                                   {
                                       MTRL_ID = g.Key.MTRL_ID,
                                       ERP_LOTNO = g.Key.ERP_LOTNO,
                                       STORE_ID = g.Key.STORE_ID,
                                       TOSTORE_ID = g.Key.TOSTORE_ID,
                                       FUnitNumber = g.Key.FUnitNumber,
                                       FBaseUnitNumber = g.Key.FBaseUnitNumber,
                                       REMARK = g.Key.REMARK,
                                       MTRL_VER = g.Key.MTRL_VER,
                                       FConvertNumerator = g.Key.FConvertNumerator,
                                       FConvertDenominator = g.Key.FConvertDenominator,
                                       FPrecision = g.Key.FPrecision,
                                       // 累加数量
                                       TotalQuantity = g.Sum(x => SafeParseDecimal(x.b.QUANTITY)),
                                       // 取第一条记录的物料信息（假设相同物料的其他信息一致）
                                       MaterialInfo = g.First().c,
                                       UnitInfo = g.First().d
                                   }).ToList();

                // 构建单据体
                List<STK_TransferDirect.BillEntry> entries = groupedData.Select(g =>
                {
                    var quantity = g.TotalQuantity;
                    var conversionRate = (g.FConvertDenominator == 0) ? 1 : g.FConvertNumerator / g.FConvertDenominator;
                    var baseQty = Math.Round(quantity / conversionRate, g.FPrecision);

                    return new STK_TransferDirect.BillEntry
                    {
                        FMaterialId = CreateDictionary("FNumber", g.MTRL_ID),
                        FNoteEntry = g.REMARK,
                        FLot = CreateDictionary("FNumber", g.ERP_LOTNO),
                        FDestLot = CreateDictionary("FNumber", g.ERP_LOTNO),
                        FQty = quantity,
                        FDestStockId = CreateDictionary("FNumber", g.TOSTORE_ID),
                        FUnitID = CreateDictionary("FNumber", g.FUnitNumber),
                        FBaseUnitId = CreateDictionary("FNumber", g.FBaseUnitNumber),
                        FSrcStockId = CreateDictionary("FNumber", g.STORE_ID),
                        FSaleQty = quantity,
                        FSaleUnitId = CreateDictionary("FNumber", g.FUnitNumber),
                        FActQty = quantity,
                        FSalBaseQty = baseQty,
                        FBaseQty = baseQty,
                        FOwnerOutId = CreateDictionary("FNumber", LocalStaticRequest.DefaultOrg),
                        FAuxPropId = string.IsNullOrEmpty(g.MTRL_VER) ? null :
                            new STK_TransferDirect.BillEntry.AuxpropId()
                            {
                                FAUXPROPID__FF100001 = CreateDictionary("FNumber", g.MTRL_VER)
                            }
                    };
                }).ToList();

                ////添加单据体，按订单号找到它的明细数据                
                //List<STK_TransferDirect.BillEntry> entries = (from a in head
                //                                              join b in R011cs on a.TRX_DATE equals b.TRX_DATE
                //                                              join c in marylist on b.MTRL_ID equals c.FNumber
                //                                              join d in units on c.FStoreUnitID equals d.FUNITID
                //                                              join e in units on c.FBaseUnitId equals e.FUNITID
                //                                              select new STK_TransferDirect.BillEntry
                //                                              {
                //                                                  FMaterialId = new Dictionary<string, string>() { { "FNumber", b.MTRL_ID } },
                //                                                  FNoteEntry = b.REMARK,
                //                                                  FLot = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                //                                                  FDestLot = new Dictionary<string, string>() { { "FNumber", b.ERP_LOTNO } },
                //                                                  FQty = decimal.Parse(b.QUANTITY),
                //                                                  FDestStockId = new Dictionary<string, string>() { { "FNumber", b.TOSTORE_ID } },
                //                                                  FUnitID = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                //                                                  FBaseUnitId = new Dictionary<string, string>() { { "FNumber", c.FBaseUnitNumber } },
                //                                                  FSrcStockId = new Dictionary<string, string>() { { "FNumber", b.STORE_ID } },
                //                                                  FSaleQty = decimal.Parse(b.QUANTITY),
                //                                                  FSaleUnitId = new Dictionary<string, string>() { { "FNumber", c.FUnitNumber } },
                //                                                  FActQty = decimal.Parse(b.QUANTITY),
                //                                                  FSalBaseQty = Math.Round(decimal.Parse(b.QUANTITY) / (d.FConvertNumerator / d.FConvertDenominator), d.FPrecision),
                //                                                  FBaseQty = Math.Round(decimal.Parse(b.QUANTITY) / (d.FConvertNumerator / d.FConvertDenominator), d.FPrecision),
                //                                                  FOwnerOutId = new Dictionary<string, string>() { { "FNumber", LocalStaticRequest.DefaultOrg } },
                //                                                  FAuxPropId = string.IsNullOrEmpty(b.MTRL_VER) ? null : new STK_TransferDirect.BillEntry.AuxpropId() { FAUXPROPID__FF100001 = new Dictionary<string, string>() { { "FNumber", b.MTRL_VER } } }
                //                                              }
                //                                         ).Distinct().ToList()
                //                                         ;
                var q = entries.Where(s => s.FQty <= 0).ToList();
                if (q.Count > 0)
                {
                    reason = "入库单数量必须大于0";
                    break;
                }
                string s = JsonConvert.SerializeObject(entries);
                inStock.FBillEntry = entries;
                //财务费用，隶属于单据体
                list.Add(inStock);

            }
            ////先将返回数据格式化
            //string jobj = JsonConvert.SerializeObject(list);
            //LogHelper.WriteLog("直接调拨单的合并数据处理结果："+ jobj);
            return list;
        }

        // 安全解析decimal的辅助方法
        private decimal SafeParseDecimal(string value)
        {
            if (string.IsNullOrEmpty(value))
                return 0m;

            return decimal.TryParse(value, out decimal result) ? result : 0m;
        }

        // 创建字典的辅助方法
        private Dictionary<string, string> CreateDictionary(string key, string value)
        {
            return new Dictionary<string, string> { { key, value ?? string.Empty } };
        }
    }
}
