From ba87992368970f6bf5b4ac2746880456b0ee9237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=99=93=E4=B8=9C?= <17363321594@163.com> Date: Sun, 28 Sep 2025 08:36:03 +0800 Subject: [PATCH] =?UTF-8?q?MRP=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MES/mm/paintedparts_call/MmCallService.cs | 255 +++++++++--------- 1 file changed, 133 insertions(+), 122 deletions(-) diff --git a/DOAN.Service/MES/mm/paintedparts_call/MmCallService.cs b/DOAN.Service/MES/mm/paintedparts_call/MmCallService.cs index 35b7347..7f783a9 100644 --- a/DOAN.Service/MES/mm/paintedparts_call/MmCallService.cs +++ b/DOAN.Service/MES/mm/paintedparts_call/MmCallService.cs @@ -59,6 +59,8 @@ namespace DOAN.Service.MES.mm.paintedparts_call .AndIF(parm.WorkOrderDate != null, it => it.WorkOrderDate == parm.WorkOrderDate) .And(it => it.Quantity > 0); var response = Queryable() + .OrderBy(m => m.MaterialCode) // 按预定义的物料号顺序 + .OrderBy(m => m.LineCode) .Where(predicate.ToExpression()) .ToPage(parm); @@ -202,20 +204,49 @@ namespace DOAN.Service.MES.mm.paintedparts_call .Queryable() .WhereIF(!string.IsNullOrEmpty(parm.LineCode) && !parm.LineCode.Contains("全部"), it => it.LineCode == parm.LineCode) .Where(x => x.WorkorderDate == checkDate) - .Select(x => new { x.ProductionCode, x.DeliveryNum }) + .Select(x => new { x.ProductionCode, x.DeliveryNum, x.LineCode }) .ToList(); - List lineProductionCodeList = workorders - .Select(x => x.ProductionCode) - .Distinct() + // 1. 按ProductionCode和LineCode分组并计算每个组的累加数量 + var productionGroups = workorders + .GroupBy(x => new { x.ProductionCode, x.LineCode }) + .Select(g => new + { + ProductionCode = g.Key.ProductionCode, + LineCode = g.Key.LineCode, + GroupTotal = g.Sum(x => x.DeliveryNum) ?? 0 + }) .ToList(); - if (!lineProductionCodeList.Any()) + + if (!productionGroups.Any()) { Context.Ado.RollbackTran(); return 0; - //throw new Exception("未找到该线别对应的工单"); } + // 计算所有组的总数量 + int totalQuantityAcrossAllGroups = productionGroups.Sum(g => g.GroupTotal); + if (totalQuantityAcrossAllGroups <= 0) + { + Context.Ado.RollbackTran(); + return 0; + } + + // 将总数量分成四份 + int baseBatch = totalQuantityAcrossAllGroups / 4; + int remainder = totalQuantityAcrossAllGroups % 4; + int[] batchQuantities = new int[4]; + for (int i = 0; i < 4; i++) + { + batchQuantities[i] = baseBatch + (i < remainder ? 1 : 0); + } + + // 获取所有唯一的ProductionCode + List lineProductionCodeList = productionGroups + .Select(x => x.ProductionCode) + .Distinct() + .ToList(); + // 查询 BOM 表 List mmCallMaterialBoms = Context .Queryable() @@ -223,58 +254,10 @@ namespace DOAN.Service.MES.mm.paintedparts_call .Where(it => !string.IsNullOrEmpty(it.subInvCode)) .ToList(); - // 计算每个 ProductionCode 的 DeliveryNum 总和 - var totalDeliveryDict = workorders - .GroupBy(x => x.ProductionCode) - .ToDictionary(g => g.Key, g => g.Sum(x => x.DeliveryNum)); - // 生成 MmCallRequests List mmCallMrpList = new(); - //// 1. 按工单顺序排序 (假设按ProductionCode排序,可根据实际需求调整) - //var sortedWorkorders = workorders.OrderBy(x => x.ProductionCode).ToList(); - - //// 2. 计算总交货量并拆分为四组 - //int totalDeliveryNum = sortedWorkorders.Sum(x => x.DeliveryNum).Value; - //int groupTarget = totalDeliveryNum / 4; - - //List> groups = new List>(); - //List currentGroup = new List(); - //int currentSum = 0; - - //foreach (var wo in sortedWorkorders) - //{ - // currentGroup.Add(wo); - // currentSum += wo.DeliveryNum.Value; - - // if (currentSum >= groupTarget * (groups.Count + 1)) - // { - // groups.Add(currentGroup); - // currentGroup = new List(); - // currentSum = 0; - // } - //} - - //// 处理剩余工单 - //if (currentGroup.Any()) - //{ - // if (groups.Count < 4) - // { - // groups.Add(currentGroup); - // } - // else - // { - // groups[3].AddRange(currentGroup); - // } - //} - - //// 确保有四组 - //while (groups.Count < 4) - //{ - // groups.Add(new List()); - //} - - // 3. 定义四个时间段 + // 定义四个时间段 var timePeriods = new List<(DateTime Start, DateTime End)> { (checkDate.Date.AddHours(8), checkDate.Date.AddHours(10)), // 8:00~10:00 @@ -283,89 +266,117 @@ namespace DOAN.Service.MES.mm.paintedparts_call (checkDate.Date.AddHours(14), checkDate.Date.AddHours(16)) // 14:00~16:00 }; - // 4. 分时段计算物料需求 - foreach (var (startTime, endTime) in timePeriods) + // 4. 为每个时间段分配批次数量(核心修复部分) + foreach (var timePeriod in timePeriods.Select((tp, idx) => new { Period = tp, Index = idx })) { - // 获取该组的ProductionCode列表并转换为HashSet以提高查找效率 - // 显式指定泛型类型参数确保从dynamic转换为string - var groupProductionCodes = new HashSet(lineProductionCodeList); + var (startTime, endTime) = timePeriod.Period; + int currentBatchIndex = timePeriod.Index; + int currentBatchQuantity = batchQuantities[currentBatchIndex]; - //// 计算该组的DeliveryNum字典 - //var groupDeliveryDict = group - // .GroupBy(x => x.ProductionCode) - // .ToDictionary(g => g.Key, g => g.Sum(x => x.DeliveryNum)); + if (currentBatchQuantity <= 0) + continue; - // 按linecode与MaterialCode进行聚合统计 - var aggregatedData = mmCallMaterialBoms - .Where(bom => groupProductionCodes.Contains(bom.InvCode)) - .GroupBy(bom => new - { - bom.subInvCode, - bom.subInvName, - bom.InvCode, - bom.InvName - }) - .Select(g => new - { - ProductionCode = g.Key.InvCode, - ProductionName = g.Key.InvName, - MaterialCode = g.Key.subInvCode, - MaterialName = g.Key.subInvName, - TotalQuantity = g.Sum(bom => - { - int totalDeliveryNum = workorders - .Where(w => w.ProductionCode == bom.InvCode) - .Sum(w => w.DeliveryNum) ?? 0; + // 计算比例分配比例 + decimal batchRatio = (decimal)currentBatchQuantity / totalQuantityAcrossAllGroups; - if (totalDeliveryNum <= 0) - { - return 0m; // 没有交货量则跳过 - } + // 使用具体元组存储分配信息,避免dynamic类型问题 + var groupAllocations = new List<(string ProductionCode, string LineCode, int Allocated)>(); + int itemIndex = 0; - decimal iusequantity; - if (!decimal.TryParse(bom.Iusequantity, out iusequantity)) - { - Context.Ado.RollbackTran(); - throw new Exception($"{bom.subInvCode}BOM使用数量异常"); - } - - return totalDeliveryNum * iusequantity; - }) - }) - .ToList(); - - // 生成该时间段的物料需求记录 - foreach (var item in aggregatedData) + foreach (var group in productionGroups) { - mmCallMrpList.Add( - new MmCallMrp + int allocated = itemIndex < productionGroups.Count - 1 + ? (int)Math.Round(group.GroupTotal * batchRatio) + : currentBatchQuantity - groupAllocations.Sum(a => a.Allocated); + + groupAllocations.Add((group.ProductionCode, group.LineCode, allocated)); + itemIndex++; + } + + // 生成当前时间段的物料需求记录 + foreach (var allocation in groupAllocations) + { + if (allocation.Allocated <= 0) continue; + + // 获取BOM数据并计算需求 + var relatedBoms = mmCallMaterialBoms + .Where(bom => bom.InvCode == allocation.ProductionCode) + .ToList(); + + var aggregatedData = relatedBoms + .GroupBy(bom => new { bom.subInvCode, bom.subInvName, bom.InvCode, bom.InvName }) + .Select(g => new + { + ProductionCode = g.Key.InvCode, + ProductionName = g.Key.InvName, + MaterialCode = g.Key.subInvCode, + MaterialName = g.Key.subInvName, + TotalQuantity = g.Sum(bom => + { + decimal iusequantity; + decimal.TryParse(bom.Iusequantity, out iusequantity); + return allocation.Allocated * iusequantity; + }) + }) + .ToList(); + + // 为每个物料创建带时间段信息的记录 + foreach (var item in aggregatedData) + { + mmCallMrpList.Add(new MmCallMrp { Id = SnowFlakeSingle.Instance.NextId().ToString(), - LineCode = lineCode, + LineCode = allocation.LineCode, WorkOrderDate = parm.WorkOrderDate.Value, ProductionCode = item.ProductionCode, ProductionName = item.ProductionName, MaterialCode = item.MaterialCode, MaterialName = item.MaterialName, Quantity = (int?)item.TotalQuantity, - IssuanceTime = - startTime.ToString("HH:mm") + "-" + endTime.ToString("HH:mm"), - Sort = getSort(startTime), + IssuanceTime = $"{startTime:HH:mm}-{endTime:HH:mm}", + Sort = currentBatchIndex + 1, CreatedBy = "系统自动生成", CreatedTime = DateTime.Now, - } - ); + }); + } } } - mmCallMrpList = mmCallMrpList - .Where(it => !string.IsNullOrEmpty(it.MaterialCode) && - (it.MaterialCode.Contains("-") || it.MaterialCode.Contains("—"))) - .ToList(); - foreach (var mrp in mmCallMrpList.Take(10)) + // 过滤并合并相同物料号和线别的记录 + var processedList = mmCallMrpList + .Where(it => !string.IsNullOrEmpty(it.MaterialCode) && + (it.MaterialCode.Contains("-") || it.MaterialCode.Contains("—"))) + .GroupBy(m => new { m.MaterialCode, m.LineCode, m.IssuanceTime }) // 按时间段分组合并 + .Select(g => new MmCallMrp { - Console.WriteLine($"MaterialCode: {mrp.MaterialCode}"); - } + Id = SnowFlakeSingle.Instance.NextId().ToString(), + LineCode = g.Key.LineCode, + WorkOrderDate = g.First().WorkOrderDate, + ProductionCode = g.First().ProductionCode, + ProductionName = g.First().ProductionName, + MaterialCode = g.Key.MaterialCode, + MaterialName = g.First().MaterialName, + Quantity = g.Sum(m => m.Quantity), + IssuanceTime = g.Key.IssuanceTime, // 保留时间段信息 + Sort = g.First().Sort, + CreatedBy = "系统自动生成", + CreatedTime = DateTime.Now + }) + .ToList(); + + // 创建排序键字典确保相同物料号排在一起 + var materialOrderDict = processedList + .Select(m => m.MaterialCode) + .Distinct() + .OrderBy(MaterialCode => MaterialCode) + .Select((MaterialCode, index) => new { MaterialCode, index }) + .ToDictionary(item => item.MaterialCode, item => item.index); + + // 按物料号和时间段排序 + mmCallMrpList = processedList + .OrderBy(m => materialOrderDict[m.MaterialCode]) + .ThenBy(m => m.Sort) // 按时间段排序 + .ToList(); // 删除旧数据并插入新数据 Context @@ -377,7 +388,6 @@ namespace DOAN.Service.MES.mm.paintedparts_call { Context.Ado.CommitTran(); return 0; - //throw new Exception($"该线别:{lineCode}日期:{checkDate}无涂装MRP清单"); } int result = Context.Insertable(mmCallMrpList).ExecuteCommand(); @@ -401,14 +411,15 @@ namespace DOAN.Service.MES.mm.paintedparts_call public static int getSort(DateTime startTime) { - if(startTime.ToString("HH:mm") == "08:00") + if (startTime.ToString("HH:mm") == "08:00") { return 1; } - else if(startTime.ToString("HH:mm") == "10:00") + else if (startTime.ToString("HH:mm") == "10:00") { return 2; - }else if(startTime.ToString("HH:mm") == "12:00") + } + else if (startTime.ToString("HH:mm") == "12:00") { return 3; }