From 6e43a1d18f55f86c35da8b71997b77c0b49066cf Mon Sep 17 00:00:00 2001 From: Loretta Date: Mon, 29 Sep 2025 13:33:40 +0200 Subject: [PATCH] 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