PageSourceSearch

https://www.farmacianeusmartinez.cat/assets/common/js/fpRedeemCart.js?v=2026092902

js farmacianeusmartinez.cat collected 2026-10-04 22:15:03 UTC 11,042 bytes, 266 lines download raw bytes

1/**
2 * Canje FarmaPremium a NIVEL CARRITO (BUG-655).
3 *
4 * Depende de newCart.js (formatCurrency, formatPoints, getDiscountLines y los selectores
5 * de resumen definidos en su cabecera): cargar SIEMPRE después de newCart.js.
6 * Solo se incluye en el layout cuando el canje está disponible ($fpRedemptionAvailable),
7 * por lo que las farmacias del club de marca (Orissens/Nakama) nunca descargan este fichero.
8 */
9
10$(document).ready(function () {
11
12    // Refresca el resumen del carrito tras aplicar/quitar canje (mismo shape que cart.quantity).
13    function fpRefreshCartSummary(data) {
14        $('.cart-count').html('(' + (data['count'] || 0) + ')');
15        $('#sticky-Cart-Count').html(data['cartInfo'] || data['count'] || 0);
16        $('.count-product-carts').html(data['count'] || 0);
17
18        $(classUpdateShippingAmountCheckbox).html(formatCurrency(data.shipping_amount)).fadeIn();
19        $('.shipping-amount-delivery').html(formatCurrency(data.shipping_amount_delivery || data.shipping_amount)).fadeIn();
20        $(classUpdateTotalBeforeDiscount).html(data.totalBeforeDiscount + '€').fadeIn();
21        $('.layette-total-net-shipping').html(formatCurrency(data.total)).fadeIn();
22
23        getDiscountLines(data);
24
25        if (data.points) {
26            $(summaryAccumulatedPoints).html('+' + formatPoints(data.points.accumulated_points));
27            $(summaryRedeemedPoints).html('-' + formatPoints(data.points.redeemed_points));
28            $(summaryTotalPoints).html(formatPoints(data.points.total_points));
29            $(summaryRemainingPoints).html(formatPoints(data.points.remaining_points));
30        }
31    }
32
33    // Fuente de verdad del importe a canjear: el input editable (admite cualquier valor).
34    function fpCartEuros($c) {
35        var v = parseFloat($c.find('.fp-redeem-cart-input').val());
36        return isNaN(v) ? 0 : v;
37    }
38
39    function fpCartToken() {
40        return $('input[name=_token]').first().val();
41    }
42
43    // Muestra el motivo del rechazo del back. 'code' es el error que devuelve el back
44    // (insufficient_balance tiene texto propio); cualquier otro cae al mensaje genérico.
45    function fpCartShowError($c, code) {
46        var texto = (code === 'insufficient_balance')
47            ? $c.attr('data-error-insufficient-text')
48            : $c.attr('data-error-text');
49
50        if (!texto) {
51            return;
52        }
53
54        $c.find('.fp-redeem-cart-error').text(texto).css('display', '');
55    }
56
57    function fpCartHideError($c) {
58        $c.find('.fp-redeem-cart-error').text('').css('display', 'none');
59    }
60
61    function fpCartBounds($c) {
62        return {
63            min: parseFloat($c.attr('data-min-euros')) || 0,
64            max: parseFloat($c.attr('data-max-euros')) || 0
65        };
66    }
67
68    // Abrir/cerrar el bloque de canje (cabecera clic)
69    $('body').on('click', '.fp-redeem-cart-head', function () {
70        var $cart = $(this).closest('.fp-redeem-cart');
71        $cart.toggleClass('is-open');
72        $cart.find('.fp-redeem-cart-body').stop(true, true).slideToggle(180);
73    });
74
75    // Slider -> input (sincroniza al arrastrar)
76    $('body').on('input change', '.fp-redeem-cart-slider', function () {
77        var $c = $(this).closest('.fp-redeem-cart');
78        $c.find('.fp-redeem-cart-input').val(parseFloat($(this).val()).toFixed(2));
79        // Al tocar el importe el error anterior deja de aplicar: si no, se queda pegado.
80        fpCartHideError($c);
81    });
82
83    // Input -> slider. Mientras escribe solo refleja en el slider; al salir (change) se acota.
84    $('body').on('input', '.fp-redeem-cart-input', function () {
85        fpCartHideError($(this).closest('.fp-redeem-cart'));
86        var $c = $(this).closest('.fp-redeem-cart');
87        var v = parseFloat($(this).val());
88        if (!isNaN(v)) {
89            $c.find('.fp-redeem-cart-slider').val(v);
90        }
91    });
92    $('body').on('change', '.fp-redeem-cart-input', function () {
93        var $c = $(this).closest('.fp-redeem-cart');
94        var b = fpCartBounds($c);
95        var v = parseFloat($(this).val());
96        if (isNaN(v)) { v = b.min; }
97        v = Math.min(b.max, Math.max(b.min, Math.round(v * 100) / 100));
98        $(this).val(v.toFixed(2));
99        $c.find('.fp-redeem-cart-slider').val(v);
100    });
101
102    // Aplicar canje (a nivel carrito)
103    $('body').on('click', '.fp-redeem-cart-apply', function () {
104        var $btn = $(this);
105        var $c = $btn.closest('.fp-redeem-cart');
106        var ratio = parseFloat($c.attr('data-ratio')) || 0.01;
107        var euros = fpCartEuros($c);
108        var points = Math.round(euros / ratio);
109        if (points <= 0 || $btn.prop('disabled')) {
110            return;
111        }
112        $btn.prop('disabled', true);
113
114        $.ajax({
115            url: $c.attr('data-apply-url'),
116            method: 'post',
117            data: { points: points, _token: fpCartToken() },
118            success: function (data) {
119                // El importe que manda es el CONFIRMADO por el back, no el que se pidió: si el
120                // back lo rechaza o lo clampa, pintar el pedido mentiría al usuario (se veía
121                // "Canje aplicado: 5,00 €" con el total sin descontar). Sin confirmación
122                // (fpRedeem ausente o sin applied) el fallback es 0 -> rama de error; NUNCA
123                // el importe pedido, que resucitaría exactamente esa mentira.
124                var applied = 0;
125                if (data && data.fpRedeem && typeof data.fpRedeem.applied !== 'undefined') {
126                    applied = parseFloat(data.fpRedeem.applied) || 0;
127                }
128
129                fpRefreshCartSummary(data);
130                $btn.prop('disabled', false);
131
132                if (!applied || applied <= 0) {
133                    fpCartShowError($c, null);
134                    return;
135                }
136
137                fpCartHideError($c);
138                var tpl = $c.attr('data-applied-text') || ':euros €';
139                $c.find('.fp-redeem-cart-applied-text').text(tpl.replace(':euros', applied.toFixed(2).replace('.', ',')));
140                // Estado aplicado: oculta cabecera/flecha y controles (lo gestiona la clase + SCSS).
141                $c.removeClass('is-open').addClass('is-applied');
142                $c.find('.fp-redeem-cart-body').css('display', 'block');
143                // Convergencia de descuentos: código y canje COEXISTEN. Ya NO se oculta el mensaje
144                // del código promocional al canjear (ambos descuentos conviven en el resumen).
145            },
146            error: function (xhr) {
147                $btn.prop('disabled', false);
148
149                // 422 = el back rechaza el canje con un motivo. Antes se ignoraba en silencio.
150                var code = null;
151                try {
152                    code = (xhr.responseJSON || JSON.parse(xhr.responseText || '{}')).error || null;
153                } catch (e) {
154                    code = null;
155                }
156
157                fpCartShowError($c, code);
158            }
159        });
160    });
161
162    // Quita el canje vía back y devuelve el bloque al estado sin canje (cabecera y controles).
163    function fpCartRemoveRedeem($c, onDone) {
164        $.ajax({
165            url: $c.attr('data-remove-url'),
166            method: 'post',
167            data: { _token: fpCartToken() },
168            success: function (data) {
169                fpRefreshCartSummary(data);
170                $c.removeClass('is-applied').addClass('is-open');
171                $c.find('.fp-redeem-cart-applied').css('display', '');
172                $c.find('.fp-redeem-cart-controls').css('display', '');
173                $c.find('.fp-redeem-cart-body').css('display', 'block');
174                if (onDone) { onDone(); }
175            },
176            error: function () {
177                if (onDone) { onDone(); }
178            }
179        });
180    }
181
182    // Quitar canje (a nivel carrito)
183    $('body').on('click', '.fp-redeem-cart-remove', function () {
184        var $btn = $(this);
185        var $c = $btn.closest('.fp-redeem-cart');
186        if ($btn.prop('disabled')) {
187            return;
188        }
189        $btn.prop('disabled', true);
190        fpCartRemoveRedeem($c, function () {
191            $btn.prop('disabled', false);
192        });
193    });
194
195    // El canje NO está soportado con transferencia bancaria (el CRM no dispara la transacción
196    // FP al confirmar el pago): al elegirla se quita el canje aplicado y se oculta el bloque.
197    function fpSyncRedeemWithPaymentMethod() {
198        var $c = $('.fp-redeem-cart');
199        if (!$c.length) { return; }
200        if (fpTransferPaymentSelected()) {
201            if ($c.hasClass('is-applied')) {
202                fpCartRemoveRedeem($c);
203            }
204            $c.hide();
205        } else {
206            $c.show();
207        }
208    }
209
210    $('body').on('change', 'input[name=payment_method]', fpSyncRedeemWithPaymentMethod);
211    // Estado inicial: la transferencia puede venir preseleccionada (farmacia sin TPV).
212    fpSyncRedeemWithPaymentMethod();
213
214});
215
216// ¿Está seleccionada la transferencia bancaria como método de pago?
217function fpTransferPaymentSelected() {
218    return $('input[name=payment_method]:checked').val() === 'transfer';
219}
220
221// Reajusta el tope del canje FP (slider + input) cuando cambia el neto del carrito.
222// GLOBAL: la invoca getDiscountLines (newCart.js) tras cualquier cambio de carrito.
223function fpApplyRedeemState(state) {
224    var $cart = $('.fp-redeem-cart');
225    if (!$cart.length) { return; }
226
227    if (!state || !state.available || fpTransferPaymentSelected()) {
228        $cart.hide();
229        return;
230    }
231    $cart.show();
232
233    var max = parseFloat(state.max) || 0;
234    var min = parseFloat($cart.attr('data-min-euros')) || 0;
235    $cart.attr('data-max-euros', max);
236
237    var $slider = $cart.find('.fp-redeem-cart-slider');
238    var $input = $cart.find('.fp-redeem-cart-input');
239    $slider.attr('max', max);
240    $input.attr('max', max);
241
242    // Si el valor seleccionado supera el nuevo tope, acotarlo.
243    var cur = parseFloat($input.val());
244    if (isNaN(cur)) { cur = min; }
245    if (cur > max) {
246        cur = Math.max(min, max);
247        $slider.val(cur);
248        $input.val(cur.toFixed(2));
249    }
250
251    // Reflejar el estado del canje recalculado por el back tras cualquier cambio de carrito.
252    var applied = parseFloat(state.applied) || 0;
253    if (applied >= min) {
254        // Sigue aplicado: refrescar el importe mostrado (el back lo reclampa al neto).
255        if ($cart.hasClass('is-applied')) {
256            var tpl = $cart.attr('data-applied-text') || ':euros €';
257            $cart.find('.fp-redeem-cart-applied-text').text(tpl.replace(':euros', applied.toFixed(2).replace('.', ',')));
258        }
259    } else if ($cart.hasClass('is-applied')) {
260        // El back ELIMINÓ el canje (ya no cabe): volver al estado no-aplicado (reaparece el slider).
261        $cart.removeClass('is-applied').addClass('is-open');
262        $cart.find('.fp-redeem-cart-applied').css('display', '');
263        $cart.find('.fp-redeem-cart-controls').css('display', '');
264        $cart.find('.fp-redeem-cart-body').css('display', 'block');
265    }
266}

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.