Developer forum

Forum » Development » Getting PriceBeforeDiscount for Product Variant

Getting PriceBeforeDiscount for Product Variant

Ruwan Dissanayake
Reply

Hi,

I want to get PriceBeforeDiscount for the variants of a product and find the minimum before-discount price to display on the product list page (to illustrate the price comparison).

I wrote the below function to iterate through the product variants and retrieve the PriceBeforeDiscount object for variant. However, all the price values are coming as 0 (please refer to the screenshot).

Any help would be greatly appreciated.

 

@functions {
public string GetMinVariantBeforePrice(
Dynamicweb.Ecommerce.ProductCatalog.ProductViewModel product,
Dynamicweb.Ecommerce.ProductCatalog.ProductViewModelSettings settings,
string unitId = "",
bool usePriceWithoutVat = false)
{
string beforePriceMin = "";
double lowestPriceValue = double.MaxValue;
bool hasVariants = product?.VariantInfo?.VariantInfo != null && product.VariantInfo.VariantInfo.Any();
if (hasVariants)
{
foreach (var variantNode in product.VariantInfo.VariantInfo)
{
// Generate the ViewModel for variant
var variantViewModel = ViewModelFactory.CreateView(settings, product.Id, variantNode.VariantID);
 
if (variantViewModel != null)
{
// Screenshot is for  variantViewModel.PriceBeforeDiscount object in below line
var currentBeforePriceObj = !string.IsNullOrEmpty(unitId)? variantViewModel.GetPrice(unitId)?.PriceBeforeDiscount: variantViewModel.PriceBeforeDiscount;
 
if (currentBeforePriceObj != null && currentBeforePriceObj.Price > 0 && currentBeforePriceObj.Price < lowestPriceValue)
{
lowestPriceValue = currentBeforePriceObj.Price;
beforePriceMin = usePriceWithoutVat? currentBeforePriceObj.PriceWithoutVatFormatted: currentBeforePriceObj.PriceFormatted;
}
}
}
}
 
// If no variants had a valid discount (so lowestPriceValue is still MaxValue), use fallback
if (lowestPriceValue == double.MaxValue)
{
var fallback = !string.IsNullOrEmpty(unitId)? product?.GetPrice(unitId)?.PriceBeforeDiscount: product?.PriceBeforeDiscount;
// Safety check here as well to ensure the fallback master product price is also > 0
if (fallback != null && fallback.Price > 0)
{
beforePriceMin = usePriceWithoutVat ? fallback.PriceWithoutVatFormatted : fallback.PriceFormatted;
}
}
 
return beforePriceMin ?? "";
}
}

Following parameters passed to above fucntion.
var product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
var unitId = ""; 

var showWithoutVat = true;

var viewSettings = new Dynamicweb.Ecommerce.ProductCatalog.ProductViewModelSettings

{
LanguageId = product?.LanguageId ?? Dynamicweb.Ecommerce.Common.Context.LanguageID
};
viewSettings.FilledProperties = new[] { "Price", "PriceInformative", "PriceBeforeDiscount", "Discount", "ProductDiscounts", "Prices" };
 
string beforePrice = GetMinVariantBeforePrice(product, viewSettings, unitId, showWithoutVat);




Replies

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply
This post has been marked as an answer

Hi Ruwan,

The root cause is that the settings object passed into your function has already been consumed --- specifically, EnsureFilledPropertiesExist() was called on it when the outer ProductViewModel was created, and it may not have FilledProperties set up for the inner CreateView calls. But there's a more fundamental issue too.

The core problem

When you call ViewModelFactory.CreateView(settings, product.Id, variantNode.VariantID), the factory internally calls settings.EnsureFilledPropertiesExist() again --- which is fine --- but the PriceSettings inside may already be initialized with an empty FilledProperties, causing price filling to be skipped silently.

More critically: if your outer settings object was created with an empty/default constructor (new ProductViewModelSettings()) or without explicit currency/country/shop values, the price context will fall back to system defaults and may not match your price matrix --- resulting in 0.

The fix: create a fresh settings object per variant call

Instead of reusing the same settings, create a dedicated one for the variant lookups:


 
code
public string GetMinVariantBeforePrice(
    Dynamicweb.Ecommerce.ProductCatalog.ProductViewModel product,
    Dynamicweb.Ecommerce.ProductCatalog.ProductViewModelSettings settings,
    string unitId = "",
    bool usePriceWithoutVat = false)
{
    string beforePriceMin = "";
    double lowestPriceValue = double.MaxValue;
    bool hasVariants = product?.VariantInfo?.VariantInfo != null &&
        product.VariantInfo.VariantInfo.Any();

    if (hasVariants)
    {
        // Create a fresh settings object for variant lookups,
        // inheriting the same currency/country/shop/language context
        var variantSettings = new Dynamicweb.Ecommerce.ProductCatalog.ProductViewModelSettings(
            settings.LanguageId,
            settings.CurrencyCode,
            settings.CountryCode,
            settings.ShopId,
            settings.UserId,
            settings.ShowPricesWithVat,
            settings.Time,
            settings.StockLocationId)
        {
            FilledProperties = new[]
            {
                nameof(Dynamicweb.Ecommerce.ProductCatalog.ProductViewModel.Price),
                nameof(Dynamicweb.Ecommerce.ProductCatalog.ProductViewModel.PriceBeforeDiscount),
                nameof(Dynamicweb.Ecommerce.ProductCatalog.ProductViewModel.Discount),
                nameof(Dynamicweb.Ecommerce.ProductCatalog.ProductViewModel.Prices)
            }
        };

        foreach (var variantNode in product.VariantInfo.VariantInfo)
        {
            var variantViewModel = Dynamicweb.Ecommerce.ProductCatalog.ViewModelFactory.CreateView(
                variantSettings, product.Id, variantNode.VariantID);

            if (variantViewModel != null)
            {
                var currentBeforePriceObj = !string.IsNullOrEmpty(unitId)
                    ? variantViewModel.GetPrice(unitId)?.PriceBeforeDiscount
                    : variantViewModel.PriceBeforeDiscount;

                if (currentBeforePriceObj != null &&
                    currentBeforePriceObj.Price > 0 &&
                    currentBeforePriceObj.Price < lowestPriceValue)
                {
                    lowestPriceValue = currentBeforePriceObj.Price;
                    beforePriceMin = usePriceWithoutVat
                        ? currentBeforePriceObj.PriceWithoutVatFormatted
                        : currentBeforePriceObj.PriceFormatted;
                }
            }
        }
    }

    // ... fallback to master product price as before
}

Why this works

Internally, ViewModelFactory.CreateView() calls settings.EnsureFilledPropertiesExist(), which sets up a PriceViewModelSettings from FilledProperties. This is what triggers the price engine. By creating a fresh settings object each time with FilledProperties explicitly set, you ensure the price context is properly initialized before the factory consumes it.

If prices are still 0 after this fix, the issue is in your price matrix data rather than the code --- verify that:

  • Prices exist in the database for these variants (or at master product level) under the currency/shop combination you're using
  • The CurrencyCodeCountryCode, and ShopId on your settings actually match the shop and currency your prices are defined against

Hope that helps!

Votes for this answer: 1
 
Ruwan Dissanayake
Reply

Thank you very much for your prompt response. It was indeed very helpful. Following your directions, along with a small tweak, I was able to fetch the discounted prices for the variants. Please find the changes I made below. I hope this approach is correct.

The following properties were not populated by default in the input parameter, settings:

settings.CurrencyCode,
settings.CountryCode,
settings.ShopId,
settings.UserId,
settings.ShowPricesWithVat

So I modified the input parameter "viewSettings",
From:

var viewSettings = new Dynamicweb.Ecommerce.ProductCatalog.ProductViewModelSettings

{
LanguageId = product?.LanguageId ?? Dynamicweb.Ecommerce.Common.Context.LanguageID
};

To:

var viewSettings = new Dynamicweb.Ecommerce.ProductCatalog.ProductViewModelSettings
{
// Language
LanguageId = product?.LanguageId ?? Dynamicweb.Ecommerce.Common.Context.LanguageID,
// Ecom Context
CurrencyCode = Dynamicweb.Ecommerce.Common.Context.Currency?.Code,
CountryCode = Dynamicweb.Ecommerce.Common.Context.Country?.Code2,
 
// Pull the ShopId directly from the current Area's ecommerce settings
ShopId = Pageview?.Area?.EcomShopId ?? "",
 
// User Context (Crucial for user-specific discounts)
UserId = Dynamicweb.Security.UserManagement.User.GetCurrentExtranetUser()?.ID ?? 0,
 
// VAT rules (Determines if prices should format with or without VAT by default)
ShowPricesWithVat = Dynamicweb.Ecommerce.Common.Context.DisplayPricesWithVat
};
viewSettings.FilledProperties = new[] { "Price", "PriceInformative", "PriceBeforeDiscount", "Discount", "ProductDiscounts", "Prices" };

 

You must be logged in to post in the forum