Saltar al contenido principal

📦 Lógica para Calcular el Costo de Envío

GABO se encarga de calcular el costo de envio para esto Magento y POS hacen la consulta como se describe en Consultar costo de envio.

1. Validaciones iniciales

Antes de calcular, se valida que existan los datos mínimos necesarios:

  • Productos
    • Listado de SKUs
  • Subtotal
    • Monto por la suma de los productos
  • Código postal
    • Numero de código postal (solo méxico)

Si falta alguno, se lanza una excepción.


2. Obtención de datos

  1. Se obtiene la configuración del código postal (getDeliveryTimeZip).
  2. Se obtienen los productos a partir de los SKUs (getProducts).
  3. Se calculan condiciones:
    • hasOVS: si hay productos con servicio OVS.
    • isExtendedZone: si el código postal pertenece a una zona extendida.
    • hasFreeShipping: si la zona aplica a envío gratis.
    • minPurchaseForFreeShipping: monto mínimo de compra para aplicar el envío gratis.

3. Reglas de envío gratis

  • Si la zona aplica a envío gratis y el subtotal de la compra es mayor o igual al mínimo requerido:
    • El costo de envío = 0.

4. Determinación de la tarifa base

Si no aplica el envío gratis, se determina el porcentaje de tarifa de envío según las condiciones:

  1. Sin OVS
    • Se aplica la tarifa paqRate.
  2. Con OVS y no es zona extendida
    • Se aplica la tarifa ovsRate.
  3. Con OVS y es zona extendida
    • Se aplica la tarifa etlRate.

5. Cálculo del costo base

Con el porcentaje obtenido:

  1. Se valida y normaliza el porcentaje.

  2. Se calcula:

    costo = (porcentaje * subtotal) redondeado a centenas - 1

    Ejemplo: 7% de $2,340 = 163.8 → redondeado = 200 → 200 - 1 = 199

  3. Si el resultado es ≤ 0, se usa el defaultShippingPrice definido en la zona.


6. Reajuste por límite máximo

Si existe un monto máximo de envío (maxShippingAmount) y está habilitado:

  • Si el costo calculado supera ese límite → se aplica el monto máximo.
  • Si no lo supera → se mantiene el costo calculado.

7. Resultado final

  • El método devuelve el costo de envío final, que puede ser:
    • 0 si aplica envío gratis.
    • Monto calculado según porcentaje y reglas.
    • Monto máximo si el costo calculado supera el límite.

🗄️ Ejemplo de configuración en gaia_catalog_delivery_time_zip

  • default_shipping_price: 89
  • paq_rate: 7%
  • ovs_rate: 7%
  • etl_rate: 7%
  • apply_for_max_shipping_amount: 1 (true)
  • max_shipping_amount: 699
  • apply_for_free_shipping: 0 (false)
  • min_purchase_for_free_shipping: NULL (sin mínimo definido)

🎯 Código

<?php

const PRODUCTS = [
"11_1" => [
"name" => "silla eames blanca",
"extra_days_code" => "PAQ",
],
"21_3" => [
"name" => "mesa eames gris",
"extra_days_code" => "OVS",
],
];
const CP_CONFIGURATIONS = [
"52000" => [
"IS_EXTEND_ZONE" => true,
"PAQ_RATE" => 0.15,
"OVS_RATE" => 0.25,
"ETL_RATE" => 0.10,
"DEFAULT_SHIPPING_PRICE" => 89,
"APPLY_MAX_SHIPPING" => true,
"MAX_SHIPPING_AMOUNT" => 699,
"APPLY_FREE_SHIPPING" => true,
"MIN_PURCHASE_FREE_SHIPPING" => 1500,
],
"99000" => [
"IS_EXTEND_ZONE" => false,
"PAQ_RATE" => 0.15,
"OVS_RATE" => 0.25,
"ETL_RATE" => 0.10,
"DEFAULT_SHIPPING_PRICE" => 89,
"APPLY_MAX_SHIPPING" => true,
"MAX_SHIPPING_AMOUNT" => 699,
"APPLY_FREE_SHIPPING" => false,
"MIN_PURCHASE_FREE_SHIPPING" => 0,
]
];

function calcularEnvio(array $skus, string $zipCode, float $subtotal): float
{
if (empty($skus) || empty($zipCode) || $subtotal <= 0) {
throw new Exception("Faltan datos requeridos");
}

$cpConfig = CP_CONFIGURATIONS[$zipCode];
$productsInfo = array_filter(PRODUCTS, fn($key) => in_array($key, $skus), ARRAY_FILTER_USE_KEY);
$hasOVS = in_array('OVS', array_column($productsInfo, 'extra_days_code'));
$isExtendedZone = $cpConfig['IS_EXTEND_ZONE'];

// Regla de envío gratis
if ($cpConfig['APPLY_FREE_SHIPPING'] && $subtotal >= $cpConfig['MIN_PURCHASE_FREE_SHIPPING']) {
return 0;
}

// Determinar porcentaje
if (!$hasOVS) {
$porcentaje = $cpConfig['PAQ_RATE'];
} elseif ($hasOVS && !$isExtendedZone) {
$porcentaje = $cpConfig['OVS_RATE'];
} else {
$porcentaje = $cpConfig['ETL_RATE'];
}

// Calcular costo base (redondeado a la centena mas cercana)
$costo = round($porcentaje * $subtotal, -2) - 1;

// Si el costo <= 0 usar default
if ($costo <= 0) {
$costo = $cpConfig['DEFAULT_SHIPPING_PRICE'];
}

// Reajuste por límite máximo
if ($cpConfig['APPLY_MAX_SHIPPING'] && $costo > $cpConfig['MAX_SHIPPING_AMOUNT']) {
$costo = $cpConfig['MAX_SHIPPING_AMOUNT'];
}

return $costo;
}


// Ejemplo de envio gratis
// * El CP permite envio gratis a partir de 1500 y el subtotal enviado es 1600

echo "Costo de envío: " . calcularEnvio(skus: ['11_1', '21_3'], zipCode: '52000', subtotal: 1600) . "\n";


// Ejemplo de costo de envio en $99
// * El CP permite envio gratis pero el subtotal enviado no lo supera y ningun producto es OVS entonces multiplica el porcertaje de PAQ (0.15) por el subtotal (=112.5) y se redondea a centena (100) al final de resta 1 (99)

echo "Costo de envío: " . calcularEnvio(skus: ['11_1'], zipCode: '52000', subtotal: 750) . "\n";


// Ejemplo de costo de envio en $399
// * El CP no permite envio gratis y un producto es OVS entonces multiplica el porcertaje de OVS (0.25) por el subtotal (=375) y se redondea a centena (400) al final de resta 1 (399)

echo "Costo de envío: " . calcularEnvio(skus: ['11_1', '21_3'], zipCode: '99000', subtotal: 1500) . "\n";