service_packs_controller.rb 7.53 KB
Newer Older
1
class ServicePacksController < ApplicationController
Khang Le's avatar
Khang Le committed
2
3
4
5
6
7
8
9
  # only allow admin
  before_action :require_admin

  # Specifying Layouts for Controllers, looking at OPENPROJECT_ROOT/app/views/layouts/admin
  layout 'admin'

  def index
    @service_packs = ServicePack.all
10
11
    # for demo
    #ServicePacksMailer.notify_under_threshold1(User.first,@service_packs.first).deliver_now
Khang Le's avatar
Khang Le committed
12
13
14
15
  end

  def new
    @service_pack = ServicePack.new
16
17
18
    # TimeEntryActivity.shared.count.times {@service_pack.mapping_rates.build}
    @sh = TimeEntryActivity.shared
    @c = TimeEntryActivity.shared.count
Khang Le's avatar
Khang Le committed
19
20
21
22
  end

  def show
    @service_pack = ServicePack.find(params[:id])
23
24
    # controller chooses not to get the thresholds.
    # assume the service pack exists.
25
    # TODO: make a separate action JSON only.
Tam Le's avatar
Tam Le committed
26
    binding.pry
27
    respond_to do |format|
28
      format.json {
29
30
        # the function already converted this to json
        render plain: ServicePackPresenter.new(@service_pack).json_export(:rate), status: 200
31
32
      }
      format.html {
33
34
35
        # http://www.chrisrolle.com/en/blog/benchmark-preload-vs-eager_load
        @rates = @service_pack.mapping_rates.preload(:activity)
        @assignments = @service_pack.assignments.preload(:project)
36
      }
Tam Le's avatar
Tam Le committed
37
      format.csv {
Tam Le's avatar
Tam Le committed
38
        render csv: ServicePackReport.new(@service_pack).call, filename: "service_pack_#{@service_pack.name}.csv"
Tam Le's avatar
Tam Le committed
39
      }
40
    end
Khang Le's avatar
Khang Le committed
41
42
  end

Tam Le's avatar
Tam Le committed
43
44
45
46
47
  # The string with the minus sign in front is a shorthand for <string>.freeze
  # reducing server processing time (and testing time) by 30%!
  # Freezing a string literal will stop it from being created anew over and over.
  # All literal strings will be frozen in Ruby 3 by default, which is a good idea.

Khang Le's avatar
Khang Le committed
48
  def create
49
    mapping_rate_attribute = params[:service_pack][:mapping_rates_attributes]
50
    # binding.pry
51
    activity_id = []
52
    mapping_rate_attribute.each {|_index, hash_value| activity_id.push(hash_value[:activity_id])}
53

54
    if activity_id.uniq.length == activity_id.length
55
      @service_pack = ServicePack.new(service_pack_params)
56
57
      # render plain: 'not duplicated'
      if @service_pack.save
58
        flash[:notice] = -'Service Pack creation successful.'
59
60
        redirect_to action: :show, id: @service_pack.id and return
      else
Tam Le's avatar
Tam Le committed
61
        flash.now[:error] = -'Service Pack creation failed.'
62
      end
Khang Le's avatar
Khang Le committed
63
    else
64
      # render plain: 'duplicated'
Tam Le's avatar
Tam Le committed
65
      flash.now[:error] = -'Only one rate can be defined to one activity.'
Khang Le's avatar
Khang Le committed
66
    end
67
68
69
70
    # the only successful path has returned 10 lines ago.
    @sh = TimeEntryActivity.shared
    @c = TimeEntryActivity.shared.count
    render 'new'
Khang Le's avatar
Khang Le committed
71
72
  end

Kiet's avatar
Kiet committed
73
  def edit
NguyenHoangTuanKiet's avatar
NguyenHoangTuanKiet committed
74
    @sp = ServicePack.find_by(id: params[:id])
Tam Le's avatar
Tam Le committed
75
    if @sp.nil?
76
      flash[:error] = -"Service Pack not found"
Tam Le's avatar
Tam Le committed
77
78
      redirect_to action: :index and return
    end
79
    # @activity = @sp.time_entry_activities.build
Kiet's avatar
Kiet committed
80
81
  end

Tam Le's avatar
Tam Le committed
82
  def update
NguyenHoangTuanKiet's avatar
NguyenHoangTuanKiet committed
83
    @sp = ServicePack.find_by(id: params[:id])
Tam Le's avatar
Tam Le committed
84
    if @sp.nil?
85
      flash[:error] = -"Service Pack not found"
Tam Le's avatar
Tam Le committed
86
87
      redirect_to action: :index and return
    end
88
    mapping_rate_attribute = params[:service_pack][:mapping_rates_attributes]
Tam Le's avatar
Tam Le committed
89
    activity_id = []
90
    mapping_rate_attribute.each {|_index, hash_value| activity_id.push(hash_value[:activity_id])}
Tam Le's avatar
Tam Le committed
91
92

    if activity_id.uniq.length == activity_id.length
93
94
95
96
      # No duplication
      add_units
      @sp.assign_attributes(service_pack_edit_params)
      # binding.pry
Tam Le's avatar
Tam Le committed
97
      if @sp.save
98
        flash[:notice] = -'Service Pack update successful.'
99
        redirect_to @sp
Tam Le's avatar
Tam Le committed
100
      else
101
        flash.now[:error] = -'Service Pack update failed.'
Tam Le's avatar
Tam Le committed
102
        render -'edit'
Tam Le's avatar
Tam Le committed
103
104
      end
    else
105
      # Duplication
106
      flash.now[:error] = -'Only one rate can be defined to one activity.'
Tam Le's avatar
Tam Le committed
107
      render -'edit'
Tam Le's avatar
Tam Le committed
108
109
110
    end
  end

Kiet's avatar
Kiet committed
111
  def destroy
112
    @sp = ServicePack.find_by(id: params[:id])
Tam Le's avatar
Tam Le committed
113
    if @sp.nil?
114
      flash[:error] = -"Service Pack not found"
Tam Le's avatar
Tam Le committed
115
116
      redirect_to action: :index and return
    end
117
    if @sp.assigned?
Tam Le's avatar
Tam Le committed
118
      flash.now[:error] = "Please unassign this SP from all projects before proceeding!"
119
      redirect_to @sp
120
121
    end
    @sp.destroy!
Kiet's avatar
Kiet committed
122
123
124
125

    redirect_to service_packs_path
  end

126
127
128
129
130
131
  # for breadcrumb code
  def show_local_breadcrumb
    true
  end

  def default_breadcrumb
Tam Le's avatar
Tam Le committed
132
    action_name == 'index'? -'Service Packs' : ActionController::Base.helpers.link_to(-'Service Packs', service_packs_path)
133
  end
Tam Le's avatar
Tam Le committed
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

  # =======================================================
  # :Docs
  # * Limit: Serving JSON only. Must be Admin to access.
  # * Purpose:
  # Return a table with consumed units for a Service Pack grouped by activities and sorted
  # from large to small.
  # * Expected Inputs:
  # [service_pack_id]: Sharing the same route with the resourceful default.
  # Put in the link. Mandatory.
  # [start_period]: Beginning of the counting period. As a date. Optional.
  # [end_period]: Ending of the counting period. As a date. Optional.
  # start_period MUST NOT be later than end_period.
  # Both or none of [start_period, end_period] can be present.
  # * Expected Outputs
  # Top class: None
  # Content: Array of object having [name, consumed]
151
152
  # - consumed: How many units are consumed (in given period)
  # - act_name: Name of activity 
Tam Le's avatar
Tam Le committed
153
154
  # Status: 200
  # * When raising error
Tam Le's avatar
Tam Le committed
155
  # HTTP 404: SP not found
Tam Le's avatar
Tam Le committed
156
157
158
  # HTTP 400: Malformed request.
  # =======================================================

Tam Le's avatar
Tam Le committed
159
  def statistics
160
161
162
    start_day = params[:start_period]&.to_date # ruby >= 2.3.0
    end_day = params[:end_period]&.to_date
    if start_day.nil? ^ end_day.nil?
163
      render json: {error: 'GET OUT!'}, status: 400 and return
164
    end
Tam Le's avatar
Tam Le committed
165
    if !ServicePack.find_by(id: params[:service_pack_id])
166
      render json: {error: 'NOT FOUND'}, status: 404 and return
Tam Le's avatar
Tam Le committed
167
    end
168
169
170
171
172

    # Notice: Change max(t3.name) to ANY_VALUE(t3.name) on production builds.
    # MySQL specific >= 5.7.5
    # https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

Tam Le's avatar
Tam Le committed
173
    get_parent_id = <<-SQL
Tam Le's avatar
Tam Le committed
174
      SELECT id, name, COALESCE(parent_id, id) AS pid
Tam Le's avatar
Tam Le committed
175
      FROM #{TimeEntryActivity.table_name}
176
      WHERE type = 'TimeEntryActivity'
177
    SQL
Tam Le's avatar
Tam Le committed
178
    body_query = <<-SQL
179
      SELECT t3.pid AS act_id, max(t3.name) AS act_name, sum(t1.units) AS consumed
Tam Le's avatar
Tam Le committed
180
181
182
183
      FROM #{ServicePackEntry.table_name} t1
      INNER JOIN #{TimeEntry.table_name} t2
      ON t1.time_entry_id = t2.id
      INNER JOIN (#{get_parent_id}) t3
184
      ON t2.activity_id = t3.id
185
    SQL
Tam Le's avatar
Tam Le committed
186
    group_clause = <<-SQL
187
188
      GROUP BY t3.pid
      ORDER BY consumed DESC
189
    SQL
Tam Le's avatar
Tam Le committed
190
    where_clause = "WHERE t1.service_pack_id = ?"
191
    where_clause << (start_day.nil? ? '' : ' AND t1.created_at BETWEEN ? AND ?')
Tam Le's avatar
Tam Le committed
192
    query = body_query + where_clause + group_clause
193
    # binding.pry
194
195
    par = start_day.nil? ? [query, params[:service_pack_id]] : [query, params[:service_pack_id], start_day, end_day]
    sql = ActiveRecord::Base.send(:sanitize_sql_array, par)
196
    render json: ActiveRecord::Base.connection.exec_query(sql).to_hash, status: 200
Tam Le's avatar
Tam Le committed
197
198
  end

Khang Le's avatar
Khang Le committed
199
200
201
  private

  def service_pack_params
202
203
    params.require(:service_pack).permit(:name, :total_units, :started_date, :expired_date, :threshold1, :threshold2,
                                         mapping_rates_attributes: [:id, :activity_id, :service_pack_id, :units_per_hour, :_destroy])
Khang Le's avatar
Khang Le committed
204
  end
205

206
207
208
  def service_pack_edit_params
    params.require(:service_pack).permit(:threshold1, :threshold2,
                                        mapping_rates_attributes: [:id, :activity_id, :service_pack_id, :units_per_hour, :_destroy])
Khang Le's avatar
Khang Le committed
209
  end
210

211
212
213
214
215
216
217
218
  def add_units
    return unless params[:service_pack][:total_units]
    if (t = params[:service_pack][:total_units].to_f) <= 0.0
      @sp.errors.add(:total_units, 'is invalid') and return
    end
    @sp.grant(t - @sp.total_units) unless t == @sp.total_units
  end

219
end