Class: TravelExpenseReport

Inherits:
ApplicationRecord show all
Defined in:
app/models/travel_expense_report.rb

Overview

This model is used to encapsulate the queries involved in the expenses report using the ActiveRecord query interface. It's a read-only model offering a more convenient way of reading information from the request_expenses table, so it's not intended for creating or updating records. Please, use the RequestExpense model for that purpose.

See Also:

Constant Summary collapse

BY =
{
  event: [
    { field: :event_id, sql: 'event_id', hidden: true },
    { field: :event_name, sql: 'events.name' },
    { field: :event_country, sql: 'events.country_code' },
    { field: :event_start_date, sql: 'events.start_date' },
    { field: :event_end_date, sql: 'events.end_date' }
  ],

  event_country: [{ field: :event_country, sql: 'events.country_code' }],

  user: [
    { field: :user_id, sql: 'requests.user_id', hidden: true },
    { field: :user_nickname, sql: 'users.nickname' },
    { field: :user_fullname, sql: 'user_profiles.full_name' },
    { field: :user_country, sql: 'user_profiles.country_code' }
  ],

  user_country: [{ field: :user_country, sql: 'user_profiles.country_code' }],

  subject: [{ field: :subject, sql: 'request_expenses.subject' }],

  request: [
    { field: :request_id, sql: 'requests.id' },
    { field: :request_state, sql: 'requests.state' },
    { field: :reimbursement_state, sql: 'reimbursements.state' },
    { field: :user_id, sql: 'requests.user_id', hidden: true },
    { field: :user_nickname, sql: 'users.nickname' },
    { field: :user_fullname, sql: 'user_profiles.full_name' },
    { field: :event_id, sql: 'event_id', hidden: true },
    { field: :event_name, sql: 'events.name' }
  ],

  expense: [
    { field: :expense_id, sql: 'request_expenses.id', hidden: true },
    { field: :request_id, sql: 'requests.id' },
    { field: :request_state, sql: 'requests.state' },
    { field: :reimbursement_state, sql: 'reimbursements.state' },
    { field: :user_id, sql: 'requests.user_id', hidden: true },
    { field: :user_nickname, sql: 'users.nickname' },
    { field: :user_fullname, sql: 'user_profiles.full_name' },
    { field: :event_id, sql: 'event_id', hidden: true },
    { field: :event_name, sql: 'events.name' },
    { field: :subject, sql: 'request_expenses.subject' }
  ]
}.freeze

Belongs to collapse

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes

Class Method Details

.byActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are by. Active Record Scope

Returns:

See Also:



75
76
77
78
79
80
81
82
# File 'app/models/travel_expense_report.rb', line 75

scope :by, lambda { |type, g|
  currency = RequestExpense.currency_field_for(type.to_sym)
  r = joins(request: [{ user: :profile }, :event])
  r = r.joins('LEFT JOIN reimbursements ON reimbursements.request_id = requests.id')
  r = r.select("sum(#{type}_amount) AS sum_amount, #{currency} AS sum_currency, #{TravelExpenseReport::BY[g.to_sym].map { |f| "#{f[:sql]} AS #{f[:field]}" }.join(', ')}")
  r = r.where("#{type}_amount IS NOT NULL")
  r = r.group("#{currency}, #{TravelExpenseReport::BY[g.to_sym].map { |f| f[:sql] }.join(', ')}")
}

.count(exp) ⇒ Integer

Total number of records for a given report

Parameters:

  • exp (ActiveRecord::Relation)

Returns:

  • (Integer)

    number of records



192
193
194
195
196
197
198
199
200
201
# File 'app/models/travel_expense_report.rb', line 192

def self.count(exp)
  count = connection.execute("select count(*) as c from (#{exp.except(:limit, :offset, :order).to_sql}) query")
  result = count.first
  # Most adapters return a hash, but the MySQL one returns an array
  if result.is_a?(Array)
    result.first.to_i
  else
    result['c'].to_i
  end
end

.event_country_code_eqActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are event country code eq. Active Record Scope

Returns:

See Also:



105
106
107
# File 'app/models/travel_expense_report.rb', line 105

scope :event_country_code_eq, lambda { |country|
  where('events.country_code' => country)
}

.event_eqActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are event eq. Active Record Scope

Returns:

See Also:



95
96
97
# File 'app/models/travel_expense_report.rb', line 95

scope :event_eq, lambda { |event_id|
  where('events.id' => event_id)
}

.event_name_containsActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are event name contains. Active Record Scope

Returns:

See Also:



100
101
102
# File 'app/models/travel_expense_report.rb', line 100

scope :event_name_contains, lambda { |name|
  where(['lower(events.name) like ?', "%#{name}%"])
}

.event_start_gteActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are event start gte. Active Record Scope

Returns:

See Also:



90
91
92
# File 'app/models/travel_expense_report.rb', line 90

scope :event_start_gte, lambda { |date|
  where(['events.start_date >= ?', date])
}

.event_start_lteActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are event start lte. Active Record Scope

Returns:

See Also:



85
86
87
# File 'app/models/travel_expense_report.rb', line 85

scope :event_start_lte, lambda { |date|
  where(['events.start_date <= ?', date])
}

.fields_for(group) ⇒ array

Ordered list of field names for a given call to the 'by' scope

Parameters:

  • group (#to_sym)

    The grouping option used when invoking the scope

Returns:

  • (array)

    The names of the resulting fields (as an array of symbols)



138
139
140
# File 'app/models/travel_expense_report.rb', line 138

def self.fields_for(group)
  TravelExpenseReport::BY[group.to_sym].reject { |f| f[:hidden] }.map { |i| i[:field] } + %i[sum_amount sum_currency]
end

.groupsarray

Available group options for calling the 'by' scope

Returns:

  • (array)

    An array with the available grouping criterias (as symbols)



145
146
147
# File 'app/models/travel_expense_report.rb', line 145

def self.groups
  TravelExpenseReport::BY.keys
end

.reimbursement_state_eqActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are reimbursement state eq. Active Record Scope

Returns:

See Also:



125
126
127
# File 'app/models/travel_expense_report.rb', line 125

scope :reimbursement_state_eq, lambda { |state|
  where('reimbursements.state' => state.to_s)
}

A relation of TravelExpenseReports that are related to. Active Record Scope

Returns:

See Also:



130
131
132
# File 'app/models/travel_expense_report.rb', line 130

scope :related_to, lambda { |user|
  joins(:request).where('requests.user_id' => user)
}

.request_state_eqActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are request state eq. Active Record Scope

Returns:

See Also:



120
121
122
# File 'app/models/travel_expense_report.rb', line 120

scope :request_state_eq, lambda { |state|
  where('requests.state' => state.to_s)
}

.user_country_code_eqActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are user country code eq. Active Record Scope

Returns:

See Also:



115
116
117
# File 'app/models/travel_expense_report.rb', line 115

scope :user_country_code_eq, lambda { |country|
  where('user_profiles.country_code' => country)
}

.user_name_containsActiveRecord::Relation<TravelExpenseReport>

A relation of TravelExpenseReports that are user name contains. Active Record Scope

Returns:

See Also:



110
111
112
# File 'app/models/travel_expense_report.rb', line 110

scope :user_name_contains, lambda { |name|
  where(['lower(user_profiles.full_name) like ? or lower(users.nickname) like ?'] + ["%#{name}%"] * 2)
}

Instance Method Details

#false_eventObject

Alias for Request#event

Returns:

  • (Object)

    Request#false_event

See Also:



17
# File 'app/models/travel_expense_report.rb', line 17

delegate :event, to: :request, prefix: false

#false_reimbursementObject

Alias for Request#reimbursement

Returns:

  • (Object)

    Request#false_reimbursement

See Also:



15
# File 'app/models/travel_expense_report.rb', line 15

delegate :reimbursement, to: :request, prefix: false

#false_userObject

Alias for Request#user

Returns:

  • (Object)

    Request#false_user

See Also:



16
# File 'app/models/travel_expense_report.rb', line 16

delegate :user, to: :request, prefix: false

Checks if the report is related to a given user.

Used during access control.

Parameters:

Returns:

  • (Boolean)

    true if the request that originated the report record is associated with the user



184
185
186
# File 'app/models/travel_expense_report.rb', line 184

def related_to?(user)
  request.user == user
end

#requestRequest

Returns:

See Also:



14
# File 'app/models/travel_expense_report.rb', line 14

belongs_to :request, -> { where 'requests.type' => 'TravelSponsorship' }

#value_for(name) ⇒ Object

Casted value of a given attribute.

Needed because ActiveRecord always return the attributes as strings when ActiveRelation#select is used. This method casts the attributes back to its correct type. Intended to be used on records resulting from a call to the 'by' scope.

Parameters:

  • name (#to_sym)

    attribute name

Returns:

  • (Object)

    value of the attribute



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'app/models/travel_expense_report.rb', line 158

def value_for(name)
  # At this point creating a more generic solution is not worthy, we simply
  # check explicitly for one of the three special cases
  if name.to_sym == :sum_amount
    # to_f.to_s to ensure that it has a decimal part (with any db engine)
    BigDecimal(sum_amount.to_f.to_s || '0.0')
  elsif %i[event_start_date event_end_date].include? name.to_sym
    d = send(name)
    if d.blank?
      nil
    elsif d.is_a?(Date)
      d
    else
      Date.parse(d)
    end
  else
    send(name)
  end
end