Просмотр исходного кода

Merge branch 'dev' of http://123.60.180.165:4647/ZZYDOP/DOPCore into dev

Murphy 2 лет назад
Родитель
Сommit
4bb1b509d7

+ 49 - 0
MicroServices/Business/Business.Application.Contracts/Dto/WorkOrdMstDto.cs

@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Business.Dto
+{
+    /// <summary>
+    /// 特殊工单排产Dto
+    /// </summary>
+    public class WorkOrdMstDto
+    {
+        /// <summary>
+        /// 工单编号
+        /// </summary>
+        public string WorkOrd { get; set; }
+
+        /// <summary>
+        /// 物料编号
+        /// </summary>
+        public string ItemNum { get; set; }
+
+        /// <summary>
+        /// 工单数量
+        /// </summary>
+        public decimal QtyOrded { get; set; }
+
+        /// <summary>
+        /// 工单生产时长(分钟)
+        /// </summary>
+        public decimal LbrVar { get; set; }
+
+        /// <summary>
+        /// 已排产时长(分钟)
+        /// </summary>
+        public decimal Worked { get; set; }
+
+        /// <summary>
+        /// 已排产数量
+        /// </summary>
+        public decimal QtyWorked { get; set; }
+
+        /// <summary>
+        /// 工序
+        /// </summary>
+        public int Op { get; set; }
+    }
+}

+ 1 - 1
MicroServices/Business/Business.Application.Contracts/SystemJob/ISystemJobAppService.cs

@@ -36,6 +36,6 @@ namespace Business.SystemJob
         /// <returns></returns>
         Task<string> SyncBaseDataToMongoDBJob();
 
-        string DoProductShceduleJob();
+        string DoProductScheduleJob();
     }
 }

+ 544 - 142
MicroServices/Business/Business.Application/ResourceExamineManagement/ProductionScheduleAppService.cs

@@ -16,6 +16,8 @@ using Volo.Abp.Application.Services;
 using Volo.Abp.MultiTenancy;
 using Microsoft.Extensions.Configuration;
 using MongoDB.Driver.Linq;
+using Amazon.Runtime.Internal.Util;
+using IdentityModel.Client;
 
 namespace Business.ResourceExamineManagement
 {
@@ -74,23 +76,32 @@ namespace Business.ResourceExamineManagement
         /// 产线休息时间记录表
         /// </summary>
         private ISqlRepository<QualityLineWorkDetail> _qualityLineWorkDetail;
+
         /// <summary>
         /// 产线人员配置表
         /// </summary>
         private ISqlRepository<ProdLineDetailRunCrew> _prodLineDetailRunCrew;
+
         /// <summary>
         /// 加班设置表
         /// </summary>
         private ISqlRepository<ResourceOccupancyTime> _resourceOccupancyTime;
+
         /// <summary>
         /// 加班设置表
         /// </summary>
         private ISqlRepository<GeneralizedCodeMaster> _generalizedCodeMaster;
+
         /// <summary>
         /// 节假日记录表
         /// </summary>
         private ISqlRepository<HolidayMaster> _holidayMaster;
 
+        /// <summary>
+        /// 排产异常记录
+        /// </summary>
+        private ISqlRepository<ScheduleExceptionMaster> _scheduleExceptionMaster;
+
         /// <summary>
         /// 雪花算法
         /// </summary>
@@ -148,7 +159,8 @@ namespace Business.ResourceExamineManagement
             ISqlRepository<QualityLineWorkDetail> qualityLineWorkDetail,
             ISqlRepository<HolidayMaster> holidayMaster,
             ISqlRepository<GeneralizedCodeMaster> generalizedCodeMaster,
-            ICurrentTenant currentTenant
+            ICurrentTenant currentTenant,
+            ISqlRepository<ScheduleExceptionMaster> scheduleExceptionMaster
             )
         {
             _itemMaster = itemMaster;
@@ -166,11 +178,12 @@ namespace Business.ResourceExamineManagement
             _holidayMaster = holidayMaster;
             _generalizedCodeMaster = generalizedCodeMaster;
             _currentTenant = currentTenant;
+            _scheduleExceptionMaster = scheduleExceptionMaster;
         }
         #endregion
 
         /// <summary>
-        /// 执行生产排产
+        /// 生产排产-定时任务
         /// </summary>
         public async void DoExt()
         {
@@ -178,27 +191,25 @@ namespace Business.ResourceExamineManagement
             IConfiguration configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();
             domain = configuration.GetConnectionString("Factory_id");
 
-            //获取提前期
-            var generalizedCodeMaster = _generalizedCodeMaster.Select(x => x.FldName == "SystemConfig" && x.Val == "WorkOrderLockPeriod" && x.Domain == domain).FirstOrDefault();
-            decimal Udecil = 0;
-            if (generalizedCodeMaster != null)
-            {
-                Udecil = generalizedCodeMaster.UDeci1;
-            }
-            //排产取4周工单排产
-            DateTime dateTime = DateTime.Now.AddDays(30);
-            DateTime date = DateTime.Now.AddDays((double)Udecil);
-            var workOrds = _workOrdMaster.Select(x => x.IsActive && x.Domain == domain && x.OrdDate < dateTime && x.OrdDate > date && x.Status == "初始").ToList();
-            await DoProductShcedule(workOrds, domain);
+            //获取排产锁定期
+            var generalizedCodeMaster = _generalizedCodeMaster.Select(p => p.FldName == "SystemConfig" && p.Val == "WorkOrderLockPeriod" && p.Domain == domain && p.IsActive).FirstOrDefault();
+            decimal lockDays = generalizedCodeMaster != null ? generalizedCodeMaster.UDeci1 : 0;
+            
+            //获取需要排产的工单(获取四周的工单:正常工单+已审批通过的特殊工单)
+            DateTime endDate = DateTime.Now.Date.AddWeeks(4).AddDays(1);
+            DateTime startDate = DateTime.Now.Date;
+            var workOrds = _workOrdMaster.Select(p => p.IsActive && p.Domain == domain && p.OrdDate < endDate && p.OrdDate >= startDate && string.IsNullOrEmpty(p.Status) && (string.IsNullOrEmpty(p.Typed) || (!string.IsNullOrEmpty(p.Typed) && p.BusinessID >0 ))).ToList();
+            await DoProductSchedule(workOrds, domain, 1);
         }
 
         /// <summary>
         /// 生产排产
         /// </summary>
-        /// <param name="workOrds">工单:定时任务执行时count=0;资源检查调用count>0</param>
+        /// <param name="workOrds">工单</param>
         /// <param name="factoryid">工单的工厂id</param>
+        /// <param name="type">排产类型:1-自动排产;2-手动排产</param>
         /// <returns></returns>
-        public async Task DoProductShcedule(List<WorkOrdMaster> workOrds, string factoryid)
+        public async Task DoProductSchedule(List<WorkOrdMaster> workOrds, string factoryid,int type)
         {
             //记录工厂id
             domain = factoryid;
@@ -209,19 +220,26 @@ namespace Business.ResourceExamineManagement
             //获取排产工单的最早计划开工日期
             DateTime earlist = DateTime.Now.Date.AddDays(1);
 
+            //特殊工单指定时间开工,不需要计算产能,只需要计算时长即可
+            var tsWorkOrds = workOrds.Where(p => !string.IsNullOrEmpty(p.Typed)).ToList();
+            //正常工单
+            var zcWorkOrds = workOrds.Where(p => string.IsNullOrEmpty(p.Typed)).ToList();
+
             //2、获取数据
             //获取工单工艺路径数据
             List<WorkOrdRouting> workOrdRoutings = _workOrdRouting.Select(p => workOrds.Select(m => m.WorkOrd).Contains(p.WorkOrd) && p.ParentOp == 0 && p.Domain == domain && p.Status != "C" && p.IsActive);
+            //特殊工单的工艺路线
+            List<WorkOrdRouting> tsWoRoutings = workOrdRoutings.Where(p => tsWorkOrds.Select(m => m.WorkOrd).Contains(p.WorkOrd)).ToList();
             //获取物料对应的生产线信息:物料、工序对应的生产线
             List<ProdLineDetail> prodLines = _prodLineDetail.Select(p => workOrds.Select(m => m.ItemNum).Contains(p.Part) && p.Domain == domain && p.IsActive);
             List<string> lines = prodLines.Select(p => p.Line).ToList();
             //获取非标准产线人员配置
             prodLineDetailRunCrews = _prodLineDetailRunCrew.Select(x => prodLines.Select(p => p.RecID).Contains(x.ProdLineDetailRecID) && x.IsActive && x.Domain == domain).ToList();
             //获取加班设置
-            List<ResourceOccupancyTime> resourceOccupancyTimes = _resourceOccupancyTime.Select(x => prodLines.Select(p => p.Line).Contains(x.Resource) && x.IsActive && x.Domain == domain && x.StartTime.Value > DateTime.Now).ToList();
-            //获取生产周期数据
+            resourceOccupancyTimes = _resourceOccupancyTime.Select(x => prodLines.Select(p => p.Line).Contains(x.Resource) && x.IsActive && x.Domain == domain && x.StartTime.Value > DateTime.Now).ToList();
+            //获取生产周期数据,TODO:需要过滤锁定期之外的工单
             List<PeriodSequenceDet> dbPeriodSequences = _periodSequenceDet.Select(p => lines.Contains(p.Line) && p.PlanDate >= earlist && p.Domain == domain && p.IsActive);
-            //获取当前日期往后的排产记录数据
+            //获取当前日期往后的排产记录数据,TODO:需要过滤锁定期之外的工单
             List<ScheduleResultOpMaster> dbSchedules = _scheduleResultOpMaster.Select(p => lines.Contains(p.Line) && p.WorkDate >= earlist && p.Domain == domain);
             //获取工作日历数据:产线的工作日历+默认的工作日历
             calendars = _shopCalendarWorkCtr.Select(p => (lines.Contains(p.ProdLine) || string.IsNullOrEmpty(p.ProdLine)) && p.Domain == domain && p.IsActive);
@@ -230,6 +248,27 @@ namespace Business.ResourceExamineManagement
             //获取节假日记录数据
             holidays = _holidayMaster.Select(p => p.Domain == domain && p.IsActive && p.Dated >= earlist);
 
+            //排产前校验
+            List<ScheduleExceptionMaster> exceptions = BeforeScheduleCheck(workOrds, workOrdRoutings, prodLines,type);
+            if (exceptions.Any())
+            {
+                using (TransactionScope scope = new TransactionScope())
+                {
+                    try
+                    {
+                        //记录排产异常数据
+                        _scheduleExceptionMaster.Insert(exceptions);
+                        scope.Complete();
+                    }
+                    catch (Exception ex)
+                    {
+                        new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductSchedule", "保存排产异常数据失败:" + ex.Message, _currentTenant.Id.ToString());
+                        scope.Dispose();
+                    }
+                }
+                return;
+            }
+            
             //3、排产
             //生产周期
             List<PeriodSequenceDet> periodSequenceDtls = new List<PeriodSequenceDet>();
@@ -238,7 +277,9 @@ namespace Business.ResourceExamineManagement
             //排产结果
             List<ScheduleResultOpMaster> allResults = new List<ScheduleResultOpMaster>();
             allResults.AddRange(dbSchedules);
-            foreach (var item in workOrds)
+            //记录特殊工单排产结果
+            List<string> ypcWorkOrds = new List<string>();
+            foreach (var item in zcWorkOrds)
             {
                 //记录产线占用情况
                 allResults.AddRange(scheduleMasters);
@@ -247,55 +288,8 @@ namespace Business.ResourceExamineManagement
                 //当前工单的产线明细
                 var curProdLines = prodLines.Where(p => p.Part == item.ItemNum).ToList();
                 
-                #region 校验
-                //工单工艺多产线关键工序、物料对应的生产线信息:物料,工序对应的生产线、工作日历数据
-                if (curRoutings.Count == 0)
-                {
-                    //记录排产异常原因
-                    new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的工单工艺流程数据维护为空", _currentTenant.Id.ToString());
-                    continue;
-                }
-                //TODO:多个关键工序校验
-
-                //物料对应生产线校验
-                var ProdLineDetails = prodLines.Where(x => x.Part == item.ItemNum).ToList();
-                if (ProdLineDetails.Count == 0)
-                {
-                    //记录排产异常原因
-                    new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的生产线物料数据维护为空", _currentTenant.Id.ToString());
-                    continue;
-                }
-                var calendarsList = calendars.Where(x => ProdLineDetails.Select(p => p.Line).Contains(x.ProdLine) || string.IsNullOrEmpty(x.ProdLine)).ToList();
-                //校验每个层级是否维护了工作日历
-                bool flag = false;
-                foreach (var rut in ProdLineDetails)
-                {
-                    var lineCals = calendarsList.Where(p => p.ProdLine == rut.Line).ToList();
-                    //当前产线未配置工作日历取标准无产线工作日历使用
-                    if (lineCals.Count == 0)
-                    {
-                        lineCals = calendars.Where(p => string.IsNullOrEmpty(p.ProdLine)).ToList();
-                        if (lineCals.Count == 0)
-                        {
-                            break;
-                        }
-                    }
-                    if (lineCals.Select(p => p.WeekDay).Distinct().Count() != 7)
-                    {
-                        flag = true;
-                        break;
-                    }
-                }
-                if (flag)
-                {
-                    //记录排产异常原因
-                    new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "工单<" + item.WorkOrd + ">的<" + item.ProdLine + ">工作日历数据维护错误", _currentTenant.Id.ToString());
-                    continue;
-                }
-                #endregion
-
                 //产线排产
-                LineSchedule(item, curRoutings, curProdLines, periodSequenceDtls, scheduleMasters, allResults);
+                LineSchedule(item, curRoutings, curProdLines, periodSequenceDtls, scheduleMasters, allResults, tsWorkOrds, ypcWorkOrds, tsWoRoutings);
 
                 List<ScheduleResultOpMaster> scheduleList = scheduleMasters.Where(s => s.WorkOrd == item.WorkOrd).ToList();
                 if (scheduleList.Any())
@@ -316,11 +310,148 @@ namespace Business.ResourceExamineManagement
                 }
                 catch (Exception ex)
                 {
-                    new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductShcedule", "记录排产数据失败:" + ex.Message, _currentTenant.Id.ToString());
+                    new NLogHelper("ProductionScheduleAppService").WriteLog("DoProductSchedule", "保存排产数据失败:" + ex.Message, _currentTenant.Id.ToString());
                     scope.Dispose();
                 }
             }
+        }
 
+        /// <summary>
+        /// 排产前校验
+        /// </summary>
+        /// <param name="workOrds">待排产工单</param>
+        /// <param name="workOrdRoutings">工单工艺路线</param>
+        /// <param name="prodLines">产线明细</param>
+        /// <param name="type">排产类型:1-自动排产,2-手动排产</param>
+        /// <returns></returns>
+        public List<ScheduleExceptionMaster> BeforeScheduleCheck(List<WorkOrdMaster> workOrds,List<WorkOrdRouting> workOrdRoutings, List<ProdLineDetail> prodLines,int type) 
+        {
+            List<ScheduleExceptionMaster> exceptions = new List<ScheduleExceptionMaster>();
+            ScheduleExceptionMaster entity;
+            foreach (var item in workOrds)
+            {
+                if (!string.IsNullOrEmpty(item.Typed))//特殊工单
+                {
+                    //只需要校验有没有维护工艺路线即可
+                    var routings = workOrdRoutings.Where(p=>p.ItemNum == item.ItemNum).ToList();
+                    if (!routings.Any())
+                    {
+                        entity = new ScheduleExceptionMaster();
+                        entity.Domain = item.Domain;
+                        entity.WorkOrd = item.WorkOrd;
+                        entity.ItemNum = item.ItemNum;
+                        entity.CreateTime = DateTime.Now;
+                        entity.Remark = "排产异常:工单没有维护工艺路线,请维护后再操作!";
+                        entity.Type = type == 1 ? "自动排产" : "手动排产";
+                        exceptions.Add(entity);
+                    }
+                    continue;
+                }
+                var curRoutings = workOrdRoutings.Where(p => p.ItemNum == item.ItemNum && p.MilestoneOp).OrderBy(p=>p.OP).Select(p=>p.OP).ToList();
+                //判断当前工单主产线是否设置了关键工序
+                if (!curRoutings.Any())//当前工单没有维护关键工序
+                {
+                    entity = new ScheduleExceptionMaster();
+                    entity.Domain = item.Domain;
+                    entity.WorkOrd = item.WorkOrd;
+                    entity.ItemNum = item.ItemNum;
+                    entity.CreateTime = DateTime.Now;
+                    entity.Remark = "排产异常:工单的工艺路径没有维护关键工序,请维护后再操作!";
+                    entity.Type = type == 1 ? "自动排产" : "手动排产";
+                    exceptions.Add(entity);
+                    continue;
+                }
+                //获取当前工单物料对应的产线信息
+                var lineDetails = prodLines.Where(x => x.Part == item.ItemNum && curRoutings.Contains(x.Op)).ToList();
+                if (!lineDetails.Any())
+                {
+                    entity = new ScheduleExceptionMaster();
+                    entity.Domain = item.Domain;
+                    entity.WorkOrd = item.WorkOrd;
+                    entity.ItemNum = item.ItemNum;
+                    entity.CreateTime = DateTime.Now;
+                    entity.Remark = "排产异常:工单的关键工序没有维护产线数据,请维护后再操作!";
+                    entity.Type = type == 1 ? "自动排产" : "手动排产";
+                    exceptions.Add(entity);
+                    continue;
+                }
+                //校验关键工序是否维护了产线
+                List<LineStartDto> lines = new List<LineStartDto>();
+                foreach (var op in curRoutings)
+                {
+                    var curLines = lineDetails.Where(p => p.Op == op).ToList();
+                    if (!curLines.Any())//当前Op没有维护产线
+                    {
+                        entity = new ScheduleExceptionMaster();
+                        entity.Domain = item.Domain;
+                        entity.WorkOrd = item.WorkOrd;
+                        entity.ItemNum = item.ItemNum;
+                        entity.CreateTime = DateTime.Now;
+                        entity.Remark = "排产异常:工单的关键工序["+ op + "]没有维护产线数据,请维护后再操作!";
+                        entity.Type = type == 1 ? "自动排产" : "手动排产";
+                        exceptions.Add(entity);
+                    }
+                    foreach (var line in curLines)
+                    {
+                        lines.Add(new LineStartDto { 
+                            Line = line.Line,
+                            Op = op
+                        });
+                    }
+                }
+                //校验一条产线是否维护了多个关键工序
+                List<string> pdLines = lines.Select(p => p.Line).Distinct().ToList();
+                foreach (var line in pdLines)
+                {
+                    var curLines = lines.Where(p => p.Line == line).ToList();
+                    if (curLines.Count() > 1)//当前物料的多个关键工序对应一条产线
+                    {
+                        entity = new ScheduleExceptionMaster();
+                        entity.Domain = item.Domain;
+                        entity.WorkOrd = item.WorkOrd;
+                        entity.ItemNum = item.ItemNum;
+                        entity.CreateTime = DateTime.Now;
+                        entity.Remark = "排产异常:工单的多个关键工序[" + string.Join("、",curLines.Select(p=>p.Op).ToList()) + "]对应同一条产线,请调整后再操作!";
+                        entity.Type = type == 1 ? "自动排产" : "手动排产";
+                        exceptions.Add(entity);
+                    }
+                }
+                
+                //校验主产线关键工序对应的产线是否维护了工作日历
+                var curCalendars = calendars.Where(x => lineDetails.Select(p => p.Line).Contains(x.ProdLine) || string.IsNullOrEmpty(x.ProdLine)).ToList();
+                foreach (var rut in lineDetails)
+                {
+                    //获取当前产线维护的工作日历
+                    var lineCals = curCalendars.Where(p => p.ProdLine == rut.Line).ToList();
+                    if (!lineCals.Any())//当前产线未维护工作日历
+                    {
+                        lineCals = calendars.Where(p => string.IsNullOrEmpty(p.ProdLine)).ToList();
+                        if (!lineCals.Any() || lineCals.Select(p => p.WeekDay).Distinct().Count() != 7)
+                        {
+                            entity = new ScheduleExceptionMaster();
+                            entity.Domain = item.Domain;
+                            entity.WorkOrd = item.WorkOrd;
+                            entity.ItemNum = item.ItemNum;
+                            entity.CreateTime = DateTime.Now;
+                            entity.Remark = "排产异常:产线["+rut.Line+ "]没有维护工作日历且标准工作日历未维护完全,请维护后再操作!";
+                            entity.Type = type == 1 ? "自动排产" : "手动排产";
+                            exceptions.Add(entity);
+                        }
+                    }
+                    else if (lineCals.Select(p => p.WeekDay).Distinct().Count() != 7)//当前产线维护了工作日历,但是没有维护完全
+                    {
+                        entity = new ScheduleExceptionMaster();
+                        entity.Domain = item.Domain;
+                        entity.WorkOrd = item.WorkOrd;
+                        entity.ItemNum = item.ItemNum;
+                        entity.CreateTime = DateTime.Now;
+                        entity.Remark = "排产异常:产线[" + rut.Line + "]工作日历没有维护完整,请维护后再操作!";
+                        entity.Type = type == 1 ? "自动排产" : "手动排产";
+                        exceptions.Add(entity);
+                    }
+                }
+            }
+            return exceptions;
         }
 
         /// <summary>
@@ -332,7 +463,10 @@ namespace Business.ResourceExamineManagement
         /// <param name="periodsDet">生产周期</param>
         /// <param name="scheduleResults">排产结果</param>
         /// <param name="allResults">产线占用记录</param>
-        public void LineSchedule(WorkOrdMaster workOrd, List<WorkOrdRouting> workOrdRoutings, List<ProdLineDetail> prodLines,List<PeriodSequenceDet> periodsDet, List<ScheduleResultOpMaster> scheduleResults, List<ScheduleResultOpMaster> allResults)
+        /// <param name="tsWorkOrds">特殊工单</param>
+        /// <param name="ypcWorkOrds">已排产特殊工单的工单编号</param>
+        /// <param name="tsWoRoutings">特殊工单的工艺路线</param>
+        public void LineSchedule(WorkOrdMaster workOrd, List<WorkOrdRouting> workOrdRoutings, List<ProdLineDetail> prodLines,List<PeriodSequenceDet> periodsDet, List<ScheduleResultOpMaster> scheduleResults, List<ScheduleResultOpMaster> allResults,List<WorkOrdMaster> tsWorkOrds,List<string> ypcWorkOrds, List<WorkOrdRouting> tsWoRoutings)
         {
             //生产周期
             List<PeriodSequenceDet> curSequences = new List<PeriodSequenceDet>();
@@ -340,6 +474,7 @@ namespace Business.ResourceExamineManagement
             List<ScheduleResultOpMaster> curScheduleRsts = new List<ScheduleResultOpMaster>();
             //记录上一产线排产开始时间
             LineStartDto lineStart = new LineStartDto();
+            TimeSpan span = TimeSpan.Zero;
             //第一层级工序有几个关键工序,就有几条产线
             for (int i = 0; i < workOrdRoutings.Count; i++)
             {
@@ -363,6 +498,254 @@ namespace Business.ResourceExamineManagement
                 decimal sumTimes = 0m;
                 //产线排产开始时间
                 DateTime workStartTime = lineStart.StartTime;
+                //记录特殊工单:获取第一天是否有特殊工单
+                var fstWOMasters = tsWorkOrds.Where(p => p.ProdLine == lineStart.Line && p.OrdDate.Value.Date == workStartTime.Date).OrderBy(p => p.OrdDate).ToList();
+                List<WorkOrdMstDto> workDtos = new List<WorkOrdMstDto>();
+                foreach (var item in fstWOMasters)
+                {
+                    int op = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First().OP;
+                    workDtos.Add(new WorkOrdMstDto{
+                        WorkOrd = item.WorkOrd,
+                        ItemNum = item.ItemNum,
+                        QtyOrded = item.QtyOrded,
+                        LbrVar = item.LbrVar * 60,
+                        Worked = 0,
+                        QtyWorked = 0,
+                        Op = op
+                    });
+                }
+                //特殊工单工作时长
+                decimal sumTsTimes = fstWOMasters.Sum(p=>p.LbrVar);
+                //特殊工单排产结束当天占用的产能时长
+                decimal dayOccupyTimes = 0m;
+                while (sumTsTimes > 0) //产线排产开始当天安排了特殊工单,则先安排特殊工单
+                {
+                    //获取当天的产能
+                    LineScheduledDto dto = GetScheduledPoint(lineStart, workStartTime, mLCalendars, mlqtyWorkDtls);
+                    //获取当天的工作时间段
+                    var curCalendar = mLCalendars.FirstOrDefault(p => !string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
+                    if (curCalendar == null)
+                    {
+                        curCalendar = mLCalendars.FirstOrDefault(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
+                    }
+                    List<LineWorkPointDto> workPoints = DealWorkDayToLevels(lineStart.Line, workStartTime, curCalendar, mlqtyWorkDtls);
+                    if (dto.EffTime >= sumTsTimes)//当天的可用产能满足特殊工单生产时长
+                    {
+                        DateTime beginTime = workStartTime;
+                        DateTime endTime = DateTime.Now;
+                        foreach (var item in workDtos)
+                        {
+                            if (item.LbrVar == item.Worked)//当前工单已排产
+                            {
+                                continue;
+                            }
+                            //工单排产完成,排产时长=工单工作时长
+                            item.Worked = item.LbrVar;
+                            //当前工单还需工作时长
+                            decimal needTime = item.LbrVar - item.Worked;
+                            //计算工单排产结束时间
+                            var curPoint = workPoints.Find(p => p.StartPoint <= beginTime && beginTime <= p.EndPoint);
+                            span = curPoint.EndPoint - beginTime;
+                            //当天工作时间段的有效生产时间(分钟)
+                            decimal effMins = (decimal)span.TotalMinutes;
+                            if (effMins >= needTime)//当前工作时间段即可满足产能
+                            {
+                                endTime = beginTime.AddMinutes((double)needTime);
+                            }
+                            else
+                            {
+                                //获取后续生产时间段
+                                var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
+                                //剩余需要工作时长
+                                decimal nextMins = needTime - effMins;
+                                foreach (var p in nextPoints)
+                                {
+                                    if (p.WorkMinutes >= nextMins)
+                                    {
+                                        endTime = p.StartPoint.AddMinutes((double)nextMins);
+                                        break;
+                                    }
+                                    nextMins -= p.WorkMinutes;
+                                }
+                            }
+                            //记录生产周期
+                            curSequences.Add(new PeriodSequenceDet
+                            {
+                                Domain = domain,
+                                Line = lineStart.Line,
+                                ItemNum = item.ItemNum,
+                                PlanDate = beginTime.Date,
+                                Period = dto.Period,
+                                OrdQty = item.QtyOrded - item.QtyWorked,
+                                WorkOrds = item.WorkOrd,
+                                Op = item.Op,
+                                IsActive = true,
+                                Status = "",
+                                CreateTime = DateTime.Now
+                            });
+                            //记录排产记录
+                            curScheduleRsts.Add(new ScheduleResultOpMaster
+                            {
+                                Domain = domain,
+                                WorkOrd = item.WorkOrd,
+                                Line = lineStart.Line,
+                                ItemNum = item.ItemNum,
+                                Op = item.Op,
+                                WorkDate = beginTime.Date,
+                                WorkQty = item.QtyOrded - item.QtyWorked,
+                                WorkStartTime = beginTime,
+                                WorkEndTime = endTime,
+                                CreateTime = DateTime.Now
+                            });
+                            //下一工单开始时间=当前工单结束时间,如果位于工作区间结尾,则为下一工作区间开始时间
+                            curPoint = workPoints.Find(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
+                            if (endTime == curPoint.EndPoint)//结束时间位于工作区间结尾
+                            {
+                                //获取后续生产时间段
+                                var nextPoint = workPoints.Where(p => p.Level == curPoint.Level + 1).FirstOrDefault();
+                                if (nextPoint != null)//存在后续工作区间
+                                {
+                                    endTime = nextPoint.StartPoint;
+                                }
+                            }
+                            beginTime = endTime;
+                        }
+                        dayOccupyTimes = sumTsTimes;
+                        if (dto.EffTime == sumTsTimes)//当天产能完全占用
+                        {
+                            //获取下一个工作日
+                            workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
+                            dayOccupyTimes = 0m;
+                        }
+                        sumTsTimes = 0;//排产完毕,特殊工单时长置0
+                    }
+                    else//当天的可用产能不满足特殊工单生产时长
+                    {
+                        decimal residueTime = dto.EffTime * 60;//当天产能剩余产能(分钟)
+                        DateTime beginTime = workStartTime;//排产开始时间
+                        DateTime endTime = dto.EndTime;//排产结束时间
+                        foreach (var item in workDtos)
+                        {
+                            if (item.LbrVar == item.Worked)//当前工单已排产
+                            {
+                                continue;
+                            }
+                            //当前工单剩余待排产时长(分钟)
+                            decimal needTime = item.LbrVar - item.Worked;
+                            if (residueTime >= needTime)//当天剩余产能满足当前工单的剩余待排产时长
+                            {
+                                //计算工单排产结束时间
+                                var curPoint = workPoints.Find(p => p.StartPoint <= beginTime && beginTime <= p.EndPoint);
+                                span = curPoint.EndPoint - beginTime;
+                                //当前工作时间段的有效生产时间(分钟)
+                                decimal effMins = (decimal)span.TotalMinutes;
+                                if (effMins >= needTime)//当前工作时间段即可满足产能
+                                {
+                                    endTime = beginTime.AddMinutes((double)needTime);
+                                }
+                                else
+                                {
+                                    //获取后续生产时间段
+                                    var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
+                                    //剩余需要工作时长
+                                    decimal nextMins = needTime - effMins;
+                                    foreach (var p in nextPoints)
+                                    {
+                                        if (p.WorkMinutes >= nextMins)
+                                        {
+                                            endTime = p.StartPoint.AddMinutes((double)nextMins);
+                                            break;
+                                        }
+                                        nextMins -= p.WorkMinutes;
+                                    }
+                                }
+                                //记录生产周期
+                                curSequences.Add(new PeriodSequenceDet
+                                {
+                                    Domain = domain,
+                                    Line = lineStart.Line,
+                                    ItemNum = item.ItemNum,
+                                    PlanDate = beginTime.Date,
+                                    Period = dto.Period,
+                                    OrdQty = item.QtyOrded - item.QtyWorked,
+                                    WorkOrds = item.WorkOrd,
+                                    Op = item.Op,
+                                    IsActive = true,
+                                    Status = "",
+                                    CreateTime = DateTime.Now
+                                });
+                                //记录排产记录
+                                curScheduleRsts.Add(new ScheduleResultOpMaster
+                                {
+                                    Domain = domain,
+                                    WorkOrd = item.WorkOrd,
+                                    Line = lineStart.Line,
+                                    ItemNum = item.ItemNum,
+                                    Op = item.Op,
+                                    WorkDate = beginTime.Date,
+                                    WorkQty = item.QtyOrded - item.QtyWorked,
+                                    WorkStartTime = beginTime,
+                                    WorkEndTime = endTime,
+                                    CreateTime = DateTime.Now
+                                });
+                                beginTime = endTime;
+                                //当前工单已排产完成,已排产时间=工单生产时长,已排产数量=工单数量
+                                item.Worked = item.LbrVar;
+                                item.QtyWorked = item.QtyWorked;
+                                //当天剩余产能
+                                residueTime -= needTime;
+                                if (residueTime == 0)
+                                {
+                                    break;
+                                }
+                            }
+                            else//当天剩余产能不满足当前工单的剩余待排产时长
+                            {
+                                //计算生产数量
+                                decimal qty = Math.Ceiling(residueTime / item.LbrVar * item.QtyOrded);
+                                //记录生产周期
+                                curSequences.Add(new PeriodSequenceDet
+                                {
+                                    Domain = domain,
+                                    Line = lineStart.Line,
+                                    ItemNum = item.ItemNum,
+                                    PlanDate = beginTime.Date,
+                                    Period = dto.Period,
+                                    OrdQty = qty,
+                                    WorkOrds = item.WorkOrd,
+                                    Op = item.Op,
+                                    IsActive = true,
+                                    Status = "",
+                                    CreateTime = DateTime.Now
+                                });
+                                //记录排产记录
+                                curScheduleRsts.Add(new ScheduleResultOpMaster
+                                {
+                                    Domain = domain,
+                                    WorkOrd = item.WorkOrd,
+                                    Line = lineStart.Line,
+                                    ItemNum = item.ItemNum,
+                                    Op = item.Op,
+                                    WorkDate = beginTime.Date,
+                                    WorkQty = qty,
+                                    WorkStartTime = beginTime,
+                                    WorkEndTime = endTime,
+                                    CreateTime = DateTime.Now
+                                });
+                                item.Worked += residueTime;
+                                item.QtyWorked += qty;
+                                residueTime = 0;
+                                break;
+                            }
+                        }
+                        //特殊工单剩余待排产时长
+                        sumTsTimes -= dto.EffTime;
+                        //获取下一个工作日
+                        workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
+                    }
+                }
+                //记录排产开始第二天及以后安排的特殊工单
+                var secWOMasters = new List<WorkOrdMaster>();
                 //排产
                 while (sumQty < workOrd.QtyOrded)
                 {
@@ -374,7 +757,6 @@ namespace Business.ResourceExamineManagement
                         //判断当天的可用生产时长能满足提前期
                         if (dto.EffTime >= lineStart.setupTime - sumTimes)
                         {
-                            sumTimes = lineStart.setupTime;
                             //当天剩余产能
                             decimal sumAmount = dto.ProductQty - Math.Floor(dto.Rate * (lineStart.setupTime - sumTimes));
                             //判断已排产数量+当天的产能是否超过工单数量
@@ -426,11 +808,11 @@ namespace Business.ResourceExamineManagement
                                 {
                                     curCalendar = mLCalendars.FirstOrDefault(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
                                 }
-                                List<LineWorkPointDto> workPoints = DealWorkDayToLevels(workStartTime, curCalendar, mlqtyWorkDtls);
+                                List<LineWorkPointDto> workPoints = DealWorkDayToLevels(lineStart.Line, workStartTime, curCalendar, mlqtyWorkDtls);
                                 var curPoint = workPoints.Find(p => p.StartPoint <= workStartTime && workStartTime <= p.EndPoint);
                                 if (curPoint != null)
                                 {
-                                    TimeSpan span = curPoint.EndPoint - workStartTime;
+                                    span = curPoint.EndPoint - workStartTime;
                                     //当天工作时间段的有效生产时间
                                     decimal effMins = (decimal)span.TotalMinutes;
                                     DateTime workEndTime = workStartTime;
@@ -486,15 +868,18 @@ namespace Business.ResourceExamineManagement
                                     });
                                 }
                             }
+                            sumTimes = lineStart.setupTime;
                         }
-                        else {
+                        else
+                        {
                             //当天的可用生产时长不能满足提前期
                             sumTimes += dto.EffTime;
                             //获取下一个工作日
                             workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
                         }
                     }
-                    else {
+                    else
+                    {
                         //判断已排产数量+当天的产能是否超过工单数量
                         if (sumQty + dto.ProductQty <= workOrd.QtyOrded)//当天的产能需要全部排产
                         {
@@ -505,7 +890,7 @@ namespace Business.ResourceExamineManagement
                                 Line = lineStart.Line,
                                 ItemNum = workOrd.ItemNum,
                                 PlanDate = workStartTime.Date,
-                                Period = dto.Period,//目前只考虑一班制
+                                Period = dto.Period,
                                 OrdQty = dto.ProductQty,
                                 WorkOrds = workOrd.WorkOrd,
                                 Op = lineStart.Op,
@@ -544,65 +929,62 @@ namespace Business.ResourceExamineManagement
                             {
                                 curCalendar = mLCalendars.FirstOrDefault(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == (int)workStartTime.DayOfWeek);
                             }
-                            List<LineWorkPointDto> workPoints = DealWorkDayToLevels(workStartTime, curCalendar, mlqtyWorkDtls);
+                            List<LineWorkPointDto> workPoints = DealWorkDayToLevels(lineStart.Line, workStartTime, curCalendar, mlqtyWorkDtls);
                             var curPoint = workPoints.Find(p => p.StartPoint <= workStartTime && workStartTime <= p.EndPoint);
-                            if (curPoint != null)
+                            span = curPoint.EndPoint - workStartTime;
+                            //当天工作时间段的有效生产时间
+                            decimal effMins = (decimal)span.TotalMinutes;
+                            DateTime workEndTime = workStartTime;
+                            if (effMins >= workTime)//当前工作时间段即可满足产能
                             {
-                                TimeSpan span = curPoint.EndPoint - workStartTime;
-                                //当天工作时间段的有效生产时间
-                                decimal effMins = (decimal)span.TotalMinutes;
-                                DateTime workEndTime = workStartTime;
-                                if (effMins >= workTime)//当前工作时间段即可满足产能
-                                {
-                                    workEndTime = workStartTime.AddMinutes((double)workTime);
-                                }
-                                else
+                                workEndTime = workStartTime.AddMinutes((double)workTime);
+                            }
+                            else
+                            {
+                                //获取后续生产时间段
+                                var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
+                                //剩余需要工作时长
+                                decimal nextMins = workTime - effMins;
+                                foreach (var p in nextPoints)
                                 {
-                                    //获取后续生产时间段
-                                    var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).ToList();
-                                    //剩余需要工作时长
-                                    decimal nextMins = workTime - effMins;
-                                    foreach (var p in nextPoints)
+                                    if (p.WorkMinutes >= nextMins)
                                     {
-                                        if (p.WorkMinutes >= nextMins)
-                                        {
-                                            workEndTime = p.StartPoint.AddMinutes((double)nextMins);
-                                            break;
-                                        }
-                                        nextMins -= p.WorkMinutes;
+                                        workEndTime = p.StartPoint.AddMinutes((double)nextMins);
+                                        break;
                                     }
+                                    nextMins -= p.WorkMinutes;
                                 }
-                                sumQty = workOrd.QtyOrded;
-                                //记录生产周期
-                                curSequences.Add(new PeriodSequenceDet
-                                {
-                                    Domain = domain,
-                                    Line = lineStart.Line,
-                                    ItemNum = workOrd.ItemNum,
-                                    PlanDate = workStartTime.Date,
-                                    Period = dto.Period,//目前只考虑一班制
-                                    OrdQty = residueQty,
-                                    WorkOrds = workOrd.WorkOrd,
-                                    Op = lineStart.Op,
-                                    IsActive = true,
-                                    Status = "",
-                                    CreateTime = DateTime.Now
-                                });
-                                //记录排产记录
-                                curScheduleRsts.Add(new ScheduleResultOpMaster
-                                {
-                                    Domain = domain,
-                                    WorkOrd = workOrd.WorkOrd,
-                                    Line = lineStart.Line,
-                                    ItemNum = workOrd.ItemNum,
-                                    Op = lineStart.Op,
-                                    WorkDate = workStartTime.Date,
-                                    WorkQty = residueQty,
-                                    WorkStartTime = workStartTime,
-                                    WorkEndTime = workEndTime,
-                                    CreateTime = DateTime.Now
-                                });
                             }
+                            sumQty = workOrd.QtyOrded;
+                            //记录生产周期
+                            curSequences.Add(new PeriodSequenceDet
+                            {
+                                Domain = domain,
+                                Line = lineStart.Line,
+                                ItemNum = workOrd.ItemNum,
+                                PlanDate = workStartTime.Date,
+                                Period = dto.Period,
+                                OrdQty = residueQty,
+                                WorkOrds = workOrd.WorkOrd,
+                                Op = lineStart.Op,
+                                IsActive = true,
+                                Status = "",
+                                CreateTime = DateTime.Now
+                            });
+                            //记录排产记录
+                            curScheduleRsts.Add(new ScheduleResultOpMaster
+                            {
+                                Domain = domain,
+                                WorkOrd = workOrd.WorkOrd,
+                                Line = lineStart.Line,
+                                ItemNum = workOrd.ItemNum,
+                                Op = lineStart.Op,
+                                WorkDate = workStartTime.Date,
+                                WorkQty = residueQty,
+                                WorkStartTime = workStartTime,
+                                WorkEndTime = workEndTime,
+                                CreateTime = DateTime.Now
+                            });
                         }
                     }
                 }
@@ -635,7 +1017,7 @@ namespace Business.ResourceExamineManagement
                 shopCal = curCalendars.Where(p =>string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
             }
             //当前日期的工作时间段
-            List<LineWorkPointDto> workPoints = DealWorkDayToLevels(startTime, shopCal, curQtyDtls);
+            List<LineWorkPointDto> workPoints = DealWorkDayToLevels(lineStart.Line, startTime, shopCal, curQtyDtls);
             //当天排产结束时间
             scheduledDto.EndTime = workPoints.Last().EndPoint;
             //计算starttime处于那个工作时间段
@@ -649,16 +1031,16 @@ namespace Business.ResourceExamineManagement
                 span = item.EndPoint - item.StartPoint;
                 scheduledDto.EffTime += (decimal)span.TotalHours;
             }
-            //判断产线当天有没有加班
-            var curOccupyTimes = resourceOccupancyTimes.Where(p=> p.Resource == lineStart.Line && p.StartTime.GetValueOrDefault().Date == startTime.Date).ToList();
-            scheduledDto.EffTime += curOccupyTimes.Sum(p=> Convert.ToDecimal(p.Ufld1));
+            ////判断产线当天有没有加班
+            //var curOccupyTimes = resourceOccupancyTimes.Where(p=> p.Resource == lineStart.Line && p.StartTime.GetValueOrDefault().Date == startTime.Date).ToList();
+            //scheduledDto.EffTime += curOccupyTimes.Sum(p=> Convert.ToDecimal(p.Ufld1));
             //判断当前产线的UPH
             var curRunCrews = prodLineDetailRunCrews.Where(p=>p.ProdLineDetailRecID == lineStart.RecID).ToList();
             //判断当前日期是否配置了UPH
             var curLevel = curRunCrews.FirstOrDefault(p => p.StartDate.GetValueOrDefault().Date <= startTime.Date && p.EndDate.GetValueOrDefault().Date >= startTime.Date);
             decimal rate = curLevel == null ? lineStart.Rate : curLevel.Rate;
             scheduledDto.Rate = rate;
-            //计算当天的产能
+            //计算当天的产能(向下取整)
             scheduledDto.ProductQty = Math.Floor(scheduledDto.EffTime * rate);
             //计算班次
             scheduledDto.Period = 1;//默认一般制
@@ -690,12 +1072,13 @@ namespace Business.ResourceExamineManagement
             //产线工作日历:当前产线的工作日历+默认工作日历
             var curCalendars = calendars.Where(p => p.ProdLine == lines[0].Line || string.IsNullOrEmpty(p.ProdLine)).ToList();
             var curQtyDtls = qualityLines.Where(p=>p.ProdLine == lines[0].Line).ToList();
-            actStart = CalcActStartTime(actStart, curCalendars, curQtyDtls);
+            actStart = CalcActStartTime(lines[0].Line, actStart, curCalendars, curQtyDtls);
             lineStart.RecID = lines[0].RecID;
             lineStart.Line = lines[0].Line;
             lineStart.StartTime = actStart;
             lineStart.setupTime = lines[0].SetupTime;
             lineStart.Rate = lines[0].Rate;
+            lineStart.Op = op;
             //循环其他产线
             for (int i = 1; i < lines.Count; i++)
             {
@@ -705,7 +1088,7 @@ namespace Business.ResourceExamineManagement
                 //产线工作日历:当前产线的工作日历+默认工作日历
                 curCalendars = calendars.Where(p => p.ProdLine == lines[i].Line || string.IsNullOrEmpty(p.ProdLine)).ToList();
                 curQtyDtls = qualityLines.Where(p => p.ProdLine == lines[i].Line).ToList();
-                StartTime = CalcActStartTime(StartTime, curCalendars, curQtyDtls);
+                StartTime = CalcActStartTime(lines[0].Line, StartTime, curCalendars, curQtyDtls);
                 if (StartTime < lineStart.StartTime)
                 {
                     lineStart.RecID = lines[i].RecID;
@@ -713,6 +1096,7 @@ namespace Business.ResourceExamineManagement
                     lineStart.StartTime = StartTime;
                     lineStart.setupTime = lines[i].SetupTime;
                     lineStart.Rate = lines[i].Rate;
+                    lineStart.Op = op;
                 }
             }
             return lineStart;
@@ -721,11 +1105,12 @@ namespace Business.ResourceExamineManagement
         /// <summary>
         /// 计算主产线实际排产开始时间
         /// </summary>
+        /// <param name="prodLine">生产线</param>
         /// <param name="startTime">开始时间</param>
         /// <param name="curCalendars">当前产线工作日历+默认工作日历</param>
         /// <param name="curQtyDtls">当前产线休息记录</param>
         /// <returns></returns>
-        public DateTime CalcActStartTime(DateTime startTime, List<ShopCalendarWorkCtr> curCalendars, List<QualityLineWorkDetail> curQtyDtls)
+        public DateTime CalcActStartTime(string prodLine,DateTime startTime, List<ShopCalendarWorkCtr> curCalendars, List<QualityLineWorkDetail> curQtyDtls)
         {
             //实际排产开始时间
             DateTime actStart = startTime;
@@ -748,7 +1133,7 @@ namespace Business.ResourceExamineManagement
                 shopCal = curCalendars.Where(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
             }
             //当前日期的工作时间段
-            List<LineWorkPointDto> workPoints = DealWorkDayToLevels(startTime, shopCal, curQtyDtls);
+            List<LineWorkPointDto> workPoints = DealWorkDayToLevels(prodLine, startTime, shopCal, curQtyDtls);
             //计算starttime处于那个工作时间段
             var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).FirstOrDefault();
             if (curPoint == null)//不处于工作时间段
@@ -877,6 +1262,7 @@ namespace Business.ResourceExamineManagement
             startDto.setupTime = lines[0].SetupTime;
             startDto.StartTime = schedule == null ? startTime : (startTime < schedule.WorkEndTime ? schedule.WorkEndTime : startTime);
             startDto.Rate = lines[0].Rate;
+            startDto.Op = op;
             //循环剩余产线,找到最早可开工产线
             for (int i = 1; i < lines.Count(); i++)
             {
@@ -890,6 +1276,7 @@ namespace Business.ResourceExamineManagement
                     startDto.setupTime = lines[i].SetupTime;
                     startDto.StartTime = startTime;
                     startDto.Rate = lines[0].Rate;
+                    startDto.Op = op;
                 }
             }
             return startDto;
@@ -920,7 +1307,7 @@ namespace Business.ResourceExamineManagement
             //当前产线的休息时间设置
             var curQtyDtls = qualityLines.Where(p => p.ProdLine == line.Line).ToList();
             //当前日期的工作时间段
-            List<LineWorkPointDto> workPoints = DealWorkDayToLevels(startTime, shopCal, curQtyDtls);
+            List<LineWorkPointDto> workPoints = DealWorkDayToLevels(line.Line, startTime, shopCal, curQtyDtls);
             //计算starttime处于那个工作时间段
             var curPoint = workPoints.Where(p => startTime >= p.StartPoint && startTime <= p.EndPoint).FirstOrDefault();
             //当前时间段可用时长
@@ -964,7 +1351,7 @@ namespace Business.ResourceExamineManagement
                 {
                     curCalendar = calendars.Where(p => string.IsNullOrEmpty(p.ProdLine) && p.WeekDay == weekDay).FirstOrDefault();
                 }
-                workPoints = DealWorkDayToLevels(nextStartTime, curCalendar, curQtyDtls).OrderBy(p => p.Level).ToList();
+                workPoints = DealWorkDayToLevels(line.Line, nextStartTime, curCalendar, curQtyDtls).OrderBy(p => p.Level).ToList();
                 //当天的工作时长(分钟)
                 decimal sumWorkMins = workPoints.Sum(p => p.WorkMinutes);
                 if (sumWorkMins >= needMinute)//当天可用提前期满足
@@ -1037,11 +1424,12 @@ namespace Business.ResourceExamineManagement
         /// <summary>
         /// 处理当前日期的工作时间段
         /// </summary>
+        /// <param name="prodLine"></param>
         /// <param name="startTime"></param>
         /// <param name="shopCal">当前产线的工作日历-周几</param>
         /// <param name="curQtyDtls">每天休息记录</param>
         /// <returns></returns>
-        public List<LineWorkPointDto> DealWorkDayToLevels(DateTime startTime, ShopCalendarWorkCtr shopCal, List<QualityLineWorkDetail> curQtyDtls)
+        public List<LineWorkPointDto> DealWorkDayToLevels(string prodLine, DateTime startTime, ShopCalendarWorkCtr shopCal, List<QualityLineWorkDetail> curQtyDtls)
         {
             //年-月-日
             string date = startTime.Date.ToString("yyyy-MM-dd");
@@ -1058,7 +1446,7 @@ namespace Business.ResourceExamineManagement
             //结束时间
             DateTime dayEndPoint = dayStartPoint.AddHours((double)(shopCal.ShiftsHours1 + shopCal.ShiftsHours2));
             dto.Level = level;
-            dto.Line = shopCal.ProdLine;
+            dto.Line = prodLine;
             dto.WeekDay = weekDay;
             dto.StartPoint = dayStartPoint;
             //按照产线休息时间切分时间段
@@ -1073,7 +1461,7 @@ namespace Business.ResourceExamineManagement
                 level++;
                 dto = new LineWorkPointDto();
                 dto.Level = level;
-                dto.Line = shopCal.ProdLine;
+                dto.Line = prodLine;
                 dto.WeekDay = weekDay;
                 dto.StartPoint = endPoint.AddMinutes(item.RestTime);
             }
@@ -1081,6 +1469,20 @@ namespace Business.ResourceExamineManagement
             span = dto.EndPoint - dto.StartPoint;
             dto.WorkMinutes = (decimal)span.TotalMinutes;
             workPoints.Add(dto);
+            //产线当天加班设置
+            var curOccupyTimes = resourceOccupancyTimes.Where(p => p.Resource == prodLine && p.StartTime.GetValueOrDefault().Date == startTime.Date).OrderBy(p=>p.StartTime).ToList();
+            foreach (var item in curOccupyTimes)
+            {
+                level++;
+                dto = new LineWorkPointDto();
+                dto.Level = level;
+                dto.Line = prodLine;
+                dto.WeekDay = weekDay;
+                dto.StartPoint = item.StartTime.GetValueOrDefault();
+                dto.EndPoint = dto.StartPoint.AddHours(Convert.ToDouble(item.Ufld1));
+                dto.WorkMinutes = Convert.ToDecimal(item.Ufld1) * 60;
+                workPoints.Add(dto);
+            }
 
             return workPoints.OrderBy(p => p.Level).ToList();
         }

+ 35 - 18
MicroServices/Business/Business.Application/ResourceExamineManagement/ResourceExamineAppService.cs

@@ -2351,7 +2351,7 @@ namespace Business.ResourceExamineManagement
         /// </summary>
         /// <param name="input"></param>
         /// <returns></returns>
-        public async Task<string> OrderResourceCheck(List<mes_morder> mo_Mes_Morders,string domain, string userAccount)
+        public async Task<string> OrderResourceCheck(List<mes_morder> mo_Mes_Morders,string domain, string userAccount, bool WordOrdXd=false)
         {
             List<mes_morder> checkMo = new List<mes_morder>();
             //只做库存是否齐套检查
@@ -2446,12 +2446,24 @@ namespace Business.ResourceExamineManagement
                                 }
                                 else {
                                     cmo.MaterialSituation = MorderEnum.CarehouseComplete;
+                                    if (WordOrdXd)
+                                    {
+                                        workords += "," + cmo.morder_no;
+                                        cmo.MaterialSituation = MorderEnum.Preparation;
+                                        cmo.morder_state = MorderEnum.Xd_state;
+                                    }
                                 }
                             }
                             else
                             {
                                 //工单状态为  “仓库不齐套”
                                 cmo.MaterialSituation = MorderEnum.NoCarehouseComplete;
+                                if (WordOrdXd)
+                                {
+                                    workords += "," + cmo.morder_no;
+                                    cmo.morder_state = MorderEnum.Xd_state;
+                                    cmo.MaterialSituation = MorderEnum.Part;
+                                }
                             }
                         }
                     }
@@ -2465,7 +2477,11 @@ namespace Business.ResourceExamineManagement
                 {
                     workords = workords.Substring(1, workords.Length - 1);
                     //如果生成领料单,需清理掉当前工单的占用,然后根据领料单的数据,来生成工单的占用
-                    await CreatePickBill(workords, domain, userAccount);
+                    var rst = await CreatePickBill(workords, domain, userAccount);
+                    if (rst != "ok")
+                    {
+                        return rst;
+                    }
                     var insertList = GetCopyOP(workords, domain);
                     if (insertList.Any())
                     {
@@ -2475,7 +2491,7 @@ namespace Business.ResourceExamineManagement
                 return "ok";
             }
             else {
-                return "没有需要检查的工单";
+                return "没有需要下达的工单";
             }
         }
 
@@ -2585,7 +2601,7 @@ namespace Business.ResourceExamineManagement
                 return await OrderResourceCheck(mo_Mes_Morders, domain, userAccount);
             }
             else { 
-                return "没有需要检查的工单";
+                return "没有需要下达的工单";
             }
         }
 
@@ -2600,12 +2616,22 @@ namespace Business.ResourceExamineManagement
         {
             if (!string.IsNullOrEmpty(workord))
             {
-                List<mes_morder> mo_Mes_Morders = _mysql_mes_morder.GetListAsync(x => domain == x.factory_id.ToString() && x.morder_no == workord && x.morder_state == MorderEnum.Initial_state && !x.IsDeleted).Result;
-                return await OrderResourceCheck(mo_Mes_Morders, domain, userAccount);
+                List<mes_morder> mo_Mes_Morders = _mysql_mes_morder.GetListAsync(x => domain == x.factory_id.ToString() && x.morder_no == workord && !x.IsDeleted).Result;
+                if (mo_Mes_Morders.Any())
+                {
+                    if (!string.IsNullOrEmpty(mo_Mes_Morders[0].morder_state) && mo_Mes_Morders[0].morder_state != MorderEnum.Initial_state)
+                    {
+                        return "工单信息为"+ mo_Mes_Morders[0].morder_state + ",不允许下达。";
+                    }
+                    return await OrderResourceCheck(mo_Mes_Morders, domain, userAccount, true);
+                }
+                else {
+                    return "工单信息不存在,请重新检查。";
+                }
             }
             else
             {
-                return "没有需要检查的工单";
+                return "没有需要下达的工单";
             }
         }
 
@@ -2961,7 +2987,7 @@ namespace Business.ResourceExamineManagement
                 //优先级排序
                 workOrdMasters = workOrdMasters.OrderBy(s => s.OrdDate).ToList();
                 //排产
-                //await _productionScheduleAppService.DoProductShcedule(workOrdMasters, param.factoryId.ToString());
+                //await _productionScheduleAppService.DoProductSchedule(workOrdMasters, param.factoryId.ToString());
                 
                 foreach (var wod in workOrdMasters)
                 {
@@ -3532,17 +3558,8 @@ namespace Business.ResourceExamineManagement
             }
             //获取工单数据
             var workOrdMasters = _workOrdMaster.Select(p => workOrds.Contains(p.WorkOrd) && p.Domain == domain);
-            //获取已排产的工单
-            List<string> dbWorkOrds = _periodSequenceDet.Select(p => workOrds.Contains(p.WorkOrds)).Select(p => p.WorkOrds).Distinct().ToList();
-            //过滤掉已排产的工单
-            workOrdMasters = workOrdMasters.Where(p => !dbWorkOrds.Contains(p.WorkOrd)).ToList();
-            if (workOrdMasters.Count == 0)
-            {
-                return "当前工单已排产,无需重新排产。";
-            }
             //排产
-            //await _productionScheduleAppService.DoProductShcedule(workOrdMasters, workOrdMasters[0].Domain);
-            //AutoCreatePickBill(workOrdMasters.Select(p => p.WorkOrd).ToList());
+            //await _productionScheduleAppService.DoProductSchedule(workOrdMasters, workOrdMasters[0].Domain, 1);
             return "ok";
         }
 

+ 1 - 1
MicroServices/Business/Business.Application/SystemJobManagement/SystemJobAppService.cs

@@ -825,7 +825,7 @@ namespace Business.SystemJobManagement
         //    return list;
         //}
 
-        public string DoProductShceduleJob()
+        public string DoProductScheduleJob()
         {
             try
             {

+ 4 - 0
MicroServices/Business/Business.Core/Enum/MorderEnum.cs

@@ -94,5 +94,9 @@ namespace Business.Core.Enum
         /// 备料滞后
         /// </summary>
         public const string Lag = "备料滞后";
+        /// <summary>
+        /// 部分备料
+        /// </summary>
+        public const string Part = "部分备料";
     }
 }

+ 2 - 2
MicroServices/Business/Business.Domain/StructuredDB/MES/IC/GeneralizedCodeMaster.cs

@@ -64,9 +64,9 @@ namespace Business.Domain
         public bool IsConfirm { get; set; }
 
         /// <summary>
-        /// 数值1
+        /// 排产锁定期
         /// </summary>
-        [Comment("数值1")]
+        [Comment("排产锁定期")]
         public decimal UDeci1 { get; set; }
 
         /// <summary>

+ 1 - 1
MicroServices/Business/Business.Domain/StructuredDB/Production/ProdLineDetail.cs

@@ -49,7 +49,7 @@ namespace Business.Domain
         /// 工序
         /// </summary>
         [Comment("工序")]
-        public int? Op { get; set; }
+        public int Op { get; set; }
 
         /// <summary>
         /// 是否有效:1-有效;0-无效

+ 5 - 1
MicroServices/Business/Business.Domain/StructuredDB/Production/ProdLineDetailRunCrew.cs

@@ -1,5 +1,7 @@
-using System;
+using Microsoft.EntityFrameworkCore;
+using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -15,6 +17,8 @@ namespace Business.StructuredDB.Production
         /// <summary>
         /// 主键ID    
         /// </summary>
+        [Comment("主键")]
+        [Key]
         public int RecID { get; set; }
 
         /// <summary>

+ 5 - 1
MicroServices/Business/Business.Domain/StructuredDB/Production/ResourceOccupancyTime.cs

@@ -1,5 +1,7 @@
-using System;
+using Microsoft.EntityFrameworkCore;
+using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -15,6 +17,8 @@ namespace Business.StructuredDB.Production
         /// <summary>
         /// 主键ID    
         /// </summary>
+        [Comment("主键")]
+        [Key]
         public int RecID { get; set; }
 
         /// <summary>

+ 60 - 0
MicroServices/Business/Business.Domain/StructuredDB/Production/ScheduleExceptionMaster.cs

@@ -0,0 +1,60 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Business.StructuredDB.Production
+{
+    /// <summary>
+    /// 排产异常记录表
+    /// </summary>
+    [Comment("排产异常记录表")]
+    public class ScheduleExceptionMaster
+    {
+        /// <summary>
+        /// 主键
+        /// </summary>
+        [Comment("主键")]
+        [Key]
+        public long RecID { get; set; }
+
+        /// <summary>
+        /// 域名
+        /// </summary>
+        [Comment("域名")]
+        public string Domain { get; set; }
+
+        /// <summary>
+        /// 工单
+        /// </summary>
+        [Comment("工单")]
+        public string WorkOrd { get; set; }
+
+        /// <summary>
+        /// 物料编码
+        /// </summary>
+        [Comment("物料编码")]
+        public string ItemNum { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [Comment("创建时间")]
+        public DateTime CreateTime { get; set; }
+
+        /// <summary>
+        /// 异常原因
+        /// </summary>
+        [Comment("异常原因")]
+        public string Remark { get; set; }
+
+        /// <summary>
+        /// 异常类型
+        /// </summary>
+        [Comment("异常类型")]
+        public string Type { get; set; }
+    }
+}

+ 13 - 1
MicroServices/Business/Business.Domain/StructuredDB/Production/WorkOrdMaster.cs

@@ -118,9 +118,21 @@ namespace Business.Domain
         public string Batch { get; set; }
 
         /// <summary>
-        /// 类型 s,销售 p,计划
+        /// 类型:正常工单-空或者Null;反之则为特殊工单
         /// </summary>
         [Comment("类型")]
         public string Typed { get; set; }
+
+        /// <summary>
+        /// 生产时长(小时)
+        /// </summary>
+        [Comment("生产时长")]
+        public decimal LbrVar { get; set; }
+
+        /// <summary>
+        /// 流程id
+        /// </summary>
+        [Comment("流程id")]
+        public long BusinessID { get; set; }
     }
 }

+ 5 - 0
MicroServices/Business/Business.EntityFrameworkCore/EntityFrameworkCore/DOP/BusinessDbContext.cs

@@ -41,6 +41,11 @@ namespace Business.EntityFrameworkCore
         public DbSet<ProdLineDetailRunCrew> ProdLineDetailRunCrew { get; set; }
         public DbSet<ResourceOccupancyTime> ResourceOccupancyTime { get; set; }
 
+        /// <summary>
+        /// ÅŲúÒì³£¼Ç¼±í
+        /// </summary>
+        public DbSet<ScheduleExceptionMaster> ScheduleExceptionMaster { get; set; }
+
         #endregion
 
         #region Sale

+ 3 - 3
MicroServices/Business/Business.HttpApi/Controllers/SystemJobController.cs

@@ -59,10 +59,10 @@ namespace Business.Controllers
         /// </summary>
         /// <returns></returns>
         [HttpGet]
-        [Route("DoProductShcedule")]
-        public string DoProductShcedule()
+        [Route("DoProductSchedule")]
+        public string DoProductSchedule()
         {
-            return _SystemJobAppService.DoProductShceduleJob();
+            return _SystemJobAppService.DoProductScheduleJob();
         }
     }
 }