Try your search with a different keyword or use * as a wildcard.
using Microsoft.AspNetCore.Http;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Tax;
using Nop.Plugin.Tax.FixedOrByCountryStateZip.Domain;
using Nop.Plugin.Tax.FixedOrByCountryStateZip.Infrastructure.Cache;
using Nop.Plugin.Tax.FixedOrByCountryStateZip.Services;
using Nop.Services.Common;
using Nop.Services.Configuration;
using Nop.Services.Localization;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Plugins;
using Nop.Services.Tax;
namespace Nop.Plugin.Tax.FixedOrByCountryStateZip;
///
/// Fixed or by country & state & zip rate tax provider
///
public class FixedOrByCountryStateZipTaxProvider : BasePlugin, ITaxProvider
{
#region Fields
protected readonly FixedOrByCountryStateZipTaxSettings _countryStateZipSettings;
protected readonly ICountryStateZipService _taxRateService;
protected readonly IGenericAttributeService _genericAttributeService;
protected readonly IHttpContextAccessor _httpContextAccessor;
protected readonly ILocalizationService _localizationService;
protected readonly IOrderTotalCalculationService _orderTotalCalculationService;
protected readonly IPaymentService _paymentService;
protected readonly ISettingService _settingService;
protected readonly IStaticCacheManager _staticCacheManager;
protected readonly ITaxCategoryService _taxCategoryService;
protected readonly ITaxService _taxService;
protected readonly IWebHelper _webHelper;
protected readonly TaxSettings _taxSettings;
#endregion
#region Ctor
public FixedOrByCountryStateZipTaxProvider(FixedOrByCountryStateZipTaxSettings countryStateZipSettings,
ICountryStateZipService taxRateService,
IGenericAttributeService genericAttributeService,
IHttpContextAccessor httpContextAccessor,
ILocalizationService localizationService,
IOrderTotalCalculationService orderTotalCalculationService,
IPaymentService paymentService,
ISettingService settingService,
IStaticCacheManager staticCacheManager,
ITaxCategoryService taxCategoryService,
ITaxService taxService,
IWebHelper webHelper,
TaxSettings taxSettings)
{
_countryStateZipSettings = countryStateZipSettings;
_taxRateService = taxRateService;
_genericAttributeService = genericAttributeService;
_httpContextAccessor = httpContextAccessor;
_localizationService = localizationService;
_orderTotalCalculationService = orderTotalCalculationService;
_paymentService = paymentService;
_settingService = settingService;
_staticCacheManager = staticCacheManager;
_taxCategoryService = taxCategoryService;
_taxService = taxService;
_webHelper = webHelper;
_taxSettings = taxSettings;
}
#endregion
#region Methods
///
/// Gets tax rate
///
/// Tax rate request
///
/// A task that represents the asynchronous operation
/// The task result contains the ax
///
public async Task GetTaxRateAsync(TaxRateRequest taxRateRequest)
{
var result = new TaxRateResult();
//the tax rate calculation by fixed rate
if (!_countryStateZipSettings.CountryStateZipEnabled)
{
result.TaxRate = await _settingService.GetSettingByKeyAsync(string.Format(FixedOrByCountryStateZipDefaults.FIXED_RATE_SETTINGS_KEY, taxRateRequest.TaxCategoryId));
return result;
}
//the tax rate calculation by country & state & zip
if (taxRateRequest.Address == null)
{
result.Errors.Add("Address is not set");
return result;
}
//first, load all tax rate records (cached) - loaded only once
var cacheKey = _staticCacheManager.PrepareKeyForDefaultCache(ModelCacheEventConsumer.ALL_TAX_RATES_MODEL_KEY);
var allTaxRates = await _staticCacheManager.GetAsync(cacheKey, async () => (await _taxRateService.GetAllTaxRatesAsync()).Select(taxRate => new TaxRate
{
Id = taxRate.Id,
StoreId = taxRate.StoreId,
TaxCategoryId = taxRate.TaxCategoryId,
CountryId = taxRate.CountryId,
StateProvinceId = taxRate.StateProvinceId,
Zip = taxRate.Zip,
Percentage = taxRate.Percentage
}).ToList());
var storeId = taxRateRequest.CurrentStoreId;
var taxCategoryId = taxRateRequest.TaxCategoryId;
var countryId = taxRateRequest.Address.CountryId;
var stateProvinceId = taxRateRequest.Address.StateProvinceId;
var zip = taxRateRequest.Address.ZipPostalCode?.Trim() ?? string.Empty;
var existingRates = allTaxRates.Where(taxRate => taxRate.CountryId == countryId && taxRate.TaxCategoryId == taxCategoryId);
//filter by store
var matchedByStore = existingRates.Where(taxRate => storeId == taxRate.StoreId || taxRate.StoreId == 0);
//filter by state/province
var matchedByStateProvince = matchedByStore.Where(taxRate => stateProvinceId == taxRate.StateProvinceId || taxRate.StateProvinceId == 0);
//filter by zip
var matchedByZip = matchedByStateProvince.Where(taxRate => string.IsNullOrWhiteSpace(taxRate.Zip) || taxRate.Zip.Equals(zip, StringComparison.InvariantCultureIgnoreCase));
//sort from particular to general, more particular cases will be the first
var foundRecords = matchedByZip.OrderBy(r => r.StoreId == 0).ThenBy(r => r.StateProvinceId == 0).ThenBy(r => string.IsNullOrEmpty(r.Zip));
var foundRecord = foundRecords.FirstOrDefault();
if (foundRecord != null)
result.TaxRate = foundRecord.Percentage;
return result;
}
///
/// Gets tax total
///
/// Tax total request
///
/// A task that represents the asynchronous operation
/// The task result contains the ax total
///
public async Task GetTaxTotalAsync(TaxTotalRequest taxTotalRequest)
{
if (_httpContextAccessor.HttpContext.Items.TryGetValue("nop.TaxTotal", out var result)
&& result is (TaxTotalResult taxTotalResult, decimal paymentTax))
{
//short-circuit to avoid circular reference when calculating payment method additional fee during the checkout process
if (!taxTotalRequest.UsePaymentMethodAdditionalFee)
return new TaxTotalResult { TaxTotal = taxTotalResult.TaxTotal - paymentTax };
return taxTotalResult;
}
var taxRates = new SortedDictionary();
var taxTotal = decimal.Zero;
//order sub total (items + checkout attributes)
var (_, _, _, _, orderSubTotalTaxRates) = await _orderTotalCalculationService
.GetShoppingCartSubTotalAsync(taxTotalRequest.ShoppingCart, false);
var subTotalTaxTotal = decimal.Zero;
foreach (var kvp in orderSubTotalTaxRates)
{
var taxRate = kvp.Key;
var taxValue = kvp.Value;
subTotalTaxTotal += taxValue;
if (taxRate > decimal.Zero && taxValue > decimal.Zero)
{
if (!taxRates.TryGetValue(taxRate, out var value))
taxRates.Add(taxRate, taxValue);
else
taxRates[taxRate] = value + taxValue;
}
}
taxTotal += subTotalTaxTotal;
//shipping
var shippingTax = decimal.Zero;
if (_taxSettings.ShippingIsTaxable)
{
var (shippingExclTax, _, _) = await _orderTotalCalculationService
.GetShoppingCartShippingTotalAsync(taxTotalRequest.ShoppingCart, false);
var (shippingInclTax, taxRate, _) = await _orderTotalCalculationService
.GetShoppingCartShippingTotalAsync(taxTotalRequest.ShoppingCart, true);
if (shippingExclTax.HasValue && shippingInclTax.HasValue)
{
shippingTax = shippingInclTax.Value - shippingExclTax.Value;
if (shippingTax < decimal.Zero)
shippingTax = decimal.Zero;
if (taxRate > decimal.Zero && shippingTax > decimal.Zero)
{
if (!taxRates.TryGetValue(taxRate, out var value))
taxRates.Add(taxRate, shippingTax);
else
taxRates[taxRate] = value + shippingTax;
}
}
}
taxTotal += shippingTax;
//short-circuit to avoid circular reference when calculating payment method additional fee during the checkout process
if (!taxTotalRequest.UsePaymentMethodAdditionalFee)
return new TaxTotalResult { TaxTotal = taxTotal };
//payment method additional fee
var paymentMethodAdditionalFeeTax = decimal.Zero;
if (_taxSettings.PaymentMethodAdditionalFeeIsTaxable)
{
var paymentMethodSystemName = taxTotalRequest.Customer != null
? await _genericAttributeService
.GetAttributeAsync(taxTotalRequest.Customer, NopCustomerDefaults.SelectedPaymentMethodAttribute, taxTotalRequest.StoreId)
: string.Empty;
var paymentMethodAdditionalFee = await _paymentService
.GetAdditionalHandlingFeeAsync(taxTotalRequest.ShoppingCart, paymentMethodSystemName);
var (paymentMethodAdditionalFeeExclTax, _) = await _taxService
.GetPaymentMethodAdditionalFeeAsync(paymentMethodAdditionalFee, false, taxTotalRequest.Customer);
var (paymentMethodAdditionalFeeInclTax, taxRate) = await _taxService
.GetPaymentMethodAdditionalFeeAsync(paymentMethodAdditionalFee, true, taxTotalRequest.Customer);
paymentMethodAdditionalFeeTax = paymentMethodAdditionalFeeInclTax - paymentMethodAdditionalFeeExclTax;
if (paymentMethodAdditionalFeeTax < decimal.Zero)
paymentMethodAdditionalFeeTax = decimal.Zero;
if (taxRate > decimal.Zero && paymentMethodAdditionalFeeTax > decimal.Zero)
{
if (!taxRates.TryGetValue(taxRate, out var value))
taxRates.Add(taxRate, paymentMethodAdditionalFeeTax);
else
taxRates[taxRate] = value + paymentMethodAdditionalFeeTax;
}
}
taxTotal += paymentMethodAdditionalFeeTax;
//add at least one tax rate (0%)
if (!taxRates.Any())
taxRates.Add(decimal.Zero, decimal.Zero);
if (taxTotal < decimal.Zero)
taxTotal = decimal.Zero;
taxTotalResult = new TaxTotalResult { TaxTotal = taxTotal, TaxRates = taxRates, };
//store values within the scope of the request to avoid duplicate calculations
_httpContextAccessor.HttpContext.Items.TryAdd("nop.TaxTotal", (taxTotalResult, paymentMethodAdditionalFeeTax));
return taxTotalResult;
}
///
/// Gets a configuration page URL
///
public override string GetConfigurationPageUrl()
{
return $"{_webHelper.GetStoreLocation()}Admin/FixedOrByCountryStateZip/Configure";
}
///
/// Install plugin
///
/// A task that represents the asynchronous operation
public override async Task InstallAsync()
{
//settings
await _settingService.SaveSettingAsync(new FixedOrByCountryStateZipTaxSettings());
//locales
await _localizationService.AddOrUpdateLocaleResourceAsync(new Dictionary
{
["Plugins.Tax.FixedOrByCountryStateZip.Fixed"] = "Fixed rate",
["Plugins.Tax.FixedOrByCountryStateZip.Tax.Categories.Manage"] = "Manage tax categories",
["Plugins.Tax.FixedOrByCountryStateZip.TaxCategoriesCanNotLoaded"] = "No tax categories can be loaded. You may manage tax categories by this link",
["Plugins.Tax.FixedOrByCountryStateZip.TaxByCountryStateZip"] = "By Country",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.TaxCategoryName"] = "Tax category",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Rate"] = "Rate",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Store"] = "Store",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Store.Hint"] = "If an asterisk is selected, then this shipping rate will apply to all stores.",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Country"] = "Country",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Country.Hint"] = "The country.",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.StateProvince"] = "State / province",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.StateProvince.Hint"] = "If an asterisk is selected, then this tax rate will apply to all customers from the given country, regardless of the state.",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Zip"] = "Zip",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Zip.Hint"] = "Zip / postal code. If zip is empty, then this tax rate will apply to all customers from the given country or state, regardless of the zip code.",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.TaxCategory"] = "Tax category",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.TaxCategory.Hint"] = "The tax category.",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Percentage"] = "Percentage",
["Plugins.Tax.FixedOrByCountryStateZip.Fields.Percentage.Hint"] = "The tax rate.",
["Plugins.Tax.FixedOrByCountryStateZip.AddRecord"] = "Add tax rate",
["Plugins.Tax.FixedOrByCountryStateZip.AddRecordTitle"] = "New tax rate",
["Plugins.Tax.FixedOrByCountryStateZip.SwitchRate"] = @"
You are going to change the way the tax rate is calculated. This will cause the tax rate to be calculated based on the settings specified on the configuration page.
Any current tax rate settings will be saved, but will not be active until you return to this tax calculation method.
",
});
await base.InstallAsync();
}
///
/// Uninstall plugin
///
/// A task that represents the asynchronous operation
public override async Task UninstallAsync()
{
//settings
await _settingService.DeleteSettingAsync();
//fixed rates
var fixedRates = await (await _taxCategoryService.GetAllTaxCategoriesAsync())
.SelectAwait(async taxCategory => await _settingService.GetSettingAsync(string.Format(FixedOrByCountryStateZipDefaults.FIXED_RATE_SETTINGS_KEY, taxCategory.Id)))
.Where(setting => setting != null).ToListAsync();
await _settingService.DeleteSettingsAsync(fixedRates);
//locales
await _localizationService.DeleteLocaleResourcesAsync("Plugins.Tax.FixedOrByCountryStateZip");
await base.UninstallAsync();
}
#endregion
}