1const classUpdateShippingAmountCheckbox = '.shipping-amount-price'; 2const classUpdateTotalBeforeDiscount = '.layette-total-before-discount-shipping'; 3const discountBoxSummary = '#summary-discount-box'; 4const totalBeforeDiscountLoggedFP= '#total-before-discount-shipping-logged'; 5const noDiscountTotalsSummary = '.no-discount-totals-summary'; 6const discountTotalsSummary = '.discount-totals-summary'; 7const summaryAccumulatedPoints = '#summary-accumulated-points'; 8const summaryRedeemedPoints = '#summary-redeemed-points'; 9const summaryTotalPoints = '#summary-total-points'; 10const summaryRemainingPoints = '#summary-remaining-points' 11const productShowInputPointsSelected = '#points-payment-method' 12const productShowRemainingUserPoints = '#user-points-value' 13 14/** 15 * Clau d'una linia de carret per als ids de DOM. Amb variants, un mateix producte ocupa 16 * diverses files i l'id de contingut sol quedava duplicat. Sense variant, clau de sempre. 17 */ 18function foLineKey(productId, axisValueId) { 19 return axisValueId ? productId + '-v' + axisValueId : '' + productId; 20} 21 22var deleteLayetteItem = function (buttonSelector, callback) { 23 var href = buttonSelector.first().attr('data-href'); 24 var productId = buttonSelector.first().attr('data-productid'); 25 $.ajax({ 26 url: href, method: 'post', beforeSend: function () { 27 buttonSelector.toggleClass('hide'); 28 }, success: function (data) { 29 if (callback != "undefined") { 30 callback(data); 31 } 32 33 //show/hide trans 34 if(data['items_count'] > 1 || data['items_count'] === 0){ 35 $('.articles-count-plural').show(); 36 $('.articles-count-singular').hide(); 37 }else{ 38 $('.articles-count-singular').show(); 39 $('.articles-count-plural').hide(); 40 } 41 42 //new cart update values 43 $('.count-product-carts').html(data['items_count']); 44 $(classUpdateShippingAmountCheckbox).html(formatCurrency(data.shipping_amount)).fadeIn(); 45 $('.shipping-amount-delivery').html(formatCurrency(data.shipping_amount_delivery)).fadeIn(); 46 $('.layette-total-net').html(formatCurrency(data.subtotal)).fadeIn(); 47 $(classUpdateTotalBeforeDiscount).html(formatCurrency(data.totalBeforeDiscount)).fadeIn(); 48 $('.layette-total-net-shipping').html(formatCurrency(data.total)).fadeIn(); 49 50 //APPLY DISCOUNT SUMMARY LINES 51 getDiscountLines(data) 52 53 // Update max amount warning after removing product 54 $('#cart-max-amount-alert').remove(); 55 if (data.exceeds_max_amount && data.max_amount_error_message) { 56 $('.cart-summary .btn-layette-content').first().before( 57 '<div class="alert alert-danger" id="cart-max-amount-alert" style="margin-bottom:10px;">' + 58 '<i class="fa fa-exclamation-triangle"></i> ' + data.max_amount_error_message + 59 '</div>' 60 ); 61 $('.payment-cart-button').prop('disabled', true).addClass('disabled'); 62 } else { 63 $('.payment-cart-button').prop('disabled', false).removeClass('disabled'); 64 } 65 66 // FARMAPREMIUM POINTS TOTAL (ORISSENS) 67 if (data.points) { 68 $(summaryAccumulatedPoints).html('+'+formatPoints(data.points.accumulated_points)); 69 $(summaryRedeemedPoints).html('-'+formatPoints(data.points.redeemed_points)); 70 $(summaryTotalPoints).html(formatPoints(data.points.total_points)); 71 $(summaryRemainingPoints).html(formatPoints(data.points.remaining_points)); 72 } 73 74 $('.cart-count').html('(' + data['items_count'] + ')'); 75 76 //left free shipping (minShipping - subtotal) TODO 77 getLeftFreeShipping(data['freeShippingLeft']); 78 79 // Toggle home delivery / no transport based on allowedHomeDelivery 80 if (data.allowedHomeDelivery === false || data.allowedHomeDelivery === null) { 81 $('label[for="home_delivery"]').hide(); 82 $('#home_delivery').prop('checked', false); 83 $('#no_transport').prop('checked', true); 84 $('.shipping-transport').hide(); 85 // Hide address fields and add notShippable hidden input 86 $('#address, #postal_code, #town_id').closest('.col-md-12, .col-md-6').hide(); 87 if ($('input[name="notShippable"]').length === 0) { 88 $('#address').closest('.row').append('<input name="notShippable" value="1" type="hidden"/>'); 89 } 90 } else if (data.allowedHomeDelivery === true) { 91 $('label[for="home_delivery"]').show(); 92 $('#home_delivery').prop('checked', true); 93 $('#no_transport').prop('checked', false); 94 $('.shipping-transport').show(); 95 // Show address fields and remove notShippable hidden input 96 $('#address, #postal_code, #town_id').closest('.col-md-12, .col-md-6').show(); 97 $('input[name="notShippable"]').remove(); 98 } 99 100 if($('.circle-count').length > 0){ 101 if(data['count'] > 0){ 102 $('.circle-count').show(); 103 }else{ 104 $('.circle-count').hide(); 105 } 106 } 107 108 //dont allow next step 109 if(data['items_count'] === 0){ 110 $('.cart-button-next').removeAttr("href").addClass('disabled'); 111 $('#payment_button').removeAttr("href").addClass('disabled'); 112 $('#payment_button').prop("disabled", true); 113 $('.payment-cart-button').removeAttr("href").addClass('disabled'); 114 $('.payment-cart-button').prop("disabled", true); 115 $('.no-articles').show(); 116 $('.count-show').hide(); 117 $('.potential-discount-container').hide(); 118 } 119 120 if (data == 0) { 121 $('#layette-custom-actions').fadeOut(); 122 $('.stickyCartButton').fadeOut(); 123 } 124 } 125 }); 126}; 127 128function formatPoints(points) { 129 if (typeof points === 'number') {
130 return points.toLocaleString('es-ES') + ' pts'; 131 } 132} 133 134function setErrorOnStockChange() { 135 let element = $('#products_no_stock'); 136 137 if (element.length) { 138 let ids = element.data('ids'); 139 let error_text = element.data('error'); 140 141 if (typeof ids === "number") { 142 ids = [ids]; 143 } 144 145 ids.forEach(function(id) { 146 $('#cart-error-' + id) 147 .show() 148 .removeClass('hide') 149 .find('span') 150 .html(error_text); 151 }); 152 } 153} 154 155 156/* 157 DOCUMENT LOADED 158 */ 159 160$(document).ready(function () { 161 162 // Quantity changer 163 jQuery('<div class="quantity-nav"><button class="quantity-button quantity-up"><i class="fa fa-chevron-up"></i></button><button class="quantity-button quantity-down"><i class="fa fa-chevron-down"></i></button></div>').insertAfter('.quantity input'); 164 $('[data-toggle="tooltip"]').tooltip(); 165 166 // TODO QTT 167 $(document).on("click", ".product-update-qty", function() { 168 var quantity = $(".product-quantity-block input"); 169 var min_quantity = 0; 170 var max_quantity = null; 171 var limit_stock = false; 172 var current_quantity = parseInt(quantity.val()); 173 174 if (typeof quantity.attr("min") != "undefined") { 175 min_quantity = quantity.attr("min"); 176 } 177 178 if (typeof quantity.attr("max") != "undefined") { 179 max_quantity = quantity.attr("max"); 180 } 181 182 if (typeof quantity.attr("limit-stock") != "undefined") { 183 limit_stock = quantity.attr("limit-stock"); 184 } 185 186 if (isNaN(quantity.val())) { 187 quantity.val(0); 188 } 189 190 if ($(this).attr("data-mode") == "up") { 191 if (!limit_stock || current_quantity < max_quantity) { 192 quantity.val(current_quantity+1); 193 } 194 } else { 195 if (parseInt(quantity.val()) > min_quantity) { 196 quantity.val(current_quantity-1); 197 } 198 } 199 }); 200 201 // TODO ADD PRODUCT TO CART 202 203 $('body').on('click', 'a.layette-add-product', function (event) { 204 event.preventDefault(); 205 // No afegir si el boto esta deshabilitat (p.ex. producte amb variants sense variant 206 // seleccionada). El boto es un <a>, aixi que prop('disabled') no te efecte; cal la classe. 207 if ($(this).hasClass('disabled')) { 208 return; 209 } 210 // if we add products from searcher engine in cart, reload (like dosfarma) 211 var reloadAfterAdd = false; 212 if ($(this).attr('data-check-reload')) { 213 var currentLocation = $(location).attr('href'); 214 if ( currentLocation.includes('cistella') || currentLocation.includes('cesta') 215 || currentLocation.includes('cesta') || currentLocation.includes('cart') 216 || currentLocation.includes('panier') ) { 217 reloadAfterAdd = true; 218 } 219 } 220 221 var product_id = $(this).attr('data-id'); 222 var paymentRadio = $('input[name="payment_option"]:checked'); 223 var payment_method = paymentRadio.length ? paymentRadio.val() : null; 224 var $addButtons = $('.ecommerce-product-add-button-' + product_id); 225 var custom = $(this).attr('data-custom'); 226 var quantitySelector = $('input[name=quantity_' + product_id + ']'); 227 228 if (quantitySelector.length != 0) { 229 var quantity = quantitySelector.val(); 230 } else { 231 var quantity = 1; 232 } 233 234 var enableQuantity = $(this).hasClass("quantity-always-enabled"); 235 $('#cart-error').hide(); 236 237 $('#cart-error-general').addClass("hidden"); 238 239 //Check if this content need reserve date 240 var reserve = processReservationDate($(this), $('input[name=type_guest]')); 241 var axisValueId = $(this).attr('data-axis-value-id'); 242 var postData = { 243 'reserve': reserve, 244 'payment_method': payment_method 245 }; 246 if (axisValueId) { 247 postData['axis_value_id'] = axisValueId; 248 } 249 $.ajax({ 250 url: $(this).attr('data-href') + '?product_id=' + product_id + '&quantity=' + quantity + '&custom=' + custom, 251 beforeSend: function () { 252 //button.toggleClass('hide'); 253 $addButtons.toggleClass('hide'); 254 }, 255 data: postData, 256 method: 'post', 257 success: function (data) { 258 259 if (typeof data.error !== 'undefined') { 260 $addButtons.toggleClass('hide'); 261 $('#cart-error-general').removeClass('hidden'); 262 $('#cart-error-general').html(data.error); 263 return; 264 } 265 266 updateCartModal(data); 267 268 var quantity = $('.product-quantity-input'); 269 var quantitySelector = $('input[name=quantity_' + data.product_id + ']'); 270 var $removeButtons = $('.ecommerce-product-remove-button-' + data.product_id); 271 272 renderProductPoints(data, null, product_id, payment_method); 273 274 quantity.val(data.product_quantity); 275 quantity.attr('data-productid', data.product_id); 276 quantity.attr('data-quantity', data.product_quantity); 277 // Sense aixo, canviar la quantitat des del modal enviava la peticio sense eix. 278 if (data.axis_value_id) { 279 quantity.attr('data-axis-value-id', data.axis_value_id); 280 } else { 281 quantity.removeAttr('data-axis-value-id'); 282 } 283 284 updateQuantityInput(data, payment_method); 285 286 $('.quantity-nav').remove(); 287 jQuery('<div class="quantity-nav"><button class="quantity-button quantity-up"><i class="fa fa-chevron-up"></i></button><button class="quantity-button quantity-down"><i class="fa fa-chevron-down"></i></button></div>').insertAfter(quantity); 288 289 if (!reloadAfterAdd) { 290 $('#cartModal').modal('show'); 291 } 292 293 $removeButtons.toggleClass('hide'); 294 if (typeof enableQuantity !== "undefined" && !en
294ableQuantity) { 295 quantitySelector.attr('disabled', '1'); 296 } 297 $('#layette-custom-actions').fadeIn(); 298 299 var $stickyCartButton = $('.stickyCartButton'); 300 var $stickyCartCount = $('#sticky-Cart-Count'); 301 $stickyCartCount.html(data.cartInfo); 302 if ($stickyCartCount.html() != 0) { 303 $stickyCartButton.fadeIn(); 304 } else { 305 $stickyCartButton.fadeOut(); 306 } 307 308 if ($('.circle-count').length > 0) { 309 if (data.count > 0) { 310 $('.circle-count').show(); 311 } else { 312 $('.circle-count').hide(); 313 } 314 } 315 316 // Google Tag Manager/eventos de analytics 317 window.dataLayer = window.dataLayer || []; 318 function gtag() { dataLayer.push(arguments); } 319 dataLayer.push({ ecommerce: null }); // Limpiar objeto anterior 320 gtag("event", data.event_type, { 321 currency: "EUR", 322 value: data.total, 323 items: [{ 324 item_id: data.product_id, 325 item_name: data.product_name, 326 price: data.payment_method === 'points' ? data.points_spent : data.product_price_quantity_after_offer, 327 quantity: data.product_quantity 328 }] 329 }); 330 331 if (reloadAfterAdd) { 332 location.reload(); 333 } 334 }, 335 error: function (thrownError) { 336 $addButtons.toggleClass('hide'); 337 $('#cart-error-general').removeClass("hidden"); 338 $('#cart-error-general').html(thrownError.statusText || thrownError.text); 339 } 340 }); 341 }); 342 343 function processReservationDate($this, $isGuest = null){ 344 if ($isGuest.val() === '1'){ 345 source = $('input[name=source]').val(); 346 appointment_at_set = $('input[name=appointment_at_set]').val(); 347 type = $('input[name=type]').val(); 348 duration = $('input[name=duration]').val(); 349 content_id = $this.attr('data-id') 350 dates = $(".stepperBar-step1 span").text() + ', ' + $(".stepperBar-step2 span").text(); 351 return {'source': source, 'appointment_at_set': appointment_at_set, 'type': type, 'content_id': content_id, 'duration': duration, 'nvgues': 1, 'price': '22', 'reserve': '1', 'dates': dates}; 352 } else { 353 return null; 354 } 355 } 356 357 $('body').on('click', '.btn-layette-disabled', function () { 358 var button = $(this); 359 var addProductButton = $('#layette-add-product-' + button.attr('data-id')); 360 var quantitySelector = $('select[name=quantity_' + button.attr('data-id') + ']'); 361 deleteLayetteItem($(this), function (data) { 362 addProductButton.toggleClass('hide'); 363 quantitySelector.removeAttr('disabled'); 364 }); 365 }); 366 367 $('body').on('click', '.layette-remove-line-checkout', function () { 368 var that = $(this); 369 var removedLineKey = that.attr('data-line-key') 370 || foLineKey(that.attr('data-productid'), that.attr('data-axis-value-id')); 371 deleteLayetteItem($(this), function (data) { 372 $('#layette-product-' + removedLineKey).fadeOut(500, function () { 373 that.toggleClass('hide'); 374 }); 375 376 if ( 377 data.total == 0.00 || 378 !data.lines || !Array.isArray(data.lines) || data.lines.length === 0 379 ) { 380 const targetUrl = $('#customLayetteIndexLink').attr('href'); 381 382 if (targetUrl) { 383 document.location.href = targetUrl; 384 } 385 } 386 387 window.dataLayer = window.dataLayer || []; 388 function gtag(){dataLayer.push(arguments);} 389 dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object. 390 391 gtag("event", data['event_type'], { 392 currency: "EUR", 393 value: data['total'], 394 items: [{ 395 item_id: data['product_id'], 396 item_name: data['product_name'], 397 price: data['product_price_quantity_after_offer'], 398 quantity: data['product_quantity'] 399 }] 400 }) 401 402 }); 403 }); 404 405 $('body').on('click', '#no_transport', function () { 406 const $this = $(this); 407 const noTransport = $this.prop('checked'); 408 409 // Limpiar errores anteriores 410 $this.find('.fop-error-container').each(function () { 411 $(this).removeClass('fop-error-container'); 412 $(this).find('.fop-error-right').remove(); 413 }); 414 415 sendDeliveryOptionRequest($this, { 416 no_transport: noTransport 417 }); 418 }); 419 420 $('body').on('click', '#home_delivery', function () { 421 const $this = $(this); 422 let noTransport = $('#no_transport').prop('checked'); 423 let homeDelivery = $('#home_delivery').prop('checked'); 424 let clickAndCollect = $('#click_and_collect').prop('checked'); 425 426 if (homeDelivery) { 427 noTransport = false;
428 clickAndCollect = false; 429 } 430 431 $('#home_delivery').prop("checked", !noTransport); 432 $('#home_delivery').prop("disabled", !noTransport); 433 434 sendDeliveryOptionRequest($this, { 435 no_transport: noTransport, 436 click_and_collect: clickAndCollect 437 }); 438 }); 439 440 $('body').on('click', '#click_and_collect', function () { 441 const $this = $(this); 442 const clickAndCollect = $this.prop('checked'); 443 444 // Limpiar errores anteriores 445 $this.find('.fop-error-container').each(function () { 446 $(this).removeClass('fop-error-container'); 447 $(this).find('.fop-error-right').remove(); 448 }); 449 450 if (clickAndCollect) { 451 $('#no_transport').prop("checked", true).prop("disabled", true).css("cursor", "not-allowed"); 452 } else { 453 $('#no_transport').prop("checked", false).prop("disabled", false).css("cursor", "pointer"); 454 } 455 456 sendDeliveryOptionRequest($this, { 457 no_transport: $('#no_transport').prop('checked'), 458 click_and_collect: clickAndCollect 459 }); 460 }); 461 462 //new buttons quantity 463 $('body').on('click', '.quantity-up', function (e) { 464 e.preventDefault(); 465 const $input = $(this).closest('.quantity').find('input.product-quantity-input'); 466 if ($input.length === 0) return; 467 468 const max = parseInt($input.attr('max')) || Infinity; 469 const currentVal = parseInt($input.val()) || 1; 470 471 if (!inputChangeCanRedeemMorePoints($input)) return false; 472 473 const newVal = Math.min(currentVal + 1, max); 474 updateQuantity($input, newVal); 475 }); 476 477 $('body').on('click', '.quantity-down', function (e) { 478 e.preventDefault(); 479 const $input = $(this).closest('.quantity').find('input.product-quantity-input'); 480 const min = parseInt($input.attr('min')) || 1; 481 const currentVal = parseInt($input.val()) || 1; 482 483 const newVal = Math.max(currentVal - 1, min); 484 if (newVal !== currentVal) { 485 updateQuantity($input, newVal); 486 } 487 }); 488 489 $('body').on('change', 'select.layette_quantity_input, select.layette_quantity_input_carrousel', function () { 490 var $select = $(this); 491 var selectName = $select.attr('name'); 492 $('select.layette_quantity_input[name=' + selectName + ']').not(this).val($select.val()); 493 $('select.layette_quantity_input_carrousel[name=' + selectName + ']').not(this).val($select.val()); 494 }); 495 496 //not used? 497 $('body').on('click', '.remodal-link', function (event) { 498 event.preventDefault(); 499 $.ajax({ 500 url: $(this).attr('href'), beforeSend: function () { 501 $('#remodal').remove(); 502 $('<div id="remodal"><div class="sk-cube-grid"><div class="sk-cube1 sk-cube"></div><div class="sk-cube2 sk-cube"></div><div class="sk-cube3 sk-cube"></div><div class="sk-cube4 sk-cube"></div><div class="sk-cube5 sk-cube"></div><div class="sk-cube6 sk-cube"></div><div class="sk-cube7 sk-cube"></div><div class="sk-cube8 sk-cube"></div><div class="sk-cube9 sk-cube"></div></div></div>').appendTo('body').remodal().open(); 503 }, success: function (data) { 504 $('#remodal').html(data); 505 } 506 }); 507 }); 508 509 //promo codes 510 $('body').on('click', '#validate_code_cart', function (event) { 511 512 event.preventDefault(); 513 var $this = $(this); 514 var validate_code_url = $this.attr("data-urlvalidate"); 515 var promo_code = $('#promo_code').val(); 516 var current_step = $this.attr('data-step'); 517 518 // remove fop-error message 519 $(this).parent().each(function () { 520 $(this).removeClass('fop-error-container') 521 $(this).find('.fop-error-right').remove() 522 $('#validate_code_cart').css("top", "10px") 523 }) 524 var $button = $(this).find('[type=submit]').last(); 525 $.ajax({ 526 type: "POST", 527 url: validate_code_url, 528 cache: false, 529 data: {'promo_code': promo_code, 'current_step': current_step}, 530 dataType: 'json', 531 success: function (data) { 532 if (data.success) { 533 $('#validate_code_cart').css("top", "10px"); 534 535 //call to function that renders section from html render on laravel controller 536 setRenderCartSectionsFromController(data) 537 538 $('#promoCodeOk').show(); 539 $('#promoCodeOk').removeClass('hide'); 540 541 $('#promoCodeError').hide(); 542 $('#promoCodeError').addClass('hide'); 543 544 $('#box-promo-code').hide(); 545 $('#box-promo-code').addClass('hide'); 546 547 } else { 548 if(data.status === 1){ 549 $('#promoCodeError').hide(); 550 $('#promoCodeError').addClass('hide'); 551 $('.fop-error-right').remove(); 552 for (var i = 0, len = data.errors.length; i < len; i++) { 553 $('[name=' + data.errors[i].field + ']').parent().before("<span class='fop-error-right'>" + data.errors[i].message + "</span>"); 554 $('[name=' + data.errors[i].field + ']').parent().parent().addClass('fop-error-container'); 555 } 556 $('#validate_code_cart').css("top", "35px"); 557 }else{ 558 $('#validate_code_cart').css("top", "10px"); 559 $('#promoCodeError').show(); 560 $('#promoCodeError').removeClass('hide'); 561 $('#promoCodeOk').hide(); 562 $('#promoCodeOk').addClass('hide'); 563 564 if(data.error == 'codeko') { 565 $('#messageCodeKo').show(); 566 $('#messageCodeKo').removeClass('hide'); 567 } else if(data.error == 'codeko_applyold'){ 568 $('#messageCodeOld').show(); 569 $('#messageCodeOld').removeClass('hide'); 570 } 571 } 572 573 574 } 575 }, 576 complete: function() { 577 $button.show(); 578 } 579 }); 580 }); 581 582 $('body').on('click', '#delete_code_cart', function (event) { 583 event.preventDefault(); 584 var $this = $(this);
585 var validate_code_url = $this.attr('data-urldelete'); 586 587 $this.find('.fop-error-container').each(function () { 588 $this.removeClass('fop-error-container'); 589 $this.find('.fop-error-right').remove(); 590 }); 591 var $button = $this.find('[type=submit]').last(); 592 $.ajax({ 593 type: "POST", 594 url: validate_code_url, 595 cache: false, 596 dataType: 'json', 597 success: function (data) { 598 if (data.success) { 599 //call to function that renders section from html render on laravel controller 600 setRenderCartSectionsFromController(data) 601 } 602 }, 603 complete: function() { 604 $button.show(); 605 } 606 }); 607 }); 608 609 // dropdown FP LOGIN 610 $(".header-farmapremium-login").on("click", function (event) { 611 event.stopPropagation(); 612 let $dropdown = $(".dropdown-menu-fp-login"); 613 614 if ($dropdown.is(":visible")) { 615 $dropdown.hide(); 616 } else { 617 $(".dropdown-menu-fp-login").hide(); 618 $dropdown.css("display", "block"); 619 } 620 }); 621 622 $(document).on("click", function (event) { 623 if (!$(event.target).closest(".farmapremium-dropdown").length) { 624 $(".dropdown-menu-fp-login").hide(); 625 } 626 }); 627 628 // TODO revisar per multiples templates 629 $('#show-mobile-farmapremium').click(function (event) { 630 event.preventDefault(); 631 $('#mobile-farmapremium').toggle(); 632 const currentOverflow = $("body").css("overflow"); 633 $("body").css("overflow", currentOverflow === "hidden" ? "scroll" : "hidden"); 634 }); 635 636 $('#close-mobile-menu-fp').click(function (event) { 637 event.preventDefault(); 638 $('#mobile-farmapremium').hide(); 639 const currentOverflow = $("body").css("overflow"); 640 $("body").css("overflow", currentOverflow === "hidden" ? "scroll" : "hidden"); 641 }) 642 643 644 setErrorOnStockChange(); 645 fireOnchangeProductQuantity(); 646}); 647 648 649function getLeftFreeShipping(freeShippingLeft) { 650 651 if(freeShippingLeft !== 0){ 652 $('#resume-shipping-left').show(); 653 $('#shipping-left').html(freeShippingLeft+ 'â¬'); 654 }else{ 655 $('#resume-shipping-left').hide(); 656 } 657 658 // Update modal free shipping message 659 var $modalShipping = $('#modal-free-shipping-left'); 660 if ($modalShipping.length) { 661 var textLeft = $modalShipping.attr('data-text-left'); 662 var textReached = $modalShipping.attr('data-text-reached'); 663 var shippingLeft = freeShippingLeft; 664 665 // If backend returns null, calculate from min and subtotal 666 if (shippingLeft === null || typeof shippingLeft === 'undefined') { 667 var min = parseFloat($modalShipping.attr('data-freeshipping-min')); 668 var subtotalText = $('#total-price-cart').text().replace('â¬', '').trim().replace(',', '.'); 669 var subtotal = parseFloat(subtotalText) || 0; 670 if (min > 0 && subtotal > 0) { 671 var left = min - subtotal; 672 shippingLeft = left > 0 ? left.toFixed(2).replace('.', ',') : 0; 673 } 674 } 675 676 if (shippingLeft && shippingLeft !== 0 && shippingLeft !== '0') { 677 $modalShipping.find('.free_shipping-modal__text').html(textLeft.replace(':amount', shippingLeft)); 678 } else { 679 $modalShipping.find('.free_shipping-modal__text').html(textReached); 680 } 681 $modalShipping.css('display', 'flex'); 682 } 683 684} 685 686// "Read more" funcionality for aside elements 687$(document).on("click", ".nav-medicines-view-more a", function() { 688 var button = $(this); 689 var elements = button.parentsUntil('ul').parent().find("li.showable"); 690 var status = button.parent().attr('data-toggle'); 691 692 elements.toArray(); 693 694 if (status == "show") { 695 button.parent().attr("data-toggle", "hide"); 696 button.find(".view-more").addClass("dnone"); 697 button.find(".view-less").removeClass("dnone"); 698 699 elements.each(function(i, el) { 700 var element_fade = setTimeout(function() { 701 $(el).fadeIn(); 702 }, 10 * i); 703 }); 704 } else { 705 button.parent().attr("data-toggle", "show"); 706 button.find(".view-more").removeClass("dnone"); 707 button.find(".view-less").addClass("dnone"); 708 709 elements.get().reverse(); 710 711 elements.each(function(i, el) { 712 var element_fade = setTimeout(function() { 713 $(el).fadeOut(); 714 }, 10 * i); 715 }); 716 } 717 718 return false;
719}); 720 721$('body').on('focus', '.product-quantity-input', function () { 722 $(this).data('prev-value', $(this).val()); 723}); 724 725var fireOnchangeProductQuantity = function() { 726 727 //enter on some browsers exec value change on input, prevent that 728 $('.product-quantity-input').on('keydown', function(e) { 729 if (e.key === 'Enter') { 730 e.preventDefault(); 731 debouncedProductQuantityChange($(this)); 732 } 733 }); 734 735 // on change qtt writing input 736 $('.product-quantity-input').on('change blur paste', function() { 737 debouncedProductQuantityChange($(this)); 738 }); 739 740 $('.layette_quantity_input').on('change blur paste', function() { 741 layetteChangeInput($(this)); 742 }); 743 744 $(".promo-question-text").click(function () { 745 $(".block-check-promo").toggle(); 746 }); 747}; 748 749// Top bar search, filter by category 750$(document).on("click", ".ecommerce-search-bar ul li a", function(e) { 751 e.preventDefault(); 752 $(".searching-category-selection-text").html("<span class='category-label truncate'>" + $(this).html() + "</span>"); 753 $(".searching-category").val(($(this).attr("data-category"))); 754}); 755 756$(document).on("focus", ".searching", function() { 757 $(this).select(); 758}); 759 760function productQuantityChange($input) { 761 var quantity = $input.val(), 762 product_id = $input.attr('data-productid'), 763 // Sense axis_value_id el backend no troba la linia de la variant i el canvi queda 764 // en un no-op silencios (HTTP 200). 765 axis_value_id = $input.attr('data-axis-value-id'), 766 href = $input.attr('data-href'), 767 paymentRadio = $('input[name="payment_option"]:checked'), 768 dataPaymentMethod = $input.attr('data-payment-method') ?? 'euros', 769 payment_method = dataPaymentMethod ? dataPaymentMethod : (paymentRadio.length ? paymentRadio.val() : null), 770 prevVal = parseInt($input.data('prev-value')) || 1; 771 772 const currentVal = parseInt($input.val()) || 1; 773 774 // CHECK IF CAN CHANGE QUANTITY 775 if (dataPaymentMethod === 'points' && currentVal > prevVal) { 776 if (!inputChangeCanRedeemMorePoints($input)) { 777 quantity = $input.val(prevVal); // previous value 778 return false; 779 } 780 } 781 782 783 //only allow numbers 784 if (typeof quantity === 'string') { 785 quantity = quantity.replace(/[^0-9]/g, "") 786 } 787 $input.val(quantity) 788 789 // only check change if input has value 790 if (!quantity) { 791 return false; 792 } 793 794 var url = href + '?product_id=' + product_id + '&quantity=' + quantity; 795 if (axis_value_id) { 796 url += '&axis_value_id=' + axis_value_id; 797 } 798 var lineKey = foLineKey(product_id, axis_value_id); 799 $.ajax({ 800 url: url, 801 data: { 802 'payment_method': payment_method 803 }, 804 method: 'post', 805 beforeSend: function () { 806 //buttonSelector.toggleClass('hide'); 807 }, 808 success: function (data) { 809 $('#cart-error').hide(); 810 $('#stock-warning-' + lineKey).hide(); 811 812 if (typeof data['error'] === "undefined") { 813 $('#cart-error-'+ lineKey).hide(); 814 } else { 815 $('#cart-error-'+ lineKey).show(); 816 $('#cart-error-'+ lineKey).removeClass('hide'); 817 $('#cart-error-'+ lineKey + ' span').html(data['error']); 818 } 819 820 // Clean previous max amount alert and re-evaluate 821 $('#cart-max-amount-alert').remove(); 822 823 // Show/hide max amount warning and disable/enable payment buttons 824 if (data.exceeds_max_amount && data.max_amount_error_message) { 825 $('.cart-summary .btn-layette-content').first().before( 826 '<div class="alert alert-danger" id="cart-max-amount-alert" style="margin-bottom:10px;">' + 827 '<i class="fa fa-exclamation-triangle"></i> ' + data.max_amount_error_message + 828 '</div>' 829 ); 830 $('.payment-cart-button').prop('disabled', true).addClass('disabled'); 831 } else { 832 $('.payment-cart-button').prop('disabled', false).removeClass('disabled'); 833 } 834 835 updateQuantityInput(data, payment_method, product_id); 836 837 $('.cart-count').html('(' + data['count'] + ')'); 838 $('#sticky-Cart-Count').html(data['cartInfo']); 839 //new 840 $('.count-product-carts').html(data['count']); 841 842 //used in products cart 843 if(data['count'] > 1 || data['count'] === 0){ 844 $('.articles-count-plural').show(); 845 $('.articles-count-singular').hide(); 846 }else{ 847 $('.articles-count-singular').show(); 848 $('.articles-count-plural').hide(); 849 } 850 851 //modal 852 $('#total-cart-products').html(data['count']); 853 $('#product-quantity').html(quantity+' x '); 854 $('#total-price-cart').html(data.subtotal + 'â¬').fadeIn(); 855 856 if (data['product_offer'] != null && data['product_price_quantity_before_offer'] != data['product_price_quantity']) { 857 $('#product-regular-price').fadeIn(); 858 $('#product-regular-price').html(data['product_price_quantity_before_offer']+ 'â¬'); 859 } else { 860 $('#product-regular-price').fadeOut(); 861 } 862 863 $('#product-price').html(data['product_price_quantity']+ 'â¬').fadeIn(); 864 865 //online shop show 866 $('#product-qtt-span').html(''); 867 $('#product-qtt-span').html(quantity); 868 869 // si tenim descompte/oferta aplicada, mostrem preu tatxat(preu previ x quantitat) amb info corresponent
870 // Guard: si la resposta no porta el camp (p.ex. URL/resposta inesperada), no petar amb .length d'undefined. 871 if (data['product_price_quantity_before_offer'] && data['product_price_quantity_before_offer'].length) { 872 var $regularPriceElement = $('#price-total-'+lineKey+'-regular-price'); 873 var payment_method = data.payment_method || 'euros'; 874 var $priceContainer = $('#price-total-'+lineKey+'-'+payment_method).parent(); 875 876 // Use line_has_discount to determine if we should show the striked price 877 // This correctly handles discounts that only apply from 2+ units 878 if (data['line_has_discount']) { 879 // If element doesn't exist, create it 880 if ($regularPriceElement.length === 0) { 881 var regularPriceHtml = '<h4 id="price-total-'+lineKey+'-regular-price" ' + 882 'style="font-size: 13px; text-decoration: line-through; color: #B1B1B1; padding: 0 .2rem;">' + 883 data['product_price_quantity_before_offer']+ 'â¬' + 884 '</h4>'; 885 886 // Insert before the main price element 887 $('#price-total-'+lineKey+'-'+payment_method).before(regularPriceHtml); 888 } else { 889 // Update and show existing element 890 $regularPriceElement.html(data['product_price_quantity_before_offer']+ 'â¬'); 891 $regularPriceElement.show(); 892 } 893 } else { 894 // Hide if exists, or do nothing if it doesn't 895 if ($regularPriceElement.length > 0) { 896 $regularPriceElement.hide(); 897 } 898 } 899 } 900 901 // get offer info 902 if (data['product_offer'] != null) { 903 904 quantity_module = data['product_offer']['apply_units'] - (quantity % data['product_offer']['apply_units']); 905 906 if ($("#product-offer-" + product_id + " .offer-product-remaining").hasClass('tooltip-message')) { 907 $("#product-offer-" + product_id + " .offer-product-applied-tooltip").hide(); 908 } 909 910 if (quantity_module == data['product_offer']['apply_units']) { 911 if ($("#product-offer-" + product_id + " .offer-product-remaining").hasClass('tooltip-message')) { 912 $("#product-offer-" + product_id + " .offer-product-applied-tooltip").show(); 913 } 914 $("#product-offer-" + product_id + " .offer-product-remaining").hide(); 915 } else { 916 $("#product-offer-" + product_id + " .offer-product-remaining big").html(quantity_module); 917 $("#product-offer-" + product_id + " .offer-product-remaining").show(); 918 919 if (quantity_module == 1) { 920 $("#product-offer-" + product_id + " .offer-product-remaining-plural").hide(); 921 $("#product-offer-" + product_id + " .offer-product-remaining-singular").show(); 922 } else { 923 $("#product-offer-" + product_id + " .offer-product-remaining-singular").hide(); 924 $("#product-offer-" + product_id + " .offer-product-remaining-plural").show(); 925 } 926 } 927 928 $(".modal-product-offer").html(data['product_offer_widget']); 929 } 930 931 let line = null; 932 933 if (Array.isArray(data.lines)) { 934 line = data.lines.find(function(l) { 935 return l.content && String(l.content.id) === String(product_id) 936 && ( 937 payment_method 938 ? l.payment_method === payment_method 939 : true 940 ); 941 }); 942 943 // FARMAPREMIUM POINTS TOTAL (ORISSENS) 944 if (data.points) { 945 $(summaryAccumulatedPoints).html('+'+formatPoints(data.points.accumulated_points)); 946 $(summaryRedeemedPoints).html('-'+formatPoints(data.points.redeemed_points)); 947 $(summaryTotalPoints).html(formatPoints(data.points.total_points)); 948 $(summaryRemainingPoints).html(formatPoints(data.points.remaining_points)); 949 } 950 } 951 952 renderProductPoints(data, line, product_id, payment_method); 953 954 $('#product-quantity-'+product_id+'').html('x '+quantity); 955 956 //APPLY DISCOUNT SUMMARY LINES 957 getDiscountLines(data) 958 959 //DATA SUBTOTAL NOT USED ANYMORE, getDiscountLines -> subtotalWithoutDiscount 960 //$('.layette-total-net').html(data.subtotal + 'â¬').fadeIn(); 961 $(classUpdateShippingAmountCheckbox).html(formatCurrency(data.shipping_amount)).fadeIn(); 962 $('.shipping-amount-delivery').html(formatCurrency(data.shipping_amount_delivery)).fadeIn(); 963 964 $(classUpdateTotalBeforeDiscount).html(data.totalBeforeDiscount + 'â¬').fadeIn(); 965 966 $('.layette-total-net-shipping').html(formatCurrency(data.total)).fadeIn(); 967 968 //left free shipping (minShipping - subtotal) 969 getLeftFreeShipping(data['freeShippingLeft']); 970 971 //todo sum prices + shipment 972 if (data == 0) { 973 $('#layette-custom-actions').fadeOut(); 974 $('.stickyCartButton').fadeOut(); 975 } 976 }, 977 error: function(data) { 978 979 var jsonResponse = $.parseJSON(data.responseText); 980 981 $('.product-quantity-input[data-productid="' + jsonResponse.productId + '"]').val(jsonResponse.count); 982 $('#product-quantity').html(jsonResponse.quantityLimit + ' x '); 983 984 $('#cart-error').show(); 985 $('#cart-error').removeClass('hide'); 986 $('#cart-error span').html(jsonResponse.error); 987 988 // lineKey i no jsonResponse.productId: la resposta d'error no porta l'eix. 989 $('#cart-error-'+ lineKey).show(); 990 $('#cart-error-'+ lineKey).removeClass('hide'); 991 $('#cart-error-'+ lineKey + ' span').html(jsonResponse.error); 992 993 return false;
994 } 995 }); 996} 997 998// debounce on input change (avoid multiple requests if user change input more than one time) 999const debouncedProductQuantityChange = debounce(function($input) { 1000 productQuantityChange($input); 1001}, 400); 1002 1003function layetteChangeInput($input) { 1004 var $this = $input; 1005 var value = $this.val().replace(/[^0-9]/g, ''); 1006 var max = parseInt($this.attr('max')); 1007 var limit_stock = $this.attr("limit-stock"); 1008 1009 if (parseInt(value) > max && limit_stock) { 1010 value = max>0?max:1; 1011 } else if (parseInt(value) < 1) { 1012 value = 1; 1013 } 1014 $this.val(value); 1015} 1016 1017function getDiscountLines (data) { 1018 1019 //SUBTOTAL WITHOUT DISCOUNT -> NOW WE ALWAYS USE THIS ONE AND SHOW DISCOUNTS AFTER 1020 $('#layette-total-net-no-discount').html(formatCurrency(data['subtotal'])); 1021 1022 // descompte total carret (INCLOU SUMA TOTS DESCOMPTES/OFERTES) 1023 $('#cart-total-discount').html('-'+formatCurrency(data['discount'])); 1024 1025 // hide discount parent line if it is 0 1026 if(data['discount'] === 0) { 1027 $('#cart-total-discount').parent().hide(); 1028 $(noDiscountTotalsSummary).show(); 1029 $(discountTotalsSummary).hide(); 1030 } else { 1031 $('#cart-total-discount').parent().show(); 1032 $(discountTotalsSummary).show(); 1033 $(noDiscountTotalsSummary).hide(); 1034 } 1035 1036 if (data['discount'] === 0) { 1037 $(totalBeforeDiscountLoggedFP).hide(); 1038 $(discountBoxSummary).hide(); 1039 } else { 1040 $(totalBeforeDiscountLoggedFP).show(); 1041 $(discountBoxSummary).show(); 1042 } 1043 1044 // descompte potencial farmapremium NO LOGIN - NO APLICA INFORMATIU 1045 $('#farmapremium-total-discount-message').html(data['discount_farmapremium_message']); 1046 $('#price-total-without-fp').html(data['total_without_farmapremium']+ 'â¬'); 1047 1048 // Show/hide potential discount container based on message content 1049 if (data['discount_farmapremium_message'] && data['discount_farmapremium_message'].length > 0) { 1050 $('.potential-discount-container').show(); 1051 } else { 1052 $('.potential-discount-container').hide(); 1053 } 1054 1055 // Punto único: con CUALQUIER cambio de carrito (añadir, cantidad, eliminar, canje) que 1056 // devuelva el estado del canje FP, reajusta el tope del slider (BUG-655). 1057 if (typeof fpApplyRedeemState === 'function' && typeof data !== 'undefined' && typeof data.fpRedeem !== 'undefined') { 1058 fpApplyRedeemState(data.fpRedeem); 1059 } 1060} 1061 1062function setRenderCartSectionsFromController (data) { 1063 1064 // get previous form values from form 1065 let previousFormValues = {}; 1066 $('#cart-address-collapse :input').each(function () { 1067 previousFormValues[this.name] = $(this).val(); 1068 }); 1069 let previousTownId = $('#town_id').val(); 1070 let previousTownName = $('#town_id').find('option:selected').text(); 1071 1072 $('.cart-summary-fixed').html(data.htmlCart_fixed); 1073 $('.cart-summary-fixed').removeClass('hide'); 1074 $('.cart-summary-fixed').show(); 1075 1076 if (data.htmlCart_content) { 1077 $('.cart-container').html(data.htmlCart_content); 1078 jQuery('<div class="quantity-nav"><button class="quantity-button quantity-up"><i class="fa fa-chevron-up"></i></button><button class="quantity-button quantity-down"><i class="fa fa-chevron-down"></i></button></div>').insertAfter('.quantity input'); 1079 $('[data-toggle="tooltip"]').tooltip(); 1080 fireOnchangeProductQuantity(); 1081 } 1082 1083 // set previous form values before action (inputs) 1084 for (let inputName in previousFormValues) { 1085 $('[name="'+inputName+'"]').val(previousFormValues[inputName]); 1086 } 1087 1088 if(data.js_content) { 1089 $('.js-section').html(data.js_content); 1090 } 1091 1092 // Restore select2 value (poblacion) 1093 if (previousTownId) { 1094 let $town = $('#town_id'); 1095 // Check if the option already exists 1096 if ($town.find('option[value="' + previousTownId + '"]').length) { 1097 $town.val(previousTownId).trigger('change'); 1098 } else { 1099 // If the option does not exist, create it 1100 let newOption = new Option(previousTownName, previousTownId, true, true); 1101 $town.append(newOption).trigger('change'); 1102 } 1103 } 1104 1105} 1106 1107function updateCartModal(data) { 1108 $('.cart-count').html('(' + data.count + ')'); 1109 $('#cart-image-product') 1110 .html(data.product_image) 1111 .attr('data-href', data.product_slug); 1112 $('#cart-image-name') 1113 .html(data.product_name) 1114 .attr('data-href', data.product_slug); 1115 $('#product-quantity').html(data.product_quantity + ' x'); 1116 $('.modal-product-offer').html(data.product_offer_widget); 1117 $('#total-cart-products').html(data.count); 1118 1119 // Update free shipping left in modal 1120 if (typeof data.freeShippingLeft !== 'undefined') { 1121 getLeftFreeShipping(data.freeShippingLeft); 1122 } 1123} 1124 1125 1126function updateQuantityInput(data, payment_method_selected = 'euros', selected_product_id = null) { 1127 var productId = selected_product_id ?? data.product_id; 1128 if (!productId && Array.isArray(data.lines) && data.lines.length === 1) { 1129 productId = data.lines[0].content.id; 1130 } 1131 1132 // 1. Busca la lÃnea del producto y método de pago seleccionado 1133 if (payment_method_selected === null) { 1134 payment_method_selected = 'euros'; 1135 } 1136 1137 var line = null; 1138 if (Array.isArray(data.lines)) { 1139 line = data.lines.find(function(l) { 1140 return l.content && String(l.content.id) === String(productId) 1141 && ( 1142 payment_method_selected 1143 ? l.payment_method === payment_method_selected 1144 : true 1145 ); 1146 }); 1147 } 1148
1149 // 2. Busca el input que coincide con el producto y el método de pago 1150 var selector = '.product-quantity-input[data-productid="' + productId + '"]'; 1151 // Amb variants hi ha diversos inputs amb el mateix data-productid: sense filtrar per 1152 // l'eix, escrivim la quantitat d'una linia a l'altra. 1153 var axisValueId = (line && line.axis_value_id) || data.axis_value_id; 1154 if (axisValueId) { 1155 selector += '[data-axis-value-id="' + axisValueId + '"]'; 1156 } 1157 // TODO check 1158 if (line && line.payment_method) { 1159 selector += '[data-payment-method="' + line.payment_method + '"]'; 1160 } else if (payment_method_selected) { 1161 selector += '[data-payment-method="' + payment_method_selected + '"]'; 1162 } 1163 var $input = $(selector); 1164 if ($input.length === 0) { 1165 // fallback: busca solo por product_id (+ eix) 1166 var fallbackSelector = '.product-quantity-input[data-productid="' + productId + '"]'; 1167 if (axisValueId) { 1168 fallbackSelector += '[data-axis-value-id="' + axisValueId + '"]'; 1169 } 1170 $input = $(fallbackSelector).first(); 1171 } 1172 1173 1174 // 3. Detecta el valor de remaining_points 1175 var remainingPoints = ''; 1176 if (data.points && typeof data.points.remaining_points !== 'undefined') { 1177 remainingPoints = data.points.remaining_points; 1178 } else if (typeof data.remaining_points !== 'undefined') { 1179 remainingPoints = data.remaining_points; 1180 } else if (line && typeof line.remaining_points !== 'undefined') { 1181 remainingPoints = line.remaining_points; 1182 } 1183 1184 // 4. Actualiza el input solo si lo encuentra 1185 if ($input.length > 0) { 1186 if (line) { 1187 $input.attr('data-redemption-points', line.redemption_points || ''); 1188 $input.attr('data-payment-method', line.payment_method || ''); 1189 $input.attr('data-remaining-points', remainingPoints !== '' ? remainingPoints : ''); 1190 $input.attr('data-quantity', line.quantity || 1); 1191 $input.val(line.quantity || 1); 1192 } else { 1193 $input.attr('data-redemption-points', data.redemption_points || ''); 1194 $input.attr('data-payment-method', data.payment_method || ''); 1195 $input.attr('data-remaining-points', remainingPoints !== '' ? remainingPoints : ''); 1196 $input.attr('data-quantity', data.product_quantity || 1); 1197 $input.val(data.product_quantity || 1); 1198 1199 if(remainingPoints && $(productShowRemainingUserPoints).length) { 1200 $(productShowRemainingUserPoints).html(formatPoints(remainingPoints)); 1201 } 1202 1203 // Productr show -> dont allow to add again if user points are not enough 1204 if ($(productShowInputPointsSelected).length) { 1205 1206 if ( $(productShowInputPointsSelected).attr('data-product-points') > remainingPoints) { 1207 // disable points option 1208 $(productShowInputPointsSelected).prop("disabled", true); 1209 1210 // select euros option 1211 var paymentOptionEurosNotDisabled = $('.payment-option-radio:not(:disabled)').first(); 1212 1213 if (paymentOptionEurosNotDisabled.length) { 1214 paymentOptionEurosNotDisabled.prop('checked', true); 1215 } 1216 } else { 1217 $(productShowInputPointsSelected).prop("disabled", false); 1218 } 1219 1220 } 1221 } 1222 1223 } 1224} 1225 1226// function used on address options (no transport(recogida) / click and collect / home_delivery) 1227function sendDeliveryOptionRequest($element, extraData = {}) { 1228 const url = $element.attr('data-url'); 1229 const pharmacyId = $element.attr('data-pharmacy-id'); 1230 const farmapremiumLogin = $element.attr('data-farmapremium'); 1231 const $button = $element.find('[type=submit]').last(); 1232 1233 $.ajax({ 1234 type: "POST", 1235 url: url, 1236 cache: false, 1237 data: { 1238 pharmacyId, 1239 farmapremiumLogin, 1240 ...extraData 1241 }, 1242 dataType: 'json', 1243 beforeSend: function () { 1244 $button.hide(); 1245 $button.after(` 1246 <div class="cssload-square"> 1247 <div class="cssload-square-part cssload-square-green"></div> 1248 <div class="cssload-square-part cssload-square-pink"></div> 1249 <div class="cssload-square-blend"></div> 1250 </div> 1251 `); 1252 }, 1253 success: function (data) { 1254 setRenderCartSectionsFromController(data); 1255 }, 1256 error: function (thrownError) { 1257 $('#cart-error').removeClass('hidden').html(thrownError.error || 'Error'); 1258 }, 1259 complete: function () { 1260 $('.cssload-square').remove(); 1261 $button.show(); 1262 $('#summary-message-code').hide().fadeIn(); 1263 } 1264 }); 1265} 1266 1267 1268function renderProductPoints(data, line = null, product_id = null, payment_method = null) { 1269 1270 let lineKey = foLineKey(product_id, (line && line.axis_value_id) || data.axis_value_id); 1271 1272 let modal_product_price = $('#product-price'); 1273 let modal_product_regular_price = $('#product-regular-price'); 1274 let modal_product_unitary_price = $('#product-price-unitary'); 1275 let modal_total_price = $('#total-price-cart'); 1276 1277 let redemption_points = Number(data.redemption_points); 1278 let product_quantity = Number(data.product_quantity); 1279 let points_spent = data.points_spent; 1280 let product_price_quantity = data.product_price_quantity; 1281 let payment_method_final = data.payment_method; 1282 1283 if (line) { 1284 redemption_points = Number(line.redemption_points); 1285 product_quantity = Number(line.quantity); 1286 points_spent = Number(line.points_spent); 1287 product_price_quantity = line.total_line; 1288 payment_method_final = line.payment_method; 1289 } 1290
1291 let puntosLinea = redemption_points * product_quantity; 1292 if (Number(points_spent) > 0) puntosLinea = Number(points_spent); 1293 1294 if (payment_method_final === 'points') { 1295 modal_product_price.html(formatPoints(puntosLinea)); 1296 modal_product_regular_price.html(''); 1297 modal_product_unitary_price.html(formatPoints(redemption_points)); 1298 modal_total_price.html(formatPoints(puntosLinea)); 1299 1300 if (product_id && payment_method) { 1301 $('#price-total-' + lineKey + '-' + payment_method).html(formatPoints(redemption_points * product_quantity)); 1302 } 1303 } else { 1304 let shouldShowOffer = data.product_price_quantity_before_offer != data.product_price_quantity_after_offer; 1305 modal_product_price.html(data.product_price_quantity_after_offer + 'â¬'); 1306 modal_product_regular_price.html(shouldShowOffer ? data.product_price_quantity_before_offer + 'â¬' : ''); 1307 modal_product_unitary_price.html(data.product_price_unitary + 'â¬'); 1308 modal_total_price.html(data.total + 'â¬'); 1309 1310 if (product_id && payment_method) { 1311 $('#price-total-' + lineKey + '-' + payment_method).html(formatCurrency(product_price_quantity)); 1312 } 1313 } 1314} 1315 1316 1317 1318function formatCurrency(value) { 1319 let number = parseFloat(value); 1320 if (isNaN(number)) { 1321 return '0,00â¬'; // Si el valor no es un número válido, devuelve 0⬠1322 } 1323 return number.toLocaleString('es-ES', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + 'â¬'; 1324} 1325 1326function inputChangeCanRedeemMorePoints($input) { 1327 const redemptionPoints = Number($input.attr('data-redemption-points')) || 0; 1328 const remainingPoints = Number($input.attr('data-remaining-points')) || 0; 1329 const paymentMethod = $input.attr('data-payment-method') ?? 'euros'; 1330 const quantity = parseInt($input.val()) || 1; 1331 1332 if (paymentMethod === 'points' && remainingPoints < redemptionPoints) { 1333 alert('No tienes suficientes puntos para añadir más unidades de este producto.'); 1334 return false; 1335 } 1336 return true; 1337} 1338 1339function updateQuantity($input, newVal) { 1340 $input.val(newVal); 1341 $input.data('prev-value', newVal); 1342 productQuantityChange($input); 1343} 1344 1345function debounce(func, wait, immediate) { 1346 var timeout; 1347 return function() { 1348 var context = this, 1349 args = arguments; 1350 var later = function() { 1351 timeout = null; 1352 if (!immediate) func.apply(context, args); 1353 }; 1354 var callNow = immediate && !timeout; 1355 clearTimeout(timeout); 1356 timeout = setTimeout(later, wait); 1357 if (callNow) func.apply(context, args); 1358 }; 1359};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.