Sales Operations Datatable - Datatables for Laravel                  [               Datatables for Laravel  ](/) 

  Toggle main menu         [Docs](https://laravel-datatables.com/docs) [Demos](/demos) [Blog](https://laravel-datatables.com/blog) [Pricing](/pricing)        Search  ⌘ K   [ Log in ](/login) 

  [Docs](https://laravel-datatables.com/docs) [Demos](/demos) [Blog](https://laravel-datatables.com/blog) [Pricing](/pricing) 

 Search docs and demos  

Type at least 2 characters to search.

    Demos menu     ### Use Cases

- [ Simple Datatable ](/demos)
- [ Products Datatable ](/demos/use-case-products)
- [ Sales Operations ](/demos/use-case-sales)

### Features

- [ Accessibility ](/demos/features-accessibility)
- [ Advanced Filtering ](/demos/features-advanced-filtering)
- [ Column Reordering ](/demos/features-column-reordering)
- [ Directionality (LTR / RTL) ](/demos/features-directionality)
- [ Export ](/demos/features-export)
- [ Pagination ](/demos/features-pagination)
- [ Frozen Columns ](/demos/features-frozen-columns)
- [ Frozen Header ](/demos/features-frozen)
- [ Frozen Header &amp; Columns ](/demos/features-frozen-both)
- [ Responsive Mobile ](/demos/features-responsive-mobile)
- [ Row Grouping ](/demos/features-row-grouping)
- [ Aggregate Totals ](/demos/features-aggregate-totals)
- [ Row Selection &amp; Bulk Actions ](/demos/features-row-selection-bulk-actions)
- [ Custom Controls ](/demos/features-custom-controls)

### Cell Renderer

- [ Action Buttons ](/demos/cell-renderer-action-buttons)
- [ Avatar ](/demos/cell-renderer-avatar)
- [ Checkmark ](/demos/cell-renderer-checkmark)
- [ Color Sample ](/demos/cell-renderer-color-sample)
- [ Email Link ](/demos/cell-renderer-email)
- [ External Link ](/demos/cell-renderer-external)
- [ Friendly Timestamp ](/demos/cell-renderer-friendly-timestamp)
- [ Image ](/demos/cell-renderer-image)
- [ Long Text ](/demos/cell-renderer-long-text)
- [ Phone Link ](/demos/cell-renderer-phone-link)
- [ Progress Bar ](/demos/cell-renderer-progress-bar)
- [ Status Badge ](/demos/cell-renderer-status-badge)
- [ Url Link ](/demos/cell-renderer-url)
- [ Wire Link ](/demos/cell-renderer-wire)

### Value Formatter

- [ Compact Number ](/demos/value-formatter-compact-number)
- [ Currency ](/demos/value-formatter-currency)
- [ Duration ](/demos/value-formatter-duration)
- [ Friendly Filesize ](/demos/value-formatter-friendly-filesize)
- [ Friendly Timestamp ](/demos/value-formatter-friendly-timestamp)
- [ Locale Timestamp ](/demos/value-formatter-locale-timestamp)
- [ Number ](/demos/value-formatter-number)
- [ Percent ](/demos/value-formatter-percent)

 Sales Operations Datatable
==========================

The Sales operations datatable is a more advanced use case showcasing various features like exporting, filtering, row selection and multi-row actions as well as a few more.

As this is just a datatable with demo data the multi-row delete action is simulated here by tracking deleted row IDs in the `$hiddenRows` variable of the datatable. This allows us also to see a dataSource() method that adds additional query constraints to the Eloquent query such as adding a new data element 'units\_sold' on the fly and hiding any "deleted" rows.

The columns() method has also increased in complexity showcasing a custom formatter callback using an anonymous function to format the Status column values and rendering it using a [ StatusBadge ](https://laravel-datatables.com/docs/renderer-status-badge).

This use case also gives a simple example of how to implement custom bulk actions by defining the bulk actions as an array called `$bulkActions` and executing the selected bulk action in the onBulkAction() method, like exporting the selected data.

Note that although row actions have been enabled for view, edit and delete, their respective row action handling has not been implemented for this simple demo. If you want to learn more about custom (dynamic) action buttons and row action handling, check out the [ Actions and Forms ](https://laravel-datatables.com/docs/actions-and-forms) and [ Cell Renderer: Action Buttons ](https://laravel-datatables.com/docs/renderer-action-buttons) documentation.

 Related documentation

 [ Creating a Table ](https://laravel-datatables.com/docs/creating-a-table) [ Displaying Columns ](https://laravel-datatables.com/docs/displaying-columns) [ Exporting ](https://laravel-datatables.com/docs/feature-exporting) [ Custom Filters ](https://laravel-datatables.com/docs/feature-custom-filters) [ Row Selection ](https://laravel-datatables.com/docs/feature-row-selection) [ Actions and Forms ](https://laravel-datatables.com/docs/actions-and-forms) [ Multi-Row Actions ](https://laravel-datatables.com/docs/customization-multi-row-actions) 

 Related demos

 [ Simple Datatable ](https://laravel-datatables.com/demos/use-case-simple) [ Products Datatable ](https://laravel-datatables.com/demos/use-case-products) [ Cell Renderer: Action Buttons ](https://laravel-datatables.com/demos/cell-renderer-action-buttons) [ Cell Renderer: Status Badge ](https://laravel-datatables.com/demos/cell-renderer-status-badge) 

          Items per page        Filters      Export        Columns   

    10  Current    25    50    100    

     CSV   XLSX   PDF   

 ​### Filter options

   Product Name      

  Category    All  Accessories Hardware Services Software Subscriptions     

 Clear all Done 

 ​###  Manage Columns 

     ID      Product Name      Category      Status      Units Sold      Unit Price     

  Apply   Cancel  

       Loading... 

              ID                Product Name                Category                Status                Units Sold                Unit Price               Actions                        1       Culpa itaque ut       Hardware       Active       114.0       €453.80       View Edit Delete

                  2       Ullam non magni       Software       Active       139.0       €423.23       View Edit Delete

                  3       Voluptatibus eos esse et       Hardware       Active       131.0       €779.98       View Edit Delete

                  4       Eum rerum harum       Subscriptions       Inactive       81.0       €674.33       View Edit Delete

                  5       Corporis velit sit       Software       Active       76.0       €1,137.90       View Edit Delete

                  6       Ex illo sed quia       Hardware       Inactive       179.0       €343.38       View Edit Delete

                  7       Molestias ea repellendus       Accessories       Active       166.0       €314.46       View Edit Delete

                  8       Qui veniam qui       Hardware       Active       140.0       €1,138.13       View Edit Delete

                  9       Deleniti aliquid dolorem       Services       Active       92.0       €11.81       View Edit Delete

                  10       Est debitis blanditiis iure       Accessories       Inactive       174.0       €819.16       View Edit Delete

        « Previous       Next »    

 1 - 10 of 320

                    1       2       3       4       5       6       7       8       9       10        ...          31       32                 

     `namespace App\Livewire\Datatables;use App\Models\Product;use MorrowIt\LaravelDatatable\Http\Livewire\Datatable;use MorrowIt\LaravelDatatable\Contracts\DataSourceInterface;use MorrowIt\LaravelDatatable\DataSources\EloquentDataSource;use MorrowIt\LaravelDatatable\Config\Column;use MorrowIt\LaravelDatatable\Renderers\StatusBadge;class UseCaseSalesOperationsDatatable extends Datatable{    public string $tableId = 'use-case-sales-operations-table';    public bool $persistTableState = true;    public bool $enableColumnReordering = true;    public bool $freezeHeader = true;    public bool $enableRowSelection = true;    public bool $enableExports = true;    public bool $persistSelectedRows = false;    public int $freezeColumns = 1;    public bool $enableAdvancedFilters = true;    public string $actionButtonsVariant = 'verbal';    public array $bulkActions = [        'export-selected-rows' => 'Export selected rows',        'change-status' => 'Change status',        'delete-selected-rows' => 'Delete selected rows',    ];    public array $hiddenRows = [];    public array $statusOverrides = [];    public function dataSource(): DataSourceInterface    {        $query = Product::query()->withSum('sales as units_sold', 'units');        if (!empty($this->hiddenRows)) {            $query->whereNotIn('id', $this->hiddenRows);        }        return new EloquentDataSource($query);    }    public function columns(): array    {        $categories = Product::query()            ->select('category')            ->whereNotNull('category')            ->where('category', '!=', '')            ->distinct()            ->orderBy('category')            ->pluck('category', 'category')            ->toArray();        return [            Column::make('id', 'ID')                ->sortable(),            Column::make('name', 'Product Name')                ->searchable()                ->sortable()                ->filterable('text'),            Column::make('category', 'Category')                ->searchable()                ->sortable()                ->filterable('select', options: $categories),            Column::make('active', 'Status')                ->sortable()                ->format(function ($value, $row) {                    $id = (string) data_get($row, 'id');                    if (array_key_exists($id, $this->statusOverrides)) {                        return $this->statusOverrides[$id] ? 'Active' : 'Inactive';                    }                    return $value ? 'Active' : 'Inactive';                })                ->renderer(new StatusBadge()),            Column::make('units_sold', 'Units Sold')                ->sortable()                ->headerClasses('text-right')                ->cellClasses('text-right')                ->format(new \MorrowIt\LaravelDatatable\Formatters\CompactNumber()),            Column::make('price', 'Unit Price')                ->sortable()                ->headerClasses('text-right')                ->cellClasses('text-right')                ->format(new \MorrowIt\LaravelDatatable\Formatters\Currency(currencyCol: 'currency')),            Column::make('actions', 'Actions')                ->renderer(new ActionButtons(variant: $this->actionButtonsVariant))                ->toggleable(false),        ];    }    protected function onBulkAction(string $action, array $selectedRows): void    {        $ids = array_values(array_map('intval', $selectedRows));        switch ($action) {            case 'export-selected-rows':                $this->export('csv');                break;            case 'change-status':                foreach ($ids as $id) {                    $key = (string) $id;                    $current = $this->statusOverrides[$key] ?? (bool) Product::query()->whereKey($id)->value('active');                    $this->statusOverrides[$key] = !$current;                }                break;            case 'delete-selected-rows':                $this->hiddenRows = array_values(array_unique(array_merge($this->hiddenRows, $ids)));                break;        }        $this->selectedRows = [];        $this->selectCurrentPage = false;    }}` 

 ```
namespace App\Livewire\Datatables;

use App\Models\Product;
use MorrowIt\LaravelDatatable\Http\Livewire\Datatable;
use MorrowIt\LaravelDatatable\Contracts\DataSourceInterface;
use MorrowIt\LaravelDatatable\DataSources\EloquentDataSource;
use MorrowIt\LaravelDatatable\Config\Column;
use MorrowIt\LaravelDatatable\Renderers\StatusBadge;

class UseCaseSalesOperationsDatatable extends Datatable
{
    public string $tableId = 'use-case-sales-operations-table';
    public bool $persistTableState = true;
    public bool $enableColumnReordering = true;
    public bool $freezeHeader = true;
    public bool $enableRowSelection = true;
    public bool $enableExports = true;
    public bool $persistSelectedRows = false;
    public int $freezeColumns = 1;
    public bool $enableAdvancedFilters = true;
    public string $actionButtonsVariant = 'verbal';

    public array $bulkActions = [
        'export-selected-rows' => 'Export selected rows',
        'change-status' => 'Change status',
        'delete-selected-rows' => 'Delete selected rows',
    ];

    public array $hiddenRows = [];
    public array $statusOverrides = [];

    public function dataSource(): DataSourceInterface
    {
        $query = Product::query()->withSum('sales as units_sold', 'units');
        if (!empty($this->hiddenRows)) {
            $query->whereNotIn('id', $this->hiddenRows);
        }

        return new EloquentDataSource($query);
    }

    public function columns(): array
    {
        $categories = Product::query()
            ->select('category')
            ->whereNotNull('category')
            ->where('category', '!=', '')
            ->distinct()
            ->orderBy('category')
            ->pluck('category', 'category')
            ->toArray();

        return [
            Column::make('id', 'ID')
                ->sortable(),
            Column::make('name', 'Product Name')
                ->searchable()
                ->sortable()
                ->filterable('text'),
            Column::make('category', 'Category')
                ->searchable()
                ->sortable()
                ->filterable('select', options: $categories),
            Column::make('active', 'Status')
                ->sortable()
                ->format(function ($value, $row) {
                    $id = (string) data_get($row, 'id');

                    if (array_key_exists($id, $this->statusOverrides)) {
                        return $this->statusOverrides[$id] ? 'Active' : 'Inactive';
                    }

                    return $value ? 'Active' : 'Inactive';
                })
                ->renderer(new StatusBadge()),
            Column::make('units_sold', 'Units Sold')
                ->sortable()
                ->headerClasses('text-right')
                ->cellClasses('text-right')
                ->format(new \MorrowIt\LaravelDatatable\Formatters\CompactNumber()),
            Column::make('price', 'Unit Price')
                ->sortable()
                ->headerClasses('text-right')
                ->cellClasses('text-right')
                ->format(new \MorrowIt\LaravelDatatable\Formatters\Currency(currencyCol: 'currency')),
            Column::make('actions', 'Actions')
                ->renderer(new ActionButtons(variant: $this->actionButtonsVariant))
                ->toggleable(false),
        ];
    }

    protected function onBulkAction(string $action, array $selectedRows): void
    {
        $ids = array_values(array_map('intval', $selectedRows));

        switch ($action) {
            case 'export-selected-rows':
                $this->export('csv');
                break;

            case 'change-status':
                foreach ($ids as $id) {
                    $key = (string) $id;
                    $current = $this->statusOverrides[$key] ?? (bool) Product::query()->whereKey($id)->value('active');
                    $this->statusOverrides[$key] = !$current;
                }
                break;

            case 'delete-selected-rows':
                $this->hiddenRows = array_values(array_unique(array_merge($this->hiddenRows, $ids)));
                break;
        }

        $this->selectedRows = [];
        $this->selectCurrentPage = false;
    }
}
```

   © 2026 Copyright (C)  [ Morrow IT Consulting &amp; Services ](https://morrow-it.services)  

 [Terms of Service](https://laravel-datatables.com/legal/terms) [Privacy](https://laravel-datatables.com/legal/privacy) [Legal Disclosure](https://laravel-datatables.com/legal/disclosure)
