solidus_admin_insights 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.
- checksums.yaml +7 -0
- data/.gitignore +54 -0
- data/Gemfile +2 -0
- data/LICENSE +26 -0
- data/README.md +71 -0
- data/Rakefile +21 -0
- data/app/assets/javascripts/spree/backend/jquery.tablesorter.min.js +4 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights.js +2 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights/paginator.js +128 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights/report_loader.js +265 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights/searcher.js +72 -0
- data/app/assets/javascripts/spree/backend/solidus_admin_insights/table_sorter.js +47 -0
- data/app/assets/javascripts/spree/backend/tmpl.js +87 -0
- data/app/assets/javascripts/spree/frontend/solidus_admin_insights.js +2 -0
- data/app/assets/stylesheets/spree/backend/override_pdf.css +71 -0
- data/app/assets/stylesheets/spree/backend/solidus_admin_insights.css +132 -0
- data/app/assets/stylesheets/spree/frontend/solidus_admin_insights.css +4 -0
- data/app/controllers/spree/admin/insights_controller.rb +103 -0
- data/app/helpers/spree/admin/base_helper_decorator.rb +17 -0
- data/app/models/spree/app_configuration_decorator.rb +3 -0
- data/app/models/spree/product_decorator.rb +3 -0
- data/app/models/spree/promotion_action_decorator.rb +3 -0
- data/app/models/spree/return_authorization_decorator.rb +4 -0
- data/app/permissions/spree/permission_sets/insight_display.rb +9 -0
- data/app/reports/spree/best_selling_products_report.rb +42 -0
- data/app/reports/spree/cart_additions_report.rb +36 -0
- data/app/reports/spree/cart_removals_report.rb +36 -0
- data/app/reports/spree/cart_updations_report.rb +40 -0
- data/app/reports/spree/payment_method_transactions_conversion_rate_report.rb +74 -0
- data/app/reports/spree/payment_method_transactions_conversion_rate_report/payment_method_state_distribution_chart.rb +39 -0
- data/app/reports/spree/payment_method_transactions_report.rb +60 -0
- data/app/reports/spree/payment_method_transactions_report/payment_method_revenue_distribution_chart.rb +36 -0
- data/app/reports/spree/product_views_report.rb +46 -0
- data/app/reports/spree/product_views_to_cart_additions_report.rb +53 -0
- data/app/reports/spree/product_views_to_purchases_report.rb +54 -0
- data/app/reports/spree/promotional_cost_report.rb +84 -0
- data/app/reports/spree/promotional_cost_report/promotional_cost_chart.rb +37 -0
- data/app/reports/spree/promotional_cost_report/usage_count_chart.rb +41 -0
- data/app/reports/spree/report.rb +131 -0
- data/app/reports/spree/report/chart.rb +11 -0
- data/app/reports/spree/report/configuration.rb +40 -0
- data/app/reports/spree/report/date_slicer.rb +61 -0
- data/app/reports/spree/report/observation.rb +49 -0
- data/app/reports/spree/report/query_fragments.rb +45 -0
- data/app/reports/spree/report/query_time_scale.rb +19 -0
- data/app/reports/spree/report/result.rb +100 -0
- data/app/reports/spree/report/timed_observation.rb +47 -0
- data/app/reports/spree/report/timed_result.rb +48 -0
- data/app/reports/spree/returned_products_report.rb +37 -0
- data/app/reports/spree/sales_performance_report.rb +107 -0
- data/app/reports/spree/sales_performance_report/profit_loss_chart.rb +37 -0
- data/app/reports/spree/sales_performance_report/profit_loss_percent_chart.rb +36 -0
- data/app/reports/spree/sales_performance_report/sale_cost_price_chart.rb +48 -0
- data/app/reports/spree/sales_tax_report.rb +64 -0
- data/app/reports/spree/sales_tax_report/monthly_sales_tax_comparison_chart.rb +39 -0
- data/app/reports/spree/shipping_cost_report.rb +89 -0
- data/app/reports/spree/shipping_cost_report/shipping_cost_distribution_chart.rb +38 -0
- data/app/reports/spree/trending_search_report.rb +50 -0
- data/app/reports/spree/trending_search_report/frequency_distribution_pie_chart.rb +41 -0
- data/app/reports/spree/unique_purchases_report.rb +39 -0
- data/app/reports/spree/user_pool_report.rb +66 -0
- data/app/reports/spree/user_pool_report/distribution_column_chart.rb +65 -0
- data/app/reports/spree/users_not_converted_report.rb +48 -0
- data/app/reports/spree/users_who_recently_purchased_report.rb +69 -0
- data/app/services/spree/report_generation_service.rb +27 -0
- data/app/views/spree/admin/insights/_chart.html.erb +4 -0
- data/app/views/spree/admin/insights/download.pdf.erb +27 -0
- data/app/views/spree/admin/insights/index.html.erb +82 -0
- data/app/views/spree/admin/insights/search/_product_views_search.html.erb +13 -0
- data/app/views/spree/admin/insights/search/_search_form.html.erb +39 -0
- data/app/views/spree/admin/insights/search/_trending_searches_search.html.erb +13 -0
- data/app/views/spree/admin/insights/search/_users_not_converted_search.html.erb +13 -0
- data/app/views/spree/admin/insights/search/_users_who_have_not_purchased_recently_search.html.erb +13 -0
- data/app/views/spree/admin/insights/search/_users_who_recently_purchased_search.html.erb +13 -0
- data/app/views/spree/admin/shared/_insights_side_menu.html.erb +5 -0
- data/app/views/spree/admin/shared/sub_menu/_insight.html.erb +7 -0
- data/app/views/spree/admin/templates/insights/_paginator.template +11 -0
- data/app/views/spree/admin/templates/insights/_search.template +76 -0
- data/app/views/spree/admin/templates/insights/_show.template +49 -0
- data/app/views/spree/layouts/pdf.html.erb +9 -0
- data/bin/rails +7 -0
- data/config/initializers/add_to_sidebar.rb +14 -0
- data/config/initializers/assets.rb +1 -0
- data/config/initializers/mime_types.rb +2 -0
- data/config/initializers/wicked_pdf.rb +21 -0
- data/config/locales/en.yml +167 -0
- data/config/routes.rb +6 -0
- data/lib/generators/solidus_admin_insights/install/install_generator.rb +36 -0
- data/lib/generators/solidus_admin_insights/install/solidus_admin_insights.rb +22 -0
- data/lib/solidus_admin_insights.rb +14 -0
- data/lib/solidus_admin_insights/engine.rb +27 -0
- data/lib/solidus_admin_insights/factories.rb +6 -0
- data/solidus_admin_insights.gemspec +42 -0
- data/spec/spec_helper.rb +93 -0
- metadata +419 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Spree::BaseHelper.class_eval do
|
|
2
|
+
def selected?(path)
|
|
3
|
+
path == 'spree/admin/insights'
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def form_action(insight, insight_type)
|
|
7
|
+
insight ? admin_insight_path(id: @report_name, type: insight_type) : 'javascript:void(0)'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def page_selector_options
|
|
11
|
+
[5, 10, 15, 30, 45, 60]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def pdf_logo(image_path = Spree::Config[:logo])
|
|
15
|
+
wicked_pdf_image_tag image_path, class: 'logo'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class BestSellingProductsReport < Spree::Report
|
|
3
|
+
DEFAULT_SORTABLE_ATTRIBUTE = :sold_count
|
|
4
|
+
HEADERS = { sku: :string, product_name: :string, sold_count: :integer }
|
|
5
|
+
SEARCH_ATTRIBUTES = { start_date: :orders_completed_from, end_date: :orders_completed_to }
|
|
6
|
+
SORTABLE_ATTRIBUTES = [:product_name, :sku, :sold_count]
|
|
7
|
+
|
|
8
|
+
deeplink product_name: { template: %Q{<a href="/admin/products/{%# o.product_slug %}" target="_blank">{%# o.product_name %}</a>} }
|
|
9
|
+
|
|
10
|
+
class Result < Spree::Report::Result
|
|
11
|
+
class Observation < Spree::Report::Observation
|
|
12
|
+
observation_fields [:product_name, :product_slug, :sku, :sold_count]
|
|
13
|
+
|
|
14
|
+
def sku
|
|
15
|
+
@sku.presence || @product_name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def report_query
|
|
21
|
+
Spree::LineItem
|
|
22
|
+
.joins(:order)
|
|
23
|
+
.joins(:variant)
|
|
24
|
+
.joins(:product)
|
|
25
|
+
.where(Spree::Product.arel_table[:name].matches(search_name))
|
|
26
|
+
.where(spree_orders: { state: 'complete' })
|
|
27
|
+
.where(spree_orders: { completed_at: reporting_period })
|
|
28
|
+
.group(:variant_id, :product_name, :product_slug, 'spree_variants.sku')
|
|
29
|
+
.select(
|
|
30
|
+
'spree_products.name as product_name',
|
|
31
|
+
'spree_products.slug as product_slug',
|
|
32
|
+
'spree_variants.sku as sku',
|
|
33
|
+
'sum(quantity) as sold_count'
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private def search_name
|
|
38
|
+
search[:name].present? ? "%#{ search[:name] }%" : '%'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class CartAdditionsReport < Spree::Report
|
|
3
|
+
DEFAULT_SORTABLE_ATTRIBUTE = :product_name
|
|
4
|
+
HEADERS = { sku: :string, product_name: :string, additions: :integer, quantity_change: :integer }
|
|
5
|
+
SEARCH_ATTRIBUTES = { start_date: :product_added_from, end_date: :product_added_to }
|
|
6
|
+
SORTABLE_ATTRIBUTES = [:product_name, :sku, :additions, :quantity_change]
|
|
7
|
+
|
|
8
|
+
deeplink product_name: { template: %Q{<a href="/admin/products/{%# o.product_slug %}" target="_blank">{%# o.product_name %}</a>} }
|
|
9
|
+
|
|
10
|
+
class Result < Spree::Report::Result
|
|
11
|
+
class Observation < Spree::Report::Observation
|
|
12
|
+
observation_fields [:product_name, :product_slug, :additions, :quantity_change, :sku]
|
|
13
|
+
|
|
14
|
+
def sku
|
|
15
|
+
@sku.presence || @product_name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def report_query
|
|
21
|
+
Spree::CartEvent
|
|
22
|
+
.added
|
|
23
|
+
.joins(:variant)
|
|
24
|
+
.joins(:product)
|
|
25
|
+
.where(created_at: reporting_period)
|
|
26
|
+
.group('product_name', 'product_slug', 'spree_variants.sku')
|
|
27
|
+
.select(
|
|
28
|
+
'spree_products.name as product_name',
|
|
29
|
+
'spree_products.slug as product_slug',
|
|
30
|
+
'spree_variants.sku as sku',
|
|
31
|
+
'count(spree_products.name) as additions',
|
|
32
|
+
'sum(spree_cart_events.quantity) as quantity_change'
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class CartRemovalsReport < Spree::Report
|
|
3
|
+
DEFAULT_SORTABLE_ATTRIBUTE = :product_name
|
|
4
|
+
HEADERS = { sku: :string, product_name: :string, removals: :integer, quantity_change: :integer }
|
|
5
|
+
SEARCH_ATTRIBUTES = { start_date: :product_removed_from, end_date: :product_removed_to }
|
|
6
|
+
SORTABLE_ATTRIBUTES = [:product_name, :sku, :removals, :quantity_change]
|
|
7
|
+
|
|
8
|
+
deeplink product_name: { template: %Q{<a href="/admin/products/{%# o.product_slug %}" target="_blank">{%# o.product_name %}</a>} }
|
|
9
|
+
|
|
10
|
+
class Result < Spree::Report::Result
|
|
11
|
+
class Observation < Spree::Report::Observation
|
|
12
|
+
observation_fields [:product_name, :product_slug, :removals, :quantity_change, :sku]
|
|
13
|
+
|
|
14
|
+
def sku
|
|
15
|
+
@sku.presence || @product_name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def report_query
|
|
21
|
+
Spree::CartEvent
|
|
22
|
+
.removed
|
|
23
|
+
.joins(:variant)
|
|
24
|
+
.joins(:product)
|
|
25
|
+
.where(created_at: reporting_period)
|
|
26
|
+
.group('product_name', 'product_slug', 'spree_variants.sku')
|
|
27
|
+
.select(
|
|
28
|
+
'spree_products.name as product_name',
|
|
29
|
+
'spree_products.slug as product_slug',
|
|
30
|
+
'spree_variants.sku as sku',
|
|
31
|
+
'count(spree_products.name) as removals',
|
|
32
|
+
'sum(spree_cart_events.quantity) as quantity_change'
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class CartUpdationsReport < Spree::Report
|
|
3
|
+
DEFAULT_SORTABLE_ATTRIBUTE = :product_name
|
|
4
|
+
HEADERS = { sku: :string, product_name: :string, updations: :integer, quantity_increase: :integer, quantity_decrease: :integer }
|
|
5
|
+
SEARCH_ATTRIBUTES = { start_date: :product_updated_from, end_date: :product_updated_to }
|
|
6
|
+
SORTABLE_ATTRIBUTES = [:product_name, :sku, :updations, :quantity_increase, :quantity_decrease]
|
|
7
|
+
|
|
8
|
+
deeplink product_name: { template: %Q{<a href="/admin/products/{%# o.product_slug %}" target="_blank">{%# o.product_name %}</a>} }
|
|
9
|
+
|
|
10
|
+
class Result < Spree::Report::Result
|
|
11
|
+
class Observation < Spree::Report::Observation
|
|
12
|
+
observation_fields [:product_name, :product_slug, :updations, :quantity_increase, :sku, :quantity_decrease]
|
|
13
|
+
|
|
14
|
+
def sku
|
|
15
|
+
@sku.presence || @product_name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def report_query
|
|
21
|
+
quantity_increase_sql = "CASE WHEN quantity > 0 then spree_cart_events.quantity ELSE 0 END"
|
|
22
|
+
quantity_decrease_sql = "CASE WHEN quantity < 0 then spree_cart_events.quantity ELSE 0 END"
|
|
23
|
+
|
|
24
|
+
Spree::CartEvent
|
|
25
|
+
.updated
|
|
26
|
+
.joins(:variant)
|
|
27
|
+
.joins(:product)
|
|
28
|
+
.where(created_at: reporting_period)
|
|
29
|
+
.group('product_name', 'product_slug', 'spree_variants.sku')
|
|
30
|
+
.select(
|
|
31
|
+
'spree_products.name as product_name',
|
|
32
|
+
'spree_products.slug as product_slug',
|
|
33
|
+
'spree_variants.sku as sku',
|
|
34
|
+
'count(spree_products.name) as updations',
|
|
35
|
+
"SUM(#{ quantity_increase_sql }) as quantity_increase",
|
|
36
|
+
"SUM(#{ quantity_decrease_sql }) as quantity_decrease"
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class PaymentMethodTransactionsConversionRateReport < Spree::Report
|
|
3
|
+
DEFAULT_SORTABLE_ATTRIBUTE = :payment_method_name
|
|
4
|
+
HEADERS = { payment_method_name: :string, payment_state: :string, months_name: :string, count: :integer }
|
|
5
|
+
SEARCH_ATTRIBUTES = { start_date: :payments_created_from, end_date: :payments_created_to }
|
|
6
|
+
SORTABLE_ATTRIBUTES = [:payment_method_name, :successful_payments_count, :failed_payments_count, :pending_payments_count, :invalid_payments_count]
|
|
7
|
+
|
|
8
|
+
class Result < Spree::Report::TimedResult
|
|
9
|
+
charts PaymentMethodStateDistributionChart
|
|
10
|
+
|
|
11
|
+
def build_empty_observations
|
|
12
|
+
super
|
|
13
|
+
@_payment_methods = @results.collect { |result| result['payment_method_name'] }.uniq
|
|
14
|
+
@observations = @_payment_methods.collect do |payment_method_name|
|
|
15
|
+
payment_states = @results
|
|
16
|
+
.select { |result| result['payment_method_name'] == payment_method_name }
|
|
17
|
+
.collect { |result| result['payment_state'] }
|
|
18
|
+
.uniq
|
|
19
|
+
|
|
20
|
+
payment_states.collect do |state|
|
|
21
|
+
@observations.collect do |observation|
|
|
22
|
+
_d_observation = observation.dup
|
|
23
|
+
_d_observation.payment_method_name = payment_method_name
|
|
24
|
+
_d_observation.payment_state = state
|
|
25
|
+
_d_observation.count = 0
|
|
26
|
+
_d_observation
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end.flatten
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Observation < Spree::Report::TimedObservation
|
|
33
|
+
observation_fields [:payment_method_name, :payment_state, :count]
|
|
34
|
+
|
|
35
|
+
def payment_state
|
|
36
|
+
if @payment_state == 'pending'
|
|
37
|
+
@payment_state
|
|
38
|
+
else
|
|
39
|
+
"capturing #{ @payment_state }"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def describes?(result, time_scale)
|
|
44
|
+
(result['payment_method_name'] == payment_method_name && result['payment_state'] == @payment_state) && super
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def report_query
|
|
50
|
+
Spree::Report::QueryFragments
|
|
51
|
+
.from_subquery(payment_methods)
|
|
52
|
+
.group(*time_scale_columns_to_s, 'payment_method_name', 'payment_state')
|
|
53
|
+
.order(*time_scale_columns)
|
|
54
|
+
.project(
|
|
55
|
+
*time_scale_columns,
|
|
56
|
+
'payment_method_name',
|
|
57
|
+
'payment_state',
|
|
58
|
+
'COUNT(payment_method_id) as count'
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private def payment_methods
|
|
63
|
+
Spree::PaymentMethod
|
|
64
|
+
.joins(:payments)
|
|
65
|
+
.where(spree_payments: { created_at: reporting_period })
|
|
66
|
+
.select(
|
|
67
|
+
'spree_payment_methods.id as payment_method_id',
|
|
68
|
+
'name as payment_method_name',
|
|
69
|
+
'state as payment_state',
|
|
70
|
+
*time_scale_selects('spree_payments')
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class Spree::PaymentMethodTransactionsConversionRateReport::PaymentMethodStateDistributionChart
|
|
2
|
+
attr_accessor :chart_data
|
|
3
|
+
|
|
4
|
+
def initialize(result)
|
|
5
|
+
@time_dimension = result.time_dimension
|
|
6
|
+
@grouped_by_payment_method = result.observations.group_by(&:payment_method_name)
|
|
7
|
+
@time_series = []
|
|
8
|
+
@time_series = @grouped_by_payment_method.values.first.collect { |observation| observation.send(@time_dimension) } if @grouped_by_payment_method.first.present?
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_h
|
|
12
|
+
@grouped_by_payment_method.collect do |method_name, observations|
|
|
13
|
+
{
|
|
14
|
+
id: 'payment-state-' + method_name,
|
|
15
|
+
json: {
|
|
16
|
+
chart: { type: 'column' },
|
|
17
|
+
title: {
|
|
18
|
+
useHTML: true,
|
|
19
|
+
text: %Q(<span class='chart-title'>#{ method_name } Conversion Status</span>
|
|
20
|
+
<span class='fa fa-question-circle' data-toggle='tooltip' title=' Tracks the status of Payments made from different payment methods such as CC, Check etc.'></span>)
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
xAxis: { categories: @time_series },
|
|
24
|
+
yAxis: {
|
|
25
|
+
title: { text: 'Count' }
|
|
26
|
+
},
|
|
27
|
+
tooltip: { valuePrefix: '#' },
|
|
28
|
+
legend: {
|
|
29
|
+
layout: 'vertical',
|
|
30
|
+
align: 'right',
|
|
31
|
+
verticalAlign: 'middle',
|
|
32
|
+
borderWidth: 0
|
|
33
|
+
},
|
|
34
|
+
series: observations.group_by(&:payment_state).map { |key, value| { name: key, data: value.map(&:count) } }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class PaymentMethodTransactionsReport < Spree::Report
|
|
3
|
+
DEFAULT_SORTABLE_ATTRIBUTE = :payment_method_name
|
|
4
|
+
HEADERS = { payment_method_name: :string, payment_amount: :integer }
|
|
5
|
+
SEARCH_ATTRIBUTES = { start_date: :payments_created_from, end_date: :payments_created_till }
|
|
6
|
+
SORTABLE_ATTRIBUTES = []
|
|
7
|
+
|
|
8
|
+
class Result < Spree::Report::TimedResult
|
|
9
|
+
charts PaymentMethodRevenueDistributionChart
|
|
10
|
+
|
|
11
|
+
def build_empty_observations
|
|
12
|
+
super
|
|
13
|
+
@_payment_methods = @results.collect { |result| result['payment_method_name'] }.uniq
|
|
14
|
+
@observations = @_payment_methods.collect do |payment_method_name|
|
|
15
|
+
@observations.collect do |observation|
|
|
16
|
+
_d_observation = observation.dup
|
|
17
|
+
_d_observation.payment_amount = 0
|
|
18
|
+
_d_observation.payment_method_name = payment_method_name
|
|
19
|
+
_d_observation
|
|
20
|
+
end
|
|
21
|
+
end.flatten
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Observation < Spree::Report::TimedObservation
|
|
25
|
+
observation_fields [:payment_method_name, :payment_amount]
|
|
26
|
+
|
|
27
|
+
def describes?(result, time_scale)
|
|
28
|
+
(result['payment_method_name'] == payment_method_name) && super
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def payment_amount
|
|
32
|
+
@payment_amount.to_f
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def report_query
|
|
38
|
+
Spree::Report::QueryFragments
|
|
39
|
+
.from_subquery(payments)
|
|
40
|
+
.group(*time_scale_columns_to_s, 'payment_method_name')
|
|
41
|
+
.order(*time_scale_columns)
|
|
42
|
+
.project(
|
|
43
|
+
*time_scale_columns,
|
|
44
|
+
'payment_method_name',
|
|
45
|
+
'SUM(payment_amount) as payment_amount'
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private def payments
|
|
50
|
+
Spree::PaymentMethod
|
|
51
|
+
.joins(:payments)
|
|
52
|
+
.where(spree_payments: { created_at: reporting_period })
|
|
53
|
+
.select(
|
|
54
|
+
*time_scale_selects('spree_payments'),
|
|
55
|
+
'spree_payment_methods.name as payment_method_name',
|
|
56
|
+
'spree_payments.amount as payment_amount',
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class Spree::PaymentMethodTransactionsReport::PaymentMethodRevenueDistributionChart
|
|
2
|
+
def initialize(result)
|
|
3
|
+
@time_dimension = result.time_dimension
|
|
4
|
+
@grouped_by_payment_method = result.observations.group_by(&:payment_method_name)
|
|
5
|
+
@time_series = []
|
|
6
|
+
if @grouped_by_payment_method.values.first.present?
|
|
7
|
+
@time_series = @grouped_by_payment_method.values.first.collect { |observation| observation.send(@time_dimension) }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_h
|
|
12
|
+
{
|
|
13
|
+
id: 'payment-methods',
|
|
14
|
+
json: {
|
|
15
|
+
chart: { type: 'column' },
|
|
16
|
+
title: {
|
|
17
|
+
useHTML: true,
|
|
18
|
+
text: "<span class='chart-title'>Payment Methods</span><span class='fa fa-question-circle' data-toggle='tooltip' title=' Compare the revenue generated by different Payment methods such as CC, Check etc.'></span>"
|
|
19
|
+
},
|
|
20
|
+
xAxis: { categories: @time_series },
|
|
21
|
+
yAxis: {
|
|
22
|
+
title: { text: 'value($)' }
|
|
23
|
+
},
|
|
24
|
+
tooltip: { valuePrefix: '$' },
|
|
25
|
+
legend: {
|
|
26
|
+
layout: 'vertical',
|
|
27
|
+
align: 'right',
|
|
28
|
+
verticalAlign: 'middle',
|
|
29
|
+
borderWidth: 0
|
|
30
|
+
},
|
|
31
|
+
series: @grouped_by_payment_method.collect { |key, value| { name: key, data: value.map(&:payment_amount) }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class ProductViewsReport < Spree::Report
|
|
3
|
+
DEFAULT_SORTABLE_ATTRIBUTE = :product_name
|
|
4
|
+
HEADERS = { product_name: :string, views: :integer, users: :integer, guest_sessions: :integer }
|
|
5
|
+
SEARCH_ATTRIBUTES = { start_date: :product_view_from, end_date: :product_view_till, name: :name}
|
|
6
|
+
SORTABLE_ATTRIBUTES = [:product_name, :views, :users, :guest_sessions]
|
|
7
|
+
|
|
8
|
+
deeplink product_name: { template: %Q{<a href="/admin/products/{%# o.product_slug %}" target="_blank">{%# o.product_name %}</a>} }
|
|
9
|
+
|
|
10
|
+
class Result < Spree::Report::Result
|
|
11
|
+
class Observation < Spree::Report::Observation
|
|
12
|
+
observation_fields [:product_name, :product_slug, :views, :users, :guest_sessions]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def report_query
|
|
17
|
+
viewed_events =
|
|
18
|
+
Spree::Product
|
|
19
|
+
.where(Spree::Product.arel_table[:name].matches(search_name))
|
|
20
|
+
.joins(:page_view_events)
|
|
21
|
+
.where(spree_page_events: { created_at: reporting_period })
|
|
22
|
+
.group('product_name', 'product_slug', 'spree_page_events.actor_id', 'spree_page_events.session_id')
|
|
23
|
+
.select(
|
|
24
|
+
'spree_products.name as product_name',
|
|
25
|
+
'spree_products.slug as product_slug',
|
|
26
|
+
'COUNT(*) as total_views_per_session',
|
|
27
|
+
'spree_page_events.session_id as session_id',
|
|
28
|
+
'spree_page_events.actor_id as actor_id'
|
|
29
|
+
)
|
|
30
|
+
Spree::Report::QueryFragments
|
|
31
|
+
.from_subquery(viewed_events)
|
|
32
|
+
.group('product_name', 'product_slug')
|
|
33
|
+
.project(
|
|
34
|
+
'product_name',
|
|
35
|
+
'product_slug',
|
|
36
|
+
'SUM(total_views_per_session) as views',
|
|
37
|
+
'COUNT(DISTINCT actor_id) as users',
|
|
38
|
+
'(COUNT(DISTINCT session_id) - COUNT(actor_id)) as guest_sessions'
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private def search_name
|
|
43
|
+
search[:name].present? ? "%#{ search[:name] }%" : '%'
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|