From 11b151a9ecade64f17cde24ec49a76f89940c07b Mon Sep 17 00:00:00 2001 From: Loretta Date: Fri, 24 Oct 2025 15:47:45 +0200 Subject: [PATCH] Price calculation improvements, fixes, etc... --- .../Controllers/CustomOrderController.cs | 73 +++++----- .../Areas/Admin/Views/Order/List.cshtml | 9 ++ .../OrderAttributesViewComponent.cs | 32 +++-- .../Domains/DataLayer/FruitBankDbContext.cs | 3 + .../Domains/DataLayer/OrderDtoDbTable.cs | 2 +- .../EventConsumers/FruitBankEventConsumer.cs | 106 +++++++------- .../Services/CustomPriceCalculationService.cs | 131 ++++++++++++++---- 7 files changed, 232 insertions(+), 124 deletions(-) diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs index 3ec06ad..9522741 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs @@ -160,7 +160,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers return RedirectToAction("List", "Order"); // store attributes in GenericAttribute table - await _genericAttributeService.SaveAttributeAsync(order, nameof(IMeasurable.IsMeasurable), model.IsMeasurable, _storeContext.GetCurrentStore().Id); + //await _genericAttributeService.SaveAttributeAsync(order, nameof(IMeasurable.IsMeasurable), model.IsMeasurable, _storeContext.GetCurrentStore().Id); await _genericAttributeService.SaveAttributeAsync(order, nameof(IOrderDto.DateOfReceipt), model.DateOfReceipt, _storeContext.GetCurrentStore().Id); _notificationService.SuccessNotification("Custom attributes saved successfully."); @@ -226,11 +226,13 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers //var productDtosById = await _dbContext.ProductDtos.GetAllByIds(orderProducts.Select(op => op.Id)).ToDictionaryAsync(p => p.Id, prodDto => prodDto); var store = _storeContext.GetCurrentStore(); + var productDtosByOrderItemId = (await _dbContext.ProductDtos.GetByIdsAsync(orderProducts.Select(x => x.Id).ToArray())).ToDictionary(k => k.Id, v => v); + var transactionSuccess = await _dbContext.TransactionSafeAsync(async _ => { await _orderService.InsertOrderAsync(order); - order.CustomOrderNumber = order.Id.ToString(); - await _orderService.UpdateOrderAsync(order); + + order.OrderTotal = 0; foreach (var item in orderProducts) { @@ -239,48 +241,47 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers if (product == null || product.StockQuantity - item.Quantity < 0) { var errorText = $"product == null || product.StockQuantity - item.Quantity < 0; productId: {product?.Id}; product?.StockQuantity - item.Quantity: {product?.StockQuantity - item.Quantity}"; - + _logger.Error($"{errorText}"); throw new Exception($"{errorText}"); } - //var (_, finalUnitPrice, _, _) = await _priceCalculationService.GetFinalPriceAsync(product, customer, store, item.Price, 0, true, item.Quantity, null, null); + var productDto = productDtosByOrderItemId[item.Id]; + var isMeasurable = productDto.IsMeasurable; - //var (presetPriceInclTax, _) = await _taxService.GetProductPriceAsync(product, finalPrice, true, customer); - //var (presetPriceExclTax, _) = await _taxService.GetProductPriceAsync(product, finalPrice, false, customer); - //model.UnitPriceExclTax = presetPriceExclTax; - //model.UnitPriceInclTax = presetPriceInclTax; - //model.Quantity = item.Quantity; - //model.SubTotalExclTax = presetPriceExclTax; - //model.SubTotalInclTax = presetPriceInclTax; - - //if (productDtosById.TryGetValue(item.Id, out var productDto)) + var orderItem = new OrderItem { - var orderItem = new OrderItem - { - OrderId = order.Id, - ProductId = item.Id, - Quantity = item.Quantity, - UnitPriceInclTax = item.Price, - UnitPriceExclTax = item.Price, - PriceInclTax = item.Price * item.Quantity, - PriceExclTax = item.Price * item.Quantity, - OriginalProductCost = product.ProductCost, - AttributeDescription = string.Empty, - AttributesXml = string.Empty, - DiscountAmountInclTax = 0, - DiscountAmountExclTax = 0 - }; - - await _orderService.InsertOrderItemAsync(orderItem); + OrderId = order.Id, + ProductId = item.Id, + Quantity = item.Quantity, + UnitPriceInclTax = item.Price, + UnitPriceExclTax = item.Price, + PriceInclTax = isMeasurable ? 0 : item.Price * item.Quantity, + PriceExclTax = isMeasurable ? 0 : item.Price * item.Quantity, + OriginalProductCost = product.ProductCost, + AttributeDescription = string.Empty, + AttributesXml = string.Empty, + DiscountAmountInclTax = 0, + DiscountAmountExclTax = 0 + }; - //await _productService.AddStockQuantityHistoryEntryAsync(product, -orderItem.Quantity, product.StockQuantity, 1); - await _productService.AdjustInventoryAsync(product, -orderItem.Quantity, orderItem.AttributesXml, ""); - //await _productService.BookReservedInventoryAsync(product, 1, item.Quantity, ""); - } - //else _logger.Error($"(productDtosById.TryGetValue(item.Id, out var product) == false); {item}"); + order.OrderTotal += orderItem.PriceInclTax; + await _orderService.InsertOrderItemAsync(orderItem); + + //await _productService.AddStockQuantityHistoryEntryAsync(product, -orderItem.Quantity, product.StockQuantity, 1); + await _productService.AdjustInventoryAsync(product, -orderItem.Quantity, orderItem.AttributesXml, ""); + //await _productService.BookReservedInventoryAsync(product, 1, item.Quantity, ""); } + order.CustomOrderNumber = order.Id.ToString(); + + order.OrderSubtotalInclTax = order.OrderTotal; + order.OrderSubtotalExclTax = order.OrderTotal; + order.OrderSubTotalDiscountInclTax = order.OrderTotal; + order.OrderSubTotalDiscountExclTax = order.OrderTotal; + + await _orderService.UpdateOrderAsync(order); + return true; }); diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml index 66882a0..e7baae9 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml @@ -345,6 +345,14 @@ Width = "80" } }; + gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.IsMeasurable)) + { + Title = T("Admin.Orders.Fields.IsMeasurable").Text, + Width = "100", + Render = new RenderCustom("renderColumnIsMeasurable"), + ClassName = NopColumnClassDefaults.CenterAll + }); + gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.IsMeasured)) { Title = T("Admin.Orders.Fields.IsMeasured").Text, @@ -352,6 +360,7 @@ Render = new RenderCustom("renderColumnIsMeasurable"), ClassName = NopColumnClassDefaults.CenterAll }); + gridModel.ColumnCollection.Add(new ColumnProperty(nameof(IOrderDto.DateOfReceipt)) { Title = T("Admin.Orders.Fields.PickupDate").Text, diff --git a/Nop.Plugin.Misc.AIPlugin/Components/OrderAttributesViewComponent.cs b/Nop.Plugin.Misc.AIPlugin/Components/OrderAttributesViewComponent.cs index 18733c2..7133647 100644 --- a/Nop.Plugin.Misc.AIPlugin/Components/OrderAttributesViewComponent.cs +++ b/Nop.Plugin.Misc.AIPlugin/Components/OrderAttributesViewComponent.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc; using Nop.Core; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Orders; +using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Services; using Nop.Services.Common; @@ -20,12 +21,15 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Components private readonly FruitBankAttributeService _fruitBankAttributeService; private readonly IWorkContext _workContext; private readonly IStoreContext _storeContext; + private FruitBankDbContext _dbContext; - public OrderAttributesViewComponent(FruitBankAttributeService fruitBankAttributeService, IWorkContext workContext, IStoreContext storeContext) + public OrderAttributesViewComponent(FruitBankDbContext dbContext, FruitBankAttributeService fruitBankAttributeService, IWorkContext workContext, IStoreContext storeContext) { _workContext = workContext; _storeContext = storeContext; _fruitBankAttributeService = fruitBankAttributeService; + + _dbContext= dbContext; } public async Task InvokeAsync(string widgetZone, object additionalData) @@ -36,19 +40,23 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Components if (model.OrderId > 0) { - var orderPickupAttributeValue = await _fruitBankAttributeService.GetGenericAttributeValueAsync(model.OrderId, nameof(IOrderDto.DateOfReceipt)); - var orderMeasurableAttributeValue = await _fruitBankAttributeService.GetGenericAttributeValueAsync(model.OrderId, nameof(IMeasurable.IsMeasurable)); + var orderDto = await _dbContext.OrderDtos.GetByIdAsync(model.OrderId, true); - model.IsMeasurable = orderMeasurableAttributeValue; - if(orderPickupAttributeValue.HasValue && orderPickupAttributeValue.Value != DateTime.MinValue) - { - model.DateOfReceipt = orderPickupAttributeValue; - } - else - { - model.DateOfReceipt = null; - } + model.IsMeasurable = orderDto.IsMeasurable; + model.DateOfReceipt = orderDto.DateOfReceipt; + + //var orderPickupAttributeValue = await _fruitBankAttributeService.GetGenericAttributeValueAsync(model.OrderId, nameof(IOrderDto.DateOfReceipt)); + + //if (orderPickupAttributeValue.HasValue && orderPickupAttributeValue.Value != DateTime.MinValue) + //{ + // model.DateOfReceipt = orderPickupAttributeValue; + //} + //else + //{ + // model.DateOfReceipt = null; + //} } + return View("~/Plugins/Misc.FruitBankPlugin/Views/OrderAttributes.cshtml", model); } } diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs index d9b953b..36eae62 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs @@ -433,6 +433,9 @@ public class FruitBankDbContext : MgDbContextBase, if (!orderItemDto.IsMeasurable) continue; + var finalPriceInclTax = decimal.Round(orderItemDto.UnitPriceInclTax * orderItemDto.Quantity, 0); + var finalPriceExclTax = decimal.Round(orderItemDto.UnitPriceExclTax * orderItemDto.Quantity, 0); + var gaNetWeight = CommonHelper.To(orderItemDto.GenericAttributes.FirstOrDefault(x => x.Key == nameof(IMeasuringNetWeight.NetWeight))?.Value ?? "0"); await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/OrderDtoDbTable.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/OrderDtoDbTable.cs index 5257fac..836ce0b 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/OrderDtoDbTable.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/OrderDtoDbTable.cs @@ -34,7 +34,7 @@ public class OrderDtoDbTable : MgDtoDbTableBase => GetAll(loadRelations).Where(o => o.OrderStatusId == (int)orderStatus); public IQueryable GetAllForMeasuring(bool loadRelations = true) - => GetAllByOrderStatus(OrderStatus.Pending, loadRelations).Where(o => o.DateOfReceipt != null); + => GetAllByOrderStatus(OrderStatus.Pending, loadRelations).Where(o => o.GenericAttributes.Any(ga => ga.Key == nameof(OrderDto.DateOfReceipt))); public IQueryable GetAllByIds(IEnumerable orderIds, bool loadRelations = true) => GetAll(loadRelations).Where(o => orderIds.Contains(o.Id)); } diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs b/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs index 4454e21..08d5415 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs @@ -11,18 +11,19 @@ using Nop.Plugin.Misc.FruitBankPlugin.Services; using Nop.Services.Events; using Mango.Nop.Core.Extensions; using Nop.Core.Domain.Orders; +using Nop.Services.Catalog; namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.EventConsumers; -public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBankDbContext ctx, FruitBankAttributeService fruitBankAttributeService, IEnumerable logWriters) : - MgEventConsumerBase(ctx, httpContextAcc, logWriters), +public class FruitBankEventConsumer : + MgEventConsumerBase, IConsumer>, - IConsumer>, - IConsumer>, + IConsumer>, + IConsumer>, IConsumer>, - IConsumer>, - IConsumer>, - IConsumer>, + IConsumer>, + IConsumer>, + IConsumer>, IConsumer>, IConsumer>, IConsumer>, @@ -30,6 +31,20 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa IConsumer>, IConsumer> { + private CustomPriceCalculationService _customPriceCalculationService; + + private readonly FruitBankDbContext _ctx; + private readonly FruitBankAttributeService _fruitBankAttributeService; + + public FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, IPriceCalculationService customPriceCalculationService, FruitBankDbContext ctx, FruitBankAttributeService fruitBankAttributeService, IEnumerable logWriters) : base(ctx, httpContextAcc, logWriters) + { + _ctx = ctx; + + _fruitBankAttributeService = fruitBankAttributeService; + _customPriceCalculationService = customPriceCalculationService as CustomPriceCalculationService; + } + + public override async Task HandleEventAsync(EntityUpdatedEvent eventMessage) { var product = await CheckAndUpdateProductManageInventoryMethodToManageStock(eventMessage.Entity); @@ -40,14 +55,14 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa if (saveProductCustomAttributesResult is { IsMeasurableChanged: true, IsMeasurable: not null }) { - var shippingItems = await ctx.ShippingItems.Table + var shippingItems = await _ctx.ShippingItems.Table .Where(si => si.ProductId == product.Id && !si.IsMeasured && si.IsMeasurable != saveProductCustomAttributesResult.IsMeasurable.Value) .ToListAsync(); if (shippingItems.Count > 0) { foreach (var shippingItem in shippingItems) shippingItem.IsMeasurable = saveProductCustomAttributesResult.IsMeasurable.Value; - await ctx.ShippingItems.UpdateAsync(shippingItems, false); + await _ctx.ShippingItems.UpdateAsync(shippingItems, false); } } @@ -68,7 +83,7 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa /// /// IsMeasureable /// - + private async Task<(bool IsMeasurableChanged, bool? IsMeasurable)> SaveProductCustomAttributesAsync(Product product) { if (product == null) return (false, null); @@ -85,7 +100,7 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa try { - var productDto = product.Id > 0 ? await ctx.ProductDtos.GetByIdAsync(product.Id, false) : null; + var productDto = product.Id > 0 ? await _ctx.ProductDtos.GetByIdAsync(product.Id, false) : null; //IsMeasurable isMeasurable = form[nameof(IMeasurable.IsMeasurable)].ToString().Contains("true"); @@ -93,7 +108,7 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa if (productDtoIsMeasurable == null || productDtoIsMeasurable.Value != isMeasurable.Value) { - await fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(product.Id, nameof(IMeasurable.IsMeasurable), isMeasurable.Value); + await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(product.Id, nameof(IMeasurable.IsMeasurable), isMeasurable.Value); isMeasurableChanged = true; } @@ -102,21 +117,21 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa var productDtoNetWeight = productDto?.GenericAttributes.GetValueOrNull(nameof(IMeasuringNetWeight.NetWeight)); if (productDtoNetWeight == null || double.Round(productDtoNetWeight.Value, 1) != netWeight) - await fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(product.Id, nameof(IMeasuringNetWeight.NetWeight), netWeight); + await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(product.Id, nameof(IMeasuringNetWeight.NetWeight), netWeight); //Tára var tare = double.Round(CommonHelper.To(form[nameof(ITare.Tare)].ToString()), 1); if (tare < 0) throw new Exception($"FruitBankEventConsumer->SaveProductCustomAttributesAsync(); (tare < 0); productId: {product.Id}; tare: {tare}"); if (productDto == null || productDto.Tare != tare) - await fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(product.Id, nameof(ITare.Tare), tare); + await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(product.Id, nameof(ITare.Tare), tare); //IncomingQuantity var incomingQuantity = CommonHelper.To(form[nameof(IIncomingQuantity.IncomingQuantity)].ToString()); if (incomingQuantity < 0) throw new Exception($"FruitBankEventConsumer->SaveProductCustomAttributesAsync(); (incomingQuantity < 0); productId: {product.Id}; incomingQuantity: {incomingQuantity}"); if (productDto == null || productDto.IncomingQuantity != incomingQuantity) - await fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(product.Id, nameof(IIncomingQuantity.IncomingQuantity), incomingQuantity); + await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(product.Id, nameof(IIncomingQuantity.IncomingQuantity), incomingQuantity); } catch (Exception ex) { @@ -151,8 +166,8 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa private async Task UpdateShippingItemMeasuringValuesAsync(ShippingItemPallet shippingItemPallet) { - var shippingItem = await ctx.ShippingItems.GetByIdAsync(shippingItemPallet.ShippingItemId, false); - await ctx.UpdateShippingItemAsync(shippingItem); + var shippingItem = await _ctx.ShippingItems.GetByIdAsync(shippingItemPallet.ShippingItemId, false); + await _ctx.UpdateShippingItemAsync(shippingItem); } public async Task HandleEventAsync(EntityInsertedEvent eventMessage) @@ -183,13 +198,13 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa private async Task UpdateShippingDocumentIsAllMeasuredAsync(ShippingItem shippingItem) { //TODO: where: && IsMeasurable!!!! - J. - var isAllShippingItemMeasured = ctx.ShippingItems.GetAll(false).Where(si => si.ShippingDocumentId == shippingItem.ShippingDocumentId).All(si => si.IsMeasured); - var shippingDocument = await ctx.ShippingDocuments.GetByIdAsync(shippingItem.ShippingDocumentId); + var isAllShippingItemMeasured = _ctx.ShippingItems.GetAll(false).Where(si => si.ShippingDocumentId == shippingItem.ShippingDocumentId).All(si => si.IsMeasured); + var shippingDocument = await _ctx.ShippingDocuments.GetByIdAsync(shippingItem.ShippingDocumentId); if (shippingDocument != null && shippingDocument.IsAllMeasured != isAllShippingItemMeasured) { shippingDocument.IsAllMeasured = isAllShippingItemMeasured; - await ctx.ShippingDocuments.UpdateAsync(shippingDocument); + await _ctx.ShippingDocuments.UpdateAsync(shippingDocument); } } @@ -209,13 +224,13 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa private async Task UpdateShippingIsAllMeasuredAsync(ShippingDocument shippingDocument) { - var isAllShippingDocumentMeasured = ctx.ShippingDocuments.GetAll(false).Where(si => si.ShippingId == shippingDocument.ShippingId).All(si => si.IsAllMeasured); - var shipping = await ctx.Shippings.GetByIdAsync(shippingDocument.ShippingId); + var isAllShippingDocumentMeasured = _ctx.ShippingDocuments.GetAll(false).Where(si => si.ShippingId == shippingDocument.ShippingId).All(si => si.IsAllMeasured); + var shipping = await _ctx.Shippings.GetByIdAsync(shippingDocument.ShippingId); if (shipping != null && shipping.IsAllMeasured != isAllShippingDocumentMeasured) { shipping.IsAllMeasured = isAllShippingDocumentMeasured; - await ctx.Shippings.UpdateAsync(shipping); + await _ctx.Shippings.UpdateAsync(shipping); } } @@ -227,46 +242,43 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa { Logger.Info($"HandleEventAsync->EntityDeletedEvent; id: {eventMessage.Entity.Id}"); - await ctx.ShippingDocuments.DeleteAsync(sd => sd.ShippingId == eventMessage.Entity.Id, true); + await _ctx.ShippingDocuments.DeleteAsync(sd => sd.ShippingId == eventMessage.Entity.Id, true); } public async Task HandleEventAsync(EntityDeletedEvent eventMessage) { Logger.Info($"HandleEventAsync->EntityDeletedEvent; id: {eventMessage.Entity.Id}"); - await ctx.ShippingItems.DeleteAsync(si => si.ShippingDocumentId == eventMessage.Entity.Id, true); + await _ctx.ShippingItems.DeleteAsync(si => si.ShippingDocumentId == eventMessage.Entity.Id, true); } public async Task HandleEventAsync(EntityDeletedEvent eventMessage) { Logger.Info($"HandleEventAsync->EntityDeletedEvent; id: {eventMessage.Entity.Id}"); - await ctx.ShippingItemPallets.DeleteAsync(sp => sp.ShippingItemId == eventMessage.Entity.Id, false); + await _ctx.ShippingItemPallets.DeleteAsync(sp => sp.ShippingItemId == eventMessage.Entity.Id, false); } - public async Task HandleEventAsync(EntityUpdatedEvent eventMessage) => await CheckAndUpdateOrderItemFinalPrices(eventMessage.Entity); - - public async Task HandleEventAsync(EntityInsertedEvent eventMessage) => await CheckAndUpdateOrderItemFinalPrices(eventMessage.Entity); - - private async Task CheckAndUpdateOrderItemFinalPrices(OrderItem orderItem) + public async Task HandleEventAsync(EntityUpdatedEvent eventMessage) { - Logger.Info($"HandleEventAsync->CheckAndUpdateOrderItemFinalPrices; orderItem.Id: {orderItem.Id}"); - - var finalPriceInclTax = decimal.Round(orderItem.UnitPriceInclTax * orderItem.Quantity, 0); - var finalPriceExclTax = decimal.Round(orderItem.UnitPriceExclTax * orderItem.Quantity, 0); - - if (orderItem.PriceInclTax != finalPriceInclTax || orderItem.PriceExclTax != finalPriceExclTax) + //await _customPriceCalculationService.CheckAndUpdateOrderItemFinalPricesAsync(eventMessage.Entity); + if (await _customPriceCalculationService.CheckAndUpdateOrderItemFinalPricesAsync(eventMessage.Entity)) { - Logger.Error($"HandleEventAsync->CheckAndUpdateOrderItemFinalPrices; " + - $"orderItem.PriceInclTax({orderItem.PriceInclTax}) != finalPriceInclTax({finalPriceInclTax}) || " + - $"orderItem.PriceExclTax({orderItem.PriceExclTax}) != finalPriceExclTax({finalPriceExclTax})"); - - orderItem.PriceInclTax = finalPriceInclTax; - orderItem.PriceExclTax = finalPriceExclTax; - - await ctx.OrderItems.UpdateAsync(orderItem, false); + var order = await _ctx.Orders.GetByIdAsync(eventMessage.Entity.OrderId); + await _customPriceCalculationService.CheckAndUpdateOrderTotalPrice(order); } } - #endregion Delete -} \ No newline at end of file + + public async Task HandleEventAsync(EntityInsertedEvent eventMessage) + { + //await _customPriceCalculationService.CheckAndUpdateOrderItemFinalPricesAsync(eventMessage.Entity); + if (await _customPriceCalculationService.CheckAndUpdateOrderItemFinalPricesAsync(eventMessage.Entity)) + { + var order = await _ctx.Orders.GetByIdAsync(eventMessage.Entity.OrderId); + await _customPriceCalculationService.CheckAndUpdateOrderTotalPrice(order); + } + } +} + +#endregion Delete diff --git a/Nop.Plugin.Misc.AIPlugin/Services/CustomPriceCalculationService.cs b/Nop.Plugin.Misc.AIPlugin/Services/CustomPriceCalculationService.cs index a6487d5..741482c 100644 --- a/Nop.Plugin.Misc.AIPlugin/Services/CustomPriceCalculationService.cs +++ b/Nop.Plugin.Misc.AIPlugin/Services/CustomPriceCalculationService.cs @@ -1,29 +1,35 @@ -using Nop.Core.Caching; +using AyCode.Core.Loggers; +using AyCode.Entities; +using FruitBank.Common.Dtos; +using Mango.Nop.Core.Loggers; +using Nop.Core; +using Nop.Core.Caching; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Customers; -using Nop.Core; +using Nop.Core.Domain.Directory; +using Nop.Core.Domain.Discounts; +using Nop.Core.Domain.Orders; +using Nop.Core.Domain.Stores; using Nop.Data; +using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Services.Catalog; using Nop.Services.Customers; +using Nop.Services.Directory; using Nop.Services.Discounts; using Nop.Services.Localization; -using Nop.Services.Logging; -using Nop.Core.Domain.Directory; -using Nop.Services.Directory; -using Nop.Core.Domain.Discounts; -using Nop.Core.Domain.Stores; namespace Nop.Plugin.Misc.FruitBankPlugin.Services; public class CustomPriceCalculationService : PriceCalculationService { - private readonly IRepository _productRepository; + private FruitBankDbContext _dbContext; private readonly IProductAttributeService _productAttributeService; private readonly ISpecificationAttributeService _specificationAttributeService; private readonly ILocalizationService _localizationService; + private ILogger _logger; public CustomPriceCalculationService( - IRepository productRepository, + FruitBankDbContext dbContext, // inject all base deps CatalogSettings catalogSettings, CurrencySettings currencySettings, @@ -40,41 +46,110 @@ public class CustomPriceCalculationService : PriceCalculationService ILocalizationService localizationService, IStaticCacheManager cacheManager, IWorkContext workContext, - ILogger logger) + IEnumerable logWriters) : base(catalogSettings, currencySettings, categoryService, currencyService, customerService, discountService, manufacturerService, productAttributeParser, productService, cacheManager) { - _productRepository = productRepository; + _logger = new Logger(logWriters.ToArray()); + + _dbContext = dbContext; // assign all base deps to local private vars if needed _productAttributeService = productAttributeService; _specificationAttributeService = specificationAttributeService; _localizationService = localizationService; } + public decimal CalculateOrderItemFinalPrice(OrderItemDto orderItemDto, decimal unitPrice, int quantity) + => decimal.Round(unitPrice * (orderItemDto.IsMeasurable ? (decimal)orderItemDto.NetWeight : quantity), 0); - public override async Task<(decimal priceWithoutDiscounts, decimal finalPrice, decimal appliedDiscountAmount, List appliedDiscounts)> GetFinalPriceAsync( - Product product, Customer customer, Store store, decimal? overriddenProductPrice, decimal additionalCharge = 0, bool includeDiscounts = true, - int quantity = 1, DateTime? rentalStartDate = null, DateTime? rentalEndDate = null) + public async Task CheckAndUpdateOrderItemFinalPricesAsync(OrderItem orderItem) { + var orderItemDto = await _dbContext.OrderItemDtos.GetByIdAsync(orderItem.Id, true); + return await CheckAndUpdateOrderItemFinalPricesAsync(orderItem, orderItemDto); + } + public async Task CheckAndUpdateOrderItemFinalPricesAsync(OrderItemDto orderItemDto) + { + var orderItem = await _dbContext.OrderItems.GetByIdAsync(orderItemDto.Id); + return await CheckAndUpdateOrderItemFinalPricesAsync(orderItem, orderItemDto); + } + + public async Task CheckAndUpdateOrderItemFinalPricesAsync(OrderItem orderItem, OrderItemDto orderItemDto) + { + _logger.Info($"CustomPriceCalculationService->CheckAndUpdateOrderItemFinalPricesAsync; orderItem.Id: {orderItem.Id}"); - var productAttributeMappings = await _specificationAttributeService.GetProductSpecificationAttributesAsync(product.Id); - //Product Attributes - foreach (var pam in productAttributeMappings) + var finalPriceInclTax = CalculateOrderItemFinalPrice(orderItemDto, orderItem.UnitPriceInclTax, orderItem.Quantity); + var finalPriceExclTax = CalculateOrderItemFinalPrice(orderItemDto, orderItem.UnitPriceExclTax, orderItem.Quantity); + + if (orderItem.PriceInclTax == finalPriceInclTax && orderItem.PriceExclTax == finalPriceExclTax) return false; + + _logger.Error($"CustomPriceCalculationService->CheckAndUpdateOrderItemFinalPricesAsync; " + + $"orderItem.PriceInclTax({orderItem.PriceInclTax}) != finalPriceInclTax({finalPriceInclTax}) || " + + $"orderItem.PriceExclTax({orderItem.PriceExclTax}) != finalPriceExclTax({finalPriceExclTax})"); + + orderItem.PriceInclTax = finalPriceInclTax; + orderItem.PriceExclTax = finalPriceExclTax; + + await _dbContext.OrderItems.UpdateAsync(orderItem, false); + return true; + } + + public async Task CheckAndUpdateOrderTotalPrice(Order order) + { + var prevOrderTotal = order.OrderTotal; + var orderItemDtos = await _dbContext.OrderItemDtos.GetAllByOrderId(order.Id).ToListAsync(); + + order.OrderTotal = 0; + + foreach (var itemDto in orderItemDtos) { - //get option - var attributeOtion = await _specificationAttributeService.GetSpecificationAttributeOptionByIdAsync(pam.SpecificationAttributeOptionId); - - //get attribute - var attribute = await _specificationAttributeService.GetSpecificationAttributeByIdAsync(attributeOtion.SpecificationAttributeId); - - // you can check for specific attribute by its name or id - if (attribute.Name == "Needs to be measured for price") - { - return (0m, 0m, 0m, []); - } + await CheckAndUpdateOrderItemFinalPricesAsync(itemDto); + order.OrderTotal += itemDto.PriceInclTax; } + if (order.OrderTotal == prevOrderTotal) return; + + _logger.Error($"HandleEventAsync->CheckAndUpdateOrderItemFinalPrices; order.OrderTotal({order.OrderTotal}) == prevOrderTotal({prevOrderTotal})"); + + order.OrderSubtotalInclTax = order.OrderTotal; + order.OrderSubtotalExclTax = order.OrderTotal; + order.OrderSubTotalDiscountInclTax = order.OrderTotal; + order.OrderSubTotalDiscountExclTax = order.OrderTotal; + + await _dbContext.Orders.UpdateAsync(order); + } + + //public override async Task<(decimal priceWithoutDiscounts, decimal finalPrice, decimal appliedDiscountAmount, List appliedDiscounts)> GetFinalPriceAsync( + // Product product, Customer customer, Store store, decimal? overriddenProductPrice, decimal additionalCharge = 0, bool includeDiscounts = true, + // int quantity = 1, DateTime? rentalStartDate = null, DateTime? rentalEndDate = null) + public override async Task<(decimal priceWithoutDiscounts, decimal finalPrice, decimal appliedDiscountAmount, List appliedDiscounts)> GetFinalPriceAsync + (Product product, Customer customer, Store store, decimal? overriddenProductPrice, decimal additionalCharge, bool includeDiscounts, int quantity, + DateTime? rentalStartDate, DateTime? rentalEndDate) + { + var productDto = await _dbContext.ProductDtos.GetByIdAsync(product.Id, true); + + if (productDto.IsMeasurable) + { + return (0, 0, 0m, []); + //return (overriddenProductPrice.GetValueOrDefault(0), overriddenProductPrice.GetValueOrDefault(0), 0m, []); + } + //var productAttributeMappings = await _specificationAttributeService.GetProductSpecificationAttributesAsync(product.Id); + ////Product Attributes + //foreach (var pam in productAttributeMappings) + //{ + // //get option + // var attributeOtion = await _specificationAttributeService.GetSpecificationAttributeOptionByIdAsync(pam.SpecificationAttributeOptionId); + + // //get attribute + // var attribute = await _specificationAttributeService.GetSpecificationAttributeByIdAsync(attributeOtion.SpecificationAttributeId); + + // // you can check for specific attribute by its name or id + // if (attribute.Name == "Needs to be measured for price") + // { + // return (0m, 0m, 0m, []); + // } + //} + return await base.GetFinalPriceAsync(product, customer, store, overriddenProductPrice, additionalCharge, includeDiscounts, quantity, rentalStartDate, rentalEndDate); } }