spree_core 4.8.3 → 4.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/line_items/find_by_variant.rb +4 -2
  3. data/app/finders/spree/products/find.rb +1 -1
  4. data/app/finders/spree/variants/find.rb +30 -0
  5. data/app/models/spree/address.rb +12 -2
  6. data/app/models/spree/adjustment.rb +5 -0
  7. data/app/models/spree/asset.rb +8 -0
  8. data/app/models/spree/calculator/flat_rate.rb +3 -0
  9. data/app/models/spree/calculator/flexi_rate.rb +3 -0
  10. data/app/models/spree/calculator/percent_on_line_item.rb +3 -0
  11. data/app/models/spree/calculator/shipping/flat_rate.rb +13 -1
  12. data/app/models/spree/cms_section_image.rb +0 -6
  13. data/app/models/spree/customer_return.rb +1 -0
  14. data/app/models/spree/icon.rb +0 -6
  15. data/app/models/spree/image/configuration/active_storage.rb +0 -8
  16. data/app/models/spree/image.rb +17 -0
  17. data/app/models/spree/line_item.rb +23 -6
  18. data/app/models/spree/option_value_variant.rb +4 -0
  19. data/app/models/spree/order/currency_updater.rb +2 -2
  20. data/app/models/spree/order/webhooks.rb +19 -0
  21. data/app/models/spree/order.rb +17 -6
  22. data/app/models/spree/order_merger.rb +6 -0
  23. data/app/models/spree/payment/gateway_options.rb +4 -0
  24. data/app/models/spree/payment/webhooks.rb +15 -0
  25. data/app/models/spree/payment.rb +7 -7
  26. data/app/models/spree/price.rb +45 -0
  27. data/app/models/spree/product/webhooks.rb +17 -0
  28. data/app/models/spree/product.rb +7 -9
  29. data/app/models/spree/promotion/actions/free_shipping.rb +13 -0
  30. data/app/models/spree/promotion/rules/first_order.rb +6 -1
  31. data/app/models/spree/promotion.rb +2 -2
  32. data/app/models/spree/promotion_handler/coupon.rb +3 -2
  33. data/app/models/spree/refund.rb +14 -2
  34. data/app/models/spree/reimbursement/emails.rb +11 -0
  35. data/app/models/spree/reimbursement.rb +1 -6
  36. data/app/models/spree/role_user.rb +1 -1
  37. data/app/models/spree/shipment/emails.rb +11 -0
  38. data/app/models/spree/shipment/webhooks.rb +11 -0
  39. data/app/models/spree/shipment.rb +58 -7
  40. data/app/models/spree/shipment_handler.rb +2 -6
  41. data/app/models/spree/shipping_method.rb +6 -0
  42. data/app/models/spree/shipping_method_zone.rb +4 -2
  43. data/app/models/spree/shipping_rate.rb +1 -1
  44. data/app/models/spree/stock/package.rb +4 -0
  45. data/app/models/spree/stock/quantifier.rb +22 -4
  46. data/app/models/spree/stock_item/webhooks.rb +6 -0
  47. data/app/models/spree/stock_item.rb +1 -3
  48. data/app/models/spree/stock_location.rb +15 -0
  49. data/app/models/spree/stock_movement/webhooks.rb +6 -0
  50. data/app/models/spree/stock_movement.rb +1 -3
  51. data/app/models/spree/store.rb +1 -5
  52. data/app/models/spree/store_credit.rb +11 -12
  53. data/app/models/spree/store_favicon_image.rb +0 -6
  54. data/app/models/spree/store_logo.rb +0 -5
  55. data/app/models/spree/store_mailer_logo.rb +0 -6
  56. data/app/models/spree/tax_rate.rb +3 -1
  57. data/app/models/spree/taxon_image/configuration/active_storage.rb +0 -6
  58. data/app/models/spree/variant/webhooks.rb +6 -0
  59. data/app/models/spree/variant.rb +21 -3
  60. data/app/models/spree/wishlist.rb +9 -0
  61. data/app/presenters/spree/variants/options_presenter.rb +34 -2
  62. data/app/services/spree/cart/add_item.rb +2 -0
  63. data/app/services/spree/cart/destroy.rb +14 -4
  64. data/app/services/spree/seeds/all.rb +3 -0
  65. data/app/services/spree/seeds/payment_methods.rb +18 -0
  66. data/app/services/spree/seeds/stock_locations.rb +1 -1
  67. data/app/services/spree/seeds/zones.rb +19 -19
  68. data/config/locales/en.yml +2 -0
  69. data/db/migrate/20240623172111_add_deleted_at_to_spree_stock_locations.rb +6 -0
  70. data/db/migrate/20240725124530_add_refunder_to_spree_refunds.rb +6 -0
  71. data/lib/spree/core/controller_helpers/order.rb +5 -5
  72. data/lib/spree/core/dependencies.rb +2 -1
  73. data/lib/spree/core/preferences/preferable.rb +4 -2
  74. data/lib/spree/core/preferences/preferable_class_methods.rb +3 -2
  75. data/lib/spree/core/version.rb +1 -1
  76. data/lib/spree/core/webhooks.rb +15 -7
  77. data/lib/spree/core.rb +1 -0
  78. data/lib/spree/money.rb +1 -1
  79. data/lib/spree/testing_support/authorization_helpers.rb +2 -2
  80. data/lib/spree/testing_support/common_rake.rb +1 -1
  81. data/spree_core.gemspec +1 -0
  82. metadata +31 -4
@@ -27,9 +27,7 @@ module Spree
27
27
  include Spree::TranslatableResourceSlug
28
28
  include Spree::MemoizedData
29
29
  include Spree::Metadata
30
- if defined?(Spree::Webhooks::HasWebhooks)
31
- include Spree::Webhooks::HasWebhooks
32
- end
30
+ include Spree::Product::Webhooks
33
31
  if defined?(Spree::VendorConcern)
34
32
  include Spree::VendorConcern
35
33
  end
@@ -181,17 +179,17 @@ module Spree
181
179
  event :activate do
182
180
  transition to: :active
183
181
  end
184
- after_transition to: :active, do: :after_activate
182
+ after_transition to: :active, do: [:after_activate, :send_product_activated_webhook]
185
183
 
186
184
  event :archive do
187
185
  transition to: :archived
188
186
  end
189
- after_transition to: :archived, do: :after_archive
187
+ after_transition to: :archived, do: [:after_archive, :send_product_archived_webhook]
190
188
 
191
189
  event :draft do
192
190
  transition to: :draft
193
191
  end
194
- after_transition to: :draft, do: :after_draft
192
+ after_transition to: :draft, do: [:after_draft, :send_product_drafted_webhook]
195
193
  end
196
194
 
197
195
  # Can't use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
@@ -584,15 +582,15 @@ module Spree
584
582
  end
585
583
 
586
584
  def after_activate
587
- # this method is prepended in api/ to queue Webhooks requests
585
+ # Implement your logic here
588
586
  end
589
587
 
590
588
  def after_archive
591
- # this method is prepended in api/ to queue Webhooks requests
589
+ # Implement your logic here
592
590
  end
593
591
 
594
592
  def after_draft
595
- # this method is prepended in api/ to queue Webhooks requests
593
+ # Implement your logic here
596
594
  end
597
595
  end
598
596
  end
@@ -13,6 +13,19 @@ module Spree
13
13
  def compute_amount(shipment)
14
14
  shipment.cost * -1
15
15
  end
16
+
17
+ # we need to persist 0 amount adjustment
18
+ def create_adjustment(order, adjustable, included = false)
19
+ amount = compute_amount(adjustable)
20
+
21
+ adjustments.new(
22
+ adjustable: adjustable,
23
+ amount: amount,
24
+ included: included,
25
+ label: label,
26
+ order: order
27
+ ).save
28
+ end
16
29
  end
17
30
  end
18
31
  end
@@ -10,7 +10,12 @@ module Spree
10
10
 
11
11
  def eligible?(order, options = {})
12
12
  @user = order.try(:user) || options[:user]
13
- @email = order.email
13
+ @email = if options[:email].present?
14
+ order.email = options[:email]
15
+ order.email
16
+ elsif order.email.present?
17
+ order.email
18
+ end
14
19
  @store = order.store
15
20
 
16
21
  if user || email
@@ -128,10 +128,10 @@ module Spree
128
128
  end
129
129
 
130
130
  # called anytime order.update_with_updater! happens
131
- def eligible?(promotable)
131
+ def eligible?(promotable, options = {})
132
132
  return false if expired? || usage_limit_exceeded?(promotable) || blacklisted?(promotable)
133
133
 
134
- !!eligible_rules(promotable, {})
134
+ !!eligible_rules(promotable, options)
135
135
  end
136
136
 
137
137
  # eligible_rules returns an array of promotion rules where eligible? is true for the promotable
@@ -1,12 +1,13 @@
1
1
  module Spree
2
2
  module PromotionHandler
3
3
  class Coupon
4
- attr_reader :order, :store
4
+ attr_reader :order, :store, :options
5
5
  attr_accessor :error, :success, :status_code
6
6
 
7
- def initialize(order)
7
+ def initialize(order, options = {})
8
8
  @order = order
9
9
  @store = order.store
10
+ @options = options
10
11
  end
11
12
 
12
13
  def apply
@@ -13,6 +13,7 @@ module Spree
13
13
  belongs_to :reimbursement, optional: true
14
14
  end
15
15
  belongs_to :reason, class_name: 'Spree::RefundReason', foreign_key: :refund_reason_id
16
+ belongs_to :refunder, class_name: "::#{Spree.admin_user_class}", optional: true
16
17
 
17
18
  has_many :log_entries, as: :source
18
19
 
@@ -31,8 +32,10 @@ module Spree
31
32
 
32
33
  attr_reader :response
33
34
 
35
+ delegate :order, :currency, to: :payment
36
+
34
37
  def money
35
- Spree::Money.new(amount, currency: payment.currency)
38
+ Spree::Money.new(amount, currency: currency)
36
39
  end
37
40
  alias display_amount money
38
41
 
@@ -46,6 +49,15 @@ module Spree
46
49
  payment.payment_method.name
47
50
  end
48
51
 
52
+ # return items for the refund
53
+ #
54
+ # @return [Array<Spree::ReturnItem>]
55
+ def return_items
56
+ return [] unless reimbursement.present?
57
+
58
+ reimbursement.customer_return&.return_items || reimbursement.return_items
59
+ end
60
+
49
61
  private
50
62
 
51
63
  # attempts to perform the refund.
@@ -53,7 +65,7 @@ module Spree
53
65
  def perform!
54
66
  return true if transaction_id.present?
55
67
 
56
- credit_cents = Spree::Money.new(amount.to_f, currency: payment.currency).amount_in_cents
68
+ credit_cents = Spree::Money.new(amount.to_f, currency: currency).amount_in_cents
57
69
 
58
70
  @response = process!(credit_cents)
59
71
 
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ class Reimbursement < Spree::Base
3
+ module Emails
4
+ def send_reimbursement_email
5
+ # you can overwrite this method in your application / extension to send out the confirmation email
6
+ # or use `spree_emails` gem
7
+ # YourEmailVendor.deliver_reimbursement_email(id) # `id` = ID of the Reimbursement being sent, you can also pass the entire object using `self`
8
+ end
9
+ end
10
+ end
11
+ end
@@ -5,6 +5,7 @@ module Spree
5
5
  if defined?(Spree::Webhooks::HasWebhooks)
6
6
  include Spree::Webhooks::HasWebhooks
7
7
  end
8
+ include Spree::Reimbursement::Emails
8
9
 
9
10
  class IncompleteReimbursementError < StandardError; end
10
11
 
@@ -141,12 +142,6 @@ module Spree
141
142
  end
142
143
  end
143
144
 
144
- def send_reimbursement_email
145
- # you can overwrite this method in your application / extension to send out the confirmation email
146
- # or use `spree_emails` gem
147
- # YourEmailVendor.deliver_reimbursement_email(id) # `id` = ID of the Reimbursement being sent, you can also pass the entire object using `self`
148
- end
149
-
150
145
  # If there are multiple different reimbursement types for a single
151
146
  # reimbursement we open ourselves to a one-cent rounding error for every
152
147
  # type over the first one. This is due to how we round #unpaid_amount and
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  class RoleUser < Spree::Base
3
3
  belongs_to :role, class_name: 'Spree::Role'
4
- belongs_to :user, class_name: "::#{Spree.user_class}"
4
+ belongs_to :user, class_name: Spree.admin_user_class.to_s
5
5
  end
6
6
  end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ class Shipment < Spree::Base
3
+ module Emails
4
+ def send_shipped_email
5
+ # you can overwrite this method in your application / extension to send out the confirmation email
6
+ # or use `spree_emails` gem
7
+ # YourEmailVendor.deliver_shipment_notification_email(@shipment.id)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ class Shipment < Spree::Base
3
+ module Webhooks
4
+ extend ActiveSupport::Concern
5
+
6
+ def send_shipment_shipped_webhook
7
+ # Implement your logic here
8
+ end
9
+ end
10
+ end
11
+ end
@@ -6,18 +6,20 @@ module Spree
6
6
  include Spree::NumberIdentifier
7
7
  include Spree::NumberAsParam
8
8
  include Spree::Metadata
9
- if defined?(Spree::Webhooks::HasWebhooks)
10
- include Spree::Webhooks::HasWebhooks
11
- end
12
9
  if defined?(Spree::Security::Shipments)
13
10
  include Spree::Security::Shipments
14
11
  end
12
+ if defined?(Spree::VendorConcern)
13
+ include Spree::VendorConcern
14
+ end
15
+ include Spree::Shipment::Emails
16
+ include Spree::Shipment::Webhooks
15
17
 
16
18
  with_options inverse_of: :shipments do
17
19
  belongs_to :address, class_name: 'Spree::Address'
18
20
  belongs_to :order, class_name: 'Spree::Order', touch: true
19
21
  end
20
- belongs_to :stock_location, class_name: 'Spree::StockLocation'
22
+ belongs_to :stock_location, -> { with_deleted }, class_name: 'Spree::StockLocation'
21
23
 
22
24
  with_options dependent: :delete_all do
23
25
  has_many :adjustments, as: :adjustable
@@ -26,6 +28,7 @@ module Spree
26
28
  has_many :state_changes, as: :stateful
27
29
  end
28
30
  has_many :shipping_methods, through: :shipping_rates
31
+ has_many :variants, through: :inventory_units
29
32
  has_one :selected_shipping_rate, -> { where(selected: true).order(:cost) }, class_name: Spree::ShippingRate.to_s
30
33
 
31
34
  after_save :update_adjustments
@@ -47,8 +50,18 @@ module Spree
47
50
  # sort by most recent shipped_at, falling back to created_at. add "id desc" to make specs that involve this scope more deterministic.
48
51
  scope :reverse_chronological, -> { order(Arel.sql('coalesce(spree_shipments.shipped_at, spree_shipments.created_at) desc'), id: :desc) }
49
52
  scope :valid, -> { where.not(state: :canceled) }
53
+ scope :canceled, -> { with_state('canceled') }
54
+ scope :not_canceled, -> { where.not(state: 'canceled') }
55
+ scope :shipped_but_canceled, -> { canceled.where.not(shipped_at: nil) }
56
+ # This scope will select the shipping_method_id from the shipments' selected shipping rate
57
+ scope :with_selected_shipping_method, lambda {
58
+ joins(:selected_shipping_rate).
59
+ where(Spree::ShippingRate.arel_table[:shipping_method_id].not_eq(nil)).
60
+ select(Spree::ShippingRate.arel_table[:shipping_method_id])
61
+ }
50
62
 
51
63
  delegate :store, :currency, to: :order
64
+ delegate :amount_in_cents, to: :display_cost
52
65
 
53
66
  # shipment state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
54
67
  state_machine initial: :pending, use_transactions: false do
@@ -66,7 +79,7 @@ module Spree
66
79
  event :ship do
67
80
  transition from: %i(ready canceled), to: :shipped
68
81
  end
69
- after_transition to: :shipped, do: :after_ship
82
+ after_transition to: :shipped, do: [:after_ship, :send_shipment_shipped_webhook]
70
83
 
71
84
  event :cancel do
72
85
  transition to: :canceled, from: %i(pending ready)
@@ -93,9 +106,20 @@ module Spree
93
106
  self.whitelisted_ransackable_attributes = ['number']
94
107
 
95
108
  extend DisplayMoney
96
- money_methods :cost, :discounted_cost, :final_price, :item_cost
109
+ money_methods :cost, :discounted_cost, :final_price, :item_cost, :additional_tax_total, :included_tax_total, :tax_total
97
110
  alias display_amount display_cost
98
111
 
112
+ # Returns the shipment number and shipping method name
113
+ #
114
+ # @return [String]
115
+ def name
116
+ [number, shipping_method&.name].compact.join(' ').strip
117
+ end
118
+
119
+ def amount
120
+ cost
121
+ end
122
+
99
123
  def add_shipping_method(shipping_method, selected = false)
100
124
  shipping_rates.create(shipping_method: shipping_method, selected: selected, cost: cost)
101
125
  end
@@ -108,17 +132,44 @@ module Spree
108
132
  manifest.each { |item| manifest_unstock(item) }
109
133
  end
110
134
 
135
+ # Returns true if the shipment has any backordered inventory units
136
+ #
137
+ # @return [Boolean]
111
138
  def backordered?
112
139
  inventory_units.any?(&:backordered?)
113
140
  end
114
141
 
142
+ # Returns true if the shipment is tracked
143
+ #
144
+ # @return [Boolean]
145
+ def tracked?
146
+ tracking.present? || tracking_url.present?
147
+ end
148
+
149
+ # Returns true if the shipment is shippable
150
+ #
151
+ # @return [Boolean]
152
+ def shippable?
153
+ can_ship? && tracked?
154
+ end
155
+
156
+ # Returns true if not all of the shipment's line items are fully shipped
157
+ #
158
+ # @return [Boolean]
159
+ def partial?
160
+ manifest.any? do |manifest_item|
161
+ line_item = manifest_item.line_item
162
+ line_item.quantity > manifest_item.quantity
163
+ end
164
+ end
165
+
115
166
  # Determines the appropriate +state+ according to the following logic:
116
167
  #
117
168
  # pending unless order is complete and +order.payment_state+ is +paid+
118
169
  # shipped if already shipped (ie. does not change the state)
119
170
  # ready all other cases
120
171
  def determine_state(order)
121
- return 'canceled' if order.canceled?
172
+ return 'canceled' if canceled? || order.canceled?
122
173
  return 'pending' unless order.can_ship?
123
174
  return 'pending' if inventory_units.any? &:backordered?
124
175
  return 'shipped' if shipped?
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class ShipmentHandler
3
+ include Spree::Shipment::Emails
4
+
3
5
  class << self
4
6
  def factory(shipment)
5
7
  # Do we have a specialized shipping-method-specific handler? e.g:
@@ -27,12 +29,6 @@ module Spree
27
29
 
28
30
  protected
29
31
 
30
- def send_shipped_email
31
- # you can overwrite this method in your application / extension to send out the confirmation email
32
- # or use `spree_emails` gem
33
- # YourEmailVendor.deliver_shipment_notification_email(@shipment.id)
34
- end
35
-
36
32
  def update_order_shipment_state
37
33
  order = @shipment.order
38
34
 
@@ -18,6 +18,8 @@ module Spree
18
18
 
19
19
  default_scope { where(deleted_at: nil) }
20
20
 
21
+ scope :with_calculator, ->(calculator) { joins(:calculator).where(calculator: { type: calculator.to_s }) }
22
+
21
23
  has_many :shipping_method_categories, dependent: :destroy
22
24
  has_many :shipping_categories, through: :shipping_method_categories
23
25
  has_many :shipping_rates, inverse_of: :shipping_method
@@ -33,6 +35,10 @@ module Spree
33
35
 
34
36
  validate :at_least_one_shipping_category
35
37
 
38
+ scope :available, -> { where(display_on: [:both]) }
39
+ scope :available_on_front_end, -> { where(display_on: [:front_end, :both]) }
40
+ scope :available_on_back_end, -> { where(display_on: [:back_end, :both]) }
41
+
36
42
  def include?(address)
37
43
  return false unless address
38
44
 
@@ -1,6 +1,8 @@
1
1
  module Spree
2
2
  class ShippingMethodZone < Spree::Base
3
- belongs_to :shipping_method
4
- belongs_to :zone
3
+ belongs_to :shipping_method, -> { with_deleted }, inverse_of: :shipping_method_zones, class_name: 'Spree::ShippingMethod'
4
+ belongs_to :zone, inverse_of: :shipping_method_zones, class_name: 'Spree::Zone'
5
+
6
+ validates :shipping_method, uniqueness: { scope: :zone }
5
7
  end
6
8
  end
@@ -14,7 +14,7 @@ module Spree
14
14
  def display_price
15
15
  price = display_base_price.to_s
16
16
 
17
- return price if tax_rate.nil? || tax_amount.zero?
17
+ return price if tax_rate.nil? || tax_amount.zero? || !tax_rate.show_rate_in_label
18
18
 
19
19
  Spree.t(
20
20
  tax_rate.included_in_price? ? :including_tax : :excluding_tax,
@@ -34,6 +34,10 @@ module Spree
34
34
  contents.detect { |item| !!item.try(:inventory_unit).try(:order) }.try(:inventory_unit).try(:order)
35
35
  end
36
36
 
37
+ def item_total
38
+ contents.sum(&:amount)
39
+ end
40
+
37
41
  def weight
38
42
  contents.sum(&:weight)
39
43
  end
@@ -10,7 +10,11 @@ module Spree
10
10
 
11
11
  def total_on_hand
12
12
  @total_on_hand ||= if variant.should_track_inventory?
13
- stock_items.sum(:count_on_hand)
13
+ if association_loaded?
14
+ stock_items.sum(&:count_on_hand)
15
+ else
16
+ stock_items.sum(:count_on_hand)
17
+ end
14
18
  else
15
19
  BigDecimal::INFINITY
16
20
  end
@@ -30,10 +34,24 @@ module Spree
30
34
 
31
35
  private
32
36
 
33
- def scope_to_location(collection)
34
- return collection.with_active_stock_location if stock_location.blank?
37
+ def association_loaded?
38
+ variant.association(:stock_items).loaded? && variant.association(:stock_locations).loaded?
39
+ end
35
40
 
36
- collection.where(stock_location: stock_location)
41
+ def scope_to_location(collection)
42
+ if stock_location.blank?
43
+ if association_loaded?
44
+ return collection.select { |si| si.stock_location&.active? }
45
+ else
46
+ return collection.with_active_stock_location
47
+ end
48
+ end
49
+
50
+ if association_loaded?
51
+ collection.select { |si| si.stock_location_id == stock_location.id }
52
+ else
53
+ collection.where(stock_location: stock_location)
54
+ end
37
55
  end
38
56
  end
39
57
  end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ class StockItem < Spree::Base
3
+ module Webhooks
4
+ end
5
+ end
6
+ end
@@ -3,9 +3,7 @@ module Spree
3
3
  acts_as_paranoid
4
4
 
5
5
  include Spree::Metadata
6
- if defined?(Spree::Webhooks::HasWebhooks)
7
- include Spree::Webhooks::HasWebhooks
8
- end
6
+ include Spree::StockItem::Webhooks
9
7
 
10
8
  with_options inverse_of: :stock_items do
11
9
  belongs_to :stock_location, class_name: 'Spree::StockLocation'
@@ -11,6 +11,8 @@ module Spree
11
11
  include Spree::VendorConcern
12
12
  end
13
13
 
14
+ acts_as_paranoid
15
+
14
16
  has_many :shipments
15
17
  has_many :stock_items, dependent: :delete_all, inverse_of: :stock_location
16
18
  has_many :variants, through: :stock_items
@@ -122,6 +124,19 @@ module Spree
122
124
  end
123
125
  end
124
126
 
127
+ def address
128
+ Spree::Address.new(
129
+ address1: address1,
130
+ address2: address2,
131
+ city: city,
132
+ state: state,
133
+ state_name: state_name,
134
+ country: country,
135
+ zipcode: zipcode,
136
+ phone: phone
137
+ )
138
+ end
139
+
125
140
  private
126
141
 
127
142
  def create_stock_items
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ class StockMovement < Spree::Base
3
+ module Webhooks
4
+ end
5
+ end
6
+ end
@@ -5,9 +5,7 @@ module Spree
5
5
  min: -2**31
6
6
  }.freeze
7
7
 
8
- if defined?(Spree::Webhooks::HasWebhooks)
9
- include Spree::Webhooks::HasWebhooks
10
- end
8
+ include Spree::StockMovement::Webhooks
11
9
 
12
10
  belongs_to :stock_item, class_name: 'Spree::StockItem', inverse_of: :stock_movements
13
11
  belongs_to :originator, polymorphic: true
@@ -110,11 +110,7 @@ module Spree
110
110
  after_commit :clear_cache
111
111
 
112
112
  def self.current(url = nil)
113
- Spree::Deprecation.warn(<<-DEPRECATION, caller)
114
- `Spree::Store.current` is deprecated and will be removed in Spree 5.0
115
- Please use `Spree::Stores::FindCurrent.new(url: "https://example.com").execute` instead
116
- DEPRECATION
117
- Stores::FindCurrent.new(url: url).execute
113
+ Spree::Dependencies.current_store_finder.constantize.new(url: url).execute
118
114
  end
119
115
 
120
116
  # FIXME: we need to drop `or_initialize` in v5
@@ -19,13 +19,16 @@ module Spree
19
19
  DEFAULT_CREATED_BY_EMAIL = 'spree@example.com'.freeze
20
20
 
21
21
  belongs_to :user, class_name: "::#{Spree.user_class}", foreign_key: 'user_id'
22
- belongs_to :category, class_name: 'Spree::StoreCreditCategory'
23
- belongs_to :created_by, class_name: Spree.admin_user_class.to_s, foreign_key: 'created_by_id'
24
- belongs_to :credit_type, class_name: 'Spree::StoreCreditType', foreign_key: 'type_id'
22
+ belongs_to :category, class_name: 'Spree::StoreCreditCategory', optional: true
23
+ belongs_to :created_by, class_name: "::#{Spree.admin_user_class}", foreign_key: 'created_by_id', optional: true
24
+ belongs_to :credit_type, class_name: 'Spree::StoreCreditType', foreign_key: 'type_id', optional: true
25
25
  belongs_to :store, class_name: 'Spree::Store'
26
+
26
27
  has_many :store_credit_events, class_name: 'Spree::StoreCreditEvent'
28
+ has_many :payments, as: :source, class_name: 'Spree::Payment'
29
+ has_many :orders, through: :payments, class_name: 'Spree::Order'
27
30
 
28
- validates :user, :category, :credit_type, :created_by, :currency, :store, presence: true
31
+ validates :currency, :store, presence: true
29
32
  validates :amount, numericality: { greater_than: 0 }
30
33
  validates :amount_used, numericality: { greater_than_or_equal_to: 0 }
31
34
  validate :amount_used_less_than_or_equal_to_amount
@@ -36,7 +39,10 @@ module Spree
36
39
 
37
40
  scope :order_by_priority, -> { includes(:credit_type).order('spree_store_credit_types.priority ASC') }
38
41
 
39
- before_validation :associate_credit_type
42
+ scope :not_authorized, -> { where(amount_authorized: 0) }
43
+ scope :not_used, -> { where(amount_authorized: 0) }
44
+ scope :available, -> { not_authorized.not_used }
45
+
40
46
  after_save :store_event
41
47
  before_destroy :validate_no_amount_used
42
48
 
@@ -249,12 +255,5 @@ module Spree
249
255
  throw(:abort)
250
256
  end
251
257
  end
252
-
253
- def associate_credit_type
254
- unless type_id
255
- credit_type_name = category.try(:non_expiring?) ? 'Non-expiring' : 'Expiring'
256
- self.credit_type = Spree::StoreCreditType.find_or_create_by(name: credit_type_name)
257
- end
258
- end
259
258
  end
260
259
  end
@@ -1,11 +1,5 @@
1
1
  module Spree
2
2
  class StoreFaviconImage < Asset
3
- if Spree.public_storage_service_name
4
- has_one_attached :attachment, service: Spree.public_storage_service_name
5
- else
6
- has_one_attached :attachment
7
- end
8
-
9
3
  VALID_CONTENT_TYPES = ['image/png', 'image/x-icon', 'image/vnd.microsoft.icon'].freeze
10
4
 
11
5
  validates :attachment,
@@ -1,9 +1,4 @@
1
1
  module Spree
2
2
  class StoreLogo < Asset
3
- if Spree.public_storage_service_name
4
- has_one_attached :attachment, service: Spree.public_storage_service_name
5
- else
6
- has_one_attached :attachment
7
- end
8
3
  end
9
4
  end
@@ -1,11 +1,5 @@
1
1
  module Spree
2
2
  class StoreMailerLogo < Asset
3
- if Spree.public_storage_service_name
4
- has_one_attached :attachment, service: Spree.public_storage_service_name
5
- else
6
- has_one_attached :attachment
7
- end
8
-
9
3
  VALID_CONTENT_TYPES = ['image/png', 'image/jpg', 'image/jpeg'].freeze
10
4
 
11
5
  validates :attachment, content_type: VALID_CONTENT_TYPES
@@ -115,11 +115,13 @@ module Spree
115
115
 
116
116
  def amount_for_label
117
117
  return '' unless show_rate_in_label?
118
+ return '' if amount.zero?
118
119
 
119
120
  ' ' + ActiveSupport::NumberHelper::NumberToPercentageConverter.convert(
120
121
  amount * 100,
121
122
  locale: I18n.locale,
122
- strip_insignificant_zeros: true
123
+ strip_insignificant_zeros: true,
124
+ precision: 2
123
125
  )
124
126
  end
125
127
  end
@@ -5,12 +5,6 @@ module Spree
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- if Spree.public_storage_service_name
9
- has_one_attached :attachment, service: Spree.public_storage_service_name
10
- else
11
- has_one_attached :attachment
12
- end
13
-
14
8
  validates :attachment, content_type: /\Aimage\/.*\z/
15
9
 
16
10
  default_scope { includes(attachment_attachment: :blob) }