Webiant Logo Webiant Logo
  1. No results found.

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

CustomerService.cs

using System.Xml;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Domain.Blogs;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Forums;
using Nop.Core.Domain.News;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Polls;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Tax;
using Nop.Core.Events;
using Nop.Core.Infrastructure;
using Nop.Data;
using Nop.Services.Common;
using Nop.Services.Localization;

namespace Nop.Services.Customers;

/// 
/// Customer service
/// 
public partial class CustomerService : ICustomerService
{
    #region Fields

    protected readonly CustomerSettings _customerSettings;
    protected readonly IEventPublisher _eventPublisher;
    protected readonly IGenericAttributeService _genericAttributeService;
    protected readonly INopDataProvider _dataProvider;
    protected readonly IRepository
_customerAddressRepository; protected readonly IRepository _blogCommentRepository; protected readonly IRepository _customerRepository; protected readonly IRepository _customerAddressMappingRepository; protected readonly IRepository _customerCustomerRoleMappingRepository; protected readonly IRepository _customerPasswordRepository; protected readonly IRepository _customerRoleRepository; protected readonly IRepository _forumPostRepository; protected readonly IRepository _forumTopicRepository; protected readonly IRepository _gaRepository; protected readonly IRepository _newsCommentRepository; protected readonly IRepository _orderRepository; protected readonly IRepository _productReviewRepository; protected readonly IRepository _productReviewHelpfulnessRepository; protected readonly IRepository _pollVotingRecordRepository; protected readonly IRepository _shoppingCartRepository; protected readonly IShortTermCacheManager _shortTermCacheManager; protected readonly IStaticCacheManager _staticCacheManager; protected readonly IStoreContext _storeContext; protected readonly ShoppingCartSettings _shoppingCartSettings; protected readonly TaxSettings _taxSettings; #endregion #region Ctor public CustomerService(CustomerSettings customerSettings, IEventPublisher eventPublisher, IGenericAttributeService genericAttributeService, INopDataProvider dataProvider, IRepository
customerAddressRepository, IRepository blogCommentRepository, IRepository customerRepository, IRepository customerAddressMappingRepository, IRepository customerCustomerRoleMappingRepository, IRepository customerPasswordRepository, IRepository customerRoleRepository, IRepository forumPostRepository, IRepository forumTopicRepository, IRepository gaRepository, IRepository newsCommentRepository, IRepository orderRepository, IRepository productReviewRepository, IRepository productReviewHelpfulnessRepository, IRepository pollVotingRecordRepository, IRepository shoppingCartRepository, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, IStoreContext storeContext, ShoppingCartSettings shoppingCartSettings, TaxSettings taxSettings) { _customerSettings = customerSettings; _eventPublisher = eventPublisher; _genericAttributeService = genericAttributeService; _dataProvider = dataProvider; _customerAddressRepository = customerAddressRepository; _blogCommentRepository = blogCommentRepository; _customerRepository = customerRepository; _customerAddressMappingRepository = customerAddressMappingRepository; _customerCustomerRoleMappingRepository = customerCustomerRoleMappingRepository; _customerPasswordRepository = customerPasswordRepository; _customerRoleRepository = customerRoleRepository; _forumPostRepository = forumPostRepository; _forumTopicRepository = forumTopicRepository; _gaRepository = gaRepository; _newsCommentRepository = newsCommentRepository; _orderRepository = orderRepository; _productReviewRepository = productReviewRepository; _productReviewHelpfulnessRepository = productReviewHelpfulnessRepository; _pollVotingRecordRepository = pollVotingRecordRepository; _shoppingCartRepository = shoppingCartRepository; _shortTermCacheManager = shortTermCacheManager; _staticCacheManager = staticCacheManager; _storeContext = storeContext; _shoppingCartSettings = shoppingCartSettings; _taxSettings = taxSettings; } #endregion #region Utilities /// /// Gets a dictionary of all customer roles mapped by ID. /// /// /// A task that represents the asynchronous operation and contains a dictionary of all customer roles mapped by ID. /// protected virtual async Task> GetAllCustomerRolesDictionaryAsync() { return await _staticCacheManager.GetAsync( _staticCacheManager.PrepareKeyForDefaultCache(NopEntityCacheDefaults.AllCacheKey), async () => await _customerRoleRepository.Table.ToDictionaryAsync(cr => cr.Id)); } #endregion #region Methods #region Customers /// /// Gets all customers /// /// Created date from (UTC); null to load all records /// Created date to (UTC); null to load all records /// Last activity date from (UTC); null to load all records /// Last activity date to (UTC); null to load all records /// Affiliate identifier /// Vendor identifier /// A list of customer role identifiers to filter by (at least one match); pass null or empty list in order to load all customers; /// Email; null to load all customers /// Username; null to load all customers /// First name; null to load all customers /// Last name; null to load all customers /// Day of birth; 0 to load all customers /// Month of birth; 0 to load all customers /// Company; null to load all customers /// Phone; null to load all customers /// Phone; null to load all customers /// IP address; null to load all customers /// 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 customers /// public virtual async Task> GetAllCustomersAsync(DateTime? createdFromUtc = null, DateTime? createdToUtc = null, DateTime? lastActivityFromUtc = null, DateTime? lastActivityToUtc = null, int affiliateId = 0, int vendorId = 0, int[] customerRoleIds = null, string email = null, string username = null, string firstName = null, string lastName = null, int dayOfBirth = 0, int monthOfBirth = 0, string company = null, string phone = null, string zipPostalCode = null, string ipAddress = null, int pageIndex = 0, int pageSize = int.MaxValue, bool getOnlyTotalCount = false) { var customers = await _customerRepository.GetAllPagedAsync(query => { if (createdFromUtc.HasValue) query = query.Where(c => createdFromUtc.Value <= c.CreatedOnUtc); if (createdToUtc.HasValue) query = query.Where(c => createdToUtc.Value >= c.CreatedOnUtc); if (lastActivityFromUtc.HasValue) query = query.Where(c => lastActivityFromUtc.Value <= c.LastActivityDateUtc); if (lastActivityToUtc.HasValue) query = query.Where(c => lastActivityToUtc.Value >= c.LastActivityDateUtc); if (affiliateId > 0) query = query.Where(c => affiliateId == c.AffiliateId); if (vendorId > 0) query = query.Where(c => vendorId == c.VendorId); query = query.Where(c => !c.Deleted); if (customerRoleIds != null && customerRoleIds.Length > 0) { query = query.Join(_customerCustomerRoleMappingRepository.Table, x => x.Id, y => y.CustomerId, (x, y) => new { Customer = x, Mapping = y }) .Where(z => customerRoleIds.Contains(z.Mapping.CustomerRoleId)) .Select(z => z.Customer) .Distinct(); } if (!string.IsNullOrWhiteSpace(email)) query = query.Where(c => c.Email.Contains(email)); if (!string.IsNullOrWhiteSpace(username)) query = query.Where(c => c.Username.Contains(username)); if (!string.IsNullOrWhiteSpace(firstName)) query = query.Where(c => c.FirstName.Contains(firstName)); if (!string.IsNullOrWhiteSpace(lastName)) query = query.Where(c => c.LastName.Contains(lastName)); if (!string.IsNullOrWhiteSpace(company)) query = query.Where(c => c.Company.Contains(company)); if (!string.IsNullOrWhiteSpace(phone)) query = query.Where(c => c.Phone.Contains(phone)); if (!string.IsNullOrWhiteSpace(zipPostalCode)) query = query.Where(c => c.ZipPostalCode.Contains(zipPostalCode)); if (dayOfBirth > 0 && monthOfBirth > 0) query = query.Where(c => c.DateOfBirth.HasValue && c.DateOfBirth.Value.Day == dayOfBirth && c.DateOfBirth.Value.Month == monthOfBirth); else if (dayOfBirth > 0) query = query.Where(c => c.DateOfBirth.HasValue && c.DateOfBirth.Value.Day == dayOfBirth); else if (monthOfBirth > 0) query = query.Where(c => c.DateOfBirth.HasValue && c.DateOfBirth.Value.Month == monthOfBirth); //search by IpAddress if (!string.IsNullOrWhiteSpace(ipAddress) && CommonHelper.IsValidIpAddress(ipAddress)) { query = query.Where(w => w.LastIpAddress == ipAddress); } query = query.OrderByDescending(c => c.CreatedOnUtc); return query; }, pageIndex, pageSize, getOnlyTotalCount); return customers; } /// /// Gets online customers /// /// Customer last activity date (from) /// A list of customer role identifiers to filter by (at least one match); pass null or empty list in order to load all customers; /// Page index /// Page size /// /// A task that represents the asynchronous operation /// The task result contains the customers /// public virtual async Task> GetOnlineCustomersAsync(DateTime lastActivityFromUtc, int[] customerRoleIds, int pageIndex = 0, int pageSize = int.MaxValue) { var query = _customerRepository.Table; query = query.Where(c => lastActivityFromUtc <= c.LastActivityDateUtc); query = query.Where(c => !c.Deleted); if (customerRoleIds != null && customerRoleIds.Length > 0) query = query.Where(c => _customerCustomerRoleMappingRepository.Table.Any(ccrm => ccrm.CustomerId == c.Id && customerRoleIds.Contains(ccrm.CustomerRoleId))); query = query.OrderByDescending(c => c.LastActivityDateUtc); var customers = await query.ToPagedListAsync(pageIndex, pageSize); return customers; } /// /// Gets customers with shopping carts /// /// Shopping cart type; pass null to load all records /// Store identifier; pass 0 to load all records /// Product identifier; pass null to load all records /// Created date from (UTC); pass null to load all records /// Created date to (UTC); pass null to load all records /// Billing country identifier; pass null to load all records /// Page index /// Page size /// /// A task that represents the asynchronous operation /// The task result contains the customers /// public virtual async Task> GetCustomersWithShoppingCartsAsync(ShoppingCartType? shoppingCartType = null, int storeId = 0, int? productId = null, DateTime? createdFromUtc = null, DateTime? createdToUtc = null, int? countryId = null, int pageIndex = 0, int pageSize = int.MaxValue) { //get all shopping cart items var items = _shoppingCartRepository.Table; //filter by type if (shoppingCartType.HasValue) items = items.Where(item => item.ShoppingCartTypeId == (int)shoppingCartType.Value); //filter shopping cart items by store if (storeId > 0 && !_shoppingCartSettings.CartsSharedBetweenStores) items = items.Where(item => item.StoreId == storeId); //filter shopping cart items by product if (productId > 0) items = items.Where(item => item.ProductId == productId); //filter shopping cart items by date if (createdFromUtc.HasValue) items = items.Where(item => createdFromUtc.Value <= item.CreatedOnUtc); if (createdToUtc.HasValue) items = items.Where(item => createdToUtc.Value >= item.CreatedOnUtc); //get all active customers var customers = _customerRepository.Table.Where(customer => customer.Active && !customer.Deleted); //filter customers by billing country if (countryId > 0) customers = from c in customers join a in _customerAddressRepository.Table on c.BillingAddressId equals a.Id where a.CountryId == countryId select c; var customersWithCarts = from c in customers join item in items on c.Id equals item.CustomerId //we change ordering for the MySQL engine to avoid problems with the ONLY_FULL_GROUP_BY server property that is set by default since the 5.7.5 version orderby _dataProvider.ConfigurationName == "MySql" ? c.CreatedOnUtc : item.CreatedOnUtc descending select c; return await customersWithCarts.Distinct().ToPagedListAsync(pageIndex, pageSize); } /// /// Gets customer for shopping cart /// /// Shopping cart /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task GetShoppingCartCustomerAsync(IList shoppingCart) { var customerId = shoppingCart.FirstOrDefault()?.CustomerId; return customerId.HasValue && customerId != 0 ? await GetCustomerByIdAsync(customerId.Value) : null; } /// /// Delete a customer /// /// Customer /// A task that represents the asynchronous operation public virtual async Task DeleteCustomerAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); if (customer.IsSystemAccount) throw new NopException($"System customer account ({customer.SystemName}) could not be deleted"); customer.Deleted = true; if (_customerSettings.SuffixDeletedCustomers) { if (!string.IsNullOrEmpty(customer.Email)) customer.Email += "-DELETED"; if (!string.IsNullOrEmpty(customer.Username)) customer.Username += "-DELETED"; } await _customerRepository.UpdateAsync(customer, false); await _customerRepository.DeleteAsync(customer); } /// /// Gets a customer /// /// Customer identifier /// /// A task that represents the asynchronous operation /// The task result contains a customer /// public virtual async Task GetCustomerByIdAsync(int customerId) { return await _customerRepository.GetByIdAsync(customerId, cache => default, useShortTermCache: true); } /// /// Get customers by identifiers /// /// Customer identifiers /// /// A task that represents the asynchronous operation /// The task result contains the customers /// public virtual async Task> GetCustomersByIdsAsync(int[] customerIds) { return await _customerRepository.GetByIdsAsync(customerIds, includeDeleted: false); } /// /// Get customers by guids /// /// Customer guids /// /// A task that represents the asynchronous operation /// The task result contains the customers /// public virtual async Task> GetCustomersByGuidsAsync(Guid[] customerGuids) { if (customerGuids == null) return null; var query = from c in _customerRepository.Table where customerGuids.Contains(c.CustomerGuid) select c; var customers = await query.ToListAsync(); return customers; } /// /// Gets a customer by GUID /// /// Customer GUID /// /// A task that represents the asynchronous operation /// The task result contains a customer /// public virtual async Task GetCustomerByGuidAsync(Guid customerGuid) { if (customerGuid == Guid.Empty) return null; var query = from c in _customerRepository.Table where c.CustomerGuid == customerGuid orderby c.Id select c; return await _shortTermCacheManager.GetAsync(async () => await query.FirstOrDefaultAsync(), NopCustomerServicesDefaults.CustomerByGuidCacheKey, customerGuid); } /// /// Get customer by email /// /// Email /// /// A task that represents the asynchronous operation /// The task result contains the customer /// public virtual async Task GetCustomerByEmailAsync(string email) { if (string.IsNullOrWhiteSpace(email)) return null; var query = from c in _customerRepository.Table orderby c.Id where c.Email == email select c; var customer = await query.FirstOrDefaultAsync(); return customer; } /// /// Get customer by system name /// /// System name /// /// A task that represents the asynchronous operation /// The task result contains the customer /// public virtual async Task GetCustomerBySystemNameAsync(string systemName) { if (string.IsNullOrWhiteSpace(systemName)) return null; var query = from c in _customerRepository.Table orderby c.Id where c.SystemName == systemName select c; var customer = await _shortTermCacheManager.GetAsync(async () => await query.FirstOrDefaultAsync(), NopCustomerServicesDefaults.CustomerBySystemNameCacheKey, systemName); return customer; } /// /// Gets built-in system record used for background tasks /// /// /// A task that represents the asynchronous operation /// The task result contains a customer object /// public virtual async Task GetOrCreateBackgroundTaskUserAsync() { var backgroundTaskUser = await GetCustomerBySystemNameAsync(NopCustomerDefaults.BackgroundTaskCustomerName); if (backgroundTaskUser is null) { var store = await _storeContext.GetCurrentStoreAsync(); //If for any reason the system user isn't in the database, then we add it backgroundTaskUser = new Customer { Email = "builtin@background-task-record.com", CustomerGuid = Guid.NewGuid(), AdminComment = "Built-in system record used for background tasks.", Active = true, IsSystemAccount = true, SystemName = NopCustomerDefaults.BackgroundTaskCustomerName, CreatedOnUtc = DateTime.UtcNow, LastActivityDateUtc = DateTime.UtcNow, RegisteredInStoreId = store.Id }; await InsertCustomerAsync(backgroundTaskUser); var guestRole = await GetCustomerRoleBySystemNameAsync(NopCustomerDefaults.GuestsRoleName) ?? throw new NopException("'Guests' role could not be loaded"); await AddCustomerRoleMappingAsync(new CustomerCustomerRoleMapping { CustomerRoleId = guestRole.Id, CustomerId = backgroundTaskUser.Id }); } return backgroundTaskUser; } /// /// Gets built-in system guest record used for requests from search engines /// /// /// A task that represents the asynchronous operation /// The task result contains a customer object /// public virtual async Task GetOrCreateSearchEngineUserAsync() { var searchEngineUser = await GetCustomerBySystemNameAsync(NopCustomerDefaults.SearchEngineCustomerName); if (searchEngineUser is null) { var store = await _storeContext.GetCurrentStoreAsync(); //If for any reason the system user isn't in the database, then we add it searchEngineUser = new Customer { Email = "builtin@search_engine_record.com", CustomerGuid = Guid.NewGuid(), AdminComment = "Built-in system guest record used for requests from search engines.", Active = true, IsSystemAccount = true, SystemName = NopCustomerDefaults.SearchEngineCustomerName, CreatedOnUtc = DateTime.UtcNow, LastActivityDateUtc = DateTime.UtcNow, RegisteredInStoreId = store.Id }; await InsertCustomerAsync(searchEngineUser); var guestRole = await GetCustomerRoleBySystemNameAsync(NopCustomerDefaults.GuestsRoleName) ?? throw new NopException("'Guests' role could not be loaded"); await AddCustomerRoleMappingAsync(new CustomerCustomerRoleMapping { CustomerRoleId = guestRole.Id, CustomerId = searchEngineUser.Id }); } return searchEngineUser; } /// /// Get customer by username /// /// Username /// /// A task that represents the asynchronous operation /// The task result contains the customer /// public virtual async Task GetCustomerByUsernameAsync(string username) { if (string.IsNullOrWhiteSpace(username)) return null; var query = from c in _customerRepository.Table orderby c.Id where c.Username == username select c; var customer = await query.FirstOrDefaultAsync(); return customer; } /// /// Insert a guest customer /// /// /// A task that represents the asynchronous operation /// The task result contains the customer /// public virtual async Task InsertGuestCustomerAsync() { var customer = new Customer { CustomerGuid = Guid.NewGuid(), Active = true, CreatedOnUtc = DateTime.UtcNow, LastActivityDateUtc = DateTime.UtcNow }; //add to 'Guests' role var guestRole = await GetCustomerRoleBySystemNameAsync(NopCustomerDefaults.GuestsRoleName) ?? throw new NopException("'Guests' role could not be loaded"); await _customerRepository.InsertAsync(customer); await AddCustomerRoleMappingAsync(new CustomerCustomerRoleMapping { CustomerId = customer.Id, CustomerRoleId = guestRole.Id }); return customer; } /// /// Insert a customer /// /// Customer /// A task that represents the asynchronous operation public virtual async Task InsertCustomerAsync(Customer customer) { await _customerRepository.InsertAsync(customer); } /// /// Updates the customer /// /// Customer /// A task that represents the asynchronous operation public virtual async Task UpdateCustomerAsync(Customer customer) { await _customerRepository.UpdateAsync(customer); } /// /// Reset data required for checkout /// /// Customer /// Store identifier /// A value indicating whether to clear coupon code /// A value indicating whether to clear selected checkout attributes /// A value indicating whether to clear "Use reward points" flag /// A value indicating whether to clear selected shipping method /// A value indicating whether to clear selected payment method /// A task that represents the asynchronous operation public virtual async Task ResetCheckoutDataAsync(Customer customer, int storeId, bool clearCouponCodes = false, bool clearCheckoutAttributes = false, bool clearRewardPoints = true, bool clearShippingMethod = true, bool clearPaymentMethod = true) { ArgumentNullException.ThrowIfNull(customer); //clear entered coupon codes if (clearCouponCodes) { await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.DiscountCouponCodeAttribute, null); await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.GiftCardCouponCodesAttribute, null); } //clear checkout attributes if (clearCheckoutAttributes) await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.CheckoutAttributes, null, storeId); //clear reward points flag if (clearRewardPoints) await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.UseRewardPointsDuringCheckoutAttribute, false, storeId); //clear selected shipping method if (clearShippingMethod) { await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.SelectedShippingOptionAttribute, null, storeId); await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.OfferedShippingOptionsAttribute, null, storeId); await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.SelectedPickupPointAttribute, null, storeId); } //clear selected payment method if (clearPaymentMethod) await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.SelectedPaymentMethodAttribute, null, storeId); await _eventPublisher.PublishAsync(new ResetCheckoutDataEvent(customer, storeId)); } /// /// Delete guest customer records /// /// Created date from (UTC); null to load all records /// Created date to (UTC); null to load all records /// A value indicating whether to delete customers only without shopping cart /// /// A task that represents the asynchronous operation /// The task result contains the number of deleted customers /// public virtual async Task DeleteGuestCustomersAsync(DateTime? createdFromUtc, DateTime? createdToUtc, bool onlyWithoutShoppingCart) { var guestRole = await GetCustomerRoleBySystemNameAsync(NopCustomerDefaults.GuestsRoleName); var allGuestCustomers = from guest in _customerRepository.Table join ccm in _customerCustomerRoleMappingRepository.Table on guest.Id equals ccm.CustomerId where ccm.CustomerRoleId == guestRole.Id select guest; var guestsToDelete = from guest in _customerRepository.Table join g in allGuestCustomers on guest.Id equals g.Id from sCart in _shoppingCartRepository.Table.Where(sci => sci.CustomerId == guest.Id).DefaultIfEmpty() from order in _orderRepository.Table.Where(o => o.CustomerId == guest.Id).DefaultIfEmpty() from blogComment in _blogCommentRepository.Table.Where(o => o.CustomerId == guest.Id).DefaultIfEmpty() from newsComment in _newsCommentRepository.Table.Where(o => o.CustomerId == guest.Id).DefaultIfEmpty() from productReview in _productReviewRepository.Table.Where(o => o.CustomerId == guest.Id).DefaultIfEmpty() from productReviewHelpfulness in _productReviewHelpfulnessRepository.Table.Where(o => o.CustomerId == guest.Id).DefaultIfEmpty() from pollVotingRecord in _pollVotingRecordRepository.Table.Where(o => o.CustomerId == guest.Id).DefaultIfEmpty() from forumTopic in _forumTopicRepository.Table.Where(o => o.CustomerId == guest.Id).DefaultIfEmpty() from forumPost in _forumPostRepository.Table.Where(o => o.CustomerId == guest.Id).DefaultIfEmpty() where (!onlyWithoutShoppingCart || sCart == null) && order == null && blogComment == null && newsComment == null && productReview == null && productReviewHelpfulness == null && pollVotingRecord == null && forumTopic == null && forumPost == null && !guest.IsSystemAccount && (createdFromUtc == null || guest.CreatedOnUtc > createdFromUtc) && (createdToUtc == null || guest.CreatedOnUtc < createdToUtc) select new { CustomerId = guest.Id }; await using var tmpGuests = await _dataProvider.CreateTempDataStorageAsync("tmp_guestsToDelete", guestsToDelete); await using var tmpAddresses = await _dataProvider.CreateTempDataStorageAsync("tmp_guestsAddressesToDelete", _customerAddressMappingRepository.Table .Where(ca => tmpGuests.Any(c => c.CustomerId == ca.CustomerId)) .Select(ca => new { AddressId = ca.AddressId })); //delete guests var totalRecordsDeleted = await _customerRepository.DeleteAsync(c => tmpGuests.Any(tmp => tmp.CustomerId == c.Id)); //delete attributes await _gaRepository.DeleteAsync(ga => tmpGuests.Any(c => c.CustomerId == ga.EntityId) && ga.KeyGroup == nameof(Customer)); //delete m -> m addresses await _customerAddressRepository.DeleteAsync(a => tmpAddresses.Any(tmp => tmp.AddressId == a.Id)); return totalRecordsDeleted; } /// /// Gets a tax display type for the customer /// /// Customer /// /// A task that represents the asynchronous operation /// The task result contains the tax display type /// public virtual async Task GetCustomerTaxDisplayTypeAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); //default tax display type var taxDisplayType = _taxSettings.TaxDisplayType; //whether customers are allowed to select tax display type and the customer has previously saved one if (_taxSettings.AllowCustomersToSelectTaxDisplayType && customer.TaxDisplayTypeId.HasValue) taxDisplayType = (TaxDisplayType)customer.TaxDisplayTypeId.Value; else { //default tax type by customer roles var defaultRoleTaxDisplayType = await GetCustomerDefaultTaxDisplayTypeAsync(customer); if (defaultRoleTaxDisplayType.HasValue) taxDisplayType = defaultRoleTaxDisplayType.Value; } return taxDisplayType; } /// /// Gets a default tax display type (if configured) /// /// Customer /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task GetCustomerDefaultTaxDisplayTypeAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); var roleWithOverriddenTaxType = (await GetCustomerRolesAsync(customer)).FirstOrDefault(cr => cr.Active && cr.OverrideTaxDisplayType); if (roleWithOverriddenTaxType == null) return null; return (TaxDisplayType)roleWithOverriddenTaxType.DefaultTaxDisplayTypeId; } /// /// Get full name /// /// Customer /// /// A task that represents the asynchronous operation /// The task result contains the customer full name /// public virtual async Task GetCustomerFullNameAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); var firstName = customer.FirstName; var lastName = customer.LastName; var fullName = string.Empty; if (!string.IsNullOrWhiteSpace(firstName) && !string.IsNullOrWhiteSpace(lastName)) { //do not inject ILocalizationService via constructor because it'll cause circular references var format = await EngineContext.Current.Resolve().GetResourceAsync("Customer.FullNameFormat"); fullName = string.Format(format, firstName, lastName); } else { if (!string.IsNullOrWhiteSpace(firstName)) fullName = firstName; if (!string.IsNullOrWhiteSpace(lastName)) fullName = lastName; } return fullName; } /// /// Formats the customer name /// /// Source /// Strip too long customer name /// Maximum customer name length /// /// A task that represents the asynchronous operation /// The task result contains the formatted text /// public virtual async Task FormatUsernameAsync(Customer customer, bool stripTooLong = false, int maxLength = 0) { if (customer == null) return string.Empty; if (await IsGuestAsync(customer)) //do not inject ILocalizationService via constructor because it'll cause circular references return await EngineContext.Current.Resolve().GetResourceAsync("Customer.Guest"); var result = string.Empty; switch (_customerSettings.CustomerNameFormat) { case CustomerNameFormat.ShowEmails: result = customer.Email; break; case CustomerNameFormat.ShowUsernames: result = customer.Username; break; case CustomerNameFormat.ShowFullNames: result = await GetCustomerFullNameAsync(customer); break; case CustomerNameFormat.ShowFirstName: result = customer.FirstName; break; default: break; } if (stripTooLong && maxLength > 0) result = CommonHelper.EnsureMaximumLength(result, maxLength); return result ?? string.Empty; } /// /// Gets coupon codes /// /// Customer /// /// A task that represents the asynchronous operation /// The task result contains the coupon codes /// public virtual async Task ParseAppliedDiscountCouponCodesAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); var existingCouponCodes = await _genericAttributeService.GetAttributeAsync(customer, NopCustomerDefaults.DiscountCouponCodeAttribute); var couponCodes = new List(); if (string.IsNullOrEmpty(existingCouponCodes)) return couponCodes.ToArray(); try { var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(existingCouponCodes); var nodeList1 = xmlDoc.SelectNodes(@"//DiscountCouponCodes/CouponCode"); foreach (XmlNode node1 in nodeList1) { if (node1.Attributes?["Code"] == null) continue; var code = node1.Attributes["Code"].InnerText.Trim(); couponCodes.Add(code); } } catch { // ignored } return couponCodes.ToArray(); } /// /// Adds a coupon code /// /// Customer /// Coupon code /// /// A task that represents the asynchronous operation /// The task result contains the new coupon codes document /// public virtual async Task ApplyDiscountCouponCodeAsync(Customer customer, string couponCode) { ArgumentNullException.ThrowIfNull(customer); var result = string.Empty; try { var existingCouponCodes = await _genericAttributeService.GetAttributeAsync(customer, NopCustomerDefaults.DiscountCouponCodeAttribute); couponCode = couponCode.Trim().ToLowerInvariant(); var xmlDoc = new XmlDocument(); if (string.IsNullOrEmpty(existingCouponCodes)) { var element1 = xmlDoc.CreateElement("DiscountCouponCodes"); xmlDoc.AppendChild(element1); } else xmlDoc.LoadXml(existingCouponCodes); var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//DiscountCouponCodes"); XmlElement gcElement = null; //find existing var nodeList1 = xmlDoc.SelectNodes(@"//DiscountCouponCodes/CouponCode"); foreach (XmlNode node1 in nodeList1) { if (node1.Attributes?["Code"] == null) continue; var couponCodeAttribute = node1.Attributes["Code"].InnerText.Trim(); if (couponCodeAttribute.ToLowerInvariant() != couponCode.ToLowerInvariant()) continue; gcElement = (XmlElement)node1; break; } //create new one if not found if (gcElement == null) { gcElement = xmlDoc.CreateElement("CouponCode"); gcElement.SetAttribute("Code", couponCode); rootElement.AppendChild(gcElement); } result = xmlDoc.OuterXml; } catch { // ignored } //apply new value await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.DiscountCouponCodeAttribute, result); } /// /// Removes a coupon code /// /// Customer /// Coupon code to remove /// /// A task that represents the asynchronous operation /// The task result contains the new coupon codes document /// public virtual async Task RemoveDiscountCouponCodeAsync(Customer customer, string couponCode) { ArgumentNullException.ThrowIfNull(customer); //get applied coupon codes var existingCouponCodes = await ParseAppliedDiscountCouponCodesAsync(customer); //clear them await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.DiscountCouponCodeAttribute, null); //save again except removed one foreach (var existingCouponCode in existingCouponCodes) if (!existingCouponCode.Equals(couponCode, StringComparison.InvariantCultureIgnoreCase)) await ApplyDiscountCouponCodeAsync(customer, existingCouponCode); } /// /// Gets coupon codes /// /// Customer /// /// A task that represents the asynchronous operation /// The task result contains the coupon codes /// public virtual async Task ParseAppliedGiftCardCouponCodesAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); var existingCouponCodes = await _genericAttributeService.GetAttributeAsync(customer, NopCustomerDefaults.GiftCardCouponCodesAttribute); var couponCodes = new List(); if (string.IsNullOrEmpty(existingCouponCodes)) return couponCodes.ToArray(); try { var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(existingCouponCodes); var nodeList1 = xmlDoc.SelectNodes(@"//GiftCardCouponCodes/CouponCode"); foreach (XmlNode node1 in nodeList1) { if (node1.Attributes?["Code"] == null) continue; var code = node1.Attributes["Code"].InnerText.Trim(); couponCodes.Add(code); } } catch { // ignored } return couponCodes.ToArray(); } /// /// Adds a coupon code /// /// Customer /// Coupon code /// /// A task that represents the asynchronous operation /// The task result contains the new coupon codes document /// public virtual async Task ApplyGiftCardCouponCodeAsync(Customer customer, string couponCode) { ArgumentNullException.ThrowIfNull(customer); var result = string.Empty; try { var existingCouponCodes = await _genericAttributeService.GetAttributeAsync(customer, NopCustomerDefaults.GiftCardCouponCodesAttribute); couponCode = couponCode.Trim().ToLowerInvariant(); var xmlDoc = new XmlDocument(); if (string.IsNullOrEmpty(existingCouponCodes)) { var element1 = xmlDoc.CreateElement("GiftCardCouponCodes"); xmlDoc.AppendChild(element1); } else xmlDoc.LoadXml(existingCouponCodes); var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//GiftCardCouponCodes"); XmlElement gcElement = null; //find existing var nodeList1 = xmlDoc.SelectNodes(@"//GiftCardCouponCodes/CouponCode"); foreach (XmlNode node1 in nodeList1) { if (node1.Attributes?["Code"] == null) continue; var couponCodeAttribute = node1.Attributes["Code"].InnerText.Trim(); if (couponCodeAttribute.ToLowerInvariant() != couponCode.ToLowerInvariant()) continue; gcElement = (XmlElement)node1; break; } //create new one if not found if (gcElement == null) { gcElement = xmlDoc.CreateElement("CouponCode"); gcElement.SetAttribute("Code", couponCode); rootElement.AppendChild(gcElement); } result = xmlDoc.OuterXml; } catch { // ignored } //apply new value await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.GiftCardCouponCodesAttribute, result); } /// /// Removes a coupon code /// /// Customer /// Coupon code to remove /// /// A task that represents the asynchronous operation /// The task result contains the new coupon codes document /// public virtual async Task RemoveGiftCardCouponCodeAsync(Customer customer, string couponCode) { ArgumentNullException.ThrowIfNull(customer); //get applied coupon codes var existingCouponCodes = await ParseAppliedGiftCardCouponCodesAsync(customer); //clear them await _genericAttributeService.SaveAttributeAsync(customer, NopCustomerDefaults.GiftCardCouponCodesAttribute, null); //save again except removed one foreach (var existingCouponCode in existingCouponCodes) if (!existingCouponCode.Equals(couponCode, StringComparison.InvariantCultureIgnoreCase)) await ApplyGiftCardCouponCodeAsync(customer, existingCouponCode); } /// /// Returns a list of guids of not existing customers /// /// The guids of the customers to check /// /// A task that represents the asynchronous operation /// The task result contains the list of guids not existing customers /// public virtual async Task GetNotExistingCustomersAsync(Guid[] guids) { ArgumentNullException.ThrowIfNull(guids); var query = _customerRepository.Table; var queryFilter = guids.Distinct().ToArray(); //filtering by guid var filter = await query.Select(c => c.CustomerGuid) .Where(c => queryFilter.Contains(c)) .ToListAsync(); return queryFilter.Except(filter).ToArray(); } #endregion #region Customer roles /// /// Add a customer-customer role mapping /// /// Customer-customer role mapping /// A task that represents the asynchronous operation public async Task AddCustomerRoleMappingAsync(CustomerCustomerRoleMapping roleMapping) { await _customerCustomerRoleMappingRepository.InsertAsync(roleMapping); } /// /// Remove a customer-customer role mapping /// /// Customer /// Customer role /// A task that represents the asynchronous operation public async Task RemoveCustomerRoleMappingAsync(Customer customer, CustomerRole role) { ArgumentNullException.ThrowIfNull(customer); ArgumentNullException.ThrowIfNull(role); var mapping = await _customerCustomerRoleMappingRepository.Table .SingleOrDefaultAsync(ccrm => ccrm.CustomerId == customer.Id && ccrm.CustomerRoleId == role.Id); if (mapping != null) await _customerCustomerRoleMappingRepository.DeleteAsync(mapping); } /// /// Delete a customer role /// /// Customer role /// A task that represents the asynchronous operation public virtual async Task DeleteCustomerRoleAsync(CustomerRole customerRole) { ArgumentNullException.ThrowIfNull(customerRole); if (customerRole.IsSystemRole) throw new NopException("System role could not be deleted"); await _customerRoleRepository.DeleteAsync(customerRole); } /// /// Gets a customer role /// /// Customer role identifier /// /// A task that represents the asynchronous operation /// The task result contains the customer role /// public virtual async Task GetCustomerRoleByIdAsync(int customerRoleId) { var allRolesById = await GetAllCustomerRolesDictionaryAsync(); return allRolesById.TryGetValue(customerRoleId, out var role) ? role : null; } /// /// Gets a customer role /// /// Customer role system name /// /// A task that represents the asynchronous operation /// The task result contains the customer role /// public virtual async Task GetCustomerRoleBySystemNameAsync(string systemName) { if (string.IsNullOrWhiteSpace(systemName)) return null; var key = _staticCacheManager.PrepareKeyForDefaultCache(NopCustomerServicesDefaults.CustomerRolesBySystemNameCacheKey, systemName); var query = from cr in _customerRoleRepository.Table orderby cr.Id where cr.SystemName == systemName select cr; var customerRole = await _staticCacheManager.GetAsync(key, async () => await query.FirstOrDefaultAsync()); return customerRole; } /// /// Get customer role identifiers /// /// Customer /// A value indicating whether to load hidden records /// /// A task that represents the asynchronous operation /// The task result contains the customer role identifiers /// public virtual async Task GetCustomerRoleIdsAsync(Customer customer, bool showHidden = false) { ArgumentNullException.ThrowIfNull(customer); return (await GetCustomerRolesAsync(customer, showHidden: showHidden)) .Select(cr => cr.Id) .ToArray(); } /// /// Gets list of customer roles /// /// Customer /// A value indicating whether to load hidden records /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task> GetCustomerRolesAsync(Customer customer, bool showHidden = false) { ArgumentNullException.ThrowIfNull(customer); var allRolesById = await GetAllCustomerRolesDictionaryAsync(); var mappings = await _shortTermCacheManager.GetAsync( async () => await _customerCustomerRoleMappingRepository.GetAllAsync(query => query.Where(crm => crm.CustomerId == customer.Id)), NopCustomerServicesDefaults.CustomerRolesCacheKey, customer); return mappings.Select(mapping => allRolesById.TryGetValue(mapping.CustomerRoleId, out var role) ? role : null) .Where(cr => cr != null && (showHidden || cr.Active)) .ToList(); } /// /// Gets all customer roles /// /// A value indicating whether to show hidden records /// /// A task that represents the asynchronous operation /// The task result contains the customer roles /// public virtual async Task> GetAllCustomerRolesAsync(bool showHidden = false) { var allRolesById = await GetAllCustomerRolesDictionaryAsync(); return allRolesById.Values .Where(cr => showHidden || cr.Active) .ToList(); } /// /// Inserts a customer role /// /// Customer role /// A task that represents the asynchronous operation public virtual async Task InsertCustomerRoleAsync(CustomerRole customerRole) { await _customerRoleRepository.InsertAsync(customerRole); } /// /// Gets a value indicating whether customer is in a certain customer role /// /// Customer /// Customer role system name /// A value indicating whether we should look only in active customer roles /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task IsInCustomerRoleAsync(Customer customer, string customerRoleSystemName, bool onlyActiveCustomerRoles = true) { ArgumentNullException.ThrowIfNull(customer); ArgumentException.ThrowIfNullOrEmpty(customerRoleSystemName); var customerRoles = await GetCustomerRolesAsync(customer, !onlyActiveCustomerRoles); return customerRoles?.Any(cr => cr.SystemName == customerRoleSystemName) ?? false; } /// /// Gets a value indicating whether customer is administrator /// /// Customer /// A value indicating whether we should look only in active customer roles /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task IsAdminAsync(Customer customer, bool onlyActiveCustomerRoles = true) { return await IsInCustomerRoleAsync(customer, NopCustomerDefaults.AdministratorsRoleName, onlyActiveCustomerRoles); } /// /// Gets a value indicating whether customer is a forum moderator /// /// Customer /// A value indicating whether we should look only in active customer roles /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task IsForumModeratorAsync(Customer customer, bool onlyActiveCustomerRoles = true) { return await IsInCustomerRoleAsync(customer, NopCustomerDefaults.ForumModeratorsRoleName, onlyActiveCustomerRoles); } /// /// Gets a value indicating whether customer is registered /// /// Customer /// A value indicating whether we should look only in active customer roles /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task IsRegisteredAsync(Customer customer, bool onlyActiveCustomerRoles = true) { return await IsInCustomerRoleAsync(customer, NopCustomerDefaults.RegisteredRoleName, onlyActiveCustomerRoles); } /// /// Gets a value indicating whether customer is guest /// /// Customer /// A value indicating whether we should look only in active customer roles /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task IsGuestAsync(Customer customer, bool onlyActiveCustomerRoles = true) { return await IsInCustomerRoleAsync(customer, NopCustomerDefaults.GuestsRoleName, onlyActiveCustomerRoles); } /// /// Gets a value indicating whether customer is vendor /// /// Customer /// A value indicating whether we should look only in active customer roles /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task IsVendorAsync(Customer customer, bool onlyActiveCustomerRoles = true) { return await IsInCustomerRoleAsync(customer, NopCustomerDefaults.VendorsRoleName, onlyActiveCustomerRoles); } /// /// Updates the customer role /// /// Customer role /// A task that represents the asynchronous operation public virtual async Task UpdateCustomerRoleAsync(CustomerRole customerRole) { await _customerRoleRepository.UpdateAsync(customerRole); } #endregion #region Customer passwords /// /// Gets customer passwords /// /// Customer identifier; pass null to load all records /// Password format; pass null to load all records /// Number of returning passwords; pass null to load all records /// /// A task that represents the asynchronous operation /// The task result contains the list of customer passwords /// public virtual async Task> GetCustomerPasswordsAsync(int? customerId = null, PasswordFormat? passwordFormat = null, int? passwordsToReturn = null) { var query = _customerPasswordRepository.Table; //filter by customer if (customerId.HasValue) query = query.Where(password => password.CustomerId == customerId.Value); //filter by password format if (passwordFormat.HasValue) query = query.Where(password => password.PasswordFormatId == (int)passwordFormat.Value); //get the latest passwords if (passwordsToReturn.HasValue) query = query.OrderByDescending(password => password.CreatedOnUtc).Take(passwordsToReturn.Value); return await query.ToListAsync(); } /// /// Get current customer password /// /// Customer identifier /// /// A task that represents the asynchronous operation /// The task result contains the customer password /// public virtual async Task GetCurrentPasswordAsync(int customerId) { if (customerId == 0) return null; //return the latest password return (await GetCustomerPasswordsAsync(customerId, passwordsToReturn: 1)).FirstOrDefault(); } /// /// Insert a customer password /// /// Customer password /// A task that represents the asynchronous operation public virtual async Task InsertCustomerPasswordAsync(CustomerPassword customerPassword) { await _customerPasswordRepository.InsertAsync(customerPassword); } /// /// Update a customer password /// /// Customer password /// A task that represents the asynchronous operation public virtual async Task UpdateCustomerPasswordAsync(CustomerPassword customerPassword) { await _customerPasswordRepository.UpdateAsync(customerPassword); } /// /// Check whether password recovery token is valid /// /// Customer /// Token to validate /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task IsPasswordRecoveryTokenValidAsync(Customer customer, string token) { ArgumentNullException.ThrowIfNull(customer); var cPrt = await _genericAttributeService.GetAttributeAsync(customer, NopCustomerDefaults.PasswordRecoveryTokenAttribute); if (string.IsNullOrEmpty(cPrt)) return false; if (!cPrt.Equals(token, StringComparison.InvariantCultureIgnoreCase)) return false; return true; } /// /// Check whether password recovery link is expired /// /// Customer /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task IsPasswordRecoveryLinkExpiredAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); if (_customerSettings.PasswordRecoveryLinkDaysValid == 0) return false; var generatedDate = await _genericAttributeService.GetAttributeAsync(customer, NopCustomerDefaults.PasswordRecoveryTokenDateGeneratedAttribute); if (!generatedDate.HasValue) return false; var daysPassed = (DateTime.UtcNow - generatedDate.Value).TotalDays; if (daysPassed > _customerSettings.PasswordRecoveryLinkDaysValid) return true; return false; } /// /// Check whether customer password is expired /// /// Customer /// /// A task that represents the asynchronous operation /// The task result contains true if password is expired; otherwise false /// public virtual async Task IsPasswordExpiredAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); //the guests don't have a password if (await IsGuestAsync(customer)) return false; //password lifetime is disabled for user if (!(await GetCustomerRolesAsync(customer)).Any(role => role.Active && role.EnablePasswordLifetime)) return false; //setting disabled for all if (_customerSettings.PasswordLifetime == 0) return false; //get current password usage time var currentLifetime = await _shortTermCacheManager.GetAsync(async () => { var customerPassword = await GetCurrentPasswordAsync(customer.Id); //password is not found, so return max value to force customer to change password if (customerPassword == null) return int.MaxValue; return (DateTime.UtcNow - customerPassword.CreatedOnUtc).Days; }, NopCustomerServicesDefaults.CustomerPasswordLifetimeCacheKey, customer); return currentLifetime >= _customerSettings.PasswordLifetime; } #endregion #region Customer address mapping /// /// Remove a customer-address mapping record /// /// Customer /// Address /// A task that represents the asynchronous operation public virtual async Task RemoveCustomerAddressAsync(Customer customer, Address address) { ArgumentNullException.ThrowIfNull(customer); if (await _customerAddressMappingRepository.Table .FirstOrDefaultAsync(m => m.AddressId == address.Id && m.CustomerId == customer.Id) is CustomerAddressMapping mapping) { if (customer.BillingAddressId == address.Id) customer.BillingAddressId = null; if (customer.ShippingAddressId == address.Id) customer.ShippingAddressId = null; await _customerAddressMappingRepository.DeleteAsync(mapping); } } /// /// Inserts a customer-address mapping record /// /// Customer /// Address /// A task that represents the asynchronous operation public virtual async Task InsertCustomerAddressAsync(Customer customer, Address address) { ArgumentNullException.ThrowIfNull(customer); ArgumentNullException.ThrowIfNull(address); if (await _customerAddressMappingRepository.Table .FirstOrDefaultAsync(m => m.AddressId == address.Id && m.CustomerId == customer.Id) is null) { var mapping = new CustomerAddressMapping { AddressId = address.Id, CustomerId = customer.Id }; await _customerAddressMappingRepository.InsertAsync(mapping); } } /// /// Gets a list of addresses mapped to customer /// /// Customer identifier /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task> GetAddressesByCustomerIdAsync(int customerId) { var query = from address in _customerAddressRepository.Table join cam in _customerAddressMappingRepository.Table on address.Id equals cam.AddressId where cam.CustomerId == customerId select address; return await _shortTermCacheManager.GetAsync(async () => await query.ToListAsync(), NopCustomerServicesDefaults.CustomerAddressesCacheKey, customerId); } /// /// Gets a address mapped to customer /// /// Customer identifier /// Address identifier /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task
GetCustomerAddressAsync(int customerId, int addressId) { if (customerId == 0 || addressId == 0) return null; var query = from address in _customerAddressRepository.Table join cam in _customerAddressMappingRepository.Table on address.Id equals cam.AddressId where cam.CustomerId == customerId && address.Id == addressId select address; return await _shortTermCacheManager.GetAsync(async () => await query.FirstOrDefaultAsync(), NopCustomerServicesDefaults.CustomerAddressCacheKey, customerId, addressId); } /// /// Gets a customer billing address /// /// Customer identifier /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task
GetCustomerBillingAddressAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); return await GetCustomerAddressAsync(customer.Id, customer.BillingAddressId ?? 0); } /// /// Gets a customer shipping address /// /// Customer /// /// A task that represents the asynchronous operation /// The task result contains the result /// public virtual async Task
GetCustomerShippingAddressAsync(Customer customer) { ArgumentNullException.ThrowIfNull(customer); return await GetCustomerAddressAsync(customer.Id, customer.ShippingAddressId ?? 0); } #endregion #endregion }