spree_advanced_reporting 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +5 -0
  3. data/README.md +23 -0
  4. data/Rakefile +20 -0
  5. data/TODO.txt +3 -0
  6. data/app/assets/images/admin/advanced_reporting/asc.gif +0 -0
  7. data/app/assets/images/admin/advanced_reporting/barchart.png +0 -0
  8. data/app/assets/images/admin/advanced_reporting/bg.gif +0 -0
  9. data/app/assets/images/admin/advanced_reporting/close.png +0 -0
  10. data/app/assets/images/admin/advanced_reporting/desc.gif +0 -0
  11. data/app/assets/images/admin/advanced_reporting/menu-current-opposite.png +0 -0
  12. data/app/assets/images/admin/advanced_reporting/open.png +0 -0
  13. data/app/assets/images/admin/advanced_reporting/save.png +0 -0
  14. data/app/assets/images/admin/advanced_reporting/search.png +0 -0
  15. data/app/assets/images/admin/advanced_reporting/usa.png +0 -0
  16. data/app/assets/images/admin/advanced_reporting/world.png +0 -0
  17. data/app/assets/javascripts/admin/advanced_reporting/advanced_reporting.js +46 -0
  18. data/app/assets/javascripts/admin/advanced_reporting/jquery.tablesorter.min.js +4 -0
  19. data/app/assets/stylesheets/admin/advanced_reporting/advanced_reporting.css.erb +18 -0
  20. data/app/assets/stylesheets/pdf.css +15 -0
  21. data/app/controllers/spree/admin/advanced_report_overview_controller.rb +21 -0
  22. data/app/controllers/spree/admin/reports_controller_decorator.rb +127 -0
  23. data/app/helpers/advanced_report_helper.rb +13 -0
  24. data/app/models/ruport/formatter/html_decorator.rb +29 -0
  25. data/app/models/ruport/formatter/wicked_pdf_decorator.rb +12 -0
  26. data/app/views/spree/admin/advanced_report_overview/index.html.erb +117 -0
  27. data/app/views/spree/admin/reports/_advanced_report_criteria.html.erb +64 -0
  28. data/app/views/spree/admin/reports/geo_base.html.erb +61 -0
  29. data/app/views/spree/admin/reports/increment_base.html.erb +63 -0
  30. data/app/views/spree/admin/reports/outstanding.html.erb +16 -0
  31. data/app/views/spree/admin/reports/top_base.html.erb +17 -0
  32. data/config/locales/en.yml +96 -0
  33. data/config/locales/pt-BR.yml +92 -0
  34. data/config/routes.rb +37 -0
  35. data/lib/spree/advanced_report.rb +225 -0
  36. data/lib/spree/advanced_report/geo_report.rb +2 -0
  37. data/lib/spree/advanced_report/geo_report/geo_profit.rb +44 -0
  38. data/lib/spree/advanced_report/geo_report/geo_revenue.rb +44 -0
  39. data/lib/spree/advanced_report/geo_report/geo_units.rb +42 -0
  40. data/lib/spree/advanced_report/increment_report.rb +78 -0
  41. data/lib/spree/advanced_report/increment_report/count.rb +33 -0
  42. data/lib/spree/advanced_report/increment_report/profit.rb +41 -0
  43. data/lib/spree/advanced_report/increment_report/revenue.rb +40 -0
  44. data/lib/spree/advanced_report/increment_report/units.rb +33 -0
  45. data/lib/spree/advanced_report/top_report.rb +2 -0
  46. data/lib/spree/advanced_report/top_report/top_customers.rb +32 -0
  47. data/lib/spree/advanced_report/top_report/top_products.rb +35 -0
  48. data/lib/spree/advanced_report/transaction_report.rb +79 -0
  49. data/lib/spree_advanced_reporting.rb +25 -0
  50. data/spree_advanced_reporting.gemspec +22 -0
  51. metadata +133 -0
@@ -0,0 +1,225 @@
1
+ module Spree
2
+ class AdvancedReport
3
+ include Ruport
4
+ attr_accessor :orders, :product_text, :date_text, :taxon_text, :ruportdata, :search,
5
+ :data, :params, :taxon, :product, :product_in_taxon, :unfiltered_params
6
+
7
+ def name
8
+ I18n.t("adv_report.base.name")
9
+ end
10
+
11
+ def description
12
+ I18n.t("adv_report.base.description")
13
+ end
14
+
15
+ def initialize(params)
16
+ # this enables subclasses to provide different defaults to the search
17
+ # by setting the defaults before calling super
18
+ self.params ||= params
19
+
20
+ self.data = {}
21
+ self.ruportdata = {}
22
+ self.unfiltered_params = params[:search].blank? ? {} : params[:search].clone
23
+
24
+ params[:search] ||= {}
25
+ params[:advanced_reporting] ||= {}
26
+
27
+ if Order.count > 0
28
+ begin
29
+ params[:search][:created_at_gt] = Time.zone.parse(params[:search][:created_at_gt]).beginning_of_day
30
+ rescue
31
+ params[:search][:created_at_gt] = Date.today.beginning_of_day
32
+ end
33
+
34
+ # TODO if lt is defined, and gt is not, gt then should use better default than end of today
35
+ # maybe 24 hours before the defined lt end of day
36
+
37
+ begin
38
+ params[:search][:created_at_lt] = Time.zone.parse(params[:search][:created_at_lt]).end_of_day
39
+ rescue
40
+ params[:search][:created_at_lt] = Date.today.end_of_day
41
+ end
42
+ end
43
+
44
+ # offer shipped vs completed order filtering
45
+ # in some cases, revenue reports should be based on the time when the revenue
46
+ # is earned (i.e. shipped) not when the order was made or the credit card was processed
47
+
48
+ # it is also important to exclude canceled orders and orders that were not completed
49
+ # before spree 1.1.3 there was a bug that caused Spree::Shipment.shipped_at not be filled
50
+ # easy fix is to copy the completed_at from the order associated with the shipment
51
+ # https://gist.github.com/3187793#file_shipments_shipped_at_fix.rb
52
+
53
+ filter_address = 'billing'
54
+
55
+ if params[:advanced_reporting][:state_based_on_taxable_address] == '1'
56
+ filter_address = Spree::Config[:tax_using_ship_address] ? 'shipping' : 'billing'
57
+ end
58
+
59
+ if params[:advanced_reporting][:order_type] == 'shipped'
60
+ shipped_search_params = {
61
+ :shipped_at_gt => params[:search][:created_at_gt],
62
+ :shipped_at_lt => params[:search][:created_at_lt],
63
+ :order_state_not_eq => 'canceled',
64
+ :order_completed_at_not_null => true
65
+ }
66
+
67
+ if params[:advanced_reporting][:state_id].present?
68
+ shipped_search_params[
69
+ filter_address == 'shipping' ? :order_ship_address_state_id_eq : :order_bill_address_state_id_eq
70
+ ] = params[:advanced_reporting][:state_id]
71
+ end
72
+
73
+ # including the ransack predicate will not speed up the SQL query but will not include only fully shipped orders
74
+ only_fully_shipped = params[:advanced_reporting][:shipment] == 'fully_shipped'
75
+ shipped_search_params[:order_inventory_units_shipment_id_not_null] = true if only_fully_shipped
76
+
77
+ # the tricky part here is that orders can have multiple shipments
78
+ # we need to prevent orders from being included twice in the report
79
+ # by choosing to include the order in the earliest report possible
80
+ # (i.e. the first order that shipped) and exclude it from any reports after that
81
+
82
+ @search = Shipment.includes(:order).search shipped_search_params
83
+
84
+ self.orders = @search.result(:distinct => true).select do |shipment|
85
+ # these manual exclusions could not be done via SQL queries as far as I could tell
86
+ # they are ordered by least to greatest SQL complexity
87
+
88
+ next true if shipment.order.shipments.size == 1
89
+
90
+ # if the shipment retrieved is the last shipment shipped for the order, then include the order
91
+ next false if shipment.order.shipments.sort { |a, b| b.shipped_at <=> a.shipped_at }.first == shipment
92
+
93
+ # conditionally exclude orders which are not fully shipped
94
+ next false if only_fully_shipped && shipment.order.inventory_units.detect { |i| i.shipment.blank? }.blank?
95
+
96
+ true
97
+ end.map(&:order)
98
+ else
99
+ params[:search][:completed_at_not_null] = true
100
+ params[:search][:state_not_eq] = 'canceled'
101
+
102
+ if params[:advanced_reporting][:state_id].present?
103
+ params[:search][
104
+ filter_address == 'shipping' ? :ship_address_state_id_eq : :bill_address_state_id_eq
105
+ ] = params[:advanced_reporting][:state_id]
106
+ end
107
+
108
+ only_fully_shipped = params[:advanced_reporting][:shipment] == 'fully_shipped'
109
+ params[:inventory_units_shipment_id_not_null] = true if only_fully_shipped
110
+
111
+ @search = Order.search(params[:search])
112
+
113
+ self.orders = @search.result(:distinct => true).select do |order|
114
+ next false if only_fully_shipped && order.inventory_units.detect { |i| i.shipment.blank? }.blank?
115
+
116
+ true
117
+ end
118
+ end
119
+
120
+ self.product_in_taxon = true
121
+ if params[:advanced_reporting]
122
+ if params[:advanced_reporting][:taxon_id] && params[:advanced_reporting][:taxon_id] != ''
123
+ self.taxon = Taxon.find(params[:advanced_reporting][:taxon_id])
124
+ end
125
+ if params[:advanced_reporting][:product_id] && params[:advanced_reporting][:product_id] != ''
126
+ self.product = Product.find(params[:advanced_reporting][:product_id])
127
+ end
128
+ end
129
+ if self.taxon && self.product && !self.product.taxons.include?(self.taxon)
130
+ self.product_in_taxon = false
131
+ end
132
+
133
+ if self.product
134
+ self.product_text = "Product: #{self.product.name}<br />"
135
+ end
136
+ if self.taxon
137
+ self.taxon_text = "Taxon: #{self.taxon.name}<br />"
138
+ end
139
+
140
+ # Above searchlogic date settings
141
+ self.date_text = "#{I18n.t("adv_report.base.range")}:"
142
+ if self.unfiltered_params
143
+ if self.unfiltered_params[:created_at_gt] != '' && self.unfiltered_params[:created_at_lt] != ''
144
+ self.date_text += " From #{self.unfiltered_params[:created_at_gt]} to #{self.unfiltered_params[:created_at_lt]}"
145
+ elsif self.unfiltered_params[:created_at_gt] != ''
146
+ self.date_text += " After #{self.unfiltered_params[:created_at_gt]}"
147
+ elsif self.unfiltered_params[:created_at_lt] != ''
148
+ self.date_text += " Before #{self.unfiltered_params[:created_at_lt]}"
149
+
150
+ # TODO this was pulled in from another branch and has some nice internationalization improvements
151
+ # if self.unfiltered_params[:created_at_greater_than] != '' && self.unfiltered_params[:created_at_less_than] != ''
152
+ # self.date_text += " #{I18n.t("adv_report.base.from")} #{self.unfiltered_params[:created_at_greater_than]} to #{self.unfiltered_params[:created_at_less_than]}"
153
+ # elsif self.unfiltered_params[:created_at_greater_than] != ''
154
+ # self.date_text += " #{I18n.t("adv_report.base.after")} #{self.unfiltered_params[:created_at_greater_than]}"
155
+ # elsif self.unfiltered_params[:created_at_less_than] != ''
156
+ # self.date_text += " #{I18n.t("adv_report.base.before")} #{self.unfiltered_params[:created_at_less_than]}"
157
+ else
158
+ self.date_text += " #{I18n.t("adv_report.base.all")}"
159
+ end
160
+ else
161
+ self.date_text += " #{I18n.t("adv_report.base.all")}"
162
+ end
163
+ end
164
+
165
+ def download_url(base, format, report_type = nil)
166
+ elements = []
167
+ params[:advanced_reporting] ||= {}
168
+ params[:advanced_reporting]["report_type"] = report_type if report_type
169
+ if params
170
+ [:search, :advanced_reporting].each do |type|
171
+ if params[type]
172
+ params[type].each { |k, v| elements << "#{type}[#{k}]=#{v}" }
173
+ end
174
+ end
175
+ end
176
+ base.gsub!(/^\/\//,'/')
177
+ base + '.' + format + '?' + elements.join('&')
178
+ end
179
+
180
+ def revenue(order)
181
+ rev = order.item_total
182
+ if !self.product.nil? && product_in_taxon
183
+ rev = order.line_items.select { |li| li.product == self.product }.inject(0) { |a, b| a += b.quantity * b.price }
184
+ elsif !self.taxon.nil?
185
+ rev = order.line_items.select { |li| li.product && li.product.taxons.include?(self.taxon) }.inject(0) { |a, b| a += b.quantity * b.price }
186
+ end
187
+ adjustment_revenue = order.adjustments.sum(:amount)
188
+ rev += adjustment_revenue if rev > 0
189
+ self.product_in_taxon ? rev : 0
190
+ end
191
+
192
+ def profit(order)
193
+ profit = order.line_items.inject(0) { |profit, li| profit + (li.variant.price - li.variant.cost_price.to_f)*li.quantity }
194
+ if !self.product.nil? && product_in_taxon
195
+ profit = order.line_items.select { |li| li.product == self.product }.inject(0) { |profit, li| profit + (li.variant.price - li.variant.cost_price.to_f)*li.quantity }
196
+ elsif !self.taxon.nil?
197
+ profit = order.line_items.select { |li| li.product && li.product.taxons.include?(self.taxon) }.inject(0) { |profit, li| profit + (li.variant.price - li.variant.cost_price.to_f)*li.quantity }
198
+ end
199
+ profit += order.adjustments.sum(:amount)
200
+ self.product_in_taxon ? profit : 0
201
+ end
202
+
203
+ def units(order)
204
+ units = order.line_items.sum(:quantity)
205
+ if !self.product.nil? && product_in_taxon
206
+ units = order.line_items.select { |li| li.product == self.product }.inject(0) { |a, b| a += b.quantity }
207
+ elsif !self.taxon.nil?
208
+ units = order.line_items.select { |li| li.product && li.product.taxons.include?(self.taxon) }.inject(0) { |a, b| a += b.quantity }
209
+ end
210
+ self.product_in_taxon ? units : 0
211
+ end
212
+
213
+ def order_count(order)
214
+ self.product_in_taxon ? 1 : 0
215
+ end
216
+
217
+ def date_range
218
+ if self.params[:search][:created_at_gt].to_date == self.params[:search][:created_at_lt].to_date
219
+ self.params[:search][:created_at_gt].to_date.to_s
220
+ else
221
+ "#{self.params[:search][:created_at_gt].to_date} &ndash; #{self.params[:search][:created_at_lt].to_date}"
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,2 @@
1
+ class Spree::AdvancedReport::GeoReport < Spree::AdvancedReport
2
+ end
@@ -0,0 +1,44 @@
1
+ class Spree::AdvancedReport::GeoReport::GeoProfit < Spree::AdvancedReport::GeoReport
2
+ def name
3
+ I18n.t("adv_report.geo_report.profit.name")
4
+ end
5
+
6
+ def column
7
+ I18n.t("adv_report.geo_report.profit.column")
8
+ end
9
+
10
+ def description
11
+ I18n.t("adv_report.geo_report.profit.description")
12
+ end
13
+
14
+ def initialize(params)
15
+ super(params)
16
+
17
+ data = { :state => {}, :country => {} }
18
+ orders.each do |order|
19
+ profit = profit(order)
20
+ if order.bill_address.state
21
+ data[:state][order.bill_address.state_id] ||= {
22
+ :name => order.bill_address.state.name,
23
+ :profit => 0
24
+ }
25
+ data[:state][order.bill_address.state_id][:profit] += profit
26
+ end
27
+ if order.bill_address.country
28
+ data[:country][order.bill_address.country_id] ||= {
29
+ :name => order.bill_address.country.name,
30
+ :profit => 0
31
+ }
32
+ data[:country][order.bill_address.country_id][:profit] += profit
33
+ end
34
+ end
35
+
36
+ [:state, :country].each do |type|
37
+ ruportdata[type] = Table(I18n.t("adv_report.geo_report.profit.table"))
38
+ data[type].each { |k, v| ruportdata[type] << { "location" => v[:name], I18n.t("adv_report.profit") => v[:profit] } }
39
+ ruportdata[type].sort_rows_by!([I18n.t("adv_report.profit")], :order => :descending)
40
+ ruportdata[type].rename_column("location", type.to_s.capitalize)
41
+ ruportdata[type].replace_column(I18n.t("adv_report.profit")) { |r| "$%0.2f" % r.send(I18n.t("adv_report.profit")) }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ class Spree::AdvancedReport::GeoReport::GeoRevenue < Spree::AdvancedReport::GeoReport
2
+ def name
3
+ I18n.t("adv_report.geo_report.revenue.name")
4
+ end
5
+
6
+ def column
7
+ I18n.t("adv_report.geo_report.revenue.column")
8
+ end
9
+
10
+ def description
11
+ I18n.t("adv_report.geo_report.revenue.description")
12
+ end
13
+
14
+ def initialize(params)
15
+ super(params)
16
+
17
+ data = { :state => {}, :country => {} }
18
+ orders.each do |order|
19
+ revenue = revenue(order)
20
+ if order.bill_address.state
21
+ data[:state][order.bill_address.state_id] ||= {
22
+ :name => order.bill_address.state.name,
23
+ :revenue => 0
24
+ }
25
+ data[:state][order.bill_address.state_id][:revenue] += revenue
26
+ end
27
+ if order.bill_address.country
28
+ data[:country][order.bill_address.country_id] ||= {
29
+ :name => order.bill_address.country.name,
30
+ :revenue => 0
31
+ }
32
+ data[:country][order.bill_address.country_id][:revenue] += revenue
33
+ end
34
+ end
35
+
36
+ [:state, :country].each do |type|
37
+ ruportdata[type] = Table(I18n.t("adv_report.geo_report.revenue.table"))
38
+ data[type].each { |k, v| ruportdata[type] << { "location" => v[:name], I18n.t("adv_report.revenue") => v[:revenue] } }
39
+ ruportdata[type].sort_rows_by!([I18n.t("adv_report.revenue")], :order => :descending)
40
+ ruportdata[type].rename_column("location", type.to_s.capitalize)
41
+ ruportdata[type].replace_column(I18n.t("adv_report.revenue")) { |r| "$%0.2f" % r.send(I18n.t("adv_report.revenue")) }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ class Spree::AdvancedReport::GeoReport::GeoUnits < Spree::AdvancedReport::GeoReport
2
+ def name
3
+ I18n.t("adv_report.geo_report.units.name")
4
+ end
5
+
6
+ def column
7
+ I18n.t("adv_report.geo_report.units.column")
8
+ end
9
+
10
+ def description
11
+ I18n.t("adv_report.geo_report.units.description")
12
+ end
13
+ def initialize(params)
14
+ super(params)
15
+
16
+ data = { :state => {}, :country => {} }
17
+ orders.each do |order|
18
+ units = units(order)
19
+ if order.bill_address.state
20
+ data[:state][order.bill_address.state_id] ||= {
21
+ :name => order.bill_address.state.name,
22
+ :units => 0
23
+ }
24
+ data[:state][order.bill_address.state_id][:units] += units
25
+ end
26
+ if order.bill_address.country
27
+ data[:country][order.bill_address.country_id] ||= {
28
+ :name => order.bill_address.country.name,
29
+ :units => 0
30
+ }
31
+ data[:country][order.bill_address.country_id][:units] += units
32
+ end
33
+ end
34
+
35
+ [:state, :country].each do |type|
36
+ ruportdata[type] = Table(I18n.t("adv_report.geo_report.units.table"))
37
+ data[type].each { |k, v| ruportdata[type] << { "location" => v[:name], I18n.t("adv_report.units") => v[:units] } }
38
+ ruportdata[type].sort_rows_by!([I18n.t("adv_report.units")], :order => :descending)
39
+ ruportdata[type].rename_column("location", type.to_s.capitalize)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,78 @@
1
+ class Spree::AdvancedReport::IncrementReport < Spree::AdvancedReport
2
+ INCREMENTS = I18n.t("adv_report.increments").map(&:to_sym)
3
+ attr_accessor :increments, :dates, :total, :all_data
4
+
5
+ def initialize(params)
6
+ super(params)
7
+
8
+ self.increments = INCREMENTS
9
+ self.ruportdata = INCREMENTS.inject({}) { |h, inc| h[inc] = Table(%w[key display value]); h }
10
+ self.data = INCREMENTS.inject({}) { |h, inc| h[inc] = {}; h }
11
+
12
+ self.dates = {
13
+ I18n.t("adv_report.daily").downcase.to_sym => {
14
+ :date_bucket => "%F",
15
+ :date_display => "%m-%d-%Y",
16
+ :header_display => I18n.t("adv_report.daily"),
17
+ },
18
+ I18n.t("adv_report.weekly").downcase.to_sym => {
19
+ :header_display => I18n.t("adv_report.weekly")
20
+ },
21
+ I18n.t("adv_report.monthly").downcase.to_sym => {
22
+ :date_bucket => "%Y-%m",
23
+ :date_display => "%B %Y",
24
+ :header_display => I18n.t("adv_report.monthly"),
25
+ },
26
+ I18n.t("adv_report.quarterly").downcase.to_sym => {
27
+ :header_display => I18n.t("adv_report.quarterly")
28
+ },
29
+ I18n.t("adv_report.yearly").downcase.to_sym => {
30
+ :date_bucket => "%Y",
31
+ :date_display => "%Y",
32
+ :header_display => I18n.t("adv_report.yearly"),
33
+ }
34
+ }
35
+ end
36
+
37
+ def generate_ruport_data
38
+ self.all_data = Table(%w[increment key display value])
39
+ INCREMENTS.each do |inc|
40
+ data[inc].each { |k,v| ruportdata[inc] << { "key" => k, "display" => v[:display], "value" => v[:value] } }
41
+ ruportdata[inc].data.each do |p|
42
+ self.all_data << { "increment" => inc.to_s.capitalize, "key" => p.data["key"], "display" => p.data["display"], "value" => p.data["value"] }
43
+ end
44
+ Rails.logger.info "THE DATES #{self.dates} : #{inc}"
45
+ ruportdata[inc].sort_rows_by!(["key"])
46
+ ruportdata[inc].remove_column("key")
47
+ ruportdata[inc].rename_column("display", dates[inc][:header_display])
48
+ ruportdata[inc].rename_column("value", self.name)
49
+ end
50
+ self.all_data.sort_rows_by!(["key"])
51
+ self.all_data.remove_column("key")
52
+ self.all_data = Grouping(self.all_data, :by => "increment")
53
+ end
54
+
55
+ def get_bucket(type, completed_at)
56
+ if type == I18n.t("adv_report.weekly").downcase.to_sym
57
+ return completed_at.beginning_of_week.strftime("%Y-%m-%d")
58
+ elsif type == I18n.t("adv_report.quarterly").downcase.to_sym
59
+ return completed_at.beginning_of_quarter.strftime("%Y-%m")
60
+ end
61
+ completed_at.strftime(dates[type][:date_bucket])
62
+ end
63
+
64
+ def get_display(type, completed_at)
65
+ if type == I18n.t("adv_report.weekly").downcase.to_sym
66
+ #funny business
67
+ #next_week = completed_at + 7
68
+ return completed_at.beginning_of_week.strftime("%m-%d-%Y") # + ' - ' + next_week.beginning_of_week.strftime("%m-%d-%Y")
69
+ elsif type == I18n.t("adv_report.quarterly").downcase.to_sym
70
+ return completed_at.strftime("%Y") + ' Q' + (completed_at.beginning_of_quarter.strftime("%m").to_i/3 + 1).to_s
71
+ end
72
+ completed_at.strftime(dates[type][:date_display])
73
+ end
74
+
75
+ def format_total
76
+ self.total
77
+ end
78
+ end
@@ -0,0 +1,33 @@
1
+ class Spree::AdvancedReport::IncrementReport::Count < Spree::AdvancedReport::IncrementReport
2
+ def name
3
+ I18n.t("adv_report.increment_report.count.name")
4
+ end
5
+
6
+ def column
7
+ I18n.t("adv_report.increment_report.count.column")
8
+ end
9
+
10
+ def description
11
+ I18n.t("adv_report.increment_report.count.description")
12
+ end
13
+
14
+ def initialize(params)
15
+ super(params)
16
+ self.total = 0
17
+ self.orders.each do |order|
18
+ date = {}
19
+ INCREMENTS.each do |type|
20
+ date[type] = get_bucket(type, order.completed_at)
21
+ data[type][date[type]] ||= {
22
+ :value => 0,
23
+ :display => get_display(type, order.completed_at),
24
+ }
25
+ end
26
+ order_count = order_count(order)
27
+ INCREMENTS.each { |type| data[type][date[type]][:value] += order_count }
28
+ self.total += order_count
29
+ end
30
+
31
+ generate_ruport_data
32
+ end
33
+ end