---
title: Grid System — columnas y acciones custom en listados BO
section: examples
slug: grid-column-module
description: "Modificar listados del Back Office con el Grid System de PrestaShop 1.7+: anadir columnas, filtros, acciones bulk y row actions a grids existentes."
keywords: prestashop grid system columna accion filtro listado back office modulo hook
last_updated: 2025-01-15
source_url: "https://ayudaprestashop.es/examples/grid-column-module"
---

# Grid System — columnas y acciones custom en listados BO

> Modificar listados del Back Office con el Grid System de PrestaShop 1.7+: anadir columnas, filtros, acciones bulk y row actions a grids existentes.

## El Grid System de PS 1.7+

El Grid System moderno de PS usa hooks `action{GridId}GridDefinitionModifier` para que los modulos puedan anadir columnas, filtros y acciones a cualquier listado del BO sin overrides. Cada grid tiene un ID unico (ej: `order`, `product`, `customer`).

## Anadir columna a grid existente

*Anadir columna 'Margen' al listado de pedidos*

```php
<?php
use PrestaShop\PrestaShop\Core\Grid\Column\Type\DataColumn;
use PrestaShop\PrestaShop\Core\Grid\Filter\Filter;
use Symfony\Component\Form\Extension\Core\Type\TextType;

public function install()
{
    return parent::install()
        && $this->registerHook('actionOrderGridDefinitionModifier')
        && $this->registerHook('actionOrderGridQueryBuilderModifier');
}

/**
 * Modificar la DEFINICION del grid (columnas, filtros, acciones)
 */
public function hookActionOrderGridDefinitionModifier(array $params): void
{
    /** @var \PrestaShop\PrestaShop\Core\Grid\Definition\GridDefinitionInterface $definition */
    $definition = $params['definition'];

    // Anadir columna de margen de beneficio
    $definition
        ->getColumns()
        ->addAfter(
            'total_paid_tax_incl',  // Despues de esta columna existente
            (new DataColumn('profit_margin'))
                ->setName('Margen')
                ->setOptions([
                    'field' => 'profit_margin',
                ])
        );

    // Anadir filtro para la nueva columna
    $definition
        ->getFilters()
        ->add(
            (new Filter('profit_margin', TextType::class))
                ->setTypeOptions([
                    'required' => false,
                    'attr'     => ['placeholder' => 'Margen...'],
                ])
                ->setAssociatedColumn('profit_margin')
        );
}

/**
 * Modificar el QUERY del grid (JOINs, campos)
 */
public function hookActionOrderGridQueryBuilderModifier(array $params): void
{
    /** @var \Doctrine\DBAL\Query\QueryBuilder $searchQueryBuilder */
    $searchQueryBuilder = $params['search_query_builder'];

    /** @var \PrestaShop\PrestaShop\Core\Search\Filters\OrderFilters $searchCriteria */
    $searchCriteria = $params['search_criteria'];

    // Anadir subquery para calcular margen
    $searchQueryBuilder->addSelect(
        'ROUND(o.total_paid_tax_excl - IFNULL(margin.cost, 0), 2) AS profit_margin'
    );

    // JOIN con subconsulta de coste
    $searchQueryBuilder->leftJoin(
        'o',
        '(' .
        'SELECT od.id_order, SUM(od.product_quantity * od.original_wholesale_price) as cost ' .
        'FROM `' . _DB_PREFIX_ . 'order_detail` od ' .
        'GROUP BY od.id_order' .
        ')',
        'margin',
        'o.id_order = margin.id_order'
    );

    // Aplicar filtro si el usuario lo uso
    foreach ($searchCriteria->getFilters() as $filterName => $filterValue) {
        if ($filterName === 'profit_margin' && !empty($filterValue)) {
            $searchQueryBuilder->andHaving(
                'profit_margin >= :margin_filter'
            );
            $searchQueryBuilder->setParameter('margin_filter', (float) $filterValue);
        }
    }
}
```

## Anadir accion por fila

*Boton 'Imprimir etiqueta' en cada fila del listado de pedidos*

```php
<?php
use PrestaShop\PrestaShop\Core\Grid\Action\Row\Type\LinkRowAction;

public function hookActionOrderGridDefinitionModifier(array $params): void
{
    $definition = $params['definition'];

    // Anadir accion de fila
    $definition
        ->getGridActions()  // Acciones globales (arriba del grid)
        // O para acciones por fila:
    ;

    $definition
        ->getColumns()
        ->addAfter('actions',  // El grupo de acciones existente
            // ... o modificar el actions column existente
        );

    // Anadir boton en la columna de acciones
    $definition->getRowActions()->add(
        (new LinkRowAction('print_label'))
            ->setName('Imprimir etiqueta')
            ->setIcon('print')
            ->setOptions([
                'route'           => 'ecom_shipping_print_label',
                'route_param_name'=> 'orderId',
                'route_param_field' => 'id_order',
            ])
    );
}
```

## Anadir bulk action

*Accion masiva: 'Exportar seleccionados a CSV'*

```php
<?php
use PrestaShop\PrestaShop\Core\Grid\Action\Bulk\Type\SubmitBulkAction;

public function hookActionOrderGridDefinitionModifier(array $params): void
{
    $definition = $params['definition'];

    $definition->getBulkActions()->add(
        (new SubmitBulkAction('export_csv'))
            ->setName('Exportar a CSV')
            ->setOptions([
                'submit_route' => 'ecom_export_orders_csv',
            ])
    );
}
```

## Modificar filtros

*Anadir filtro dropdown al listado de productos*

```php
<?php
use PrestaShop\PrestaShop\Core\Grid\Filter\Filter;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

public function hookActionProductGridDefinitionModifier(array $params): void
{
    $definition = $params['definition'];

    // Filtro: margen de beneficio (rangos)
    $definition->getFilters()->add(
        (new Filter('margin_range', ChoiceType::class))
            ->setTypeOptions([
                'choices' => [
                    'Todos'      => '',
                    'Negativo'   => 'negative',
                    '0-10%'      => 'low',
                    '10-30%'     => 'medium',
                    '>30%'       => 'high',
                ],
                'required' => false,
                'placeholder' => 'Margen...',
            ])
            ->setAssociatedColumn('profit_margin')
    );
}

public function hookActionProductGridQueryBuilderModifier(array $params): void
{
    $searchQueryBuilder = $params['search_query_builder'];
    $searchCriteria = $params['search_criteria'];

    foreach ($searchCriteria->getFilters() as $name => $value) {
        if ($name === 'margin_range' && !empty($value)) {
            switch ($value) {
                case 'negative':
                    $searchQueryBuilder->andWhere('p.price < p.wholesale_price');
                    break;
                case 'low':
                    $searchQueryBuilder->andWhere(
                        '((p.price - p.wholesale_price) / NULLIF(p.price, 0)) BETWEEN 0 AND 0.10'
                    );
                    break;
                case 'medium':
                    $searchQueryBuilder->andWhere(
                        '((p.price - p.wholesale_price) / NULLIF(p.price, 0)) BETWEEN 0.10 AND 0.30'
                    );
                    break;
                case 'high':
                    $searchQueryBuilder->andWhere(
                        '((p.price - p.wholesale_price) / NULLIF(p.price, 0)) > 0.30'
                    );
                    break;
            }
        }
    }
}
```

## Hooks disponibles para grids

| Grid ID | Hook Definition | Hook Query | Listado |
| --- | --- | --- | --- |
| order | actionOrderGridDefinitionModifier | actionOrderGridQueryBuilderModifier | Pedidos |
| product | actionProductGridDefinitionModifier | actionProductGridQueryBuilderModifier | Productos |
| customer | actionCustomerGridDefinitionModifier | actionCustomerGridQueryBuilderModifier | Clientes |
| cart | actionCartGridDefinitionModifier | actionCartGridQueryBuilderModifier | Carritos |
| invoice | actionInvoiceGridDefinitionModifier | actionInvoiceGridQueryBuilderModifier | Facturas |
| module | actionModuleGridDefinitionModifier | actionModuleGridQueryBuilderModifier | Modulos |
| employee | actionEmployeeGridDefinitionModifier | actionEmployeeGridQueryBuilderModifier | Empleados |

> **[TIP] Descubrir el Grid ID**
>
> Para descubrir el grid ID de un listado, busca en el controller Symfony del BO la linea `$gridFactory->getGrid()` o el servicio que contiene `grid_definition_factory`. El ID es el primer argumento del `GridDefinition`.


---

*Fuente: [https://ayudaprestashop.es/examples/grid-column-module](https://ayudaprestashop.es/examples/grid-column-module). Version Markdown generada automaticamente para consumo por LLMs.*
