solidus_subscriptions-alpha 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) 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/solidus_subscriptions/version.rb~ +3 -0
  99. data/lib/tasks/process_subscriptions.rake +6 -6
  100. metadata +4 -3
@@ -1,15 +1,15 @@
1
- # Spree::Orders may contain many subscription_line_items. When the order is
2
- # finalized these subscription_line_items are converted into subscritpions.
3
- # The order needs to be able to get a list of associated subscription_line_items
4
- # to be able to populate the full subscriptions.
5
- module Spree
6
- module Orders
7
- module SubscriptionLineItemsAssociation
8
- def self.prepended(base)
9
- base.has_many :subscription_line_items, through: :line_items
10
- end
11
- end
12
- end
13
- end
14
-
15
- Spree::Order.prepend Spree::Orders::SubscriptionLineItemsAssociation
1
+ # Spree::Orders may contain many subscription_line_items. When the order is
2
+ # finalized these subscription_line_items are converted into subscritpions.
3
+ # The order needs to be able to get a list of associated subscription_line_items
4
+ # to be able to populate the full subscriptions.
5
+ module Spree
6
+ module Orders
7
+ module SubscriptionLineItemsAssociation
8
+ def self.prepended(base)
9
+ base.has_many :subscription_line_items, through: :line_items
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Spree::Order.prepend Spree::Orders::SubscriptionLineItemsAssociation
@@ -1,11 +1,11 @@
1
- module Spree
2
- module Products
3
- module Subscribable
4
- def self.prepended(klass)
5
- klass.delegate :"subscribable", :"subscribable=", to: :find_or_build_master
6
- end
7
- end
8
- end
9
- end
10
-
11
- Spree::Product.prepend Spree::Products::Subscribable
1
+ module Spree
2
+ module Products
3
+ module Subscribable
4
+ def self.prepended(klass)
5
+ klass.delegate :"subscribable", :"subscribable=", to: :find_or_build_master
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ Spree::Product.prepend Spree::Products::Subscribable
@@ -1,18 +1,18 @@
1
- # Spree::Users maintain a list of the subscriptions associated with them
2
- module Spree
3
- module Users
4
- module HaveManySubscritptions
5
- def self.prepended(base)
6
- base.has_many(
7
- :subscriptions,
8
- class_name: 'SolidusSubscriptions::Subscription',
9
- foreign_key: 'user_id'
10
- )
11
-
12
- base.accepts_nested_attributes_for :subscriptions
13
- end
14
- end
15
- end
16
- end
17
-
18
- Spree.user_class.prepend(Spree::Users::HaveManySubscritptions)
1
+ # Spree::Users maintain a list of the subscriptions associated with them
2
+ module Spree
3
+ module Users
4
+ module HaveManySubscritptions
5
+ def self.prepended(base)
6
+ base.has_many(
7
+ :subscriptions,
8
+ class_name: 'SolidusSubscriptions::Subscription',
9
+ foreign_key: 'user_id'
10
+ )
11
+
12
+ base.accepts_nested_attributes_for :subscriptions
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ Spree.user_class.prepend(Spree::Users::HaveManySubscritptions)
@@ -1,13 +1,13 @@
1
- module Spree
2
- module Variants
3
- module VariantPrettyName
4
- def pretty_name
5
- name = product.name
6
- name += " - #{options_text}" if options_text.present?
7
- name
8
- end
9
- end
10
- end
11
- end
12
-
13
- Spree::Variant.prepend Spree::Variants::VariantPrettyName
1
+ module Spree
2
+ module Variants
3
+ module VariantPrettyName
4
+ def pretty_name
5
+ name = product.name
6
+ name += " - #{options_text}" if options_text.present?
7
+ name
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ Spree::Variant.prepend Spree::Variants::VariantPrettyName
@@ -1,22 +1,22 @@
1
- # This job is responsible for creating a consolidated installment from a
2
- # list of installments and processing it.
3
-
4
- module SolidusSubscriptions
5
- class ProcessInstallmentsJob < ActiveJob::Base
6
- queue_as Config.processing_queue
7
-
8
- # Process a collection of installments
9
- #
10
- # @param installment_ids [Array<Integer>] The ids of the
11
- # installments to be processed together and fulfilled by the same order
12
- #
13
- # @return [Spree::Order] The order which fulfills the list of installments
14
- def perform(installment_ids)
15
- return if installment_ids.empty?
16
-
17
- installments = SolidusSubscriptions::Installment.where(id: installment_ids).
18
- includes(subscription: [:line_items, :user])
19
- Checkout.new(installments).process
20
- end
21
- end
22
- end
1
+ # This job is responsible for creating a consolidated installment from a
2
+ # list of installments and processing it.
3
+
4
+ module SolidusSubscriptions
5
+ class ProcessInstallmentsJob < ActiveJob::Base
6
+ queue_as Config.processing_queue
7
+
8
+ # Process a collection of installments
9
+ #
10
+ # @param installment_ids [Array<Integer>] The ids of the
11
+ # installments to be processed together and fulfilled by the same order
12
+ #
13
+ # @return [Spree::Order] The order which fulfills the list of installments
14
+ def perform(installment_ids)
15
+ return if installment_ids.empty?
16
+
17
+ installments = SolidusSubscriptions::Installment.where(id: installment_ids).
18
+ includes(subscription: [:line_items, :user])
19
+ Checkout.new(installments).process
20
+ end
21
+ end
22
+ end
@@ -1,141 +1,141 @@
1
- # This class takes a collection of installments and populates a new spree
2
- # order with the correct contents based on the subscriptions associated to the
3
- # intallments. This is to group together subscriptions being
4
- # processed on the same day for a specific user
5
- module SolidusSubscriptions
6
- class Checkout
7
- # @return [Array<Installment>] The collection of installments to be used
8
- # when generating a new order
9
- attr_reader :installments
10
-
11
- delegate :user, to: :subscription
12
-
13
- # Get a new instance of a Checkout
14
- #
15
- # @param installments [Array<Installment>] The collection of installments
16
- # to be used when generating a new order
17
- def initialize(installments)
18
- @installments = installments
19
- raise UserMismatchError.new(installments) if different_owners?
20
- end
21
-
22
- # Generate a new Spree::Order based on the information associated to the
23
- # installments
24
- #
25
- # @return [Spree::Order]
26
- def process
27
- populate
28
-
29
- # Installments are removed and set for future processing if they are
30
- # out of stock. If there are no line items left there is nothing to do
31
- return if installments.empty?
32
-
33
- if checkout
34
- Config.success_dispatcher_class.new(installments, order).dispatch
35
- return order
36
- end
37
-
38
- # A new order will only have 1 payment that we created
39
- if order.payments.any?(&:failed?)
40
- Config.payment_failed_dispatcher_class.new(installments, order).dispatch
41
- installments.clear
42
- nil
43
- end
44
- ensure
45
- # Any installments that failed to be processed will be reprocessed
46
- unfulfilled_installments = installments.select(&:unfulfilled?)
47
- if unfulfilled_installments.any?
48
- Config.failure_dispatcher_class.
49
- new(unfulfilled_installments, order).dispatch
50
- end
51
- end
52
-
53
- # The order fulfilling the consolidated installment
54
- #
55
- # @return [Spree::Order]
56
- def order
57
- @order ||= Spree::Order.create(
58
- user: user,
59
- email: user.email,
60
- store: subscription.store || Spree::Store.default,
61
- subscription_order: true
62
- )
63
- end
64
-
65
- private
66
-
67
- def checkout
68
- order.update!
69
- apply_promotions
70
-
71
- order.checkout_steps.count.times do
72
- order.ship_address = ship_address if order.state == "address"
73
- create_payment if order.state == "payment"
74
- order.next! if order.next_transition.present?
75
- end
76
-
77
- # Do this as a separate "quiet" transition so that it returns true or
78
- # false rather than raising a failed transition error
79
- order.complete
80
- end
81
-
82
- def populate
83
- unfulfilled_installments = []
84
-
85
- order_line_items = installments.flat_map do |installment|
86
- line_items = installment.line_item_builder.spree_line_items
87
-
88
- unfulfilled_installments.push(installment) if line_items.empty?
89
-
90
- line_items
91
- end
92
-
93
- # Remove installments which had no stock from the active list
94
- # They will be reprocessed later
95
- @installments -= unfulfilled_installments
96
- if unfulfilled_installments.any?
97
- Config.out_of_stock_dispatcher_class.new(unfulfilled_installments).dispatch
98
- end
99
-
100
- return if installments.empty?
101
- order_builder.add_line_items(order_line_items)
102
- end
103
-
104
- def order_builder
105
- @order_builder ||= OrderBuilder.new(order)
106
- end
107
-
108
- def subscription
109
- installments.first.subscription
110
- end
111
-
112
- def ship_address
113
- subscription.shipping_address || user.ship_address
114
- end
115
-
116
- def active_card
117
- if SolidusSupport.solidus_gem_version < Gem::Version.new("2.2.0")
118
- user.credit_cards.default.last
119
- else
120
- user.wallet.default_wallet_payment_source.payment_source
121
- end
122
- end
123
-
124
- def create_payment
125
- order.payments.create(
126
- source: active_card,
127
- amount: order.total,
128
- payment_method: Config.default_gateway
129
- )
130
- end
131
-
132
- def apply_promotions
133
- Spree::PromotionHandler::Cart.new(order).activate
134
- order.updater.update # reload totals
135
- end
136
-
137
- def different_owners?
138
- installments.map { |i| i.subscription.user }.uniq.length > 1
139
- end
140
- end
141
- end
1
+ # This class takes a collection of installments and populates a new spree
2
+ # order with the correct contents based on the subscriptions associated to the
3
+ # intallments. This is to group together subscriptions being
4
+ # processed on the same day for a specific user
5
+ module SolidusSubscriptions
6
+ class Checkout
7
+ # @return [Array<Installment>] The collection of installments to be used
8
+ # when generating a new order
9
+ attr_reader :installments
10
+
11
+ delegate :user, to: :subscription
12
+
13
+ # Get a new instance of a Checkout
14
+ #
15
+ # @param installments [Array<Installment>] The collection of installments
16
+ # to be used when generating a new order
17
+ def initialize(installments)
18
+ @installments = installments
19
+ raise UserMismatchError.new(installments) if different_owners?
20
+ end
21
+
22
+ # Generate a new Spree::Order based on the information associated to the
23
+ # installments
24
+ #
25
+ # @return [Spree::Order]
26
+ def process
27
+ populate
28
+
29
+ # Installments are removed and set for future processing if they are
30
+ # out of stock. If there are no line items left there is nothing to do
31
+ return if installments.empty?
32
+
33
+ if checkout
34
+ Config.success_dispatcher_class.new(installments, order).dispatch
35
+ return order
36
+ end
37
+
38
+ # A new order will only have 1 payment that we created
39
+ if order.payments.any?(&:failed?)
40
+ Config.payment_failed_dispatcher_class.new(installments, order).dispatch
41
+ installments.clear
42
+ nil
43
+ end
44
+ ensure
45
+ # Any installments that failed to be processed will be reprocessed
46
+ unfulfilled_installments = installments.select(&:unfulfilled?)
47
+ if unfulfilled_installments.any?
48
+ Config.failure_dispatcher_class.
49
+ new(unfulfilled_installments, order).dispatch
50
+ end
51
+ end
52
+
53
+ # The order fulfilling the consolidated installment
54
+ #
55
+ # @return [Spree::Order]
56
+ def order
57
+ @order ||= Spree::Order.create(
58
+ user: user,
59
+ email: user.email,
60
+ store: subscription.store || Spree::Store.default,
61
+ subscription_order: true
62
+ )
63
+ end
64
+
65
+ private
66
+
67
+ def checkout
68
+ order.update!
69
+ apply_promotions
70
+
71
+ order.checkout_steps.count.times do
72
+ order.ship_address = ship_address if order.state == "address"
73
+ create_payment if order.state == "payment"
74
+ order.next! if order.next_transition.present?
75
+ end
76
+
77
+ # Do this as a separate "quiet" transition so that it returns true or
78
+ # false rather than raising a failed transition error
79
+ order.complete
80
+ end
81
+
82
+ def populate
83
+ unfulfilled_installments = []
84
+
85
+ order_line_items = installments.flat_map do |installment|
86
+ line_items = installment.line_item_builder.spree_line_items
87
+
88
+ unfulfilled_installments.push(installment) if line_items.empty?
89
+
90
+ line_items
91
+ end
92
+
93
+ # Remove installments which had no stock from the active list
94
+ # They will be reprocessed later
95
+ @installments -= unfulfilled_installments
96
+ if unfulfilled_installments.any?
97
+ Config.out_of_stock_dispatcher_class.new(unfulfilled_installments).dispatch
98
+ end
99
+
100
+ return if installments.empty?
101
+ order_builder.add_line_items(order_line_items)
102
+ end
103
+
104
+ def order_builder
105
+ @order_builder ||= OrderBuilder.new(order)
106
+ end
107
+
108
+ def subscription
109
+ installments.first.subscription
110
+ end
111
+
112
+ def ship_address
113
+ subscription.shipping_address || user.ship_address
114
+ end
115
+
116
+ def active_card
117
+ if SolidusSupport.solidus_gem_version < Gem::Version.new("2.2.0")
118
+ user.credit_cards.default.last
119
+ else
120
+ user.wallet.default_wallet_payment_source.payment_source
121
+ end
122
+ end
123
+
124
+ def create_payment
125
+ order.payments.create(
126
+ source: active_card,
127
+ amount: order.total,
128
+ payment_method: Config.default_gateway
129
+ )
130
+ end
131
+
132
+ def apply_promotions
133
+ Spree::PromotionHandler::Cart.new(order).activate
134
+ order.updater.update # reload totals
135
+ end
136
+
137
+ def different_owners?
138
+ installments.map { |i| i.subscription.user }.uniq.length > 1
139
+ end
140
+ end
141
+ end
@@ -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