Error executing template "Designs/Swift-v2/eCom7/CartV2/Step/Cart.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_e052e5101430485ca5cb8cf7237ca8db.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) in /_/src/Application/Providers/Dynamicweb.Rendering.Providers.NetCore/Razor/RazorTemplateRenderingProvider.cs:line 68 at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) in E:\dw10agent\_w\2\s\src\Core\Dynamicweb.Core\Rendering\TemplateRenderingService.cs:line 14 at Dynamicweb.Rendering.Template.RenderRazorTemplate() in E:\dw10agent\_w\2\s\src\Core\Dynamicweb.Core\Rendering\Template.cs:line 847
1 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 2 @using Dynamicweb 3 4 @{ 5 bool anonymousUser = Pageview.User == null; 6 string iconPath = "/Files/Images/Icons/"; 7 int currentUserId = Dynamicweb.Core.Converter.ToInt32(GetGlobalValue("Global:Extranet.UserID")); 8 var shopPageId = GetPageIdByNavigationTag("Shop"); 9 var cart = Dynamicweb.Frontend.ContentViewModelFactory.CreateParagraphInfoViewModel(Pageview.CurrentParagraph)?.Item ?? null; 10 var checkoutAnonymousLink = cart?.GetLink("CheckoutAnonymousPageLink") is object ? cart.GetLink("CheckoutAnonymousPageLink").Url : string.Empty; 11 var checkoutSignedInLink = cart?.GetLink("CheckoutSignedInPageLink") is object ? cart.GetLink("CheckoutSignedInPageLink").Url : string.Empty; 12 string checkoutLink = currentUserId == 0 ? checkoutAnonymousLink : checkoutSignedInLink; 13 var quoteCheckoutLink = cart?.GetLink("QuoteCheckoutPageLink") is object ? cart.GetLink("QuoteCheckoutPageLink").Url : string.Empty; 14 bool isQuote = Dynamicweb.Ecommerce.Services.Orders.GetById(GetString("Ecom:Order.ID")).IsQuote; 15 var cartServicePageId = GetPageIdByNavigationTag("CartService"); 16 } 17 <div class="grid gap-0" id="Cart"> 18 <header class="g-col-12"> 19 <div class="pb-3 pb-lg-0 pt-3 pt-lg-5"> 20 <h1 class="h3 mb-2">@Translate("Shopping Cart")</h1> 21 <p class="mb-0"><span>@Translate("Items in your shopping cart"):</span> <span>@GetString("Ecom:Order.OrderLines.TotalProductQuantity")</span></p> 22 </div> 23 </header> 24 25 <div class="g-col-12 g-col-lg-8 pe-lg-5 pb-md-5"> 26 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 27 @using Dynamicweb.Ecommerce.Orders 28 29 @{ 30 var voucherCodeForErrorCheck = string.Empty; 31 Order? orderForErrorCheck = !string.IsNullOrEmpty(GetString("Ecom:Order.ID")) ? new OrderService().GetById(GetString("Ecom:Order.ID")) : Dynamicweb.Ecommerce.Common.Context.Cart; 32 bool hasAppliedVoucherForErrorCheck = orderForErrorCheck is object && orderForErrorCheck.VoucherUseType != VoucherUseCategoryType.None && !string.IsNullOrWhiteSpace(orderForErrorCheck.VoucherCode); 33 if (hasAppliedVoucherForErrorCheck) 34 { 35 voucherCodeForErrorCheck = orderForErrorCheck?.VoucherCode; 36 } 37 38 List<LoopItem> validationErrors = new List<LoopItem>(); 39 foreach (LoopItem item in GetLoop("ValidationErrors")) 40 { 41 bool isAppliedVoucherValidationError = hasAppliedVoucherForErrorCheck && string.Equals(item.GetString("Ecom:Cart.ValidationError.FieldSystemName"), "EcomOrderVoucherCode", StringComparison.OrdinalIgnoreCase); 42 if (!isAppliedVoucherValidationError) 43 { 44 validationErrors.Add(item); 45 } 46 } 47 } 48 49 @if (validationErrors.Count != 0) 50 { 51 <div class="alert alert-warning" role="alert"> 52 <ul class="m-0 list-unstyled"> 53 @foreach (LoopItem item in validationErrors) 54 { 55 <li>@item.GetValue("Ecom:Cart.ValidationError.ErrorMessage")</li> 56 } 57 </ul> 58 </div> 59 } 60 61 62 @* Logic to remove the voucher code error, when going to next step in checkout *@ 63 <input type="hidden" name="EcomOrderVoucherCode" value="@voucherCodeForErrorCheck"> 64 65 66 <form name="ordersubmit" id="ordersubmit" method="post" autocomplete="off"> 67 <input type="hidden" name="cartcmd" value="updateorderlines" /> 68 <input type="hidden" name="redirect" value="false" /> 69 <input type="hidden" name="LayoutTemplate" value="Swift-v2_MiniCart.cshtml" /> 70 <input type="hidden" name="MiniCartBehaviour" value="0" /> 71 72 <header class="py-2 pt-lg-5 border-bottom d-none d-lg-block"> 73 <div class="grid" style="line-height: 1;"> 74 <div class="g-col-2">@Translate("Product")</div> 75 <div class="g-col-9 grid"> 76 <div class="g-col-10 g-col-md-6 g-start-8"></div> 77 <div class="g-col-2 g-col-md-3 g-start-8">@Translate("Quantity")</div> 78 </div> 79 <div class="g-col-1 text-end">@Translate("Total")</div> 80 </div> 81 </header> 82 <script> 83 window.dataLayer = window.dataLayer || []; 84 window.dataLayer.push({ 85 event: "view_cart", 86 ecommerce: { 87 currency: "@GetString("Ecom:Order.Currency.Code")", 88 value: @GetDouble("Ecom:Order.Price.PriceWithVAT.Value").ToString("0.00", System.Globalization.CultureInfo.InvariantCulture), 89 items: [ 90 @foreach (LoopItem orderline in GetLoop("OrderLines")) 91 { 92 <text> 93 { 94 item_id: "@orderline.GetString("Ecom:Order:OrderLine.ProductID")", 95 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(orderline.GetString("Ecom:Order:OrderLine.ProductName"))", 96 currency: "@orderline.GetString("Ecom:Order:OrderLine.UnitPrice.CurrencyCode")", 97 price: @orderline.GetDouble("Ecom:Order:OrderLine.UnitPriceWithProductDiscount.Price.Value").ToString("0.00", System.Globalization.CultureInfo.InvariantCulture), 98 discount: @Math.Abs(orderline.GetDouble("Ecom:Order:OrderLine.UnitDiscount.Price.Value")).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture), 99 quantity: @orderline.GetDouble("Ecom:Order:OrderLine.Quantity").ToString("0.00", System.Globalization.CultureInfo.InvariantCulture) 100 },</text> 101 } 102 ] 103 } 104 }); 105 </script> 106 107 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 108 @using Dynamicweb.Frontend 109 @using Dynamicweb.Rendering 110 @using System.Globalization 111 @using System 112 113 @functions{ 114 string? DoubleToString(double? value) 115 { 116 if (value.HasValue) 117 { 118 return Math.Round(value.Value, 2).ToString(CultureInfo.InvariantCulture); 119 } 120 return null; 121 } 122 } 123 124 @{ 125 var orderlines = GetLoop("OrderLines"); 126 } 127 128 @for (int i = orderlines.Count - 1; i >= 0; i--) 129 { 130 var orderline = orderlines[i]; 131 bool neverOutOfStock = orderline.GetBoolean("Ecom:Product.NeverOutOfStock"); 132 string name = orderline.GetString("Ecom:Order:OrderLine.ProductName"); 133 string number = orderline.GetString("Ecom:Order:OrderLine.ProductNumber"); 134 string primaryImage = orderline.GetString("Ecom:Product.PrimaryImage"); 135 string image = $"/Admin/Public/GetImage.ashx?width=180&height=180&image={primaryImage}&format=webp"; 136 string removeFromBasketLink = orderline.GetString("Ecom:Order:OrderLine.DeleteLink"); 137 string priceTotalWithDiscounts = orderline.GetString("Ecom:Order:OrderLine.TotalPriceWithProductDiscounts.PriceFormatted"); 138 string priceTotalWithDiscountsClean = orderline.GetString("Ecom:Order:OrderLine.TotalPriceWithProductDiscounts.Price"); 139 string unitPrice = orderline.GetString("Ecom:Order:OrderLine.UnitPrice.PriceFormatted"); 140 string unitPriceWithDiscount = orderline.GetDouble("Ecom:Order:OrderLine.UnitPriceWithProductDiscount.Price.Value").ToString("0.00", System.Globalization.CultureInfo.InvariantCulture); 141 string discountAmount = Math.Abs(orderline.GetDouble("Ecom:Order:OrderLine.UnitDiscount.Price.Value")).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture); 142 string unitPriceCurrency = orderline.GetString("Ecom:Order:OrderLine.Price.CurrencyCode"); 143 string discountPrice = orderline.GetString("Ecom:Order:OrderLine.UnitPriceWithProductDiscount.PriceFormatted"); 144 string discountTotal = orderline.GetString("Ecom:Order:OrderLine.TotalDiscount.PriceFormatted"); 145 string variantText = orderline.GetString("Ecom:Order:OrderLine.ProductVariantText"); 146 var orderlineId = orderline.GetValue("Ecom:Order:OrderLine.Id"); 147 double? stepQty = orderline.GetDouble("Ecom:Product.PurchaseQuantityStep") > 0 ? orderline.GetDouble("Ecom:Product.PurchaseQuantityStep") : 1; 148 var quantity = orderline.GetDouble("Ecom:Order:OrderLine.Quantity"); 149 var unitName = orderline.GetString("Ecom:Order:OrderLine.UnitName"); 150 string primaryGroupId = orderline.GetString("Ecom:Order:OrderLine.PrimaryOrDefaultGroupId"); 151 string productId = orderline.GetString("Ecom:Order:OrderLine.ProductID"); 152 string variantId = orderline.GetString("Ecom:Order:OrderLine.ProductVariantID"); 153 string unitId = orderline.GetString("Ecom:Order:OrderLine.UnitID"); 154 string productType = orderline.GetString("Ecom.Product.Type"); 155 double? stockQuantity = orderline.GetDouble("Ecom:Product.Stock"); 156 double? minQty = orderline.GetDouble("Ecom:Product.PurchaseMinimumQuantity") > 0 ? orderline.GetDouble("Ecom:Product.PurchaseMinimumQuantity") : 1; 157 bool isStockType = orderline.GetInteger("Ecom.Product.Type") == 0; 158 bool setMaxQty = isStockType && !neverOutOfStock; 159 double? maxQty = setMaxQty ? stockQuantity : null; 160 161 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 162 link += $"&GroupID={System.Net.WebUtility.UrlEncode(primaryGroupId)}"; 163 link += $"&ProductID={System.Net.WebUtility.UrlEncode(productId)}"; 164 link += !string.IsNullOrEmpty(variantId) ? $"&VariantID={System.Net.WebUtility.UrlEncode(variantId)}" : ""; 165 166 bool isProductIdOrNumber = !string.IsNullOrEmpty(orderline.GetString("Ecom:Order:OrderLine.ProductID")) || !string.IsNullOrEmpty(orderline.GetString("Ecom:Order:OrderLine.ProductNumber")); //Live Cart orderlines may not return ProductId. Manually entered products may not have ProductNumber 167 bool isProduct = orderline.GetBoolean("Ecom:Order:OrderLine.IsProduct") || (orderline.GetBoolean("Ecom:Order:OrderLine.IsDiscount") && isProductIdOrNumber); 168 bool orderIsQuote = Dynamicweb.Ecommerce.Services.Orders.GetById(GetString("Ecom:Order.ID")).IsQuote; 169 170 if (isProduct) 171 { 172 var bomItems = orderline.GetLoop("BOMItems"); 173 <article class="grid pt-3" style="row-gap: 0;" data-product-id="@productId" data-variant-id="@variantId" data-unit-id="@unitId" data-orderline-total-price="@priceTotalWithDiscountsClean" data-orderline-unit-price="@unitPriceWithDiscount"> 174 <div class="g-col-3 g-col-lg-2"> 175 <a href="@link" class="ratio ratio-1x1 d-block" title="@name"> 176 <img class="cart-item-img" src="@image" style="object-fit: contain;" alt="@name"> 177 </a> 178 </div> 179 180 <div class="g-col-9 g-col-lg-10"> 181 182 <div class="grid" style="row-gap: 0.5rem;--bs-gap:0;"> 183 184 <div class="g-col-12 g-col-md-8 grid grid-7 grid-md-9" style="row-gap: 0.5rem;"> 185 <div class="g-col-7 g-col-md-6"> 186 @* Title *@ 187 <h3 class="fs-7 fs-md-6 mb-0"> 188 <a href="@link" class="text-decoration-none" title="@name">@name</a> 189 </h3> 190 191 @* Variants *@ 192 @if (!string.IsNullOrEmpty(variantText)) 193 { 194 <p class="mt-2 mb-0 fs-7 fs-md-6">@variantText</p> 195 } 196 197 @* Number *@ 198 @if (!string.IsNullOrEmpty(number)) 199 { 200 <p class="mt-2 mb-0 fs-8 fs-md-7 text-muted">@(number)</p> 201 } 202 203 @if (orderline.GetBoolean("Ecom:Order:OrderLine.IsDiscount")) 204 { 205 @* Free product notice *@ 206 <p class="mt-2 mb-0 fs-7 fs-md-6 opacity-75">@Translate("Free product")</p> 207 } 208 else 209 { 210 @* Unit price *@ 211 <p class="fs-7 fs-md-6 mt-2 mb-0"> 212 @if (!string.IsNullOrEmpty(unitName)) 213 { 214 <span>@unitName: </span> 215 } 216 @if (discountPrice == unitPrice) 217 { 218 <span class="text-price">@unitPrice</span> 219 } 220 else 221 { 222 <span class="text-price">@discountPrice</span> 223 <span class="text-decoration-line-through fs-7"> 224 <span class="text-price">@unitPrice</span> 225 </span> 226 } 227 </p> 228 } 229 </div> 230 231 <div class="g-col-7 g-col-md-3"> 232 @if (!orderline.GetBoolean("Ecom:Order:OrderLine.IsDiscount") && !orderIsQuote) 233 { 234 @* Quantity *@ 235 <div class="input-group js-input-group"> 236 237 @{ 238 var orderlineViewModel = new Dynamicweb.Ecommerce.Frontend.OrderLineViewModel(){ 239 Id = orderlineId.ToString(), 240 ProductId = productId, 241 ProductVariantId = variantId, 242 UnitId = unitId, 243 Quantity = quantity 244 }; 245 } 246 247 @RenderPartial("Components/QuantitySelectorOrderline.cshtml", orderlineViewModel) 248 249 </div> 250 } 251 else 252 { 253 <div class="text-center" style="max-width: 140px;">@quantity</div> 254 } 255 </div> 256 </div> 257 258 <div class="g-col-12 g-col-md-4 text-end"> 259 @* Total *@ 260 <div class="fs-7 fs-md-6"> 261 <span class="text-price">@priceTotalWithDiscounts</span> 262 </div> 263 @if (orderline.GetDouble("Ecom:Order:OrderLine.TotalDiscount.Price.Value") != 0) 264 { 265 <div class="fs-8"> 266 <span class="text-price">@discountTotal</span> 267 </div> 268 } 269 </div> 270 271 <div class="g-col-12"> 272 @if (!orderIsQuote) 273 { 274 string quantityValue = quantity.ToString().Replace(",", "."); 275 string clickProductLink = $"return removeProductLink('{productId}', '{Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(name)}', '{Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(variantText)}', '{unitPriceCurrency}', '{unitPriceWithDiscount}', '{discountAmount}', '{quantityValue}')"; 276 277 <a href="@removeFromBasketLink" class="btn btn-link btn-sm p-0 m-0 fs-8" title="@Translate("Remove")" onclick="@clickProductLink">@Translate("Remove")</a> 278 } 279 280 <script> 281 function removeProductLink(productId, productName, productVariant, productCurrency, productPrice, productDiscount, productQuantity) { 282 window.dataLayer = window.dataLayer || []; 283 window.dataLayer.push({ 284 event: "remove_from_cart", 285 ecommerce: { 286 currency: productCurrency, 287 value: (parseFloat(productPrice) * parseFloat(productQuantity)), 288 items: [ 289 { 290 item_id: productId, 291 item_name: productName, 292 item_variant: productVariant, 293 currency: productCurrency, 294 price: productPrice, 295 discount: productDiscount, 296 item_list_id: "cart", 297 item_list_name: "Cart", 298 quantity: productQuantity 299 } 300 ] 301 } 302 }); 303 } 304 </script> 305 306 </div> 307 </div> 308 </div> 309 310 @* Bottom border *@ 311 <div class="g-col-12 mt-3"> 312 <span class="d-none d-lg-block border-bottom"></span> 313 </div> 314 315 @if (bomItems.Count > 0) { 316 <div class="g-col-12 mt-3"> 317 @* BOM items *@ 318 @foreach (var bomItem in bomItems) { 319 string bomImage = bomItem.GetString("Ecom:Product.PrimaryImage"); 320 bomImage = !bomImage.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? bomImage : $"/Admin/Public/GetImage.ashx?width=200&height=200&image={bomImage}&format=webp"; 321 322 string bomPriceTotalWithDiscounts = bomItem.GetString("Ecom:Order:OrderLine.TotalPriceWithProductDiscounts.PriceFormatted"); 323 string bomDiscountTotal = bomItem.GetString("Ecom:Order:OrderLine.TotalDiscount.PriceFormatted"); 324 325 var bomVariantName = string.Join(" ", bomItem.GetLoop("VariantGroups") 326 .SelectMany(g => g.GetLoop("VariantOptions")) 327 .Where(o => o.GetBoolean("Ecom:VariantOption.Selected")) 328 .Select(o => $"<span>{o.GetString("Ecom:VariantOption.Name")} </span>")); 329 330 331 <div class="grid"> 332 <div class="g-col-3 g-col-lg-2 d-flex align-items-center gap-2"> 333 <div class="icon-2 p-0 p-lg-3 opacity-25"> 334 @ReadFile("/Files/Images/Icons/arrow-turn-down-right.svg") 335 </div> 336 @if (!string.IsNullOrEmpty(bomImage)) { 337 <div class="ratio ratio-1x1 d-block" title="@name"> 338 <img class="cart-item-img" src="@bomImage" style="object-fit: contain;" alt="@name"> 339 </div> 340 } 341 </div> 342 <div class="g-col-9 g-col-lg-10 grid" style="row-gap: 0.5rem; --bs-gap:0;"> 343 <div class="g-col-12 g-col-md-8 grid grid-7 grid-md-9"> 344 <div class="g-col-7 g-col-md-6"> 345 <h3 class="fs-7 fs-md-6 m-0">@bomItem.GetString("Ecom:Product.Name") </h3> 346 <div class="fs-7 fs-md-6">@bomVariantName</div> 347 </div> 348 <div class="g-col-7 g-col-md-3"> 349 <p class="fs-7 fs-md-6 mb-0 text-center" style="max-width: 140px;">@bomItem.GetDouble("Ecom:Order:OrderLine.Quantity")</p> 350 </div> 351 </div> 352 <div class="g-col-12 g-col-md-4 text-end"> 353 @* Total - Hide when PriceType = Fixed *@ 354 @if (orderline.GetInteger("Ecom:Product.PriceType") != 1) { 355 <div class="fs-7 fs-md-6"> 356 <span class="text-price">@bomPriceTotalWithDiscounts</span> 357 </div> 358 if (bomItem.GetDouble("Ecom:Order:OrderLine.TotalDiscount.Price.Value") != 0) 359 { 360 <div class="fs-8"> 361 <span class="text-price">@bomDiscountTotal</span> 362 </div> 363 } 364 } 365 </div> 366 </div> 367 </div> 368 369 @* Bottom border *@ 370 <div class="w-100 my-3"> 371 <span class="d-none d-lg-block border-bottom"></span> 372 </div> 373 } 374 </div> 375 } 376 </article> 377 } 378 } 379 380 381 @if(Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Desktop) 382 { 383 <text>@inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 384 @using Dynamicweb.Ecommerce.Orders 385 386 @* Red lines may be due to variables defined in the parent tempalte - There is no actual error *@ 387 388 @{ 389 390 <div class="align-items-center d-grid d-lg-flex gap-2 justify-content-lg-end mt-lg-4"> 391 392 <a href="Default.aspx?ID=@shopPageId" class="btn btn-link text-decoration-none me-lg-3 order-last order-lg-first swift_cartbackbutton" data-dw-button="link"> 393 <span class="d-flex align-items-center justify-content-center position-relative"> 394 <span class="icon-2 me-1 position-absolute position-lg-static start-0"> 395 @ReadFile(iconPath + "chevron-left.svg") 396 </span> 397 <span>@Translate("Continue shopping")</span> 398 </span> 399 </a> 400 401 @if (!anonymousUser && isQuote) 402 { 403 <a href="@($"{Context.Current.Request.Url}&cartcmd=emptycart")" class="btn btn-secondary" data-dw-button="secondary"> 404 <span class="d-flex align-items-center justify-content-center position-relative"> 405 <span>@Translate("Clear Cart")</span> 406 <span class="icon-2 ms-1 position-absolute position-lg-static end-0"> 407 @ReadFile(iconPath + "trash-can.svg") 408 </span> 409 </span> 410 </a> 411 } 412 413 @if (!string.IsNullOrEmpty(quoteCheckoutLink) && !anonymousUser && !isQuote) 414 { 415 <a href="@quoteCheckoutLink" class="btn btn-secondary" data-dw-button="secondary"> 416 <span class="d-flex align-items-center justify-content-center position-relative"> 417 <span>@Translate("Request quote")</span> 418 <span class="icon-2 ms-1 position-absolute position-lg-static end-0"> 419 @ReadFile(iconPath + "chevron-right.svg") 420 </span> 421 </span> 422 </a> 423 } 424 425 <a href="@checkoutLink" class="btn btn-primary" data-dw-button="primary" id="GoToCheckoutButton"> 426 <span class="d-flex align-items-center justify-content-center position-relative"> 427 <span>@Translate("Checkout")</span> 428 <span class="icon-2 ms-1 position-absolute position-lg-static end-0"> 429 @ReadFile(iconPath + "chevron-right.svg") 430 </span> 431 </span> 432 </a> 433 434 </div> 435 } 436 </text> 437 } 438 439 </form> 440 </div> 441 442 <aside class="g-col-12 g-col-lg-4 position-relative"> 443 <div class="p-3 p-lg-5 pb-lg-4 sticky-top z-1" data-dw-colorscheme="@Pageview.CurrentParagraph?.ColorSchemeId"> 444 <header> 445 <h4 class="fs-6 fw-normal mb-2">@Translate("Order summary")</h4> 446 </header> 447 448 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 449 450 @{ 451 double totalPriceWithoutOrderDiscountsFeesAndTaxesValue = 452 GetDouble("Ecom:Order.TotalPriceWithoutDiscountsFeesAndTaxes.Price.Value") + 453 GetDouble("Ecom:Order.TotalProductDiscount.Price.Value"); 454 var orderCurrency = Dynamicweb.Ecommerce.Services.Currencies.GetCurrency(GetString("Ecom:Order.Currency.Code")); 455 string totalPriceWithoutOrderDiscountsFeesAndTaxes = orderCurrency != null ? 456 Dynamicweb.Ecommerce.Services.Currencies.Format(orderCurrency, 457 totalPriceWithoutOrderDiscountsFeesAndTaxesValue) : string.Empty; 458 459 bool showPricesWithVat = Dynamicweb.Ecommerce.Common.Context.DisplayPricesWithVat; 460 bool renderVatMessage = (GetString("Ecom:Order.OrderLines.Total.PriceWithVAT") != 461 GetString("Ecom:Order.OrderLines.Total.PriceWithoutVAT")) || Dynamicweb.Ecommerce.Common.Context.Country?.Vat != null; 462 463 bool disableVouchers = Pageview.CurrentParagraph?.Item["DisableVouchers"] != null ? 464 (Boolean)Pageview.CurrentParagraph.Item["DisableVouchers"] : false; 465 disableVouchers = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("CompletedOrderId")) ? true : 466 disableVouchers; 467 468 <div class="d-flex flex-column gap-2 gap-lg-3 pb-3 bottom-0 sticky-receipt" id="orderSummaryFooter"> 469 470 @if (!disableVouchers && Pageview.CurrentParagraph?.Item.SystemName != "Swift-v2_QuoteCheckoutApp") 471 { 472 <text> 473 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 474 @using Dynamicweb.Ecommerce.Orders 475 476 @{ 477 string voucherCodeError = GetString("Ecom:Cart.ValidationError.EcomOrderVoucherCode.ErrorMessage"); 478 string renderedVoucherCode = GetString("Ecom:Order.Customer.VoucherCode"); 479 string appliedVoucherCode = string.Empty; 480 Order? order = !string.IsNullOrWhiteSpace(GetString("Ecom:Order.ID")) ? new OrderService().GetById(GetString("Ecom:Order.ID")) : Dynamicweb.Ecommerce.Common.Context.Cart; 481 482 if (order is object && order.VoucherUseType != VoucherUseCategoryType.None && !string.IsNullOrWhiteSpace(order.VoucherCode)) 483 { 484 appliedVoucherCode = order.VoucherCode; 485 } 486 487 List<string> voucherCodes = new List<string>(); 488 if (!string.IsNullOrWhiteSpace(appliedVoucherCode)) 489 { 490 voucherCodes.AddRange(Dynamicweb.Ecommerce.Services.Vouchers.GetVoucherCodesFromString(appliedVoucherCode)); 491 } 492 493 bool hasAppliedVouchers = voucherCodes.Count > 0; 494 bool voucherWasPostedButNotApplied = !string.IsNullOrWhiteSpace(renderedVoucherCode) && string.IsNullOrWhiteSpace(appliedVoucherCode); 495 string voucherErrorMessage = hasAppliedVouchers ? string.Empty : voucherCodeError; 496 if (string.IsNullOrWhiteSpace(voucherErrorMessage) && voucherWasPostedButNotApplied) 497 { 498 voucherErrorMessage = Translate("Voucher could not be applied"); 499 } 500 string validState = !string.IsNullOrWhiteSpace(voucherErrorMessage) ? "is-invalid" : string.Empty; 501 502 <div class="py-3 border-top" id="orderVoucherForm"> 503 <form method="post" id="VoucherForm"> 504 <div class="input-group input-primary-button-group has-validation"> 505 <input type="text" class="form-control @(validState)" name="EcomOrderVoucherCode" placeholder="@Translate("Add voucher here")" id="EcomOrderVoucherCodeField" value="" aria-label="@Translate("Voucher input")"> 506 <button class="btn btn-sm btn-primary d-inline-flex align-items-center justify-content-center" data-dw-button="primary" type="submit" id="EcomOrderVoucherApply" style="min-width: 100px;"> 507 <span>@Translate("Apply")</span> 508 </button> 509 510 <div class="invalid-feedback"> 511 @voucherErrorMessage 512 </div> 513 </div> 514 <label for="EcomOrderVoucherCodeField" class="visually-hidden">@Translate("Add voucher here")</label> 515 </form> 516 517 @if (hasAppliedVouchers) 518 { 519 <div class="d-flex flex-wrap gap-2 pt-3"> 520 @for (int i = 0; i < voucherCodes.Count; i++) 521 { 522 string code = voucherCodes[i]; 523 List<string> remainingVoucherCodes = new List<string>(voucherCodes); 524 remainingVoucherCodes.RemoveAt(i); 525 string remainingVoucherCode = string.Join(",", remainingVoucherCodes); 526 527 <form method="post"> 528 <input type="hidden" name="EcomOrderVoucherCode" value="@remainingVoucherCode"> 529 <button type="submit" class="badge fw-normal d-inline-flex align-items-center justify-content-center opacity-50 opacity-100-hover border-0" style="background-color:var(--dw-color-foreground);color:var(--dw-color-background);" aria-label="@Translate("Remove voucher")" id="RemoveVoucherButton_@i"> 530 <span class="icon-2"> 531 @ReadFile("/Files/Images/Icons/xmark.svg") 532 </span> 533 <span class="mx-2">@code</span> 534 </button> 535 </form> 536 } 537 </div> 538 } 539 </div> 540 } 541 542 </text> 543 } 544 545 <div class="fs-8 border-top pt-2 pt-lg-3"> 546 @* Subtotal *@ 547 <div class="d-flex justify-content-between fw-bold"> 548 <span>@Translate("Subtotal")</span> 549 <span class="text-end"> 550 <span class="d-block"> 551 @if (GetDouble("Ecom:Order.TotalPriceWithoutDiscountsFeesAndTaxes.Price.Value") != 552 totalPriceWithoutOrderDiscountsFeesAndTaxesValue) 553 { 554 <span 555 class="text-price fw-normal text-decoration-line-through">@GetString("Ecom:Order.TotalPriceWithoutDiscountsFeesAndTaxes.PriceFormatted")</span> 556 } 557 <span class="text-price">@totalPriceWithoutOrderDiscountsFeesAndTaxes</span> 558 </span> 559 560 @if (renderVatMessage && !showPricesWithVat) 561 { 562 <span class="fs-8 fw-normal d-block">@Translate("Excl. VAT")</span> 563 } 564 else if (renderVatMessage) 565 { 566 <span class="fs-8 fw-normal d-block">@Translate("Incl. VAT")</span> 567 } 568 </span> 569 </div> 570 571 @* Discounts (only order discounts) *@ 572 @foreach (LoopItem orderline in GetLoop("OrderLines")) 573 { 574 bool isDiscount = orderline.GetBoolean("Ecom:Order:OrderLine.IsDiscount"); 575 bool isProductIdOrNumber = !string.IsNullOrEmpty(orderline.GetString("Ecom:Order:OrderLine.ProductID")) || 576 !string.IsNullOrEmpty(orderline.GetString("Ecom:Order:OrderLine.ProductNumber")); //Live Cart orderlines may not 577 bool isOrderAmountDiscount = orderline.GetInteger("Ecom:Order:OrderLine.Type") == 1 && !isProductIdOrNumber; 578 if (isDiscount && isOrderAmountDiscount) 579 { 580 <div class="d-flex justify-content-between"> 581 <span>@orderline.GetString("Ecom:Order:OrderLine.ProductName")</span><span 582 class="text-price">@orderline.GetString("Ecom:Order:OrderLine.TotalPriceWithProductDiscounts.PriceFormatted")</span> 583 </div> 584 } 585 } 586 587 @* Delivery *@ 588 @if (!string.IsNullOrEmpty(GetString("Ecom:Order.ShippingMethodID"))) 589 { 590 if (GetDouble("Ecom:Order.ShippingFee.Price.Value") != 0.0) 591 { 592 <div class="d-flex justify-content-between"> 593 <span>@Translate("Delivery")</span> 594 <span>@GetString("Ecom:Order.ShippingFee.PriceFormatted")</span> 595 </div> 596 } 597 } 598 else 599 { 600 <div class="d-flex justify-content-between"> 601 <span>@Translate("Delivery")</span> 602 <span>@Translate("Calculated in next step")</span> 603 </div> 604 } 605 606 @* Payment *@ 607 @if (!string.IsNullOrEmpty(GetString("Ecom:Order.PaymentMethodID"))) 608 { 609 if (GetDouble("Ecom:Order.PaymentFee.Price.Value") != 0) 610 { 611 <div class="d-flex justify-content-between"> 612 <span>@Translate("Payment")</span> 613 <span class="text-price">@GetString("Ecom:Order.PaymentFee.PriceFormatted")</span> 614 </div> 615 } 616 } 617 else 618 { 619 <div class="d-flex justify-content-between"> 620 <span>@Translate("Payment")</span> 621 <span>@Translate("Calculated in next step")</span> 622 </div> 623 } 624 625 @* Taxes *@ 626 @if (GetDouble("Ecom:Order.Taxes.Total.Price.Value") != 0) 627 { 628 <div class="d-flex justify-content-between mb-2"> 629 <span>@Translate("Sales tax")</span> 630 <span>@GetString("Ecom:Order.Taxes.Total.PriceFormatted")</span> 631 </div> 632 } 633 </div> 634 <div class="fs-8 border-top pt-2 pt-lg-3"> 635 <div class="d-flex justify-content-between fs-6 fw-bold"> 636 <span>@Translate("Total")</span> 637 @if (renderVatMessage && !showPricesWithVat) 638 { 639 <span class="text-price text-end"> 640 <span class="fs-5 d-block">@GetString("Ecom:Order.Price.PriceFormatted")</span> 641 <span class="fs-8 fw-normal d-block">@Translate("Excl. VAT")</span> 642 </span> 643 } 644 else if (renderVatMessage) 645 { 646 <span class="text-price text-end"> 647 <span class="fs-5 d-block">@GetString("Ecom:Order.Price.PriceFormatted")</span> 648 <span class="fs-8 fw-normal d-block">@Translate("Incl. VAT")</span> 649 </span> 650 } 651 else 652 { 653 <span class="text-price fs-5">@GetString("Ecom:Order.Price.PriceFormatted")</span> 654 } 655 </div> 656 657 @if (renderVatMessage) 658 { 659 <div class="d-flex justify-content-between"><span>@Translate("VAT")</span><span 660 class="text-price">@GetString("Ecom:Order.Price.VATFormatted")</span></div> 661 if (!showPricesWithVat) 662 { 663 <div class="d-flex justify-content-between"><span>@Translate("Total incl. VAT")</span><span 664 class="text-price">@GetString("Ecom:Order.OrderLines.Total.PriceWithVAT")</span></div> 665 } 666 } 667 668 @if (GetDouble("Ecom:Order.TotalDiscount.Price.Value") != 0) 669 { 670 <div class="d-flex justify-content-between"><span>@Translate("Discount")</span><span 671 class="text-price">@GetString("Ecom:Order.TotalDiscount.PriceFormatted")</span></div> 672 } 673 </div> 674 </div> 675 } 676 677 678 @if(Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Mobile || Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Tablet) 679 { 680 <text>@inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 681 @using Dynamicweb.Ecommerce.Orders 682 683 @* Red lines may be due to variables defined in the parent tempalte - There is no actual error *@ 684 685 @{ 686 687 <div class="align-items-center d-grid d-lg-flex gap-2 justify-content-lg-end mt-lg-4"> 688 689 <a href="Default.aspx?ID=@shopPageId" class="btn btn-link text-decoration-none me-lg-3 order-last order-lg-first swift_cartbackbutton" data-dw-button="link"> 690 <span class="d-flex align-items-center justify-content-center position-relative"> 691 <span class="icon-2 me-1 position-absolute position-lg-static start-0"> 692 @ReadFile(iconPath + "chevron-left.svg") 693 </span> 694 <span>@Translate("Continue shopping")</span> 695 </span> 696 </a> 697 698 @if (!anonymousUser && isQuote) 699 { 700 <a href="@($"{Context.Current.Request.Url}&cartcmd=emptycart")" class="btn btn-secondary" data-dw-button="secondary"> 701 <span class="d-flex align-items-center justify-content-center position-relative"> 702 <span>@Translate("Clear Cart")</span> 703 <span class="icon-2 ms-1 position-absolute position-lg-static end-0"> 704 @ReadFile(iconPath + "trash-can.svg") 705 </span> 706 </span> 707 </a> 708 } 709 710 @if (!string.IsNullOrEmpty(quoteCheckoutLink) && !anonymousUser && !isQuote) 711 { 712 <a href="@quoteCheckoutLink" class="btn btn-secondary" data-dw-button="secondary"> 713 <span class="d-flex align-items-center justify-content-center position-relative"> 714 <span>@Translate("Request quote")</span> 715 <span class="icon-2 ms-1 position-absolute position-lg-static end-0"> 716 @ReadFile(iconPath + "chevron-right.svg") 717 </span> 718 </span> 719 </a> 720 } 721 722 <a href="@checkoutLink" class="btn btn-primary" data-dw-button="primary" id="GoToCheckoutButton"> 723 <span class="d-flex align-items-center justify-content-center position-relative"> 724 <span>@Translate("Checkout")</span> 725 <span class="icon-2 ms-1 position-absolute position-lg-static end-0"> 726 @ReadFile(iconPath + "chevron-right.svg") 727 </span> 728 </span> 729 </a> 730 731 </div> 732 } 733 </text> 734 } 735 </div> 736 </aside> 737 738 <script> 739 document.addEventListener('updated.swift.cart', (event) => { 740 swift.PageUpdater.Update(document.querySelector("#ordersubmit"), true); 741 }); 742 </script> 743 </div> 744