From 070d7ec3d243e2797b4edebbbac25d7cdcd3802a Mon Sep 17 00:00:00 2001 From: Loretta Date: Mon, 1 Dec 2025 16:17:57 +0100 Subject: [PATCH] .Net10, VS2026; StockTaking in progress... --- .../Controllers/StockSignalREndpointServer.cs | 119 ++++++++++++++++++ .../Controllers/FruitBankDataController.cs | 26 ++-- .../Domains/DataLayer/FruitBankDbContext.cs | 6 +- .../DataLayer/Interfaces/IStockTakingDbSet.cs | 10 ++ .../Interfaces/IStockTakingItemDbSet.cs | 10 ++ .../Interfaces/IStockTakingItemPalletDbSet.cs | 10 ++ .../Domains/DataLayer/StockTakingDbContext.cs | 74 +++++++++++ .../Domains/DataLayer/StockTakingDbTable.cs | 32 +++++ .../DataLayer/StockTakingItemDbTable.cs | 31 +++++ .../DataLayer/StockTakingItemPalletDbTable.cs | 28 +++++ .../Infrastructure/PluginNopStartup.cs | 6 + .../Mapping/NameCompatibility.cs | 6 +- .../Nop.Plugin.Misc.FruitBankPlugin.csproj | 8 +- 13 files changed, 351 insertions(+), 15 deletions(-) create mode 100644 Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/StockSignalREndpointServer.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingDbSet.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingItemDbSet.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingItemPalletDbSet.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingDbContext.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingDbTable.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingItemDbTable.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingItemPalletDbTable.cs diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/StockSignalREndpointServer.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/StockSignalREndpointServer.cs new file mode 100644 index 0000000..2562c71 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/StockSignalREndpointServer.cs @@ -0,0 +1,119 @@ +using AyCode.Core.Loggers; +using AyCode.Services.SignalRs; +using FruitBank.Common.Entities; +using FruitBank.Common.Server.Interfaces; +using FruitBank.Common.Server.Services.SignalRs; +using FruitBank.Common.SignalRs; +using Mango.Nop.Core.Loggers; +using Nop.Core; +using Nop.Core.Events; +using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers; + +public class StockSignalREndpointServer(StockTakingDbContext ctx, SignalRSendToClientService sendToClient, IEventPublisher eventPublisher, IWorkContext workContext, IEnumerable logWriters) + : IStockSignalREndpointServer +{ + private const int LastStockTakingDays = 15; + private readonly ILogger _logger = new Logger(logWriters.ToArray()); + + [SignalR(SignalRTags.GetStockTakings)] + public async Task> GetStockTakings() + { + return await ctx.StockTakings.GetAll(true).ToListAsync(); + } + + public async Task> GetStockTakingsByProductId(int productId) + { + throw new NotImplementedException(); + } + + [SignalR(SignalRTags.AddStockTaking)] + public async Task AddStockTaking(StockTaking stockTaking) + { + var result = await ctx.TransactionSafeAsync(async _ => + { + await ctx.StockTakings.InsertAsync(stockTaking); + + var productDtos = await ctx.ProductDtos.GetAll(true).ToListAsync(); + + foreach (var productDto in productDtos) + { + var stockTakingItem = new StockTakingItem + { + StockTakingId = stockTaking.Id, + ProductId = productDto.Id, + //IsMeasurable = productDto.IsMeasurable, + OriginalStockQuantity = productDto.StockQuantity, + OriginalNetWeight = productDto.NetWeight + }; + + await ctx.StockTakingItems.InsertAsync(stockTakingItem); + } + + return true; + }); + + if (result) return await ctx.StockTakings.GetByIdAsync(stockTaking.Id, false); + return null; + + } + + public async Task UpdateStockTaking(StockTaking stockTaking) + { + throw new NotImplementedException(); + } + + [SignalR(SignalRTags.GetStockTakingItems)] + public async Task> GetStockTakingItems() + { + return await ctx.StockTakingItems.GetAll(true).ToListAsync(); + } + + [SignalR(SignalRTags.GetStockTakingItemsById)] + public async Task GetStockTakingItemsById(int stockTakingItemId) + { + var result = await ctx.StockTakingItems.GetByIdAsync(stockTakingItemId, true); + return result; + } + + public async Task> GetStockTakingItemsByProductId(int productId) + { + throw new NotImplementedException(); + } + + public async Task> GetStockTakingItemsByStockTakingId(int stockTakingId) + { + throw new NotImplementedException(); + } + + public async Task AddStockTakingItem(StockTakingItem stockTakingItem) + { + throw new NotImplementedException(); + } + + public async Task UpdateStockTakingItem(StockTakingItem stockTakingItem) + { + throw new NotImplementedException(); + } + + public async Task> GetStockTakingItemPallets() + { + throw new NotImplementedException(); + } + + public async Task> GetStockTakingItemPalletsByProductId(int productId) + { + throw new NotImplementedException(); + } + + public async Task AddStockTakingItemPallet(StockTakingItemPallet stockTakingItemPallet) + { + throw new NotImplementedException(); + } + + public async Task UpdateStockTakingItemPallet(StockTakingItemPallet stockTakingItemPallet) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs b/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs index c34535c..d80f74f 100644 --- a/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs @@ -70,20 +70,30 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers return await ctx.GenericAttributeDtos.GetByIdAsync(genericAttributeDto.Id); } + [SignalR(SignalRTags.GetStockQuantityHistoryDtos)] + public async Task> GetStockQuantityHistoryDtos() + { + _logger.Detail($"GetStockQuantityHistoryDtos invoked; lastDaysCount: {LastShippingDays}"); + + var fromDateUtc = DateTime.UtcNow.Date.AddDays(-LastShippingDays); + return await ctx.StockQuantityHistoryDtos.GetAll(true).Where(sqh => sqh.CreatedOnUtc >= fromDateUtc).ToListAsync(); + } + + [SignalR(SignalRTags.GetStockQuantityHistoryDtosByProductId)] + public async Task> GetStockQuantityHistoryDtosByProductId(int productId) + { + _logger.Detail($"GetStockQuantityHistoryDtosByProductId invoked; productId: {productId}; lastDaysCount: {LastShippingDays}"); + + var fromDateUtc = DateTime.UtcNow.Date.AddDays(-LastShippingDays); + return await ctx.StockQuantityHistoryDtos.GetByProductIdAsync(productId, true).Where(sqh => sqh.CreatedOnUtc >= fromDateUtc).ToListAsync(); + } + [SignalR(SignalRTags.GetMeasuringModels)] public Task> GetMeasuringModels() { throw new NotImplementedException("GetMeasuringModels"); } - [SignalR(SignalRTags.GetStockQuantityHistoryDtos)] - public async Task> GetStockQuantityHistoryDtos() - => await ctx.StockQuantityHistoryDtos.GetAll(true).ToListAsync(); - - [SignalR(SignalRTags.GetStockQuantityHistoryDtosByProductId)] - public async Task> GetStockQuantityHistoryDtosByProductId(int productId) - => await ctx.StockQuantityHistoryDtos.GetByProductIdAsync(productId, true).ToListAsync(); - [SignalR(SignalRTags.GetPartners)] public async Task> GetPartners() { diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs index 1a3587c..8b05e30 100644 --- a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/FruitBankDbContext.cs @@ -6,6 +6,7 @@ using FruitBank.Common.Dtos; using FruitBank.Common.Entities; using FruitBank.Common.Interfaces; using FruitBank.Common.Models; +using Mango.Nop.Core.Dtos; using Mango.Nop.Core.Entities; using Mango.Nop.Core.Extensions; using Mango.Nop.Core.Loggers; @@ -25,6 +26,7 @@ using Nop.Plugin.Misc.FruitBankPlugin.Services; using Nop.Services.Catalog; using Nop.Services.Security; using WebMarkupMin.Core.Loggers; +using static Nop.Services.Security.StandardPermission; namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; @@ -101,8 +103,7 @@ public class FruitBankDbContext : MgDbContextBase, Files = filesDbTable; Partners = partnerDbTable; - Products = productRepository; - + ProductDtos = productDtoDbTable; OrderDtos = orderDtoDbTable; @@ -766,4 +767,5 @@ public class FruitBankDbContext : MgDbContextBase, await CustomerAddressMappings.InsertAsync(customerAddressMapping); return customerAddressMapping; } + } \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingDbSet.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingDbSet.cs new file mode 100644 index 0000000..22f2bff --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingDbSet.cs @@ -0,0 +1,10 @@ +using FruitBank.Common.Entities; +using Mango.Nop.Data.Interfaces; +using Nop.Data; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces; + +public interface IStockTakingDbSet : IMgDbTableBase where TDbTable : IRepository +{ + public TDbTable StockTakings { get; set; } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingItemDbSet.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingItemDbSet.cs new file mode 100644 index 0000000..7674bec --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingItemDbSet.cs @@ -0,0 +1,10 @@ +using FruitBank.Common.Entities; +using Mango.Nop.Data.Interfaces; +using Nop.Data; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces; + +public interface IStockTakingItemDbSet : IMgDbTableBase where TDbTable : IRepository +{ + public TDbTable StockTakingItems { get; set; } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingItemPalletDbSet.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingItemPalletDbSet.cs new file mode 100644 index 0000000..08eef5e --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/Interfaces/IStockTakingItemPalletDbSet.cs @@ -0,0 +1,10 @@ +using FruitBank.Common.Entities; +using Mango.Nop.Data.Interfaces; +using Nop.Data; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces; + +public interface IStockTakingItemPalletDbSet : IMgDbTableBase where TDbTable : IRepository +{ + public TDbTable StockTakingItemPallets { get; set; } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingDbContext.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingDbContext.cs new file mode 100644 index 0000000..7f77053 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingDbContext.cs @@ -0,0 +1,74 @@ +#nullable enable +using AyCode.Core.Loggers; +using FruitBank.Common.Dtos; +using Mango.Nop.Core.Entities; +using Mango.Nop.Core.Loggers; +using Mango.Nop.Data.Repositories; +using Nop.Core; +using Nop.Core.Caching; +using Nop.Core.Domain.Catalog; +using Nop.Core.Domain.Common; +using Nop.Core.Domain.Orders; +using Nop.Core.Events; +using Nop.Data; +using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces; +using Nop.Plugin.Misc.FruitBankPlugin.Services; +using Nop.Services.Catalog; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; + +public class StockTakingDbContext : MgDbContextBase, + IStockTakingDbSet, + IStockTakingItemDbSet, + IStockTakingItemPalletDbSet +{ + public ProductDtoDbTable ProductDtos { get; set; } + + public StockTakingDbTable StockTakings { get; set; } + public StockTakingItemDbTable StockTakingItems { get; set; } + public StockTakingItemPalletDbTable StockTakingItemPallets { get; set; } + public StockQuantityHistoryDtoDbTable StockQuantityHistoryDtos { get; set; } + + public IRepository GenericAttributes { get; set; } + public IRepository StockQuantityHistories { get; set; } + public IRepository StockQuantityHistoriesExt { get; set; } + + private readonly IStoreContext _storeContext; + private readonly IProductService _productService; + private readonly IStaticCacheManager _staticCacheManager; + protected readonly IEventPublisher _eventPublisher; + + public StockTakingDbContext(INopDataProvider dataProvider, ILockService lockService, IStoreContext storeContext, + ProductDtoDbTable productDtoDbTable, + StockQuantityHistoryDtoDbTable stockQuantityHistoryDtos, + StockTakingDbTable stockTakingDbTable, + StockTakingItemDbTable stockTakingItemDbTable, + StockTakingItemPalletDbTable stockTakingItemPalletDbTable, + IProductService productService, IStaticCacheManager staticCacheManager, + IRepository orderRepository, + IRepository orderItemRepository, + IRepository productRepository, + IRepository genericAttributes, + IRepository stockQuantityHistories, + IRepository stockQuantityHistoriesExt, + IEventPublisher eventPublisher, + IEnumerable logWriters) : base(productRepository, orderRepository, orderItemRepository, dataProvider, lockService, new Logger(logWriters.ToArray())) + { + _eventPublisher = eventPublisher; + _storeContext = storeContext; + _productService = productService; + _staticCacheManager = staticCacheManager; + + ProductDtos = productDtoDbTable; + + GenericAttributes = genericAttributes; + + StockQuantityHistories = stockQuantityHistories; + StockQuantityHistoriesExt = stockQuantityHistoriesExt; + StockQuantityHistoryDtos = stockQuantityHistoryDtos; + + StockTakings = stockTakingDbTable; + StockTakingItems = stockTakingItemDbTable; + StockTakingItemPallets = stockTakingItemPalletDbTable; + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingDbTable.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingDbTable.cs new file mode 100644 index 0000000..7f187c7 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingDbTable.cs @@ -0,0 +1,32 @@ +using FruitBank.Common.Entities; +using LinqToDB; +using Mango.Nop.Data.Repositories; +using Nop.Core.Caching; +using Nop.Core.Configuration; +using Nop.Core.Events; +using Nop.Data; +using static LinqToDB.Reflection.Methods.LinqToDB.Insert; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; + +public class StockTakingDbTable : MgDbTableBase +{ + public StockTakingDbTable(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings) + : base(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings) + { + } + + public override IQueryable GetAll() => base.GetAll(); + + public IQueryable GetAll(bool loadRelations) + { + return loadRelations + ? GetAll() + .LoadWith(st => st.StockTakingItems).ThenLoad(sti => sti.Product).ThenLoad(prod => prod.GenericAttributes) + //.LoadWith(st => st.StockTakingItems).ThenLoad(sti => sti.StockTakingItemPallets) + : GetAll().LoadWith(st => st.StockTakingItems); + } + + public Task GetByIdAsync(int id, bool loadRelations) + => GetAll(loadRelations).FirstOrDefaultAsync(st => st.Id == id); +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingItemDbTable.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingItemDbTable.cs new file mode 100644 index 0000000..324b819 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingItemDbTable.cs @@ -0,0 +1,31 @@ +using FruitBank.Common.Entities; +using LinqToDB; +using Mango.Nop.Data.Repositories; +using Nop.Core.Caching; +using Nop.Core.Configuration; +using Nop.Core.Events; +using Nop.Data; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; + +public class StockTakingItemDbTable : MgDbTableBase +{ + public StockTakingItemDbTable(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings) + : base(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings) + { + } + + public override IQueryable GetAll() => base.GetAll(); + + public IQueryable GetAll(bool loadRelations) + { + return loadRelations + ? GetAll() + .LoadWith(sti => sti.StockTaking) + //.LoadWith(sti => sti.StockTakingItemPallets) + .LoadWith(sti => sti.Product).ThenLoad(prod => prod.GenericAttributes) + : GetAll(); + } + + public Task GetByIdAsync(int stockTakingItemId, bool loadRelations) => GetAll(loadRelations).FirstOrDefaultAsync(sti => sti.Id == stockTakingItemId); +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingItemPalletDbTable.cs b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingItemPalletDbTable.cs new file mode 100644 index 0000000..3c1c7ff --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/StockTakingItemPalletDbTable.cs @@ -0,0 +1,28 @@ +using FruitBank.Common.Entities; +using LinqToDB; +using Mango.Nop.Data.Repositories; +using Nop.Core.Caching; +using Nop.Core.Configuration; +using Nop.Core.Events; +using Nop.Data; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; + +public class StockTakingItemPalletDbTable : MgDbTableBase +{ + public StockTakingItemPalletDbTable(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings) + : base(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings) + { + } + + public override IQueryable GetAll() => base.GetAll(); + + public IQueryable GetAll(bool loadRelations) + { + return loadRelations + ? GetAll() + .LoadWith(stip => stip.StockTakingItem).ThenLoad(sti => sti.StockTaking) + .LoadWith(stip => stip.StockTakingItem).ThenLoad(sti => sti.Product).ThenLoad(prod => prod.GenericAttributes) + : GetAll(); + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs index e1f11a7..9f836f3 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs @@ -75,11 +75,17 @@ public class PluginNopStartup : INopStartup services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); //services.AddScoped(); services.AddScoped(); diff --git a/Nop.Plugin.Misc.AIPlugin/Mapping/NameCompatibility.cs b/Nop.Plugin.Misc.AIPlugin/Mapping/NameCompatibility.cs index 010dd47..e4d2e6f 100644 --- a/Nop.Plugin.Misc.AIPlugin/Mapping/NameCompatibility.cs +++ b/Nop.Plugin.Misc.AIPlugin/Mapping/NameCompatibility.cs @@ -7,6 +7,7 @@ using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Common; using Nop.Core.Domain.Orders; using Nop.Data.Mapping; +using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; namespace Nop.Plugin.Misc.FruitBankPlugin.Mapping; @@ -37,7 +38,10 @@ public partial class NameCompatibility : INameCompatibility { typeof(StockQuantityHistoryDto), nameof(StockQuantityHistory)}, { typeof(StockQuantityHistoryExt), FruitBankConstClient.StockQuantityHistoryExtDbTableName}, - + + { typeof(StockTaking), FruitBankConstClient.StockTakingDbTableName}, + { typeof(StockTakingItem), FruitBankConstClient.StockTakingItemDbTableName}, + { typeof(StockTakingItemPallet), FruitBankConstClient.StockTakingItemPalletDbTableName}, }; diff --git a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj index 2e1a406..8cb8ff4 100644 --- a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj +++ b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj @@ -24,10 +24,10 @@ - - - - + + + +