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("'", "'");
45 selectedJsonModel = selectedJsonModel.Replace("'", "'");
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&Compression=85&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&Compression=85&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&Height=50&Compression=85&Crop=5&Format=webp&Quality=85&fillcanvas=True&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&Height=400&Compression=85&Crop=5&Format=webp&Quality=85&fillcanvas=True&Image=' + image">
160 <source media="(max-width:991.98px)" :srcset="'/Admin/Public/GetImage.ashx?Width=530&Height=530&Compression=85&Crop=5&Format=webp&Quality=85&fillcanvas=True&Image=' + image">
161 <img :src="'/Admin/Public/GetImage.ashx?Width=530&Height=530&Compression=85&Crop=5&Format=webp&Quality=85&fillcanvas=True&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&Height=250&Compression=85&Crop=5&Format=webp&Quality=85&fillcanvas=True&Image=' + image">
195 <source media="(max-width:991.98px)" :srcset="'/Admin/Public/GetImage.ashx?Width=400&Height=350&Compression=85&Crop=5&Format=webp&Quality=85&fillcanvas=True&Image=' + image">
196 <img :src="'/Admin/Public/GetImage.ashx?Width=427&Height=427&Compression=85&Crop=5&Format=webp&Quality=85&fillcanvas=True&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&Height=630&Compression=85&Crop=5&Format=webp&Quality=85&Image=@(pdpImage)">
228 <source media="(max-width:991.98px)" srcset="/Admin/Public/GetImage.ashx?Width=530&Height=530&Compression=85&Crop=5&Format=webp&Quality=85&Image=@(pdpImage)">
229 <source media="(max-width:767.98px)" srcset="/Admin/Public/GetImage.ashx?Width=400&Height=400&Compression=85&Crop=5&Format=webp&Quality=85&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&Crop=5&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&Crop=5&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("'", "'");
722 <article class="basic_related-product">
723 <a href="/Default.aspx?ID=@NORRIQ.Common8.Razor.Navigation.GetPageIdByNavigationTag("plp")&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&Height=427&Compression=85&Crop=5&fillcanvas=true&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("'", "'");
817 <article class="basic_related-product">
818 <a href="/Default.aspx?ID=@NORRIQ.Common8.Razor.Navigation.GetPageIdByNavigationTag("plp")&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&Height=427&Compression=85&Crop=5&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