Error executing template "Designs/Rapido/TemplateRenderer/Custom__CpqPrintPdf.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at Dna.Winnebago.Cpq.Cipher.StringToByteArray(String hex)
   at Dna.Winnebago.Cpq.Cipher.DecryptBuildId(String text)
   at CompiledRazorTemplates.Dynamic.RazorEngine_6748dd02ecca41dc99d6198e60da0888.Execute() in e:\WGOIISShare\WGO_Prod\Files\Templates\Designs\Rapido\TemplateRenderer\Custom__CpqPrintPdf.cshtml:line 324
   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 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 2 @using System.Collections.Generic 3 @using System.Linq 4 @using System.Text.RegularExpressions 5 @using System.Net 6 @using System.Web 7 @using Dynamicweb.Rapido.Blocks.Components.General 8 @using Dynamicweb.Content.Items; 9 @using Dynamicweb.Core; 10 @using HtmlAgilityPack 11 @using Dynamicweb.Ecommerce; 12 @using System.IO; 13 @using Dynamicweb.Ecommerce.Orders 14 @using Dynamicweb.Ecommerce.Products; 15 16 17 @using Dna.Winnebago.Cpq 18 @using Dynamicweb.Content.Items 19 @using Dynamicweb.Ecommerce 20 @using Dynamicweb.Ecommerce.Common 21 @using Dynamicweb.Ecommerce.Orders 22 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 23 24 @functions 25 { 26 public class SelectedOption 27 { 28 public string Title { get; set; } 29 public string OptionCode { get; set; } 30 public bool IsVariantOption => VariantOptionCodes.Any(); 31 public string[] VariantOptionCodes { get; set; } = {}; 32 public string ShortDescription { get; set; } 33 public string LongDescription { get; set; } 34 public string ThumbnailImage { get; set; } 35 public string ImagePath { get; set; } 36 public double Price { get; set; } 37 public bool IsReverse { get; set; } 38 public bool IsMandatory { get; set; } 39 } 40 41 public class Floorplan 42 { 43 public string Name { get; set; } 44 public string ImagePath { get; set; } 45 public string Summary { get; set; } 46 public List<SelectedOption> Options { get; set; } 47 public double Price { get; set; } 48 public double TotalPrice { get; set; } 49 } 50 51 public class QuoteSummary 52 { 53 public string BuildId { get; set; } 54 public List<string> SelectedOptionCodes { get; set; } 55 public List<string> LeadsOptionCodes { get; set; } 56 public List<string> LeadsOptionTitles { get; set; } 57 public string BannerImagePath { get; set; } 58 public string RvImagePath { get; set; } 59 public string ModelName { get; set; } 60 public string ModelNumber { get; set; } 61 public string MainGroupName { get; set; } 62 public string ModelYear { get; set; } 63 public Floorplan Floorplan { get; set; } 64 public SelectedOption Exterior { get; set; } 65 public SelectedOption Interior { get; set; } 66 public List<SelectedOption> SelectedOptions { get; set; } 67 public double AddOnOptionsPrice { get; set; } 68 public double TotalPrice { get; set; } 69 public string TotalPriceFormatted { get; set; } 70 public string BrochureLink { get; set; } 71 } 72 73 protected IEnumerable<SelectedOption> GetSelectedOptionsFromOrderLines(IEnumerable<OrderLine> orderLines, Func<OrderLine, Item> getItemFn) 74 { 75 return orderLines.Select(orderLine => new 76 { 77 orderLine, 78 item = getItemFn(orderLine) 79 }) 80 .Where(x => x.item != null) 81 .Select(orderLineItemCombo => CombineOption(orderLineItemCombo.orderLine, orderLineItemCombo.item)); 82 } 83 84 protected Func<Item, bool> GetItemByModelFn(string modelName) => item => modelName.Equals(item["ModelName"]?.ToString(), StringComparison.InvariantCultureIgnoreCase); 85 protected Func<Item, bool> GetItemByFloorplanFn(string floorplanName) => item => item["Floorplan"]?.ToString() == floorplanName; 86 87 protected IEnumerable<Item> GetItemsByModel(IEnumerable<Item> items, string modelName) 88 { 89 return items.Where(GetItemByModelFn(modelName)); 90 } 91 92 protected IEnumerable<Item> GetItemsByModelAndFloorplan(IEnumerable<Item> items, string modelName, string floorplanName) 93 { 94 return GetItemsByModel(items, modelName).Where(GetItemByFloorplanFn(floorplanName)); 95 } 96 97 protected bool IsReverseItem(Item item) 98 { 99 return bool.TryParse(item["IsReverse"]?.ToString(), out var isReverse) && isReverse; 100 } 101 102 protected IEnumerable<Item> GetReverseItemsByModelAndFloorplan(IEnumerable<Item> items, string modelName, string floorplanName) 103 { 104 var itemsList = items.ToList(); 105 return GetItemsByModelAndFloorplan(itemsList, modelName, floorplanName) 106 .Concat(GetItemsByModel(itemsList, modelName)) 107 .Where(IsReverseItem) 108 .GroupBy(item => item["OptionCode"]?.ToString(), item => item, (optionCode, combinedItems) => 109 { 110 var floorplanItem = combinedItems.FirstOrDefault(item => item["Floorplan"]?.ToString() == floorplanName); 111 var modelItem = combinedItems.FirstOrDefault(item => string.IsNullOrWhiteSpace(item["Floorplan"]?.ToString())); 112 return floorplanItem ?? modelItem; 113 }) 114 .Where(item => item != null); 115 } 116 117 protected string[] GetVariantOptionCodes(Item item) => GetVariantOptionCodes(item["VariantOptionCodes"]?.ToString() ?? ""); 118 public string[] GetVariantOptionCodes(string optionCode) => optionCode.Split('-').ToArray(); 119 120 protected bool IsVariantOptionItem(Item item) => !string.IsNullOrWhiteSpace(item["VariantOptionCodes"]?.ToString()); 121 122 protected IEnumerable<Item> GetItemsByVariantOption(IEnumerable<Item> items, IList<string> allSelectedOptionCodes) 123 { 124 return items.Where(IsVariantOptionItem) 125 .Where(item => GetVariantOptionCodes(item).All(allSelectedOptionCodes.Contains)); 126 } 127 128 protected Item GetItemFromOrderLine(OrderLine orderLine, IEnumerable<Item> items, string modelName, string floorplanName, IList<string> selectedOptionCodes) 129 { 130 var itemsList = items.ToList(); 131 var modelItems = GetItemsByModel(itemsList, modelName) 132 .Where(item => item["OptionCode"]?.ToString() == orderLine.ProductNumber) 133 .ToList(); 134 135 var variantOptionItems = GetItemsByVariantOption(modelItems, selectedOptionCodes).ToList(); 136 var selectedItems = variantOptionItems.Any() ? variantOptionItems : modelItems; 137 return selectedItems.FirstOrDefault(GetItemByFloorplanFn(floorplanName)) ?? selectedItems.FirstOrDefault(item => string.IsNullOrWhiteSpace(item["Floorplan"]?.ToString())); 138 } 139 140 protected SelectedOption CombineOption(OrderLine orderLine, Item item) 141 { 142 var imageListRelationId = Convert.ToInt32(item["Images"]); 143 var images = ItemList.GetItemListById(imageListRelationId)?.Relations.Cast<Item>().ToList(); 144 var compositeImage = images?.FirstOrDefault(x => x["Name"]?.ToString() == "Composite"); 145 var overlayImage = images?.FirstOrDefault(x => x["Name"]?.ToString() == "Overlay"); 146 var mainImage = images?.FirstOrDefault(x => x["Name"]?.ToString() == "Main"); 147 var isReverseItem = IsReverseItem(item); 148 var price = orderLine?.Price.Price ?? Convert.ToDouble(int.TryParse(item["Price"]?.ToString(), out var itemPrice) ? itemPrice : 0) 149 * (isReverseItem ? -1 : 1); 150 151 var variantOptionCodes = item["VariantOptionCodes"]?.ToString().Split('-') ?? new string[] {}; 152 var optionCode = $"{item["OptionCode"]}{(variantOptionCodes.Any() ? "-" + string.Join("-", variantOptionCodes) : string.Empty)}"; 153 154 return new SelectedOption 155 { 156 Title = string.IsNullOrWhiteSpace(item["Title"]?.ToString()) ? orderLine?.ProductName : item["Title"]?.ToString(), 157 OptionCode = optionCode, 158 VariantOptionCodes = variantOptionCodes, 159 ShortDescription = item["Subtitle"]?.ToString(), 160 LongDescription = item["LongDescription"]?.ToString(), 161 ThumbnailImage = overlayImage != null ? overlayImage["File"].ToString() : item["ThumbnailImage"]?.ToString(), 162 ImagePath = compositeImage != null ? compositeImage["File"].ToString() : mainImage != null ? mainImage["File"].ToString() : item["ThumbnailImage"]?.ToString(), 163 Price = price, 164 IsReverse = isReverseItem, 165 IsMandatory = bool.TryParse(item["IsMandatory"]?.ToString(), out var success) && success, 166 }; 167 } 168 169 public string FormatPrice(int price) 170 { 171 return FormatPrice(Convert.ToDouble(price)); 172 } 173 174 public string FormatPrice(double price) 175 { 176 return string.Format("{0:c}", price); 177 } 178 179 public QuoteSummary GetQuoteSummary(string quoteId, bool fullSummary = false) 180 { 181 if (string.IsNullOrWhiteSpace(quoteId)) 182 { 183 return new QuoteSummary(); 184 } 185 186 var order = Services.Orders.GetById(quoteId); 187 if (order == null) 188 { 189 return new QuoteSummary(); 190 } 191 192 var orderLines = order.OrderLines; 193 var selectedOptionCodes = orderLines 194 .Where(orderLine => orderLine.Product.Groups.All(group => group.Name != "CPQ")) 195 .Select(orderLine => orderLine.ProductNumber) 196 .ToList(); 197 198 var floorplanOrderLine = orderLines.FirstOrDefault(orderLine => orderLine.Product.Groups.Any(group => group.Name == "CPQ")); 199 var docRepositoryArea = Dynamicweb.Services.Areas.GetAreas().FirstOrDefault(x => x.Name == "Documentation Repository"); 200 201 var parentProduct = Services.Products.GetProductById(floorplanOrderLine.ProductId, floorplanOrderLine.ProductId, Context.LanguageID); 202 var modelName = parentProduct?.Name; 203 var modelYear = parentProduct?.GetProductFieldValue("Year").ToString(); 204 var floorplanName = floorplanOrderLine?.Product.Name; 205 206 var buildId = Cipher.EncryptQuoteId(quoteId); 207 208 if (!fullSummary) 209 { 210 return new QuoteSummary 211 { 212 BuildId = buildId, 213 SelectedOptionCodes = selectedOptionCodes, 214 ModelName = modelName, 215 ModelYear = modelYear, 216 Floorplan = new Floorplan 217 { 218 Name = floorplanName 219 } 220 }; 221 } 222 223 var page = Dynamicweb.Services.Pages.GetPageByNavigationTag(docRepositoryArea == null ? 22 : docRepositoryArea.ID, $"CpqFloorplanOptions_{String.Concat(modelName.Where(c => !Char.IsWhiteSpace(c)))}{modelYear}"); 224 var pageId = page.ID; 225 var paragraphs = Dynamicweb.Services.Paragraphs.GetParagraphsByPageId(pageId); 226 var items = paragraphs.Select(x => x.Item).ToList(); 227 var nonSelectedOptionGroupNames = new List<string> {"Exterior", "Interior", "FloorplanOptions", "Base", "Delivery"}; 228 var nonSelectedReverseOptions = GetReverseItemsByModelAndFloorplan(items, modelName, floorplanName) 229 .Where(item => !selectedOptionCodes.Contains(item["OptionCode"]?.ToString())) 230 .Select(item => CombineOption(null, item)) 231 .ToList(); 232 var selectedOptions = orderLines 233 .Where(orderLine => orderLine.Product.Groups.All(group => !nonSelectedOptionGroupNames.Contains(group.Name))) 234 .Select(orderLine => new 235 { 236 orderLine, 237 item = GetItemFromOrderLine(orderLine, items, modelName, floorplanName, selectedOptionCodes) 238 }) 239 .Where(x => x.item != null) 240 .Select(orderLineItemCombo => CombineOption(orderLineItemCombo.orderLine, orderLineItemCombo.item)) 241 .ToList(); 242 243 var interiorOption = GetSelectedOptionsFromOrderLines( 244 orderLines.Where(orderLine => orderLine.Product.Groups.Any(group => group.Name == "Interior")), 245 orderLine => GetItemFromOrderLine(orderLine, items, modelName, floorplanName, selectedOptionCodes)) 246 .FirstOrDefault(); 247 248 var exteriorOption = GetSelectedOptionsFromOrderLines( 249 orderLines.Where(orderLine => orderLine.Product.Groups.Any(group => group.Name == "Exterior")), 250 orderLine => GetItemFromOrderLine(orderLine, items, modelName, floorplanName, selectedOptionCodes)) 251 .FirstOrDefault(); 252 253 var floorplanSelectedOptions = GetSelectedOptionsFromOrderLines( 254 orderLines.Where(orderLine => orderLine.Product.Groups.Any(group => group.Name == "FloorplanOptions")), 255 orderLine => GetItemFromOrderLine(orderLine, items, modelName, floorplanName, selectedOptionCodes)) 256 .ToList(); 257 258 var basePriceWithReverse = floorplanOrderLine.Price.Price 259 - nonSelectedReverseOptions.Sum(x => x.Price) 260 + selectedOptions.Where(x => x.IsReverse).Sum(x => x.Price) 261 + selectedOptions.Where(x => x.IsMandatory).Sum(x => x.Price); 262 var floorplan = new Floorplan 263 { 264 Name = floorplanName, 265 ImagePath = "/Files/Images/" + floorplanOrderLine?.Product.GetCategoryValue("Configure_Price_Quote", "Floorplan_Image_Bare"), 266 Summary = floorplanOrderLine?.Product.LongDescription, 267 Options = floorplanSelectedOptions, 268 Price = basePriceWithReverse, 269 TotalPrice = basePriceWithReverse + floorplanSelectedOptions 270 .Where(x => !x.IsMandatory) 271 .Select(x => x.Price) 272 .Sum() 273 }; 274 275 var cpqGroup = Services.ProductGroups.GetToplevelGroups().FirstOrDefault(x => x.Name == "CPQ"); 276 var bannerImagePath = string.IsNullOrWhiteSpace(cpqGroup?.LargeImage) ? "/Files" + parentProduct.ImageSmall : "/Files" + cpqGroup?.LargeImage; 277 var rvImagePath = exteriorOption?.ImagePath ?? "/Files" + parentProduct.ImageMedium; 278 var totalPrice = order.Price.Price; 279 var totalPriceFormatted = order.Price.PriceFormatted; 280 281 var rawBrochureLink = parentProduct.ProductFieldValues.GetProductFieldValue("ModelBrochure")?.Value as string; 282 283 var fullQuoteSummary = new QuoteSummary 284 { 285 BuildId = buildId, 286 SelectedOptionCodes = selectedOptionCodes, 287 LeadsOptionCodes = selectedOptions 288 .Concat(floorplan.Options) 289 .Select(x => x.OptionCode) 290 .ToList(), 291 LeadsOptionTitles = selectedOptions 292 .Concat(floorplan.Options) 293 .Select(x => $"{(x.IsReverse ? "(Delete) " : "")}{x.Title}") 294 .ToList(), 295 BannerImagePath = bannerImagePath, 296 RvImagePath = rvImagePath, 297 ModelName = modelName, 298 ModelYear = modelYear, 299 Floorplan = floorplan, 300 Exterior = exteriorOption, 301 Interior = interiorOption, 302 SelectedOptions = selectedOptions 303 .Where(x => !x.IsReverse) 304 .Concat(nonSelectedReverseOptions) 305 .ToList(), 306 AddOnOptionsPrice = selectedOptions 307 .Where(x => !x.IsReverse && !x.IsMandatory) 308 .Concat(nonSelectedReverseOptions) 309 .Select(x => x.Price) 310 .Sum(), 311 TotalPrice = totalPrice, 312 TotalPriceFormatted = totalPriceFormatted, 313 BrochureLink = string.IsNullOrWhiteSpace(rawBrochureLink) ? string.Empty : "/Files/Images/" + rawBrochureLink, 314 MainGroupName = parentProduct.Groups.Any(x => x.Name.ToLower().Equals("motorhomes")) ? "Motorhomes" : "Towables", 315 ModelNumber = parentProduct.GetCategoryValue("Motorhomes_and_Towables", "WGO_Model_Number")?.ToString() 316 }; 317 return fullQuoteSummary; 318 } 319 } 320 321 322 @{ 323 var buildId = Dynamicweb.Context.Current.Request.Params["buildId"]; 324 var quoteId = Dna.Winnebago.Cpq.Cipher.DecryptBuildId(buildId); 325 326 var quoteSummary = GetQuoteSummary(quoteId, true); 327 var currentDate = DateTime.Now.ToString("M/dd/yyy"); 328 329 <style> 330 331 /* custom font imports */ 332 @("@font-face") { 333 font-family: "UnitedSans-Heavy"; 334 src: url("/Files/Templates/Designs/Rapido/css/fonts/UnitedSans/UnitedSansReg-Heavy.otf") format("opentype"); 335 font-weight: normal; 336 font-style: normal; 337 } 338 339 @("@font-face") 340 { 341 font-family: "BertholdGrotesk-Regular"; 342 src: url("/Files/Templates/Designs/Rapido/css/fonts/BertholdGrotesk/BertholdAkzidenzGrotesk-Regular.otf") format("opentype"); 343 font-weight: normal; 344 font-style: normal; 345 } 346 347 @("@media print") { 348 349 .cpq--print-summary-container { 350 padding: 0px 85px; 351 min-height: 100%; 352 } 353 354 .current-date { 355 color: #656565 !important; 356 } 357 358 .grid.cpq--print-summary-header { 359 font-family: "BertholdGrotesk-Regular", Arial, Helvetica, sans-serif; 360 border-bottom: 3px solid black; 361 padding-bottom: 20px; 362 height: 100px; 363 } 364 365 .print-logo { 366 width: 450px; 367 } 368 369 .build-summary { 370 position: relative; 371 color: #656565 !important; 372 } 373 374 .build-summary p { 375 color: #656565 !important; 376 font-weight: bold; 377 margin-bottom: 0px; 378 } 379 /*Main Left Side Content*/ 380 381 382 .cpq--print-model-year { 383 font-family: "BertholdGrotesk-Regular", Arial, Helvetica, sans-serif; 384 margin-bottom: 0px; 385 color: #656565 !important; 386 font-size: 30px; 387 font-weight: bold; 388 } 389 390 .cpq--print-model-name{ 391 font-family: "UnitedSans-Heavy", Arial, Helvetica, sans-serif !important; 392 } 393 394 .cpq--floorplan-images-wrapper { 395 position: relative; 396 } 397 398 .cpq--print-model-image, { 399 width: 400px; 400 } 401 402 .cpq--print-overlay-image { 403 width: 400px; 404 position: absolute; 405 bottom: 0; 406 top: 0; 407 left: 0; 408 right: 0; 409 } 410 411 .cpq--print-left-closing-text { 412 color: #656565 !important; 413 font-size: 12px; 414 line-height: normal; 415 letter-spacing: 1px; 416 } 417 418 .cpq--print-left-closing-text h1 { 419 font-family: "UnitedSans-Heavy",Arial,Helvetica,sans-serif !important; 420 } 421 422 .cpq--print-details-col { 423 flex-direction: row; 424 } 425 426 427 .cpq--details-section-header { 428 border-bottom: 2px solid #d0d0d0; 429 color: #656565 !important; 430 margin-bottom: 5px; 431 font-size: 14px; 432 } 433 434 .cpq--section-details-wrapper { 435 display: flex; 436 flex-direction: row; 437 padding-bottom: 20px; 438 justify-content: space-between; 439 } 440 441 .cpq--section-details-wrapper .cpq--details-main { 442 padding: 0px; 443 margin-right: 25px; 444 } 445 .cpq--details-main p { 446 margin-bottom: 0px; 447 } 448 449 .item-title { 450 font-family: "BertholdGrotesk-Regular", Arial, Helvetica, sans-serif; 451 font-weight: bold; 452 font-size: 12px; 453 letter-spacing: 0.5px; 454 } 455 456 .item-description { 457 font-family: "BertholdGrotesk-Regular", Arial, Helvetica, sans-serif; 458 letter-spacing: normal; 459 font-size: 10px; 460 color: #656565 !important; 461 line-height: normal; 462 letter-spacing: 1px; 463 } 464 465 .pricing-wrapper { 466 padding: 0; 467 } 468 469 .item-price { 470 font-family: "BertholdGrotesk-Regular", Arial, Helvetica, sans-serif; 471 font-size: 18px; 472 margin-bottom: 10px; 473 text-align: right; 474 } 475 476 .cpq--section-details-wrapper.warranty { 477 font-size: 16px; 478 display: block; 479 } 480 481 .cpq--print-footer-grid { 482 border-top: 2px solid black; 483 } 484 485 .cpq--print-footer-grid div { 486 padding-top: 20px; 487 } 488 489 490 .total-price-row .cpq--footer-total-label { 491 font-weight: bold; 492 font-size: 14px; 493 } 494 495 496 .cpq--footer-total-value { 497 font-family: "BertholdGrotesk-Regular", Arial, Helvetica, sans-serif; 498 font-weight: bold; 499 font-size: 20px; 500 } 501 502 .page-footer { 503 font-size: 14px; 504 color: #656565 !important; 505 } 506 507 508 .cpq--print-main-grid { 509 position:relative; 510 padding-top: 50px; 511 } 512 513 .cpq--pdf-footer-container { 514 position: fixed; 515 bottom: 0; 516 width: 85%; 517 } 518 519 /*Hide other page elements*/ 520 #Block__MasterFooterContent, 521 .main-navigation, 522 .aside--skip--nav, 523 #auryc-feedback-tab, 524 #Block__RizzoNoErpConnectionMessage, 525 .countdownTimer-wrapper--link, 526 #CookiePanel, 527 .grid footer__newsletter-signup { 528 display: none; 529 } 530 } 531 </style> 532 <div class="cpq--print-summary-container"> 533 <div class="cpq--print-header-container"> 534 <p class="current-date">@currentDate</p> 535 <div class="grid cpq--print-summary-header"> 536 <div class="grid__col-6"> 537 <img class="print-logo" src="/Files/Images/Winnebago/logo.svg" alt="Winnebago Logo"/> 538 </div> 539 <div class="grid__col-6 grid--justify-end"> 540 <p class="build-summary">Build Summary</p> 541 </div> 542 </div> 543 </div> 544 545 @*Floorplan Section*@ 546 <div class="grid cpq--print-main-grid"> 547 <div class="grid__col-6 cpq-print-summary-left"> 548 <p class="cpq--print-model-year">@quoteSummary.ModelYear</p> 549 <h1 class="cpq--print-model-name">@quoteSummary.ModelName</h1> 550 <img class="cpq--print-model-image" src="@quoteSummary.RvImagePath" alt="@quoteSummary.ModelName Exterior Image"/> 551 <div class="cpq--floorplan-images-wrapper"> 552 <img class="cpq--print-model-image" src="@quoteSummary.Floorplan.ImagePath" alt="@quoteSummary.Floorplan.Name Floorplan Image"/> 553 @foreach (var floorplanOption in quoteSummary.Floorplan.Options) 554 { 555 <img class="cpq--print-overlay-image" src="@floorplanOption.ThumbnailImage" alt="@quoteSummary.Floorplan.Name Floorplan Image"/> 556 } 557 </div> 558 <p class="cpq--print-left-closing-text"> 559 @Translate("CpqSummaryDisclaimer", "*MSRP shown in USD. Pricing does not include freight, taxes, title, license, dealer prep charges, or other miscellaneous fees. Due to our commitment to continuous product improvement, Winnebago reserves the right to change components, standard features, options, specifications, materials, and pricing without notice and at any time. Please contact your local dealer to review current product details and pricing.") 560 </p> 561 </div> 562 <div class="grid__col-6"> 563 @*Floorplan Section*@ 564 <p class="cpq--details-section-header">Floorplan</p> 565 <div class="cpq--section-details-wrapper"> 566 <div class="grid__col-6 cpq--details-main"> 567 <p class="item-title">@quoteSummary.Floorplan.Name</p> 568 <p class="item-description">@quoteSummary.Floorplan.Summary</p> 569 </div> 570 <div class="grid__col-5 pricing-wrapper grid--justify-end"> 571 <p class="item-price">@(quoteSummary.Floorplan.Price > 0 ? FormatPrice(quoteSummary.Floorplan.Price): Translate("Included"))</p> 572 </div> 573 </div> 574 @foreach (var option in quoteSummary.Floorplan.Options) 575 { 576 <div class="cpq--section-details-wrapper"> 577 <div class="grid__col-6 cpq--details-main"> 578 <p class="item-title">@option.Title</p> 579 <p class="item-description">@option.ShortDescription</p> 580 </div> 581 <div class="grid__col-5 pricing-wrapper grid--justify-end"> 582 <p class="item-price">@(option.Price > 0 ? FormatPrice(option.Price) : Translate("Included"))</p> 583 </div> 584 </div> 585 } 586 587 @*Exterior Section*@ 588 <p class="cpq--details-section-header">Exterior</p> 589 <div class="cpq--section-details-wrapper"> 590 <div class="grid__col-6 cpq--details-main"> 591 <p class="item-title">@quoteSummary.Exterior.Title</p> 592 </div> 593 <div class="grid__col-5 pricing-wrapper grid--justify-end"> 594 <p class="item-price">@(quoteSummary.Exterior.Price > 0 ? FormatPrice(quoteSummary.Exterior.Price) : Translate("Included"))</p> 595 </div> 596 </div> 597 598 @*Interior Section*@ 599 <p class="cpq--details-section-header">Interior</p> 600 <div class="cpq--section-details-wrapper"> 601 <div class="grid__col-6 cpq--details-main"> 602 <p class="item-title">@quoteSummary.Interior.Title</p> 603 </div> 604 <div class="grid__col-5 pricing-wrapper grid--justify-end"> 605 <p class="item-price">@(quoteSummary.Interior.Price > 0 ? FormatPrice(quoteSummary.Interior.Price) : Translate("Included"))</p> 606 </div> 607 </div> 608 609 @*Selected Options Section*@ 610 <p class="cpq--details-section-header">Options</p> 611 @foreach (var selectedOption in quoteSummary.SelectedOptions) 612 { 613 <div class="cpq--section-details-wrapper"> 614 <div class="grid__col-6 cpq--details-main"> 615 <p class="item-title">@selectedOption.Title</p> 616 <p class="item-description">@selectedOption.ShortDescription</p> 617 </div> 618 <div class="grid__col-5 pricing-wrapper grid--justify-end"> 619 <p class="item-price">@(selectedOption.Price > 0 ? FormatPrice(selectedOption.Price) : Translate("Included"))</p> 620 </div> 621 </div> 622 } 623 624 @*Warranty Section*@ 625 <p class="cpq--details-section-header">Warranty</p> 626 <div class="cpq--section-details-wrapper warranty"> 627 @if (quoteSummary.MainGroupName == "Motorhomes") 628 { 629 <p class="warranty-item">@Translate("CpqWarrantyMotorhomes1", "36-month/100,000-mile basic limited warranty")</p> 630 <p class="warranty-item">@Translate("CpqWarrantyMotorhomes2", "10-year limited parts-and-labor warranty on fiberglass roof skin")</p> 631 } 632 else 633 { 634 <p class="warranty-item">@Translate("CpqWarrantyTowables1", "3-Year Limited Structural Warranty")</p> 635 <p class="warranty-item">@Translate("CpqWarrantyTowables2", "1-Year Limited Base Warranty")</p> 636 } 637 </div> 638 </div> 639 </div> 640 @*PDF and Page Footer*@ 641 <div class="cpq--pdf-footer-container"> 642 <div class="grid cpq--print-footer-grid"> 643 <div class="grid__col-6 total-price-row"> 644 <p class="cpq--footer-total-label">Total Price (MSRP)</p> 645 </div> 646 <div class="grid__col-6 grid--justify-end"> 647 <p class="cpq--footer-total-value">@FormatPrice(quoteSummary.TotalPrice)</p> 648 </div> 649 </div> 650 <div class="grid page-footer"> 651 <div class="grid__col-6"> 652 <p>www.winnebago.com</p> 653 </div> 654 <div class="grid__col-6 grid--justify-end"> 655 <p>Page 1/1</p> 656 </div> 657 </div> 658 </div> 659 </div> 660 }