diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Examples/CustomProductProvider.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Examples/CustomProductProvider.cs index 20bf509..42d56ed 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Examples/CustomProductProvider.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Examples/CustomProductProvider.cs @@ -27,15 +27,16 @@ public override string GetProductIdentifier(Settings settings, Product product, /// /// Gets the price. /// - /// The product. + /// The product info. /// The quantity. + /// The product. /// PriceInfo. - public override PriceInfo GetPriceInfo(LiveContext context, ProductInfo product, double quantity) + public override PriceInfo GetPriceInfo(LiveContext context, ProductInfo productInfo, double quantity, Product product) { // Example: if we have a price per kilogram - we need to multiply it by quantity - if (double.TryParse(product["TotalPrice"].ToString(), out double unitPriceWithoutVat)) + if (double.TryParse(productInfo["TotalPrice"].ToString(), out double unitPriceWithoutVat)) { - double? unitPriceWithVat = (double?)product["TotalPriceWithVat"]; + double? unitPriceWithVat = (double?)productInfo["TotalPriceWithVat"]; var currency = Common.Context.Currency; if (currency is null) @@ -47,7 +48,7 @@ public override PriceInfo GetPriceInfo(LiveContext context, ProductInfo product, } else { - return base.GetPriceInfo(context, product, quantity); + return base.GetPriceInfo(context, productInfo, quantity, product); } } } diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs index ccd1143..d1fbd41 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Connectors/Connector.cs @@ -9,8 +9,6 @@ using System.Collections.Concurrent; using System.Net; using System.Net.Http; -using System.Net.Mail; -using System.Runtime.ExceptionServices; using System.Xml; using static Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Notifications.Communication; @@ -19,7 +17,7 @@ namespace Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Connectors /// /// Main class to interact with a remote ERP. /// - internal static class Connector + public static class Connector { /// /// The maximum retry count @@ -40,7 +38,7 @@ public static bool EnableThrowExceptions { return Core.Converter.ToBoolean(Context.Current?.Items?["DynamicwebLiveIntegrationAddInThrowExceptions"]); } - set + internal set { if (Context.Current?.Items != null) { @@ -68,7 +66,7 @@ private static ConnectorBase GetConnector(Settings settings, Logger logger, Subm { return new EndpointConnector(settings, logger, submitType, order); } - } + } /// /// Calculates the order. @@ -278,7 +276,7 @@ private static XmlDocument Communicate(Settings settings, string request, string logger.Log(ErrorLevel.ConnectionError, $"An error occurred while calling {referenceName} from Web Service: '{ex.Message}'."); Diagnostics.ExecutionTable.Current.Add($"DynamicwebLiveIntegration: An error occurred while calling {referenceName} from Web Service: '{ex.Message}'."); - HandleException(connector, settings, endpoint, ex, out httpStatusCode, ref retry); + HandleException(connector, settings, endpoint, ex, out httpStatusCode, ref retry); NotificationManager.Notify(OnAfterErpException, new OnAfterErpExceptionArgs(request, erpXmlResponse, referenceName, ex, settings, logger)); } @@ -357,8 +355,27 @@ private static void HandleException(ConnectorBase connector, Settings settings, } } - internal static string RetrievePDF(Settings settings, string requestString, SubmitType submitType) + /// + /// Retrieves a PDF document as a Base64-encoded string based on the specified request and submission type. + /// + /// The returned string can be decoded to obtain the original PDF file. Ensure that the + /// provided settings and request string are valid to avoid exceptions during execution. + /// The settings used to configure the connector and logging behavior. Cannot be null. + /// The request data to be sent to the connector for PDF retrieval. Cannot be null or empty. + /// The type of submission to use when executing the request. + /// A Base64-encoded string representing the retrieved PDF document. + public static string RetrievePDF(Settings settings, string requestString, SubmitType submitType) { + if (settings is null) + { + throw new ArgumentNullException(nameof(settings)); + } + + if (string.IsNullOrEmpty(requestString)) + { + throw new ArgumentException("RequestString value cannot be null or empty.", nameof(requestString)); + } + Diagnostics.ExecutionTable.Current.Add("DynamicwebLiveIntegration.Connector.RetrievePDF START"); string base64EncodedPDF; var logger = new Logger(settings); diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj index 8365b8a..0e6cccb 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj @@ -1,6 +1,6 @@  - 10.4.35 + 10.4.36 1.0.0.0 Live Integration Live Integration diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductManager.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductManager.cs index 4a06c61..fc6b4f8 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductManager.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductManager.cs @@ -50,6 +50,10 @@ internal static ProductProviderBase ProductProvider if (!ReferenceEquals(addIn, typeof(ProductProviderBase))) { provider = (ProductProviderBase)AddInManager.GetInstance(addIn); + // Honor legacy 3-parameter overrides in subclasses so existing customizations are not silently + // bypassed when internal callers use the new overload with 4-parameters. + var method = addIn.GetMethod(nameof(ProductProviderBase.GetPriceInfo), [typeof(LiveContext), typeof(ProductInfo), typeof(double)]); + provider.HasLegacyGetPriceInfoOverride = method is not null; break; } } diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductPriceProvider.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductPriceProvider.cs index 313bd85..6cf42d3 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductPriceProvider.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductPriceProvider.cs @@ -132,7 +132,7 @@ public PriceInfo FindPriceInfo(PriceContext context, PriceProductSelection selec ? productInfo : null; - return productInfo != null ? productProvider.GetPriceInfo(liveContext, productInfo, selection.Quantity) : null; + return productInfo != null ? productProvider.GetPriceInfo(liveContext, productInfo, selection.Quantity, selection.Product) : null; } catch (Exception e) { @@ -161,7 +161,7 @@ IEnumerable> IPriceInfoProvider.FindQ Quantity = unitPrice.Quantity ?? 0, UnitId = unitPrice.UnitId, }, - ProductProviderBase.GetPriceInfo(context, unitPrice.Amount, unitPrice.AmountWithVat) + ProductProviderBase.GetPriceInfo(context, unitPrice.Amount, unitPrice.AmountWithVat, product) )); } return result; diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductProviderBase.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductProviderBase.cs index dbf7be5..8245bf6 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductProviderBase.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Products/ProductProviderBase.cs @@ -19,6 +19,8 @@ namespace Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Products /// public class ProductProviderBase { + internal bool HasLegacyGetPriceInfoOverride { get; set; } + /// /// Creates a unique product identifier by concatenating the product ID or number (depends on the CalculatePriceUsingProductNumber setting), the variant ID and the language ID. /// Override to build up your own unique identifier. @@ -93,22 +95,46 @@ public virtual Product GetProductWithUnit(Product product, string unitId) /// /// Gets the price. /// - /// The product. + /// The product info. /// The quantity. /// PriceInfo - /// product + /// productInfo /// /// /// - public virtual PriceInfo GetPriceInfo(LiveContext context, ProductInfo product, double quantity) + [Obsolete("Use GetPriceInfo(LiveContext context, ProductInfo productInfo, double quantity, Product product) instead")] + public virtual PriceInfo GetPriceInfo(LiveContext context, ProductInfo productInfo, double quantity) => + GetPriceInfoCore(context, productInfo, quantity, null); + + /// + /// Gets the price. + /// + /// The product info. + /// The quantity. + /// The product. + /// PriceInfo + /// productInfo + /// + /// + /// + public virtual PriceInfo GetPriceInfo(LiveContext context, ProductInfo productInfo, double quantity, Product product) { - if (product == null) + if (HasLegacyGetPriceInfoOverride) { - throw new ArgumentNullException(nameof(product)); +#pragma warning disable CS0618 + return GetPriceInfo(context, productInfo, quantity); +#pragma warning restore CS0618 } - double? priceWithoutVat = (double?)product["TotalPrice"]; - double? priceWithVat = (double?)product["TotalPriceWithVat"]; + return GetPriceInfoCore(context, productInfo, quantity, product); + } + + private static PriceInfo GetPriceInfoCore(LiveContext context, ProductInfo productInfo, double quantity, Product product) + { + ArgumentNullException.ThrowIfNull(productInfo); + + double? priceWithoutVat = (double?)productInfo["TotalPrice"]; + double? priceWithVat = (double?)productInfo["TotalPriceWithVat"]; if (!priceWithoutVat.HasValue) { @@ -117,7 +143,7 @@ public virtual PriceInfo GetPriceInfo(LiveContext context, ProductInfo product, quantity = 1; } - var erpPriceResponse = ((IList)product["Prices"] ?? Enumerable.Empty()) + var erpPriceResponse = ((IList)productInfo["Prices"] ?? Enumerable.Empty()) .Where(p => quantity >= p.Quantity.GetValueOrDefault(1)) .OrderByDescending(p => p.Quantity.GetValueOrDefault(1)) .FirstOrDefault(); @@ -126,8 +152,7 @@ public virtual PriceInfo GetPriceInfo(LiveContext context, ProductInfo product, priceWithVat = erpPriceResponse?.AmountWithVat; } - PriceInfo result = GetPriceInfo(context.PriceContext != null ? context.PriceContext : new PriceContext(context.Currency, context.Country), priceWithoutVat, priceWithVat); - return result; + return GetPriceInfo(context.PriceContext ?? new PriceContext(context.Currency, context.Country), priceWithoutVat, priceWithVat, product); } /// @@ -138,7 +163,7 @@ public virtual PriceInfo GetPriceInfo(LiveContext context, ProductInfo product, /// The quantity. public virtual void FillProductValues(ProductInfo productInfo, Product product, Settings settings, double quantity, LiveContext context) { - var price = GetPriceInfo(context, productInfo, quantity); + var price = GetPriceInfo(context, productInfo, quantity, product); PriceInfo productPrice = PriceManager.GetPrice(context.PriceContext ?? new PriceContext(context.Currency, context.Country), product); productPrice.PriceWithoutVAT = price.PriceWithoutVAT; productPrice.PriceWithVAT = price.PriceWithVAT; @@ -274,7 +299,7 @@ public virtual void FillProductFieldValues(Product product, ProductInfo productI } } - internal static PriceInfo GetPriceInfo(PriceContext priceContext, double? priceWithoutVat, double? priceWithVat) + internal static PriceInfo GetPriceInfo(PriceContext priceContext, double? priceWithoutVat, double? priceWithVat, Product product) { PriceInfo result = new PriceInfo(priceContext.Currency); result.PriceWithoutVAT = priceWithoutVat != null ? priceWithoutVat.Value : 0; @@ -294,7 +319,9 @@ internal static PriceInfo GetPriceInfo(PriceContext priceContext, double? priceW Price = result.PriceWithoutVAT, Currency = priceContext.Currency }; - var calculated = PriceCalculated.Create(priceContext, price); + var calculated = product == null + ? PriceCalculated.Create(priceContext, price) + : PriceCalculated.Create(priceContext, price, product); if (calculated != null) { calculated.Calculate();