Webiant Logo Webiant Logo
  1. No results found.

    Try your search with a different keyword or use * as a wildcard.

OrderService.cs

using System.Globalization;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Shipping;
using Nop.Data;
using Nop.Services.Catalog;
using Nop.Services.Html;
using Nop.Services.Shipping;

namespace Nop.Services.Orders;

/// 
/// Order service
/// 
public partial class OrderService : IOrderService
{
    #region Fields

    protected readonly IHtmlFormatter _htmlFormatter;
    protected readonly IProductService _productService;
    protected readonly IRepository
_addressRepository; protected readonly IRepository _customerRepository; protected readonly IRepository _orderRepository; protected readonly IRepository _orderItemRepository; protected readonly IRepository _orderNoteRepository; protected readonly IRepository _productRepository; protected readonly IRepository _productWarehouseInventoryRepository; protected readonly IRepository _recurringPaymentRepository; protected readonly IRepository _recurringPaymentHistoryRepository; protected readonly IShipmentService _shipmentService; private static readonly char[] _separator = [';']; #endregion #region Ctor public OrderService(IHtmlFormatter htmlFormatter, IProductService productService, IRepository
addressRepository, IRepository customerRepository, IRepository orderRepository, IRepository orderItemRepository, IRepository orderNoteRepository, IRepository productRepository, IRepository productWarehouseInventoryRepository, IRepository recurringPaymentRepository, IRepository recurringPaymentHistoryRepository, IShipmentService shipmentService) { _htmlFormatter = htmlFormatter; _productService = productService; _addressRepository = addressRepository; _customerRepository = customerRepository; _orderRepository = orderRepository; _orderItemRepository = orderItemRepository; _orderNoteRepository = orderNoteRepository; _productRepository = productRepository; _productWarehouseInventoryRepository = productWarehouseInventoryRepository; _recurringPaymentRepository = recurringPaymentRepository; _recurringPaymentHistoryRepository = recurringPaymentHistoryRepository; _shipmentService = shipmentService; } #endregion #region Utilities /// /// Gets the value indicating whether there are shipment items with a positive quantity in order shipments. /// /// Order /// Predicate to filter shipments or null to check all shipments /// The containing the value indicating whether there are shipment items with a positive quantity in order shipments. protected virtual async Task HasShipmentItemsAsync(Order order, Func predicate = null) { var shipments = await _shipmentService.GetShipmentsByOrderIdAsync(order.Id); if (shipments?.Any(shipment => predicate == null || predicate(shipment)) == true) { var orderItems = await GetOrderItemsAsync(order.Id, isShipEnabled: true); if (orderItems?.Any() == true) { foreach (var shipment in shipments) { if (predicate?.Invoke(shipment) == false) continue; bool hasPositiveQuantity(ShipmentItem shipmentItem) { return orderItems.Any(orderItem => orderItem.Id == shipmentItem.OrderItemId && shipmentItem.Quantity > 0); } var shipmentItems = await _shipmentService.GetShipmentItemsByShipmentIdAsync(shipment.Id); if (shipmentItems?.Any(hasPositiveQuantity) == true) return true; } } } return false; } #endregion #region Methods #region Orders /// /// Gets an order /// /// The order identifier /// /// A task that represents the asynchronous operation /// The task result contains the order /// public virtual async Task GetOrderByIdAsync(int orderId) { return await _orderRepository.GetByIdAsync(orderId, cache => default, useShortTermCache: true); } /// /// Gets an order /// /// The custom order number /// /// A task that represents the asynchronous operation /// The task result contains the order /// public virtual async Task GetOrderByCustomOrderNumberAsync(string customOrderNumber) { if (string.IsNullOrEmpty(customOrderNumber)) return null; return await _orderRepository.Table .FirstOrDefaultAsync(o => o.CustomOrderNumber == customOrderNumber); } /// /// Gets an order by order item identifier /// /// The order item identifier /// /// A task that represents the asynchronous operation /// The task result contains the order /// public virtual async Task GetOrderByOrderItemAsync(int orderItemId) { if (orderItemId == 0) return null; return await (from o in _orderRepository.Table join oi in _orderItemRepository.Table on o.Id equals oi.OrderId where oi.Id == orderItemId select o).FirstOrDefaultAsync(); } /// /// Get orders by identifiers /// /// Order identifiers /// /// A task that represents the asynchronous operation /// The task result contains the order /// public virtual async Task> GetOrdersByIdsAsync(int[] orderIds) { return await _orderRepository.GetByIdsAsync(orderIds, includeDeleted: false); } /// /// Get orders by guids /// /// Order guids /// /// A task that represents the asynchronous operation /// The task result contains the orders /// public virtual async Task> GetOrdersByGuidsAsync(Guid[] orderGuids) { if (orderGuids == null) return null; var query = from o in _orderRepository.Table where orderGuids.Contains(o.OrderGuid) select o; var orders = await query.ToListAsync(); return orders; } /// /// Gets an order /// /// The order identifier /// /// A task that represents the asynchronous operation /// The task result contains the order /// public virtual async Task GetOrderByGuidAsync(Guid orderGuid) { if (orderGuid == Guid.Empty) return null; var query = from o in _orderRepository.Table where o.OrderGuid == orderGuid select o; var order = await query.FirstOrDefaultAsync(); return order; } /// /// Deletes an order /// /// The order /// A task that represents the asynchronous operation public virtual async Task DeleteOrderAsync(Order order) { await _orderRepository.DeleteAsync(order); } /// /// Search orders /// /// Store identifier; 0 to load all orders /// Vendor identifier; null to load all orders /// Customer identifier; 0 to load all orders /// Product identifier which was purchased in an order; 0 to load all orders /// Affiliate identifier; 0 to load all orders /// Billing country identifier; 0 to load all orders /// Warehouse identifier, only orders with products from a specified warehouse will be loaded; 0 to load all orders /// Payment method system name; null to load all records /// Created date from (UTC); null to load all records /// Created date to (UTC); null to load all records /// Order status identifiers; null to load all orders /// Payment status identifiers; null to load all orders /// Shipping status identifiers; null to load all orders /// Billing phone. Leave empty to load all records. /// Billing email. Leave empty to load all records. /// Billing last name. Leave empty to load all records. /// Search in order notes. Leave empty to load all records. /// Page index /// Page size /// A value in indicating whether you want to load only total number of records. Set to "true" if you don't want to load data from database /// /// A task that represents the asynchronous operation /// The task result contains the orders /// public virtual async Task> SearchOrdersAsync(int storeId = 0, int vendorId = 0, int customerId = 0, int productId = 0, int affiliateId = 0, int warehouseId = 0, int billingCountryId = 0, string paymentMethodSystemName = null, DateTime? createdFromUtc = null, DateTime? createdToUtc = null, List osIds = null, List psIds = null, List ssIds = null, string billingPhone = null, string billingEmail = null, string billingLastName = "", string orderNotes = null, int pageIndex = 0, int pageSize = int.MaxValue, bool getOnlyTotalCount = false) { var query = _orderRepository.Table; if (storeId > 0) query = query.Where(o => o.StoreId == storeId); if (vendorId > 0) { query = from o in query join oi in _orderItemRepository.Table on o.Id equals oi.OrderId join p in _productRepository.Table on oi.ProductId equals p.Id where p.VendorId == vendorId select o; query = query.Distinct(); } if (customerId > 0) query = query.Where(o => o.CustomerId == customerId); if (productId > 0) { query = from o in query join oi in _orderItemRepository.Table on o.Id equals oi.OrderId where oi.ProductId == productId select o; query = query.Distinct(); } if (warehouseId > 0) { var manageStockInventoryMethodId = (int)ManageInventoryMethod.ManageStock; query = from o in query join oi in _orderItemRepository.Table on o.Id equals oi.OrderId join p in _productRepository.Table on oi.ProductId equals p.Id join pwi in _productWarehouseInventoryRepository.Table on p.Id equals pwi.ProductId into ps from pwi in ps.DefaultIfEmpty() where //"Use multiple warehouses" enabled //we search in each warehouse (p.ManageInventoryMethodId == manageStockInventoryMethodId && p.UseMultipleWarehouses && pwi.WarehouseId == warehouseId) || //"Use multiple warehouses" disabled //we use standard "warehouse" property ((p.ManageInventoryMethodId != manageStockInventoryMethodId || !p.UseMultipleWarehouses) && p.WarehouseId == warehouseId) select o; query = query.Distinct(); } if (!string.IsNullOrEmpty(paymentMethodSystemName)) query = query.Where(o => o.PaymentMethodSystemName == paymentMethodSystemName); if (affiliateId > 0) query = query.Where(o => o.AffiliateId == affiliateId); if (createdFromUtc.HasValue) query = query.Where(o => createdFromUtc.Value <= o.CreatedOnUtc); if (createdToUtc.HasValue) query = query.Where(o => createdToUtc.Value >= o.CreatedOnUtc); if (osIds != null && osIds.Any()) query = query.Where(o => osIds.Contains(o.OrderStatusId)); if (psIds != null && psIds.Any()) query = query.Where(o => psIds.Contains(o.PaymentStatusId)); if (ssIds != null && ssIds.Any()) query = query.Where(o => ssIds.Contains(o.ShippingStatusId)); if (!string.IsNullOrEmpty(orderNotes)) query = query.Where(o => _orderNoteRepository.Table.Any(oNote => oNote.OrderId == o.Id && oNote.Note.Contains(orderNotes))); query = from o in query join oba in _addressRepository.Table on o.BillingAddressId equals oba.Id where (billingCountryId <= 0 || (oba.CountryId == billingCountryId)) && (string.IsNullOrEmpty(billingPhone) || (!string.IsNullOrEmpty(oba.PhoneNumber) && oba.PhoneNumber.Contains(billingPhone))) && (string.IsNullOrEmpty(billingEmail) || (!string.IsNullOrEmpty(oba.Email) && oba.Email.Contains(billingEmail))) && (string.IsNullOrEmpty(billingLastName) || (!string.IsNullOrEmpty(oba.LastName) && oba.LastName.Contains(billingLastName))) select o; query = query.Where(o => !o.Deleted); query = query.OrderByDescending(o => o.CreatedOnUtc); //database layer paging return await query.ToPagedListAsync(pageIndex, pageSize, getOnlyTotalCount); } /// /// Inserts an order /// /// Order /// A task that represents the asynchronous operation public virtual async Task InsertOrderAsync(Order order) { await _orderRepository.InsertAsync(order); } /// /// Updates the order /// /// The order /// A task that represents the asynchronous operation public virtual async Task UpdateOrderAsync(Order order) { await _orderRepository.UpdateAsync(order); } /// /// Parse tax rates /// /// Order /// /// Rates public virtual SortedDictionary ParseTaxRates(Order order, string taxRatesStr) { var taxRatesDictionary = new SortedDictionary(); if (string.IsNullOrEmpty(taxRatesStr)) return taxRatesDictionary; var lines = taxRatesStr.Split(_separator, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { if (string.IsNullOrEmpty(line.Trim())) continue; var taxes = line.Split(':'); if (taxes.Length != 2) continue; try { var taxRate = decimal.Parse(taxes[0].Trim(), CultureInfo.InvariantCulture); var taxValue = decimal.Parse(taxes[1].Trim(), CultureInfo.InvariantCulture); taxRatesDictionary.Add(taxRate, taxValue); } catch { // ignored } } //add at least one tax rate (0%) if (!taxRatesDictionary.Any()) taxRatesDictionary.Add(decimal.Zero, decimal.Zero); return taxRatesDictionary; } /// /// Gets a value indicating whether an order has items to be added to a shipment /// /// Order /// /// A task that represents the asynchronous operation /// The task result contains a value indicating whether an order has items to be added to a shipment /// public virtual async Task HasItemsToAddToShipmentAsync(Order order) { ArgumentNullException.ThrowIfNull(order); foreach (var orderItem in await GetOrderItemsAsync(order.Id, isShipEnabled: true)) //we can ship only shippable products { var totalNumberOfItemsCanBeAddedToShipment = await GetTotalNumberOfItemsCanBeAddedToShipmentAsync(orderItem); if (totalNumberOfItemsCanBeAddedToShipment <= 0) continue; //yes, we have at least one item to create a new shipment return true; } return false; } /// /// Gets a value indicating whether an order has items to ship /// /// Order /// /// A task that represents the asynchronous operation /// The task result contains a value indicating whether an order has items to ship /// public virtual async Task HasItemsToShipAsync(Order order) { ArgumentNullException.ThrowIfNull(order); if (order.PickupInStore) return false; return await HasShipmentItemsAsync(order, shipment => !shipment.ShippedDateUtc.HasValue); } /// /// Gets a value indicating whether there are shipment items to mark as 'ready for pickup' in order shipments. /// /// Order /// /// A task that represents the asynchronous operation /// The task result contains a value indicating whether there are shipment items to mark as 'ready for pickup' in order shipments. /// public virtual async Task HasItemsToReadyForPickupAsync(Order order) { ArgumentNullException.ThrowIfNull(order); if (!order.PickupInStore) return false; return await HasShipmentItemsAsync(order, shipment => !shipment.ReadyForPickupDateUtc.HasValue); } /// /// Gets a value indicating whether an order has items to deliver /// /// Order /// /// A task that represents the asynchronous operation /// The task result contains a value indicating whether an order has items to deliver /// public virtual async Task HasItemsToDeliverAsync(Order order) { ArgumentNullException.ThrowIfNull(order); return await HasShipmentItemsAsync(order, shipment => (shipment.ShippedDateUtc.HasValue || shipment.ReadyForPickupDateUtc.HasValue) && !shipment.DeliveryDateUtc.HasValue); } #endregion #region Orders items /// /// Gets an order item /// /// Order item identifier /// /// A task that represents the asynchronous operation /// The task result contains the order item /// public virtual async Task GetOrderItemByIdAsync(int orderItemId) { return await _orderItemRepository.GetByIdAsync(orderItemId, cache => default, useShortTermCache: true); } /// /// Gets a product of specify order item /// /// Order item identifier /// /// A task that represents the asynchronous operation /// The task result contains the product /// public virtual async Task GetProductByOrderItemIdAsync(int orderItemId) { if (orderItemId == 0) return null; return await (from p in _productRepository.Table join oi in _orderItemRepository.Table on p.Id equals oi.ProductId where oi.Id == orderItemId select p).SingleOrDefaultAsync(); } /// /// Gets a list items of order /// /// Order identifier /// Value indicating whether this product is returnable; pass null to ignore /// Value indicating whether the entity is ship enabled; pass null to ignore /// Vendor identifier; pass 0 to ignore /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task> GetOrderItemsAsync(int orderId, bool? isNotReturnable = null, bool? isShipEnabled = null, int vendorId = 0) { if (orderId == 0) return new List(); return await (from oi in _orderItemRepository.Table join p in _productRepository.Table on oi.ProductId equals p.Id where oi.OrderId == orderId && (!isShipEnabled.HasValue || (p.IsShipEnabled == isShipEnabled.Value)) && (!isNotReturnable.HasValue || (p.NotReturnable == isNotReturnable)) && (vendorId <= 0 || (p.VendorId == vendorId)) select oi).ToListAsync(); } /// /// Gets an item /// /// Order identifier /// /// A task that represents the asynchronous operation /// The task result contains the order item /// public virtual async Task GetOrderItemByGuidAsync(Guid orderItemGuid) { if (orderItemGuid == Guid.Empty) return null; var query = from orderItem in _orderItemRepository.Table where orderItem.OrderItemGuid == orderItemGuid select orderItem; var item = await query.FirstOrDefaultAsync(); return item; } /// /// Gets all downloadable order items /// /// Customer identifier; null to load all records /// /// A task that represents the asynchronous operation /// The task result contains the order items /// public virtual async Task> GetDownloadableOrderItemsAsync(int customerId) { if (customerId == 0) throw new ArgumentOutOfRangeException(nameof(customerId)); var query = from orderItem in _orderItemRepository.Table join o in _orderRepository.Table on orderItem.OrderId equals o.Id join p in _productRepository.Table on orderItem.ProductId equals p.Id where customerId == o.CustomerId && p.IsDownload && !o.Deleted orderby o.CreatedOnUtc descending, orderItem.Id select orderItem; var orderItems = await query.ToListAsync(); return orderItems; } /// /// Delete an order item /// /// The order item /// A task that represents the asynchronous operation public virtual async Task DeleteOrderItemAsync(OrderItem orderItem) { await _orderItemRepository.DeleteAsync(orderItem); } /// /// Gets a total number of items in all shipments /// /// Order item /// /// A task that represents the asynchronous operation /// The task result contains the total number of items in all shipments /// public virtual async Task GetTotalNumberOfItemsInAllShipmentsAsync(OrderItem orderItem) { ArgumentNullException.ThrowIfNull(orderItem); var totalInShipments = 0; var shipments = await _shipmentService.GetShipmentsByOrderIdAsync(orderItem.OrderId); for (var i = 0; i < shipments.Count; i++) { var shipment = shipments[i]; var si = (await _shipmentService.GetShipmentItemsByShipmentIdAsync(shipment.Id)) .FirstOrDefault(x => x.OrderItemId == orderItem.Id); if (si != null) { totalInShipments += si.Quantity; } } return totalInShipments; } /// /// Gets a total number of already items which can be added to new shipments /// /// Order item /// /// A task that represents the asynchronous operation /// The task result contains the total number of already delivered items which can be added to new shipments /// public virtual async Task GetTotalNumberOfItemsCanBeAddedToShipmentAsync(OrderItem orderItem) { ArgumentNullException.ThrowIfNull(orderItem); var totalInShipments = await GetTotalNumberOfItemsInAllShipmentsAsync(orderItem); var qtyOrdered = orderItem.Quantity; var qtyCanBeAddedToShipmentTotal = qtyOrdered - totalInShipments; if (qtyCanBeAddedToShipmentTotal < 0) qtyCanBeAddedToShipmentTotal = 0; return qtyCanBeAddedToShipmentTotal; } /// /// Gets a value indicating whether download is allowed /// /// Order item to check /// /// A task that represents the asynchronous operation /// The task result contains the true if download is allowed; otherwise, false. /// public virtual async Task IsDownloadAllowedAsync(OrderItem orderItem) { if (orderItem is null) return false; var order = await GetOrderByIdAsync(orderItem.OrderId); if (order == null || order.Deleted) return false; //order status if (order.OrderStatus == OrderStatus.Cancelled) return false; var product = await _productService.GetProductByIdAsync(orderItem.ProductId); if (product == null || !product.IsDownload) return false; //payment status switch (product.DownloadActivationType) { case DownloadActivationType.WhenOrderIsPaid: if (order.PaymentStatus == PaymentStatus.Paid && order.PaidDateUtc.HasValue) { //expiration date if (product.DownloadExpirationDays.HasValue) { if (order.PaidDateUtc.Value.AddDays(product.DownloadExpirationDays.Value) > DateTime.UtcNow) { return true; } } else { return true; } } break; case DownloadActivationType.Manually: if (orderItem.IsDownloadActivated) { //expiration date if (product.DownloadExpirationDays.HasValue) { if (order.CreatedOnUtc.AddDays(product.DownloadExpirationDays.Value) > DateTime.UtcNow) { return true; } } else { return true; } } break; default: break; } return false; } /// /// Gets a value indicating whether license download is allowed /// /// Order item to check /// /// A task that represents the asynchronous operation /// The task result contains the true if license download is allowed; otherwise, false. /// public virtual async Task IsLicenseDownloadAllowedAsync(OrderItem orderItem) { if (orderItem == null) return false; return await IsDownloadAllowedAsync(orderItem) && orderItem.LicenseDownloadId.HasValue && orderItem.LicenseDownloadId > 0; } /// /// Inserts a order item /// /// Order item /// A task that represents the asynchronous operation public virtual async Task InsertOrderItemAsync(OrderItem orderItem) { await _orderItemRepository.InsertAsync(orderItem); } /// /// Updates a order item /// /// Order item /// A task that represents the asynchronous operation public virtual async Task UpdateOrderItemAsync(OrderItem orderItem) { await _orderItemRepository.UpdateAsync(orderItem); } #endregion #region Orders notes /// /// Gets an order note /// /// The order note identifier /// /// A task that represents the asynchronous operation /// The task result contains the order note /// public virtual async Task GetOrderNoteByIdAsync(int orderNoteId) { return await _orderNoteRepository.GetByIdAsync(orderNoteId); } /// /// Gets a list notes of order /// /// Order identifier /// Value indicating whether a customer can see a note; pass null to ignore /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task> GetOrderNotesByOrderIdAsync(int orderId, bool? displayToCustomer = null) { if (orderId == 0) return new List(); var query = _orderNoteRepository.Table.Where(on => on.OrderId == orderId); if (displayToCustomer.HasValue) { query = query.Where(on => on.DisplayToCustomer == displayToCustomer); } return await query.ToListAsync(); } /// /// Deletes an order note /// /// The order note /// A task that represents the asynchronous operation public virtual async Task DeleteOrderNoteAsync(OrderNote orderNote) { await _orderNoteRepository.DeleteAsync(orderNote); } /// /// Formats the order note text /// /// Order note /// Formatted text public virtual string FormatOrderNoteText(OrderNote orderNote) { ArgumentNullException.ThrowIfNull(orderNote); var text = orderNote.Note; if (string.IsNullOrEmpty(text)) return string.Empty; text = _htmlFormatter.FormatText(text, false, true, false, false, false, false); return text; } /// /// Inserts an order note /// /// The order note /// A task that represents the asynchronous operation public virtual async Task InsertOrderNoteAsync(OrderNote orderNote) { await _orderNoteRepository.InsertAsync(orderNote); } #endregion #region Recurring payments /// /// Deletes a recurring payment /// /// Recurring payment /// A task that represents the asynchronous operation public virtual async Task DeleteRecurringPaymentAsync(RecurringPayment recurringPayment) { await _recurringPaymentRepository.DeleteAsync(recurringPayment); } /// /// Gets a recurring payment /// /// The recurring payment identifier /// /// A task that represents the asynchronous operation /// The task result contains the recurring payment /// public virtual async Task GetRecurringPaymentByIdAsync(int recurringPaymentId) { return await _recurringPaymentRepository.GetByIdAsync(recurringPaymentId, cache => default); } /// /// Inserts a recurring payment /// /// Recurring payment /// A task that represents the asynchronous operation public virtual async Task InsertRecurringPaymentAsync(RecurringPayment recurringPayment) { await _recurringPaymentRepository.InsertAsync(recurringPayment); } /// /// Updates the recurring payment /// /// Recurring payment /// A task that represents the asynchronous operation public virtual async Task UpdateRecurringPaymentAsync(RecurringPayment recurringPayment) { await _recurringPaymentRepository.UpdateAsync(recurringPayment); } /// /// Search recurring payments /// /// The store identifier; 0 to load all records /// The customer identifier; 0 to load all records /// The initial order identifier; 0 to load all records /// Initial order status identifier; null to load all records /// Page index /// Page size /// A value indicating whether to show hidden records /// /// A task that represents the asynchronous operation /// The task result contains the recurring payments /// public virtual async Task> SearchRecurringPaymentsAsync(int storeId = 0, int customerId = 0, int initialOrderId = 0, OrderStatus? initialOrderStatus = null, int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false) { int? initialOrderStatusId = null; if (initialOrderStatus.HasValue) initialOrderStatusId = (int)initialOrderStatus.Value; var query1 = from rp in _recurringPaymentRepository.Table join o in _orderRepository.Table on rp.InitialOrderId equals o.Id join c in _customerRepository.Table on o.CustomerId equals c.Id where !rp.Deleted && (showHidden || !o.Deleted) && (showHidden || !c.Deleted) && (showHidden || rp.IsActive) && (customerId == 0 || o.CustomerId == customerId) && (storeId == 0 || o.StoreId == storeId) && (initialOrderId == 0 || o.Id == initialOrderId) && (!initialOrderStatusId.HasValue || initialOrderStatusId.Value == 0 || o.OrderStatusId == initialOrderStatusId.Value) select rp.Id; var query2 = from rp in _recurringPaymentRepository.Table where query1.Contains(rp.Id) orderby rp.StartDateUtc, rp.Id select rp; var recurringPayments = await query2.ToPagedListAsync(pageIndex, pageSize); return recurringPayments; } #endregion #region Recurring payments history /// /// Gets a recurring payment history /// /// The recurring payment /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task> GetRecurringPaymentHistoryAsync(RecurringPayment recurringPayment) { ArgumentNullException.ThrowIfNull(recurringPayment); return await _recurringPaymentHistoryRepository.Table .Where(rph => rph.RecurringPaymentId == recurringPayment.Id) .ToListAsync(); } /// /// Inserts a recurring payment history entry /// /// Recurring payment history entry /// A task that represents the asynchronous operation public virtual async Task InsertRecurringPaymentHistoryAsync(RecurringPaymentHistory recurringPaymentHistory) { await _recurringPaymentHistoryRepository.InsertAsync(recurringPaymentHistory); } #endregion #endregion }