Developer forum

Forum » Development » PriceProvider with custom information

PriceProvider with custom information

Aki Ruuskanen
Aki Ruuskanen
Reply

Hi,

We have a solution where customer rent products and can choose a rental period. We get the prices from the ERP to where we send product, user and the period. We dicussed the possibility to use the PriceProvider for this but came to the conclusion that in this scenation we cannot do that.

The reason is that from the ERP, we dont only get the total price for the period, but also the price per day and "type of day". The price provider returns a PriceRaw and to my knowledge that cannot be extended to hold more information. 

We have until now using our our own price logic that we built for this for this but it would be nice to be able to it in a more standard way. I cannot se any good way though, how to handle more information in the priceprovider that just a PriceRaw object. 

Regards / Aki


Replies

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Hi Aki

You should always use a priceprovider for the price.

But you can do make an architecture where you fetch the information frrom the ERP from some kind of home grown service, and then let the price provider ask for the actual price information from that service and have other logic in other places also get its information inside that service.

So describing you problem to ChatGPT with some thoughts this is what comes out:

 

Below is a conceptual example in pseudo code of how you might architect your price provider, backed by an ERP-service call and a caching mechanism, so that you can:

  1. Ask for the main (final) price for Dynamicweb (returned via a PriceRaw instance).
  2. Cache all additional ERP pricing data (e.g. daily price, discount info, etc.) in memory or another store.
  3. Make that additional data available later to other functionality that needs it (e.g. to display daily price in the UI).

High-Level Overview

  1. PriceProvider

    • Implementation of Dynamicweb’s PriceProvider that must return a PriceRaw.
    • It calls into a PriceService to retrieve the required information for a given product, user, and rental period.
    • Once it gets the price data, it converts or selects the final price for Dynamicweb into a PriceRaw object.
  2. PriceService

    • Handles logic around either fetching data from cache or requesting it from the ERP.
    • Responsible for storing the full ERP results in a cache (so that subsequent uses do not need to call the ERP again).
  3. ERP Client

    • Encapsulates calling the ERP’s API.
    • Returns a complex object that includes daily prices, discount tiers, etc.
  4. Caching

    • Either in-memory (e.g., MemoryCache) or a distributed cache.
    • Keyed by a combination of product + user + rental period (the most important factors).
    • Returns a data object that holds the entire set of ERP data (not just the final price).
  5. Consumers of the Additional Data

    • Code or UI parts that need the extra data (daily price, discount details) can pull from the same PriceService or from a specialized “RentalInfoService” that wraps the same cache.
    • They do not call the ERP again; they just read from the already cached object.

Pseudo Code

1. The Data Structures

// 1) Represents the full data retrieved from the ERP
//    (not just the final price used by Dynamicweb)
public class ErpPriceData
{
    public decimal FinalPrice { get; set; }       // e.g. total for rental period
    public decimal DailyPrice { get; set; }       // e.g. price per day
    public decimal DiscountAmount { get; set; }
    public DateTime RentalStart { get; set; }
    public DateTime RentalEnd { get; set; }
    // Add more fields as needed from the ERP response
}

// 2) A simple cache key for the combination of product, user, and rental period
public class PriceCacheKey
{
    public string ProductId { get; }
    public string UserId { get; }
    public DateTime StartDate { get; }
    public DateTime EndDate { get; }

    public PriceCacheKey(string productId, string userId, DateTime startDate, DateTime endDate)
    {
        ProductId = productId;
        UserId = userId;
        StartDate = startDate;
        EndDate = endDate;
    }

    // Implement Equals and GetHashCode properly so that the cache can match keys
    public override bool Equals(object obj)
    {
        // Pseudo code for equality check
        // ...
    }

    public override int GetHashCode()
    {
        // Pseudo code for generating a hash
        // ...
    }
}

2. The Caching Layer

// Could be a static class, a service, or use built-in .NET MemoryCache.
// Here is a simplified approach:
public static class PriceCache
{
    private static readonly Dictionary<PriceCacheKey, ErpPriceData> _cache 
        = new Dictionary<PriceCacheKey, ErpPriceData>();

    public static ErpPriceData GetOrAdd(PriceCacheKey key, Func<ErpPriceData> fetchFromErp)
    {
        if (_cache.TryGetValue(key, out var cachedResult))
        {
            return cachedResult;
        }

        // If not in cache, fetch from ERP
        var erpData = fetchFromErp();

        // Store in cache
        _cache[key] = erpData;

        return erpData;
    }
}

3. The ERP Client

public class ErpClient
{
    public ErpPriceData FetchPriceData(string productId, string userId, DateTime startDate, DateTime endDate)
    {
        // Pseudo code to call out to your ERP, e.g. via HTTP/REST, SOAP, etc.
        // For example:
        /*
        var request = new ErpRequest 
        {
            ProductId = productId,
            UserId = userId,
            RentalStart = startDate,
            RentalEnd = endDate
        };
        var response = HttpClient.Post("http://myerp/pricing", request);
        */

        // Then parse response:
        // Convert the ERP response to ErpPriceData
        var data = new ErpPriceData
        {
            FinalPrice = /* parse from response */,
            DailyPrice = /* parse from response */,
            DiscountAmount = /* parse from response */,
            RentalStart = startDate,
            RentalEnd = endDate
        };

        return data;
    }
}

4. The PriceService (Orchestration Logic)

public class PriceService
{
    private readonly ErpClient _erpClient;

    public PriceService()
    {
        _erpClient = new ErpClient();
    }

    public ErpPriceData GetFullPriceData(string productId, string userId, DateTime startDate, DateTime endDate)
    {
        // Build our cache key
        var cacheKey = new PriceCacheKey(productId, userId, startDate, endDate);

        // Retrieve from cache or do ERP call
        var erpPriceData = PriceCache.GetOrAdd(cacheKey, () => {
            return _erpClient.FetchPriceData(productId, userId, startDate, endDate);
        });

        return erpPriceData;
    }
}

5. The PriceProvider (Dynamicweb Integration)

// This class extends or implements the relevant PriceProvider interface in Dynamicweb
public class RentalPriceProvider // : Dynamicweb.eCommerce.Prices.PriceProvider (example)
{
    private readonly PriceService _priceService = new PriceService();

    // This is the method that Dynamicweb will call to get the final price
    public PriceRaw GetPrice(PriceProviderContext context)
    {
        // 1) Extract needed info from the context
        var productId = context.ProductId;
        var userId = context.UserId;
        var rentalStart = context.GetCustomField<DateTime>("RentalStart"); // example
        var rentalEnd   = context.GetCustomField<DateTime>("RentalEnd");   // example

        // 2) Retrieve full ERP data from our PriceService
        var erpData = _priceService.GetFullPriceData(productId, userId, rentalStart, rentalEnd);

        // 3) Convert the final price from ERP to a PriceRaw (the only thing DW sees)
        var priceRaw = new PriceRaw
        {
            Price = erpData.FinalPrice,
            // Possibly set other standard fields from erpData if relevant
        };

        // 4) Return the price
        return priceRaw;
    }
}

6. Using the Cached Data Elsewhere

// Example: Some UI or a different piece of code wants to display daily price
public class SomeRentalUI
{
    private readonly PriceService _priceService = new PriceService();

    public void ShowRentalPrice(string productId, string userId, DateTime startDate, DateTime endDate)
    {
        // We fetch data from the same PriceService which uses the same cache.
        var erpData = _priceService.GetFullPriceData(productId, userId, startDate, endDate);

        // Now we have daily price, discount, etc.
        Console.WriteLine("Daily price is " + erpData.DailyPrice);
    }
}

Summary

  1. PriceProvider (Dynamicweb integration layer)

    • Returns PriceRaw to Dynamicweb.
    • Delegates fetching logic to PriceService.
  2. PriceService

    • Orchestrates the retrieval of ERP data.
    • Uses a PriceCache to avoid repeated calls.
  3. PriceCache

    • A dictionary or MemoryCache that stores ErpPriceData objects indexed by a composite key (Product + User + RentalPeriod).
  4. ERP Client

    • Knows how to call and parse the ERP response into an ErpPriceData.
  5. Extra Data Consumption

    • Other parts of the code that need additional ERP-derived info can call the PriceService and get an already cached ErpPriceData. This avoids calling the ERP a second time.

With this approach, you ensure that:

  • You only call the ERP once per (product, user, date range) combination.
  • You store all relevant ERP pricing data in a single object.
  • The PriceProvider remains simple and only returns a PriceRaw object to Dynamicweb.
  • The rest of the code can reuse the same data (daily price, discount info, etc.) from the cache without refetching from the ERP.
 
Aki Ruuskanen
Aki Ruuskanen
Reply

Hi,

Thanks for the input Nicolai and ChatGPT. :) 

Out thoughts has also been to use a cache for the rest of the information. Wanted to explore if there perhaps were other solutions to the problem. 

Regards / Aki

 

You must be logged in to post in the forum