142 lines
5.5 KiB
C#
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");
|
|
}
|
|
}
|