Error executing template "/Designs/Swift/Paragraph/Custom_Swift_ProductDetailsInfo.cshtml"
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
   at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
   at System.Collections.Generic.List`1.get_Item(Int32 index)
   at CompiledRazorTemplates.Dynamic.RazorEngine_08250693537f4c34bf63bb76f5c6eaf1.Execute() in D:\dynamicweb.net\Solutions\Novicell\danitech.cloud.dynamicweb-cms.com\Files\Templates\\Designs\Swift\Paragraph\Custom_Swift_ProductDetailsInfo.cshtml:line 232
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 4 @using Dynamicweb.Ecommerce.Products.FieldDisplayGroups 5 @using Dynamicweb.Frontend 6 @using System.Drawing 7 @using System.Text.RegularExpressions 8 @using Danitech.Website.CustomModules.Helpers 9 @using System.Configuration; 10 @using Newtonsoft.Json 11 12 @functions { 13 //Find contrast color (white, black) 14 public static string GetContrastColor(string hexString) 15 { 16 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString); 17 18 int nThreshold = 105; 19 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + 20 (bg.B * 0.114)); 21 22 string foreColor = (255 - bgDelta < nThreshold) ? "#333" : "#fff"; 23 return foreColor; 24 } 25 } 26 27 @functions{ 28 private dynamic GetQuantityDiscount(string productNumber) 29 { 30 if (Dynamicweb.Environment.CookieManager.GetCookie(ConfigurationManager.AppSettings["DanitechAPIJWTTokenName"]) == null) 31 { 32 return null; 33 } 34 35 var client = new System.Net.Http.HttpClient(); 36 var request = new System.Net.Http.HttpRequestMessage 37 { 38 Method = System.Net.Http.HttpMethod.Get, 39 RequestUri = new Uri(ConfigurationManager.AppSettings["DanitechAPIURL"] + "User/discount/getquantitydiscounts?productNumbers=" + productNumber), 40 Headers = 41 { 42 { "Authorization", $"Bearer {Dynamicweb.Environment.CookieManager.GetCookie(ConfigurationManager.AppSettings["DanitechAPIJWTTokenName"]).Value}" }, 43 }, 44 }; 45 46 string result = ""; 47 using (var response = client.SendAsync(request).Result) 48 { 49 if (response.IsSuccessStatusCode) 50 { 51 result = response.Content.ReadAsStringAsync().Result; 52 } 53 } 54 55 if (string.IsNullOrEmpty(result)) 56 { 57 return null; 58 } 59 else 60 { 61 return JsonConvert.DeserializeObject<dynamic>(result); 62 } 63 } 64 } 65 66 @functions{ 67 private string PriceFormatted(string price) 68 { 69 if (decimal.TryParse(price, out decimal result)) 70 { 71 return result.ToString("N2"); 72 } 73 74 return "-"; 75 } 76 } 77 78 @{ 79 ProductViewModel product = new ProductViewModel(); 80 81 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 82 { 83 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 84 } 85 86 if (Pageview.User == null || string.IsNullOrEmpty(Pageview.User.CustomerNumber)) 87 { 88 StockHelper stockHelper = new StockHelper(); 89 double? stock = stockHelper.GetStock(product.Number).GetAwaiter().GetResult(); 90 91 if (stock != null) 92 { 93 product.StockLevel = stock; 94 } 95 } 96 97 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 98 bool anonymousUser = Pageview.User == null; 99 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser; 100 hideAddToCart = product.VariantInfo.VariantInfo != null && Model.Item.GetBoolean("HideVariantSelector") ? true : hideAddToCart; 101 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser; 102 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false; 103 104 bool isDiscontinued = product.Discontinued; 105 bool IsNeverOutOfStock = product.NeverOutOfstock; 106 string[] variantId = product.VariantId.Split('.'); 107 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : ""; 108 disableAddToCart = isDiscontinued ? "disabled" : disableAddToCart; 109 disableAddToCart = IsNeverOutOfStock ? "" : disableAddToCart; 110 111 // Does product has a expected delivery data 112 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery > DateTime.Now; 113 string expectedDeliveryDate = product.ExpectedDelivery?.ToShortDateString() ?? ""; 114 115 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 116 if (!url.Contains("LayoutTemplate")) 117 { 118 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 119 } 120 121 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList(); 122 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>(); 123 124 foreach (var selection in selectedDisplayGroups) 125 { 126 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 127 { 128 if (selection == group.Id) 129 { 130 mainFeatures.Add(group); 131 } 132 } 133 } 134 135 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 136 137 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6"); 138 139 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 140 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding; 141 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding; 142 143 string quantityPricesLayout = Model.Item.GetRawValueString("QuantityPricesLayout", "list"); 144 145 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\""; 146 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; 147 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; 148 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : ""; 149 150 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 151 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 152 153 string priceMin = ""; 154 string priceMax = ""; 155 156 var favoriteParameters = new Dictionary<string, object>(); 157 if (!anonymousUser && !hideFavoritesSelector) 158 { 159 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists(); 160 int defaultFavoriteListId = 0; 161 162 if (favoreiteLists.Count() == 1) 163 { 164 foreach (FavoriteList list in favoreiteLists) 165 { 166 defaultFavoriteListId = list.ListId; 167 } 168 } 169 170 favoriteParameters.Add("ListId", defaultFavoriteListId); 171 } 172 173 var badgeParms = new Dictionary<string, object>(); 174 badgeParms.Add("size", "h7"); 175 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 176 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 177 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 178 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 179 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 180 181 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 182 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 183 DateTime createdDate = product.Created.Value; 184 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 185 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 186 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 187 188 dynamic quantityDiscounts = null; 189 bool quantityDiscountsHasValues = false; 190 191 if (!anonymousUser) 192 { 193 quantityDiscounts = GetQuantityDiscount(product.Number); 194 quantityDiscountsHasValues = quantityDiscounts != null && quantityDiscounts.productDiscounts != null && quantityDiscounts.productDiscounts.HasValues && quantityDiscounts.productDiscounts[0].quantityDiscounts.Count > 0; 195 } 196 } 197 <div class="h-100 @(contentPadding) @(theme) item_@Model.Item.SystemName.ToLower()"> 198 <div class="d-flex flex-column gap-4 js-product"> 199 @if (showBadges) 200 { 201 <div class="swift_badge-collection"> 202 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 203 </div> 204 } 205 206 @RenderProductNameAndManufacturerLogo(product, titleFontSize, Model.Item.GetBoolean("HideProductNumber")) 207 208 @if (!hidePrice && !isDiscontinued) 209 { 210 <div> 211 <div class="h4" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 212 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 213 214 <div>@Translate("SRP")</div> 215 216 @if (showPricesWithVat == "false" && !neverShowVat) 217 { 218 var itemPropPrice = 0d; 219 var srpPriceFormatted = "-"; 220 221 if (product.Prices.Count > 1) 222 { 223 var minPrice = product.Prices.Min(p => p.Price); 224 var maxPrice = product.Prices.Max(p => p.Price); 225 226 srpPriceFormatted = minPrice != maxPrice ? minPrice.PriceWithoutVatFormatted + " - " + maxPrice.PriceWithoutVatFormatted : minPrice.PriceWithoutVatFormatted; 227 228 itemPropPrice = minPrice.PriceWithoutVat; 229 } 230 else 231 { 232 srpPriceFormatted = product.Prices[0].Price.PriceWithoutVatFormatted; 233 234 itemPropPrice = product.Prices[0].Price.PriceWithoutVat; 235 } 236 237 <span itemprop="price" content="@itemPropPrice" class="d-none"></span> 238 <span class="text-price">@srpPriceFormatted</span> 239 } 240 else 241 { 242 var itemPropPrice = 0d; 243 var srpPriceFormatted = "-"; 244 245 if (product.Prices.Count > 1) 246 { 247 var minPrice = product.Prices.Min(p => p.Price); 248 var maxPrice = product.Prices.Max(p => p.Price); 249 250 srpPriceFormatted = minPrice != maxPrice ? minPrice.PriceFormatted + " - " + maxPrice.PriceFormatted : minPrice.PriceFormatted; 251 itemPropPrice = minPrice.Price; 252 } 253 else 254 { 255 srpPriceFormatted = product.Prices[0].Price.PriceFormatted; 256 itemPropPrice = product.Prices[0].Price.Price; 257 } 258 259 <span itemprop="price" content="@itemPropPrice" class="d-none"></span> 260 <span class="text-price">@srpPriceFormatted</span> 261 } 262 </div> 263 @if (showPricesWithVat == "false" && !neverShowVat) 264 { 265 var price = "-"; 266 267 if (product.Prices.Count > 1) 268 { 269 var minPrice = product.Prices.Min(p => p.Price); 270 var maxPrice = product.Prices.Max(p => p.Price); 271 272 price = minPrice != maxPrice ? minPrice.PriceWithVatFormatted + " - " + maxPrice.PriceWithVatFormatted : minPrice.PriceWithVatFormatted; 273 } 274 else 275 { 276 price = product.Prices[0].Price.PriceWithVatFormatted; 277 } 278 279 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 280 } 281 282 @if (quantityDiscountsHasValues) 283 { 284 <div class="standalone__quantitydiscount"> 285 <h2>@Translate("Your prices")</h2> 286 287 @if (quantityPricesLayout == "list") 288 { 289 <div class="mt-3"> 290 @foreach (var quantityPrice in quantityDiscounts.productDiscounts[0].quantityDiscounts) 291 { 292 string quantityLabel = Translate("PCS"); 293 294 <small class="d-block opacity-75"><span>@quantityPrice.minimumQuantity @quantityLabel</span> - <span>@quantityPrice.discountPercent<text>%</text></span> - <span class="fw-bold">@PriceFormatted(quantityPrice.discountedPrice.ToString()) @quantityPrice.currencyCode</span></small> 295 } 296 </div> 297 } 298 else if (quantityPricesLayout == "table") 299 { 300 <div class="grid"> 301 <table class="table table-striped mt-3 g-col-12 g-col-lg-6"> 302 <thead> 303 <tr> 304 <th>@Translate("Min quantity")</th> 305 <th>@Translate("Discount percent")</th> 306 <th>@Translate("PCS price excl VAT")</th> 307 </tr> 308 </thead> 309 <tbody> 310 @foreach (var quantityPrice in quantityDiscounts.productDiscounts[0].quantityDiscounts) 311 { 312 <tr> 313 <td>@quantityPrice.minimumQuantity</td> 314 <td>@quantityPrice.discountPercent<text>%</text></td> 315 <td>@quantityPrice.discountedPrice @quantityPrice.currencyCode</td> 316 </tr> 317 } 318 </tbody> 319 </table> 320 </div> 321 } 322 </div> 323 } 324 </div> 325 } 326 327 @if (!string.IsNullOrEmpty(product.ShortDescription)) 328 { 329 <div class="mb-0-last-child" itemprop="disambiguatingDescription"> 330 @product.ShortDescription 331 </div> 332 } 333 334 @if (mainFeatures.Count > 0) 335 { 336 foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 337 { 338 <dl class="grid gap-0"> 339 @foreach (var field in mainFeatureGroup.Fields) 340 { 341 @RenderField(field.Value) 342 } 343 </dl> 344 } 345 } 346 347 @if (product.VariantInfo.VariantInfo != null && !Model.Item.GetBoolean("HideVariantSelector")) 348 { 349 int groupNumber = 1; 350 351 <form class="mb-3 js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())" id="VariantSelector_@Model.ID"> 352 <input type="hidden" name="variantid" /> 353 354 @foreach (var variantGroup in product.VariantGroups()) 355 { 356 VariantGroupViewModel group = variantGroup; 357 358 <h3 class="h6">@group.Name</h3> 359 <div class="mb-3 js-variant-group" data-group-id="@groupNumber"> 360 @foreach (var option in group.Options) 361 { 362 string active = variantId.Contains(option.Id) ? "active" : ""; 363 364 if (!string.IsNullOrEmpty(option.Color)) 365 { 366 string contrastColor = GetContrastColor(option.Color); 367 <button type="button" class="btn colorbox rounded-circle me-1 mb-2 d-inline-block variant-option border js-variant-option @active" style="background-color: @option.Color --variantoption-check-color: @contrastColor" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id" id="@(product.Id)_@(option.Id)_@Pageview.CurrentParagraph.ID"></button> 368 } 369 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value)) 370 { 371 <button type="button" class="btn p-0 d-inline-block mb-2 variant-option border js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id" id="@(product.Id)_@(option.Id)_@Pageview.CurrentParagraph.ID"> 372 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" /> 373 </button> 374 } 375 else 376 { 377 <button type="button" class="btn btn-secondary d-inline-block mb-2 variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id" id="@(product.Id)_@(option.Id)_@Pageview.CurrentParagraph.ID"> 378 @option.Name 379 </button> 380 } 381 } 382 </div> 383 384 groupNumber++; 385 } 386 </form> 387 } 388 389 <div class="d-flex flex-row flex-nowrap gap-2" id="AddToCart_@Model.ID"> 390 @if (!hideAddToCart && !isDiscontinued) 391 { 392 <form method="post" action="@url" class="flex-fill"> 393 <input type="hidden" name="redirect" value="false" /> 394 <input type="hidden" name="ProductId" value="@product.Id" /> 395 <input type="hidden" name="cartcmd" value="add" /> 396 397 @if (!string.IsNullOrEmpty(product.VariantId)) 398 { 399 <input type="hidden" name="VariantId" value="@product.VariantId" /> 400 } 401 @if (!Model.Item.GetBoolean("QuantitySelector")) 402 { 403 <input id="Quantity_@product.Id" name="Quantity" value="@valueQty" type="hidden"> 404 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary w-100 js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID">@Translate("Add to cart")</button> 405 } 406 else 407 { 408 <div class="input-group input-primary-button-group js-input-group d-flex flex-row flex-nowrap"> 409 <label for="Quantity_@(product.Id)" class="visually-hidden">@Translate("Quantity")</label> 410 <input id="Quantity_@product.Id" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 96px; min-width:64px;" type="number"> 411 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID">@Translate("Add to cart")</button> 412 </div> 413 414 if (stepQty != "1") 415 { 416 <div class="invalid-feedback d-none"> 417 @Translate("Please select a quantity that is dividable by") @stepQty 418 </div> 419 } 420 } 421 </form> 422 if (!anonymousUser && !hideFavoritesSelector) 423 { 424 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 425 } 426 } 427 else if (!anonymousUser && !hideFavoritesSelector && !isDiscontinued) 428 { 429 <div class="flex-fill" id="AddToFavorites_@Model.ID"> 430 @Translate("Add to favorites") @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 431 </div> 432 } 433 434 @if (isDiscontinued && product.ReplacementProduct != null) 435 { 436 List<ProductInfoViewModel> replacementProductList = new List<ProductInfoViewModel>(); 437 replacementProductList.Add(product.ReplacementProduct); 438 var replacementProduct = replacementProductList.GetProducts().FirstOrDefault(); 439 440 if ((product.DiscontinuedAction == 0 || product.DiscontinuedAction == 1) && product?.ReplacementProduct.ProductId != null) 441 { 442 var parms = new Dictionary<string, object>(); 443 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 444 parms.Add("fullwidth", true); 445 parms.Add("columns", Model.GridRowColumnCount); 446 447 string imagePath = replacementProduct?.DefaultImage?.Value != null ? replacementProduct.DefaultImage.Value : ""; 448 449 var defaultGroupId = replacementProduct.PrimaryOrDefaultGroup.Id; 450 var selectedDetailPage = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(defaultGroupId)?.Meta.PrimaryPage ?? string.Empty; 451 452 string link = string.IsNullOrEmpty(selectedDetailPage) ? $"/Default.aspx?ID={Pageview.Page.ID}&groupid={defaultGroupId}" : selectedDetailPage; 453 link += "&productid=" + replacementProduct.Id; 454 link += !string.IsNullOrEmpty(replacementProduct.VariantId) ? "&variantid=" + replacementProduct.VariantId : ""; 455 456 <div class="w-100"> 457 <div class="fw-bold w-100">@Translate("Sorry, this product is no longer available").</div> 458 <div>@Translate("We recommend this replacement product instead"):</div> 459 460 <a href="@link"> 461 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 462 </a> 463 464 <div>@replacementProduct.Name</div> 465 466 @if (!hidePrice) 467 { 468 <div class="mb-3"> 469 <div class="h4" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 470 @if (showPricesWithVat == "false" && !neverShowVat) 471 { 472 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted; 473 474 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 475 if (product.Price.Price != product.PriceBeforeDiscount.Price) 476 { 477 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 478 } 479 } 480 else 481 { 482 string beforePrice = product.PriceBeforeDiscount.PriceFormatted; 483 484 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 485 if (product.Price.Price != product.PriceBeforeDiscount.Price) 486 { 487 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 488 } 489 } 490 491 @if (showPricesWithVat == "false" && !neverShowVat) 492 { 493 string price = product.Price.PriceWithoutVatFormatted; 494 if (product?.VariantInfo?.VariantInfo != null) 495 { 496 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 497 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 498 } 499 if (priceMin != priceMax) 500 { 501 price = priceMin + " - " + priceMax; 502 } 503 <span class="text-price">@price</span> 504 } 505 else 506 { 507 string price = product.Price.PriceFormatted; 508 if (product?.VariantInfo?.VariantInfo != null) 509 { 510 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 511 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 512 } 513 if (priceMin != priceMax) 514 { 515 price = priceMin + " - " + priceMax; 516 } 517 <span class="text-price">@price</span> 518 } 519 </div> 520 521 @if (showPricesWithVat == "false" && !neverShowVat) 522 { 523 string price = product.Price.PriceWithVatFormatted; 524 if (product?.VariantInfo?.VariantInfo != null) 525 { 526 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 527 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 528 } 529 if (priceMin != priceMax) 530 { 531 price = priceMin + " - " + priceMax; 532 } 533 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 534 } 535 </div> 536 } 537 538 <a href="@link" class="btn btn-primary w-100">@Translate("Go to the replacement")</a> 539 </div> 540 } 541 } 542 </div> 543 </div> 544 @if (!Model.Item.GetBoolean("HideStockState")) 545 { 546 if (!IsNeverOutOfStock && !isDiscontinued) 547 { 548 <div class="mt-3 js-stock-state"> 549 550 @if (product.StockLevel > 0) 551 { 552 if (!Model.Item.GetBoolean("HideInventory")) 553 { 554 <p class="small text-success m-0">@product.StockLevel @Translate("Products available in stock")</p> 555 } 556 else 557 { 558 <p class="small text-success m-0">@Translate("Available in stock")</p> 559 } 560 } 561 562 else 563 { 564 <p class="small text-danger m-0">@Translate("Out of Stock")</p> 565 } 566 567 @if (hasExpectedDelivery) 568 { 569 <p> 570 <span>@Translate("Expected back in stock:")</span> 571 <span>@expectedDeliveryDate</span> 572 </p> 573 } 574 575 </div> 576 } 577 } 578 </div> 579 580 @helper RenderField(FieldValueViewModel field) 581 { 582 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 583 bool noValues = false; 584 585 if (!string.IsNullOrEmpty(fieldValue)) 586 { 587 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 588 { 589 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 590 noValues = values.Count > 0 ? false : true; 591 } 592 } 593 594 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 595 { 596 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt> 597 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3"> 598 @RenderFieldValue(field) 599 </dd> 600 } 601 } 602 603 @helper RenderFieldValue(FieldValueViewModel field) 604 { 605 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 606 607 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 608 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 609 610 bool isColor = false; 611 612 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 613 { 614 int valueCount = 0; 615 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 616 int totalValues = values.Count; 617 618 foreach (FieldOptionValueViewModel option in values) 619 { 620 if (option.Value.Substring(0, 1) == "#") 621 { 622 isColor = true; 623 } 624 625 if (!isColor) 626 { 627 @option.Name 628 } 629 else 630 { 631 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span> 632 } 633 634 if (valueCount != totalValues && valueCount < (totalValues - 1)) 635 { 636 if (isColor) 637 { 638 <text> </text> 639 } 640 else 641 { 642 <text>, </text> 643 } 644 } 645 valueCount++; 646 } 647 } 648 else 649 { 650 if (fieldValue.Substring(0, 1) == "#") 651 { 652 isColor = true; 653 } 654 655 if (!isColor) 656 { 657 @fieldValue 658 } 659 else 660 { 661 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 662 } 663 } 664 } 665 666 @if (product.VariantInfo.VariantInfo != null) 667 { 668 <script type="module"> 669 swift.VariantSelector.init(); 670 </script> 671 } 672 673 674 @helper RenderProductNameAndManufacturerLogo(ProductViewModel product, string titleFontSize, bool hideProductNumber) 675 { 676 <div> 677 @if (product.ProductFields != null && product.ProductFields.ContainsKey("BrandLogo")) 678 { 679 var brandLogo = product.ProductFields["BrandLogo"]; 680 if (brandLogo != null && !string.IsNullOrEmpty(brandLogo.Value.ToString()) && Regex.IsMatch(brandLogo.Value.ToString(), @"(.png|.jpg|.jpeg)$", RegexOptions.IgnoreCase)) 681 { 682 <div class="d-flex flex-row-reverse"> 683 <div class="p-2"> 684 <img src="/Admin/Public/GetImage.ashx?image=@brandLogo.Value&width=100&format=webp" /> 685 </div> 686 </div> 687 } 688 } 689 <div> 690 <h1 class="@titleFontSize" itemprop="name">@product.Name</h1> 691 @if (!hideProductNumber) 692 { 693 <div class="opacity-85">@product.Number</div> 694 } 695 </div> 696 </div> 697 }

Specifikationer

Variant feature selector DisplacementNo, ShaftTypeNew, DirectionOfRotation
Pumpegruppe 0.5
Ved at klikke 'Acceptér Alle' så giver til tiladelse til at vi må indsamle information om dig til forskellige formål, hvilket inkluderer: Funktionalitet, Statestik og Marketing