Webiant Logo Webiant Logo
  1. No results found.

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

PriceFormatter.cs

using System.Globalization;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Tax;
using Nop.Services.Directory;
using Nop.Services.Localization;

namespace Nop.Services.Catalog;

/// 
/// Price formatter
/// 
public partial class PriceFormatter : IPriceFormatter
{
    #region Fields

    protected readonly CurrencySettings _currencySettings;
    protected readonly ICurrencyService _currencyService;
    protected readonly ILocalizationService _localizationService;
    protected readonly IMeasureService _measureService;
    protected readonly IPriceCalculationService _priceCalculationService;
    protected readonly IWorkContext _workContext;
    protected readonly TaxSettings _taxSettings;

    #endregion

    #region Ctor

    public PriceFormatter(CurrencySettings currencySettings,
        ICurrencyService currencyService,
        ILocalizationService localizationService,
        IMeasureService measureService,
        IPriceCalculationService priceCalculationService,
        IWorkContext workContext,
        TaxSettings taxSettings)
    {
        _currencySettings = currencySettings;
        _currencyService = currencyService;
        _localizationService = localizationService;
        _measureService = measureService;
        _priceCalculationService = priceCalculationService;
        _workContext = workContext;
        _taxSettings = taxSettings;
    }

    #endregion

    #region Utilities

    /// 
    /// Gets currency string
    /// 
    /// Amount
    /// A value indicating whether to show a currency
    /// Target currency
    /// Currency string without exchange rate
    protected virtual string GetCurrencyString(decimal amount,
        bool showCurrency, Currency targetCurrency)
    {
        ArgumentNullException.ThrowIfNull(targetCurrency);

        string result;
        if (!string.IsNullOrEmpty(targetCurrency.CustomFormatting))
            //custom formatting specified by a store owner
            result = amount.ToString(targetCurrency.CustomFormatting);
        else
        {
            if (!string.IsNullOrEmpty(targetCurrency.DisplayLocale))
                //default behavior
                result = amount.ToString("C", new CultureInfo(targetCurrency.DisplayLocale));
            else
            {
                //not possible because "DisplayLocale" should be always specified
                //but anyway let's just handle this behavior
                result = $"{amount:N} ({targetCurrency.CurrencyCode})";
                return result;
            }
        }

        //display currency code?
        if (showCurrency && _currencySettings.DisplayCurrencyLabel)
            result = $"{result} ({targetCurrency.CurrencyCode})";
        return result;
    }

    /// 
    /// Formats the shipping price
    /// 
    /// Price
    /// A value indicating whether to show a currency
    /// Target currency
    /// Language
    /// A value indicating whether price includes tax
    /// A value indicating whether to show tax suffix
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the price
    /// 
    protected virtual async Task FormatShippingPriceAsync(decimal price, bool showCurrency,
        Currency targetCurrency, int languageId, bool priceIncludesTax, bool showTax)
    {
        return await FormatPriceAsync(price, showCurrency, targetCurrency, languageId, priceIncludesTax, showTax);
    }

    /// 
    /// Formats the payment method additional fee
    /// 
    /// Price
    /// A value indicating whether to show a currency
    /// Target currency
    /// Language
    /// A value indicating whether price includes tax
    /// A value indicating whether to show tax suffix
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the price
    /// 
    protected virtual async Task FormatPaymentMethodAdditionalFeeAsync(decimal price, bool showCurrency,
        Currency targetCurrency, int languageId, bool priceIncludesTax, bool showTax)
    {
        return await FormatPriceAsync(price, showCurrency, targetCurrency, languageId,
            priceIncludesTax, showTax);
    }

    #endregion

    #region Methods

    /// 
    /// Formats the price
    /// 
    /// Price
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the price
    /// 
    public virtual async Task FormatPriceAsync(decimal price)
    {
        return await FormatPriceAsync(price, true, await _workContext.GetWorkingCurrencyAsync());
    }

    /// 
    /// Formats the price
    /// 
    /// Price
    /// A value indicating whether to show a currency
    /// Target currency
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the price
    /// 
    public virtual async Task FormatPriceAsync(decimal price, bool showCurrency, Currency targetCurrency)
    {
        var priceIncludesTax = await _workContext.GetTaxDisplayTypeAsync() == TaxDisplayType.IncludingTax;
        return await FormatPriceAsync(price, showCurrency, targetCurrency, (await _workContext.GetWorkingLanguageAsync()).Id, priceIncludesTax);
    }

    /// 
    /// Formats the price
    /// 
    /// Price
    /// A value indicating whether to show a currency
    /// A value indicating whether to show tax suffix
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the price
    /// 
    public virtual async Task FormatPriceAsync(decimal price, bool showCurrency, bool showTax)
    {
        var priceIncludesTax = await _workContext.GetTaxDisplayTypeAsync() == TaxDisplayType.IncludingTax;
        return await FormatPriceAsync(price, showCurrency, await _workContext.GetWorkingCurrencyAsync(), (await _workContext.GetWorkingLanguageAsync()).Id, priceIncludesTax, showTax);
    }

    /// 
    /// Formats the price
    /// 
    /// Price
    /// A value indicating whether to show a currency
    /// Currency code
    /// A value indicating whether to show tax suffix
    /// Language
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the price
    /// 
    public virtual async Task FormatPriceAsync(decimal price, bool showCurrency,
        string currencyCode, bool showTax, int languageId)
    {
        var currency = await _currencyService.GetCurrencyByCodeAsync(currencyCode) ?? new Currency
        {
            CurrencyCode = currencyCode
        };

        var priceIncludesTax = await _workContext.GetTaxDisplayTypeAsync() == TaxDisplayType.IncludingTax;
        return await FormatPriceAsync(price, showCurrency, currency, languageId, priceIncludesTax, showTax);
    }

    /// 
    /// Formats the order price
    /// 
    /// Price
    /// Currency rate
    /// Customer currency code
    /// A value indicating whether to display price on customer currency
    /// Primary store currency
    /// Language
    /// A value indicating whether price includes tax
    /// A value indicating whether to show tax suffix
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the price
    /// 
    public virtual async Task FormatOrderPriceAsync(decimal price,
        decimal currencyRate, string customerCurrencyCode, bool displayCustomerCurrency,
        Currency primaryStoreCurrency, int languageId, bool? priceIncludesTax = null, bool? showTax = null)
    {
        var needAddPriceOnCustomerCurrency = primaryStoreCurrency.CurrencyCode != customerCurrencyCode && displayCustomerCurrency;
        var includesTax = priceIncludesTax ?? await _workContext.GetTaxDisplayTypeAsync() == TaxDisplayType.IncludingTax;
        var priceText = await FormatPriceAsync(price, true, primaryStoreCurrency,
            languageId, includesTax, showTax ?? _taxSettings.DisplayTaxSuffix);

        if (!needAddPriceOnCustomerCurrency || await _currencyService.GetCurrencyByCodeAsync(customerCurrencyCode) is not Currency currency)
            return priceText;

        var customerPrice = _currencyService.ConvertCurrency(price, currencyRate);
        var customerPriceText = await FormatPriceAsync(customerPrice, true, currency,
            languageId, includesTax, showTax ?? _taxSettings.DisplayTaxSuffix);
        priceText += $"
[{customerPriceText}]"; return priceText; } /// /// Formats the price /// /// Price /// A value indicating whether to show a currency /// Currency code /// Language /// A value indicating whether price includes tax /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatPriceAsync(decimal price, bool showCurrency, string currencyCode, int languageId, bool priceIncludesTax) { var currency = await _currencyService.GetCurrencyByCodeAsync(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; return await FormatPriceAsync(price, showCurrency, currency, languageId, priceIncludesTax); } /// /// Formats the price /// /// Price /// A value indicating whether to show a currency /// Target currency /// Language /// A value indicating whether price includes tax /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatPriceAsync(decimal price, bool showCurrency, Currency targetCurrency, int languageId, bool priceIncludesTax) { return await FormatPriceAsync(price, showCurrency, targetCurrency, languageId, priceIncludesTax, _taxSettings.DisplayTaxSuffix); } /// /// Formats the price /// /// Price /// A value indicating whether to show a currency /// Target currency /// Language /// A value indicating whether price includes tax /// A value indicating whether to show tax suffix /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatPriceAsync(decimal price, bool showCurrency, Currency targetCurrency, int languageId, bool priceIncludesTax, bool showTax) { //we should round it no matter of "ShoppingCartSettings.RoundPricesDuringCalculation" setting price = await _priceCalculationService.RoundPriceAsync(price, targetCurrency); var currencyString = GetCurrencyString(price, showCurrency, targetCurrency); if (!showTax) return currencyString; //show tax suffix string formatStr; if (priceIncludesTax) { formatStr = await _localizationService.GetResourceAsync("Products.InclTaxSuffix", languageId, false); if (string.IsNullOrEmpty(formatStr)) formatStr = "{0} incl tax"; } else { formatStr = await _localizationService.GetResourceAsync("Products.ExclTaxSuffix", languageId, false); if (string.IsNullOrEmpty(formatStr)) formatStr = "{0} excl tax"; } return string.Format(formatStr, currencyString); } /// /// Formats the price of rental product (with rental period) /// /// Product /// Price /// /// A task that represents the asynchronous operation /// The task result contains the rental product price with period /// public virtual async Task FormatRentalProductPeriodAsync(Product product, string price) { ArgumentNullException.ThrowIfNull(product); if (!product.IsRental) return price; if (string.IsNullOrWhiteSpace(price)) return price; var result = product.RentalPricePeriod switch { RentalPricePeriod.Days => string.Format(await _localizationService.GetResourceAsync("Products.Price.Rental.Days"), price, product.RentalPriceLength), RentalPricePeriod.Weeks => string.Format(await _localizationService.GetResourceAsync("Products.Price.Rental.Weeks"), price, product.RentalPriceLength), RentalPricePeriod.Months => string.Format(await _localizationService.GetResourceAsync("Products.Price.Rental.Months"), price, product.RentalPriceLength), RentalPricePeriod.Years => string.Format(await _localizationService.GetResourceAsync("Products.Price.Rental.Years"), price, product.RentalPriceLength), _ => throw new NopException("Not supported rental period"), }; return result; } /// /// Formats the shipping price /// /// Price /// A value indicating whether to show a currency /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatShippingPriceAsync(decimal price, bool showCurrency) { var priceIncludesTax = await _workContext.GetTaxDisplayTypeAsync() == TaxDisplayType.IncludingTax; return await FormatShippingPriceAsync(price, showCurrency, await _workContext.GetWorkingCurrencyAsync(), (await _workContext.GetWorkingLanguageAsync()).Id, priceIncludesTax); } /// /// Formats the shipping price /// /// Price /// A value indicating whether to show a currency /// Target currency /// Language /// A value indicating whether price includes tax /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatShippingPriceAsync(decimal price, bool showCurrency, Currency targetCurrency, int languageId, bool priceIncludesTax) { var showTax = _taxSettings.ShippingIsTaxable && _taxSettings.DisplayTaxSuffix; return await FormatShippingPriceAsync(price, showCurrency, targetCurrency, languageId, priceIncludesTax, showTax); } /// /// Formats the shipping price /// /// Price /// A value indicating whether to show a currency /// Currency code /// Language /// A value indicating whether price includes tax /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatShippingPriceAsync(decimal price, bool showCurrency, string currencyCode, int languageId, bool priceIncludesTax) { var currency = await _currencyService.GetCurrencyByCodeAsync(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; return await FormatShippingPriceAsync(price, showCurrency, currency, languageId, priceIncludesTax); } /// /// Formats the payment method additional fee /// /// Price /// A value indicating whether to show a currency /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatPaymentMethodAdditionalFeeAsync(decimal price, bool showCurrency) { var priceIncludesTax = await _workContext.GetTaxDisplayTypeAsync() == TaxDisplayType.IncludingTax; return await FormatPaymentMethodAdditionalFeeAsync(price, showCurrency, await _workContext.GetWorkingCurrencyAsync(), (await _workContext.GetWorkingLanguageAsync()).Id, priceIncludesTax); } /// /// Formats the payment method additional fee /// /// Price /// A value indicating whether to show a currency /// Target currency /// Language /// A value indicating whether price includes tax /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatPaymentMethodAdditionalFeeAsync(decimal price, bool showCurrency, Currency targetCurrency, int languageId, bool priceIncludesTax) { var showTax = _taxSettings.PaymentMethodAdditionalFeeIsTaxable && _taxSettings.DisplayTaxSuffix; return await FormatPaymentMethodAdditionalFeeAsync(price, showCurrency, targetCurrency, languageId, priceIncludesTax, showTax); } /// /// Formats the payment method additional fee /// /// Price /// A value indicating whether to show a currency /// Currency code /// Language /// A value indicating whether price includes tax /// /// A task that represents the asynchronous operation /// The task result contains the price /// public virtual async Task FormatPaymentMethodAdditionalFeeAsync(decimal price, bool showCurrency, string currencyCode, int languageId, bool priceIncludesTax) { var currency = await _currencyService.GetCurrencyByCodeAsync(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; return await FormatPaymentMethodAdditionalFeeAsync(price, showCurrency, currency, languageId, priceIncludesTax); } /// /// Formats a tax rate /// /// Tax rate /// Formatted tax rate public virtual string FormatTaxRate(decimal taxRate) { return taxRate.ToString("G29", CultureInfo.InvariantCulture); } /// /// Format base price (PAngV) /// /// Product /// Product price (in primary currency). Pass null if you want to use a default produce price /// Total weight of product (with attribute weight adjustment). Pass null if you want to use a default produce weight /// /// A task that represents the asynchronous operation /// The task result contains the base price /// public virtual async Task FormatBasePriceAsync(Product product, decimal? productPrice, decimal? totalWeight = null) { ArgumentNullException.ThrowIfNull(product); if (!product.BasepriceEnabled) return null; var productAmount = totalWeight.HasValue && totalWeight.Value > decimal.Zero ? totalWeight.Value : product.BasepriceAmount; //Amount in product cannot be 0 if (productAmount == 0) return null; var referenceAmount = product.BasepriceBaseAmount; var productUnit = await _measureService.GetMeasureWeightByIdAsync(product.BasepriceUnitId); //measure weight cannot be loaded if (productUnit == null) return null; var referenceUnit = await _measureService.GetMeasureWeightByIdAsync(product.BasepriceBaseUnitId); //measure weight cannot be loaded if (referenceUnit == null) return null; productPrice ??= product.Price; var basePrice = productPrice.Value / //do not round. otherwise, it can cause issues await _measureService.ConvertWeightAsync(productAmount, productUnit, referenceUnit, false) * referenceAmount; var basePriceInCurrentCurrency = await _currencyService.ConvertFromPrimaryStoreCurrencyAsync(basePrice, await _workContext.GetWorkingCurrencyAsync()); var basePriceStr = await FormatPriceAsync(basePriceInCurrentCurrency, true, false); var result = string.Format(await _localizationService.GetResourceAsync("Products.BasePrice"), basePriceStr, referenceAmount.ToString("G29"), referenceUnit.Name); return result; } #endregion }