431 lines
17 KiB
C#
431 lines
17 KiB
C#
using Nop.Core;
|
|
using Nop.Core.Domain.Catalog;
|
|
using Nop.Core.Domain.Common;
|
|
using Nop.Core.Domain.Orders;
|
|
using Nop.Core.Domain.Shipping;
|
|
using Nop.Data;
|
|
using Nop.Services.Shipping.Pickup;
|
|
using Nop.Services.Shipping.Tracking;
|
|
|
|
namespace Nop.Services.Shipping;
|
|
|
|
/// <summary>
|
|
/// Shipment service
|
|
/// </summary>
|
|
public partial class ShipmentService : IShipmentService
|
|
{
|
|
#region Fields
|
|
|
|
protected readonly IPickupPluginManager _pickupPluginManager;
|
|
protected readonly IRepository<Address> _addressRepository;
|
|
protected readonly IRepository<Order> _orderRepository;
|
|
protected readonly IRepository<OrderItem> _orderItemRepository;
|
|
protected readonly IRepository<Product> _productRepository;
|
|
protected readonly IRepository<Shipment> _shipmentRepository;
|
|
protected readonly IRepository<ShipmentItem> _siRepository;
|
|
protected readonly IShippingPluginManager _shippingPluginManager;
|
|
|
|
#endregion
|
|
|
|
#region Ctor
|
|
|
|
public ShipmentService(IPickupPluginManager pickupPluginManager,
|
|
IRepository<Address> addressRepository,
|
|
IRepository<Order> orderRepository,
|
|
IRepository<OrderItem> orderItemRepository,
|
|
IRepository<Product> productRepository,
|
|
IRepository<Shipment> shipmentRepository,
|
|
IRepository<ShipmentItem> siRepository,
|
|
IShippingPluginManager shippingPluginManager)
|
|
{
|
|
_pickupPluginManager = pickupPluginManager;
|
|
_addressRepository = addressRepository;
|
|
_orderRepository = orderRepository;
|
|
_orderItemRepository = orderItemRepository;
|
|
_productRepository = productRepository;
|
|
_shipmentRepository = shipmentRepository;
|
|
_siRepository = siRepository;
|
|
_shippingPluginManager = shippingPluginManager;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// Deletes a shipment
|
|
/// </summary>
|
|
/// <param name="shipment">Shipment</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
public virtual async Task DeleteShipmentAsync(Shipment shipment)
|
|
{
|
|
await _shipmentRepository.DeleteAsync(shipment);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search shipments
|
|
/// </summary>
|
|
/// <param name="vendorId">Vendor identifier; 0 to load all records</param>
|
|
/// <param name="warehouseId">Warehouse identifier, only shipments with products from a specified warehouse will be loaded; 0 to load all orders</param>
|
|
/// <param name="shippingCountryId">Shipping country identifier; 0 to load all records</param>
|
|
/// <param name="shippingStateId">Shipping state identifier; 0 to load all records</param>
|
|
/// <param name="shippingCounty">Shipping county; null to load all records</param>
|
|
/// <param name="shippingCity">Shipping city; null to load all records</param>
|
|
/// <param name="trackingNumber">Search by tracking number</param>
|
|
/// <param name="loadNotShipped">A value indicating whether we should load only not shipped shipments</param>
|
|
/// <param name="loadNotReadyForPickup">A value indicating whether we should load only not ready for pickup shipments</param>
|
|
/// <param name="loadNotDelivered">A value indicating whether we should load only not delivered shipments</param>
|
|
/// <param name="orderId">Order identifier; 0 to load all records</param>
|
|
/// <param name="createdFromUtc">Created date from (UTC); null to load all records</param>
|
|
/// <param name="createdToUtc">Created date to (UTC); null to load all records</param>
|
|
/// <param name="pageIndex">Page index</param>
|
|
/// <param name="pageSize">Page size</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the shipments
|
|
/// </returns>
|
|
public virtual async Task<IPagedList<Shipment>> GetAllShipmentsAsync(int vendorId = 0, int warehouseId = 0,
|
|
int shippingCountryId = 0,
|
|
int shippingStateId = 0,
|
|
string shippingCounty = null,
|
|
string shippingCity = null,
|
|
string trackingNumber = null,
|
|
bool loadNotShipped = false,
|
|
bool loadNotReadyForPickup = false,
|
|
bool loadNotDelivered = false,
|
|
int orderId = 0,
|
|
DateTime? createdFromUtc = null, DateTime? createdToUtc = null,
|
|
int pageIndex = 0, int pageSize = int.MaxValue)
|
|
{
|
|
var shipments = await _shipmentRepository.GetAllPagedAsync(query =>
|
|
{
|
|
if (orderId > 0)
|
|
query = query.Where(o => o.OrderId == orderId);
|
|
|
|
if (!string.IsNullOrEmpty(trackingNumber))
|
|
query = query.Where(s => s.TrackingNumber.Contains(trackingNumber));
|
|
|
|
if (shippingCountryId > 0)
|
|
query = from s in query
|
|
join o in _orderRepository.Table on s.OrderId equals o.Id
|
|
where _addressRepository.Table.Any(a =>
|
|
a.Id == (o.PickupInStore ? o.PickupAddressId : o.ShippingAddressId) &&
|
|
a.CountryId == shippingCountryId)
|
|
select s;
|
|
|
|
if (shippingStateId > 0)
|
|
query = from s in query
|
|
join o in _orderRepository.Table on s.OrderId equals o.Id
|
|
where _addressRepository.Table.Any(a =>
|
|
a.Id == (o.PickupInStore ? o.PickupAddressId : o.ShippingAddressId) &&
|
|
a.StateProvinceId == shippingStateId)
|
|
select s;
|
|
|
|
if (!string.IsNullOrWhiteSpace(shippingCounty))
|
|
query = from s in query
|
|
join o in _orderRepository.Table on s.OrderId equals o.Id
|
|
where _addressRepository.Table.Any(a =>
|
|
a.Id == (o.PickupInStore ? o.PickupAddressId : o.ShippingAddressId) &&
|
|
a.County.Contains(shippingCounty))
|
|
select s;
|
|
|
|
if (!string.IsNullOrWhiteSpace(shippingCity))
|
|
query = from s in query
|
|
join o in _orderRepository.Table on s.OrderId equals o.Id
|
|
where _addressRepository.Table.Any(a =>
|
|
a.Id == (o.PickupInStore ? o.PickupAddressId : o.ShippingAddressId) &&
|
|
a.City.Contains(shippingCity))
|
|
select s;
|
|
|
|
if (loadNotShipped)
|
|
query = from s in query
|
|
join o in _orderRepository.Table on s.OrderId equals o.Id
|
|
where !s.ShippedDateUtc.HasValue && !o.PickupInStore
|
|
select s;
|
|
|
|
if (loadNotReadyForPickup)
|
|
query = from s in query
|
|
join o in _orderRepository.Table on s.OrderId equals o.Id
|
|
where !s.ReadyForPickupDateUtc.HasValue && o.PickupInStore
|
|
select s;
|
|
|
|
if (loadNotDelivered)
|
|
query = query.Where(s => !s.DeliveryDateUtc.HasValue);
|
|
|
|
if (createdFromUtc.HasValue)
|
|
query = query.Where(s => createdFromUtc.Value <= s.CreatedOnUtc);
|
|
|
|
if (createdToUtc.HasValue)
|
|
query = query.Where(s => createdToUtc.Value >= s.CreatedOnUtc);
|
|
|
|
query = from s in query
|
|
join o in _orderRepository.Table on s.OrderId equals o.Id
|
|
where !o.Deleted
|
|
select s;
|
|
|
|
query = query.Distinct();
|
|
|
|
if (vendorId > 0)
|
|
{
|
|
var queryVendorOrderItems = from orderItem in _orderItemRepository.Table
|
|
join p in _productRepository.Table on orderItem.ProductId equals p.Id
|
|
where p.VendorId == vendorId
|
|
select orderItem.Id;
|
|
|
|
query = from s in query
|
|
join si in _siRepository.Table on s.Id equals si.ShipmentId
|
|
where queryVendorOrderItems.Contains(si.OrderItemId)
|
|
select s;
|
|
|
|
query = query.Distinct();
|
|
}
|
|
|
|
if (warehouseId > 0)
|
|
{
|
|
query = from s in query
|
|
join si in _siRepository.Table on s.Id equals si.ShipmentId
|
|
where si.WarehouseId == warehouseId
|
|
select s;
|
|
|
|
query = query.Distinct();
|
|
}
|
|
|
|
query = query.OrderByDescending(s => s.CreatedOnUtc);
|
|
|
|
return query;
|
|
}, pageIndex, pageSize);
|
|
|
|
return shipments;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get shipment by identifiers
|
|
/// </summary>
|
|
/// <param name="shipmentIds">Shipment identifiers</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the shipments
|
|
/// </returns>
|
|
public virtual async Task<IList<Shipment>> GetShipmentsByIdsAsync(int[] shipmentIds)
|
|
{
|
|
return await _shipmentRepository.GetByIdsAsync(shipmentIds);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a shipment
|
|
/// </summary>
|
|
/// <param name="shipmentId">Shipment identifier</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the shipment
|
|
/// </returns>
|
|
public virtual async Task<Shipment> GetShipmentByIdAsync(int shipmentId)
|
|
{
|
|
return await _shipmentRepository.GetByIdAsync(shipmentId, cache => default, useShortTermCache: true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of order shipments
|
|
/// </summary>
|
|
/// <param name="orderId">Order identifier</param>
|
|
/// <param name="shipped">A value indicating whether to count only shipped or not shipped shipments; pass null to ignore</param>
|
|
/// <param name="readyForPickup">A value indicating whether to load only ready for pickup shipments; pass null to ignore</param>
|
|
/// <param name="vendorId">Vendor identifier; pass 0 to ignore</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the result
|
|
/// </returns>
|
|
public virtual async Task<IList<Shipment>> GetShipmentsByOrderIdAsync(int orderId, bool? shipped = null, bool? readyForPickup = null, int vendorId = 0)
|
|
{
|
|
if (orderId == 0)
|
|
return new List<Shipment>();
|
|
|
|
var shipments = _shipmentRepository.Table;
|
|
|
|
if (shipped.HasValue)
|
|
shipments = shipments.Where(s => s.ShippedDateUtc.HasValue == shipped);
|
|
|
|
if (readyForPickup.HasValue)
|
|
shipments = shipments.Where(s => s.ReadyForPickupDateUtc.HasValue == readyForPickup);
|
|
|
|
return await shipments.Where(shipment => shipment.OrderId == orderId).ToListAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts a shipment
|
|
/// </summary>
|
|
/// <param name="shipment">Shipment</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
public virtual async Task InsertShipmentAsync(Shipment shipment)
|
|
{
|
|
await _shipmentRepository.InsertAsync(shipment);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the shipment
|
|
/// </summary>
|
|
/// <param name="shipment">Shipment</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
public virtual async Task UpdateShipmentAsync(Shipment shipment)
|
|
{
|
|
await _shipmentRepository.UpdateAsync(shipment);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a shipment items of shipment
|
|
/// </summary>
|
|
/// <param name="shipmentId">Shipment identifier</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the shipment items
|
|
/// </returns>
|
|
public virtual async Task<IList<ShipmentItem>> GetShipmentItemsByShipmentIdAsync(int shipmentId)
|
|
{
|
|
if (shipmentId == 0)
|
|
return null;
|
|
|
|
return await _siRepository.Table.Where(si => si.ShipmentId == shipmentId).ToListAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts a shipment item
|
|
/// </summary>
|
|
/// <param name="shipmentItem">Shipment item</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
public virtual async Task InsertShipmentItemAsync(ShipmentItem shipmentItem)
|
|
{
|
|
await _siRepository.InsertAsync(shipmentItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a shipment item
|
|
/// </summary>
|
|
/// <param name="shipmentItem">Shipment Item</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
public virtual async Task DeleteShipmentItemAsync(ShipmentItem shipmentItem)
|
|
{
|
|
await _siRepository.DeleteAsync(shipmentItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates a shipment item
|
|
/// </summary>
|
|
/// <param name="shipmentItem">Shipment item</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
public virtual async Task UpdateShipmentItemAsync(ShipmentItem shipmentItem)
|
|
{
|
|
await _siRepository.UpdateAsync(shipmentItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a shipment item
|
|
/// </summary>
|
|
/// <param name="shipmentItemId">Shipment item identifier</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the shipment item
|
|
/// </returns>
|
|
public virtual async Task<ShipmentItem> GetShipmentItemByIdAsync(int shipmentItemId)
|
|
{
|
|
return await _siRepository.GetByIdAsync(shipmentItemId, cache => default, useShortTermCache: true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get quantity in shipments. For example, get planned quantity to be shipped
|
|
/// </summary>
|
|
/// <param name="product">Product</param>
|
|
/// <param name="warehouseId">Warehouse identifier</param>
|
|
/// <param name="ignoreShipped">Ignore already shipped shipments</param>
|
|
/// <param name="ignoreDelivered">Ignore already delivered shipments</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the quantity
|
|
/// </returns>
|
|
public virtual async Task<int> GetQuantityInShipmentsAsync(Product product, int warehouseId,
|
|
bool ignoreShipped, bool ignoreDelivered)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(product);
|
|
|
|
//only products with "use multiple warehouses" are handled this way
|
|
if (product.ManageInventoryMethod != ManageInventoryMethod.ManageStock)
|
|
return 0;
|
|
if (!product.UseMultipleWarehouses)
|
|
return 0;
|
|
|
|
const int cancelledOrderStatusId = (int)OrderStatus.Cancelled;
|
|
|
|
var query = _siRepository.Table;
|
|
|
|
query = from si in query
|
|
join s in _shipmentRepository.Table on si.ShipmentId equals s.Id
|
|
join o in _orderRepository.Table on s.OrderId equals o.Id
|
|
where !o.Deleted && o.OrderStatusId != cancelledOrderStatusId
|
|
select si;
|
|
|
|
query = query.Distinct();
|
|
|
|
if (warehouseId > 0)
|
|
query = query.Where(si => si.WarehouseId == warehouseId);
|
|
if (ignoreShipped)
|
|
{
|
|
query = from si in query
|
|
join s in _shipmentRepository.Table on si.ShipmentId equals s.Id
|
|
where !s.ShippedDateUtc.HasValue
|
|
select si;
|
|
}
|
|
|
|
if (ignoreDelivered)
|
|
{
|
|
query = from si in query
|
|
join s in _shipmentRepository.Table on si.ShipmentId equals s.Id
|
|
where !s.DeliveryDateUtc.HasValue
|
|
select si;
|
|
}
|
|
|
|
var queryProductOrderItems = from orderItem in _orderItemRepository.Table
|
|
where orderItem.ProductId == product.Id
|
|
select orderItem.Id;
|
|
query = from si in query
|
|
where queryProductOrderItems.Any(orderItemId => orderItemId == si.OrderItemId)
|
|
select si;
|
|
|
|
//some null validation
|
|
var result = Convert.ToInt32(await query.SumAsync(si => (int?)si.Quantity));
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the tracker of the shipment
|
|
/// </summary>
|
|
/// <param name="shipment">Shipment</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the shipment tracker
|
|
/// </returns>
|
|
public virtual async Task<IShipmentTracker> GetShipmentTrackerAsync(Shipment shipment)
|
|
{
|
|
var order = await _orderRepository.GetByIdAsync(shipment.OrderId, cache => default, useShortTermCache: true);
|
|
IShipmentTracker shipmentTracker = null;
|
|
|
|
if (order.PickupInStore)
|
|
{
|
|
var pickupPointProvider = await _pickupPluginManager
|
|
.LoadPluginBySystemNameAsync(order.ShippingRateComputationMethodSystemName);
|
|
|
|
if (pickupPointProvider != null)
|
|
shipmentTracker = await pickupPointProvider.GetShipmentTrackerAsync();
|
|
}
|
|
else
|
|
{
|
|
var shippingRateComputationMethod = await _shippingPluginManager
|
|
.LoadPluginBySystemNameAsync(order.ShippingRateComputationMethodSystemName);
|
|
|
|
if (shippingRateComputationMethod != null)
|
|
shipmentTracker = await shippingRateComputationMethod.GetShipmentTrackerAsync();
|
|
}
|
|
|
|
return shipmentTracker;
|
|
}
|
|
|
|
#endregion
|
|
} |