💰 Calculo de tarifas de envio — peso, precio y reglas

Actualizado: 2024-12-01

Un modulo de transportista debe implementar getOrderShippingCost() y/o getOrderShippingCostExternal(). PrestaShop llama a estos metodos para calcular el coste de envio durante el checkout.

#getOrderShippingCost() — implementacion

Implementar getOrderShippingCost() en el modulo
php
<?php

class MyCarrierModule extends CarrierModule
{
    /**
     * Calcula el coste de envio usando los rangos definidos en el BO.
     *
     * @param Cart  $params  El carrito actual
     * @param float $shipping_cost Coste calculado por PrestaShop (rangos del BO)
     * @return float|bool  El coste final, o false si no aplica
     */
    public function getOrderShippingCost($params, $shipping_cost)
    {
        // $shipping_cost ya viene calculado por PS segun los rangos del BO
        // Podemos modificarlo o simplemente devolverlo
        if ($shipping_cost === false) {
            return false; // Fuera de rango → no disponible
        }

        // Ejemplo: aplicar un recargo por entrega en domicilio
        $extraCharge = (float) Configuration::get('MYCARRIER_EXTRA');
        return (float) $shipping_cost + $extraCharge;
    }

    /**
     * Calcula el coste con logica completamente propia (ignora rangos del BO).
     * Se usa cuando la tarifa viene de una API externa.
     *
     * @param Cart  $params  El carrito actual
     * @return float|bool
     */
    public function getOrderShippingCostExternal($params)
    {
        $cart   = $params; // $params es el objeto Cart
        $weight = (float) $cart->getTotalWeight();
        $total  = (float) $cart->getOrderTotal(false, Cart::ONLY_PRODUCTS);

        // Llamada a API externa para obtener la tarifa
        $rate = $this->fetchRateFromApi($weight, $total, $cart->id_address_delivery);

        return $rate !== null ? (float) $rate : false;
    }
}

#Calcular por peso

Logica de calculo por peso sin usar rangos del BO
php
<?php

private function calculateByWeight(Cart $cart): float
{
    $weight = (float) $cart->getTotalWeight(); // en kg por defecto

    // Tarifa escalonada por peso
    $rates = [
        ['max' => 1.0,  'price' => 3.99],
        ['max' => 5.0,  'price' => 5.99],
        ['max' => 10.0, 'price' => 7.99],
        ['max' => 20.0, 'price' => 9.99],
        ['max' => PHP_FLOAT_MAX, 'price' => 14.99], // >20kg
    ];

    foreach ($rates as $rate) {
        if ($weight <= $rate['max']) {
            return (float) $rate['price'];
        }
    }

    return false; // No deberia llegar aqui por el ultimo catch-all
}

// ── Obtener peso del carrito ──
$weight    = $cart->getTotalWeight();     // Total en unidad del sistema
$products  = $cart->getProducts();
foreach ($products as $product) {
    // $product['weight'] = peso unitario
    // $product['cart_quantity'] = cantidad
    $lineWeight = (float) $product['weight'] * (int) $product['cart_quantity'];
}

#Calcular por importe del pedido

Envio gratuito a partir de un importe minimo
php
<?php

private function calculateByOrderTotal(Cart $cart): float
{
    // Importe sin impuestos ni envio
    $total = (float) $cart->getOrderTotal(false, Cart::ONLY_PRODUCTS);

    $freeShippingThreshold = (float) Configuration::get('MYCARRIER_FREE_FROM');
    $baseRate              = (float) Configuration::get('MYCARRIER_BASE_RATE');

    // Envio gratis a partir del umbral
    if ($freeShippingThreshold > 0 && $total >= $freeShippingThreshold) {
        return 0.0;
    }

    // Tarifa porcentual sobre el pedido
    $percentageRate = (float) Configuration::get('MYCARRIER_PERCENT_RATE');
    if ($percentageRate > 0) {
        return round($total * $percentageRate / 100, 2);
    }

    return $baseRate;
}

#Rangos y zonas en el BO

Si usas getOrderShippingCost() (sin External), PrestaShop aplica los rangos configurados en el BO (Transporte > Transportistas > Editar > Rangos de precio/peso) antes de llamar al modulo. El valor $shipping_cost que recibe el metodo ya tiene la tarifa calculada segun esos rangos.

Configurar rangos de peso al instalar el transportista
php
<?php

// En install() del modulo, crear rangos de peso automaticamente:
private function createWeightRanges(int $idCarrier): void
{
    $ranges = [
        ['delimiter1' => 0,  'delimiter2' => 1],
        ['delimiter1' => 1,  'delimiter2' => 5],
        ['delimiter1' => 5,  'delimiter2' => 10],
        ['delimiter1' => 10, 'delimiter2' => 20],
    ];

    foreach ($ranges as $rangeData) {
        $range              = new RangeWeight();
        $range->id_carrier  = $idCarrier;
        $range->delimiter1  = $rangeData['delimiter1'];
        $range->delimiter2  = $rangeData['delimiter2'];
        $range->add();
    }
}

#Tarifas dinamicas via API externa

Obtener tarifa de API externa con cache
php
<?php

private function fetchRateFromApi(float $weight, float $total, int $idAddress): ?float
{
    // Cache por session para no llamar a la API en cada renderizado
    $cacheKey = 'mycarrier_rate_' . $idAddress . '_' . round($weight, 2);
    if (Cache::isStored($cacheKey)) {
        return (float) Cache::retrieve($cacheKey);
    }

    $address = new Address($idAddress);
    $country = new Country($address->id_country);

    try {
        $response = file_get_contents(
            'https://api.mycarrier.com/rate?'
            . http_build_query([
                'weight'     => $weight,
                'country'    => $country->iso_code,
                'postal'     => $address->postcode,
                'api_key'    => Configuration::get('MYCARRIER_API_KEY'),
            ])
        );

        $data = json_decode($response, true);
        $rate = (float) ($data['price'] ?? 0);

        Cache::store($cacheKey, $rate, 300); // Cache 5 minutos
        return $rate;

    } catch (\Exception $e) {
        PrestaShopLogger::addLog(
            'MyCarrier API error: ' . $e->getMessage(),
            3, null, 'MyCarrierModule'
        );
        return null;
    }
}
Descargar en Markdown Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.