using AyCode.Core.Loggers; using FruitBank.Common.Entities; using FruitBank.Common.Enums; using LinqToDB; using Mango.Nop.Core.Loggers; using Mango.Nop.Data.Repositories; using Nop.Core.Caching; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Customers; 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; namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; public class PreorderDbContext : IPreorderDbSet, IPreorderItemDbSet { private readonly ILogger _logger; public PreorderDbTable Preorders { get; set; } public PreorderItemDbTable PreorderItems { get; set; } // Read-only access to related NopCommerce repositories needed during conversion public IRepository Customers { get; set; } public IRepository Products { get; set; } public IRepository Orders { get; set; } public IRepository OrderItems { get; set; } public PreorderDbContext( PreorderDbTable preorderDbTable, PreorderItemDbTable preorderItemDbTable, IRepository customerRepository, IRepository productRepository, IRepository orderRepository, IRepository orderItemRepository, IEnumerable logWriters) { Preorders = preorderDbTable; PreorderItems = preorderItemDbTable; Customers = customerRepository; Products = productRepository; Orders = orderRepository; OrderItems = orderItemRepository; _logger = new Logger(logWriters.ToArray()); } /// /// Insert a complete preorder with all its items in one operation. /// Returns the saved preorder (with Id populated). /// public async Task InsertPreorderAsync(Preorder preorder, IList items) { preorder.CreatedOnUtc = DateTime.UtcNow; preorder.UpdatedOnUtc = DateTime.UtcNow; preorder.Status = PreorderStatus.Pending; await Preorders.InsertAsync(preorder); foreach (var item in items) { item.PreorderId = preorder.Id; item.FulfilledQuantity = 0; item.Status = PreorderItemStatus.Pending; await PreorderItems.InsertAsync(item); } _logger.Info($"PreorderDbContext: inserted Preorder #{preorder.Id} with {items.Count} items for customer #{preorder.CustomerId}"); return preorder; } /// /// Returns all pending preorder items for a set of productIds, ordered by PreorderId (FCFS). /// Used by PreorderConversionService after IncomingQuantity is written. /// public async Task> GetPendingItemsForProductsAsync(IList productIds) { // Fetch all items for these products first, then filter by status in memory // LinqToDB cannot translate enum comparisons to SQL in this codebase var all = await PreorderItems.Table .Where(i => productIds.Contains(i.ProductId)) .OrderBy(i => i.PreorderId) .ToListAsync(); return all.Where(i => i.Status == PreorderItemStatus.Pending || i.Status == PreorderItemStatus.PartiallyFulfilled) .ToList(); } /// /// After conversion: check if all items in a preorder are resolved and update the preorder's status. /// public async Task RefreshPreorderStatusAsync(int preorderId) { var preorder = await Preorders.GetByIdAsync(preorderId); if (preorder == null) return; var items = await PreorderItems.GetAllByPreorderIdAsync(preorderId).ToListAsync(); var hasDropped = items.Any(i => i.Status == PreorderItemStatus.Dropped); var hasPartial = items.Any(i => i.Status == PreorderItemStatus.PartiallyFulfilled); var hasPending = items.Any(i => i.Status == PreorderItemStatus.Pending); var allFulfilled = items.All(i => i.Status == PreorderItemStatus.Fulfilled); preorder.Status = (hasDropped || hasPartial) && !hasPending ? PreorderStatus.PartiallyFulfilled : allFulfilled ? PreorderStatus.Confirmed : PreorderStatus.Pending; preorder.UpdatedOnUtc = DateTime.UtcNow; await Preorders.UpdateAsync(preorder); _logger.Info($"PreorderDbContext: Preorder #{preorderId} status → {preorder.Status}"); } /// /// Mark a preorder as cancelled (customer or admin action). /// public async Task CancelPreorderAsync(int preorderId) { var preorder = await Preorders.GetByIdAsync(preorderId); if (preorder == null) return; preorder.Status = PreorderStatus.Cancelled; preorder.UpdatedOnUtc = DateTime.UtcNow; await Preorders.UpdateAsync(preorder); var items = await PreorderItems.GetAllByPreorderIdAsync(preorderId).ToListAsync(); var cancellableStatuses = new[] { PreorderItemStatus.Pending, PreorderItemStatus.PartiallyFulfilled }; foreach (var item in items.Where(i => cancellableStatuses.Contains(i.Status))) { item.Status = PreorderItemStatus.Dropped; await PreorderItems.UpdateAsync(item); } _logger.Info($"PreorderDbContext: Preorder #{preorderId} cancelled"); } }