spree_core 5.1.0.beta3 → 5.1.0.rc1
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 +4 -4
- data/app/helpers/spree/base_helper.rb +6 -1
- data/app/helpers/spree/images_helper.rb +19 -16
- data/app/helpers/spree/shipment_helper.rb +12 -0
- data/app/jobs/spree/gift_cards/bulk_generate_job.rb +13 -0
- data/app/models/concerns/spree/parameterizable_name.rb +11 -0
- data/app/models/concerns/spree/payment_source_concern.rb +39 -0
- data/app/models/concerns/spree/product_scopes.rb +1 -5
- data/app/models/concerns/spree/stores/socials.rb +6 -2
- data/app/models/concerns/spree/user_methods.rb +2 -1
- data/app/models/spree/ability.rb +2 -0
- data/app/models/spree/address.rb +11 -3
- data/app/models/spree/credit_card.rb +2 -24
- data/app/models/spree/gateway/custom_payment_source_method.rb +33 -0
- data/app/models/spree/gift_card.rb +162 -0
- data/app/models/spree/gift_card_batch.rb +79 -0
- data/app/models/spree/inventory_unit.rb +11 -4
- data/app/models/spree/line_item.rb +13 -2
- data/app/models/spree/option_type.rb +5 -15
- data/app/models/spree/option_value.rb +18 -12
- data/app/models/spree/order/checkout.rb +2 -0
- data/app/models/spree/order/gift_card.rb +51 -0
- data/app/models/spree/order/store_credit.rb +20 -1
- data/app/models/spree/order.rb +51 -6
- data/app/models/spree/order_merger.rb +12 -0
- data/app/models/spree/page_sections/featured_posts.rb +4 -0
- data/app/models/spree/payment.rb +2 -1
- data/app/models/spree/payment_method/store_credit.rb +1 -1
- data/app/models/spree/payment_method.rb +1 -0
- data/app/models/spree/payment_source.rb +21 -0
- data/app/models/spree/post.rb +1 -0
- data/app/models/spree/price.rb +7 -1
- data/app/models/spree/product/slugs.rb +103 -0
- data/app/models/spree/product.rb +7 -81
- data/app/models/spree/product_property.rb +2 -2
- data/app/models/spree/promotion_handler/coupon.rb +39 -0
- data/app/models/spree/property.rb +2 -8
- data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +1 -1
- data/app/models/spree/return_item.rb +4 -0
- data/app/models/spree/shipment.rb +19 -0
- data/app/models/spree/shipping_method.rb +8 -9
- data/app/models/spree/store.rb +2 -0
- data/app/models/spree/store_credit.rb +9 -6
- data/app/models/spree/store_credit_event.rb +8 -4
- data/app/models/spree/taxon.rb +8 -1
- data/app/models/spree/theme.rb +1 -1
- data/app/models/spree/variant.rb +1 -1
- data/app/models/spree/wishlist.rb +7 -0
- data/app/presenters/spree/csv/product_variant_presenter.rb +14 -3
- data/app/services/spree/addresses/phone_validator.rb +20 -0
- data/app/services/spree/cart/destroy.rb +1 -1
- data/app/services/spree/cart/recalculate.rb +1 -0
- data/app/services/spree/gift_cards/apply.rb +66 -0
- data/app/services/spree/gift_cards/redeem.rb +17 -0
- data/app/services/spree/gift_cards/remove.rb +38 -0
- data/app/services/spree/products/prepare_nested_attributes.rb +9 -2
- data/app/views/spree/addresses/_form.html.erb +1 -1
- data/app/views/spree/shared/_payment.html.erb +13 -4
- data/config/locales/en.yml +44 -14
- data/config/routes.rb +1 -0
- data/db/migrate/20250506073057_create_spree_gift_cards_and_spree_gift_card_batches.rb +37 -0
- data/db/migrate/20250530101236_enable_pg_trgm_extension.rb +13 -0
- data/db/migrate/20250605131334_add_missing_fields_to_users.rb +25 -0
- data/lib/generators/spree/install/install_generator.rb +0 -8
- data/lib/spree/core/configuration.rb +4 -0
- data/lib/spree/core/controller_helpers/store.rb +13 -1
- data/lib/spree/core/dependencies.rb +5 -0
- data/lib/spree/core/engine.rb +8 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +2 -1
- data/lib/spree/permitted_attributes.rb +23 -10
- data/lib/spree/testing_support/authorization_helpers.rb +5 -9
- data/lib/spree/testing_support/common_rake.rb +7 -1
- data/lib/spree/testing_support/factories/gift_card_batch_factory.rb +5 -0
- data/lib/spree/testing_support/factories/gift_card_factory.rb +17 -0
- data/lib/spree/testing_support/factories/page_section_factory.rb +2 -1
- data/lib/spree/testing_support/factories/payment_factory.rb +5 -0
- data/lib/spree/testing_support/factories/payment_method_factory.rb +5 -0
- data/lib/spree/testing_support/factories/payment_source_factory.rb +5 -0
- data/lib/spree/testing_support/factories/post_factory.rb +4 -0
- data/lib/spree/testing_support/factories/promotion_rule_factory.rb +4 -0
- data/lib/spree/testing_support/factories/theme_factory.rb +1 -1
- data/lib/spree/testing_support/factories/user_factory.rb +2 -2
- metadata +48 -11
- data/config/initializers/state_machine.rb +0 -37
data/app/models/spree/product.rb
CHANGED
@@ -20,14 +20,17 @@
|
|
20
20
|
|
21
21
|
module Spree
|
22
22
|
class Product < Spree.base_class
|
23
|
-
|
23
|
+
acts_as_paranoid
|
24
|
+
acts_as_taggable_on :tags, :labels
|
25
|
+
auto_strip_attributes :name
|
26
|
+
|
24
27
|
include Spree::ProductScopes
|
25
28
|
include Spree::MultiStoreResource
|
26
29
|
include Spree::TranslatableResource
|
27
|
-
include Spree::TranslatableResourceSlug
|
28
30
|
include Spree::MemoizedData
|
29
31
|
include Spree::Metadata
|
30
32
|
include Spree::Product::Webhooks
|
33
|
+
include Spree::Product::Slugs
|
31
34
|
if defined?(Spree::VendorConcern)
|
32
35
|
include Spree::VendorConcern
|
33
36
|
end
|
@@ -50,39 +53,10 @@ module Spree
|
|
50
53
|
if defined?(PgSearch)
|
51
54
|
include PgSearch::Model
|
52
55
|
|
53
|
-
|
54
|
-
pg_search_scope :search_by_name, against: { name: 'A', meta_title: 'B' }, using: { trigram: { threshold: 0.3, word_similarity: true } }
|
55
|
-
else
|
56
|
-
pg_search_scope :search_by_name, against: { name: 'A', meta_title: 'B' }, using: { tsearch: { any_word: true, prefix: true } }
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
before_save :set_slug
|
61
|
-
acts_as_paranoid
|
62
|
-
# deleted translation values also need to be accessible for index views listing deleted resources
|
63
|
-
default_scope { unscope(where: :deleted_at) }
|
64
|
-
def set_slug
|
65
|
-
self.slug = generate_slug
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def generate_slug
|
71
|
-
if name.blank? && slug.blank?
|
72
|
-
translated_model.name.to_url
|
73
|
-
elsif slug.blank?
|
74
|
-
name.to_url
|
75
|
-
else
|
76
|
-
slug.to_url
|
77
|
-
end
|
56
|
+
pg_search_scope :search_by_name, against: { name: 'A', meta_title: 'B' }, using: { trigram: { threshold: 0.3, word_similarity: true } }
|
78
57
|
end
|
79
58
|
end
|
80
59
|
|
81
|
-
friendly_id :slug_candidates, use: [:history, :scoped, :mobility], scope: spree_base_uniqueness_scope
|
82
|
-
acts_as_paranoid
|
83
|
-
auto_strip_attributes :name
|
84
|
-
acts_as_taggable_on :tags, :labels
|
85
|
-
|
86
60
|
# we need to have this callback before any dependent: :destroy associations
|
87
61
|
# https://github.com/rails/rails/issues/3458
|
88
62
|
before_destroy :ensure_not_in_complete_orders
|
@@ -148,17 +122,12 @@ module Spree
|
|
148
122
|
after_initialize :ensure_master
|
149
123
|
after_initialize :assign_default_tax_category
|
150
124
|
|
151
|
-
before_validation :downcase_slug
|
152
|
-
before_validation :normalize_slug, on: :update
|
153
125
|
before_validation :validate_master
|
154
126
|
before_validation :ensure_default_shipping_category
|
155
127
|
|
156
128
|
after_create :add_associations_from_prototype
|
157
129
|
after_create :build_variants_from_option_values_hash, if: :option_values_hash
|
158
130
|
|
159
|
-
after_destroy :punch_slug
|
160
|
-
after_restore :update_slug_history
|
161
|
-
|
162
131
|
after_save :save_master
|
163
132
|
after_save :run_touch_callbacks, if: :anything_changed?
|
164
133
|
after_save :reset_nested_changes
|
@@ -176,7 +145,6 @@ module Spree
|
|
176
145
|
validates :price, if: :requires_price?
|
177
146
|
end
|
178
147
|
|
179
|
-
validates :slug, presence: true, uniqueness: { allow_blank: true, case_sensitive: true, scope: spree_base_uniqueness_scope }
|
180
148
|
validate :discontinue_on_must_be_later_than_make_active_at, if: -> { make_active_at && discontinue_on }
|
181
149
|
|
182
150
|
scope :for_store, ->(store) { joins(:store_products).where(StoreProduct.table_name => { store_id: store.id }) }
|
@@ -231,7 +199,7 @@ module Spree
|
|
231
199
|
having("SUM(#{Spree::StockItem.table_name}.count_on_hand) <= 0")
|
232
200
|
}
|
233
201
|
scope :out_of_stock, lambda {
|
234
|
-
joins(:stock_items).where("#{Spree::
|
202
|
+
joins(:stock_items).where("#{Spree::Variant.table_name}.track_inventory = ? OR #{Spree::StockItem.table_name}.count_on_hand <= ?", false, 0)
|
235
203
|
}
|
236
204
|
|
237
205
|
scope :by_best_selling, lambda { |order_direction = :desc|
|
@@ -515,17 +483,6 @@ module Spree
|
|
515
483
|
where conditions.inject(:or)
|
516
484
|
end
|
517
485
|
|
518
|
-
def self.slug_available?(slug, id)
|
519
|
-
!where(slug: slug).where.not(id: id).exists?
|
520
|
-
end
|
521
|
-
|
522
|
-
def ensure_slug_is_unique(candidate_slug)
|
523
|
-
return slug if candidate_slug.blank? || slug.blank?
|
524
|
-
return candidate_slug if self.class.slug_available?(candidate_slug, id)
|
525
|
-
|
526
|
-
normalize_friendly_id([candidate_slug, uuid_for_friendly_id])
|
527
|
-
end
|
528
|
-
|
529
486
|
# Suitable for displaying only variants that has at least one option value.
|
530
487
|
# There may be scenarios where an option type is removed and along with it
|
531
488
|
# all option values. At that point all variants associated with only those
|
@@ -741,25 +698,6 @@ module Spree
|
|
741
698
|
self.tax_category = Spree::TaxCategory.default if new_record?
|
742
699
|
end
|
743
700
|
|
744
|
-
def normalize_slug
|
745
|
-
self.slug = normalize_friendly_id(slug)
|
746
|
-
end
|
747
|
-
|
748
|
-
def punch_slug
|
749
|
-
# punch slug with date prefix to allow reuse of original
|
750
|
-
return if frozen?
|
751
|
-
|
752
|
-
update_column(:slug, "#{Time.current.to_i}_#{slug}"[0..254])
|
753
|
-
|
754
|
-
translations.with_deleted.each do |t|
|
755
|
-
t.update_column :slug, "#{Time.current.to_i}_#{t.slug}"[0..254]
|
756
|
-
end
|
757
|
-
end
|
758
|
-
|
759
|
-
def update_slug_history
|
760
|
-
save!
|
761
|
-
end
|
762
|
-
|
763
701
|
def anything_changed?
|
764
702
|
saved_changes? || @nested_changes
|
765
703
|
end
|
@@ -815,14 +753,6 @@ module Spree
|
|
815
753
|
end
|
816
754
|
end
|
817
755
|
|
818
|
-
# Try building a slug based on the following fields in increasing order of specificity.
|
819
|
-
def slug_candidates
|
820
|
-
[
|
821
|
-
:name,
|
822
|
-
[:name, :sku]
|
823
|
-
]
|
824
|
-
end
|
825
|
-
|
826
756
|
def run_touch_callbacks
|
827
757
|
run_callbacks(:touch)
|
828
758
|
end
|
@@ -870,10 +800,6 @@ module Spree
|
|
870
800
|
previously_new_record? || tag_list_previously_changed? || available_on_previously_changed?
|
871
801
|
end
|
872
802
|
|
873
|
-
def downcase_slug
|
874
|
-
slug&.downcase!
|
875
|
-
end
|
876
|
-
|
877
803
|
def after_activate
|
878
804
|
# Implement your logic here
|
879
805
|
end
|
@@ -31,9 +31,9 @@ module Spree
|
|
31
31
|
default_scope { order(:position) }
|
32
32
|
|
33
33
|
scope :filterable, -> { joins(:property).where(Property.table_name => { filterable: true }) }
|
34
|
-
scope :for_products, ->(products) {
|
34
|
+
scope :for_products, ->(products) { where(product_id: products) }
|
35
35
|
scope :sort_by_property_position, -> {
|
36
|
-
unscope(:order).joins(:property).order(
|
36
|
+
unscope(:order).joins(:property).order(Spree::Property.table_name => { position: :asc })
|
37
37
|
}
|
38
38
|
|
39
39
|
self.whitelisted_ransackable_attributes = ['value', 'filter_param']
|
@@ -11,6 +11,27 @@ module Spree
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def apply
|
14
|
+
if load_gift_card_code
|
15
|
+
|
16
|
+
if @gift_card.expired?
|
17
|
+
set_error_code :gift_card_expired
|
18
|
+
return self
|
19
|
+
elsif @gift_card.redeemed?
|
20
|
+
set_error_code :gift_card_already_redeemed
|
21
|
+
return self
|
22
|
+
end
|
23
|
+
|
24
|
+
result = order.apply_gift_card(@gift_card)
|
25
|
+
|
26
|
+
if result.success?
|
27
|
+
set_success_code(:gift_card_applied)
|
28
|
+
else
|
29
|
+
set_error_code(result.value, result.error.value || {})
|
30
|
+
end
|
31
|
+
|
32
|
+
return self
|
33
|
+
end
|
34
|
+
|
14
35
|
if order.coupon_code.present?
|
15
36
|
if promotion.present? && promotion.actions.exists?
|
16
37
|
handle_present_promotion
|
@@ -26,6 +47,18 @@ module Spree
|
|
26
47
|
end
|
27
48
|
|
28
49
|
def remove(coupon_code)
|
50
|
+
if order.gift_card
|
51
|
+
result = order.remove_gift_card
|
52
|
+
|
53
|
+
if result.success?
|
54
|
+
set_success_code(:gift_card_removed)
|
55
|
+
else
|
56
|
+
set_error_code(result.value)
|
57
|
+
end
|
58
|
+
|
59
|
+
return self
|
60
|
+
end
|
61
|
+
|
29
62
|
promotion = order.promotions.with_coupon_code(coupon_code)
|
30
63
|
if promotion.present?
|
31
64
|
# Order promotion has to be destroyed before line item removing
|
@@ -179,6 +212,12 @@ module Spree
|
|
179
212
|
def handle_coupon_code(discount, coupon_code)
|
180
213
|
discount.source.promotion.coupon_codes.unused.find_by(code: coupon_code)&.apply_order!(order)
|
181
214
|
end
|
215
|
+
|
216
|
+
def load_gift_card_code
|
217
|
+
return unless order.coupon_code.present?
|
218
|
+
|
219
|
+
@gift_card = order.store.gift_cards.find_by(code: order.coupon_code.downcase)
|
220
|
+
end
|
182
221
|
end
|
183
222
|
end
|
184
223
|
end
|
@@ -9,19 +9,13 @@ module Spree
|
|
9
9
|
include Spree::Webhooks::HasWebhooks
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
translates(*TRANSLATABLE_FIELDS)
|
15
|
-
else
|
16
|
-
TRANSLATABLE_FIELDS = %i[presentation].freeze
|
17
|
-
translates(*TRANSLATABLE_FIELDS, column_fallback: true)
|
18
|
-
end
|
12
|
+
TRANSLATABLE_FIELDS = %i[presentation].freeze
|
13
|
+
translates(*TRANSLATABLE_FIELDS, column_fallback: !Spree.always_use_translations?)
|
19
14
|
|
20
15
|
self::Translation.class_eval do
|
21
16
|
auto_strip_attributes :presentation
|
22
17
|
end
|
23
18
|
|
24
|
-
auto_strip_attributes :name, :presentation
|
25
19
|
acts_as_list
|
26
20
|
|
27
21
|
has_many :property_prototypes, class_name: 'Spree::PropertyPrototype'
|
@@ -49,7 +49,7 @@ module Spree
|
|
49
49
|
user: reimbursement.order.user,
|
50
50
|
amount: unpaid_amount,
|
51
51
|
category: category,
|
52
|
-
created_by:
|
52
|
+
created_by: reimbursement.order.store.users.first,
|
53
53
|
memo: "Refund for uncreditable payments on order #{reimbursement.order.number}",
|
54
54
|
currency: reimbursement.order.currency,
|
55
55
|
store: reimbursement.order.store
|
@@ -13,6 +13,10 @@ module Spree
|
|
13
13
|
@_return_quantity = value.to_i
|
14
14
|
end
|
15
15
|
|
16
|
+
def pre_tax_amount=(amount)
|
17
|
+
self[:pre_tax_amount] = Spree::LocalizedNumber.parse(amount)
|
18
|
+
end
|
19
|
+
|
16
20
|
def return_quantity
|
17
21
|
@_return_quantity.nil? ? inventory_unit.quantity : @_return_quantity
|
18
22
|
end
|
@@ -221,10 +221,27 @@ module Spree
|
|
221
221
|
inventory_units.where(line_item_id: line_item.id, variant_id: line_item.variant_id || variant.id)
|
222
222
|
end
|
223
223
|
|
224
|
+
# Returns the cost of the shipment
|
225
|
+
#
|
226
|
+
# @return [BigDecimal]
|
224
227
|
def item_cost
|
225
228
|
manifest.map { |m| (m.line_item.price + (m.line_item.adjustment_total / m.line_item.quantity)) * m.quantity }.sum
|
226
229
|
end
|
227
230
|
|
231
|
+
# Returns the weight of the shipment
|
232
|
+
#
|
233
|
+
# @return [BigDecimal]
|
234
|
+
def item_weight
|
235
|
+
manifest.map { |m| m.line_item.item_weight }.sum
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns the weight unit of the shipment
|
239
|
+
#
|
240
|
+
# @return [String]
|
241
|
+
def weight_unit
|
242
|
+
manifest.first.line_item.weight_unit
|
243
|
+
end
|
244
|
+
|
228
245
|
def line_items
|
229
246
|
inventory_units.includes(:line_item).map(&:line_item).uniq
|
230
247
|
end
|
@@ -416,6 +433,8 @@ module Spree
|
|
416
433
|
end
|
417
434
|
|
418
435
|
def can_get_rates?
|
436
|
+
return true unless order.requires_ship_address?
|
437
|
+
|
419
438
|
order.ship_address&.valid?
|
420
439
|
end
|
421
440
|
|
@@ -39,22 +39,21 @@ module Spree
|
|
39
39
|
joins(:calculator).where(spree_calculators: { type: Spree::Calculator::Shipping::DigitalDelivery.to_s })
|
40
40
|
}
|
41
41
|
|
42
|
-
|
43
|
-
# full text search
|
44
|
-
include PgSearch::Model
|
45
|
-
pg_search_scope :search_by_name, against: %i[name]
|
46
|
-
else
|
47
|
-
scope :search_by_name, ->(query) { where('name LIKE ?', "%#{query}%") }
|
48
|
-
end
|
42
|
+
scope :search_by_name, ->(query) { where(arel_table[:name].lower.matches("%#{query}%")) }
|
49
43
|
|
50
44
|
def include?(address)
|
45
|
+
return true unless requires_zone_check?
|
51
46
|
return false unless address
|
52
47
|
|
53
|
-
zones.includes(zone_members
|
48
|
+
zones.includes(:zone_members).any? do |zone|
|
54
49
|
zone.include?(address)
|
55
50
|
end
|
56
51
|
end
|
57
52
|
|
53
|
+
def requires_zone_check?
|
54
|
+
!calculator.is_a?(Spree::Calculator::Shipping::DigitalDelivery)
|
55
|
+
end
|
56
|
+
|
58
57
|
def build_tracking_url(tracking)
|
59
58
|
return if tracking.blank?
|
60
59
|
|
@@ -93,7 +92,7 @@ module Spree
|
|
93
92
|
if estimated_transit_business_days_min == estimated_transit_business_days_max
|
94
93
|
estimated_transit_business_days_min.to_s
|
95
94
|
else
|
96
|
-
|
95
|
+
[estimated_transit_business_days_min, estimated_transit_business_days_max].compact.join("-")
|
97
96
|
end
|
98
97
|
end
|
99
98
|
|
data/app/models/spree/store.rb
CHANGED
@@ -16,13 +16,12 @@ module Spree
|
|
16
16
|
AUTHORIZE_ACTION = 'authorize'.freeze
|
17
17
|
ALLOCATION_ACTION = 'allocation'.freeze
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
belongs_to :store, class_name: 'Spree::Store'
|
21
20
|
belongs_to :user, class_name: "::#{Spree.user_class}", foreign_key: 'user_id'
|
22
21
|
belongs_to :category, class_name: 'Spree::StoreCreditCategory', optional: true
|
23
22
|
belongs_to :created_by, class_name: "::#{Spree.admin_user_class}", foreign_key: 'created_by_id', optional: true
|
24
23
|
belongs_to :credit_type, class_name: 'Spree::StoreCreditType', foreign_key: 'type_id', optional: true
|
25
|
-
belongs_to :
|
24
|
+
belongs_to :originator, polymorphic: true, optional: true
|
26
25
|
|
27
26
|
has_many :store_credit_events, class_name: 'Spree::StoreCreditEvent'
|
28
27
|
has_many :payments, as: :source, class_name: 'Spree::Payment'
|
@@ -34,14 +33,16 @@ module Spree
|
|
34
33
|
validate :amount_used_less_than_or_equal_to_amount
|
35
34
|
validate :amount_authorized_less_than_or_equal_to_amount
|
36
35
|
|
37
|
-
delegate :name, to: :category, prefix: true
|
38
|
-
delegate :email, to: :created_by, prefix: true
|
36
|
+
delegate :name, to: :category, prefix: true, allow_nil: true
|
37
|
+
delegate :email, to: :created_by, prefix: true, allow_nil: true
|
39
38
|
|
40
39
|
scope :order_by_priority, -> { includes(:credit_type).order('spree_store_credit_types.priority ASC') }
|
41
40
|
|
42
41
|
scope :not_authorized, -> { where(amount_authorized: 0) }
|
43
42
|
scope :not_used, -> { where("#{Spree::StoreCredit.table_name}.amount_used < #{Spree::StoreCredit.table_name}.amount") }
|
44
43
|
scope :available, -> { not_authorized.not_used }
|
44
|
+
scope :with_gift_card, -> { where(originator_type: 'Spree::GiftCard') }
|
45
|
+
scope :without_gift_card, -> { where(originator_type: [nil, '']).or(where.not(originator_type: 'Spree::GiftCard')) }
|
45
46
|
|
46
47
|
after_save :store_event
|
47
48
|
before_destroy :validate_no_amount_used
|
@@ -184,7 +185,9 @@ module Spree
|
|
184
185
|
|
185
186
|
class << self
|
186
187
|
def default_created_by
|
187
|
-
Spree.
|
188
|
+
Spree::Deprecation.warn('StoreCredit#default_created_by is deprecated and will be removed in Spree 6.0. Please use store.users.first instead.')
|
189
|
+
|
190
|
+
Spree::Store.current.users.first
|
188
191
|
end
|
189
192
|
end
|
190
193
|
|
@@ -2,9 +2,15 @@ module Spree
|
|
2
2
|
class StoreCreditEvent < Spree.base_class
|
3
3
|
acts_as_paranoid
|
4
4
|
|
5
|
+
#
|
6
|
+
# Associations
|
5
7
|
belongs_to :store_credit
|
6
8
|
belongs_to :originator, polymorphic: true
|
9
|
+
has_one :payment, -> { where(source_type: Spree::StoreCredit.to_s) }, foreign_key: :response_code, primary_key: :authorization_code
|
10
|
+
has_one :order, through: :payment
|
7
11
|
|
12
|
+
#
|
13
|
+
# Scopes
|
8
14
|
scope :exposed_events, -> { where.not(action: [Spree::StoreCredit::ELIGIBLE_ACTION, Spree::StoreCredit::AUTHORIZE_ACTION]) }
|
9
15
|
scope :reverse_chronological, -> { order(created_at: :desc) }
|
10
16
|
|
@@ -21,15 +27,13 @@ module Spree
|
|
21
27
|
Spree.t('store_credit.authorized')
|
22
28
|
when Spree::StoreCredit::ALLOCATION_ACTION
|
23
29
|
Spree.t('store_credit.allocated')
|
30
|
+
when Spree::StoreCredit::ELIGIBLE_ACTION
|
31
|
+
Spree.t('store_credit.eligible')
|
24
32
|
when Spree::StoreCredit::VOID_ACTION, Spree::StoreCredit::CREDIT_ACTION
|
25
33
|
Spree.t('store_credit.credit')
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
29
|
-
def order
|
30
|
-
store.payments.find_by(response_code: authorization_code).try(:order)
|
31
|
-
end
|
32
|
-
|
33
37
|
def allocation?
|
34
38
|
action == Spree::StoreCredit::ALLOCATION_ACTION
|
35
39
|
end
|
data/app/models/spree/taxon.rb
CHANGED
@@ -17,10 +17,13 @@ module Spree
|
|
17
17
|
include Spree::TranslatableResource
|
18
18
|
include Spree::TranslatableResourceSlug
|
19
19
|
include Spree::Metadata
|
20
|
+
include Spree::MemoizedData
|
20
21
|
if defined?(Spree::Webhooks::HasWebhooks)
|
21
22
|
include Spree::Webhooks::HasWebhooks
|
22
23
|
end
|
23
24
|
|
25
|
+
MEMOIZED_METHODS = %w[cached_self_and_descendants_ids].freeze
|
26
|
+
|
24
27
|
#
|
25
28
|
# Magic methods
|
26
29
|
#
|
@@ -154,6 +157,10 @@ module Spree
|
|
154
157
|
sort_order == 'manual'
|
155
158
|
end
|
156
159
|
|
160
|
+
def page_builder_image
|
161
|
+
square_image.presence || image
|
162
|
+
end
|
163
|
+
|
157
164
|
def active_products_with_descendants
|
158
165
|
@active_products_with_descendants ||= store.products.
|
159
166
|
joins(:classifications).
|
@@ -368,7 +375,7 @@ module Spree
|
|
368
375
|
end
|
369
376
|
|
370
377
|
def cached_self_and_descendants_ids
|
371
|
-
Rails.cache.fetch("#{cache_key_with_version}/descendant-ids") do
|
378
|
+
@cached_self_and_descendants_ids ||= Rails.cache.fetch("#{cache_key_with_version}/descendant-ids") do
|
372
379
|
self_and_descendants.ids
|
373
380
|
end
|
374
381
|
end
|
data/app/models/spree/theme.rb
CHANGED
@@ -155,7 +155,7 @@ module Spree
|
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
|
-
# Returns an array of available layout section classes for the theme
|
158
|
+
# Returns an array of available layout section classes for the theme, eg. header, footer, newsletter, etc.
|
159
159
|
#
|
160
160
|
# @return [Array<Class>]
|
161
161
|
def available_layout_sections
|
data/app/models/spree/variant.rb
CHANGED
@@ -82,7 +82,7 @@ module Spree
|
|
82
82
|
|
83
83
|
after_touch :clear_in_stock_cache
|
84
84
|
|
85
|
-
scope :in_stock, -> { left_joins(:stock_items).where("#{Spree::
|
85
|
+
scope :in_stock, -> { left_joins(:stock_items).where("#{Spree::Variant.table_name}.track_inventory = ? OR #{Spree::StockItem.table_name}.count_on_hand > ?", false, 0) }
|
86
86
|
scope :backorderable, -> { left_joins(:stock_items).where(spree_stock_items: { backorderable: true }) }
|
87
87
|
scope :in_stock_or_backorderable, -> { in_stock.or(backorderable) }
|
88
88
|
|
@@ -36,6 +36,13 @@ module Spree
|
|
36
36
|
@wished_items_count ||= variant_ids.count
|
37
37
|
end
|
38
38
|
|
39
|
+
# returns the variant ids in the wishlist
|
40
|
+
#
|
41
|
+
# @return [Array<Integer>]
|
42
|
+
def variant_ids
|
43
|
+
@variant_ids ||= wished_items.pluck(:variant_id)
|
44
|
+
end
|
45
|
+
|
39
46
|
def self.get_by_param(param)
|
40
47
|
find_by(token: param)
|
41
48
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Spree
|
2
2
|
module CSV
|
3
3
|
class ProductVariantPresenter
|
4
|
+
include Spree::ImagesHelper
|
5
|
+
|
4
6
|
CSV_HEADERS = [
|
5
7
|
'product_id',
|
6
8
|
'sku',
|
@@ -99,9 +101,9 @@ module Spree
|
|
99
101
|
variant.backorderable?,
|
100
102
|
variant.tax_category&.name,
|
101
103
|
variant.digital?,
|
102
|
-
variant.images[0]
|
103
|
-
variant.images[1]
|
104
|
-
variant.images[2]
|
104
|
+
spree_image_url(variant.images[0], image_url_options),
|
105
|
+
spree_image_url(variant.images[1], image_url_options),
|
106
|
+
spree_image_url(variant.images[2], image_url_options),
|
105
107
|
index.positive? ? option_type(0)&.presentation : nil,
|
106
108
|
index.positive? ? option_value(option_type(0)) : nil,
|
107
109
|
index.positive? ? option_type(1)&.presentation : nil,
|
@@ -125,6 +127,15 @@ module Spree
|
|
125
127
|
def option_value(option_type)
|
126
128
|
variant.option_values.find { |ov| ov.option_type == option_type }&.presentation
|
127
129
|
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def image_url_options
|
134
|
+
{
|
135
|
+
width: 1000,
|
136
|
+
height: 1000
|
137
|
+
}
|
138
|
+
end
|
128
139
|
end
|
129
140
|
end
|
130
141
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# This validator can be overridden to implement custom phone validation logic.
|
2
|
+
# Currently uses the phonelib gem to validate phone numbers and ensure they
|
3
|
+
# belong to the address country.
|
4
|
+
|
5
|
+
require 'phonelib'
|
6
|
+
|
7
|
+
module Spree
|
8
|
+
module Addresses
|
9
|
+
class PhoneValidator < ActiveModel::Validator
|
10
|
+
def validate(address)
|
11
|
+
return if !Spree::Config[:address_requires_phone] || address.phone.blank? || address.country.blank? || address.country_iso.blank?
|
12
|
+
|
13
|
+
phone = Phonelib.parse(address.phone)
|
14
|
+
unless phone.valid_for_country?(address.country_iso)
|
15
|
+
address.errors.add(:phone, :invalid)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -14,7 +14,7 @@ module Spree
|
|
14
14
|
private
|
15
15
|
|
16
16
|
def check_if_can_be_destroyed(order:)
|
17
|
-
return failure(false, Spree.t(:cannot_be_destroyed)) unless order&.
|
17
|
+
return failure(false, Spree.t(:cannot_be_destroyed)) unless order&.can_be_deleted?
|
18
18
|
|
19
19
|
success(order: order)
|
20
20
|
end
|
@@ -4,6 +4,7 @@ module Spree
|
|
4
4
|
prepend Spree::ServiceModule::Base
|
5
5
|
|
6
6
|
def call(order:, line_item:, line_item_created: false, options: {})
|
7
|
+
order.remove_gift_card if order.gift_card.present?
|
7
8
|
order_updater = ::Spree::OrderUpdater.new(order)
|
8
9
|
|
9
10
|
order.payments.store_credits.checkout.destroy_all
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Applies a gift card to an order
|
3
|
+
# under the hood it creates a store credit payment record and updates the gift card amount used
|
4
|
+
# @param gift_card [Spree::GiftCard] the gift card to apply
|
5
|
+
# @param order [Spree::Order] the order to apply the gift card to
|
6
|
+
# @return [Spree::Order] the order with the gift card applied
|
7
|
+
module Spree
|
8
|
+
module GiftCards
|
9
|
+
class Apply
|
10
|
+
prepend Spree::ServiceModule::Base
|
11
|
+
|
12
|
+
def call(gift_card:, order:)
|
13
|
+
# we shouldn't mix an order with a gift card and a store credit
|
14
|
+
return failure(:gift_card_using_store_credit_error) if order.using_store_credit?
|
15
|
+
|
16
|
+
# we shouldn't allow a gift card to be applied to an order with a different currency
|
17
|
+
return failure(:gift_card_mismatched_currency) if gift_card.currency != order.currency
|
18
|
+
|
19
|
+
amount = [gift_card.amount_remaining, order.total].min
|
20
|
+
store = order.store
|
21
|
+
|
22
|
+
return failure(:gift_card_no_amount_remaining) unless amount.positive? || order.total.zero?
|
23
|
+
|
24
|
+
payment_method = ensure_store_credit_payment_method!(store)
|
25
|
+
|
26
|
+
gift_card.lock!
|
27
|
+
order.with_lock do
|
28
|
+
store_credit = gift_card.store_credits.create!(
|
29
|
+
store: store,
|
30
|
+
user: order.user,
|
31
|
+
amount: amount,
|
32
|
+
currency: order.currency,
|
33
|
+
originator: gift_card,
|
34
|
+
action_originator: gift_card
|
35
|
+
)
|
36
|
+
gift_card.amount_used += amount
|
37
|
+
gift_card.save!
|
38
|
+
|
39
|
+
order.update!(gift_card: gift_card)
|
40
|
+
order.payments.create!(
|
41
|
+
source: store_credit,
|
42
|
+
payment_method: payment_method,
|
43
|
+
amount: amount,
|
44
|
+
state: 'checkout',
|
45
|
+
response_code: store_credit.generate_authorization_code
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
success(order.reload)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def ensure_store_credit_payment_method!(store)
|
55
|
+
payment_method = store.payment_methods.find_or_initialize_by(
|
56
|
+
type: 'Spree::PaymentMethod::StoreCredit'
|
57
|
+
)
|
58
|
+
payment_method.name ||= Spree.t(:store_credit_name)
|
59
|
+
payment_method.active = true
|
60
|
+
payment_method.save! if payment_method.new_record?
|
61
|
+
|
62
|
+
payment_method
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Spree
|
2
|
+
module GiftCards
|
3
|
+
class Redeem
|
4
|
+
prepend Spree::ServiceModule::Base
|
5
|
+
|
6
|
+
def call(gift_card:)
|
7
|
+
if gift_card.amount_remaining.zero?
|
8
|
+
gift_card.redeem!
|
9
|
+
else
|
10
|
+
gift_card.partial_redeem!
|
11
|
+
end
|
12
|
+
|
13
|
+
success(gift_card)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|