spree_advanced_reporting 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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