| Developer | Project | Company | Report hours | 
|---|---|---|---|
| Tamera Bogan | quibusdam sed sit | 
Apple
 | 92.0 | 
| Tamera Bogan | repudiandae officiis qui | 
Google
 | 67.0 | 
| Tamera Bogan | saepe hic temporibus | 
Microsoft
 | 67.0 | 
| Tamera Bogan | suscipit consectetur beatae | 
Google
 | 73.0 | 
| Tamera Bogan | vitae eum pariatur | 
Google
 | 62.0 | 
| Tami Gibson | animi aperiam tenetur | 
Google
 | 67.0 | 
| Tami Gibson | doloribus hic corporis | 
Microsoft
 | 52.0 | 
| Tami Gibson | eum amet quia | 
Google
 | 88.0 | 
| Tami Gibson | odio velit repudiandae | 
Apple
 | 58.0 | 
| Tami Gibson | perspiciatis totam perferendis | 
Apple
 | 80.0 | 
| Tami Gibson | quibusdam sed sit | 
Apple
 | 64.0 | 
| Tami Gibson | repudiandae officiis qui | 
Google
 | 47.0 | 
| Tami Gibson | saepe hic temporibus | 
Microsoft
 | 53.0 | 
| Tami Gibson | suscipit consectetur beatae | 
Google
 | 62.0 | 
| Tami Gibson | vitae eum pariatur | 
Google
 | 38.0 | 
| Tamika Cronin | animi aperiam tenetur | 
Google
 | 106.0 | 
| Tamika Cronin | doloribus hic corporis | 
Microsoft
 | 38.0 | 
| Tamika Cronin | eum amet quia | 
Google
 | 57.0 | 
| Tamika Cronin | odio velit repudiandae | 
Apple
 | 38.0 | 
| Tamika Cronin | perspiciatis totam perferendis | 
Apple
 | 49.0 | 
| Tamika Cronin | quibusdam sed sit | 
Apple
 | 35.0 | 
| Tamika Cronin | repudiandae officiis qui | 
Google
 | 39.0 | 
| Tamika Cronin | saepe hic temporibus | 
Microsoft
 | 95.0 | 
| Tamika Cronin | suscipit consectetur beatae | 
Google
 | 94.0 | 
| Tamika Cronin | vitae eum pariatur | 
Google
 | 84.0 | 
Grid:
class TimeEntriesGrid < ApplicationGrid
  #
  # Scope
  #
  scope do
    User.select(
      "users.name",
      "projects.name as project_name",
      "accounts.name as account_name",
      "sum(time_entries.hours) as report_hours"
    ).joins(
      time_entries: {project: :account}
    ).group("projects.name", "users.name", "accounts.name").
    order("users.name")
  end
  #
  # Filters
  #
  filter(
    :project_id, :enum,
    :select => :project_id_select,
    :multiple => true,
    :include_blank => false
  ) do |value|
    self.where(:time_entries => {:project_id => value})
  end
  filter(
    :year, :enum,
    :select => :year_select,
    :include_blank => false,
    :default => lambda {Date.today.year}
  ) do |value|
    self.where([
      "extract(year from time_entries.date) = ?",
      value.to_i
    ])
  end
  filter(
    :month, :enum,
    :select => :month_select,
    :include_blank => false,
    :default => lambda {Date.today.month}
  ) do |value|
    self.where([
      "extract(month from time_entries.date) = ?",
      value.to_i
    ])
  end
  #
  # Columns
  #
  column(:user_name, :header => "Developer", :order => "users.name") do
    self.name
  end
  column(:project_name, :header => "Project", :order => "projects.name")
  column(
    :account_name,
    :header => "Company",
    :order => "accounts.name",
  ) do |model|
    format(model.account_name) do
      render :partial => "time_entries/company", :locals => {:model => model}
    end
  end
  column(:report_hours, order: "sum(time_entries.hours)")
  protected
  def year_select
    TimeEntry.all.any? ? (TimeEntry.minimum(:date).year..TimeEntry.maximum(:date).year) : []
  end
  def month_select
    Date::MONTHNAMES[1..12].enum_for(:each_with_index).collect {|name, index| [name, index + 1]}
  end
  def project_id_select
    Project.all.map {|p| [p.name, p.id]}
  end
end
Controller:
class TimeEntriesController < ApplicationController
  def index
    @time_entries_grid = TimeEntriesGrid.new(params[:g]) do |scope|
      scope.page(params[:page] )
    end
  end
end
View:
.grid-index-grid
  %div{class: 'overflow-x-auto p-4'}
    = datagrid_form_with model: @time_entries_grid, url: time_entries_path
    %br/
    = datagrid_table(@time_entries_grid)
    = paginate @time_entries_grid.assets, window: 2
  %div
    = render :partial => "shared/source", :object => @time_entries_grid