🎫 Reglas de carrito programaticas — cupones y descuentos

Actualizado: 2025-01-15
💡
Uso real

Este patron se usa en modulos de fidelizacion, campanas de email marketing, welcome coupons para nuevos registros, y programas de afiliados.

#CartRule: la clase clave

En PrestaShop, los cupones y reglas de carrito son instancias de CartRule. Esta clase extiende ObjectModel y controla: tipo de descuento, condiciones de activacion, restricciones por grupo/producto/categoria, fechas de validez, y usos maximos.

#Crear cupon de porcentaje

Cupon 15% para un cliente especifico
php
<?php
/**
 * Crear cupon de 15% de descuento
 * Ejemplo: regalo de bienvenida tras registro
 */
function createWelcomeCoupon(int $idCustomer): ?CartRule
{
    $customer = new Customer($idCustomer);
    if (!Validate::isLoadedObject($customer)) {
        return null;
    }

    $cartRule = new CartRule();

    // Codigo unico del cupon (lo que el cliente introduce en checkout)
    $cartRule->code = 'WELCOME-' . strtoupper(Tools::passwdGen(8));

    // Nombre del cupon (multilang, visible en BO y emails)
    foreach (Language::getLanguages(true) as $lang) {
        $cartRule->name[$lang['id_lang']] = 'Bienvenida -15% para ' . $customer->firstname;
    }

    // Tipo de descuento: porcentaje
    $cartRule->reduction_percent = 15.00;
    $cartRule->reduction_tax     = true;  // Descuento sobre precio con IVA

    // Validez: 30 dias desde hoy
    $cartRule->date_from = date('Y-m-d H:i:s');
    $cartRule->date_to   = date('Y-m-d H:i:s', strtotime('+30 days'));

    // Restriccion: solo para este cliente
    $cartRule->id_customer = $idCustomer;

    // Uso: 1 vez por cliente, 1 uso total
    $cartRule->quantity            = 1;   // Usos totales
    $cartRule->quantity_per_user   = 1;   // Usos por cliente

    // Minimo de compra: 30 EUR
    $cartRule->minimum_amount          = 30.00;
    $cartRule->minimum_amount_tax      = true;
    $cartRule->minimum_amount_currency  = (int) Configuration::get('PS_CURRENCY_DEFAULT');
    $cartRule->minimum_amount_shipping  = false;  // No incluir gastos de envio

    // Opciones adicionales
    $cartRule->active         = 1;
    $cartRule->highlight      = 1;  // Mostrar en checkout aunque no se aplique
    $cartRule->partial_use    = 0;  // No generar vale con el resto

    if ($cartRule->add()) {
        return $cartRule;
    }

    return null;
}

// Uso en hookActionCustomerAccountAdd (registro de cliente)
public function hookActionCustomerAccountAdd($params)
{
    $customer = $params['newCustomer'];
    $coupon = createWelcomeCoupon((int) $customer->id);

    if ($coupon) {
        // Enviar email con el cupon
        Mail::Send(
            (int) $customer->id_lang,
            'welcome_coupon',    // Template en mails/
            'Tu cupon de bienvenida',
            [
                '{coupon_code}'    => $coupon->code,
                '{coupon_value}'   => '15%',
                '{coupon_expires}' => $coupon->date_to,
                '{firstname}'      => $customer->firstname,
            ],
            $customer->email,
            $customer->firstname . ' ' . $customer->lastname,
            null, null, null, null, _PS_MODULE_DIR_ . $this->name . '/mails/'
        );
    }
}

#Cupon de importe fijo

Cupon de 10 EUR de descuento
php
<?php
$cartRule = new CartRule();
$cartRule->code = 'DESCUENTO10-' . Tools::passwdGen(6);

foreach (Language::getLanguages(true) as $lang) {
    $cartRule->name[$lang['id_lang']] = 'Descuento 10 EUR';
}

// Tipo: importe fijo
$cartRule->reduction_amount   = 10.00;
$cartRule->reduction_tax      = true;         // Descuento sobre precio con impuestos
$cartRule->reduction_currency  = (int) Configuration::get('PS_CURRENCY_DEFAULT');

// Sin producto especifico (aplica a todo el carrito)
$cartRule->reduction_product  = 0;

$cartRule->date_from = date('Y-m-d H:i:s');
$cartRule->date_to   = date('Y-m-d H:i:s', strtotime('+7 days'));
$cartRule->quantity          = 100;  // 100 usos totales
$cartRule->quantity_per_user = 1;    // 1 uso por persona
$cartRule->active = 1;
$cartRule->add();

#Envio gratis automatico

Envio gratis sin codigo (automatico)
php
<?php
/**
 * Regla de carrito sin codigo = se aplica automaticamente
 * Perfecto para: envio gratis a partir de X EUR, promociones temporales
 */
$cartRule = new CartRule();

// SIN CODIGO → se aplica automaticamente cuando se cumplen las condiciones
$cartRule->code = '';

foreach (Language::getLanguages(true) as $lang) {
    $cartRule->name[$lang['id_lang']] = 'Envio gratis pedidos +50 EUR';
}

// Tipo: envio gratis
$cartRule->free_shipping = true;

// Condicion: minimo 50 EUR en el carrito
$cartRule->minimum_amount          = 50.00;
$cartRule->minimum_amount_tax      = true;
$cartRule->minimum_amount_currency  = (int) Configuration::get('PS_CURRENCY_DEFAULT');

// Valido indefinidamente
$cartRule->date_from = '2024-01-01 00:00:00';
$cartRule->date_to   = '2030-12-31 23:59:59';

$cartRule->quantity          = 0;  // 0 = ilimitado
$cartRule->quantity_per_user = 0;  // 0 = ilimitado
$cartRule->active = 1;
$cartRule->priority = 1;  // Prioridad alta

$cartRule->add();

#Restricciones avanzadas

Restricciones por grupo, producto y categoria
php
<?php
// Despues de $cartRule->add(), puedes anadir restricciones:

// === Restringir a grupos de clientes ===
// Solo para grupo "VIP" (id_group = 4)
$cartRule->addRestriction(
    CartRule::FILTER_GROUP,
    [4]  // IDs de grupos permitidos
);
// O manualmente:
Db::getInstance()->insert('cart_rule_group', [
    'id_cart_rule' => (int) $cartRule->id,
    'id_group'     => 4,
]);

// === Restringir a productos especificos ===
// El cupon solo aplica si el carrito contiene estos productos
Db::getInstance()->insert('cart_rule_product_rule_group', [
    'id_cart_rule' => (int) $cartRule->id,
    'quantity'     => 1,  // Minimo 1 unidad del producto
]);
$idGroup = Db::getInstance()->Insert_ID();

Db::getInstance()->insert('cart_rule_product_rule', [
    'id_product_rule_group' => $idGroup,
    'type'                  => 'products',  // 'products', 'categories', 'attributes', 'manufacturers'
]);
$idRule = Db::getInstance()->Insert_ID();

// Productos especificos
$productIds = [42, 55, 78];
foreach ($productIds as $idProduct) {
    Db::getInstance()->insert('cart_rule_product_rule_value', [
        'id_product_rule' => $idRule,
        'id_item'         => (int) $idProduct,
    ]);
}

// === Restringir a categorias ===
// Misma estructura pero con type = 'categories'
Db::getInstance()->insert('cart_rule_product_rule', [
    'id_product_rule_group' => $idGroup,
    'type'                  => 'categories',
]);
$idRule2 = Db::getInstance()->Insert_ID();
Db::getInstance()->insert('cart_rule_product_rule_value', [
    'id_product_rule' => $idRule2,
    'id_item'         => 12,  // id_category
]);

#Generar cupones masivamente

Generar 500 cupones unicos para campana de marketing
php
<?php
function generateCampaignCoupons(
    string $prefix,
    float  $discount,
    int    $count,
    string $expiresAt
): array {
    $codes = [];

    for ($i = 0; $i < $count; $i++) {
        $cartRule = new CartRule();
        $code = $prefix . '-' . strtoupper(Tools::passwdGen(8));
        $cartRule->code = $code;

        foreach (Language::getLanguages(true) as $lang) {
            $cartRule->name[$lang['id_lang']] = "Campana {$prefix} #{$i}";
        }

        $cartRule->reduction_percent   = $discount;
        $cartRule->reduction_tax       = true;
        $cartRule->date_from           = date('Y-m-d H:i:s');
        $cartRule->date_to             = $expiresAt;
        $cartRule->quantity            = 1;
        $cartRule->quantity_per_user   = 1;
        $cartRule->active              = 1;

        if ($cartRule->add()) {
            $codes[] = $code;
        }
    }

    return $codes;
}

// Generar 500 cupones de 20% para Black Friday
$coupons = generateCampaignCoupons(
    'BF2025',
    20.0,
    500,
    '2025-12-01 23:59:59'
);
// $coupons = ['BF2025-A8K2M9X1', 'BF2025-P3Q7R4T6', ...]

#Aplicar cupon al carrito via codigo

Aplicar un cupon al carrito actual
php
<?php
/**
 * Aplicar cupon programaticamente (por ejemplo, desde un hook)
 */
function applyCouponToCart(string $code, Context $context): array
{
    $idCartRule = CartRule::getIdByCode($code);

    if (!$idCartRule) {
        return ['success' => false, 'error' => 'Codigo no valido'];
    }

    $cartRule = new CartRule($idCartRule);

    // Verificar que se puede usar
    $error = $cartRule->checkValidity(
        $context,
        false,                             // in_order_process
        true,                              // display_error (devuelve mensaje)
        true                               // already_in_cart check
    );

    if (is_string($error) && !empty($error)) {
        return ['success' => false, 'error' => $error];
    }

    // Anadir al carrito
    $context->cart->addCartRule((int) $cartRule->id);

    return [
        'success'  => true,
        'code'     => $code,
        'discount' => $cartRule->reduction_percent > 0
            ? $cartRule->reduction_percent . '%'
            : Tools::displayPrice($cartRule->reduction_amount),
    ];
}
Descargar en Markdown Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.