📦 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
- Se obtiene la configuración del código postal (
getDeliveryTimeZip). - Se obtienen los productos a partir de los SKUs (
getProducts). - 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:
- Sin OVS
- Se aplica la tarifa paqRate.
- Con OVS y no es zona extendida
- Se aplica la tarifa ovsRate.
- Con OVS y es zona extendida
- Se aplica la tarifa etlRate.
5. Cálculo del costo base
Con el porcentaje obtenido:
-
Se valida y normaliza el porcentaje.
-
Se calcula:
costo = (porcentaje * subtotal) redondeado a centenas - 1Ejemplo: 7% de $2,340 = 163.8 → redondeado = 200 → 200 - 1 = 199
-
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: 89paq_rate: 7%ovs_rate: 7%etl_rate: 7%apply_for_max_shipping_amount: 1 (true)max_shipping_amount: 699apply_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";