/// <summary> /// Install the plugin /// </summary> /// <returns>A task that represents the asynchronous operation</returns> public override async Task InstallAsync() { //sample pickup point var country = await _countryService.GetCountryByThreeLetterIsoCodeAsync("USA"); var state = await _stateProvinceService.GetStateProvinceByAbbreviationAsync("NY", country?.Id); var address = new Address { Address1 = "21 West 52nd Street", City = "New York", CountryId = country?.Id, StateProvinceId = state?.Id, ZipPostalCode = "10021", CreatedOnUtc = DateTime.UtcNow }; await _addressService.InsertAddressAsync(address); var pickupPoint = new StorePickupPoint { Name = "New York store", AddressId = address.Id, OpeningHours = "10.00 - 19.00", PickupFee = 1.99m }; await _storePickupPointService.InsertStorePickupPointAsync(pickupPoint); //locales await _localizationService.AddOrUpdateLocaleResourceAsync(new Dictionary <string, string> { ["Plugins.Pickup.PickupInStore.AddNew"] = "Add a new pickup point", ["Plugins.Pickup.PickupInStore.Fields.Description"] = "Description", ["Plugins.Pickup.PickupInStore.Fields.Description.Hint"] = "Specify a description of the pickup point.", ["Plugins.Pickup.PickupInStore.Fields.DisplayOrder"] = "Display order", ["Plugins.Pickup.PickupInStore.Fields.DisplayOrder.Hint"] = "Specify the pickup point display order.", ["Plugins.Pickup.PickupInStore.Fields.Latitude"] = "Latitude", ["Plugins.Pickup.PickupInStore.Fields.Latitude.Hint"] = "Specify a latitude (DD.dddddddd°).", ["Plugins.Pickup.PickupInStore.Fields.Latitude.InvalidPrecision"] = "Precision should be less then 8", ["Plugins.Pickup.PickupInStore.Fields.Latitude.InvalidRange"] = "Latitude should be in range -90 to 90", ["Plugins.Pickup.PickupInStore.Fields.Latitude.IsNullWhenLongitudeHasValue"] = "Latitude and Longitude should be specify together", ["Plugins.Pickup.PickupInStore.Fields.Longitude"] = "Longitude", ["Plugins.Pickup.PickupInStore.Fields.Longitude.Hint"] = "Specify a longitude (DD.dddddddd°).", ["Plugins.Pickup.PickupInStore.Fields.Longitude.InvalidPrecision"] = "Precision should be less then 8", ["Plugins.Pickup.PickupInStore.Fields.Longitude.InvalidRange"] = "Longitude should be in range -180 to 180", ["Plugins.Pickup.PickupInStore.Fields.Longitude.IsNullWhenLatitudeHasValue"] = "Latitude and Longitude should be specify together", ["Plugins.Pickup.PickupInStore.Fields.Name"] = "Name", ["Plugins.Pickup.PickupInStore.Fields.Name.Hint"] = "Specify a name of the pickup point.", ["Plugins.Pickup.PickupInStore.Fields.OpeningHours"] = "Opening hours", ["Plugins.Pickup.PickupInStore.Fields.OpeningHours.Hint"] = "Specify opening hours of the pickup point (Monday - Friday: 09:00 - 19:00 for example).", ["Plugins.Pickup.PickupInStore.Fields.PickupFee"] = "Pickup fee", ["Plugins.Pickup.PickupInStore.Fields.PickupFee.Hint"] = "Specify a fee for the shipping to the pickup point.", ["Plugins.Pickup.PickupInStore.Fields.Store"] = "Store", ["Plugins.Pickup.PickupInStore.Fields.Store.Hint"] = "A store name for which this pickup point will be available.", ["Plugins.Pickup.PickupInStore.Fields.TransitDays"] = "Transit days", ["Plugins.Pickup.PickupInStore.Fields.TransitDays.Hint"] = "The number of days of delivery of the goods to pickup point.", ["Plugins.Pickup.PickupInStore.NoPickupPoints"] = "No pickup points are available" }); await base.InstallAsync(); }
/// <summary> /// Gets or sets a pickup point address for tax calculation /// </summary> /// <param name="pickupPoint">Pickup point</param> /// <returns> /// A task that represents the asynchronous operation /// The task result contains the address /// </returns> protected virtual async Task <Address> LoadPickupPointTaxAddressAsync(PickupPoint pickupPoint) { if (pickupPoint == null) { throw new ArgumentNullException(nameof(pickupPoint)); } var country = await _countryService.GetCountryByTwoLetterIsoCodeAsync(pickupPoint.CountryCode); var state = await _stateProvinceService.GetStateProvinceByAbbreviationAsync(pickupPoint.StateAbbreviation, country?.Id); return(new Address { CountryId = country?.Id ?? 0, StateProvinceId = state?.Id ?? 0, County = pickupPoint.County, City = pickupPoint.City, Address1 = pickupPoint.Address, ZipPostalCode = pickupPoint.ZipPostalCode }); }
/// <summary> /// Invoke the widget view component /// </summary> /// <param name="widgetZone">Widget zone</param> /// <param name="additionalData">Additional parameters</param> /// <returns> /// A task that represents the asynchronous operation /// The task result contains the view component result /// </returns> public async Task <IViewComponentResult> 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 <string>(); 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.FirstOrDefault(); //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))?.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 <string> 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)); }
/// <summary> /// Prepares the checkout pickup points model /// </summary> /// <param name="cart">Cart</param> /// <returns> /// A task that represents the asynchronous operation /// The task result contains the checkout pickup points model /// </returns> protected virtual async Task <CheckoutPickupPointsModel> PrepareCheckoutPickupPointsModelAsync(IList <ShoppingCartItem> cart) { var model = new CheckoutPickupPointsModel { AllowPickupInStore = _shippingSettings.AllowPickupInStore }; if (!model.AllowPickupInStore) { return(model); } model.DisplayPickupPointsOnMap = _shippingSettings.DisplayPickupPointsOnMap; model.GoogleMapsApiKey = _shippingSettings.GoogleMapsApiKey; var pickupPointProviders = await _pickupPluginManager.LoadActivePluginsAsync(await _workContext.GetCurrentCustomerAsync(), (await _storeContext.GetCurrentStoreAsync()).Id); if (pickupPointProviders.Any()) { var languageId = (await _workContext.GetWorkingLanguageAsync()).Id; var pickupPointsResponse = await _shippingService.GetPickupPointsAsync((await _workContext.GetCurrentCustomerAsync()).BillingAddressId ?? 0, await _workContext.GetCurrentCustomerAsync(), storeId : (await _storeContext.GetCurrentStoreAsync()).Id); if (pickupPointsResponse.Success) { model.PickupPoints = await pickupPointsResponse.PickupPoints.SelectAwait(async point => { var country = await _countryService.GetCountryByTwoLetterIsoCodeAsync(point.CountryCode); var state = await _stateProvinceService.GetStateProvinceByAbbreviationAsync(point.StateAbbreviation, country?.Id); var pickupPointModel = new CheckoutPickupPointModel { Id = point.Id, Name = point.Name, Description = point.Description, ProviderSystemName = point.ProviderSystemName, Address = point.Address, City = point.City, County = point.County, StateName = state != null ? await _localizationService.GetLocalizedAsync(state, x => x.Name, languageId) : string.Empty, CountryName = country != null ? await _localizationService.GetLocalizedAsync(country, x => x.Name, languageId) : string.Empty, ZipPostalCode = point.ZipPostalCode, Latitude = point.Latitude, Longitude = point.Longitude, OpeningHours = point.OpeningHours }; var cart = await _shoppingCartService.GetShoppingCartAsync(await _workContext.GetCurrentCustomerAsync(), ShoppingCartType.ShoppingCart, (await _storeContext.GetCurrentStoreAsync()).Id); var amount = await _orderTotalCalculationService.IsFreeShippingAsync(cart) ? 0 : point.PickupFee; if (amount > 0) { (amount, _) = await _taxService.GetShippingPriceAsync(amount, await _workContext.GetCurrentCustomerAsync()); amount = await _currencyService.ConvertFromPrimaryStoreCurrencyAsync(amount, await _workContext.GetWorkingCurrencyAsync()); pickupPointModel.PickupFee = await _priceFormatter.FormatShippingPriceAsync(amount, true); } //adjust rate var(shippingTotal, _) = await _orderTotalCalculationService.AdjustShippingRateAsync(point.PickupFee, cart, true); var(rateBase, _) = await _taxService.GetShippingPriceAsync(shippingTotal, await _workContext.GetCurrentCustomerAsync()); var rate = await _currencyService.ConvertFromPrimaryStoreCurrencyAsync(rateBase, await _workContext.GetWorkingCurrencyAsync()); pickupPointModel.PickupFee = await _priceFormatter.FormatShippingPriceAsync(rate, true); return(pickupPointModel); }).ToListAsync(); } else { foreach (var error in pickupPointsResponse.Errors) { model.Warnings.Add(error); } } } //only available pickup points var shippingProviders = await _shippingPluginManager.LoadActivePluginsAsync(await _workContext.GetCurrentCustomerAsync(), (await _storeContext.GetCurrentStoreAsync()).Id); if (!shippingProviders.Any()) { if (!pickupPointProviders.Any()) { model.Warnings.Add(await _localizationService.GetResourceAsync("Checkout.ShippingIsNotAllowed")); model.Warnings.Add(await _localizationService.GetResourceAsync("Checkout.PickupPoints.NotAvailable")); } model.PickupInStoreOnly = true; model.PickupInStore = true; return(model); } return(model); }