using GB5Shared.DTO.Framework.Login; using MMDAL.CustomCode.Scheduling; using MMDAL.DTO.Scheduling; namespace MMBLL.Scheduling.Engine { public class BatchEngine : IBatchEngine { private readonly IBatchRuleDAL _BatchRuleDAL; public BatchEngine(IBatchRuleDAL batchRuleDAL) { _BatchRuleDAL = batchRuleDAL; } public async Task> GroupAsync( IReadOnlyList eus, LoginDTO login, CancellationToken ct) { var groups = new List(); // Group EUs by (WorkCenterId, ProcessId, ItemId) — the batch key var batchCandidates = eus.GroupBy(e => (e.WorkCenterId, e.ProcessId, e.ItemId,e.SkuId)); foreach (var candidate in batchCandidates) { var rule = await _BatchRuleDAL .GetBatchRuleForProcess(candidate.Key.WorkCenterId, candidate.Key.ProcessId, login, ct) .ConfigureAwait(false); if (rule is null) { // No batch rule — each EU becomes its own single-item group foreach (var eu in candidate) groups.Add(SingleGroup(eu)); continue; } // Accumulate EUs into batches respecting Min/Max/Preferred quantities var batch = new List(); decimal accum = 0; foreach (var eu in candidate) { batch.Add(eu); accum += eu.Quantity; bool isFull = rule.BatchMaximum > 0 && accum >= rule.BatchMaximum; if (isFull) { groups.Add(BuildGroup(batch, rule.BatchContinuity, candidate.Key.WorkCenterId, candidate.Key.ProcessId)); batch = new List(); accum = 0; } } // Remaining EUs in partial batch (only commit if >= minimum) if (batch.Count > 0) { if (rule.BatchMinimum > 0 && accum < rule.BatchMinimum) { // Below minimum — schedule individually rather than holding demand foreach (var eu in batch) groups.Add(SingleGroup(eu)); } else { groups.Add(BuildGroup(batch, rule.BatchContinuity, candidate.Key.WorkCenterId, candidate.Key.ProcessId)); } } } return groups; } private static BatchGroup SingleGroup(ExecutionUnit eu) => new() { RootBatchNumber = $"SGL-{eu.EuId}", Items = new List { eu }, CombinedDurationMinutes = eu.DurationMinutes, TotalQuantity = eu.Quantity, BatchContinuity = eu.BatchContinuity, WorkCenterId = eu.WorkCenterId, ProcessId = eu.ProcessId }; private static BatchGroup BuildGroup( List batch, byte batchContinuity, int workCenterId, int processId) => new() { RootBatchNumber = $"BCH-{batch[0].EuId}", Items = new List(batch), CombinedDurationMinutes = batch.Sum(e => e.DurationMinutes), TotalQuantity = batch.Sum(e => e.Quantity), BatchContinuity = batchContinuity, WorkCenterId = workCenterId, ProcessId = processId }; } }