solidus_subscriptions-alpha 0.0.3 → 0.0.4

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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +26 -26
  3. data/README.md +130 -130
  4. data/Rakefile +28 -28
  5. data/app/assets/javascripts/spree/backend/solidus_subscriptions.js +1 -1
  6. data/app/assets/javascripts/spree/frontend/solidus_subscriptions.js +1 -1
  7. data/app/assets/stylesheets/spree/backend/solidus_subscriptions.css +4 -4
  8. data/app/assets/stylesheets/spree/frontend/solidus_subscriptions.css +4 -4
  9. data/app/controllers/solidus_subscriptions/api/v1/line_items_controller.rb +35 -35
  10. data/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb +44 -44
  11. data/app/controllers/spree/admin/subscriptions_controller.rb +66 -66
  12. data/app/decorators/spree/controllers/api/line_items/create_subscription_line_items.rb +28 -28
  13. data/app/decorators/spree/controllers/orders/create_subscription_line_items.rb +33 -33
  14. data/app/decorators/spree/line_items/subscription_line_items_association.rb +22 -22
  15. data/app/decorators/spree/orders/after_create.rb +15 -15
  16. data/app/decorators/spree/orders/finalize_creates_subscriptions.rb +19 -19
  17. data/app/decorators/spree/orders/subscription_line_items_association.rb +15 -15
  18. data/app/decorators/spree/products/subscribable_decorator.rb +11 -11
  19. data/app/decorators/spree/users/have_many_subscriptions.rb +18 -18
  20. data/app/decorators/spree/variant_pretty_name.rb +13 -13
  21. data/app/jobs/solidus_subscriptions/process_installments_job.rb +22 -22
  22. data/app/models/solidus_subscriptions/checkout.rb +141 -141
  23. data/app/models/solidus_subscriptions/dispatcher.rb +32 -32
  24. data/app/models/solidus_subscriptions/failure_dispatcher.rb +19 -19
  25. data/app/models/solidus_subscriptions/installment.rb +126 -126
  26. data/app/models/solidus_subscriptions/installment_detail.rb +23 -23
  27. data/app/models/solidus_subscriptions/interval.rb +24 -24
  28. data/app/models/solidus_subscriptions/line_item.rb +98 -98
  29. data/app/models/solidus_subscriptions/line_item_builder.rb +44 -44
  30. data/app/models/solidus_subscriptions/order_builder.rb +40 -40
  31. data/app/models/solidus_subscriptions/out_of_stock_dispatcher.rb +19 -19
  32. data/app/models/solidus_subscriptions/payment_failed_dispatcher.rb +23 -23
  33. data/app/models/solidus_subscriptions/subscription.rb +217 -217
  34. data/app/models/solidus_subscriptions/subscription_generator.rb +60 -60
  35. data/app/models/solidus_subscriptions/subscription_line_item_builder.rb +21 -21
  36. data/app/models/solidus_subscriptions/subscription_order_promotion_rule.rb +25 -25
  37. data/app/models/solidus_subscriptions/subscription_promotion_rule.rb +38 -38
  38. data/app/models/solidus_subscriptions/success_dispatcher.rb +16 -16
  39. data/app/models/solidus_subscriptions/unsubscribable_error.rb +17 -17
  40. data/app/models/solidus_subscriptions/user_mismatch_error.rb +15 -15
  41. data/app/overrides/views/admin_subscribable_checkbox.rb +13 -13
  42. data/app/overrides/views/admin_subscriptions_menu_link.rb +8 -8
  43. data/app/overrides/views/subscription_line_item_fields.rb +6 -6
  44. data/app/views/spree/admin/products/_subscribable_checkbox.html.erb +8 -8
  45. data/app/views/spree/admin/promotions/rules/_subscription_order_promotion_rule.html.erb +0 -0
  46. data/app/views/spree/admin/promotions/rules/_subscription_promotion_rule.html.erb +0 -0
  47. data/app/views/spree/admin/shared/_no_objects_found.html.erb +4 -4
  48. data/app/views/spree/admin/shared/_subscription_tab.html.erb +3 -3
  49. data/app/views/spree/admin/solidus_subscriptions/subscriptions/_subscription.html.erb +66 -66
  50. data/app/views/spree/admin/subscriptions/_form.html.erb +81 -81
  51. data/app/views/spree/admin/subscriptions/_legacy_form.html.erb +81 -81
  52. data/app/views/spree/admin/subscriptions/_legacy_sidebar.html.erb +28 -28
  53. data/app/views/spree/admin/subscriptions/edit.html.erb +21 -21
  54. data/app/views/spree/admin/subscriptions/index.html.erb +119 -119
  55. data/app/views/spree/admin/subscriptions/new.html.erb +9 -9
  56. data/app/views/spree/admin/variants/_subscribable_checkbox.html.erb +6 -6
  57. data/app/views/spree/frontend/products/_subscription_line_item_fields.html.erb +30 -30
  58. data/config/locales/en.yml +91 -91
  59. data/config/routes.rb +25 -25
  60. data/db/migrate/20160825164850_create_solidus_subscriptions_subscriptions.rb +11 -11
  61. data/db/migrate/20160825173548_create_solidus_subscriptions_line_items.rb +17 -17
  62. data/db/migrate/20160825202248_create_solidus_subscriptions_installments.rb +23 -23
  63. data/db/migrate/20160825211202_create_solidus_subscriptions_installment_details.rb +22 -22
  64. data/db/migrate/20160825214240_add_subscribable_to_spree_variants.rb +5 -5
  65. data/db/migrate/20160829201653_change_subscription_line_items_installments_to_max_installments.rb +5 -5
  66. data/db/migrate/20160902220242_remove_state_from_solidus_susbscriptions_installment_details.rb +5 -5
  67. data/db/migrate/20160902220604_add_successful_to_solidus_subscriptions_installment_details.rb +5 -5
  68. data/db/migrate/20160902221218_add_message_to_solidus_subscriptions_installment_details.rb +5 -5
  69. data/db/migrate/20160922164101_add_interval_length_and_units_to_subscription_line_items.rb +8 -8
  70. data/db/migrate/20161006191003_add_skip_count_to_solidus_subscriptions_subscriptions.rb +5 -5
  71. data/db/migrate/20161006191127_add_successive_skip_count_to_solidus_subscriptions_subscriptions.rb +5 -5
  72. data/db/migrate/20161014212649_allow_spree_line_item_id_to_be_null.rb +5 -5
  73. data/db/migrate/20161017155749_add_order_id_to_solidus_subscriptions_installment_details.rb +6 -6
  74. data/db/migrate/20161017175509_remove_order_id_from_solidus_subscriptions_installments.rb +6 -6
  75. data/db/migrate/20161017201944_add_subscription_order_to_spree_orders.rb +5 -5
  76. data/db/migrate/20161221155142_add_store_to_solidus_subscriptions_subscriptions.rb +6 -6
  77. data/db/migrate/20161223152905_add_address_id_to_solidus_subscriptions_subscriptions.rb +7 -7
  78. data/db/migrate/20170106224713_change_line_item_max_installments_to_end_date.rb +6 -6
  79. data/db/migrate/20170111224458_change_subscription_actionable_date_to_datetime.rb +5 -5
  80. data/db/migrate/20170111232801_change_inteval_actionable_date_to_datetime.rb +5 -5
  81. data/db/migrate/20170112012407_add_config_options_to_subscriptions.rb +7 -7
  82. data/lib/generators/solidus_subscriptions/install/install_generator.rb +30 -30
  83. data/lib/solidus_subscriptions.rb +6 -6
  84. data/lib/solidus_subscriptions/ability.rb +19 -19
  85. data/lib/solidus_subscriptions/config.rb +97 -97
  86. data/lib/solidus_subscriptions/engine.rb +56 -56
  87. data/lib/solidus_subscriptions/permitted_attributes.rb +36 -36
  88. data/lib/solidus_subscriptions/processor.rb +108 -108
  89. data/lib/solidus_subscriptions/testing_support/factories.rb +5 -5
  90. data/lib/solidus_subscriptions/testing_support/factories/installment_detail_factory.rb +7 -7
  91. data/lib/solidus_subscriptions/testing_support/factories/installment_factory.rb +21 -21
  92. data/lib/solidus_subscriptions/testing_support/factories/line_item_factory.rb +18 -18
  93. data/lib/solidus_subscriptions/testing_support/factories/spree/line_item_factory.rb +17 -17
  94. data/lib/solidus_subscriptions/testing_support/factories/spree/order_factory.rb +18 -18
  95. data/lib/solidus_subscriptions/testing_support/factories/spree_modification_factory.rb +8 -8
  96. data/lib/solidus_subscriptions/testing_support/factories/subscription_factory.rb +43 -43
  97. data/lib/solidus_subscriptions/version.rb +3 -3
  98. data/lib/tasks/process_subscriptions.rake +6 -6
  99. metadata +3 -3
@@ -1,32 +1,32 @@
1
- module SolidusSubscriptions
2
- class Dispatcher
3
- attr_reader :installments, :order
4
-
5
- # Get a new instance of the FailureDispatcher
6
- #
7
- # @param installments [Array<SolidusSubscriptions::Installment>] The
8
- # installments which have failed to be fulfilled
9
- #
10
- # @return [SolidusSubscriptions::FailureDispatcher]
11
- def initialize(installments, order = nil)
12
- @installments = installments
13
- @order = order
14
- end
15
-
16
- def dispatch
17
- notify
18
- end
19
-
20
- private
21
-
22
- def notify
23
- Rails.logger.tagged('Event') do
24
- Rails.logger.info message.squish.tr("\n", ' ')
25
- end
26
- end
27
-
28
- def message
29
- raise 'A message should be set in subclasses of Dispatcher'
30
- end
31
- end
32
- end
1
+ module SolidusSubscriptions
2
+ class Dispatcher
3
+ attr_reader :installments, :order
4
+
5
+ # Get a new instance of the FailureDispatcher
6
+ #
7
+ # @param installments [Array<SolidusSubscriptions::Installment>] The
8
+ # installments which have failed to be fulfilled
9
+ #
10
+ # @return [SolidusSubscriptions::FailureDispatcher]
11
+ def initialize(installments, order = nil)
12
+ @installments = installments
13
+ @order = order
14
+ end
15
+
16
+ def dispatch
17
+ notify
18
+ end
19
+
20
+ private
21
+
22
+ def notify
23
+ Rails.logger.tagged('Event') do
24
+ Rails.logger.info message.squish.tr("\n", ' ')
25
+ end
26
+ end
27
+
28
+ def message
29
+ raise 'A message should be set in subclasses of Dispatcher'
30
+ end
31
+ end
32
+ end
@@ -1,19 +1,19 @@
1
- # A handler for behaviour that should happen after installments are marked as
2
- # failures
3
- module SolidusSubscriptions
4
- class FailureDispatcher < Dispatcher
5
- def dispatch
6
- order.touch :completed_at
7
- order.cancel!
8
- installments.each { |i| i.failed!(order) }
9
- super
10
- end
11
-
12
- def message
13
- "
14
- Something went wrong processing installments: #{installments.map(&:id).join(', ')}.
15
- They have been marked for reprocessing.
16
- "
17
- end
18
- end
19
- end
1
+ # A handler for behaviour that should happen after installments are marked as
2
+ # failures
3
+ module SolidusSubscriptions
4
+ class FailureDispatcher < Dispatcher
5
+ def dispatch
6
+ order.touch :completed_at
7
+ order.cancel!
8
+ installments.each { |i| i.failed!(order) }
9
+ super
10
+ end
11
+
12
+ def message
13
+ "
14
+ Something went wrong processing installments: #{installments.map(&:id).join(', ')}.
15
+ They have been marked for reprocessing.
16
+ "
17
+ end
18
+ end
19
+ end
@@ -1,126 +1,126 @@
1
- # This class represents a single iteration of a subscription. It is fulfulled
2
- # by a conmpleted order and maintains an association which tracks all attempts
3
- # successful or othewise at fulfulling this installment
4
- module SolidusSubscriptions
5
- class Installment < ActiveRecord::Base
6
- has_many :details, class_name: 'SolidusSubscriptions::InstallmentDetail'
7
- belongs_to(
8
- :subscription,
9
- class_name: 'SolidusSubscriptions::Subscription',
10
- inverse_of: :installments
11
- )
12
-
13
- validates :subscription, presence: true
14
-
15
- scope :fulfilled, (lambda do
16
- joins(:details).where(InstallmentDetail.table_name => { success: true }).distinct
17
- end)
18
-
19
- scope :unfulfilled, (lambda do
20
- fulfilled_ids = fulfilled.pluck(:id)
21
- where.not(id: fulfilled_ids).distinct
22
- end)
23
-
24
- scope :actionable, (lambda do
25
- unfulfilled.where("#{table_name}.actionable_date <= ?", Time.zone.now)
26
- end)
27
-
28
- # Get the builder for the subscription_line_item. This will be an
29
- # object that can generate the appropriate line item for the subscribable
30
- # object
31
- #
32
- # @return [SolidusSubscriptions::LineItemBuilder]
33
- def line_item_builder
34
- subscription.line_item_builder
35
- end
36
-
37
- # Mark this installment as out of stock.
38
- #
39
- # @return [SolidusSubscriptions::InstallmentDetail] The record of the failed
40
- # processing attempt
41
- def out_of_stock
42
- advance_actionable_date!
43
-
44
- details.create!(
45
- success: false,
46
- message: I18n.t('solidus_subscriptions.installment_details.out_of_stock')
47
- )
48
- end
49
-
50
- # Mark this installment as a success
51
- #
52
- # @param order [Spree::Order] The order generated for this processing
53
- # attempt
54
- #
55
- # @return [SolidusSubscriptions::InstallmentDetail] The record of the
56
- # successful processing attempt
57
- def success!(order)
58
- update!(actionable_date: nil)
59
-
60
- details.create!(
61
- success: true,
62
- order: order,
63
- message: I18n.t('solidus_subscriptions.installment_details.success')
64
- )
65
- end
66
-
67
- # Mark this installment as a failure
68
- #
69
- # @param order [Spree::Order] The order generated for this processing
70
- # attempt
71
- #
72
- # @return [SolidusSubscriptions::InstallmentDetail] The record of the
73
- # failed processing attempt
74
- def failed!(order)
75
- advance_actionable_date!
76
-
77
- details.create!(
78
- success: false,
79
- order: order,
80
- message: I18n.t('solidus_subscriptions.installment_details.failed')
81
- )
82
- end
83
-
84
- # Does this installment still need to be fulfilled by a completed order
85
- #
86
- # @return [Boolean]
87
- def unfulfilled?
88
- !fulfilled?
89
- end
90
-
91
- # Had this installment been fulfilled by a completed order
92
- #
93
- # @return [Boolean]
94
- def fulfilled?
95
- details.where(success: true).exists?
96
- end
97
-
98
- # Mark this installment as having a failed payment
99
- #
100
- # @param order [Spree::Order] The order generated for this processing
101
- # attempt
102
- #
103
- # @return [SolidusSubscriptions::InstallmentDetail] The record of the
104
- # failed processing attempt
105
- def payment_failed!(order)
106
- advance_actionable_date!
107
-
108
- details.create!(
109
- success: false,
110
- order: order,
111
- message: I18n.t('solidus_subscriptions.installment_details.payment_failed')
112
- )
113
- end
114
-
115
- private
116
-
117
- def advance_actionable_date!
118
- update!(actionable_date: next_actionable_date)
119
- end
120
-
121
- def next_actionable_date
122
- return if Config.reprocessing_interval.nil?
123
- (DateTime.current + Config.reprocessing_interval).beginning_of_minute
124
- end
125
- end
126
- end
1
+ # This class represents a single iteration of a subscription. It is fulfulled
2
+ # by a conmpleted order and maintains an association which tracks all attempts
3
+ # successful or othewise at fulfulling this installment
4
+ module SolidusSubscriptions
5
+ class Installment < ActiveRecord::Base
6
+ has_many :details, class_name: 'SolidusSubscriptions::InstallmentDetail'
7
+ belongs_to(
8
+ :subscription,
9
+ class_name: 'SolidusSubscriptions::Subscription',
10
+ inverse_of: :installments
11
+ )
12
+
13
+ validates :subscription, presence: true
14
+
15
+ scope :fulfilled, (lambda do
16
+ joins(:details).where(InstallmentDetail.table_name => { success: true }).distinct
17
+ end)
18
+
19
+ scope :unfulfilled, (lambda do
20
+ fulfilled_ids = fulfilled.pluck(:id)
21
+ where.not(id: fulfilled_ids).distinct
22
+ end)
23
+
24
+ scope :actionable, (lambda do
25
+ unfulfilled.where("#{table_name}.actionable_date <= ?", Time.zone.now)
26
+ end)
27
+
28
+ # Get the builder for the subscription_line_item. This will be an
29
+ # object that can generate the appropriate line item for the subscribable
30
+ # object
31
+ #
32
+ # @return [SolidusSubscriptions::LineItemBuilder]
33
+ def line_item_builder
34
+ subscription.line_item_builder
35
+ end
36
+
37
+ # Mark this installment as out of stock.
38
+ #
39
+ # @return [SolidusSubscriptions::InstallmentDetail] The record of the failed
40
+ # processing attempt
41
+ def out_of_stock
42
+ advance_actionable_date!
43
+
44
+ details.create!(
45
+ success: false,
46
+ message: I18n.t('solidus_subscriptions.installment_details.out_of_stock')
47
+ )
48
+ end
49
+
50
+ # Mark this installment as a success
51
+ #
52
+ # @param order [Spree::Order] The order generated for this processing
53
+ # attempt
54
+ #
55
+ # @return [SolidusSubscriptions::InstallmentDetail] The record of the
56
+ # successful processing attempt
57
+ def success!(order)
58
+ update!(actionable_date: nil)
59
+
60
+ details.create!(
61
+ success: true,
62
+ order: order,
63
+ message: I18n.t('solidus_subscriptions.installment_details.success')
64
+ )
65
+ end
66
+
67
+ # Mark this installment as a failure
68
+ #
69
+ # @param order [Spree::Order] The order generated for this processing
70
+ # attempt
71
+ #
72
+ # @return [SolidusSubscriptions::InstallmentDetail] The record of the
73
+ # failed processing attempt
74
+ def failed!(order)
75
+ advance_actionable_date!
76
+
77
+ details.create!(
78
+ success: false,
79
+ order: order,
80
+ message: I18n.t('solidus_subscriptions.installment_details.failed')
81
+ )
82
+ end
83
+
84
+ # Does this installment still need to be fulfilled by a completed order
85
+ #
86
+ # @return [Boolean]
87
+ def unfulfilled?
88
+ !fulfilled?
89
+ end
90
+
91
+ # Had this installment been fulfilled by a completed order
92
+ #
93
+ # @return [Boolean]
94
+ def fulfilled?
95
+ details.where(success: true).exists?
96
+ end
97
+
98
+ # Mark this installment as having a failed payment
99
+ #
100
+ # @param order [Spree::Order] The order generated for this processing
101
+ # attempt
102
+ #
103
+ # @return [SolidusSubscriptions::InstallmentDetail] The record of the
104
+ # failed processing attempt
105
+ def payment_failed!(order)
106
+ advance_actionable_date!
107
+
108
+ details.create!(
109
+ success: false,
110
+ order: order,
111
+ message: I18n.t('solidus_subscriptions.installment_details.payment_failed')
112
+ )
113
+ end
114
+
115
+ private
116
+
117
+ def advance_actionable_date!
118
+ update!(actionable_date: next_actionable_date)
119
+ end
120
+
121
+ def next_actionable_date
122
+ return if Config.reprocessing_interval.nil?
123
+ (DateTime.current + Config.reprocessing_interval).beginning_of_minute
124
+ end
125
+ end
126
+ end
@@ -1,23 +1,23 @@
1
- # This class represents a single attempt to fulfill an installment. It will
2
- # indicate the result of that attept.
3
- module SolidusSubscriptions
4
- class InstallmentDetail < ActiveRecord::Base
5
- belongs_to(
6
- :installment,
7
- class_name: 'SolidusSubscriptions::Installment',
8
- inverse_of: :details
9
- )
10
-
11
- belongs_to(:order, class_name: 'Spree::Order')
12
-
13
- validates :installment, presence: true
14
- alias_attribute :successful, :success
15
-
16
- # Was the attempt at fulfilling this installment a failure?
17
- #
18
- # @return [Boolean]
19
- def failed?
20
- !success
21
- end
22
- end
23
- end
1
+ # This class represents a single attempt to fulfill an installment. It will
2
+ # indicate the result of that attept.
3
+ module SolidusSubscriptions
4
+ class InstallmentDetail < ActiveRecord::Base
5
+ belongs_to(
6
+ :installment,
7
+ class_name: 'SolidusSubscriptions::Installment',
8
+ inverse_of: :details
9
+ )
10
+
11
+ belongs_to(:order, class_name: 'Spree::Order')
12
+
13
+ validates :installment, presence: true
14
+ alias_attribute :successful, :success
15
+
16
+ # Was the attempt at fulfilling this installment a failure?
17
+ #
18
+ # @return [Boolean]
19
+ def failed?
20
+ !success
21
+ end
22
+ end
23
+ end
@@ -1,24 +1,24 @@
1
- # This module is intended to be included into any active record
2
- # modle which needs to be aware of how intervals and stored and
3
- # calculated in the db.
4
- #
5
- # Base models must have the following fields: interval_length (integer) and interval_units (integer)
6
- module SolidusSubscriptions
7
- module Interval
8
- def self.included(base)
9
- base.enum interval_units: {
10
- day: 0,
11
- week: 1,
12
- month: 2,
13
- year: 3
14
- }
15
- end
16
-
17
- # Calculates the number of seconds in the interval.
18
- #
19
- # @return [Integer] The number of seconds.
20
- def interval
21
- ActiveSupport::Duration.new(interval_length, { interval_units.pluralize.to_sym => interval_length })
22
- end
23
- end
24
- end
1
+ # This module is intended to be included into any active record
2
+ # modle which needs to be aware of how intervals and stored and
3
+ # calculated in the db.
4
+ #
5
+ # Base models must have the following fields: interval_length (integer) and interval_units (integer)
6
+ module SolidusSubscriptions
7
+ module Interval
8
+ def self.included(base)
9
+ base.enum interval_units: {
10
+ day: 0,
11
+ week: 1,
12
+ month: 2,
13
+ year: 3
14
+ }
15
+ end
16
+
17
+ # Calculates the number of seconds in the interval.
18
+ #
19
+ # @return [Integer] The number of seconds.
20
+ def interval
21
+ ActiveSupport::Duration.new(interval_length, { interval_units.pluralize.to_sym => interval_length })
22
+ end
23
+ end
24
+ end