spree_core 5.4.0.beta2 → 5.4.0.beta3
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/finders/spree/products/find.rb +1 -14
- data/app/models/concerns/spree/product_scopes.rb +19 -2
- data/app/models/spree/api_key.rb +8 -5
- data/app/models/spree/market.rb +1 -2
- data/app/models/spree/market_country.rb +17 -0
- data/app/models/spree/newsletter_subscriber.rb +0 -3
- data/app/models/spree/order.rb +2 -5
- data/app/models/spree/product.rb +1 -13
- data/app/models/spree/reimbursement.rb +0 -2
- data/app/models/spree/shipment.rb +0 -1
- data/app/models/spree/shipment_handler.rb +0 -2
- data/app/services/spree/newsletter/subscribe.rb +2 -2
- data/app/subscribers/spree/invitation_email_subscriber.rb +1 -1
- data/config/locales/en.yml +1 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +0 -1
- data/lib/tasks/cli.rake +50 -0
- metadata +5 -14
- data/app/models/concerns/spree/filter_param.rb +0 -21
- data/app/models/spree/newsletter_subscriber/emails.rb +0 -12
- data/app/models/spree/order/emails.rb +0 -24
- data/app/models/spree/reimbursement/emails.rb +0 -11
- data/app/models/spree/shipment/emails.rb +0 -12
- data/lib/generators/spree/cursor_rules/cursor_rules_generator.rb +0 -19
- data/lib/generators/spree/cursor_rules/templates/spree_rules.mdc +0 -385
- data/lib/spree/core/importer/order.rb +0 -244
- data/lib/spree/core/importer/product.rb +0 -67
- data/lib/spree/core/importer.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea9fcfc9e4ac5937296c789f151d5d431b60a2fadb131e5a7bca034cae47b2ab
|
|
4
|
+
data.tar.gz: e89e3fd21bf14b0bb0bda0de42c0ecc49d173add3d9ce58c29b3c781eafb55fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b9bd748be9f9b4949d9b71887990fe3ac2377a6f4e7ed3d3060b0be073d6c199bd92a633bb2c62e7b8ee13dc0d7dae07e1bedd3d4d9dc5edebf3e4fc3839cae7
|
|
7
|
+
data.tar.gz: 8f8639ca080722372b01f91df71e54e0dc3f8f39d82cdbd1b3ad72103d995dbaf9bc50ef0f2a6032b0d400c2e94387edd2e06f64a52f3932391ed49ee5650014
|
|
@@ -22,14 +22,8 @@ module Spree
|
|
|
22
22
|
@in_stock = params.dig(:filter, :in_stock)
|
|
23
23
|
@backorderable = params.dig(:filter, :backorderable)
|
|
24
24
|
@purchasable = params.dig(:filter, :purchasable)
|
|
25
|
-
@out_of_stock = params.dig(:filter, :out_of_stock).to_b
|
|
26
25
|
@tags = params.dig(:filter, :tags).to_s.split(',').compact_blank
|
|
27
26
|
@vendor_ids = params.dig(:filter, :vendor_ids)&.split(',')&.compact_blank || []
|
|
28
|
-
|
|
29
|
-
if @purchasable.present? && @out_of_stock.present?
|
|
30
|
-
@purchasable = false
|
|
31
|
-
@out_of_stock = false
|
|
32
|
-
end
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
def execute
|
|
@@ -50,7 +44,6 @@ module Spree
|
|
|
50
44
|
products = show_only_stock(products)
|
|
51
45
|
products = show_only_backorderable(products)
|
|
52
46
|
products = show_only_purchasable(products)
|
|
53
|
-
products = show_only_out_of_stock(products)
|
|
54
47
|
products = by_taxonomies(products)
|
|
55
48
|
products = ordered(products)
|
|
56
49
|
products = by_vendor_ids(products)
|
|
@@ -62,7 +55,7 @@ module Spree
|
|
|
62
55
|
|
|
63
56
|
attr_reader :ids, :skus, :price, :currency, :taxons, :concat_taxons, :name, :options, :option_value_ids, :scope,
|
|
64
57
|
:sort_by, :deleted, :discontinued, :store, :in_stock, :backorderable, :purchasable, :tags,
|
|
65
|
-
:query, :vendor_ids, :
|
|
58
|
+
:query, :vendor_ids, :slug, :taxonomies
|
|
66
59
|
|
|
67
60
|
def query?
|
|
68
61
|
query.present?
|
|
@@ -294,12 +287,6 @@ module Spree
|
|
|
294
287
|
products.in_stock_or_backorderable
|
|
295
288
|
end
|
|
296
289
|
|
|
297
|
-
def show_only_out_of_stock(products)
|
|
298
|
-
return products unless out_of_stock.present?
|
|
299
|
-
|
|
300
|
-
products.out_of_stock
|
|
301
|
-
end
|
|
302
|
-
|
|
303
290
|
def map_prices(prices)
|
|
304
291
|
prices.map do |price|
|
|
305
292
|
price == 'Infinity' ? BigDecimal::INFINITY : price.to_f
|
|
@@ -81,9 +81,26 @@ module Spree
|
|
|
81
81
|
where(Price.table_name => { amount: price.. })
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
add_search_scope
|
|
85
|
-
|
|
84
|
+
# Can't use add_search_scope for this as it needs a default argument
|
|
85
|
+
# Ransack calls with '1' to activate, '0' or nil to skip
|
|
86
|
+
# In Ruby code: in_stock(true) for in-stock, in_stock(false) for out-of-stock
|
|
87
|
+
def self.in_stock(in_stock = true)
|
|
88
|
+
if in_stock == '0' || !in_stock
|
|
89
|
+
all
|
|
90
|
+
else
|
|
91
|
+
joins(:variants_including_master).merge(Spree::Variant.in_stock_or_backorderable)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
search_scopes << :in_stock
|
|
95
|
+
|
|
96
|
+
def self.out_of_stock(out_of_stock = true)
|
|
97
|
+
if out_of_stock == '0' || !out_of_stock
|
|
98
|
+
all
|
|
99
|
+
else
|
|
100
|
+
where.not(id: joins(:variants_including_master).merge(Spree::Variant.in_stock_or_backorderable))
|
|
101
|
+
end
|
|
86
102
|
end
|
|
103
|
+
search_scopes << :out_of_stock
|
|
87
104
|
|
|
88
105
|
add_search_scope :backorderable do
|
|
89
106
|
joins(:variants_including_master).merge(Spree::Variant.backorderable)
|
data/app/models/spree/api_key.rb
CHANGED
|
@@ -6,11 +6,14 @@ module Spree
|
|
|
6
6
|
PREFIXES = { 'publishable' => 'pk_', 'secret' => 'sk_' }.freeze
|
|
7
7
|
TOKEN_LENGTH = 24
|
|
8
8
|
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
9
|
+
# Returns the raw token value. For publishable keys this is the persisted
|
|
10
|
+
# +token+ column. For secret keys it is only available in memory immediately
|
|
11
|
+
# after creation (not persisted).
|
|
12
|
+
#
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
def plaintext_token
|
|
15
|
+
publishable? ? token : @plaintext_token
|
|
16
|
+
end
|
|
14
17
|
|
|
15
18
|
belongs_to :store, class_name: 'Spree::Store'
|
|
16
19
|
belongs_to :created_by, polymorphic: true, optional: true
|
data/app/models/spree/market.rb
CHANGED
|
@@ -44,8 +44,7 @@ module Spree
|
|
|
44
44
|
joins(:market_countries)
|
|
45
45
|
.where(store_id: store.id)
|
|
46
46
|
.where(spree_market_countries: { country_id: country.id })
|
|
47
|
-
.
|
|
48
|
-
.first
|
|
47
|
+
.take
|
|
49
48
|
end
|
|
50
49
|
|
|
51
50
|
# Returns the default market for a store, or falls back to the first by position
|
|
@@ -8,6 +8,7 @@ module Spree
|
|
|
8
8
|
validates :market, :country, presence: true
|
|
9
9
|
validates :country_id, uniqueness: { scope: :market_id }
|
|
10
10
|
validate :country_covered_by_shipping_zone
|
|
11
|
+
validate :country_unique_per_store
|
|
11
12
|
|
|
12
13
|
private
|
|
13
14
|
|
|
@@ -21,5 +22,21 @@ module Spree
|
|
|
21
22
|
errors.add(:country, :not_in_shipping_zone)
|
|
22
23
|
end
|
|
23
24
|
end
|
|
25
|
+
|
|
26
|
+
def country_unique_per_store
|
|
27
|
+
return if market.blank? || country.blank?
|
|
28
|
+
|
|
29
|
+
store = market.store
|
|
30
|
+
return if store.blank?
|
|
31
|
+
|
|
32
|
+
existing = self.class.joins(:market)
|
|
33
|
+
.where(country_id: country_id)
|
|
34
|
+
.where(spree_markets: { store_id: store.id, deleted_at: nil })
|
|
35
|
+
.where.not(id: id)
|
|
36
|
+
|
|
37
|
+
if existing.exists?
|
|
38
|
+
errors.add(:country, :already_in_market)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
24
41
|
end
|
|
25
42
|
end
|
data/app/models/spree/order.rb
CHANGED
|
@@ -3,7 +3,6 @@ require_dependency 'spree/order/currency_updater'
|
|
|
3
3
|
require_dependency 'spree/order/digital'
|
|
4
4
|
require_dependency 'spree/order/payments'
|
|
5
5
|
require_dependency 'spree/order/store_credit'
|
|
6
|
-
require_dependency 'spree/order/emails'
|
|
7
6
|
require_dependency 'spree/order/gift_card'
|
|
8
7
|
|
|
9
8
|
module Spree
|
|
@@ -22,7 +21,6 @@ module Spree
|
|
|
22
21
|
include Spree::Order::Payments
|
|
23
22
|
include Spree::Order::StoreCredit
|
|
24
23
|
include Spree::Order::AddressBook
|
|
25
|
-
include Spree::Order::Emails
|
|
26
24
|
include Spree::Order::Webhooks
|
|
27
25
|
include Spree::Core::NumberGenerator.new(prefix: 'R')
|
|
28
26
|
include Spree::Order::GiftCard
|
|
@@ -311,9 +309,9 @@ module Spree
|
|
|
311
309
|
# @return [Boolean]
|
|
312
310
|
def order_refunded?
|
|
313
311
|
return false if item_count.zero?
|
|
312
|
+
return false if refunds_total.zero?
|
|
314
313
|
|
|
315
|
-
|
|
316
|
-
refunds_total == total_minus_store_credits - additional_tax_total.abs
|
|
314
|
+
payment_state.in?(%w[void failed]) || refunds_total == total_minus_store_credits - additional_tax_total.abs
|
|
317
315
|
end
|
|
318
316
|
|
|
319
317
|
def refunds_total
|
|
@@ -965,7 +963,6 @@ module Spree
|
|
|
965
963
|
payments.store_credits.pending.each(&:void!)
|
|
966
964
|
end
|
|
967
965
|
|
|
968
|
-
send_cancel_email
|
|
969
966
|
update_with_updater!
|
|
970
967
|
send_order_canceled_webhook
|
|
971
968
|
publish_order_canceled_event
|
data/app/models/spree/product.rb
CHANGED
|
@@ -197,18 +197,6 @@ module Spree
|
|
|
197
197
|
scope :by_source, ->(source) { send(source) }
|
|
198
198
|
scope :paused, -> { where(status: 'paused') }
|
|
199
199
|
scope :published, -> { where(status: 'active') }
|
|
200
|
-
scope :in_stock_items, -> { joins(:variants).merge(Spree::Variant.in_stock_or_backorderable) }
|
|
201
|
-
scope :out_of_stock_items, lambda {
|
|
202
|
-
joins(variants_including_master: :stock_items).
|
|
203
|
-
where(spree_variants: { track_inventory: true }).
|
|
204
|
-
where.not(id: Spree::Variant.where(track_inventory: false).pluck(:product_id).uniq).
|
|
205
|
-
where(spree_stock_items: { backorderable: false }).
|
|
206
|
-
group(:id).
|
|
207
|
-
having("SUM(#{Spree::StockItem.table_name}.count_on_hand) <= 0")
|
|
208
|
-
}
|
|
209
|
-
scope :out_of_stock, lambda {
|
|
210
|
-
joins(:stock_items).where("#{Spree::Variant.table_name}.track_inventory = ? OR #{Spree::StockItem.table_name}.count_on_hand <= ?", false, 0)
|
|
211
|
-
}
|
|
212
200
|
|
|
213
201
|
attr_accessor :option_values_hash
|
|
214
202
|
|
|
@@ -232,7 +220,7 @@ module Spree
|
|
|
232
220
|
self.whitelisted_ransackable_associations = %w[taxons stores variants_including_master master variants tags labels
|
|
233
221
|
shipping_category classifications option_types]
|
|
234
222
|
self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name in_taxon price_between
|
|
235
|
-
multi_search
|
|
223
|
+
multi_search in_stock out_of_stock with_option_value_ids
|
|
236
224
|
ascend_by_price descend_by_price]
|
|
237
225
|
|
|
238
226
|
[
|
|
@@ -4,7 +4,6 @@ module Spree
|
|
|
4
4
|
|
|
5
5
|
include Spree::Core::NumberGenerator.new(prefix: 'RI', length: 9)
|
|
6
6
|
include Spree::NumberIdentifier
|
|
7
|
-
include Spree::Reimbursement::Emails
|
|
8
7
|
|
|
9
8
|
class IncompleteReimbursementError < StandardError; end
|
|
10
9
|
|
|
@@ -116,7 +115,6 @@ module Spree
|
|
|
116
115
|
if unpaid_amount_within_tolerance?
|
|
117
116
|
reimbursed!
|
|
118
117
|
reimbursement_success_hooks.each { |h| h.call self }
|
|
119
|
-
send_reimbursement_email
|
|
120
118
|
else
|
|
121
119
|
errored!
|
|
122
120
|
reimbursement_failure_hooks.each { |h| h.call self }
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class ShipmentHandler
|
|
3
|
-
include Spree::Shipment::Emails
|
|
4
3
|
include Spree::IntegrationsConcern
|
|
5
4
|
|
|
6
5
|
class << self
|
|
@@ -25,7 +24,6 @@ module Spree
|
|
|
25
24
|
@shipment.process_order_payments if Spree::Config[:auto_capture_on_dispatch]
|
|
26
25
|
@shipment.touch :shipped_at
|
|
27
26
|
update_order_shipment_state
|
|
28
|
-
send_shipped_email
|
|
29
27
|
end
|
|
30
28
|
|
|
31
29
|
protected
|
|
@@ -19,8 +19,8 @@ module Spree
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
#
|
|
23
|
-
subscriber.
|
|
22
|
+
# publish event to trigger email delivery via subscriber
|
|
23
|
+
subscriber.publish_event('newsletter_subscriber.subscribed') unless subscriber.verified?
|
|
24
24
|
subscriber
|
|
25
25
|
end
|
|
26
26
|
|
data/config/locales/en.yml
CHANGED
|
@@ -292,6 +292,7 @@ en:
|
|
|
292
292
|
spree/market_country:
|
|
293
293
|
attributes:
|
|
294
294
|
country:
|
|
295
|
+
already_in_market: is already assigned to another market in this store
|
|
295
296
|
not_in_shipping_zone: is not covered by any shipping zone. Please set up a shipping zone first.
|
|
296
297
|
spree/payment:
|
|
297
298
|
attributes:
|
data/lib/spree/core/version.rb
CHANGED
data/lib/spree/core.rb
CHANGED
|
@@ -427,7 +427,6 @@ require 'spree/events'
|
|
|
427
427
|
require 'spree/webhooks'
|
|
428
428
|
|
|
429
429
|
require 'spree/core/partials'
|
|
430
|
-
require 'spree/core/importer'
|
|
431
430
|
require 'spree/core/controller_helpers/auth'
|
|
432
431
|
require 'spree/core/controller_helpers/common'
|
|
433
432
|
require 'spree/core/controller_helpers/order'
|
data/lib/tasks/cli.rake
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
namespace :spree do
|
|
2
|
+
namespace :cli do
|
|
3
|
+
desc 'Ensure a default publishable API key exists and print its token'
|
|
4
|
+
task ensure_api_key: :environment do
|
|
5
|
+
store = Spree::Store.default
|
|
6
|
+
key = store.api_keys.active.publishable.first ||
|
|
7
|
+
store.api_keys.create!(name: 'Default', key_type: 'publishable')
|
|
8
|
+
print key.plaintext_token
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
desc 'Create an API key'
|
|
12
|
+
task create_api_key: :environment do
|
|
13
|
+
name = ENV.fetch('NAME')
|
|
14
|
+
key_type = ENV.fetch('KEY_TYPE')
|
|
15
|
+
store = Spree::Store.default
|
|
16
|
+
key = store.api_keys.create!(name: name, key_type: key_type)
|
|
17
|
+
print key.plaintext_token
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
desc 'List API keys (pipe-delimited)'
|
|
21
|
+
task list_api_keys: :environment do
|
|
22
|
+
Spree::Store.default.api_keys.order(created_at: :desc).each do |k|
|
|
23
|
+
status = k.revoked_at ? 'revoked' : 'active'
|
|
24
|
+
token = k.secret? ? k.token_prefix : k.token
|
|
25
|
+
puts [k.prefixed_id, k.name, k.key_type, token, k.created_at.strftime('%Y-%m-%d %H:%M'), status].join('|')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc 'Revoke an API key by prefixed ID'
|
|
30
|
+
task revoke_api_key: :environment do
|
|
31
|
+
id = ENV.fetch('ID')
|
|
32
|
+
key = Spree::Store.default.api_keys.find_by_prefix_id!(id)
|
|
33
|
+
key.revoke!
|
|
34
|
+
print key.name
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
desc 'Create an admin user'
|
|
38
|
+
task create_admin: :environment do
|
|
39
|
+
email = ENV.fetch('EMAIL')
|
|
40
|
+
password = ENV.fetch('PASSWORD')
|
|
41
|
+
admin = Spree.admin_user_class.create!(
|
|
42
|
+
email: email,
|
|
43
|
+
password: password,
|
|
44
|
+
password_confirmation: password
|
|
45
|
+
)
|
|
46
|
+
admin.add_role('admin', Spree::Store.default)
|
|
47
|
+
print admin.email
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.4.0.
|
|
4
|
+
version: 5.4.0.beta3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean Schofield
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2026-02
|
|
13
|
+
date: 2026-03-02 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: i18n-tasks
|
|
@@ -859,7 +859,6 @@ files:
|
|
|
859
859
|
- app/models/concerns/spree/default_price.rb
|
|
860
860
|
- app/models/concerns/spree/display_money.rb
|
|
861
861
|
- app/models/concerns/spree/display_on.rb
|
|
862
|
-
- app/models/concerns/spree/filter_param.rb
|
|
863
862
|
- app/models/concerns/spree/has_image_alt_text.rb
|
|
864
863
|
- app/models/concerns/spree/image_methods.rb
|
|
865
864
|
- app/models/concerns/spree/integrations_concern.rb
|
|
@@ -977,7 +976,6 @@ files:
|
|
|
977
976
|
- app/models/spree/metafields/rich_text.rb
|
|
978
977
|
- app/models/spree/metafields/short_text.rb
|
|
979
978
|
- app/models/spree/newsletter_subscriber.rb
|
|
980
|
-
- app/models/spree/newsletter_subscriber/emails.rb
|
|
981
979
|
- app/models/spree/option_type.rb
|
|
982
980
|
- app/models/spree/option_type_prototype.rb
|
|
983
981
|
- app/models/spree/option_value.rb
|
|
@@ -987,7 +985,6 @@ files:
|
|
|
987
985
|
- app/models/spree/order/checkout.rb
|
|
988
986
|
- app/models/spree/order/currency_updater.rb
|
|
989
987
|
- app/models/spree/order/digital.rb
|
|
990
|
-
- app/models/spree/order/emails.rb
|
|
991
988
|
- app/models/spree/order/gift_card.rb
|
|
992
989
|
- app/models/spree/order/payments.rb
|
|
993
990
|
- app/models/spree/order/store_credit.rb
|
|
@@ -1075,7 +1072,6 @@ files:
|
|
|
1075
1072
|
- app/models/spree/refund_reason.rb
|
|
1076
1073
|
- app/models/spree/reimbursement.rb
|
|
1077
1074
|
- app/models/spree/reimbursement/credit.rb
|
|
1078
|
-
- app/models/spree/reimbursement/emails.rb
|
|
1079
1075
|
- app/models/spree/reimbursement/reimbursement_type_engine.rb
|
|
1080
1076
|
- app/models/spree/reimbursement/reimbursement_type_validator.rb
|
|
1081
1077
|
- app/models/spree/reimbursement_performer.rb
|
|
@@ -1109,7 +1105,6 @@ files:
|
|
|
1109
1105
|
- app/models/spree/role_user.rb
|
|
1110
1106
|
- app/models/spree/shipment.rb
|
|
1111
1107
|
- app/models/spree/shipment/custom_events.rb
|
|
1112
|
-
- app/models/spree/shipment/emails.rb
|
|
1113
1108
|
- app/models/spree/shipment/webhooks.rb
|
|
1114
1109
|
- app/models/spree/shipment_handler.rb
|
|
1115
1110
|
- app/models/spree/shipping_calculator.rb
|
|
@@ -1453,8 +1448,6 @@ files:
|
|
|
1453
1448
|
- lib/generators/spree/authentication/dummy/dummy_generator.rb
|
|
1454
1449
|
- lib/generators/spree/authentication/dummy/templates/authentication_helpers.rb.tt
|
|
1455
1450
|
- lib/generators/spree/authentication/dummy/templates/create_spree_admin_users.rb.tt
|
|
1456
|
-
- lib/generators/spree/cursor_rules/cursor_rules_generator.rb
|
|
1457
|
-
- lib/generators/spree/cursor_rules/templates/spree_rules.mdc
|
|
1458
1451
|
- lib/generators/spree/dummy/dummy_generator.rb
|
|
1459
1452
|
- lib/generators/spree/dummy/templates/app/assets/config/manifest.js
|
|
1460
1453
|
- lib/generators/spree/dummy/templates/rails/application.rb
|
|
@@ -1486,9 +1479,6 @@ files:
|
|
|
1486
1479
|
- lib/spree/core/dependencies.rb
|
|
1487
1480
|
- lib/spree/core/dependencies_helper.rb
|
|
1488
1481
|
- lib/spree/core/engine.rb
|
|
1489
|
-
- lib/spree/core/importer.rb
|
|
1490
|
-
- lib/spree/core/importer/order.rb
|
|
1491
|
-
- lib/spree/core/importer/product.rb
|
|
1492
1482
|
- lib/spree/core/number_generator.rb
|
|
1493
1483
|
- lib/spree/core/partials.rb
|
|
1494
1484
|
- lib/spree/core/permission_configuration.rb
|
|
@@ -1634,6 +1624,7 @@ files:
|
|
|
1634
1624
|
- lib/spree/translation_migrations.rb
|
|
1635
1625
|
- lib/spree/webhooks.rb
|
|
1636
1626
|
- lib/spree_core.rb
|
|
1627
|
+
- lib/tasks/cli.rake
|
|
1637
1628
|
- lib/tasks/core.rake
|
|
1638
1629
|
- lib/tasks/dependencies.rake
|
|
1639
1630
|
- lib/tasks/images.rake
|
|
@@ -1662,9 +1653,9 @@ licenses:
|
|
|
1662
1653
|
- BSD-3-Clause
|
|
1663
1654
|
metadata:
|
|
1664
1655
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
1665
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.
|
|
1656
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.4.0.beta3
|
|
1666
1657
|
documentation_uri: https://docs.spreecommerce.org/
|
|
1667
|
-
source_code_uri: https://github.com/spree/spree/tree/v5.4.0.
|
|
1658
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.4.0.beta3
|
|
1668
1659
|
post_install_message:
|
|
1669
1660
|
rdoc_options: []
|
|
1670
1661
|
require_paths:
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
module FilterParam
|
|
3
|
-
extend ActiveSupport::Concern
|
|
4
|
-
|
|
5
|
-
included do
|
|
6
|
-
before_save :set_filter_param
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
protected
|
|
10
|
-
|
|
11
|
-
def set_filter_param
|
|
12
|
-
return if param_candidate.blank?
|
|
13
|
-
|
|
14
|
-
self.filter_param = param_candidate.parameterize
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def param_candidate
|
|
18
|
-
name
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
class NewsletterSubscriber < Spree.base_class
|
|
3
|
-
module Emails
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
def deliver_newsletter_email_verification
|
|
7
|
-
# you can overwrite this method in your application / extension to send out the confirmation email
|
|
8
|
-
# or use `spree_emails` gem
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
class Order < Spree.base_class
|
|
3
|
-
module Emails
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
def deliver_order_confirmation_email
|
|
7
|
-
Spree::Deprecation.warn('Spree::Order#deliver_order_confirmation_email is deprecated and will be removed in Spree 5.5. Please create a Subscriber for order.completed event.')
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
# If you would like to also send confirmation email to store owner(s)
|
|
11
|
-
def deliver_store_owner_order_notification_email?
|
|
12
|
-
false
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def deliver_store_owner_order_notification_email
|
|
16
|
-
Spree::Deprecation.warn('Spree::Order#deliver_store_owner_order_notification_email is deprecated and will be removed in Spree 5.5. Please create a Subscriber for order.completed event.')
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def send_cancel_email
|
|
20
|
-
Spree::Deprecation.warn('Spree::Order#send_cancel_email is deprecated and will be removed in Spree 5.5. Please create a Subscriber for order.canceled event.')
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
class Reimbursement < Spree.base_class
|
|
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
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
class Shipment < Spree.base_class
|
|
3
|
-
module Emails
|
|
4
|
-
def send_shipped_email
|
|
5
|
-
Spree::Deprecation.warn("Shipment#send_shipped_email is deprecated and will be removed in Spree 5.5. Please use events")
|
|
6
|
-
# you can overwrite this method in your application / extension to send out the confirmation email
|
|
7
|
-
# or use `spree_emails` gem
|
|
8
|
-
# YourEmailVendor.deliver_shipment_notification_email(@shipment.id)
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
class CursorRulesGenerator < Rails::Generators::Base
|
|
3
|
-
desc 'Set up Cursor Rules - copies all Spree cursor rules to .cursor/rules directory'
|
|
4
|
-
|
|
5
|
-
def self.source_paths
|
|
6
|
-
paths = superclass.source_paths
|
|
7
|
-
paths << File.expand_path('templates', __dir__)
|
|
8
|
-
paths.flatten
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def create_cursor_rules_directory
|
|
12
|
-
empty_directory '.cursor/rules'
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def copy_cursor_rules
|
|
16
|
-
copy_file 'spree_rules.mdc', '.cursor/rules/spree_rules.mdc'
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
alwaysApply: true
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Cursor Rules for Spree Commerce Development
|
|
6
|
-
|
|
7
|
-
## General Development Guidelines
|
|
8
|
-
|
|
9
|
-
### Framework & Architecture
|
|
10
|
-
|
|
11
|
-
- Spree is built on Ruby on Rails and follows MVC architecture
|
|
12
|
-
- All Spree code must be namespaced under `Spree::` module
|
|
13
|
-
- Spree is distributed as Rails engines with separate gems (core, admin, api, storefront, emails, etc.)
|
|
14
|
-
- Follow Rails conventions and the Rails Security Guide
|
|
15
|
-
- Prefer Rails idioms and standard patterns over custom solutions
|
|
16
|
-
|
|
17
|
-
### Code Organization
|
|
18
|
-
|
|
19
|
-
- Place all models in `app/models/spree/` directory
|
|
20
|
-
- Place all controllers in `app/controllers/spree/` directory
|
|
21
|
-
- Place all views in `app/views/spree/` directory
|
|
22
|
-
- Place all services in `app/services/spree/` directory
|
|
23
|
-
- Place all mailers in `app/mailers/spree/` directory
|
|
24
|
-
- Place all API serializers in `app/serializers/spree/` directory
|
|
25
|
-
- Place all helpers in `app/helpers/spree/` directory
|
|
26
|
-
- Place all jobs in `app/jobs/spree/` directory
|
|
27
|
-
- Place all presenters in `app/presenters/spree/` directory
|
|
28
|
-
- Use consistent file naming: `spree/product.rb` for `Spree::Product` class
|
|
29
|
-
- Group related functionality into concerns when appropriate
|
|
30
|
-
- Do not call `Spree::User` directly, use `Spree.user_class` instead
|
|
31
|
-
- Do not call `Spree::AdminUser` directly, use `Spree.admin_user_class` instead
|
|
32
|
-
|
|
33
|
-
## Naming Conventions & Structure
|
|
34
|
-
|
|
35
|
-
### Classes & Modules
|
|
36
|
-
|
|
37
|
-
```ruby
|
|
38
|
-
# ✅ Correct naming
|
|
39
|
-
module Spree
|
|
40
|
-
class Product < Spree.base_class
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
module Spree
|
|
45
|
-
module Admin
|
|
46
|
-
class ProductsController < ResourceController
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# ❌ Incorrect - missing namespace
|
|
52
|
-
class Product < ApplicationRecord
|
|
53
|
-
end
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Always inherit from `Spree.base_class` when creating models.
|
|
57
|
-
|
|
58
|
-
### File Paths
|
|
59
|
-
|
|
60
|
-
- Models: `app/models/spree/product.rb`
|
|
61
|
-
- Controllers: `app/controllers/spree/admin/products_controller.rb`
|
|
62
|
-
- Views: `app/views/spree/admin/products/`
|
|
63
|
-
- Decorators: `app/models/spree/product_decorator.rb`
|
|
64
|
-
|
|
65
|
-
## Model Development
|
|
66
|
-
|
|
67
|
-
### Model Patterns
|
|
68
|
-
|
|
69
|
-
- Use ActiveRecord associations appropriately, always pass `class_name` and `dependent` options
|
|
70
|
-
- Implement concerns for shared functionality
|
|
71
|
-
- Use scopes for reusable query patterns
|
|
72
|
-
- Include `Spree::Metafields` concern for models that need metadata support
|
|
73
|
-
|
|
74
|
-
```ruby
|
|
75
|
-
# ✅ Good model structure
|
|
76
|
-
class Spree::Product < Spree.base_class
|
|
77
|
-
include Spree::Metafields
|
|
78
|
-
|
|
79
|
-
has_many :variants, class_name: 'Spree::Variant', dependent: :destroy
|
|
80
|
-
|
|
81
|
-
scope :available, -> { where(available_on: ..Time.current) }
|
|
82
|
-
|
|
83
|
-
validates :name, presence: true
|
|
84
|
-
validates :slug, presence: true, uniqueness: { scope: spree_base_uniqueness_scope }
|
|
85
|
-
end
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
For uniqueness validation, always use `scope: spree_base_uniqueness_scope`
|
|
89
|
-
|
|
90
|
-
## Controller Development
|
|
91
|
-
|
|
92
|
-
### Controller Inheritance
|
|
93
|
-
|
|
94
|
-
- Admin controllers inherit from `Spree::Admin::ResourceController` which handles most of CRUD operations
|
|
95
|
-
- API controllers inherit from `Spree::Api::V2::BaseController`
|
|
96
|
-
- Storefront controllers inherit from `Spree::StoreController`
|
|
97
|
-
|
|
98
|
-
### Parameter Handling
|
|
99
|
-
|
|
100
|
-
- Always use strong parameters
|
|
101
|
-
|
|
102
|
-
```ruby
|
|
103
|
-
# ✅ Proper parameter handling
|
|
104
|
-
def permitted_product_params
|
|
105
|
-
params.require(:product).permit(:name, :description, :price)
|
|
106
|
-
end
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Customization & Extensions
|
|
110
|
-
|
|
111
|
-
### Spree::Dependencies System (Preferred Method)
|
|
112
|
-
|
|
113
|
-
Dependencies allow you to replace parts of Spree core with custom implementations. This is the preferred method for customization.
|
|
114
|
-
|
|
115
|
-
#### Global Customization
|
|
116
|
-
|
|
117
|
-
In `config/initializers/spree.rb`:
|
|
118
|
-
|
|
119
|
-
```ruby
|
|
120
|
-
# Single service replacement
|
|
121
|
-
Spree::Dependencies.cart_add_item_service = 'MyAddToCartService'
|
|
122
|
-
|
|
123
|
-
# Or using block syntax
|
|
124
|
-
Spree.dependencies do |dependencies|
|
|
125
|
-
dependencies.cart_add_item_service = 'MyAddToCartService'
|
|
126
|
-
dependencies.checkout_complete_service = 'MyCheckoutCompleteService'
|
|
127
|
-
end
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
#### API Level Customization
|
|
131
|
-
|
|
132
|
-
```ruby
|
|
133
|
-
# Storefront API specific
|
|
134
|
-
Spree::Api::Dependencies.storefront_cart_serializer = 'MyCartSerializer'
|
|
135
|
-
Spree::Api::Dependencies.storefront_cart_add_item_service = 'MyAddToCartService'
|
|
136
|
-
|
|
137
|
-
# Platform API specific
|
|
138
|
-
Spree::Api::Dependencies.platform_product_serializer = 'MyProductSerializer'
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
#### Service Implementation
|
|
142
|
-
|
|
143
|
-
```ruby
|
|
144
|
-
# ✅ Proper service inheritance
|
|
145
|
-
class MyAddToCartService < Spree::Cart::AddItem
|
|
146
|
-
def call(order:, variant:, quantity: nil, public_metadata: {}, private_metadata: {}, options: {})
|
|
147
|
-
ApplicationRecord.transaction do
|
|
148
|
-
run :add_to_line_item
|
|
149
|
-
run Spree.cart_recalculate_service
|
|
150
|
-
run :update_external_system
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
private
|
|
155
|
-
|
|
156
|
-
def update_external_system(result)
|
|
157
|
-
# Custom logic here
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
#### Available Injection Points
|
|
163
|
-
|
|
164
|
-
Common dependencies you can override:
|
|
165
|
-
- Cart services: `cart_add_item_service`, `cart_update_service`, `cart_remove_item_service`
|
|
166
|
-
- Checkout services: `checkout_next_service`, `checkout_complete_service`
|
|
167
|
-
- Order services: `order_approve_service`, `order_cancel_service`
|
|
168
|
-
- Payment services: `payment_create_service`, `payment_process_service`
|
|
169
|
-
- Ability classes: `ability_class`
|
|
170
|
-
- Serializers: Various API serializers for different endpoints
|
|
171
|
-
|
|
172
|
-
### Decorators (Use Sparingly)
|
|
173
|
-
|
|
174
|
-
Decorators should be a last resort - they make upgrades difficult. Use `Module.prepend` pattern for decorators.
|
|
175
|
-
|
|
176
|
-
#### Model Decorators
|
|
177
|
-
|
|
178
|
-
```ruby
|
|
179
|
-
# ✅ Proper decorator structure
|
|
180
|
-
module Spree
|
|
181
|
-
module ProductDecorator
|
|
182
|
-
def self.prepended(base)
|
|
183
|
-
base.has_many :videos, class_name: 'Spree::Video', dependent: :destroy
|
|
184
|
-
base.before_validation :strip_whitespaces
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
def custom_name
|
|
188
|
-
name.upcase
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
private
|
|
192
|
-
|
|
193
|
-
def strip_whitespaces
|
|
194
|
-
self.name = name.strip if name.present?
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
Product.prepend(ProductDecorator)
|
|
199
|
-
end
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
#### Controller Decorators
|
|
203
|
-
|
|
204
|
-
```ruby
|
|
205
|
-
# ✅ Controller decorator with dependency injection
|
|
206
|
-
module Spree
|
|
207
|
-
module Admin
|
|
208
|
-
module ProductsControllerDecorator
|
|
209
|
-
def self.prepended(base)
|
|
210
|
-
base.before_action :load_custom_data, only: [:show, :edit]
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
def custom_action
|
|
214
|
-
# Custom action implementation
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
private
|
|
218
|
-
|
|
219
|
-
def load_custom_data
|
|
220
|
-
@custom_data = fetch_custom_data
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
ProductsController.prepend(ProductsControllerDecorator)
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
### View Customization
|
|
230
|
-
|
|
231
|
-
#### Admin Panel Injection Points
|
|
232
|
-
|
|
233
|
-
Use partial injection for admin customization:
|
|
234
|
-
|
|
235
|
-
```ruby
|
|
236
|
-
# In config/initializers/spree.rb
|
|
237
|
-
Rails.application.config.spree_admin.head_partials << 'spree/admin/shared/custom_head'
|
|
238
|
-
Rails.application.config.spree_admin.body_end_partials << 'spree/admin/shared/custom_footer'
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
Available injection points:
|
|
242
|
-
- `head_partials` - Injects into `<head>` tag
|
|
243
|
-
- `body_start_partials` - Injects at start of `<body>`
|
|
244
|
-
- `body_end_partials` - Injects at end of `<body>`
|
|
245
|
-
|
|
246
|
-
#### Storefront Themes
|
|
247
|
-
|
|
248
|
-
Create custom themes for storefront customization:
|
|
249
|
-
|
|
250
|
-
```bash
|
|
251
|
-
bin/rails g spree:storefront:theme MyTheme
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
```ruby
|
|
255
|
-
# Register theme in config/initializers/spree.rb
|
|
256
|
-
Spree.page_builder.themes << Spree::Themes::MyTheme
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
#### View Overrides
|
|
260
|
-
|
|
261
|
-
Override specific views by creating files in your app:
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
app/views/themes/my_theme/spree/products/index.html.erb
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
Do not override views for admin, only for storefront. Avoid overriding any views for Checkout or Cart.
|
|
268
|
-
|
|
269
|
-
### Authentication Integration
|
|
270
|
-
|
|
271
|
-
```ruby
|
|
272
|
-
# In config/initializers/spree.rb
|
|
273
|
-
Spree.user_class = 'User'
|
|
274
|
-
Spree.admin_user_class = 'AdminUser'
|
|
275
|
-
|
|
276
|
-
# Custom authentication
|
|
277
|
-
Rails.application.config.to_prepare do
|
|
278
|
-
Spree::ApplicationController.include MyAuthenticationModule
|
|
279
|
-
end
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
## Testing Guidelines
|
|
283
|
-
|
|
284
|
-
### Test Structure
|
|
285
|
-
|
|
286
|
-
- Use RSpec for testing
|
|
287
|
-
- Place specs in `spec/` directory following Rails conventions
|
|
288
|
-
- Use Spree's testing helpers and factories
|
|
289
|
-
|
|
290
|
-
```ruby
|
|
291
|
-
# ✅ Proper test structure
|
|
292
|
-
require 'spec_helper'
|
|
293
|
-
|
|
294
|
-
RSpec.describe Spree::Product, type: :model do
|
|
295
|
-
let(:product) { create(:product) }
|
|
296
|
-
|
|
297
|
-
describe '#available?' do
|
|
298
|
-
it 'returns true when product is available' do
|
|
299
|
-
expect(product.available?).to be true
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### Factory Usage
|
|
306
|
-
|
|
307
|
-
```ruby
|
|
308
|
-
# Use Spree factories
|
|
309
|
-
create(:product, name: 'Test Product')
|
|
310
|
-
create(:order_with_line_items)
|
|
311
|
-
create(:user)
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
## Performance & Security
|
|
315
|
-
|
|
316
|
-
### Database Queries
|
|
317
|
-
|
|
318
|
-
- Use includes/joins to avoid N+1 queries
|
|
319
|
-
- Add database indexes for frequently queried fields
|
|
320
|
-
- Use counter caches for associations that are counted frequently
|
|
321
|
-
|
|
322
|
-
### Security
|
|
323
|
-
|
|
324
|
-
- Always use strong parameters in controllers
|
|
325
|
-
- Sanitize user input
|
|
326
|
-
- Use Spree's built-in authorization system (CanCanCan)
|
|
327
|
-
- Validate file uploads and restrict file types
|
|
328
|
-
|
|
329
|
-
## Common Patterns
|
|
330
|
-
|
|
331
|
-
### Service Objects
|
|
332
|
-
|
|
333
|
-
```ruby
|
|
334
|
-
# ✅ Spree service pattern
|
|
335
|
-
module Spree
|
|
336
|
-
class MyCustomService
|
|
337
|
-
prepend Spree::ServiceModule::Base
|
|
338
|
-
|
|
339
|
-
def call(param1:, param2: nil)
|
|
340
|
-
# Service logic here
|
|
341
|
-
success(result_data)
|
|
342
|
-
rescue StandardError => e
|
|
343
|
-
failure(e.message)
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
end
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### API Development
|
|
350
|
-
|
|
351
|
-
```ruby
|
|
352
|
-
# ✅ Custom API endpoint
|
|
353
|
-
module Spree
|
|
354
|
-
module Api
|
|
355
|
-
module V2
|
|
356
|
-
class CustomController < Spree::Api::V2::BaseController
|
|
357
|
-
def index
|
|
358
|
-
render json: serialized_collection
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
private
|
|
362
|
-
|
|
363
|
-
def serialized_collection
|
|
364
|
-
Spree.api.storefront_product_serializer.new(
|
|
365
|
-
collection,
|
|
366
|
-
include: resource_includes,
|
|
367
|
-
fields: sparse_fields
|
|
368
|
-
).serializable_hash
|
|
369
|
-
end
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
end
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
## Avoid These Patterns
|
|
377
|
-
|
|
378
|
-
❌ Direct inheritance from Rails classes without Spree namespace
|
|
379
|
-
❌ Monkey patching without using decorators or dependencies
|
|
380
|
-
❌ Hard-coding configuration values
|
|
381
|
-
❌ Direct SQL queries without using ActiveRecord
|
|
382
|
-
❌ Creating models outside Spree namespace when extending core functionality
|
|
383
|
-
❌ Using class_eval decorators (use Module.prepend instead)
|
|
384
|
-
❌ Overriding entire view files when partial injection would work
|
|
385
|
-
❌ Modifying core Spree files directly
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
module Core
|
|
3
|
-
module Importer
|
|
4
|
-
class Order
|
|
5
|
-
def self.import(user, params)
|
|
6
|
-
Spree::Deprecation.warn('Spree::Core::Importer::Order is deprecated and will be removed in Spree 5.5. Please use `Spree::Imports::Order` instead.')
|
|
7
|
-
|
|
8
|
-
ensure_country_id_from_params params[:ship_address_attributes]
|
|
9
|
-
ensure_state_id_from_params params[:ship_address_attributes]
|
|
10
|
-
ensure_country_id_from_params params[:bill_address_attributes]
|
|
11
|
-
ensure_state_id_from_params params[:bill_address_attributes]
|
|
12
|
-
|
|
13
|
-
create_params = params.slice :currency
|
|
14
|
-
order = Spree::Order.create! create_params
|
|
15
|
-
order.associate_user!(user)
|
|
16
|
-
|
|
17
|
-
shipments_attrs = params.delete(:shipments_attributes)
|
|
18
|
-
|
|
19
|
-
create_line_items_from_params(params.delete(:line_items_attributes), order)
|
|
20
|
-
create_shipments_from_params(shipments_attrs, order)
|
|
21
|
-
create_adjustments_from_params(params.delete(:adjustments_attributes), order)
|
|
22
|
-
create_payments_from_params(params.delete(:payments_attributes), order)
|
|
23
|
-
|
|
24
|
-
if completed_at = params.delete(:completed_at)
|
|
25
|
-
order.completed_at = completed_at
|
|
26
|
-
order.state = 'complete'
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
params.delete(:user_id) unless user.try(:has_spree_role?, 'admin') && params.key?(:user_id)
|
|
30
|
-
|
|
31
|
-
order.update!(params)
|
|
32
|
-
|
|
33
|
-
order.create_proposed_shipments unless shipments_attrs.present?
|
|
34
|
-
|
|
35
|
-
# Really ensure that the order totals & states are correct
|
|
36
|
-
order.updater.update
|
|
37
|
-
if shipments_attrs.present?
|
|
38
|
-
order.shipments.each_with_index do |shipment, index|
|
|
39
|
-
shipment.update_columns(cost: shipments_attrs[index][:cost].to_f) if shipments_attrs[index][:cost].present?
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
order.reload
|
|
43
|
-
rescue StandardError => e
|
|
44
|
-
order.destroy if order&.persisted?
|
|
45
|
-
raise e.message
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def self.create_shipments_from_params(shipments_hash, order)
|
|
49
|
-
return [] unless shipments_hash
|
|
50
|
-
|
|
51
|
-
shipments_hash.each do |s|
|
|
52
|
-
shipment = order.shipments.build
|
|
53
|
-
shipment.tracking = s[:tracking]
|
|
54
|
-
shipment.stock_location = Spree::StockLocation.find_by(admin_name: s[:stock_location]) ||
|
|
55
|
-
Spree::StockLocation.find_by!(name: s[:stock_location])
|
|
56
|
-
inventory_units = create_inventory_units_from_order_and_params(order, s[:inventory_units])
|
|
57
|
-
|
|
58
|
-
inventory_units.each do |inventory_unit|
|
|
59
|
-
inventory_unit.shipment = shipment
|
|
60
|
-
|
|
61
|
-
if s[:shipped_at].present?
|
|
62
|
-
inventory_unit.pending = false
|
|
63
|
-
inventory_unit.state = 'shipped'
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
inventory_unit.save!
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
if s[:shipped_at].present?
|
|
70
|
-
shipment.shipped_at = s[:shipped_at]
|
|
71
|
-
shipment.state = 'shipped'
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
shipment.save!
|
|
75
|
-
|
|
76
|
-
shipping_method = Spree::ShippingMethod.find_by(name: s[:shipping_method]) ||
|
|
77
|
-
Spree::ShippingMethod.find_by!(admin_name: s[:shipping_method])
|
|
78
|
-
rate = shipment.shipping_rates.create!(shipping_method: shipping_method, cost: s[:cost])
|
|
79
|
-
|
|
80
|
-
shipment.selected_shipping_rate_id = rate.id
|
|
81
|
-
shipment.update_amounts
|
|
82
|
-
|
|
83
|
-
adjustments = s.delete(:adjustments_attributes)
|
|
84
|
-
create_adjustments_from_params(adjustments, order, shipment)
|
|
85
|
-
rescue StandardError => e
|
|
86
|
-
raise "Order import shipments: #{e.message} #{s}"
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def self.create_inventory_units_from_order_and_params(order, inventory_unit_params)
|
|
91
|
-
inventory_unit_params.each_with_object([]) do |inventory_unit_param, inventory_units|
|
|
92
|
-
ensure_variant_id_from_params(inventory_unit_param)
|
|
93
|
-
existing = inventory_units.detect { |unit| unit.variant_id == inventory_unit_param[:variant_id] }
|
|
94
|
-
if existing
|
|
95
|
-
existing.quantity += 1
|
|
96
|
-
else
|
|
97
|
-
line_item = order.line_items.detect { |ln| ln.variant_id == inventory_unit_param[:variant_id] }
|
|
98
|
-
inventory_units << InventoryUnit.new(line_item: line_item, order_id: order.id, variant: line_item.variant, quantity: 1)
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def self.create_line_items_from_params(line_items, order)
|
|
104
|
-
return {} unless line_items
|
|
105
|
-
|
|
106
|
-
line_items.each do |line_item|
|
|
107
|
-
adjustments = line_item.delete(:adjustments_attributes)
|
|
108
|
-
extra_params = line_item.except(:variant_id, :quantity, :sku)
|
|
109
|
-
line_item = ensure_variant_id_from_params(line_item)
|
|
110
|
-
variant = Spree::Variant.find(line_item[:variant_id])
|
|
111
|
-
line_item = Cart::AddItem.call(order: order, variant: variant, quantity: line_item[:quantity]).value
|
|
112
|
-
# Raise any errors with saving to prevent import succeeding with line items
|
|
113
|
-
# failing silently.
|
|
114
|
-
if extra_params.present?
|
|
115
|
-
line_item.update!(extra_params)
|
|
116
|
-
else
|
|
117
|
-
line_item.save!
|
|
118
|
-
end
|
|
119
|
-
create_adjustments_from_params(adjustments, order, line_item)
|
|
120
|
-
rescue StandardError => e
|
|
121
|
-
raise "Order import line items: #{e.message} #{line_item}"
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def self.create_adjustments_from_params(adjustments, order, adjustable = nil)
|
|
126
|
-
return [] unless adjustments
|
|
127
|
-
|
|
128
|
-
adjustments.each do |a|
|
|
129
|
-
adjustment = (adjustable || order).adjustments.build(
|
|
130
|
-
order: order,
|
|
131
|
-
amount: a[:amount].to_f,
|
|
132
|
-
label: a[:label],
|
|
133
|
-
source_type: source_type_from_adjustment(a)
|
|
134
|
-
)
|
|
135
|
-
adjustment.save!
|
|
136
|
-
adjustment.close!
|
|
137
|
-
rescue StandardError => e
|
|
138
|
-
raise "Order import adjustments: #{e.message} #{a}"
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def self.create_payments_from_params(payments_hash, order)
|
|
143
|
-
return [] unless payments_hash
|
|
144
|
-
|
|
145
|
-
payments_hash.each do |p|
|
|
146
|
-
payment = order.payments.build order: order
|
|
147
|
-
payment.amount = p[:amount].to_f
|
|
148
|
-
# Order API should be using state as that's the normal payment field.
|
|
149
|
-
# spree_wombat serializes payment state as status so imported orders should fall back to status field.
|
|
150
|
-
payment.state = p[:state] || p[:status] || 'completed'
|
|
151
|
-
payment.created_at = p[:created_at] if p[:created_at]
|
|
152
|
-
payment.payment_method = Spree::PaymentMethod.find_by!(name: p[:payment_method])
|
|
153
|
-
payment.source = create_source_payment_from_params(p[:source], payment) if p[:source]
|
|
154
|
-
payment.save!
|
|
155
|
-
rescue StandardError => e
|
|
156
|
-
raise "Order import payments: #{e.message} #{p}"
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def self.create_source_payment_from_params(source_hash, payment)
|
|
161
|
-
Spree::CreditCard.create(
|
|
162
|
-
month: source_hash[:month],
|
|
163
|
-
year: source_hash[:year],
|
|
164
|
-
cc_type: source_hash[:cc_type],
|
|
165
|
-
last_digits: source_hash[:last_digits],
|
|
166
|
-
name: source_hash[:name],
|
|
167
|
-
payment_method: payment.payment_method,
|
|
168
|
-
gateway_customer_profile_id: source_hash[:gateway_customer_profile_id],
|
|
169
|
-
gateway_payment_profile_id: source_hash[:gateway_payment_profile_id],
|
|
170
|
-
imported: true
|
|
171
|
-
)
|
|
172
|
-
rescue StandardError => e
|
|
173
|
-
raise "Order import source payments: #{e.message} #{source_hash}"
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def self.ensure_variant_id_from_params(hash)
|
|
177
|
-
sku = hash.delete(:sku)
|
|
178
|
-
unless hash[:variant_id].present?
|
|
179
|
-
hash[:variant_id] = Spree::Variant.active.find_by!(sku: sku).id
|
|
180
|
-
end
|
|
181
|
-
hash
|
|
182
|
-
rescue ActiveRecord::RecordNotFound => e
|
|
183
|
-
raise "Ensure order import variant: Variant w/SKU #{sku} not found."
|
|
184
|
-
rescue StandardError => e
|
|
185
|
-
raise "Ensure order import variant: #{e.message} #{hash}"
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def self.ensure_country_id_from_params(address)
|
|
189
|
-
return if address.nil? || address[:country_id].present? || address[:country].nil?
|
|
190
|
-
|
|
191
|
-
begin
|
|
192
|
-
search = {}
|
|
193
|
-
if name = address[:country]['name']
|
|
194
|
-
search[:name] = name
|
|
195
|
-
elsif iso_name = address[:country]['iso_name']
|
|
196
|
-
search[:iso_name] = iso_name.upcase
|
|
197
|
-
elsif iso = address[:country]['iso']
|
|
198
|
-
search[:iso] = iso.upcase
|
|
199
|
-
elsif iso3 = address[:country]['iso3']
|
|
200
|
-
search[:iso3] = iso3.upcase
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
address.delete(:country)
|
|
204
|
-
address[:country_id] = Spree::Country.where(search).first!.id
|
|
205
|
-
rescue StandardError => e
|
|
206
|
-
raise "Ensure order import address country: #{e.message} #{search}"
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def self.ensure_state_id_from_params(address)
|
|
211
|
-
return if address.nil? || address[:state_id].present? || address[:state].nil?
|
|
212
|
-
|
|
213
|
-
begin
|
|
214
|
-
search = {}
|
|
215
|
-
if name = address[:state]['name']
|
|
216
|
-
search[:name] = name
|
|
217
|
-
elsif abbr = address[:state]['abbr']
|
|
218
|
-
search[:abbr] = abbr.upcase
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
address.delete(:state)
|
|
222
|
-
search[:country_id] = address[:country_id]
|
|
223
|
-
|
|
224
|
-
if state = Spree::State.where(search).first
|
|
225
|
-
address[:state_id] = state.id
|
|
226
|
-
else
|
|
227
|
-
address[:state_name] = search[:name] || search[:abbr]
|
|
228
|
-
end
|
|
229
|
-
rescue StandardError => e
|
|
230
|
-
raise "Ensure order import address state: #{e.message} #{search}"
|
|
231
|
-
end
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def self.source_type_from_adjustment(adjustment)
|
|
235
|
-
if adjustment[:tax]
|
|
236
|
-
'Spree::TaxRate'
|
|
237
|
-
elsif adjustment[:promotion]
|
|
238
|
-
'Spree::PromotionAction'
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
end
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
module Core
|
|
3
|
-
module Importer
|
|
4
|
-
class Product
|
|
5
|
-
attr_reader :product, :product_attrs, :variants_attrs, :options_attrs, :store
|
|
6
|
-
|
|
7
|
-
def initialize(product, product_params, options = {})
|
|
8
|
-
Spree::Deprecation.warn('Spree::Core::Importer::Product is deprecated and will be removed in Spree 5.5. Please use `Spree::Imports::Product` instead.')
|
|
9
|
-
|
|
10
|
-
@store = options[:store] || Spree::Store.default
|
|
11
|
-
@product = product || Spree::Product.new(product_params)
|
|
12
|
-
@product.stores << @store if @product.stores.exclude?(@store)
|
|
13
|
-
|
|
14
|
-
@product_attrs = product_params.to_h
|
|
15
|
-
@variants_attrs = (options[:variants_attrs] || []).map(&:to_h)
|
|
16
|
-
@options_attrs = options[:options_attrs] || []
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def create
|
|
20
|
-
if product.save
|
|
21
|
-
variants_attrs.each do |variant_attribute|
|
|
22
|
-
# make sure the product is assigned before the options=
|
|
23
|
-
product.variants.create({ product: product }.merge(variant_attribute))
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
set_up_options
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
product
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def update
|
|
33
|
-
if product.update(product_attrs)
|
|
34
|
-
variants_attrs.each do |variant_attribute|
|
|
35
|
-
# update the variant if the id is present in the payload
|
|
36
|
-
if variant_attribute['id'].present?
|
|
37
|
-
product.variants.find(variant_attribute['id']).update(variant_attribute)
|
|
38
|
-
else
|
|
39
|
-
# make sure the product is assigned before the options=
|
|
40
|
-
product.variants.create({ product: product }.merge(variant_attribute))
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
set_up_options
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
product
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
def set_up_options
|
|
53
|
-
options_attrs.each do |name|
|
|
54
|
-
option_type = Spree::OptionType.where(name: name).first_or_initialize do |option_type|
|
|
55
|
-
option_type.presentation = name
|
|
56
|
-
option_type.save!
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
unless product.option_types.include?(option_type)
|
|
60
|
-
product.option_types << option_type
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|