From 6e43a1d18f55f86c35da8b71997b77c0b49066cf Mon Sep 17 00:00:00 2001 From: Loretta Date: Mon, 29 Sep 2025 13:33:40 +0200 Subject: [PATCH 1/2] improvements, fixes, etc... --- .../Controllers/FruitBankDataController.cs | 11 +- .../Domains/DataLayer/FruitBankDbContext.cs | 107 ++++++++++------ .../Domains/DataLayer/ShippingDbTable.cs | 9 +- .../Domains/DataLayer/ShippingItemDbTable.cs | 1 - .../EventConsumers/FruitBankEventConsumer.cs | 12 +- Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs | 45 ++++--- .../Infrastructure/PluginNopStartup.cs | 5 +- .../Nop.Plugin.Misc.FruitBankPlugin.csproj | 3 + .../Services/FruitBankAttributeService.cs | 117 ++++++++++++++++++ .../Services/ILockService.cs | 5 + .../Services/LockService.cs | 14 +++ 11 files changed, 263 insertions(+), 66 deletions(-) create mode 100644 Nop.Plugin.Misc.AIPlugin/Services/FruitBankAttributeService.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Services/ILockService.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Services/LockService.cs diff --git a/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs b/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs index bfb74fb..4a9ab00 100644 --- a/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs @@ -190,11 +190,18 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers { _logger.Detail($"GetMeasuringUsers invoked"); - var customers = await ctx.GetCustormersBySystemRoleName(FruitBankConst.MeasuringRoleSystemName).Select(c => new CustomerDto(c)).ToListAsync(); - + var customers = await ctx.GetCustomersBySystemRoleName(FruitBankConst.MeasuringRoleSystemName).Select(c => new CustomerDto(c)).ToListAsync(); return customers; //.ToModelDto(); } + [SignalR(SignalRTags.GetCustomerRolesByCustomerId)] + public async Task> GetCustomerRolesByCustomerId(int customerId) + { + _logger.Detail($"GetCustomerRolesByCustomerId invoked; customerId: {customerId}"); + + return await ctx.GetCustomerRolesByCustomerId(customerId).ToListAsync(); + } + [SignalR(SignalRTags.GetProductDtos)] public async Task> GetProductDtos() { diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs index 0c4241c..07425e2 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs @@ -1,24 +1,32 @@ -using AyCode.Core.Loggers; +#nullable enable +using AyCode.Core.Loggers; +using AyCode.Utils.Extensions; using FruitBank.Common.Entities; using FruitBank.Common.Interfaces; using FruitBank.Common.Models; +using FruitBank.Common.Server; +using LinqToDB; +using LinqToDB.Common; using Mango.Nop.Core.Loggers; using Mango.Nop.Core.Repositories; +using Nop.Core; using Nop.Core.Caching; +using Nop.Core.ComponentModel; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Customers; using Nop.Data; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces; +using Nop.Plugin.Misc.FruitBankPlugin.Services; using Nop.Services.Catalog; -using NUglify.Helpers; +using Nop.Services.Common; using System.Transactions; -using LinqToDB; -using Nop.Core.ComponentModel; namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet, IShippingDbSet, IShippingItemDbSet, IShippingDocumentDbSet { + private readonly FruitBankAttributeService _fruitBankAttributeService; + private readonly IStoreContext _storeContext; private readonly IProductService _productService; private readonly IStaticCacheManager _staticCacheManager; @@ -33,16 +41,19 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet public IRepository CustomerRoleMappings { get; set; } - public FruitBankDbContext(INopDataProvider dataProvider, PartnerDbTable partnerDbTable, ShippingDbTable shippingDbTable, ShippingItemDbTable shippingItemDbTable, + public FruitBankDbContext(INopDataProvider dataProvider, ILockService lockService, FruitBankAttributeService fruitBankAttributeService, IStoreContext storeContext, + PartnerDbTable partnerDbTable, ShippingDbTable shippingDbTable, ShippingItemDbTable shippingItemDbTable, ShippingDocumentDbTable shippingDocumentDbTable, IProductService productService, IStaticCacheManager staticCacheManager, IRepository productRepository, IRepository customerRepository, IRepository customerCustomerRoleMappingRepository, IRepository customerRoleRepository, - IEnumerable logWriters) : base(dataProvider, logWriters) + IEnumerable logWriters) : base(dataProvider, lockService, logWriters) { + _storeContext = storeContext; _productService = productService; _staticCacheManager = staticCacheManager; + _fruitBankAttributeService = fruitBankAttributeService; Partners = partnerDbTable; Shippings = shippingDbTable; @@ -66,7 +77,7 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet return query; } - public IQueryable GetCustormersBySystemRoleName(string systemRoleName) + public IQueryable GetCustomersBySystemRoleName(string systemRoleName) { if (systemRoleName.IsNullOrWhiteSpace()) throw new ArgumentException("systemRoleName.IsNullOrWhiteSpace()", nameof(systemRoleName)); @@ -80,32 +91,38 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet return query.Distinct().OrderBy(o => o.Username); } + public IQueryable GetCustomerRolesByCustomerId(int customerId) + { + var query = + from cr in CustomerRoles.Table + join crm in CustomerRoleMappings.Table on cr.Id equals crm.CustomerRoleId + where crm.CustomerId == customerId + select cr; + + return query.Distinct(); + } + public IQueryable GetAllProducts() => Products.Table.Where(p => !p.Deleted).OrderBy(o => o.Name); public Task UpdateMeasuredShippingItemAsync(ShippingItem shippingItem) { - if (!shippingItem.IsMeasurable || shippingItem.IsValidMeasuringValues()) return UpdateShippingItemAsync(shippingItem); + if (shippingItem.IsValidMeasuringValues()) return UpdateShippingItemAsync(shippingItem); Logger.Error("shippingItem.IsMeasurable && !shippingItem.IsValidMeasuringValues()"); return Task.FromResult(false); } - private static readonly ReaderWriterLockSlim _locker = new(LockRecursionPolicy.NoRecursion); - - public async Task UpdateShippingItemAsync(ShippingItem shippingItem) + public Task UpdateShippingItemAsync(ShippingItem shippingItem) { if (shippingItem == null) { Logger.Error("shippingItem == null"); - return await Task.FromResult(false); + return Task.FromResult(false); } - //using (new ReaderWriteLockDisposable(_locker)) - //_locker.EnterWriteLock(); - //try - //{ - using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + return TransactionSafeAsync(async tr => + { try { //Mi van ha nem jött meg a termék? Nem fogják tudni menteni... - J. @@ -119,8 +136,7 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet var dbShippingItem = await ShippingItems.GetByIdAsync(shippingItem.Id); if (dbShippingItem == null) { - Logger.Error("dbShippingItem == null"); - return await Task.FromResult(false); + throw new Exception("dbShippingItem == null"); } Product product = null; @@ -130,8 +146,12 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet if (product == null) { - Logger.Error($"shippingItem.ProductId > 0 && product == null; shippingItem.ProductId: {shippingItem.ProductId}"); - return await Task.FromResult(false); + throw new Exception($"shippingItem.ProductId > 0 && product == null; shippingItem.ProductId: {shippingItem.ProductId}"); + + //shippingItem.ProductId = null; + + //if (!dbShippingItem.IsMeasured) shippingItem.ProductId = null; + //else return await Task.FromResult(false); } } @@ -139,7 +159,6 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet await ShippingItems.UpdateAsync(shippingItem); - //TODO: a measuredweight-eket is! - J. if (shippingItem.ProductId != dbShippingItem.ProductId || shippingItem.IsMeasured != dbShippingItem.IsMeasured || shippingItem.IsMeasurable != dbShippingItem.IsMeasurable || shippingItem.MeasuredQuantity != dbShippingItem.MeasuredQuantity || @@ -149,15 +168,28 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet if (shippingItem.IsMeasured) { - product!.StockQuantity += shippingItem.MeasuredQuantity.GetValueOrDefault(0); + product!.StockQuantity += shippingItem.MeasuredQuantity.GetValueOrDefault(0) - dbShippingItem.MeasuredQuantity.GetValueOrDefault(0); + + //IsMeasurable + //TODO: ROUND!!!! - J. + var measuringValues = new MeasuringAttributeValues + { + NetWeight = shippingItem.MeasuredNetWeight - dbShippingItem.MeasuredNetWeight, + GrossWeight = shippingItem.MeasuredGrossWeight- dbShippingItem.MeasuredGrossWeight, + IsMeasurable = shippingItem.IsMeasurable + }; if (!await UpdateProductStockQuantityAsync(product)) { - Logger.Error($"UpdateProductStockQuantity() == false; shippingItem! id: {product.Id}"); - return await Task.FromResult(false); + throw new Exception($"UpdateProductStockQuantity() == false; shippingItem! id: {product.Id}"); } + + //var measuringValues = new MeasuringAttributeValues(shippingItem.MeasuredNetWeight, shippingItem.MeasuredGrossWeight, shippingItem.IsMeasurable); + await _fruitBankAttributeService.InsertOrUpdateMeasuringAttributeValuesAsync(product.Id, measuringValues, true); } + if (productIdUnchanged) return true; + if (dbShippingItem.IsMeasured) { product = await Products.GetByIdAsync(dbShippingItem.ProductId); @@ -168,31 +200,30 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet if (!await UpdateProductStockQuantityAsync(product)) { - Logger.Error($"UpdateProductStockQuantity() == false; dbShippingItem! id: {product.Id}"); - return await Task.FromResult(false); + throw new Exception($"UpdateProductStockQuantity() == false; dbShippingItem! id: {product.Id}"); + //return await Task.FromResult(false); } + + var measuringValues = new MeasuringAttributeValues(-dbShippingItem.MeasuredNetWeight, -dbShippingItem.MeasuredGrossWeight, dbShippingItem.IsMeasurable); + await _fruitBankAttributeService.InsertOrUpdateMeasuringAttributeValuesAsync(product.Id, measuringValues, true); + } else Logger.Warning($"product == null; dbShippingItem.ProductId: {dbShippingItem.ProductId}"); } } //else //TODO: productIdUnchanged-et lekezelni! - J. + + return true; } catch (Exception ex) { - Logger.Error($"UpdateShippingItemAsync Transaction ERROR! Id: {shippingItem?.Id}", ex); - return await Task.FromResult(false); + throw new Exception($"UpdateShippingItemAsync Transaction ERROR! Id: {shippingItem?.Id}; ex: {ex.Message}", ex); } - - transaction.Complete(); - //} - //finally - //{ - // _locker.ExitWriteLock(); - //} - - return await Task.FromResult(true); + }); } + + private async Task UpdateProductStockQuantityAsync(int productId) { var product = await Products.GetByIdAsync(productId); diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingDbTable.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingDbTable.cs index 6614130..d20f7cd 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingDbTable.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingDbTable.cs @@ -29,9 +29,16 @@ public class ShippingDbTable : MgDbTableBase } public IQueryable GetAllNotMeasured(bool loadRelations) - => GetAll(loadRelations).Where(s => !s.IsAllMeasured); + => GetAll(loadRelations).Where(s => !s.IsAllMeasured || s.MeasuredDate == null || s.MeasuredDate < DateTime.Now.AddDays(1000)); public Task GetByIdAsync(int id, bool loadRelations) => GetAll(loadRelations).FirstOrDefaultAsync(s => s.Id == id); + + protected override void OnUpdate(Shipping entity) + { + if (entity is { IsAllMeasured: true, MeasuredDate: null }) entity.MeasuredDate = DateTime.Now; + + base.OnUpdate(entity); + } } \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingItemDbTable.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingItemDbTable.cs index e06e2a1..3fb5b89 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingItemDbTable.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingItemDbTable.cs @@ -41,5 +41,4 @@ public class ShippingItemDbTable : MgDbTableBase public IQueryable GetAllByShippingDocumentIdAsync(int shippingDocumentId, bool loadRelations) => GetAll(loadRelations).Where(si => si.ShippingDocumentId == shippingDocumentId); - } \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs b/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs index 1ee6b79..7fa9c37 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs @@ -2,26 +2,30 @@ using FruitBank.Common.Entities; using FruitBank.Common.Interfaces; using FruitBank.Common.Loggers; +using FruitBank.Common.Server; using Mango.Nop.Core.Loggers; using Mango.Nop.Services; using Microsoft.AspNetCore.Http; +using Nop.Core; using Nop.Core.Domain.Catalog; using Nop.Core.Events; using Nop.Plugin.Misc.FruitBankPlugin.Controllers; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; +using Nop.Services.Common; using Nop.Services.Events; namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.EventConsumers; -public class FruitBankEventConsumer(IHttpContextAccessor httpContextAccessor, FruitBankDbContext ctx, IEnumerable logWriters) : +public class FruitBankEventConsumer(IHttpContextAccessor httpContextAccessor, FruitBankDbContext ctx, IGenericAttributeService genericAttributeService, IStoreContext storeContext, IEnumerable logWriters) : MgEventConsumer(httpContextAccessor, logWriters), IConsumer>, IConsumer> { - public override Task HandleEventAsync(EntityUpdatedEvent eventMessage) + public override async Task HandleEventAsync(EntityUpdatedEvent eventMessage) { - - return base.HandleEventAsync(eventMessage); + var isMeasurableProduct = await genericAttributeService.GetAttributeAsync(eventMessage.Entity, FruitBankConst.IsMeasureableAttributeName, storeContext.GetCurrentStore().Id); + + await base.HandleEventAsync(eventMessage); } public async Task HandleEventAsync(EntityInsertedEvent eventMessage) diff --git a/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs b/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs index 98a1d9b..8620b58 100644 --- a/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs +++ b/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs @@ -21,7 +21,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin public class FruitBankPlugin : BasePlugin, IWidgetPlugin { protected readonly IActionContextAccessor _actionContextAccessor; + private readonly ISettingService _settingService; + //private readonly IWebHelper _webHelper; protected readonly IPermissionService _permissionService; protected readonly ILocalizationService _localizationService; @@ -35,9 +37,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin ISettingService settingService, //IWebHelper webHelper, ILocalizationService localizationService, - IPermissionService permissionService, - IUrlHelperFactory urlHelperFactory, - IAdminMenu adminMenu) + IPermissionService permissionService, + IUrlHelperFactory urlHelperFactory, + IAdminMenu adminMenu) { _actionContextAccessor = actionContextAccessor; _settingService = settingService; @@ -52,8 +54,13 @@ namespace Nop.Plugin.Misc.FruitBankPlugin public override async Task InstallAsync() { //TODO: Add "Measuring" role - FruitBankConst.MeasuringRoleSystemName + //TODO: Add "MeasuringRevisor" role - FruitBankConst.MeasuringRevisorRoleSystemName + + //TODO: Add "IsMeasurable" product attribute - FruitBankConst.IsMeasureableAttributeName //TODO: Add "NeedsToBeMeasured" product attribute if not exists + //TODO: Add unique index to GenericAttribute[EntityId, KeyGroup, Key]???!? ÁTGONDOLNI, mert lehet esetleg lista is a visszaadott, de a nopcommerce kódban felülírja ha azonos key-el vannak! - J. + // Default settings var settings = new OpenAiSettings { @@ -129,24 +136,24 @@ namespace Nop.Plugin.Misc.FruitBankPlugin Title = await _localizationService.GetResourceAsync("Plugins.Misc.SignalRApi.Menu.AI"), IconClass = "far fa-dot-circle", ChildNodes = new List - { - new() { - // SystemName = "FruitBankPlugin.Configure", - // Title = "AI Assistant", - // Url = $"{_webHelper.GetStoreLocation()}Admin/FruitBankPluginAdmin/Configure", - // Visible = true - Visible = true, - SystemName = PluginDescriptor.SystemName, - Title = PluginDescriptor.FriendlyName, - IconClass = "far fa-circle", - Url = _adminMenu.GetMenuItemUrl("FruitBankPlugin", "Configure"), - //Url = "Admin/SignalRApi/Configure", - //ControllerName = "SignalRApi", - //ActionName = "Configure", - //RouteValues = new RouteValueDictionary { { "area", AreaNames.ADMIN } } + new() + { + // SystemName = "FruitBankPlugin.Configure", + // Title = "AI Assistant", + // Url = $"{_webHelper.GetStoreLocation()}Admin/FruitBankPluginAdmin/Configure", + // Visible = true + Visible = true, + SystemName = PluginDescriptor.SystemName, + Title = PluginDescriptor.FriendlyName, + IconClass = "far fa-circle", + Url = _adminMenu.GetMenuItemUrl("FruitBankPlugin", "Configure"), + //Url = "Admin/SignalRApi/Configure", + //ControllerName = "SignalRApi", + //ActionName = "Configure", + //RouteValues = new RouteValueDictionary { { "area", AreaNames.ADMIN } } + } } - } }); } diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs index de6089e..654d95d 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs @@ -6,6 +6,7 @@ using FruitBank.Common.Interfaces; using FruitBank.Common.Server.Services.Loggers; using FruitBank.Common.Server.Services.SignalRs; using Mango.Nop.Core.Loggers; +using Mango.Nop.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.SignalR; @@ -36,12 +37,14 @@ public class PluginNopStartup : INopStartup //register services and interfaces - + services.AddSingleton(); + services.AddScoped(); //services.AddScoped(); services.AddScoped(); //services.AddSingleton(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj index 9971053..fa3c373 100644 --- a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj +++ b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj @@ -99,6 +99,9 @@ ..\..\..\..\FruitBankHybridApp\FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Services.Server.dll + + ..\..\..\..\FruitBankHybridApp\FruitBank.Common.Server\bin\Debug\net9.0\AyCode.Utils.dll + ..\..\..\..\FruitBankHybridApp\FruitBank.Common.Server\bin\Debug\net9.0\FruitBank.Common.dll diff --git a/Nop.Plugin.Misc.AIPlugin/Services/FruitBankAttributeService.cs b/Nop.Plugin.Misc.AIPlugin/Services/FruitBankAttributeService.cs new file mode 100644 index 0000000..20bc6d8 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Services/FruitBankAttributeService.cs @@ -0,0 +1,117 @@ +#nullable enable +using FruitBank.Common.Interfaces; +using Nop.Core; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Common; +using Nop.Services.Common; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Services; + +public class FruitBankAttributeService(IGenericAttributeService genericAttributeService, IStoreContext storeContext) +{ + public async Task GetGenericAttributeAsync(int entityId, string key, int storeId) + { + return (await genericAttributeService.GetAttributesForEntityAsync(entityId, typeof(TEntityType).Name)).SingleOrDefault(ga => ga.StoreId == storeId && ga.Key == key); + } + + public async Task> GetMeasuringAttributesAsync(int entityId, int storeId) + { + var measuringAttributes = (await genericAttributeService.GetAttributesForEntityAsync(entityId, typeof(TEntityType).Name)) + .Where(ga => ga.StoreId == storeId && + (ga.Key is nameof(MeasuringAttributeValues.NetWeight) or nameof(MeasuringAttributeValues.GrossWeight) or nameof(MeasuringAttributeValues.IsMeasurable))) + .ToList(); + + return measuringAttributes; + } + + public Task GetMeasuringAttributeValuesAsync(int entityId) + => GetMeasuringAttributeValuesAsync(entityId, storeContext.GetCurrentStore().Id); + + public async Task GetMeasuringAttributeValuesAsync(int entityId, int storeId) + { + var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); + + if (measuringAttributes.Count == 0) return null; + + var measuringAttributeValues = new MeasuringAttributeValues( + CommonHelper.To(measuringAttributes.FirstOrDefault(ga => ga.Key == nameof(MeasuringAttributeValues.NetWeight))), + CommonHelper.To(measuringAttributes.FirstOrDefault(ga => ga.Key == nameof(MeasuringAttributeValues.GrossWeight))), + CommonHelper.To(measuringAttributes.FirstOrDefault(ga => ga.Key == nameof(MeasuringAttributeValues.IsMeasurable)))); + + return measuringAttributeValues; + } + + public Task InsertOrUpdateMeasuringAttributeValuesAsync(int entityId, MeasuringAttributeValues measuringAttributeValues, bool cumulativeWeightUpdate) + => InsertOrUpdateMeasuringAttributeValuesAsync(entityId, storeContext.GetCurrentStore().Id, measuringAttributeValues, cumulativeWeightUpdate); + + public async Task InsertOrUpdateMeasuringAttributeValuesAsync(int entityId, int storeId, MeasuringAttributeValues measuringAttributeValues, bool cumulativeWeightUpdate) + { + if (!measuringAttributeValues.HasValues()) return false; + + var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); + + var netWeightAttribute = measuringAttributes.FirstOrDefault(ma => ma.Key == nameof(MeasuringAttributeValues.NetWeight)); + if (netWeightAttribute != null) await UpdateMeasuringWeightAttributeValueAsync(netWeightAttribute, measuringAttributeValues.NetWeight!.Value, cumulativeWeightUpdate); + else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.NetWeight), measuringAttributeValues.NetWeight!.Value, storeId); + + var grossWeightAttribute = measuringAttributes.FirstOrDefault(ma => ma.Key == nameof(MeasuringAttributeValues.GrossWeight)); + if (grossWeightAttribute != null) await UpdateMeasuringWeightAttributeValueAsync(grossWeightAttribute, measuringAttributeValues.GrossWeight!.Value, cumulativeWeightUpdate); + else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.GrossWeight), measuringAttributeValues.GrossWeight!.Value, storeId); + + var isMeasurableAttribute = measuringAttributes.FirstOrDefault(ma => ma.Key == nameof(MeasuringAttributeValues.IsMeasurable)); + if (isMeasurableAttribute != null) await UpdateGenericAttributeAsync(isMeasurableAttribute, measuringAttributeValues.IsMeasurable!.Value); + else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.IsMeasurable), measuringAttributeValues.IsMeasurable!.Value, storeId); + + return true; + } + + private async Task UpdateMeasuringWeightAttributeValueAsync(GenericAttribute genericAttribute, double newWeightValue, bool cumulativeWeightUpdate) + { + await UpdateGenericAttributeAsync(genericAttribute, cumulativeWeightUpdate ? CommonHelper.To(genericAttribute.Value) + newWeightValue : newWeightValue); + } + + public async Task DeleteAllMeasuringAttributesAsync(int entityId, int storeId) + { + var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); + if (measuringAttributes.Count == 0) return; + + foreach (var measuringAttribute in measuringAttributes) + { + await genericAttributeService.DeleteAttributeAsync(measuringAttribute); + } + } + + public async Task InsertGenericAttributeAsync(int entityId, string key, TPropType value, int storeId) + { + var genericAttribute = new GenericAttribute + { + Key = key, + EntityId = entityId, + KeyGroup = typeof(TEntityType).Name, + Value = CommonHelper.To(value), + StoreId = storeId + }; + + await genericAttributeService.InsertAttributeAsync(genericAttribute); + } + + public async Task UpdateGenericAttributeAsync(int entityId, string key, TPropType newValue, int storeId) + { + var ga = await GetGenericAttributeAsync(entityId, key, storeId); + await UpdateGenericAttributeAsync(ga!, newValue); + } + + public async Task UpdateGenericAttributeAsync(GenericAttribute genericAttribute, TPropType newValue) + { + genericAttribute.Value = CommonHelper.To(newValue); + await genericAttributeService.UpdateAttributeAsync(genericAttribute); + } + + public async Task DeleteGenericAttributeAsync(int entityId, string key, int storeId) + { + var ga = await GetGenericAttributeAsync(entityId, key, storeId); + if (ga == null) return; + + await genericAttributeService.DeleteAttributeAsync(ga); + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Services/ILockService.cs b/Nop.Plugin.Misc.AIPlugin/Services/ILockService.cs new file mode 100644 index 0000000..160a6b4 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Services/ILockService.cs @@ -0,0 +1,5 @@ +using Mango.Nop.Services; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Services; + +public interface ILockService : IMgLockService{} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Services/LockService.cs b/Nop.Plugin.Misc.AIPlugin/Services/LockService.cs new file mode 100644 index 0000000..787a38f --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Services/LockService.cs @@ -0,0 +1,14 @@ +using Mango.Nop.Services; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Services; + +public class LockService : MgLockService, ILockService +{ + public LockService() : this(new SemaphoreSlim(1)) + {} + + public LockService(SemaphoreSlim semaphoreSlim) : base(semaphoreSlim) + { + + } +} \ No newline at end of file From 53a336cb3e8883d854e6484e90391ea8d2f4aa17 Mon Sep 17 00:00:00 2001 From: Loretta Date: Tue, 30 Sep 2025 07:24:36 +0200 Subject: [PATCH 2/2] improvements, fixes, etc... --- .../Domains/DataLayer/FruitBankDbContext.cs | 125 +++++++++--------- .../Domains/DataLayer/ShippingItemDbTable.cs | 22 ++- .../EventConsumers/FruitBankEventConsumer.cs | 17 ++- Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs | 2 +- .../Services/EventConsumer.cs | 3 +- .../Services/FruitBankAttributeService.cs | 59 +++++---- 6 files changed, 126 insertions(+), 102 deletions(-) diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs index 07425e2..64b0bf3 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs @@ -127,119 +127,114 @@ public class FruitBankDbContext : MgDbContextBase, IPartnerDbSet { //Mi van ha nem jött meg a termék? Nem fogják tudni menteni... - J. - if (shippingItem.MeasuredQuantity <= 0) shippingItem.MeasuredQuantity = null; + Product? product = null; + var productIsMeasurable = false; - if (!shippingItem.IsMeasurable || shippingItem.MeasuredNetWeight <= 0) shippingItem.MeasuredNetWeight = null; - if (!shippingItem.IsMeasurable || shippingItem.MeasuredGrossWeight <= 0) shippingItem.MeasuredGrossWeight = null; - - //Update elÅ‘tt kivesszük a korábbi ShippingItem-et a db-bÅ‘l! - J. - var dbShippingItem = await ShippingItems.GetByIdAsync(shippingItem.Id); - if (dbShippingItem == null) - { - throw new Exception("dbShippingItem == null"); - } - - Product product = null; if (shippingItem.ProductId > 0) { product = await Products.GetByIdAsync(shippingItem.ProductId); if (product == null) - { throw new Exception($"shippingItem.ProductId > 0 && product == null; shippingItem.ProductId: {shippingItem.ProductId}"); - //shippingItem.ProductId = null; - - //if (!dbShippingItem.IsMeasured) shippingItem.ProductId = null; - //else return await Task.FromResult(false); - } + productIsMeasurable = await _fruitBankAttributeService.IsMeasurableEntityAsync(product.Id); } + shippingItem.IsMeasurable = productIsMeasurable; + + if (shippingItem.MeasuredQuantity <= 0) shippingItem.MeasuredQuantity = null; + if (!shippingItem.IsMeasurable || shippingItem.MeasuredNetWeight <= 0) shippingItem.MeasuredNetWeight = null; + if (!shippingItem.IsMeasurable || shippingItem.MeasuredGrossWeight <= 0) shippingItem.MeasuredGrossWeight = null; + + //Update elÅ‘tt kivesszük a korábbi ShippingItem-et a db-bÅ‘l! - J. + var dbShippingItem = await ShippingItems.GetByIdAsync(shippingItem.Id); + if (dbShippingItem == null) throw new Exception($"dbShippingItem == null; shippingItem.Id: {shippingItem.Id}"); + shippingItem.IsMeasured = product != null && shippingItem.IsValidMeasuringValues(); - await ShippingItems.UpdateAsync(shippingItem); + await ShippingItems.UpdateAsync(shippingItem, shippingItem.IsMeasured != dbShippingItem.IsMeasured); - if (shippingItem.ProductId != dbShippingItem.ProductId || - shippingItem.IsMeasured != dbShippingItem.IsMeasured || shippingItem.IsMeasurable != dbShippingItem.IsMeasurable || - shippingItem.MeasuredQuantity != dbShippingItem.MeasuredQuantity || - shippingItem.MeasuredNetWeight != dbShippingItem.MeasuredNetWeight || shippingItem.MeasuredGrossWeight != dbShippingItem.MeasuredGrossWeight) + if (shippingItem.ProductId == dbShippingItem.ProductId && + shippingItem.IsMeasured == dbShippingItem.IsMeasured && shippingItem.IsMeasurable == dbShippingItem.IsMeasurable && + shippingItem.MeasuredQuantity == dbShippingItem.MeasuredQuantity && + // ReSharper disable once CompareOfFloatsByEqualityOperator + shippingItem.MeasuredNetWeight == dbShippingItem.MeasuredNetWeight && + // ReSharper disable once CompareOfFloatsByEqualityOperator + shippingItem.MeasuredGrossWeight == dbShippingItem.MeasuredGrossWeight) { - var productIdUnchanged = shippingItem.ProductId == dbShippingItem.ProductId; + return true; + } - if (shippingItem.IsMeasured) + var productIdUnchanged = shippingItem.ProductId == dbShippingItem.ProductId; + + if (shippingItem.IsMeasured) + { + product!.StockQuantity += productIdUnchanged ? shippingItem.MeasuredQuantity!.Value - dbShippingItem.MeasuredQuantity.GetValueOrDefault(0) : shippingItem.MeasuredQuantity!.Value; + + if (!await UpdateProductStockQuantityAsync(product, true)) + throw new Exception($"UpdateProductStockQuantity() == false; shippingItem! product.Id: {product.Id}"); + + if (productIsMeasurable) { - product!.StockQuantity += shippingItem.MeasuredQuantity.GetValueOrDefault(0) - dbShippingItem.MeasuredQuantity.GetValueOrDefault(0); - - //IsMeasurable - //TODO: ROUND!!!! - J. var measuringValues = new MeasuringAttributeValues { - NetWeight = shippingItem.MeasuredNetWeight - dbShippingItem.MeasuredNetWeight, - GrossWeight = shippingItem.MeasuredGrossWeight- dbShippingItem.MeasuredGrossWeight, + NetWeight = productIdUnchanged ? shippingItem.MeasuredNetWeight - dbShippingItem.MeasuredNetWeight.GetValueOrDefault(0) : shippingItem.MeasuredNetWeight, + GrossWeight = productIdUnchanged ? shippingItem.MeasuredGrossWeight - dbShippingItem.MeasuredGrossWeight.GetValueOrDefault(0) : shippingItem.MeasuredGrossWeight, IsMeasurable = shippingItem.IsMeasurable }; - if (!await UpdateProductStockQuantityAsync(product)) - { - throw new Exception($"UpdateProductStockQuantity() == false; shippingItem! id: {product.Id}"); - } - - //var measuringValues = new MeasuringAttributeValues(shippingItem.MeasuredNetWeight, shippingItem.MeasuredGrossWeight, shippingItem.IsMeasurable); await _fruitBankAttributeService.InsertOrUpdateMeasuringAttributeValuesAsync(product.Id, measuringValues, true); } - - if (productIdUnchanged) return true; - - if (dbShippingItem.IsMeasured) - { - product = await Products.GetByIdAsync(dbShippingItem.ProductId); - - if (product != null) - { - product.StockQuantity -= dbShippingItem.MeasuredQuantity.GetValueOrDefault(0); - - if (!await UpdateProductStockQuantityAsync(product)) - { - throw new Exception($"UpdateProductStockQuantity() == false; dbShippingItem! id: {product.Id}"); - //return await Task.FromResult(false); - } - - var measuringValues = new MeasuringAttributeValues(-dbShippingItem.MeasuredNetWeight, -dbShippingItem.MeasuredGrossWeight, dbShippingItem.IsMeasurable); - await _fruitBankAttributeService.InsertOrUpdateMeasuringAttributeValuesAsync(product.Id, measuringValues, true); - - } - else Logger.Warning($"product == null; dbShippingItem.ProductId: {dbShippingItem.ProductId}"); - } } + + if (productIdUnchanged || !dbShippingItem.IsMeasured) return true; + + product = await Products.GetByIdAsync(dbShippingItem.ProductId); + + if (product != null) + { + productIsMeasurable = await _fruitBankAttributeService.IsMeasurableEntityAsync(product.Id); + product.StockQuantity -= dbShippingItem.MeasuredQuantity.GetValueOrDefault(0); + + if (!await UpdateProductStockQuantityAsync(product, true)) + throw new Exception($"UpdateProductStockQuantity() == false; dbShippingItem! product.Id: {product.Id}"); + + if (!productIsMeasurable) return true; + + var measuringValues = new MeasuringAttributeValues(-dbShippingItem.MeasuredNetWeight.GetValueOrDefault(0), -dbShippingItem.MeasuredGrossWeight.GetValueOrDefault(0), dbShippingItem.IsMeasurable); + await _fruitBankAttributeService.InsertOrUpdateMeasuringAttributeValuesAsync(product.Id, measuringValues, true); + + } + else Logger.Warning($"product == null; dbShippingItem.ProductId: {dbShippingItem.ProductId}"); //else //TODO: productIdUnchanged-et lekezelni! - J. return true; } catch (Exception ex) { - throw new Exception($"UpdateShippingItemAsync Transaction ERROR! Id: {shippingItem?.Id}; ex: {ex.Message}", ex); + throw new Exception($"UpdateShippingItemAsync->TransactionSafeAsync error! shippingItem.Id: {shippingItem.Id}; ex: {ex.Message}", ex); } }); } - private async Task UpdateProductStockQuantityAsync(int productId) + private async Task UpdateProductStockQuantityAsync(int productId, bool publishEvent) { var product = await Products.GetByIdAsync(productId); - if (product != null) return await UpdateProductStockQuantityAsync(product); + if (product != null) return await UpdateProductStockQuantityAsync(product, publishEvent); Logger.Error($"product == null; id: {productId}"); return await Task.FromResult(false); } - private async Task UpdateProductStockQuantityAsync(Product product) + private async Task UpdateProductStockQuantityAsync(Product product, bool publishEvent) { //Itt mi legyen? RollBack? - J. if (product.StockQuantity < 0) Logger.Error($"product.StockQuantity < 0; Id: {product.Id}; StockQuantity: {product.StockQuantity}"); - await Products.UpdateAsync(product, true); + await Products.UpdateAsync(product, publishEvent); return await Task.FromResult(true); //var updatedRowsCount = await DataProvider.ExecuteNonQueryAsync($"update product set {nameof(Product.StockQuantity)} = {product.StockQuantity} where {nameof(Product.Id)} = {product.Id}"); diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingItemDbTable.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingItemDbTable.cs index 3fb5b89..142afbd 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingItemDbTable.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/ShippingItemDbTable.cs @@ -16,8 +16,19 @@ public class ShippingItemDbTable : MgDbTableBase { } - public override IQueryable GetAll() - => base.GetAll(); + protected override void OnUpdate(ShippingItem entity) + { + RoundMeasuredValue(entity); + base.OnUpdate(entity); + } + + protected override void OnInsert(ShippingItem entity) + { + RoundMeasuredValue(entity); + base.OnInsert(entity); + } + + public override IQueryable GetAll() => base.GetAll(); public IQueryable GetAll(bool loadRelations) { @@ -41,4 +52,11 @@ public class ShippingItemDbTable : MgDbTableBase public IQueryable GetAllByShippingDocumentIdAsync(int shippingDocumentId, bool loadRelations) => GetAll(loadRelations).Where(si => si.ShippingDocumentId == shippingDocumentId); + + private static void RoundMeasuredValue(ShippingItem shippingItem) + { + if (shippingItem.MeasuredNetWeight.HasValue) shippingItem.MeasuredNetWeight = double.Round(shippingItem.MeasuredNetWeight.Value, 1); + if (shippingItem.MeasuredGrossWeight.HasValue) shippingItem.MeasuredGrossWeight = double.Round(shippingItem.MeasuredGrossWeight.Value, 1); + + } } \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs b/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs index 7fa9c37..ceb7026 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/EventConsumers/FruitBankEventConsumer.cs @@ -11,20 +11,25 @@ using Nop.Core.Domain.Catalog; using Nop.Core.Events; using Nop.Plugin.Misc.FruitBankPlugin.Controllers; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; +using Nop.Plugin.Misc.FruitBankPlugin.Services; using Nop.Services.Common; using Nop.Services.Events; namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.EventConsumers; -public class FruitBankEventConsumer(IHttpContextAccessor httpContextAccessor, FruitBankDbContext ctx, IGenericAttributeService genericAttributeService, IStoreContext storeContext, IEnumerable logWriters) : - MgEventConsumer(httpContextAccessor, logWriters), - IConsumer>, - IConsumer> +public class FruitBankEventConsumer(IHttpContextAccessor httpContextAccessor, FruitBankDbContext ctx, FruitBankAttributeService fruitBankAttributeService, IStoreContext storeContext, IEnumerable logWriters) : + MgEventConsumer(httpContextAccessor, logWriters), IConsumer>, IConsumer> { public override async Task HandleEventAsync(EntityUpdatedEvent eventMessage) { - var isMeasurableProduct = await genericAttributeService.GetAttributeAsync(eventMessage.Entity, FruitBankConst.IsMeasureableAttributeName, storeContext.GetCurrentStore().Id); + var product = eventMessage.Entity; + var isMeasurableProduct = await fruitBankAttributeService.IsMeasurableEntityAsync(product.Id); + var shippingItems = await ctx.ShippingItems.Table + .Where(si => si.ProductId == product.Id && !si.IsMeasured && si.IsMeasurable != isMeasurableProduct) + .ToListAsync(); + + await ctx.ShippingItems.UpdateAsync(shippingItems, false); await base.HandleEventAsync(eventMessage); } @@ -53,7 +58,7 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAccessor, Fr private async Task UpdateShippingDocumentIsAllMeasuredAsync(ShippingItem shippingItem) { - //TODO: where: && IsMeasureable!!!! - J. + //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); diff --git a/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs b/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs index 0ae795d..d582035 100644 --- a/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs +++ b/Nop.Plugin.Misc.AIPlugin/FruitBankPlugin.cs @@ -56,7 +56,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin //TODO: Add "Measuring" role - FruitBankConst.MeasuringRoleSystemName //TODO: Add "MeasuringRevisor" role - FruitBankConst.MeasuringRevisorRoleSystemName - //TODO: Add "IsMeasurable" product attribute - FruitBankConst.IsMeasureableAttributeName + //TODO: Add "IsMeasurable" product attribute - FruitBankConst.IsMeasurableAttributeName //TODO: Add "NeedsToBeMeasured" product attribute if not exists //TODO: Add unique index to GenericAttribute[EntityId, KeyGroup, Key]???!? ÁTGONDOLNI, mert lehet esetleg lista is a visszaadott, de a nopcommerce kódban felülírja ha azonos key-el vannak! - J. diff --git a/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs b/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs index c124fd2..55e4b9e 100644 --- a/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs +++ b/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs @@ -1,4 +1,5 @@ using System.Linq; +using FruitBank.Common.Interfaces; using Nop.Core; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Orders; @@ -96,7 +97,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services System.Diagnostics.Debug.WriteLine($"Spec Attribute: {specificationAttribute.Name}, Option: {specificationAttributeOption.Name}"); // Check if this is your "NeedsToBeMeasured" specification attribute - if (specificationAttribute.Name == "Measureable" && + if (specificationAttribute.Name == "Measureable" && //nameof(MeasuringAttributeValues.IsMeasurable) specificationAttributeOption.Name == "Yes") // or whatever value you set { requiresMeasurement = true; diff --git a/Nop.Plugin.Misc.AIPlugin/Services/FruitBankAttributeService.cs b/Nop.Plugin.Misc.AIPlugin/Services/FruitBankAttributeService.cs index 20bc6d8..75410a3 100644 --- a/Nop.Plugin.Misc.AIPlugin/Services/FruitBankAttributeService.cs +++ b/Nop.Plugin.Misc.AIPlugin/Services/FruitBankAttributeService.cs @@ -9,14 +9,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services; public class FruitBankAttributeService(IGenericAttributeService genericAttributeService, IStoreContext storeContext) { - public async Task GetGenericAttributeAsync(int entityId, string key, int storeId) + public async Task GetGenericAttributeAsync(int entityId, string key, int storeId) { - return (await genericAttributeService.GetAttributesForEntityAsync(entityId, typeof(TEntityType).Name)).SingleOrDefault(ga => ga.StoreId == storeId && ga.Key == key); + return (await genericAttributeService.GetAttributesForEntityAsync(entityId, typeof(TEntity).Name)).SingleOrDefault(ga => ga.StoreId == storeId && ga.Key == key); } - public async Task> GetMeasuringAttributesAsync(int entityId, int storeId) + public async Task> GetMeasuringAttributesAsync(int entityId, int storeId) { - var measuringAttributes = (await genericAttributeService.GetAttributesForEntityAsync(entityId, typeof(TEntityType).Name)) + var measuringAttributes = (await genericAttributeService.GetAttributesForEntityAsync(entityId, typeof(TEntity).Name)) .Where(ga => ga.StoreId == storeId && (ga.Key is nameof(MeasuringAttributeValues.NetWeight) or nameof(MeasuringAttributeValues.GrossWeight) or nameof(MeasuringAttributeValues.IsMeasurable))) .ToList(); @@ -24,12 +24,12 @@ public class FruitBankAttributeService(IGenericAttributeService genericAttribute return measuringAttributes; } - public Task GetMeasuringAttributeValuesAsync(int entityId) - => GetMeasuringAttributeValuesAsync(entityId, storeContext.GetCurrentStore().Id); + public Task GetMeasuringAttributeValuesAsync(int entityId) + => GetMeasuringAttributeValuesAsync(entityId, storeContext.GetCurrentStore().Id); - public async Task GetMeasuringAttributeValuesAsync(int entityId, int storeId) + public async Task GetMeasuringAttributeValuesAsync(int entityId, int storeId) { - var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); + var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); if (measuringAttributes.Count == 0) return null; @@ -41,28 +41,33 @@ public class FruitBankAttributeService(IGenericAttributeService genericAttribute return measuringAttributeValues; } - public Task InsertOrUpdateMeasuringAttributeValuesAsync(int entityId, MeasuringAttributeValues measuringAttributeValues, bool cumulativeWeightUpdate) - => InsertOrUpdateMeasuringAttributeValuesAsync(entityId, storeContext.GetCurrentStore().Id, measuringAttributeValues, cumulativeWeightUpdate); - - public async Task InsertOrUpdateMeasuringAttributeValuesAsync(int entityId, int storeId, MeasuringAttributeValues measuringAttributeValues, bool cumulativeWeightUpdate) + public Task IsMeasurableEntityAsync(int entityId) => IsMeasurableEntityAsync(entityId, storeContext.GetCurrentStore().Id); + public async Task IsMeasurableEntityAsync(int entityId, int storeId) { - if (!measuringAttributeValues.HasValues()) return false; + var measurableAttribute = await GetGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.IsMeasurable), storeId); + return measurableAttribute != null && CommonHelper.To(measurableAttribute.Value); + } - var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); + public Task InsertOrUpdateMeasuringAttributeValuesAsync(int entityId, MeasuringAttributeValues measuringAttributeValues, bool cumulativeWeightUpdate) + => InsertOrUpdateMeasuringAttributeValuesAsync(entityId, storeContext.GetCurrentStore().Id, measuringAttributeValues, cumulativeWeightUpdate); + + public async Task InsertOrUpdateMeasuringAttributeValuesAsync(int entityId, int storeId, MeasuringAttributeValues measuringAttributeValues, bool cumulativeWeightUpdate) + { + if (!measuringAttributeValues.HasValues()) throw new Exception($"FruitBankAttributeService->InsertOrUpdateMeasuringAttributeValuesAsync; measuringAttributeValues.HasValues() == false; entityId: {entityId}; values: {measuringAttributeValues}"); + + var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); var netWeightAttribute = measuringAttributes.FirstOrDefault(ma => ma.Key == nameof(MeasuringAttributeValues.NetWeight)); if (netWeightAttribute != null) await UpdateMeasuringWeightAttributeValueAsync(netWeightAttribute, measuringAttributeValues.NetWeight!.Value, cumulativeWeightUpdate); - else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.NetWeight), measuringAttributeValues.NetWeight!.Value, storeId); + else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.NetWeight), measuringAttributeValues.NetWeight!.Value, storeId); var grossWeightAttribute = measuringAttributes.FirstOrDefault(ma => ma.Key == nameof(MeasuringAttributeValues.GrossWeight)); if (grossWeightAttribute != null) await UpdateMeasuringWeightAttributeValueAsync(grossWeightAttribute, measuringAttributeValues.GrossWeight!.Value, cumulativeWeightUpdate); - else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.GrossWeight), measuringAttributeValues.GrossWeight!.Value, storeId); + else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.GrossWeight), measuringAttributeValues.GrossWeight!.Value, storeId); var isMeasurableAttribute = measuringAttributes.FirstOrDefault(ma => ma.Key == nameof(MeasuringAttributeValues.IsMeasurable)); if (isMeasurableAttribute != null) await UpdateGenericAttributeAsync(isMeasurableAttribute, measuringAttributeValues.IsMeasurable!.Value); - else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.IsMeasurable), measuringAttributeValues.IsMeasurable!.Value, storeId); - - return true; + else await InsertGenericAttributeAsync(entityId, nameof(MeasuringAttributeValues.IsMeasurable), measuringAttributeValues.IsMeasurable!.Value, storeId); } private async Task UpdateMeasuringWeightAttributeValueAsync(GenericAttribute genericAttribute, double newWeightValue, bool cumulativeWeightUpdate) @@ -70,9 +75,9 @@ public class FruitBankAttributeService(IGenericAttributeService genericAttribute await UpdateGenericAttributeAsync(genericAttribute, cumulativeWeightUpdate ? CommonHelper.To(genericAttribute.Value) + newWeightValue : newWeightValue); } - public async Task DeleteAllMeasuringAttributesAsync(int entityId, int storeId) + public async Task DeleteAllMeasuringAttributesAsync(int entityId, int storeId) { - var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); + var measuringAttributes = await GetMeasuringAttributesAsync(entityId, storeId); if (measuringAttributes.Count == 0) return; foreach (var measuringAttribute in measuringAttributes) @@ -81,13 +86,13 @@ public class FruitBankAttributeService(IGenericAttributeService genericAttribute } } - public async Task InsertGenericAttributeAsync(int entityId, string key, TPropType value, int storeId) + public async Task InsertGenericAttributeAsync(int entityId, string key, TPropType value, int storeId) { var genericAttribute = new GenericAttribute { Key = key, EntityId = entityId, - KeyGroup = typeof(TEntityType).Name, + KeyGroup = typeof(TEntity).Name, Value = CommonHelper.To(value), StoreId = storeId }; @@ -95,9 +100,9 @@ public class FruitBankAttributeService(IGenericAttributeService genericAttribute await genericAttributeService.InsertAttributeAsync(genericAttribute); } - public async Task UpdateGenericAttributeAsync(int entityId, string key, TPropType newValue, int storeId) + public async Task UpdateGenericAttributeAsync(int entityId, string key, TPropType newValue, int storeId) { - var ga = await GetGenericAttributeAsync(entityId, key, storeId); + var ga = await GetGenericAttributeAsync(entityId, key, storeId); await UpdateGenericAttributeAsync(ga!, newValue); } @@ -107,9 +112,9 @@ public class FruitBankAttributeService(IGenericAttributeService genericAttribute await genericAttributeService.UpdateAttributeAsync(genericAttribute); } - public async Task DeleteGenericAttributeAsync(int entityId, string key, int storeId) + public async Task DeleteGenericAttributeAsync(int entityId, string key, int storeId) { - var ga = await GetGenericAttributeAsync(entityId, key, storeId); + var ga = await GetGenericAttributeAsync(entityId, key, storeId); if (ga == null) return; await genericAttributeService.DeleteAttributeAsync(ga);