public IEnumerable <Tuple <IMaterialBatch, Amount> > AutoResolve(int materialId, Amount requiredAmount, bool unresolvedAsNullBatch = false, int?batchId = null) { var batches = new List <MaterialBatchComponent>(); if (batchId == null) { batches.AddRange(m_batchRepository.GetMaterialBatches( DateTime.Now.AddYears(-1), DateTime.Now.AddYears(1), false, materialId).OrderBy(b => b.Batch.Created)); } else { var batch = m_batchRepository.GetBatchById(batchId.Value).Ensure(); if (batch.Batch.MaterialId != materialId) { throw new InvalidOperationException("Invalid entity reference"); } batches.Add(batch); } foreach (var batch in batches) { if (!requiredAmount.IsPositive) { yield break; } var batchAvailableAmount = GetAvailableAmount(batch.Batch.Id); if (!batchAvailableAmount.IsPositive) { continue; } var amountToAllocate = m_amountProcessor.Min(requiredAmount, batchAvailableAmount); yield return(new Tuple <IMaterialBatch, Amount>(batch.Batch, amountToAllocate)); requiredAmount = m_amountProcessor.Subtract(requiredAmount, amountToAllocate); } if (requiredAmount.IsPositive && unresolvedAsNullBatch) { yield return(new Tuple <IMaterialBatch, Amount>(null, requiredAmount)); } }
public void SaveEvent(int eventTypeId, int materialId, string batchNumber, decimal quantity, string reason, string unitSymbol, long?sourceOrderId = null) { var eventType = GetAllEventTypes().FirstOrDefault(et => et.Id == eventTypeId).Ensure(); if (!eventType.IsSubtracting) { throw new NotSupportedException("Toto jeste neni"); } if (eventType.RequiresNote && ((reason?.Trim() ?? string.Empty).Length < 5)) { throw new InvalidOperationException("Důvod musí mít alespoň 5 znaků"); } var material = m_materialRepository.GetMaterialById(materialId).Ensure(); if (!material.AutomaticBatches && string.IsNullOrWhiteSpace(batchNumber?.Trim())) { throw new InvalidOperationException("Je třeba zadat číslo šarže"); } var unit = material.NominalUnit; if (!string.IsNullOrWhiteSpace(unitSymbol)) { unit = m_unitRepository.GetUnitBySymbol(unitSymbol); if (unit == null || (!m_conversionHelper.AreCompatible(material.NominalUnit.Id, unit.Id))) { throw new InvalidOperationException($"Pro materiál \"{material.Name}\" nelze použít jednotku \"{unitSymbol}\""); } } var batches = new List <IMaterialBatch>(); if (!string.IsNullOrWhiteSpace(batchNumber)) { batches.AddRange(m_batchRepository.GetBatches(new BatchKey(material.Id, batchNumber)).OrderBy(b => b.Created)); } else { batches.AddRange(m_batchRepository .GetMaterialBatches(DateTime.Now.AddYears(-2), DateTime.Now.AddYears(99), true, materialId, false, false, false).Select(b => b.Batch).OrderBy(b => b.Created)); } var eventAmount = new Amount(quantity, unit); var groupingKey = Guid.NewGuid().ToString("N"); var eventDt = DateTime.Now; using (var tx = m_cache.OpenTransaction()) { foreach (var batch in batches) { var available = m_batchFacade.Value.GetAvailableAmount(batch.Id); if (available.IsNotPositive) { continue; } var toProcess = m_amountProcessor.Min(available, eventAmount); var evt = m_cache.New <IMaterialStockEvent>(); evt.EventGroupingKey = groupingKey; evt.BatchId = batch.Id; evt.Delta = toProcess.Value; evt.UnitId = toProcess.Unit.Id; evt.Note = reason; evt.TypeId = eventTypeId; evt.UserId = m_session.User.Id; evt.EventDt = eventDt; evt.SourcePurchaseOrderId = sourceOrderId; m_cache.Save(evt); m_batchFacade.Value.ReleaseBatchAmountCache(batch.Id); eventAmount = m_amountProcessor.Subtract(eventAmount, toProcess); if (!eventAmount.IsPositive) { break; } } tx.Commit(); } }
private void SaveAllocation(int eventId, List <ISaleEventAllocation> existing, SaleEventAllocationDto dto) { var existingAllocations = existing .Where(a => a.Batch.MaterialId == dto.MaterialId && a.Batch.BatchNumber == dto.BatchNumber).ToList(); if (existing.Any() && !existingAllocations.Any()) { throw new InvalidOperationException("Pri zmene existujici prodejni akce nesmi byt zmeneny polozky, jen vracene mnozstvi"); } if (dto.ReturnedQuantity != null && m_amountProcessor.GreaterThan(dto.ReturnedQuantity, dto.AllocatedQuantity)) { throw new InvalidOperationException("Nelze vracet vetsi nez blokovane mnozstvi"); } if (!existingAllocations.Any()) { var batches = m_batchFacade.ProposeAllocations(dto.MaterialId, dto.BatchNumber, dto.AllocatedQuantity) .ToList(); if (batches.Any(b => b.Item1 == null)) { throw new InvalidOperationException($"Pozadovane mnozstvi {dto.AllocatedQuantity} {m_materialRepository.GetMaterialById(dto.MaterialId)?.Name} neni v sarzi {dto.BatchNumber} k dispozici"); } foreach (var batch in batches) { m_database.Save(m_database.New <ISaleEventAllocation>(a => { a.AllocationDt = DateTime.Now; a.AllocatedQuantity = batch.Item2.Value; a.UnitId = batch.Item2.Unit.Id; a.BatchId = batch.Item1.Value; a.AllocationUserId = m_session.User.Id; a.SaleEventId = eventId; if (dto.ReturnedQuantity != null) { a.ReturnedQuantity = dto.ReturnedQuantity.Value; a.ReturnDt = DateTime.Now; a.ReturnUserId = m_session.User.Id; } })); } return; } var totalAllocatedInDb = m_amountProcessor.Sum(existingAllocations.Select(a => new Amount(a.AllocatedQuantity, a.Unit))); if (!m_amountProcessor.AreEqual(totalAllocatedInDb, dto.AllocatedQuantity)) { throw new InvalidOperationException("Nelze zmenit alokovane mnozstvi pro existujici akci"); } var totalReturnedInDb = m_amountProcessor.Sum(existingAllocations.Where(a => a.ReturnedQuantity != null) .Select(a => new Amount(a.ReturnedQuantity.Value, a.Unit))); var returnedAmount = dto.ReturnedQuantity == null ? new Amount(0, dto.AllocatedQuantity.Unit) : dto.ReturnedQuantity; if (m_amountProcessor.AreEqual(totalReturnedInDb, returnedAmount)) { return; } if (m_amountProcessor.GreaterThan(totalReturnedInDb, returnedAmount)) { throw new InvalidOperationException("Nelze snizovat vracene mnozstvi"); } var newReturn = m_amountProcessor.Subtract(returnedAmount, totalReturnedInDb); foreach (var existingAloc in existingAllocations) { if (!newReturn.IsPositive) { break; } var alreadyReturnedHere = existingAloc.ReturnedQuantity == null ? new Amount(0, existingAloc.Unit) : new Amount(existingAloc.ReturnedQuantity.Value, existingAloc.Unit); var toReturnHere = m_amountProcessor.Subtract(new Amount(existingAloc.AllocatedQuantity, existingAloc.Unit), alreadyReturnedHere); if (!toReturnHere.IsPositive) { continue; } var actuallyReturningHere = m_amountProcessor.Min(toReturnHere, newReturn); existingAloc.ReturnedQuantity = actuallyReturningHere.Value; existingAloc.UnitId = actuallyReturningHere.Unit.Id; m_database.Save(existingAloc); newReturn = m_amountProcessor.Subtract(newReturn, actuallyReturningHere); } if (newReturn.IsPositive) { throw new InvalidOperationException("Nebylo mozne vratit vsechno pozadovane mnozstvi"); } }