Эх сурвалжийг харах

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

tangdi 2 жил өмнө
parent
commit
db6a2db238

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

@@ -55,5 +55,10 @@ namespace Business.Dto
         /// 工序
         /// </summary>
         public int Op { get; set; }
+
+        /// <summary>
+        /// 工序清场时长(分钟)
+        /// </summary>
+        public decimal WaitTime { get; set; }
     }
 }

+ 2 - 2
MicroServices/Business/Business.Application.Contracts/ResourceExamineManagement/IResourceExamineAppService.cs

@@ -17,9 +17,9 @@ namespace Business.ResourceExamineManagement
         /// <summary>
         /// Éú²úÅŲú
         /// </summary>
-        /// <param name="workOrds"></param>
+        /// <param name="domain"></param>
         /// <returns></returns>
-        Task<string> ProductionSchedule(string workOrds, string domain);
+        Task<string> ProductionSchedule(string domain);
 
         /// <summary>
         /// Ï´ïÁìÁÏ

+ 354 - 109
MicroServices/Business/Business.Application/ResourceExamineManagement/ProductionScheduleAppService.cs

@@ -18,6 +18,7 @@ using Microsoft.Extensions.Configuration;
 using MongoDB.Driver.Linq;
 using Amazon.Runtime.Internal.Util;
 using IdentityModel.Client;
+using Amazon.Runtime;
 
 namespace Business.ResourceExamineManagement
 {
@@ -196,8 +197,9 @@ namespace Business.ResourceExamineManagement
             decimal lockDays = generalizedCodeMaster != null ? generalizedCodeMaster.UDeci1 : 0;
             
             //获取需要排产的工单(获取四周的工单:正常工单+已审批通过的特殊工单)
-            DateTime endDate = DateTime.Now.Date.AddWeeks(4).AddDays(1);
-            DateTime startDate = DateTime.Now.Date;
+            DateTime endDate = DateTime.Now.Date.AddDays(28).AddDays(1);
+            //取数开始时间需要排除掉锁定期内的工单
+            DateTime startDate = DateTime.Now.Date.AddDays(1).AddDays((double)lockDays);
             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);
         }
@@ -205,7 +207,7 @@ namespace Business.ResourceExamineManagement
         /// <summary>
         /// 生产排产
         /// </summary>
-        /// <param name="workOrds">工单</param>
+        /// <param name="workOrds">需要重新排产的工单</param>
         /// <param name="factoryid">工单的工厂id</param>
         /// <param name="type">排产类型:1-自动排产;2-手动排产</param>
         /// <returns></returns>
@@ -236,10 +238,15 @@ namespace Business.ResourceExamineManagement
             prodLineDetailRunCrews = _prodLineDetailRunCrew.Select(x => prodLines.Select(p => p.RecID).Contains(x.ProdLineDetailRecID) && x.IsActive && x.Domain == domain).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);
+            //获取锁定期之外的工单排产数据
+            List<PeriodSequenceDet> delPeriodSequences = dbPeriodSequences.Where(p => workOrds.Select(m => m.WorkOrd).Contains(p.WorkOrds)).ToList();
+            List<ScheduleResultOpMaster> delSchedules = dbSchedules.Where(p => workOrds.Select(m => m.WorkOrd).Contains(p.WorkOrd)).ToList();
+            //产线占用记录排除锁定期之前的工单
+            dbSchedules = dbSchedules.Where(p => !workOrds.Select(m => m.WorkOrd).Contains(p.WorkOrd)).ToList();
             //获取工作日历数据:产线的工作日历+默认的工作日历
             calendars = _shopCalendarWorkCtr.Select(p => (lines.Contains(p.ProdLine) || string.IsNullOrEmpty(p.ProdLine)) && p.Domain == domain && p.IsActive);
             //获取产线休息记录数据
@@ -302,7 +309,7 @@ namespace Business.ResourceExamineManagement
             List<WorkOrdMstDto> workDtos = new List<WorkOrdMstDto>();
             foreach (var item in notSchedules)
             {
-                int op = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First().OP;
+                var curOp = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First();
                 workDtos.Add(new WorkOrdMstDto
                 {
                     WorkOrd = item.WorkOrd,
@@ -313,7 +320,8 @@ namespace Business.ResourceExamineManagement
                     LbrVar = item.LbrVar * 60,
                     Worked = 0,
                     QtyWorked = 0,
-                    Op = op
+                    Op = curOp.OP,
+                    WaitTime = curOp.WaitTime * 60
                 });
             }
             TsLineSchedule(workDtos, periodSequenceDtls, scheduleMasters);
@@ -331,8 +339,12 @@ namespace Business.ResourceExamineManagement
             {
                 try
                 {
-                    //记录排产数据
+                    //更新工单计划开工时间、计划结束时间
                     _workOrdMaster.Update(workOrds);
+                    //删除锁定期之外的工单排产记录
+                    _periodSequenceDet.Delete(delPeriodSequences);
+                    _scheduleResultOpMaster.Delete(delSchedules);
+                    //保存排产记录
                     _periodSequenceDet.Insert(periodSequenceDtls);
                     _scheduleResultOpMaster.Insert(scheduleMasters);
                     scope.Complete();
@@ -361,7 +373,7 @@ namespace Business.ResourceExamineManagement
             {
                 if (!string.IsNullOrEmpty(item.Typed))//特殊工单
                 {
-                    //只需要校验有没有维护工艺路线即可
+                    //校验有没有维护工艺路线即可
                     var routings = workOrdRoutings.Where(p=>p.ItemNum == item.ItemNum).ToList();
                     if (!routings.Any())
                     {
@@ -374,6 +386,36 @@ namespace Business.ResourceExamineManagement
                         entity.Type = type == 1 ? "自动排产" : "手动排产";
                         exceptions.Add(entity);
                     }
+
+                    //获取当前产线维护的工作日历
+                    var lineCals = calendars.Where(p => p.ProdLine == item.ProdLine).ToList();
+                    //获取默认工作日历
+                    var mrCals = calendars.Where(p => string.IsNullOrEmpty(p.ProdLine)).ToList();
+                    if (!lineCals.Any())//当前产线未维护工作日历
+                    {
+                        if (!mrCals.Any() || mrCals.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 = "排产异常:产线[" + item.ProdLine + "]没有维护工作日历且标准工作日历未维护完全,请维护后再操作!";
+                            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 = "排产异常:产线[" + item.ProdLine + "]工作日历没有维护完整,请维护后再操作!";
+                        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();
@@ -526,24 +568,42 @@ namespace Business.ResourceExamineManagement
                 //记录特殊工单:获取第一天是否有特殊工单
                 List<WorkOrdMaster> 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
-                    });
-                }
                 //记录排产开始第二天及以后安排的特殊工单
                 var secWOMasters = new List<WorkOrdMaster>();
-                //特殊工单工作时长(分钟)
-                decimal sumTsTimes = fstWOMasters.Sum(p => p.LbrVar) * 60;
-                while (sumTsTimes > 0) //产线排产开始当天安排了特殊工单,则先安排特殊工单
+                //特殊工单总清场时长(分钟)
+                decimal sumCleanTimes = 0m;
+                //最后一个特殊工单的清场时长(分钟)
+                decimal lstCleanTime = 0m;
+                //新增特殊工单最后一个清场时长
+                decimal secCleanTime = 0m;
+                if (fstWOMasters.Any())
+                {
+                    foreach (var item in fstWOMasters)
+                    {
+                        var curOp = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First();
+                        workDtos.Add(new WorkOrdMstDto
+                        {
+                            WorkOrd = item.WorkOrd,
+                            ItemNum = item.ItemNum,
+                            QtyOrded = item.QtyOrded,
+                            LbrVar = item.LbrVar * 60,
+                            Worked = 0,
+                            QtyWorked = 0,
+                            Op = curOp.OP,
+                            WaitTime = curOp.WaitTime * 60
+                        });
+                        sumCleanTimes += curOp.WaitTime * 60;
+                    }
+                    //获取最后一个特殊工单的清场时长
+                    var last = fstWOMasters.Last();
+                    lstCleanTime = tsWoRoutings.Where(p => p.WorkOrd == last.WorkOrd).OrderByDescending(p => p.OP).First().WaitTime * 60;
+                }
+                //特殊工单排产需要考虑的时长=特殊工单工作时长(分钟)+除最后一个特殊工单之外的清场时长
+                decimal sumTsTimes = fstWOMasters.Sum(p => p.LbrVar) * 60 + sumCleanTimes - lstCleanTime;
+                //工单排产第一天标识
+                bool isFstDay = true;
+                //产线排产开始当天安排了特殊工单,则先安排特殊工单
+                while (sumTsTimes > 0)
                 {
                     secWOMasters.Clear();
                     //获取当天的产能
@@ -555,67 +615,130 @@ namespace Business.ResourceExamineManagement
                         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)
+                    //特殊工单排产第一天之后:如果当天的可用产能大于特殊工单生产时长,此时需要考虑当天是否安排了其他的特殊工单
+                    if (dto.EffTime > sumTsTimes && !isFstDay)
                     {
                         //获取特殊工单
                         secWOMasters = tsWorkOrds.Where(p => p.ProdLine == lineStart.Line && p.OrdDate.Value.Date == workStartTime.Date).OrderBy(p => p.OrdDate).ToList();
-                        //此时需要过滤掉第一天的工单
-                        secWOMasters = secWOMasters.Where(p=> !workDtos.Select(m=>m.WorkOrd).Contains(p.WorkOrd)).ToList();
-                        //记录新增待排产特殊工单
-                        foreach (var item in secWOMasters)
+                        if (secWOMasters.Any())
                         {
-                            int op = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First().OP;
-                            workDtos.Add(new WorkOrdMstDto
+                            sumCleanTimes = 0m;
+                            //新增特殊工单最后一个清场时长
+                            secCleanTime = 0m;
+                            //记录新增待排产特殊工单
+                            foreach (var item in secWOMasters)
                             {
-                                WorkOrd = item.WorkOrd,
-                                ItemNum = item.ItemNum,
-                                QtyOrded = item.QtyOrded,
-                                LbrVar = item.LbrVar * 60,
-                                Worked = 0,
-                                QtyWorked = 0,
-                                Op = op
-                            });
+                                var curOp = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First();
+                                workDtos.Add(new WorkOrdMstDto
+                                {
+                                    WorkOrd = item.WorkOrd,
+                                    ItemNum = item.ItemNum,
+                                    QtyOrded = item.QtyOrded,
+                                    LbrVar = item.LbrVar * 60,
+                                    Worked = 0,
+                                    QtyWorked = 0,
+                                    Op = curOp.OP,
+                                    WaitTime = curOp.WaitTime * 60
+                                });
+                                sumCleanTimes += curOp.WaitTime * 60;
+                            }
+                            //获取最后一个特殊工单的清场时长
+                            var last = secWOMasters.Last();
+                            secCleanTime = tsWoRoutings.Where(p => p.WorkOrd == last.WorkOrd).OrderByDescending(p => p.OP).First().WaitTime * 60;
+                            //特殊工单待排产时长=前一天特殊工单到最后一个清场时长+新增的特殊工单生产时长+新增特殊工单除最后一个清场时长之外的清场时长之和
+                            sumTsTimes += (lstCleanTime + secWOMasters.Sum(p => p.LbrVar) * 60 + sumCleanTimes - secCleanTime);
+                            lstCleanTime = secCleanTime;
                         }
-                        //特殊工单待排产时长增加新增的特殊工单生产时长
-                        sumTsTimes += secWOMasters.Sum(p => p.LbrVar) * 60;
                     }
-                    if (dto.EffTime >= sumTsTimes)//当天的可用生产时长满足特殊工单生产时长
+                    //当天的可用生产时长满足特殊工单生产时长+清场时间(最后一个工单清场时间除外)
+                    if (dto.EffTime >= sumTsTimes)
                     {
+                        //可用生产时长(分钟)
+                        decimal remainTime = dto.EffTime;
                         DateTime beginTime = workStartTime;
                         DateTime endTime = dto.EndTime;
+                        //获取最后一个工单
+                        var lastWork = workDtos.Last();
+                        //开始或结束时间所处时间段
+                        var curPoint = new LineWorkPointDto();
+                        //特殊工单排产
                         foreach (var item in workDtos)
                         {
-                            if (item.LbrVar == item.Worked)//当前工单已排产,跳过
+                            //当前工单已排产,跳过
+                            if (item.LbrVar == item.Worked)
                             {
                                 continue;
                             }
-                            //当前工单还需工作时长
-                            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)//当前工作时间段即可满足产能
+                            //当前工单排产需要时长
+                            decimal needTime = 0m;
+                            //最后一个特殊工单
+                            if (item.WorkOrd == lastWork.WorkOrd)
                             {
-                                endTime = beginTime.AddMinutes((double)needTime);
+                                //最后一个工单的生产时长+清场时长
+                                needTime = lastWork.QtyOrded - lastWork.QtyWorked + lastWork.WaitTime;
+                                //剩余可用工作时长满足最后一个特殊工单的生产时长+清场时长
+                                if (remainTime >= needTime)
+                                {
+                                    //获取排产结束时间所处以及之后的生产时间段
+                                    var nextPoints = workPoints.Where(p => p.Level >= curPoint.Level).OrderBy(p => p.Level).ToList();
+                                    //处理第一个时间段的开始时间
+                                    nextPoints[0].StartPoint = beginTime;
+                                    foreach (var p in nextPoints)
+                                    {
+                                        span = p.EndPoint - p.StartPoint;
+                                        //当前工作时间段的有效生产时间
+                                        decimal effMins = (decimal)span.TotalMinutes;
+                                        if (effMins >= needTime)
+                                        {
+                                            endTime = p.StartPoint.AddMinutes((double)needTime);
+                                            break;
+                                        }
+                                        needTime -= effMins;
+                                    }
+                                    remainTime -= needTime;
+                                }
+                                //剩余可用工作时长不能满足清场时长
+                                else
+                                {
+                                    endTime = dto.EndTime;
+                                    //还需排产的清场时间
+                                    decimal qcTime = needTime - remainTime;
+                                    endTime.AddMinutes((double)qcTime);
+                                    remainTime = 0;
+                                }
+                                //最后一个工单的清场时长
+                                lstCleanTime = 0m;
                             }
-                            else
-                            {
-                                //获取后续生产时间段
-                                var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).OrderBy(p => p.Level).ToList();
-                                //剩余需要工作时长
-                                decimal nextMins = needTime - effMins;
-                                foreach (var p in nextPoints)
+                            //最后一个特殊工单之前的工单:剩余排产时长和清场时间全部排产
+                            else {
+                                //当前工单还需工作时长+工单的清场时长
+                                needTime = item.LbrVar - item.Worked + item.WaitTime;
+                                //计算工单排产结束时间
+                                curPoint = workPoints.Find(p => p.StartPoint <= beginTime && beginTime <= p.EndPoint);
+                                span = curPoint.EndPoint - beginTime;
+                                //当天工作时间段的有效生产时间(分钟)
+                                decimal effMins = (decimal)span.TotalMinutes;
+                                if (effMins >= needTime)//当前工作时间段即可满足产能
                                 {
-                                    if (p.WorkMinutes >= nextMins)
+                                    endTime = beginTime.AddMinutes((double)needTime);
+                                }
+                                else
+                                {
+                                    //获取后续生产时间段
+                                    var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).OrderBy(p => p.Level).ToList();
+                                    //剩余需要工作时长
+                                    decimal nextMins = needTime - effMins;
+                                    foreach (var p in nextPoints)
                                     {
-                                        endTime = p.StartPoint.AddMinutes((double)nextMins);
-                                        break;
+                                        if (p.WorkMinutes >= nextMins)
+                                        {
+                                            endTime = p.StartPoint.AddMinutes((double)nextMins);
+                                            break;
+                                        }
+                                        nextMins -= p.WorkMinutes;
                                     }
-                                    nextMins -= p.WorkMinutes;
                                 }
+                                remainTime -= needTime;
                             }
                             //记录生产周期
                             curSequences.Add(new PeriodSequenceDet
@@ -647,54 +770,70 @@ namespace Business.ResourceExamineManagement
                                 CreateTime = DateTime.Now
                             });
                             //下一工单开始时间=当前工单结束时间,如果位于工作区间结尾,则为下一工作区间开始时间
-                            curPoint = workPoints.Find(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
-                            if (endTime == curPoint.EndPoint)//结束时间位于工作区间结尾
+                            curPoint = workPoints.FirstOrDefault(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
+                            //结束时间位于工作区间结尾
+                            if (curPoint != null && endTime == curPoint.EndPoint)
                             {
                                 //获取后续生产时间段
-                                var nextPoint = workPoints.Where(p => p.Level == curPoint.Level + 1).FirstOrDefault();
-                                if (nextPoint != null)//存在后续工作区间
+                                curPoint = workPoints.Where(p => p.Level == curPoint.Level + 1).FirstOrDefault();
+                                //存在后续工作区间
+                                if (curPoint != null)
                                 {
-                                    endTime = nextPoint.StartPoint;
+                                    endTime = curPoint.StartPoint;
                                 }
                             }
                             beginTime = endTime;
                             //工单排产完成,排产时长=工单工作时长
                             item.Worked = item.LbrVar;
                             item.QtyWorked = item.QtyOrded;
-                            sumTsTimes -= needTime;
                         }
-                        if (dto.EffTime == sumTsTimes)//当天产能完全占用
+                        //当天产能完全占用,且清场时间在下班后
+                        if (curPoint == null)
                         {
                             sumTsTimes = 0;//排产完毕,特殊工单时长置0
                             //获取下一个工作日
                             workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
+                            isFstDay = true;
                             //特殊工单排产完成,占用了当天的全部产能,则需要判断下一个工作日是否存在特殊工单,如果存在,则需要继续排特殊工单
                             secWOMasters = tsWorkOrds.Where(p => p.ProdLine == lineStart.Line && p.OrdDate.Value.Date == workStartTime.Date).OrderBy(p => p.OrdDate).ToList();
-                            //此时需要过滤掉已排产的工单
-                            secWOMasters = secWOMasters.Where(p => !workDtos.Select(m => m.WorkOrd).Contains(p.WorkOrd)).ToList();
                             //记录新增待排产特殊工单
-                            foreach (var item in secWOMasters)
+                            if (secWOMasters.Any())
                             {
-                                int op = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First().OP;
-                                workDtos.Add(new WorkOrdMstDto
+                                sumCleanTimes = 0m;
+                                //新增特殊工单最后一个清场时长(分钟)
+                                secCleanTime = 0m;
+                                foreach (var item in secWOMasters)
                                 {
-                                    WorkOrd = item.WorkOrd,
-                                    ItemNum = item.ItemNum,
-                                    QtyOrded = item.QtyOrded,
-                                    LbrVar = item.LbrVar * 60,
-                                    Worked = 0,
-                                    QtyWorked = 0,
-                                    Op = op
-                                });
+                                    var curOp = tsWoRoutings.Where(p => p.WorkOrd == item.WorkOrd).OrderByDescending(p => p.OP).First();
+                                    workDtos.Add(new WorkOrdMstDto
+                                    {
+                                        WorkOrd = item.WorkOrd,
+                                        ItemNum = item.ItemNum,
+                                        QtyOrded = item.QtyOrded,
+                                        LbrVar = item.LbrVar * 60,
+                                        Worked = 0,
+                                        QtyWorked = 0,
+                                        Op = curOp.OP,
+                                        WaitTime = curOp.WaitTime * 60
+                                    });
+                                    sumCleanTimes += curOp.WaitTime * 60;
+                                }
+                                //获取最后一个特殊工单的清场时长
+                                var last = secWOMasters.Last();
+                                secCleanTime = tsWoRoutings.Where(p => p.WorkOrd == last.WorkOrd).OrderByDescending(p => p.OP).First().WaitTime * 60;
+                                //特殊工单待排产时长=前一天特殊工单到最后一个清场时长+新增的特殊工单生产时长+新增特殊工单除最后一个清场时长之外的清场时长之和
+                                sumTsTimes += (lstCleanTime + secWOMasters.Sum(p => p.LbrVar) * 60 + sumCleanTimes - secCleanTime);
+                                lstCleanTime = secCleanTime;
                             }
-                            //特殊工单待排产时长增加新增带排产特殊工单生产时长
-                            sumTsTimes += secWOMasters.Sum(p => p.LbrVar) * 60;
                         }
-                        else {//当天可用生产时长大于特殊工单生产时长
-                            sumTsTimes = 0;//排产完毕,特殊工单时长置0
+                        //当天可用生产时长大于特殊工单生产时长+清场时长
+                        else
+                        {
+                            //排产完毕,特殊工单时长置0
+                            sumTsTimes = 0;
                             //处理workStartTime
                             workStartTime = endTime;
-                            var curPoint = workPoints.Find(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
+                            curPoint = workPoints.Find(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
                             if (endTime == curPoint.EndPoint)
                             {
                                 var nextPoint = workPoints.Find(p => p.Level == curPoint.Level + 1);
@@ -705,7 +844,8 @@ namespace Business.ResourceExamineManagement
                             }
                         }
                     }
-                    else//当天的可用产能不满足特殊工单生产时长,则当天产能全部排特殊工单
+                    //当天的可用产能不满足特殊工单生产时长,则当天产能全部排特殊工单(至少最后一个特殊工单的生产时长不能满足)
+                    else
                     {
                         decimal residueTime = dto.EffTime;//当天产能剩余产能(分钟)
                         DateTime beginTime = workStartTime;//排产开始时间
@@ -718,7 +858,8 @@ namespace Business.ResourceExamineManagement
                             }
                             //当前工单剩余待排产时长(分钟)
                             decimal needTime = item.LbrVar - item.Worked;
-                            if (residueTime >= needTime)//当天剩余产能满足当前工单的剩余待排产时长
+                            //当天剩余产能满足当前工单的剩余待排产时长
+                            if (residueTime >= needTime)
                             {
                                 //计算工单排产结束时间
                                 var curPoint = workPoints.Find(p => p.StartPoint <= beginTime && beginTime <= p.EndPoint);
@@ -745,6 +886,50 @@ namespace Business.ResourceExamineManagement
                                         nextMins -= p.WorkMinutes;
                                     }
                                 }
+                                //当天剩余产能
+                                residueTime -= needTime;
+                                //处理清场时间,两种场景:
+                                //1、residueTime<=item.WaitTime,结束时间加上剩余清场时长
+                                if (residueTime <= item.WaitTime)
+                                {
+                                    decimal otherTime = item.WaitTime - residueTime;
+                                    endTime = dto.EndTime.AddMinutes((double)otherTime);
+                                    residueTime = 0;
+                                }
+                                //2、residueTime>item.WaitTime,剩余时长大于,清场时长,需要过滤休息时间
+                                else
+                                {
+                                    //清场时长
+                                    needTime = item.WaitTime;
+                                    //获取工单排产结束时间所处时间段
+                                    curPoint = workPoints.Find(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
+                                    span = curPoint.EndPoint - endTime;
+                                    //当前工作时间段的有效生产时间(分钟)
+                                    effMins = (decimal)span.TotalMinutes;
+                                    //当前工作时间段即可清场时长
+                                    if (effMins >= needTime)
+                                    {
+                                        endTime = endTime.AddMinutes((double)needTime);
+                                    }
+                                    else
+                                    {
+                                        //获取后续生产时间段
+                                        var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).OrderBy(p => p.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;
+                                        }
+                                    }
+                                    residueTime -= needTime;
+                                }
+
                                 //记录生产周期
                                 curSequences.Add(new PeriodSequenceDet
                                 {
@@ -774,27 +959,28 @@ namespace Business.ResourceExamineManagement
                                     WorkEndTime = endTime,
                                     CreateTime = DateTime.Now
                                 });
+                                //当前工单已排产完成,已排产时间=工单生产时长,已排产数量=工单数量
+                                item.Worked = item.LbrVar;
+                                item.QtyWorked = item.QtyWorked;
+
                                 beginTime = endTime;
+                                //获取结束时间所处时间段
                                 curPoint = workPoints.Find(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
-                                if (endTime == curPoint.EndPoint)
+                                if (curPoint != null && endTime == curPoint.EndPoint)
                                 {
                                     var nextPoint = workPoints.Find(p => p.Level == curPoint.Level + 1);
                                     if (nextPoint != null)
                                     {
-                                        beginTime = nextPoint.StartPoint;
+                                        endTime = nextPoint.StartPoint;
                                     }
+                                    beginTime = endTime;
                                 }
-                                //当前工单已排产完成,已排产时间=工单生产时长,已排产数量=工单数量
-                                item.Worked = item.LbrVar;
-                                item.QtyWorked = item.QtyWorked;
-                                //当天剩余产能
-                                residueTime -= needTime;
-                                if (residueTime == 0)
-                                {
+                                else {
                                     break;
                                 }
                             }
-                            else//当天剩余产能不满足当前工单的剩余待排产时长
+                            //当天剩余产能不满足当前工单的剩余待排产时长
+                            else
                             {
                                 //计算生产数量
                                 decimal qty = Math.Ceiling(residueTime / item.LbrVar * item.QtyOrded);
@@ -830,13 +1016,14 @@ namespace Business.ResourceExamineManagement
                                 item.Worked += residueTime;
                                 item.QtyWorked += qty;
                                 residueTime = 0;
+                                //特殊工单剩余待排产时长(分钟)
+                                sumTsTimes -= residueTime;
                                 break;
                             }
                         }
-                        //特殊工单剩余待排产时长(分钟)
-                        sumTsTimes -= dto.EffTime;
                         //获取下一个工作日
                         workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
+                        isFstDay = false;
                     }
                 }
                 //记录已排产特殊工单
@@ -850,8 +1037,8 @@ namespace Business.ResourceExamineManagement
                 fstWOMasters.Clear();
                 workDtos.Clear();
                 sumTsTimes = 0m;
-                //正常工单排产第一天标识
-                bool isFstDay = true;
+                //工单排产第一天标识
+                isFstDay = true;
                 //前一天是否有特殊工单未排产完全
                 bool isFullPC = true;
                 while (sumQty < workOrd.QtyOrded || sumTsTimes > 0)
@@ -2552,7 +2739,7 @@ namespace Business.ResourceExamineManagement
         {
             foreach (var item in workOrds)
             {
-                //工单生产时长(分钟)
+                //工单生产时长(分钟)
                 decimal sumTimes = item.LbrVar;
                 //工单的排产开始时间
                 DateTime workStartTime = item.OrdDate;
@@ -2578,7 +2765,7 @@ namespace Business.ResourceExamineManagement
                     //获取当天的产能
                     LineScheduledDto dto = GetScheduledPoint(lineStart, workStartTime, mLCalendars, mlqtyWorkDtls);
                     //当天的可用生产时长不能满足特殊工单生产时长
-                    if (dto.EffTime <= sumTimes)
+                    if (dto.EffTime < sumTimes)
                     {
                         decimal qty = Math.Ceiling(dto.EffTime / item.LbrVar * item.QtyOrded);
                         //记录生产周期
@@ -2614,14 +2801,19 @@ namespace Business.ResourceExamineManagement
                         sumTimes -= dto.EffTime;
                         //已排产数量
                         item.QtyWorked += qty;
+                        item.Worked += dto.EffTime;
                         //继续排下一个工作日
                         workStartTime = GetNextWorkDay((int)workStartTime.DayOfWeek, workStartTime, mLCalendars);
                     }
-                    else// 最后一天的产能只能占用一部分
+                    //最后一天的产能全部占用或只能占用一部分
+                    else
                     {
                         //剩余生产时长(分钟)
                         decimal workTime = sumTimes;
+                        //排产开始时间位于哪个时间段
                         var curPoint = workPoints.Find(p => p.StartPoint <= workStartTime && workStartTime <= p.EndPoint);
+                        //结束时间位于时间段
+                        var lastPoint = new LineWorkPointDto();
                         TimeSpan span = curPoint.EndPoint - workStartTime;
                         //当前工作时间段的有效生产时间
                         decimal effMins = (decimal)span.TotalMinutes;
@@ -2640,12 +2832,45 @@ namespace Business.ResourceExamineManagement
                             {
                                 if (p.WorkMinutes >= nextMins)
                                 {
+                                    //记录结束时间位于哪个时间段
+                                    lastPoint = p;
                                     workEndTime = p.StartPoint.AddMinutes((double)nextMins);
                                     break;
                                 }
                                 nextMins -= p.WorkMinutes;
                             }
                         }
+                        //计算清场时间
+                        //剩余可用工作时长(分钟)
+                        decimal remainTime = CalcRemainWorkTime(workEndTime,workPoints);
+                        //剩余可用工作时长满足清场时长
+                        if (remainTime >= item.WaitTime)
+                        {
+                            //清场需要时长(分钟)
+                            decimal needTime = item.WaitTime;
+                            //获取排产结束时间所处以及之后的生产时间段
+                            var nextPoints = workPoints.Where(p => p.Level >= lastPoint.Level).OrderBy(p => p.Level).ToList();
+                            //处理第一个时间段的开始时间
+                            nextPoints[0].StartPoint = workEndTime;
+                            foreach (var p in nextPoints)
+                            {
+                                span = p.EndPoint - p.StartPoint;
+                                //当前工作时间段的有效生产时间
+                                effMins = (decimal)span.TotalMinutes;
+                                if (effMins >= needTime)
+                                {
+                                    workEndTime = p.StartPoint.AddMinutes((double)needTime);
+                                    break;
+                                }
+                                needTime -= effMins;
+                            }
+                        }
+                        //剩余可用工作时长不能满足清场时长
+                        else
+                        {
+                            decimal needTime = item.WaitTime - remainTime;
+                            workEndTime.AddMinutes((double)needTime);
+                        }
                         //记录生产周期
                         periodsDet.Add(new PeriodSequenceDet
                         {
@@ -2682,6 +2907,26 @@ namespace Business.ResourceExamineManagement
             }
         }
 
+        /// <summary>
+        /// 计算当天排产完成的剩余生产时长
+        /// </summary>
+        /// <param name="endTime">排产结束时间</param>
+        /// <param name="workPoints">当天的工作时间段</param>
+        /// <returns></returns>
+        public decimal CalcRemainWorkTime(DateTime endTime, List<LineWorkPointDto> workPoints)
+        {
+            decimal remainTime = 0m;
+            var curPoint = workPoints.Find(p => p.StartPoint <= endTime && endTime <= p.EndPoint);
+            TimeSpan span = curPoint.EndPoint - endTime;
+            remainTime = (decimal)span.TotalMinutes;
+            var nextPoints = workPoints.Where(p => p.Level > curPoint.Level).OrderBy(p => p.Level).ToList();
+            foreach (var item in nextPoints)
+            {
+                remainTime += item.WorkMinutes;
+            }
+            return remainTime;
+        }
+
         /// <summary>
         /// 获取产线当天的开工时间,结束时间,有效工作时长,生产数量
         /// </summary>

+ 21 - 20
MicroServices/Business/Business.Application/ResourceExamineManagement/ResourceExamineAppService.cs

@@ -3573,26 +3573,6 @@ namespace Business.ResourceExamineManagement
             return Wildcard.Replace(serialNumber, formData);
         }
 
-        /// <summary>
-        /// 生产排产
-        /// </summary>
-        /// <param name="workOrd"></param>
-        /// <returns></returns>
-        /// <exception cref="NotImplementedException"></exception>
-        public async Task<string> ProductionSchedule(string workOrd,string domain)
-        {
-            List<string> workOrds = workOrd.Split(",").ToList();
-            if (workOrds.Count == 0)
-            {
-                return "没有需要排产的工单。";
-            }
-            //获取工单数据
-            var workOrdMasters = _workOrdMaster.Select(p => workOrds.Contains(p.WorkOrd) && p.Domain == domain);
-            //排产
-            //await _productionScheduleAppService.DoProductSchedule(workOrdMasters, workOrdMasters[0].Domain, 1);
-            return "ok";
-        }
-
         /// <summary>
         /// 生成领料单
         /// </summary>
@@ -4154,5 +4134,26 @@ namespace Business.ResourceExamineManagement
                 dlist.Add(d);
             });
         }
+
+        /// <summary>
+        /// 手动排产接口
+        /// </summary>
+        /// <param name="domain"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        public async Task<string> ProductionSchedule(string 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.AddDays(28).AddDays(1);
+            //取数开始时间需要排除掉锁定期内的工单
+            DateTime startDate = DateTime.Now.Date.AddDays(1).AddDays((double)lockDays);
+            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 _productionScheduleAppService.DoProductSchedule(workOrds, domain, 2);
+            return "ok";
+        }
     }
 }

+ 6 - 0
MicroServices/Business/Business.Domain/StructuredDB/Production/WorkOrdRouting.cs

@@ -81,6 +81,12 @@ namespace Business.Domain
         [Comment("订单数量")]
         public decimal? QtyOrded { get; set; }
 
+        /// <summary>
+        /// 工序清场时长(小时)
+        /// </summary>
+        [Comment("工序清场时长")]
+        public decimal WaitTime { get; set; }
+
         /// <summary>
         /// 平行加工件数,下序开工前置数量
         /// </summary>

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

@@ -35,13 +35,13 @@ namespace Business.Controllers
         /// <summary>
         /// 生产排产
         /// </summary>
-        /// <param name="workOrds"></param>
+        /// <param name="domain"></param>
         /// <returns></returns>
         [HttpPost]
         [Route("productionschedule")]
-        public Task<string> ProductionSchedule(string workOrds, string domain)
+        public Task<string> ProductionSchedule(string domain)
         {
-            return _ResourceExamineAppService.ProductionSchedule(workOrds, domain);
+            return _ResourceExamineAppService.ProductionSchedule(domain);
         }
 
         /// <summary>