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";