public async Task <IActionResult> ExemptionCertificates()
        {
            var customer = await _workContext.GetCurrentCustomerAsync();

            if (!await _customerService.IsRegisteredAsync(customer))
            {
                return(Challenge());
            }

            //ensure that Avalara tax provider is active
            if (!await _taxPluginManager.IsPluginActiveAsync(AvalaraTaxDefaults.SystemName, customer))
            {
                return(RedirectToRoute("CustomerInfo"));
            }

            if (!_avalaraTaxSettings.EnableCertificates)
            {
                return(RedirectToRoute("CustomerInfo"));
            }

            //ACL
            if (_avalaraTaxSettings.CustomerRoleIds.Any())
            {
                var customerRoleIds = await _customerService.GetCustomerRoleIdsAsync(customer);

                if (!customerRoleIds.Intersect(_avalaraTaxSettings.CustomerRoleIds).Any())
                {
                    return(RedirectToRoute("CustomerInfo"));
                }
            }

            var token = await _avalaraTaxManager.CreateTokenAsync(customer);

            var link = await _avalaraTaxManager.GetInvitationAsync(customer) ?? AvalaraTaxDefaults.CertExpressUrl;

            var certificates = await _avalaraTaxManager.GetCustomerCertificatesAsync(customer);

            var model = new TaxExemptionModel
            {
                Token        = token,
                Link         = link,
                CustomerId   = customer.Id,
                Certificates = certificates?.Select(certificate => new ExemptionCertificateModel
                {
                    Id             = certificate.id ?? 0,
                    Status         = certificate.status,
                    SignedDate     = certificate.signedDate.ToShortDateString(),
                    ExpirationDate = certificate.expirationDate.ToShortDateString(),
                    ExposureZone   = certificate.exposureZone?.name
                }).ToList() ?? new List <ExemptionCertificateModel>()
            };

            model.AvailableExposureZones = (await _avalaraTaxManager.GetExposureZonesAsync())
                                           .Select(zone => new SelectListItem(zone.name, zone.name))
                                           .ToList();

            return(View("~/Plugins/Tax.Avalara/Views/Customer/ExemptionCertificates.cshtml", model));
        }
        /// <returns>A task that represents the asynchronous operation</returns>
        public async Task <IActionResult> ExportProducts(string selectedIds)
        {
            //ensure that Avalara tax provider is active
            if (!await _taxPluginManager.IsPluginActiveAsync(AvalaraTaxDefaults.SystemName))
            {
                return(RedirectToAction("List", "Product"));
            }

            if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageTaxSettings))
            {
                return(AccessDeniedView());
            }

            //export items
            var exportedItems = await _avalaraTaxManager.ExportProductsAsync(selectedIds);

            if (exportedItems.HasValue)
            {
                if (exportedItems > 0)
                {
                    _notificationService.SuccessNotification(string.Format(await _localizationService.GetResourceAsync("Plugins.Tax.Avalara.Items.Export.Success"), exportedItems));
                }
                else
                {
                    _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Tax.Avalara.Items.Export.AlreadyExported"));
                }
            }
            else
            {
                _notificationService.ErrorNotification(await _localizationService.GetResourceAsync("Plugins.Tax.Avalara.Items.Export.Error"));
            }

            return(RedirectToAction("List", "Product"));
        }
        /// <returns>A task that represents the asynchronous operation</returns>
        public override async Task <IActionResult> Categories()
        {
            //ensure that Avalara tax provider is active
            if (!await _taxPluginManager.IsPluginActiveAsync(AvalaraTaxDefaults.SystemName))
            {
                //if isn't active return base action result
                RouteData.Values["controller"] = "Tax";
                return(await base.Categories());
            }

            if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageTaxSettings))
            {
                return(AccessDeniedView());
            }

            //prepare model
            var model        = new Models.Tax.TaxCategorySearchModel();
            var cacheKey     = _cacheManager.PrepareKeyForDefaultCache(AvalaraTaxDefaults.TaxCodeTypesCacheKey);
            var taxCodeTypes = await _cacheManager.GetAsync(cacheKey, async() => await _avalaraTaxManager.GetTaxCodeTypesAsync());

            if (taxCodeTypes != null)
            {
                model.AvailableTypes = taxCodeTypes.Select(type => new SelectListItem(type.Value, type.Key)).ToList();
            }
            model.SetGridPageSize();

            //use overridden view
            return(View("~/Plugins/Tax.Avalara/Views/Tax/Categories.cshtml", model));
        }
Esempio n. 4
0
        /// <summary>
        /// Handle customer activated event
        /// </summary>
        /// <param name="eventMessage">Event message</param>
        /// <returns>A task that represents the asynchronous operation</returns>
        public async Task HandleEventAsync(CustomerActivatedEvent eventMessage)
        {
            if (eventMessage.Customer is null)
            {
                return;
            }

            //ensure that Avalara tax provider is active for the passed customer, since it's the event from the public area
            if (!await _taxPluginManager.IsPluginActiveAsync(AvalaraTaxDefaults.SystemName, eventMessage.Customer))
            {
                return;
            }

            if (!_avalaraTaxSettings.EnableCertificates)
            {
                return;
            }

            //create customer
            await _avalaraTaxManager.CreateOrUpdateCustomerAsync(eventMessage.Customer);
        }
Esempio n. 5
0
        /// <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));
            }

            if (!_avalaraTaxSettings.EnableCertificates)
            {
                return(Content(string.Empty));
            }

            //ACL
            if (_avalaraTaxSettings.CustomerRoleIds.Any())
            {
                var customerRoleIds = await _customerService.GetCustomerRoleIdsAsync(customer);

                if (!customerRoleIds.Intersect(_avalaraTaxSettings.CustomerRoleIds).Any())
                {
                    return(Content(string.Empty));
                }
            }

            //ensure that it's a proper widget zone
            if (!widgetZone.Equals(PublicWidgetZones.OrderSummaryContentBefore))
            {
                return(Content(string.Empty));
            }

            //ensure that model is passed
            if (additionalData is not ShoppingCartModel cartModel || cartModel.OrderReviewData?.Display != true)
            {
                return(Content(string.Empty));
            }

            var store = await _storeContext.GetCurrentStoreAsync();

            var validCertificate = await _avalaraTaxManager.GetValidCertificatesAsync(customer, store.Id);

            var certificateValue = !string.IsNullOrEmpty(validCertificate?.exemptionNumber)
                ? validCertificate.exemptionNumber
                : validCertificate?.id?.ToString();

            return(View("~/Plugins/Tax.Avalara/Views/Checkout/AppliedCertificate.cshtml", certificateValue));
        }
Esempio n. 6
0
        /// <summary>
        /// Invoke the widget view component
        /// </summary>
        /// <param name="widgetZone">Widget zone</param>
        /// <param name="additionalData">Additional parameters</param>
        /// <returns>View component result</returns>
        public async Task <IViewComponentResult> InvokeAsync(string widgetZone, object additionalData)
        {
            //ensure that Avalara tax provider is active
            if (!await _taxPluginManager.IsPluginActiveAsync(AvalaraTaxDefaults.SystemName))
            {
                return(Content(string.Empty));
            }

            if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageTaxSettings))
            {
                return(Content(string.Empty));
            }

            //ensure that it's a proper widget zone
            if (!widgetZone.Equals(AdminWidgetZones.ProductListButtons))
            {
                return(Content(string.Empty));
            }

            return(View("~/Plugins/Tax.Avalara/Views/Product/ExportItems.cshtml"));
        }
Esempio n. 7
0
        /// <summary>
        /// Handle model received event
        /// </summary>
        /// <param name="eventMessage">Event message</param>
        /// <returns>A task that represents the asynchronous operation</returns>
        public async Task HandleEventAsync(ModelReceivedEvent <BaseNopModel> eventMessage)
        {
            //get entity by received model
            var entity = eventMessage.Model switch
            {
                CustomerModel customerModel => (BaseEntity)await _customerService.GetCustomerByIdAsync(customerModel.Id),
                CustomerRoleModel customerRoleModel => await _customerService.GetCustomerRoleByIdAsync(customerRoleModel.Id),
                ProductModel productModel => await _productService.GetProductByIdAsync(productModel.Id),
                CheckoutAttributeModel checkoutAttributeModel => await _checkoutAttributeService.GetCheckoutAttributeByIdAsync(checkoutAttributeModel.Id),
                _ => null
            };

            if (entity == null)
            {
                return;
            }

            //ensure that Avalara tax provider is active
            if (!await _taxPluginManager.IsPluginActiveAsync(AvalaraTaxDefaults.SystemName))
            {
                return;
            }

            if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageTaxSettings))
            {
                return;
            }

            //whether there is a form value for the entity use code
            if (_httpContextAccessor.HttpContext.Request.Form.TryGetValue(AvalaraTaxDefaults.EntityUseCodeAttribute, out var entityUseCodeValue) &&
                !StringValues.IsNullOrEmpty(entityUseCodeValue))
            {
                //save attribute
                var entityUseCode = !entityUseCodeValue.ToString().Equals(Guid.Empty.ToString()) ? entityUseCodeValue.ToString() : null;
                await _genericAttributeService.SaveAttributeAsync(entity, AvalaraTaxDefaults.EntityUseCodeAttribute, entityUseCode);
            }
        }
        /// <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 model is passed
            if (additionalData is not BaseNopEntityModel entityModel)
            {
                return(Content(string.Empty));
            }

            //ensure that Avalara tax provider is active
            if (!await _taxPluginManager.IsPluginActiveAsync(AvalaraTaxDefaults.SystemName))
            {
                return(Content(string.Empty));
            }

            if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageTaxSettings))
            {
                return(Content(string.Empty));
            }

            //ensure that it's a proper widget zone
            if (!widgetZone.Equals(AdminWidgetZones.CustomerDetailsBlock) &&
                !widgetZone.Equals(AdminWidgetZones.CustomerRoleDetailsTop) &&
                !widgetZone.Equals(AdminWidgetZones.ProductDetailsBlock) &&
                !widgetZone.Equals(AdminWidgetZones.CheckoutAttributeDetailsBlock))
            {
                return(Content(string.Empty));
            }

            //get Avalara pre-defined entity use codes
            var cacheKey             = _staticCacheManager.PrepareKeyForDefaultCache(AvalaraTaxDefaults.EntityUseCodesCacheKey);
            var cachedEntityUseCodes = await _staticCacheManager.GetAsync(cacheKey, async() => await _avalaraTaxManager.GetEntityUseCodesAsync());

            var entityUseCodes = cachedEntityUseCodes?.Select(useCode => new SelectListItem
            {
                Value = useCode.code,
                Text  = $"{useCode.name} ({string.Join(", ", useCode.validCountries)})"
            }).ToList() ?? new List <SelectListItem>();

            //add the special item for 'undefined' with empty guid value
            var defaultValue = Guid.Empty.ToString();

            entityUseCodes.Insert(0, new SelectListItem
            {
                Value = defaultValue,
                Text  = await _localizationService.GetResourceAsync("Plugins.Tax.Avalara.Fields.EntityUseCode.None")
            });

            //prepare model
            var model = new EntityUseCodeModel
            {
                Id             = entityModel.Id,
                EntityUseCodes = entityUseCodes
            };

            //get entity by the model identifier
            BaseEntity entity = null;

            if (widgetZone.Equals(AdminWidgetZones.CustomerDetailsBlock))
            {
                model.PrecedingElementId = nameof(CustomerModel.IsTaxExempt);
                entity = await _customerService.GetCustomerByIdAsync(entityModel.Id);
            }

            if (widgetZone.Equals(AdminWidgetZones.CustomerRoleDetailsTop))
            {
                model.PrecedingElementId = nameof(CustomerRoleModel.TaxExempt);
                entity = await _customerService.GetCustomerRoleByIdAsync(entityModel.Id);
            }

            if (widgetZone.Equals(AdminWidgetZones.ProductDetailsBlock))
            {
                model.PrecedingElementId = nameof(ProductModel.IsTaxExempt);
                entity = await _productService.GetProductByIdAsync(entityModel.Id);
            }

            if (widgetZone.Equals(AdminWidgetZones.CheckoutAttributeDetailsBlock))
            {
                model.PrecedingElementId = nameof(CheckoutAttributeModel.IsTaxExempt);
                entity = await _checkoutAttributeService.GetCheckoutAttributeByIdAsync(entityModel.Id);
            }

            //try to get previously saved entity use code
            model.AvalaraEntityUseCode = entity == null ? defaultValue :
                                         await _genericAttributeService.GetAttributeAsync <string>(entity, AvalaraTaxDefaults.EntityUseCodeAttribute);

            return(View("~/Plugins/Tax.Avalara/Views/EntityUseCode/EntityUseCode.cshtml", model));
        }
        /// <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));
        }