FOX - WooCommerce Currency Switcher Professional

Redondear precios convertidos en WooCommerce: .99, enteros y múltiplos

Un tipo de cambio convierte un limpio 49,00 en 45,37 o en 4.083,12, y pocas tiendas quieren mostrar eso. FOX no tiene un ajuste de redondeo, porque cada tienda quiere un patrón distinto, pero ofrece dos filtros para redondear los precios convertidos en WooCommerce como prefieras: números enteros, terminaciones en .99 o .95, múltiplos de 5, 10, 100 o 1000, por moneda. La mayoría de los fragmentos de código que "no funcionan" fallan por una de estas razones: solo se usa uno de los dos filtros, también se redondea la moneda base, los impuestos se añaden después del redondeo o el código no se ejecuta en la petición que vuelve a dibujar los precios. Esta guía repasa cada una de ellas.

Dos filtros, y por qué necesitas ambos

FOX convierte los precios en dos lugares, y cada uno tiene su propio filtro:

  • woocs_raw_woocommerce_price recibe cada precio convertido por separado: productos simples, cada variación y, con Es múltiple permitido (Is multiple allowed) = No, cada importe mostrado en la moneda seleccionada.
  • woocs_woocommerce_variation_prices recibe los precios que WooCommerce reúne para un producto variable con el fin de construir su rango de precios, el "45,37 – 61,20" bajo el título. Se usa con Es múltiple permitido (Is multiple allowed) = Sí (Yes); con No, el rango se muestra a través del primer filtro.

Con solo el primer filtro, un producto variable muestra un rango sin redondear y, al seleccionar una variación, un precio redondeado. Con solo el segundo, el rango se redondea y la variación seleccionada no. Asocia la misma función a los dos.

Para una moneda convertida, ambos filtros reciben una cadena numérica con punto como separador decimal, ya redondeada según el ajuste Decimal de la moneda en la pestaña Divisas (Currencies). Los dos se ejecutan también para la moneda base, sin conversión, así que la función debe decidir sobre qué monedas actuar.

Una función de redondeo como punto de partida

/*
 * Round converted prices per currency.
 * Child theme functions.php, or a snippets plugin set to run everywhere.
 */
function myshop_woocs_round_price($price) {
    global $WOOCS;
    if (!isset($WOOCS) || !is_numeric($price) || (float) $price <= 0) {
        return $price; // keep empty and free prices as they are
    }
    $p = (float) $price;

    switch ($WOOCS->current_currency) {
        case 'EUR':
        case 'GBP':
            $p = ceil($p) - 0.01;      // 45.37 -> 45.99
            break;
        case 'JPY':
            $p = ceil($p / 100) * 100; // 4,083 -> 4,100
            break;
        default:
            return $price;             // the base currency and all others stay exact
    }
    return $p > 0 ? $p : $price;
}
add_filter('woocs_raw_woocommerce_price', 'myshop_woocs_round_price');
add_filter('woocs_woocommerce_variation_prices', 'myshop_woocs_round_price');

Sustituye los códigos de moneda y las fórmulas por los tuyos. Dejar la moneda base fuera del switch es importante: de lo contrario, los precios que escribiste en tus productos también se redondean, y el importe que introdujiste deja de ser el importe que cobras.

Recetas para redondear precios

Pon cualquiera de estas fórmulas en lugar de las líneas del switch; $p es el precio convertido.

ResultadoFórmula45,37 se convierte en
Número entero más cercanoround($p)45
Número entero, siempre hacia arriba / hacia abajoceil($p) / floor($p)46 / 45
Terminación .99, hacia arribaceil($p) - 0.0145,99
Terminación .99, la más cercanaround($p + 0.01) - 0.0144,99
Terminación .95, hacia arribaceil($p) - 0.0545,95
Múltiplo de 0,50 más cercanoround($p * 2) / 245,50
Hacia arriba, al siguiente múltiplo de 5ceil($p / 5) * 550
Múltiplo de 10 / 100 / 1000 más cercanoround($p, -1) / -2 / -350 / 0 / 0
Hacia arriba, terminado en 9 (49, 149, 1.999)ceil($p / 10) * 10 - 149

Los múltiplos de 100 o de 1000 solo tienen sentido en monedas donde los precios son tan altos; el cero de la tabla es la advertencia. Para terminaciones como .99, la moneda debe mostrar dos decimales: con Decimal en 0, el resultado se muestra como un número entero. Las FAQ breves sobre 99 céntimos, 50 céntimos, números enteros y múltiplos de 500 muestran versiones para una sola moneda.

Dos cosas que parecen redondeo no lo son. Ocultar centavos (Hide cents) en la pestaña Divisas (Currencies) quita los céntimos del precio mostrado y los deja en el total del carrito, de modo que un producto que aparece como 45 se cobra a 45,37. Precios sin centavos (Prices without cents) en la pestaña Opciones (Options) recalcula las monedas de la lista con cero decimales, lo que sí es un redondeo real a números enteros; su propia descripción te pide probar el pago tras activarlo, y eso incluye un pedido con un cupón de porcentaje.

Los precios fijos que introduces por moneda también pasan por woocs_raw_woocommerce_price. Una fórmula deja sin cambios un precio que ya encaja en su patrón, así que un 19,99 fijo se queda en 19,99 con la regla .99, pero un 20 fijo pasa a 19,99. Excluye del switch las monedas cuyos precios fijos deban quedar tal como se introdujeron.

Redondeo e impuestos

woocs_raw_woocommerce_price se ejecuta sobre el precio del producto antes de que WooCommerce añada o quite el impuesto. woocs_woocommerce_variation_prices es distinto: cuando la tienda muestra los precios con impuestos incluidos, el rango del producto variable que recibe ya contiene el impuesto. Lo que eso implica depende de Precios introducidos con impuestos (Prices entered with tax) y Mostrar precios en la tienda (Display prices in the shop) en WooCommerce → Ajustes → Impuestos.

Precios introducidos sin impuestos, mostrados con impuestos

WooCommerce añade el impuesto a tu precio neto redondeado, y 45,99 más un 20 % se muestra como 55,19, mientras que el rango del producto variable, redondeado después del impuesto, termina en .99. Para que todos los precios que ve el cliente terminen en .99, redondea el importe bruto en el primer filtro y vuelve a convertirlo con tu tasa, y mantén la fórmula simple en el segundo. Esto funciona cuando a tus clientes se les aplica una sola tasa de impuesto:

/*
 * .99 endings on prices shown with 20% tax. Child theme functions.php.
 */
add_filter('woocs_raw_woocommerce_price', function ($price) {
    global $WOOCS;
    if (!isset($WOOCS) || !is_numeric($price) || (float) $price <= 0
        || $WOOCS->current_currency === $WOOCS->default_currency) {
        return $price;
    }
    $tax = 1.20; // your tax rate
    return (ceil((float) $price * $tax) - 0.01) / $tax; // net price, shown with tax as .99
});

add_filter('woocs_woocommerce_variation_prices', function ($price) {
    global $WOOCS;
    if (!isset($WOOCS) || !is_numeric($price) || (float) $price <= 0
        || $WOOCS->current_currency === $WOOCS->default_currency) {
        return $price;
    }
    return ceil((float) $price) - 0.01; // the range already includes tax
});

Precios introducidos con impuestos

En ese caso, ambos filtros reciben precios brutos, y los clientes con la tasa de tu país base ven el precio redondeado sin cambios. Para un cliente de un país con otra tasa, WooCommerce quita tu impuesto base y añade el suyo, de modo que 45,99 con un 20 % en tu país pasa a 44,84 con un 17 %. Si quieres un único precio bruto en todas partes, WooCommerce tiene un filtro justo para eso; entonces es el margen el que varía según el país:

add_filter('woocommerce_adjust_non_base_location_prices', '__return_false');

Cuando la moneda en sí no cambia, este es el comportamiento estándar de los impuestos de WooCommerce y ocurre igual con FOX desactivado.

Totales del carrito, envío y precios aproximados

Con Es múltiple permitido (Is multiple allowed) = Sí (Yes), el carrito se calcula a partir de los precios de producto redondeados, de modo que los totales de línea y el total del carrito coinciden con lo que vio el cliente. Con No, los importes convertidos existen solo en pantalla: woocs_raw_woocommerce_price recibe cada importe mostrado, incluidos los subtotales del carrito, el envío y las líneas de impuestos, y redondea cada uno por separado, por lo que las filas pueden dejar de sumar el total. Entonces el pago muestra la moneda base, que la función anterior no toca. Si te molestan las filas redondeadas del carrito, devuelve $price sin cambios cuando is_cart() sea verdadero.

Los gastos de envío no son precios de producto y necesitan su propio filtro. Con Es múltiple permitido (Is multiple allowed) = Sí (Yes), FOX convierte las tarifas de envío en woocommerce_package_rates con prioridad 9999, así que el redondeo debe ejecutarse más tarde:

/*
 * Round converted shipping costs up to whole units, after FOX has converted them.
 * Child theme functions.php.
 */
add_filter('woocommerce_package_rates', function ($rates, $package) {
    global $WOOCS;
    if (!isset($WOOCS) || $WOOCS->current_currency === $WOOCS->default_currency) {
        return $rates;
    }
    foreach ($rates as $rate) {
        $cost = (float) $rate->get_cost();
        if ($cost <= 0) {
            continue; // free shipping stays free
        }
        $cost = ceil($cost);
        $rate->set_cost($cost);
        if (wc_tax_enabled() && !empty($rate->get_taxes()) && !WC()->customer->is_vat_exempt()) {
            $rate->set_taxes(WC_Tax::calc_shipping_tax($cost, WC_Tax::get_shipping_tax_rates()));
        }
    }
    return $rates;
}, 10000, 2);

WooCommerce guarda las tarifas calculadas en la sesión del cliente, así que durante las pruebas cambia el contenido del carrito o la dirección para ver los nuevos costes.

Los importes aproximados de Mostrar aprox. precio (Show approx. price) y Mostrar monto aproximado (Show approximate amount) se calculan directamente a partir de los tipos de cambio y no pasan por estos filtros, de modo que quedan sin redondear.

Cuando el redondeo no se ve

  • Precios antiguos en productos variables. WooCommerce guarda en caché los rangos de precios de los productos variables. Tras añadir o cambiar el código, límpialos en WooCommerce → Estado → Herramientas → Transitorios de WooCommerce (WooCommerce transients), o guarda el producto, y vacía la caché de página.
  • Redondeado en la primera carga, sin redondear un momento después. Con Estoy usando un complemento de caché en mi sitio (I am using cache plugin on my site) activada, FOX vuelve a dibujar los precios a través de admin-ajax.php. Un plugin de fragmentos de código configurado para ejecutarse solo en el front end no se ejecuta en esa petición. Configura el fragmento para que se ejecute en todas partes, o pon el código en el functions.php del tema hijo.
  • No cambia nada en absoluto. Haz que la función devuelva una constante como 777 por un momento. Si los precios no muestran 777, el código no está cargado o algo más fija los precios: una plantilla de precios de terceros, un plugin de precios dinámicos o los precios fijos.
  • Error fatal tras pegar el código. El código copiado de páginas web suele traer un guion tipográfico en lugar de un signo menos, comillas curvas, o &gt; en lugar de >. Vuelve a escribir esos caracteres a mano. Cannot redeclare significa que el mismo nombre de función se pegó dos veces; deja una sola copia.
  • Precios redondeados que tú no redondeaste. Otro fragmento o plugin usa el mismo filtro. Busca woocs_raw_woocommerce_price en el tema y en los fragmentos.

Lista de verificación

  • Una sola función asociada a woocs_raw_woocommerce_price y a woocs_woocommerce_variation_prices.
  • La moneda base, y cualquier moneda con precios fijos que deban quedar exactos, excluidas.
  • Configuración de impuestos revisada: redondeo del importe bruto o woocommerce_adjust_non_base_location_prices donde haga falta.
  • Envío redondeado por separado con una prioridad superior a 9999.
  • Transitorios de WooCommerce y cachés de página vaciados, con el código ejecutándose en las peticiones AJAX.

Si los precios siguen sin redondearse con un tema por defecto y solo WooCommerce y FOX activos, publica tu fragmento y el enlace a un producto en el foro de soporte de FOX, y lo revisaremos.