Error executing template "Designs/Tefcold/eCom/ProductCatalog/basic_Pim.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at Tefcold.Web.CustomCode.Razor.ProductViewModelExtensions.GetSpecifications(ProductViewModel product)
   at Tefcold.Web.CustomCode.Extensions.ProductExtensions.GetAsseccoriesList(ProductViewModel viewModel)
   at CompiledRazorTemplates.Dynamic.RazorEngine_e69da02e0c3643b28bf6b4197cb0c198.Execute() in E:\Solutions\Live\Tefcold.Web\Files\Templates\Designs\Tefcold\eCom\ProductCatalog\basic_Pim.cshtml:line 27
   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.b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @using System.Globalization 2 @using NLWI.Platforms.Dynamicweb9.Specs 3 @using NLWI.Core.Factory 4 @using System.Linq; 5 @using System.Text.RegularExpressions 6 @using Dynamicweb.Ecommerce.International 7 @using Dynamicweb.Ecommerce.ProductCatalog 8 @using Dynamicweb.Ecommerce.Products 9 @using Dynamicweb.Security.UserManagement 10 @using NORRIQ.Common8.Ecom 11 @using Newtonsoft.Json 12 @using Newtonsoft.Json.Serialization 13 @using NORRIQ.SalesPersonLogin.Services 14 @using NORRIQ.Seo.Canonical 15 @using Tefcold.Web.CustomCode.AsyncProductList.Models 16 @using Tefcold.Web.CustomCode.Extensions 17 @using Tefcold.Web.CustomCode.Items 18 @using Tefcold.Web.CustomCode.Items.Properties 19 @using Tefcold.Web.CustomCode.Items.Settings 20 @using Tefcold.Web.CustomCode.Razor 21 @using Tefcold.Web.CustomCode.Stocks.Helpers 22 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Ecommerce.ProductCatalog.ProductViewModel> 23 @{ 24 var stockInformation = StockLocationHelper.GetShopStockInformation(Pageview.ID); 25 var variantId = System.Web.HttpContext.Current.Request.QueryString["VariantID"]; 26 var selectedModel = (!string.IsNullOrEmpty(variantId) ? Model.Variants?.FirstOrDefault(a => string.Equals(a.VariantId, variantId)) : null) ?? Model; 27 var accessories = selectedModel.GetAsseccoriesList(); 28 var relatedProducts = Model.GetProductRelationGroup("Related Products");// Configuration 29 var perfionImageNames = new string[] { "PrimaryImage", "DetailImage1", "DetailImage2", "BrandedImage", "PackedImage", "OtherImages", "WithContentImage", "OpenImage", "ImageRange", "ImageOnLocation" }; 30 //var specsToList = new HashSet<string>() { "Fitting", "Features", "USP", "Gender", "Season", "Year" }; @*Leave empty for all *@ 31 32 var websiteSettings = Dynamicweb.Services.Items.GetItem(Pageview.Area.ItemType, Pageview.Area.ItemId) 33 .ToCodeFirstItem<Websites>(); 34 35 var inspirationGroupIds = websiteSettings.InspirationGroupIds ?? new List<string>(); 36 37 selectedModel.StockUnits = selectedModel.StockUnits.GetShopStocks(Pageview.Area.EcomShopId).ToList(); 38 var convertedSelectedModel = new AsyncProductWithSpecification(new SimpleProduct(selectedModel, null, stockInformation, inspirationGroupIds, true)); 39 convertedSelectedModel.Product.DefaultPrice.CurrencyCode = Pageview.Area.EcomCurrencyId; 40 var convertedMasterModel = new AsyncProductWithSpecification(new SimpleProduct(Model, null, stockInformation, inspirationGroupIds, true)); 41 42 var selectedJsonModel = JsonConvert.SerializeObject(convertedSelectedModel, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); 43 var selectedMasterProductModel = JsonConvert.SerializeObject(convertedMasterModel, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); 44 selectedMasterProductModel = selectedMasterProductModel.Replace("'", "&#39;"); 45 selectedJsonModel = selectedJsonModel.Replace("'", "&#39;"); 46 47 string asyncPrefix = "Async "; 48 var userInTefcoldUserGroup = ImpersonationService.IsCurrentlyImpersonating(); 49 FieldValueViewModel isSpareField; 50 selectedModel.ProductFields.TryGetValue("IsSparePart", out isSpareField); 51 var isSparePart = (bool)isSpareField.Value; 52 53 54 var isCZShop = Pageview.Area.EcomShopId.Equals("SHOP2", StringComparison.InvariantCultureIgnoreCase) || 55 Pageview.Area.EcomShopId.Equals("SHOP3", StringComparison.InvariantCultureIgnoreCase); 56 57 if (isSparePart) 58 { 59 60 Pageview.Meta.AddTag("robots", "noindex,nofollow"); 61 } 62 63 // Specs 64 var specs = ProductViewModelExtensions.GetSpecifications(selectedModel); 65 var images = perfionImageNames.SelectMany(a => specs.GetAllByKey(a)); 66 var imageAlt = specs.GetByKey("ProductName"); 67 68 var ecoIcon = specs.GetByKey("EcoIcon"); 69 70 var trueString = true.ToString().ToLower(); 71 72 string basicPimPrefix = "PDP "; 73 var languageId = Model.LanguageId; 74 75 var productService = ObjectFactory.GetInstance<ProductService>(); 76 LanguageService languageService = new LanguageService(); 77 //ProductFields 78 79 DateTime result; 80 string earliestHarborArrival = selectedModel.ProductFields.FirstOrDefault(f => f.Value.SystemName == "EarliestArrivalFromHarbor").Value?.Value?.ToString() ?? string.Empty; 81 82 if (DateTime.TryParseExact(earliestHarborArrival, "yyyy-mm-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) 83 { 84 var earliestArrivalTranslateString = Translate(basicPimPrefix + "stock arriving soon. Due", "Stock arriving soon. Due"); 85 earliestHarborArrival = result.ToString("dd/mm/yyyy"); 86 earliestHarborArrival = earliestArrivalTranslateString + " " + earliestHarborArrival; 87 } 88 89 //websiteSettings = Pageview.GetWebsiteSettings(); 90 string shortSellingText = specs.GetByKey("SellingTextShort")?.Value; 91 string energyArrow = specs.GetByKey("EnergyArrow")?.Value; 92 string energyLabel = specs.GetByKey("EnergyLabel")?.Value; 93 var marketingMessage = specs.GetByKey("MarketingMessage")?.Value; 94 string sticker = specs.GetByKey("Stickers")?.Value; 95 string stickerCz = specs.GetByKey("StickersCZ")?.Value; 96 string netVolumeEnergyRating = specs.GetByKey("NetVolumeEnergyRating")?.Value; 97 string totalDisplayArea = specs.GetByKey("TotalDisplayArea")?.Value; 98 var standardCustomer = Pageview.AreaSettings.GetString("StandardCustomer"); 99 standardCustomer = string.IsNullOrEmpty(standardCustomer) ? "" : standardCustomer; 100 string detailImage1Text = specs.GetByKey("DetailImage1Text")?.Value; 101 string detailImage2Text = specs.GetByKey("DetailImage2Text")?.Value; 102 } 103 <product-details-simple-pim :product='@selectedJsonModel' 104 :images='@Newtonsoft.Json.JsonConvert.SerializeObject(images.Select(a => a.Value))' 105 inline-template 106 language-id="@Model.LanguageId"> 107 <div class="basic_pim" v-bind:class="{'initialized': initialized }"> 108 <section class="basic_pim-pdp" itemscope="" itemtype="https://schema.org/Product"> 109 <div class="basic_pim-media"> 110 <div class="@(images.Count() > 1 ? "basic_pim-pictos thumbs" : "basic_pim-pictos")"> 111 <div class="stickers"> 112 @if (!isCZShop && !string.IsNullOrEmpty(sticker)) 113 { 114 <img src="/Admin/Public/GetImage.ashx?Height=58&amp;Compression=85&amp;Image=@sticker" alt="@Translate(basicPimPrefix + "Sticker", "Sticker")" class="img-fluid" /> 115 } 116 else if (isCZShop && !string.IsNullOrEmpty(stickerCz)) 117 { 118 <img src="/Admin/Public/GetImage.ashx?Height=40&amp;Compression=85&amp;Image=@stickerCz" alt="@Translate(basicPimPrefix + "Sticker", "Sticker")" class="img-fluid" /> 119 } 120 @if (websiteSettings.ShowPriceWitVatAndDiscount) 121 { 122 <span v-if="ShowDiscount" class="sticker discount"> 123 -{{discount}}% 124 </span> 125 } 126 127 </div> 128 @if (!string.IsNullOrEmpty(ecoIcon?.Value)) 129 { 130 <img src="@(ecoIcon?.Value)" alt="@Translate(basicPimPrefix + "eco product", "Eco product")" class="img-fluid" style="margin-left:auto;" /> 131 } 132 133 </div> 134 <gallery :items="images" 135 :index="index" 136 @@close="index = null"> 137 </gallery> 138 <template v-if="images.length > 1"> 139 <slick ref="slick" 140 class="basic_pim-thumbs" 141 id="pdp-thumbs" 142 :options="slickPimOptionsThumbs"> 143 <figure v-for="thumb in images" class="basic_pim-thumb"> 144 <img :src="'/Admin/Public/GetImage.ashx?Width=50&amp;Height=50&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;fillcanvas=True&amp;Image=' + thumb" 145 alt="@selectedModel.Name" 146 class="img-fluid" /> 147 </figure> 148 </slick> 149 <slick ref="slick" 150 class="basic_pim-images" 151 id="pdp-images" 152 :options="slickPimOptionsImages"> 153 <figure class="basic_pim-image" 154 v-for="(image, imageIndex) in images" 155 :key="imageIndex" 156 @@click="setIndex(imageIndex)" 157 title="@Translate(basicPimPrefix + "show image", "Show image")"> 158 <picture> 159 <source media="(max-width:767.98px)" :srcset="'/Admin/Public/GetImage.ashx?Width=400&amp;Height=400&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;fillcanvas=True&amp;Image=' + image"> 160 <source media="(max-width:991.98px)" :srcset="'/Admin/Public/GetImage.ashx?Width=530&amp;Height=530&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;fillcanvas=True&amp;Image=' + image"> 161 <img :src="'/Admin/Public/GetImage.ashx?Width=530&amp;Height=530&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;fillcanvas=True&amp;Image=' + image" 162 alt="@selectedModel.Name" 163 class="img-fluid" 164 itemprop="image" /> 165 </picture> 166 @if (!string.IsNullOrEmpty(detailImage1Text) || !string.IsNullOrEmpty(detailImage2Text)) 167 { 168 <figcaption class="alert bg-white" v-if="imageIndex < 3"> 169 @if (!string.IsNullOrEmpty(detailImage1Text)) 170 { 171 <template v-if="imageIndex == 1"> 172 @detailImage1Text 173 </template> 174 } 175 @if (!string.IsNullOrEmpty(detailImage2Text)) 176 { 177 <template v-if="imageIndex == 2"> 178 @detailImage2Text 179 </template> 180 } 181 </figcaption> 182 } 183 </figure> 184 </slick> 185 </template> 186 <template v-if="images.length == 1"> 187 <div class="basic_pim-images"> 188 <figure class="basic_pim-image" 189 v-for="(image, imageIndex) in images" 190 :key="imageIndex" 191 @@click="setIndex(imageIndex)" 192 title="@Translate(basicPimPrefix + "show image", "Show image")"> 193 <picture> 194 <source media="(max-width:767.98px)" :srcset="'/Admin/Public/GetImage.ashx?Width=300&amp;Height=250&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;fillcanvas=True&amp;Image=' + image"> 195 <source media="(max-width:991.98px)" :srcset="'/Admin/Public/GetImage.ashx?Width=400&amp;Height=350&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;fillcanvas=True&amp;Image=' + image"> 196 <img :src="'/Admin/Public/GetImage.ashx?Width=427&amp;Height=427&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;fillcanvas=True&amp;Image=' + image" 197 alt="@selectedModel.Name" 198 class="img-fluid" 199 itemprop="image"> 200 </picture> 201 @if (!string.IsNullOrEmpty(detailImage1Text) || !string.IsNullOrEmpty(detailImage2Text)) 202 { 203 <figcaption class="alert bg-white"> 204 @if (!string.IsNullOrEmpty(detailImage1Text)) 205 { 206 <template v-if="imageIndex == 1"> 207 @detailImage1Text 208 </template> 209 } 210 @if (!string.IsNullOrEmpty(detailImage2Text)) 211 { 212 <template v-if="imageIndex == 2"> 213 @detailImage2Text 214 </template> 215 } 216 </figcaption> 217 } 218 </figure> 219 </div> 220 </template> 221 <template v-if="images.length == 0"> 222 @{ 223 var pdpImage = "/Files/Images/default.jpg"; 224 } 225 <div class="basic_pim-image"> 226 <picture class="basic_pim-image"> 227 <source media="(max-width:1199.98px)" srcset="/Admin/Public/GetImage.ashx?Width=630&amp;Height=630&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;Image=@(pdpImage)"> 228 <source media="(max-width:991.98px)" srcset="/Admin/Public/GetImage.ashx?Width=530&amp;Height=530&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;Image=@(pdpImage)"> 229 <source media="(max-width:767.98px)" srcset="/Admin/Public/GetImage.ashx?Width=400&amp;Height=400&amp;Compression=85&amp;Crop=5&amp;Format=webp&amp;Quality=85&amp;Image=@(pdpImage)"> 230 <img src="@(pdpImage)" 231 alt="@Translate(basicPimPrefix + "No product picture", "No product picture")" 232 class="img-fluid" 233 itemprop="image"> 234 </picture> 235 </div> 236 </template> 237 </div> 238 <div class="basic_pim-content"> 239 <header> 240 <h1 itemprop="name"> 241 @selectedModel.Name 242 </h1> 243 <p itemprop="category">@(selectedModel?.ShortDescription ?? "")</p> 244 </header> 245 @if (specs.GetAllByKey("BulletPoints").Any()) 246 { 247 <ul class="basic_pim-specs"> 248 @foreach (var bp in specs.GetAllByKey("BulletPoints")) 249 { 250 <li> 251 @bp.Value 252 </li> 253 } 254 </ul> 255 } 256 257 @* 258 <template name="item-stock-state" v-if="stockLocationState!=0"> 259 <p :class="'stock out-of-stock'" v-if="stockLocationState==5"> 260 <link itemprop="availability" href="http://schema.org/SoldOut" /> 261 @Translate(asyncPrefix + "Out Of Stock", "Out Of Stock") 262 </p> 263 <p :class="'stock few-in-stock'" v-if="stockLocationState==7"> 264 <link itemprop="availability" href="http://schema.org/LimitedAvailability" /> 265 @Translate(asyncPrefix + "Few In Stock", "Few In Stock") 266 </p> 267 <p :class="'stock in-stock'" v-if="stockLocationState==8"> 268 <link itemprop="availability" href="http://schema.org/InStock" /> 269 @Translate(asyncPrefix + "In Stock", "In Stock") 270 </p> 271 </template> 272 *@ 273 274 @if (!string.IsNullOrEmpty(marketingMessage) && Pageview.IsCurrentlyB2B()) 275 { 276 <span class="basic_pim-marketing-message">@marketingMessage</span> 277 } 278 279 @if (Pageview.IsAllowedToShop()) 280 { 281 <stock-location-component inline-template not-in-stock-text="@Translate(basicPimPrefix + "not in stock text", "N/A")" :default-stock-units='product.product.stockUnits' :product='product' earliest-harbor-arrival-date="@earliestHarborArrival"> 282 <div class="basic_pim-stocks"> 283 <template v-if="stockUnits.length > 0 && !loading"> 284 <div v-for="unit in stockUnits" class="custom-control custom-radio"> 285 <input type="radio" v-model="selectedVal" name="stocks" :id="unit.stockLocation.name" :value="unit.stockLocation.name" class="custom-control-input" :disabled="unit.quantity <=0 || getStockLocation!=''" :checked="getStockLocation == unit.stockLocation.name"> 286 <label v-if="!@userInTefcoldUserGroup.ToString().ToLower()" :for="unit.stockLocation.name" class="custom-control-label">{{ unit.stockLocation.description }} {{ GetStockAmountString(unit.quantity) }} @Translate("PDP In Stock", "In Stock")</label> 287 <label v-else :for="unit.stockLocation.name" class="custom-control-label">{{ unit.stockLocation.description }} {{ unit.quantity }} @Translate("PDP In Stock", "In Stock")</label> 288 </div> 289 </template> 290 <template v-if="stocksHasNoQuantity"> 291 <div class="custom-control custom-radio">{{earliestHarborArrivalDate}}</div> 292 </template> 293 <template v-else-if="loading"> 294 <span class="spinner-sm-default"></span> 295 </template> 296 297 </div> 298 </stock-location-component> 299 } 300 @if (!Pageview.IsAllowedToShop()) 301 { 302 <buying-component inline-template :initial-product='@selectedMasterProductModel' :selected-product='@selectedMasterProductModel'> 303 <div class="basic_pim-buying"> 304 <ul class="basic_pim-variants" v-if="initialProduct.product.simpleVariants && initialProduct.product.simpleVariants.length > 0"> 305 <li> 306 @{ 307 FieldValueViewModel productImg; 308 FieldValueViewModel productColor; 309 Model.ProductFields.TryGetValue("productVariantColorImage", out productImg); 310 Model.ProductFields.TryGetValue("productVariantValue", out productColor); 311 } 312 <a href="Default.aspx?ID=@Pageview.ID&ProductId=@Model.Id" class="@(Model.Number == selectedModel.Number ? "active" : "")" :style="getImageOrColor('@(productImg?.Value)','@(productColor?.Value)')"></a> 313 </li> 314 315 @if (Model.Variants != null) 316 { 317 foreach (var variant in Model.Variants) 318 { 319 FieldValueViewModel variantImg; 320 FieldValueViewModel variantColor; 321 variant.ProductFields.TryGetValue("productVariantColorImage", out variantImg); 322 variant.ProductFields.TryGetValue("productVariantValue", out variantColor); 323 324 <li> 325 <a href="/Default.aspx?ID=@Pageview.ID&ProductId=@variant.Id&VariantId=@variant.VariantId" class="@(variant.Number == selectedModel.Number ? "active" : "")" :style="getImageOrColor('@(variantImg?.Value)','@(variantColor?.Value)')"></a> 326 </li> 327 328 } 329 } 330 </ul> 331 </div> 332 </buying-component> 333 <p itemprop="sku"> 334 @Translate(basicPimPrefix + "Product number", "Product number"): @selectedModel.Number @(string.IsNullOrEmpty(selectedModel.GetNavItemNumber()) ? "" : "("+Translate("Substition for","Substitution for ")+ $"{selectedModel.GetNavItemNumber()})") 335 </p> 336 <a href="@NORRIQ.Common8.Razor.Navigation.GetUrlByNavigationTag("contact")" class="btn btn-outline-secondary btn-sm mt-3"> 337 @Translate("create account", "Create account") 338 </a> 339 } 340 else 341 { 342 <buying-component inline-template @@discount="onDiscount($event)" :initial-product='@selectedMasterProductModel' :selected-product='@selectedMasterProductModel' :chosen-warranty-code="chosenWarrantyCode" :warranty-info="warrantyInfo"> 343 <div class="basic_pim-buying" itemprop="offers" itemscope="" itemtype="https://schema.org/Offer"> 344 <div class="pdp-energy"> 345 <async-price class-type="asyncprice-pdp" 346 :product='@selectedJsonModel' 347 :default-price="@selectedModel.Price.PriceWithoutVat.ToString(CultureInfo.InvariantCulture)" 348 :only-standard-price="@(Pageview.IsCurrentlyB2C().ToString().ToLower())" 349 list-price="true" 350 :should-emit-warranties="true" 351 language-id="@languageId" 352 standard-customer="@standardCustomer" 353 @@discount="onDiscount($event)"> 354 </async-price> 355 @if (!string.IsNullOrEmpty(energyArrow)) 356 { 357 string name = Regex.Replace(selectedModel.Name.Trim(), "[^A-Za-z0-9_. ]+", ""); 358 //var lang = languageService.GetLanguage(selectedModel.LanguageId)?.Code2; 359 360 //string filePattern = $"[[type]]-{selectedModel.Number}-{name}-{lang}"; 361 <div class="pdp-energy-data"> 362 <a href="@energyLabel" target="_blank" class="pdp-energy-label"> 363 <img src="/Admin/Public/GetImage.ashx?Height=40&amp;Crop=5&amp;Image=@energyArrow" /> 364 </a> 365 <a href="#" v-on:click="getProductReport($event,'@selectedModel.Number', '@name','@selectedModel.LanguageId')" target="_blank" class="pdp-energy-link"> 366 367 @Translate(basicPimPrefix + "product sheet", "Product Sheet") 368 369 </a> 370 </div> 371 } 372 </div> 373 <ul class="basic_pim-variants" v-if="initialProduct.product.simpleVariants && initialProduct.product.simpleVariants.length > 0"> 374 <li> 375 @{ 376 FieldValueViewModel productImg; 377 FieldValueViewModel productColor; 378 Model.ProductFields.TryGetValue("productVariantColorImage", out productImg); 379 Model.ProductFields.TryGetValue("productVariantValue", out productColor); 380 } 381 <a href="Default.aspx?ID=@Pageview.ID&ProductId=@Model.Id" class="@(Model.Number == selectedModel.Number ? "active" : "")" :style="getImageOrColor('@(productImg?.Value)','@(productColor?.Value)')"></a> 382 </li> 383 384 @if (Model.Variants != null) 385 { 386 foreach (var variant in Model.Variants) 387 { 388 FieldValueViewModel variantImg; 389 FieldValueViewModel variantColor; 390 variant.ProductFields.TryGetValue("productVariantColorImage", out variantImg); 391 variant.ProductFields.TryGetValue("productVariantValue", out variantColor); 392 393 <li> 394 <a href="/Default.aspx?ID=@Pageview.ID&ProductId=@variant.Id&VariantId=@variant.VariantId" class="@(variant.Number == selectedModel.Number ? "active" : "")" :style="getImageOrColor('@(variantImg?.Value)','@(variantColor?.Value)')"></a> 395 </li> 396 397 } 398 } 399 </ul> 400 <div class="pdp-btn-group"> 401 <add-to-basket-simple class="addtobasketsimple-pdp" 402 :product='@selectedJsonModel' 403 :price-without-vat="currentItemPriceWithoutVat" 404 button-class="btn btn-primary" 405 :group-warranty-code="chosenWarrantyCode" 406 :warranty-info="warrantyInfo" 407 :unit-of-measure="'PCS'" 408 language-id="@languageId" 409 :only-spare-parts="@((Pageview.User.OnlySpareParts() && !isSparePart).ToString().ToLower())" 410 :is-marketing-user="@(Pageview.User.OnlyView().ToString().ToLower())" 411 :ishvasuser="@(Pageview.User.IsHVACUser().ToString().ToLower())" 412 standard-customer="@standardCustomer"> 413 </add-to-basket-simple> 414 @if (Pageview.IsCurrentlyB2B()) 415 { 416 <favorite-lists :ui-error-message-translation="'@Translate(basicPimPrefix + " Error while retrieving favorite list", "Error while retrieving favorite list")'" :is-favorite-mode="@NORRIQ.Common8.Razor.Navigation.GetPageIdByNavigationTag("favorites") == @Pageview.ID" :product='@selectedJsonModel'></favorite-lists> 417 } 418 <add-to-compare :product-number="selectedProduct.product.number"></add-to-compare> 419 </div> 420 </div> 421 </buying-component> 422 <p itemprop="sku"> 423 @Translate(basicPimPrefix + "Product number", "Product number"): @selectedModel.Number @(string.IsNullOrEmpty(selectedModel.GetNavItemNumber()) ? "" : "(" + Translate("Substition for", "Substitution for ") + $"{selectedModel.GetNavItemNumber()})") 424 425 </p> 426 if (isCZShop && !string.IsNullOrEmpty(specs.GetByKey("PhdCode")?.Value)) 427 { 428 <p class="phdCode">@Translate(basicPimPrefix + "PhdCode", "PhdCode") @(specs.GetByKey("PhdCode")?.Value)</p> 429 } 430 if (!isSparePart && (Pageview.IsCurrentlyB2B() && Pageview.User.AllowWarranty() || Pageview.IsCurrentlyB2C())) 431 { 432 <div class="basic_pim-warranty" v-if="@((!isSparePart).ToString().ToLower()) && warranties?.length>0"> 433 <div v-for="(warranty,index) in warranties" class="custom-control custom-radio"> 434 <input type="radio" name="warranty" :id="'warranty' + index" v-model="chosenWarranty" :value="warranty" class="custom-control-input" /> 435 <label :for="'warranty' + index" class="custom-control-label"> 436 {{getDescription(warranty.itemNo) +' - ' + product.product.defaultPrice.currencyCode + ' ' + warranty.price.priceWithoutVat + ',-'}} 437 </label> 438 </div> 439 @*<template v-if="warrantyLoading"> 440 <div class="basic_listview-loader text-center">http://localhost:55277/http://localhost:55277/ 441 <span class="spinner-lg-default"></span> 442 </div> 443 </template>*@ 444 </div> 445 } 446 } 447 @if (isSparePart && specs.GetByGroup("Specifications").Any()) 448 { 449 <ul class="list-unstyled spare-specs mt-3"> 450 @foreach (var group in specs.GetByGroup("Specifications").Where(x => x.Key != "EcoIcon").OrderBy(x => x.Group2Order).ThenBy(x => x.ValueSortOrder).GroupBy(x => x.Group2)) 451 { 452 453 454 foreach (var spec in group) 455 { 456 457 <li> 458 459 <span>@spec.Caption</span> 460 <span> 461 @spec.Value 462 @spec.Unit 463 </span> 464 </li> 465 } 466 467 } 468 </ul> 469 } 470 </div> 471 </section> 472 @if (!isSparePart) 473 { 474 <section class="basic_pim-collapse"> 475 @if (!string.IsNullOrEmpty(selectedModel.LongDescription) || !string.IsNullOrEmpty(shortSellingText) || specs.GetByGroup("Specifications").Any()) 476 { 477 <div class="basic_pim-col" visible id="specs1"> 478 @if (!string.IsNullOrEmpty(selectedModel.LongDescription) || !string.IsNullOrEmpty(shortSellingText)) 479 { 480 <button class="btn-collapse" id="description" v-b-toggle.long-description> 481 @Translate(basicPimPrefix + "product description", "Product description") 482 </button> 483 <b-collapse id="long-description" visible appear accordion="specs1"> 484 <template> 485 <div class="body-collapse"> 486 @if (!string.IsNullOrEmpty(shortSellingText)) 487 { 488 <p>@shortSellingText</p> 489 } 490 @if (!string.IsNullOrEmpty(selectedModel.LongDescription)) 491 { 492 <p>@selectedModel.LongDescription</p> 493 } 494 </div> 495 </template> 496 </b-collapse> 497 } 498 @if (specs.GetByGroup("Specifications").Any()) 499 { 500 <button class="btn-collapse" id="specs" v-b-toggle.specs-list> 501 @Translate(basicPimPrefix + "Product Specs", "Product Specifications") 502 </button> 503 <b-collapse id="specs-list" accordion="specs1"> 504 <template> 505 <div class="body-collapse full"> 506 <table class="table table-specs"> 507 508 <tbody> 509 510 @foreach (var group in specs.GetByGroup("Specifications").Where(x => x.Key != "EcoIcon").OrderBy(x => x.Group2Order).ThenBy(x => x.ValueSortOrder).GroupBy(x => x.Group2)) 511 { 512 513 <tr> 514 <th>@group.Key</th> 515 <th></th> 516 </tr> 517 foreach (var spec in group) 518 { 519 if (spec.Key == "EnergyArrow") 520 { 521 continue; 522 } 523 if (spec.Key == "EnergyArrowText" && !string.IsNullOrEmpty(energyArrow)) 524 { 525 <tr> 526 <td> 527 @spec.Caption 528 </td> 529 <td valign="middle" style="vertical-align:middle;"> 530 <a href="@energyLabel" target="_blank" style="display:flex;"> 531 <img src="/Admin/Public/GetImage.ashx?Height=18&amp;Crop=5&amp;Image=@energyArrow" /> 532 </a> 533 </td> 534 </tr> 535 } 536 else 537 { 538 <tr> 539 <td> 540 @spec.Caption 541 </td> 542 <td valign="middle" style="vertical-align:middle;"> 543 @spec.Value 544 @spec.Unit 545 </td> 546 </tr> 547 } 548 } 549 } 550 <tr> 551 <th></th> 552 <th></th> 553 </tr> 554 </tbody> 555 </table> 556 557 </div> 558 </template> 559 </b-collapse> 560 } 561 </div> 562 } 563 <div class="basic_pim-col" id="specs2"> 564 565 <button class="btn-collapse" id="download" v-b-toggle.download-documents> 566 @Translate(basicPimPrefix + "downloads", "Downloads") 567 </button> 568 <b-collapse id="download-documents" accordion="specs2"> 569 <template> 570 <div class="body-collapse"> 571 <ul class="basic_pim-downloads"> 572 @{ 573 574 string name = Regex.Replace(selectedModel.Name.Trim(), "[^A-Za-z0-9_. ]+", ""); 575 var lang = languageService.GetLanguage(selectedModel.LanguageId)?.Code2; 576 577 } 578 @{ string filePattern = $"[[type]]-{selectedModel.Number}-{name}-{lang}"; } 579 <li> 580 <a href="#" v-on:click="getProductReport($event,'@selectedModel.Number', '@name','@lang')" target="_blank"> 581 <svg> 582 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 583 </svg> 584 @Translate(basicPimPrefix + "product sheet", "Product Sheet") 585 </a> 586 </li> 587 @if (!string.IsNullOrEmpty(@specs.GetByKey("Usermanual").Value)) 588 { 589 <li> 590 <a href="#" v-on:click="downloadProductFile($event,'@name','@specs.GetByKey("Usermanual").Value','@filePattern.Replace("[[type]]","Usermanual")','Usermanual')" target="_blank"> 591 <svg> 592 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 593 </svg> 594 @Translate(basicPimPrefix + "User manual", "User manual") 595 </a> 596 </li> 597 } 598 599 <template v-if="sparePartReportExist"> 600 <li> 601 <a href="#" v-on:click="getProductReport($event,'@selectedModel.Number', '@name','@lang', 'true')" target="_blank"> 602 <svg> 603 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 604 </svg> 605 @Translate(basicPimPrefix + "spare part list", "Spare part list") 606 </a> 607 </li> 608 </template> 609 @if (!string.IsNullOrEmpty(energyLabel)) 610 { 611 <li> 612 <a href="#" v-on:click="downloadProductFile($event,'@name','@energyLabel','@filePattern.Replace("[[type]]","EnergyClassification")','EnergyClassification')" target="_blank"> 613 <svg> 614 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 615 </svg> 616 @Translate(basicPimPrefix + "Energy Classification", "Energy Classification") 617 </a> 618 </li> 619 } 620 @if (Pageview.User != null) 621 { 622 if (!string.IsNullOrEmpty(specs.GetByKey("WiringDiagrams").Value)) 623 { 624 <li> 625 <a href="#" v-on:click="downloadProductFile($event,'@name','@specs.GetByKey("WiringDiagrams").Value','@filePattern.Replace("[[type]]","WiringDiagrams")','WiringDiagrams')" target="_blank"> 626 <svg> 627 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 628 </svg> 629 @Translate(basicPimPrefix + "Wiring diagram", "Wiring diagram") 630 </a> 631 </li> 632 } 633 if (!string.IsNullOrEmpty(specs.GetByKey("Drawings").Value)) 634 { 635 <li> 636 <a href="#" v-on:click="downloadProductFile($event,'@name','@specs.GetByKey("Drawings").Value','@filePattern.Replace("[[type]]","Drawings")','Drawings')" target="_blank"> 637 <svg> 638 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 639 </svg> 640 @Translate(basicPimPrefix + "Drawings", "Drawings") 641 </a> 642 </li> 643 } 644 if (!string.IsNullOrEmpty(specs.GetByKey("BrandingFile").Value)) 645 { 646 <li> 647 <a href="#" v-on:click="downloadProductFile($event,'@name','@specs.GetByKey("BrandingFile").Value','@filePattern.Replace("[[type]]","Branding")','BrandingFile')" target="_blank"> 648 <svg> 649 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 650 </svg> 651 @Translate(basicPimPrefix + "Branding File", "Branding drawing") 652 </a> 653 </li> 654 } 655 if (!string.IsNullOrEmpty(specs.GetByKey("QuickGuide").Value)) 656 { 657 <li> 658 <a href="#" v-on:click="downloadProductFile($event,'@name','@specs.GetByKey("QuickGuide").Value','@filePattern.Replace("[[type]]","QuickGuide")','QuickGuide')" target="_blank"> 659 <svg> 660 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 661 </svg> 662 @Translate(basicPimPrefix + "QuickGuide", "Quick Guide") 663 </a> 664 </li> 665 } 666 if (!string.IsNullOrEmpty(specs.GetByKey("AssemblyGuide").Value)) 667 { 668 <li> 669 <a href="#" v-on:click="downloadProductFile($event,'@name','@specs.GetByKey("AssemblyGuide").Value','@filePattern.Replace("[[type]]","AssemblyGuide")','AssemblyGuide')" target="_blank"> 670 <svg> 671 <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/files/dist/icons/icons.svg#download"></use> 672 </svg> 673 @Translate(basicPimPrefix + "AssemblyGuide", "Assembly Guide") 674 </a> 675 </li> 676 } 677 } 678 679 680 681 </ul> 682 </div> 683 684 </template> 685 </b-collapse> 686 687 @if (!isSparePart) 688 { 689 <product-spare-parts-view :page-view-id="@Pageview.ID" 690 :language-id="'@selectedModel.LanguageId'" 691 :product-number="@selectedModel.Number" 692 :exploded-drawing="'@(specs.GetByKey("ExplodedDrawing")?.Value)'" 693 :spare-part-report-exist="sparePartReportExist" 694 product-name="@name" 695 language-code="@lang" 696 :sparepart-location="product.product.defaultStockLocationName"> 697 </product-spare-parts-view> 698 } 699 700 </div> 701 </section> 702 } 703 @if (relatedProducts != null && relatedProducts.RelatedProducts.Any()) 704 { 705 <section class="basic_related"> 706 <template> 707 <header class="basic_related-header" id="related-header"> 708 <h2 class="text-center"> 709 @Translate(basicPimPrefix + "Related Products", "Related Products") 710 </h2> 711 </header> 712 <slick ref="slick" 713 class="basic_related-grid" 714 :options="slickRelatedOptions"> 715 @foreach (var relProduct in relatedProducts.RelatedProducts) 716 { 717 718 //var relProduct = productService.GetProductById(rel.Id, rel.VariantId, rel.LanguageId); 719 var converted = new AsyncProductWithSpecification(new SimpleProduct(relProduct.ToViewModel(), null, stockInformation, inspirationGroupIds, true)); 720 var relProductJson = JsonConvert.SerializeObject(converted, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); 721 relProductJson = relProductJson.Replace("'", "&#39;"); 722 <article class="basic_related-product"> 723 <a href="/Default.aspx?ID=@NORRIQ.Common8.Razor.Navigation.GetPageIdByNavigationTag("plp")&amp;ProductID=@relProduct.Id" class="basic_related-url"> 724 <figure style="min-height: 1px;"> 725 @{ 726 var image = string.IsNullOrEmpty(relProduct.ImageLarge) ? "/Files/Images/default.jpg" : "/Files/" + relProduct.ImageLarge; 727 } 728 <img src="/Admin/Public/GetImage.ashx?Width=427&amp;Height=427&amp;Compression=85&amp;Crop=5&amp;fillcanvas=true&amp;Image=@image" 729 alt="@relProduct.Name" 730 class="img-fluid" /> 731 </figure> 732 <header> 733 <h1>@relProduct.Name</h1> 734 <p class="basic_related-category">@relProduct.ShortDescription</p> 735 <p class="basic_related-sku">@Translate(basicPimPrefix + "Product Number", "Product Number") @relProduct.Number</p> 736 </header> 737 </a> 738 739 @if (Pageview.IsCurrentlyB2B()) 740 { 741 742 <buying-component @@discount="onDiscount($event)" inline-template :initial-product='@relProductJson' :selected-product='@relProductJson'> 743 <footer> 744 <async-price class-type="asyncprice-plp" 745 :default-price="@relProduct.Price.PriceWithoutVAT.ToString(CultureInfo.InvariantCulture)" 746 :product='@relProductJson' 747 unit-of-measure="PCS" 748 only-price="true" 749 should-emit-warranties="true" 750 :only-standard-price="@(Pageview.IsCurrentlyB2C().ToString().ToLower())" 751 standard-customer="@standardCustomer" 752 @@discount="onDiscount($event)"> 753 </async-price> 754 <add-to-basket-simple :product='@relProductJson' 755 button-class="btn btn-primary" 756 :unit-of-measure="'PCS'" 757 class="addtobasketsimple-plp ml-auto" 758 :price-without-vat="currentItemPriceWithoutVat" 759 language-id="@languageId" 760 :only-spare-parts="@((Pageview.User.OnlySpareParts() && !isSparePart).ToString().ToLower())" 761 :is-marketing-user="@(Pageview.User.OnlyView().ToString().ToLower())" 762 :ishvasuser="@(Pageview.User.IsHVACUser().ToString().ToLower())" 763 standard-customer="@standardCustomer" 764 @@discount="onDiscount($event)"> 765 </add-to-basket-simple> 766 </footer> 767 </buying-component> 768 769 } 770 else 771 { 772 <footer> 773 <async-price class-type="asyncprice-plp" 774 :default-price="@relProduct.Price.PriceWithoutVAT.ToString(CultureInfo.InvariantCulture)" 775 :product='@relProductJson' 776 only-price="true" 777 :only-standard-price="@(Pageview.IsCurrentlyB2C().ToString().ToLower())" 778 standard-customer="@standardCustomer" /> 779 780 <add-to-basket-simple :product='@relProductJson' 781 :unit-of-measure="'PCS'" 782 button-class="btn btn-primary" 783 class="addtobasketsimple-plp ml-auto" 784 language-id="@languageId" 785 :price-without-vat="@relProduct.Price.PriceWithoutVAT.ToString(CultureInfo.InvariantCulture)" 786 :only-spare-parts="@((Pageview.User.OnlySpareParts() && !isSparePart).ToString().ToLower())" 787 :is-marketing-user="@(Pageview.User.OnlyView().ToString().ToLower())" 788 :ishvasuser="@(Pageview.User.IsHVACUser().ToString().ToLower())" 789 standard-customer="@standardCustomer"> 790 </add-to-basket-simple> 791 </footer> 792 } 793 </article> 794 } 795 </slick> 796 </template> 797 </section> 798 } 799 @if (accessories != null && accessories.Any()) 800 { 801 802 <section class="basic_related"> 803 <template> 804 <header class="basic_related-header" id="accessories-header"> 805 <h2 class="text-center"> 806 @Translate(basicPimPrefix + "Accessories", "Accessories") 807 </h2> 808 </header> 809 <slick ref="slick" 810 class="basic_related-grid" 811 :options="slickAccessoriesOptions"> 812 @foreach (var accessory in accessories) 813 { 814 var converted = new AsyncProductWithSpecification(new SimpleProduct(accessory.ToViewModel(), null, stockInformation, inspirationGroupIds, true)); 815 var accessoryJson = JsonConvert.SerializeObject(converted, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); 816 accessoryJson = accessoryJson.Replace("'", "&#39;"); 817 <article class="basic_related-product"> 818 <a href="/Default.aspx?ID=@NORRIQ.Common8.Razor.Navigation.GetPageIdByNavigationTag("plp")&amp;ProductID=@accessory.Id" class="basic_related-url"> 819 <figure> 820 @{ 821 var image = string.IsNullOrEmpty(accessory.ImageLarge) ? "/Files/Images/default.jpg" : "/Files/" + accessory.ImageLarge; 822 } 823 <img src="/Admin/Public/GetImage.ashx?Width=427&amp;Height=427&amp;Compression=85&amp;Crop=5&amp;Image=@image" 824 alt="@accessory.Name" 825 class="img-fluid" /> 826 </figure> 827 <header> 828 <h1>@accessory.Name</h1> 829 <p class="basic_related-category">@accessory.ShortDescription</p> 830 <p class="basic_related-sku">@Translate(basicPimPrefix + "Product Number", "Product Number") @accessory.Number</p> 831 </header> 832 </a> 833 @if (Pageview.IsCurrentlyB2B()) 834 { 835 836 <buying-component inline-template :initial-product='@accessoryJson'> 837 <footer> 838 <async-price class-type="asyncprice-plp" 839 :default-price="@accessory.Price.PriceWithoutVAT.ToString(CultureInfo.InvariantCulture)" 840 :product='@accessoryJson' 841 only-price="true" 842 standard-customer="@standardCustomer"> 843 </async-price> 844 <add-to-basket-simple :product='@accessoryJson' 845 button-class="btn btn-primary" 846 class="addtobasketsimple-plp ml-auto" 847 language-id="@languageId" 848 :price-without-vat="@accessory.Price.PriceWithoutVAT.ToString(CultureInfo.InvariantCulture)" 849 :only-spare-parts="@((Pageview.User.OnlySpareParts() && !isSparePart).ToString().ToLower())" 850 :is-marketing-user="@(Pageview.User.OnlyView().ToString().ToLower())" 851 :ishvasuser="@(Pageview.User.IsHVACUser().ToString().ToLower())" 852 standard-customer="@standardCustomer"> 853 </add-to-basket-simple> 854 </footer> 855 </buying-component> 856 857 } 858 else 859 { 860 <footer> 861 <div class="asyncprice-plp"> 862 <div class="price-withoutvat"> 863 <span class="price-label">@Translate(basicPimPrefix + "price without vat", "Price without VAT")</span> 864 <p class="price"> 865 <span class="unit-price"> 866 @accessory.Price.PriceWithoutVATFormattedNoSymbol @accessory.Price.Currency.Code 867 </span> 868 </p> 869 </div> 870 </div> 871 <add-to-basket-simple :product='@accessoryJson' 872 button-class="btn btn-primary" 873 class="addtobasketsimple-plp ml-auto" 874 language-id="@languageId" 875 :price-without-vat="@accessory.Price.PriceWithoutVAT.ToString(CultureInfo.InvariantCulture)" 876 :only-spare-parts="@((Pageview.User.OnlySpareParts() && !isSparePart).ToString().ToLower())" 877 :is-marketing-user="@(Pageview.User.OnlyView().ToString().ToLower())" 878 :ishvasuser="@(Pageview.User.IsHVACUser().ToString().ToLower())" 879 standard-customer="@standardCustomer"> 880 </add-to-basket-simple> 881 </footer> 882 } 883 </article> 884 } 885 </slick> 886 </template> 887 </section> 888 } 889 890 </div> 891 </product-details-simple-pim> 892