Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/Domains/DataLayer/PreorderDbContext.cs

142 lines
5.5 KiB
C#

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<PreorderDbTable>,
IPreorderItemDbSet<PreorderItemDbTable>
{
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<Customer> Customers { get; set; }
public IRepository<Product> Products { get; set; }
public IRepository<Order> Orders { get; set; }
public IRepository<OrderItem> OrderItems { get; set; }
public PreorderDbContext(
PreorderDbTable preorderDbTable,
PreorderItemDbTable preorderItemDbTable,
IRepository<Customer> customerRepository,
IRepository<Product> productRepository,
IRepository<Order> orderRepository,
IRepository<OrderItem> orderItemRepository,
IEnumerable<IAcLogWriterBase> logWriters)
{
Preorders = preorderDbTable;
PreorderItems = preorderItemDbTable;
Customers = customerRepository;
Products = productRepository;
Orders = orderRepository;
OrderItems = orderItemRepository;
_logger = new Logger<PreorderDbContext>(logWriters.ToArray());
}
/// <summary>
/// Insert a complete preorder with all its items in one operation.
/// Returns the saved preorder (with Id populated).
/// </summary>
public async Task<Preorder> InsertPreorderAsync(Preorder preorder, IList<PreorderItem> 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;
}
/// <summary>
/// Returns all pending preorder items for a set of productIds, ordered by PreorderId (FCFS).
/// Used by PreorderConversionService after IncomingQuantity is written.
/// </summary>
public async Task<List<PreorderItem>> GetPendingItemsForProductsAsync(IList<int> 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();
}
/// <summary>
/// After conversion: check if all items in a preorder are resolved and update the preorder's status.
/// </summary>
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}");
}
/// <summary>
/// Mark a preorder as cancelled (customer or admin action).
/// </summary>
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");
}
}