using System.Net; using Microsoft.AspNetCore.Mvc; using Nop.Core; using Nop.Core.Domain.Common; using Nop.Core.Domain.Directory; using Nop.Core.Domain.Tax; using Nop.Plugin.Tax.Avalara.Models.Checkout; using Nop.Plugin.Tax.Avalara.Services; using Nop.Services.Common; using Nop.Services.Customers; using Nop.Services.Directory; using Nop.Services.Localization; using Nop.Services.Tax; using Nop.Web.Framework.Components; using Nop.Web.Framework.Infrastructure; namespace Nop.Plugin.Tax.Avalara.Components; /// /// Represents a view component to validate entered address and display a confirmation dialog on the checkout page /// public class AddressValidationViewComponent : NopViewComponent { #region Fields protected readonly AvalaraTaxManager _avalaraTaxManager; protected readonly AvalaraTaxSettings _avalaraTaxSettings; protected readonly IAddressService _addressService; protected readonly ICountryService _countryService; protected readonly ICustomerService _customerService; protected readonly ILocalizationService _localizationService; protected readonly IStateProvinceService _stateProvinceService; protected readonly ITaxPluginManager _taxPluginManager; protected readonly IWorkContext _workContext; protected readonly TaxSettings _taxSettings; #endregion #region Ctor public AddressValidationViewComponent(AvalaraTaxManager avalaraTaxManager, AvalaraTaxSettings avalaraTaxSettings, IAddressService addressService, ICountryService countryService, ICustomerService customerService, ILocalizationService localizationService, IStateProvinceService stateProvinceService, ITaxPluginManager taxPluginManager, IWorkContext workContext, TaxSettings taxSettings) { _avalaraTaxManager = avalaraTaxManager; _avalaraTaxSettings = avalaraTaxSettings; _addressService = addressService; _countryService = countryService; _customerService = customerService; _localizationService = localizationService; _stateProvinceService = stateProvinceService; _taxPluginManager = taxPluginManager; _workContext = workContext; _taxSettings = taxSettings; } #endregion #region Methods /// /// Invoke the widget view component /// /// Widget zone /// Additional parameters /// /// A task that represents the asynchronous operation /// The task result contains the view component result /// public async Task InvokeAsync(string widgetZone, object additionalData) { //ensure that Avalara tax provider is active var customer = await _workContext.GetCurrentCustomerAsync(); if (!await _taxPluginManager.IsPluginActiveAsync(AvalaraTaxDefaults.SystemName, customer)) return Content(string.Empty); //ensure that it's a proper widget zone if (!widgetZone.Equals(PublicWidgetZones.CheckoutConfirmTop) && !widgetZone.Equals(PublicWidgetZones.OpCheckoutConfirmTop)) return Content(string.Empty); //ensure thet address validation is enabled if (!_avalaraTaxSettings.ValidateAddress) return Content(string.Empty); //validate entered by customer addresses only var addressId = _taxSettings.TaxBasedOn == TaxBasedOn.BillingAddress ? customer.BillingAddressId : _taxSettings.TaxBasedOn == TaxBasedOn.ShippingAddress ? customer.ShippingAddressId : null; var address = await _addressService.GetAddressByIdAsync(addressId ?? 0); if (address == null) return Content(string.Empty); //validate address var validationResult = await _avalaraTaxManager.ValidateAddressAsync(address); //whether there are errors in validation result var errorDetails = validationResult?.messages? .Where(message => message.severity.Equals("Error", StringComparison.InvariantCultureIgnoreCase)) .Select(message => message.details) ?? new List(); if (errorDetails.Any()) { //display error message to customer return View("~/Plugins/Tax.Avalara/Views/Checkout/AddressValidation.cshtml", new AddressValidationModel { Message = string.Format(await _localizationService.GetResourceAsync("Plugins.Tax.Avalara.AddressValidation.Error"), WebUtility.HtmlEncode(string.Join("; ", errorDetails))), IsError = true }); } //if there are no errors and no validated addresses, nothing to display if (!validationResult?.validatedAddresses?.Any() ?? true) return Content(string.Empty); //get validated address info var validatedAddressInfo = validationResult.validatedAddresses.First(); //create new address as a copy of address to validate and with details of the validated one var validatedAddress = _addressService.CloneAddress(address); validatedAddress.City = validatedAddressInfo.city; validatedAddress.CountryId = (await _countryService.GetCountryByTwoLetterIsoCodeAsync(validatedAddressInfo.country))?.Id; validatedAddress.Address1 = validatedAddressInfo.line1; validatedAddress.Address2 = validatedAddressInfo.line2; validatedAddress.ZipPostalCode = validatedAddressInfo.postalCode; validatedAddress.StateProvinceId = (await _stateProvinceService.GetStateProvinceByAbbreviationAsync(validatedAddressInfo.region, validatedAddress.CountryId))?.Id; //try to find an existing address with the same values var existingAddress = _addressService.FindAddress((await _customerService.GetAddressesByCustomerIdAsync(customer.Id)).ToList(), validatedAddress.FirstName, validatedAddress.LastName, validatedAddress.PhoneNumber, validatedAddress.Email, validatedAddress.FaxNumber, validatedAddress.Company, validatedAddress.Address1, validatedAddress.Address2, validatedAddress.City, validatedAddress.County, validatedAddress.StateProvinceId, validatedAddress.ZipPostalCode, validatedAddress.CountryId, validatedAddress.CustomAttributes); //if the found address is the same as address to validate, nothing to display if (address.Id == existingAddress?.Id) return Content(string.Empty); //otherwise display to customer a confirmation dialog about address updating var model = new AddressValidationModel(); if (existingAddress == null) { await _addressService.InsertAddressAsync(validatedAddress); model.AddressId = validatedAddress.Id; model.IsNewAddress = true; } else model.AddressId = existingAddress.Id; async Task getAddressLineAsync(Address address) => WebUtility.HtmlEncode($"{(!string.IsNullOrEmpty(address.Address1) ? $"{address.Address1}, " : string.Empty)}" + $"{(!string.IsNullOrEmpty(address.Address2) ? $"{address.Address2}, " : string.Empty)}" + $"{(!string.IsNullOrEmpty(address.City) ? $"{address.City}, " : string.Empty)}" + $"{(await _stateProvinceService.GetStateProvinceByAddressAsync(address) is StateProvince stateProvince ? $"{stateProvince.Name}, " : string.Empty)}" + $"{(await _countryService.GetCountryByAddressAsync(address) is Country country ? $"{country.Name}, " : string.Empty)}" + $"{(!string.IsNullOrEmpty(address.ZipPostalCode) ? $"{address.ZipPostalCode}, " : string.Empty)}" .TrimEnd(' ').TrimEnd(',')); model.Message = string.Format(await _localizationService.GetResourceAsync("Plugins.Tax.Avalara.AddressValidation.Confirm"), await getAddressLineAsync(address), await getAddressLineAsync(existingAddress ?? validatedAddress)); return View("~/Plugins/Tax.Avalara/Views/Checkout/AddressValidation.cshtml", model); } #endregion }