using AyCode.Core.Extensions; using AyCode.Core.Loggers; using AyCode.Services.Server.SignalRs; using FruitBank.Common.Dtos; using FruitBank.Common.Entities; using FruitBank.Common.Interfaces; using FruitBank.Common.Server.Services.SignalRs; using FruitBank.Common.Services; using Mango.Nop.Core.Extensions; using Mango.Nop.Core.Loggers; using Microsoft.CodeAnalysis.Operations; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Common; using Nop.Core.Domain.Orders; using Nop.Core.Events; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Services.Catalog; using Nop.Services.Events; namespace Nop.Plugin.Misc.FruitBankPlugin.Services; #nullable enable public class MeasurementService : MeasurementServiceBase, IMeasurementService { private readonly FruitBankDbContext _dbContext; private readonly IEventPublisher _eventPublisher; private readonly FruitBankAttributeService _fruitBankAttributeService; private readonly SignalRSendToClientService _signalRSendToClientService; private readonly CustomPriceCalculationService _customPriceCalculationService; public MeasurementService(FruitBankDbContext dbContext, SignalRSendToClientService signalRSendToClientService, FruitBankAttributeService fruitBankAttributeService, IPriceCalculationService customPriceCalculationService, IEventPublisher eventPublisher, IEnumerable logWriters) : base(new Logger(logWriters.ToArray())) { _dbContext = dbContext; _eventPublisher = eventPublisher; _fruitBankAttributeService = fruitBankAttributeService; _signalRSendToClientService = signalRSendToClientService; _customPriceCalculationService = (CustomPriceCalculationService)customPriceCalculationService; } public async Task DeleteOrderItemConstraintsAsync(int orderItemId) => await DeleteOrderItemConstraintsAsync(await _dbContext.OrderItems.GetByIdAsync(orderItemId)); public async Task DeleteOrderItemConstraintsAsync(OrderItem orderItem) { Logger.Info($"MeasurementService->DeleteOrderItemConstraintsAsync() invoked; orderItem.Id: {orderItem?.Id}"); if (orderItem == null) return; await _dbContext.DeleteOrderItemConstraintsSafeAsync(orderItem); await _signalRSendToClientService.SendOrderItemDeleted(orderItem); var order = await _dbContext.Orders.GetByIdAsync(orderItem.OrderId); await _customPriceCalculationService.CheckAndUpdateOrderTotalPrice(order); } public async Task OrderItemInsertedOrUpdatedPostProcess(OrderItem orderItem) { var orderItemDto = await _dbContext.OrderItemDtos.GetByIdAsync(orderItem.Id, true); await CheckAndUpdateOrderItemFinalPricesAsync(orderItem, orderItemDto); await _signalRSendToClientService.SendOrderItemChanged(orderItemDto); } public async Task CheckAndUpdateOrderItemFinalPricesAsync(OrderItem orderItem, OrderItemDto? orderItemDtoHelper = null) { if (await _customPriceCalculationService.CheckAndUpdateOrderItemFinalPricesAsync(orderItem, orderItemDtoHelper)) { var order = await _dbContext.Orders.GetByIdAsync(orderItem.OrderId); await _customPriceCalculationService.CheckAndUpdateOrderTotalPrice(order); } } public async Task OrderItemMeasuringReset(int orderItemId) => await OrderItemMeasuringReset(await _dbContext.OrderItems.GetByIdAsync(orderItemId)); public async Task OrderItemMeasuringReset(OrderItem orderItem) { var order = await _dbContext.Orders.GetByIdAsync(orderItem.OrderId); var orderItemPallets = await _dbContext.OrderItemPallets.GetAllByOrderItemId(orderItem.Id, false).ToListAsync(); var result = await _dbContext.TransactionSafeAsync(async _ => { await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(order.Id, nameof(IOrderDto.RevisorId), 0); foreach (var orderItemPallet in orderItemPallets) { orderItemPallet.RevisorId = 0; orderItemPallet.IsMeasured = false; await _dbContext.OrderItemPallets.UpdateAsync(orderItemPallet); } if (order.OrderStatus == OrderStatus.Complete) await _dbContext.SetOrderStatusToPendingAsync(order); return true; }); if (!result) return result; foreach (var orderItemPallet in orderItemPallets) await _signalRSendToClientService.SendOrderItemPalletChanged(orderItemPallet); await _signalRSendToClientService.SendOrderChanged(await _dbContext.OrderDtos.GetByIdAsync(orderItem.OrderId, true)); return result; } public async Task?> ProcessAndSaveFullShippingJson(string fullShippingJson, int customerId) { var partners = fullShippingJson.JsonTo>(); if (partners == null || partners.Count == 0) return partners; var a = partners.SelectMany(x => x.ShippingDocuments?.SelectMany(sd => sd.ShippingItems?.Where(si => si.ProductId.GetValueOrDefault(0) > 0).Select(si => si.ProductId!.Value) ?? []) ?? []).ToHashSet(); var productDtosById = await _dbContext.ProductDtos.GetAllByIds(a, false, false).ToDictionaryAsync(k => k.Id, v => v); var result = await _dbContext.TransactionSafeAsync(async _ => { foreach (var partner in partners) { //await _dbContext.Partners.InsertAsync(partner, false); if (partner.ShippingDocuments == null) continue; foreach (var shippingDocument in partner.ShippingDocuments) { //shippingDocument.PartnerId = 0; await _dbContext.ShippingDocuments.InsertAsync(shippingDocument, false); if (shippingDocument.ShippingItems == null) continue; foreach (var shippingItem in shippingDocument.ShippingItems) { shippingItem.NameOnDocument = shippingItem.Name; if (shippingItem.ProductId != null && productDtosById.TryGetValue(shippingItem.ProductId.Value, out var productDto)) { shippingItem.Name = productDto.Name; shippingItem.IsMeasurable = productDto.IsMeasurable; //TODO: Update Product Incoming attribute! - J. } else shippingItem.ProductId = null; shippingItem.ShippingDocumentId = shippingDocument.Id; await _dbContext.ShippingItems.InsertAsync(shippingItem, false); } } } return true; }); return result ? partners : null; } public async Task CalculateAndSetAverageWeight(ShippingItemPallet shippingItemPallet) { var productDto = await _dbContext.ProductDtos.GetByIdAsync(shippingItemPallet.ShippingItem.ProductId); if (productDto != null) { double averageWeight = 0; //TODO if ismeasurable if (shippingItemPallet.IsValidMeasuringValues(shippingItemPallet.ShippingItem.IsMeasurable)) { //this is only for the current pallet, the average weight should be calculated from all pallets of the product, //so we check if there are any pallets with valid measuring values and calculate the average weight from them, //otherwise we use the current pallet's weight as the average weight var shippingItemPallets = await _dbContext.ShippingItemPallets.GetAllByShippingItemIdAsync(shippingItemPallet.ShippingItemId, false).ToListAsync(); var allOtherPalletsInThisShippingItem = await shippingItemPallets.Where(sp => sp.Id != shippingItemPallet.Id).ToListAsync(); //we need an object to store the netweight and trayquantity for average calculation var validPalletsForAverageCalculation = new List<(double NetWeight, int TrayQuantity)>(); if(allOtherPalletsInThisShippingItem.Count != 0) { //get all pallets with valid measuring values for (int i = 0; i < allOtherPalletsInThisShippingItem.Count; i++) { if (allOtherPalletsInThisShippingItem[i].IsValidMeasuringValues(shippingItemPallet.ShippingItem.IsMeasurable)) { validPalletsForAverageCalculation.Add((allOtherPalletsInThisShippingItem[i].NetWeight, allOtherPalletsInThisShippingItem[i].TrayQuantity)); } } } //add current pallet to the valid pallets if it has valid measuring values validPalletsForAverageCalculation.Add(new(shippingItemPallet.NetWeight, shippingItemPallet.TrayQuantity)); //calculate the average weight from the valid pallets var totalNetWeight = validPalletsForAverageCalculation.Sum(x => x.NetWeight); var totalTrayQuantity = validPalletsForAverageCalculation.Sum(x => x.TrayQuantity); var TotalAverageWeight = totalTrayQuantity > 0 ? totalNetWeight / totalTrayQuantity : 0; averageWeight = TotalAverageWeight; } //TODO ELSE - we have to get the net weight from the document, not from measuring await SetProductAverageWeight(productDto, averageWeight); await _dbContext.ProductDtos.UpdateAsync(productDto); } } public async Task SetProductAverageWeight(ProductDto productDto, double averageWeight) { await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(productDto.Id, nameof(IProductDto.AverageWeight), averageWeight); } }