spree_core 4.4.1 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/products/find.rb +1 -3
  3. data/app/finders/spree/taxons/find.rb +1 -1
  4. data/app/helpers/spree/base_helper.rb +12 -9
  5. data/app/helpers/spree/locale_helper.rb +6 -2
  6. data/app/helpers/spree/products_helper.rb +9 -4
  7. data/app/jobs/spree/variants/remove_from_incomplete_orders_job.rb +9 -0
  8. data/app/jobs/spree/variants/remove_line_item_job.rb +9 -0
  9. data/app/models/concerns/spree/calculated_adjustments.rb +1 -1
  10. data/app/models/concerns/spree/display_link.rb +17 -29
  11. data/app/models/concerns/spree/image_methods.rb +21 -9
  12. data/app/models/concerns/spree/metadata.rb +2 -2
  13. data/app/models/concerns/spree/product_scopes.rb +10 -8
  14. data/app/models/spree/address.rb +7 -1
  15. data/app/models/spree/asset/support/active_storage.rb +3 -2
  16. data/app/models/spree/asset.rb +3 -0
  17. data/app/models/spree/cms_page.rb +4 -0
  18. data/app/models/spree/cms_section.rb +12 -12
  19. data/app/models/spree/cms_section_image.rb +15 -0
  20. data/app/models/spree/cms_section_image_one.rb +4 -0
  21. data/app/models/spree/cms_section_image_three.rb +4 -0
  22. data/app/models/spree/cms_section_image_two.rb +4 -0
  23. data/app/models/spree/credit_card.rb +10 -4
  24. data/app/models/spree/customer_return.rb +3 -0
  25. data/app/models/spree/digital.rb +4 -0
  26. data/app/models/spree/digital_link.rb +7 -0
  27. data/app/models/spree/gateway/bogus.rb +1 -1
  28. data/app/models/spree/icon.rb +5 -1
  29. data/app/models/spree/image/configuration/active_storage.rb +5 -1
  30. data/app/models/spree/image.rb +3 -3
  31. data/app/models/spree/inventory_unit.rb +5 -2
  32. data/app/models/spree/legacy_user.rb +1 -2
  33. data/app/models/spree/line_item.rb +4 -1
  34. data/app/models/spree/linkable/homepage.rb +3 -0
  35. data/app/models/spree/linkable/uri.rb +3 -0
  36. data/app/models/spree/log_entry.rb +4 -0
  37. data/app/models/spree/menu.rb +3 -0
  38. data/app/models/spree/menu_item.rb +7 -11
  39. data/app/models/spree/option_type.rb +4 -0
  40. data/app/models/spree/option_value.rb +5 -0
  41. data/app/models/spree/order/address_book.rb +1 -0
  42. data/app/models/spree/order/store_credit.rb +0 -8
  43. data/app/models/spree/order.rb +11 -2
  44. data/app/models/spree/order_merger.rb +1 -1
  45. data/app/models/spree/payment/processing.rb +1 -1
  46. data/app/models/spree/payment.rb +7 -1
  47. data/app/models/spree/payment_capture_event.rb +4 -0
  48. data/app/models/spree/payment_method/store_credit.rb +1 -1
  49. data/app/models/spree/payment_method.rb +3 -0
  50. data/app/models/spree/payment_source.rb +10 -0
  51. data/app/models/spree/preference.rb +4 -0
  52. data/app/models/spree/price.rb +3 -0
  53. data/app/models/spree/product.rb +56 -14
  54. data/app/models/spree/product_property.rb +1 -0
  55. data/app/models/spree/promotion/rules/option_value.rb +2 -2
  56. data/app/models/spree/promotion.rb +6 -0
  57. data/app/models/spree/promotion_rule.rb +1 -1
  58. data/app/models/spree/promotion_rule_user.rb +1 -1
  59. data/app/models/spree/property.rb +3 -0
  60. data/app/models/spree/prototype.rb +3 -0
  61. data/app/models/spree/refund.rb +8 -0
  62. data/app/models/spree/reimbursement.rb +3 -0
  63. data/app/models/spree/return_authorization.rb +3 -0
  64. data/app/models/spree/return_item.rb +4 -0
  65. data/app/models/spree/role.rb +1 -1
  66. data/app/models/spree/role_user.rb +1 -1
  67. data/app/models/spree/shipment.rb +7 -0
  68. data/app/models/spree/shipping_category.rb +3 -0
  69. data/app/models/spree/shipping_method.rb +6 -0
  70. data/app/models/spree/state_change.rb +1 -1
  71. data/app/models/spree/stock/availability_validator.rb +9 -3
  72. data/app/models/spree/stock/content_item.rb +1 -1
  73. data/app/models/spree/stock/quantifier.rb +1 -1
  74. data/app/models/spree/stock_item.rb +5 -0
  75. data/app/models/spree/stock_location.rb +19 -5
  76. data/app/models/spree/stock_movement.rb +4 -0
  77. data/app/models/spree/stock_transfer.rb +3 -0
  78. data/app/models/spree/store.rb +19 -14
  79. data/app/models/spree/store_credit.rb +4 -1
  80. data/app/models/spree/store_favicon_image.rb +17 -0
  81. data/app/models/spree/store_logo.rb +9 -0
  82. data/app/models/spree/store_mailer_logo.rb +13 -0
  83. data/app/models/spree/tax_category.rb +6 -0
  84. data/app/models/spree/tax_rate.rb +6 -1
  85. data/app/models/spree/taxon.rb +4 -1
  86. data/app/models/spree/taxon_image/configuration/active_storage.rb +5 -1
  87. data/app/models/spree/taxon_image.rb +3 -2
  88. data/app/models/spree/taxonomy.rb +4 -1
  89. data/app/models/spree/variant.rb +43 -14
  90. data/app/models/spree/wished_item.rb +4 -0
  91. data/app/models/spree/wishlist.rb +4 -1
  92. data/app/models/spree/zone.rb +3 -0
  93. data/app/services/spree/addresses/create.rb +1 -1
  94. data/app/services/spree/addresses/update.rb +7 -2
  95. data/app/services/spree/cart/remove_line_item.rb +1 -0
  96. data/app/services/spree/variants/remove_line_items.rb +15 -0
  97. data/config/locales/en.yml +5 -2
  98. data/config/routes.rb +43 -0
  99. data/db/migrate/20211201202851_update_linkable_resource_types.rb +10 -0
  100. data/db/migrate/20211203082008_add_settings_to_payment_methods.rb +11 -0
  101. data/db/migrate/20211229162122_disable_propagate_all_variants_by_default.rb +5 -0
  102. data/db/migrate/20220103082046_add_status_and_make_active_at_to_spree_products.rb +7 -0
  103. data/db/migrate/20220106230929_add_internal_note_to_spree_orders.rb +5 -0
  104. data/db/migrate/20220113052823_create_payment_sources.rb +22 -0
  105. data/db/migrate/20220117100333_add_make_active_at_to_spree_products.rb +17 -0
  106. data/db/migrate/20220120092821_add_metadata_to_spree_tax_rates.rb +13 -0
  107. data/db/migrate/20220201103922_add_first_name_and_last_name_to_spree_users.rb +9 -0
  108. data/db/migrate/20220222083546_add_barcode_to_spree_variants.rb +6 -0
  109. data/db/migrate/20220329113557_fix_cms_pages_unique_indexes.rb +8 -0
  110. data/db/migrate/20220613133029_add_metadata_to_spree_stock_items.rb +13 -0
  111. data/lib/friendly_id/paranoia.rb +4 -0
  112. data/lib/generators/spree/dummy/dummy_generator.rb +13 -2
  113. data/lib/generators/spree/dummy/templates/package.json +12 -0
  114. data/lib/generators/spree/dummy/templates/rails/test.rb +2 -0
  115. data/lib/spree/core/configuration.rb +90 -0
  116. data/lib/spree/core/controller_helpers/auth.rb +3 -1
  117. data/lib/spree/core/controller_helpers/currency.rb +7 -5
  118. data/lib/spree/core/controller_helpers/locale.rb +8 -6
  119. data/lib/spree/core/controller_helpers/order.rb +4 -2
  120. data/lib/spree/core/controller_helpers/search.rb +1 -1
  121. data/lib/spree/core/controller_helpers/store.rb +5 -3
  122. data/lib/spree/core/dependencies.rb +130 -0
  123. data/lib/spree/core/engine.rb +47 -37
  124. data/{app/models/spree → lib/spree/core}/preferences/configuration.rb +3 -0
  125. data/{app/models/spree → lib/spree/core}/preferences/preferable.rb +3 -0
  126. data/lib/spree/core/search/base.rb +9 -5
  127. data/lib/spree/core/version.rb +1 -1
  128. data/lib/spree/core.rb +25 -5
  129. data/lib/spree/permitted_attributes.rb +9 -7
  130. data/lib/spree/testing_support/common_rake.rb +1 -0
  131. data/lib/spree/testing_support/factories/favicon_image_factory.rb +9 -0
  132. data/lib/spree/testing_support/factories/icon_factory.rb +3 -1
  133. data/lib/spree/testing_support/factories/image_factory.rb +3 -1
  134. data/lib/spree/testing_support/factories/menu_item_factory.rb +1 -1
  135. data/lib/spree/testing_support/factories/order_factory.rb +2 -1
  136. data/lib/spree/testing_support/factories/product_factory.rb +6 -1
  137. data/lib/spree/testing_support/factories/product_property_factory.rb +1 -0
  138. data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
  139. data/lib/spree/testing_support/factories/return_authorization_factory.rb +1 -1
  140. data/lib/spree/testing_support/factories/role_factory.rb +1 -1
  141. data/lib/spree/testing_support/factories/shipping_category_factory.rb +1 -1
  142. data/lib/spree/testing_support/factories/stock_location_factory.rb +3 -2
  143. data/lib/spree/testing_support/factories/store_factory.rb +1 -1
  144. data/lib/spree/testing_support/factories/taxon_image_factory.rb +3 -1
  145. data/lib/spree/testing_support/factories/user_factory.rb +4 -0
  146. data/lib/spree/testing_support/factories/variant_factory.rb +4 -0
  147. data/lib/spree_core.rb +2 -1
  148. data/lib/tasks/core.rake +12 -0
  149. data/spree_core.gemspec +2 -3
  150. metadata +64 -53
  151. data/app/models/friendly_id/slug_decorator.rb +0 -9
  152. data/app/models/spree/app_configuration.rb +0 -86
  153. data/app/models/spree/order_contents.rb +0 -31
  154. data/lib/friendly_id/slug_rails5_patch.rb +0 -11
  155. data/lib/spree/core/app_dependencies.rb +0 -126
  156. /data/{app/models/spree → lib/spree/core}/preferences/preferable_class_methods.rb +0 -0
  157. /data/{app/models/spree → lib/spree/core}/preferences/scoped_store.rb +0 -0
  158. /data/{app/models/spree → lib/spree/core}/preferences/store.rb +0 -0
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  class StateChange < Spree::Base
3
- belongs_to :user, class_name: Spree.user_class.to_s, optional: true
3
+ belongs_to :user, class_name: "::#{Spree.user_class}", optional: true
4
4
  belongs_to :stateful, polymorphic: true
5
5
 
6
6
  def <=>(other)
@@ -14,9 +14,15 @@ module Spree
14
14
  display_name = variant.name.to_s
15
15
  display_name += " (#{variant.options_text})" unless variant.options_text.blank?
16
16
 
17
- line_item.errors.add(:quantity,
18
- :selected_quantity_not_available,
19
- message: Spree.t(:selected_quantity_not_available, item: display_name.inspect))
17
+ if variant.available?
18
+ line_item.errors.add(:quantity,
19
+ :selected_quantity_not_available,
20
+ message: Spree.t(:selected_quantity_not_available, item: display_name.inspect))
21
+ else
22
+ line_item.errors.add(:base,
23
+ :only_active_products_can_be_added_to_cart,
24
+ message: Spree.t(:only_active_products_can_be_added_to_cart))
25
+ end
20
26
  end
21
27
 
22
28
  private
@@ -12,7 +12,7 @@ module Spree
12
12
  delegate :line_item,
13
13
  :quantity,
14
14
  :variant, to: :inventory_unit
15
- delegate :price, to: :variant
15
+ delegate :price, to: :line_item
16
16
  delegate :dimension,
17
17
  :volume,
18
18
  :weight, to: :variant, prefix: true
@@ -12,7 +12,7 @@ module Spree
12
12
  @total_on_hand ||= if variant.should_track_inventory?
13
13
  stock_items.sum(:count_on_hand)
14
14
  else
15
- Float::INFINITY
15
+ BigDecimal::INFINITY
16
16
  end
17
17
  end
18
18
 
@@ -2,6 +2,11 @@ module Spree
2
2
  class StockItem < Spree::Base
3
3
  acts_as_paranoid
4
4
 
5
+ include Metadata
6
+ if defined?(Spree::Webhooks)
7
+ include Spree::Webhooks::HasWebhooks
8
+ end
9
+
5
10
  with_options inverse_of: :stock_items do
6
11
  belongs_to :stock_location, class_name: 'Spree::StockLocation'
7
12
  belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant'
@@ -1,6 +1,15 @@
1
1
  module Spree
2
2
  class StockLocation < Spree::Base
3
3
  include UniqueName
4
+ if defined?(Spree::Webhooks)
5
+ include Spree::Webhooks::HasWebhooks
6
+ end
7
+ if defined?(Spree::Security::StockLocations)
8
+ include Spree::Security::StockLocations
9
+ end
10
+ if defined?(Spree::VendorConcern)
11
+ include Spree::VendorConcern
12
+ end
4
13
 
5
14
  has_many :shipments
6
15
  has_many :stock_items, dependent: :delete_all, inverse_of: :stock_location
@@ -51,9 +60,15 @@ module Spree
51
60
  # @param variant Variant instance or Variant ID
52
61
  #
53
62
  # @return [StockItem] Corresponding StockItem for the StockLocation's variant.
54
- def stock_item_or_create(variant)
55
- variant_id = variant.is_a?(Spree::Variant) ? variant.id : variant
56
- stock_item(variant_id) || stock_items.create(variant_id: variant_id)
63
+ def stock_item_or_create(variant_or_variant_id)
64
+ if variant_or_variant_id.is_a?(Spree::Variant)
65
+ variant_id = variant_or_variant_id.id
66
+ variant = variant_or_variant_id
67
+ else
68
+ variant_id = variant_or_variant_id
69
+ variant = Spree::Variant.find(variant_or_variant_id)
70
+ end
71
+ stock_item(variant_id) || propagate_variant(variant)
57
72
  end
58
73
 
59
74
  def count_on_hand(variant)
@@ -86,8 +101,7 @@ module Spree
86
101
  end
87
102
 
88
103
  def fill_status(variant, quantity)
89
- if item = stock_item(variant)
90
-
104
+ if item = stock_item_or_create(variant)
91
105
  if item.count_on_hand >= quantity
92
106
  on_hand = quantity
93
107
  backordered = 0
@@ -5,6 +5,10 @@ module Spree
5
5
  min: -2**31
6
6
  }.freeze
7
7
 
8
+ if defined?(Spree::Webhooks)
9
+ include Spree::Webhooks::HasWebhooks
10
+ end
11
+
8
12
  belongs_to :stock_item, class_name: 'Spree::StockItem', inverse_of: :stock_movements
9
13
  belongs_to :originator, polymorphic: true
10
14
 
@@ -4,6 +4,9 @@ module Spree
4
4
  include NumberIdentifier
5
5
  include NumberAsParam
6
6
  include Metadata
7
+ if defined?(Spree::Webhooks)
8
+ include Spree::Webhooks::HasWebhooks
9
+ end
7
10
 
8
11
  has_many :stock_movements, as: :originator
9
12
 
@@ -1,5 +1,12 @@
1
1
  module Spree
2
2
  class Store < Spree::Base
3
+ if defined?(Spree::Webhooks)
4
+ include Spree::Webhooks::HasWebhooks
5
+ end
6
+ if defined?(Spree::Security::Stores)
7
+ include Spree::Security::Stores
8
+ end
9
+
3
10
  typed_store :settings, coder: ActiveRecord::TypedStore::IdentityCoder do |s|
4
11
  # Spree Digital Asset Configurations
5
12
  s.boolean :limit_digital_download_count, default: true, null: false
@@ -9,8 +16,7 @@ module Spree
9
16
  s.integer :digital_asset_link_expire_time, default: 300, null: false # 5 minutes in seconds
10
17
  end
11
18
 
12
- MAILER_LOGO_CONTENT_TYPES = ['image/png', 'image/jpg', 'image/jpeg'].freeze
13
- FAVICON_CONTENT_TYPES = ['image/png', 'image/x-icon', 'image/vnd.microsoft.icon'].freeze
19
+ attr_accessor :skip_validate_not_last
14
20
 
15
21
  acts_as_paranoid
16
22
 
@@ -58,7 +64,7 @@ module Spree
58
64
 
59
65
  validates :digital_asset_authorized_clicks, numericality: { only_integer: true, greater_than: 0 }
60
66
  validates :digital_asset_authorized_days, numericality: { only_integer: true, greater_than: 0 }
61
- validates :code, uniqueness: { conditions: -> { with_deleted } }
67
+ validates :code, uniqueness: { case_sensitive: false, conditions: -> { with_deleted } }
62
68
  validates :mail_from_address, email: { allow_blank: false }
63
69
 
64
70
  # FIXME: we should remove this condition in v5
@@ -71,19 +77,18 @@ module Spree
71
77
 
72
78
  default_scope { order(created_at: :asc) }
73
79
 
74
- has_one_attached :logo
75
- has_one_attached :mailer_logo
76
- has_one_attached :favicon_image
80
+ has_one :logo, class_name: 'Spree::StoreLogo', dependent: :destroy, as: :viewable
81
+ accepts_nested_attributes_for :logo, reject_if: :all_blank
82
+
83
+ has_one :mailer_logo, class_name: 'Spree::StoreMailerLogo', dependent: :destroy, as: :viewable
84
+ accepts_nested_attributes_for :mailer_logo, reject_if: :all_blank
77
85
 
78
- validates :mailer_logo, content_type: MAILER_LOGO_CONTENT_TYPES
79
- validates :favicon_image, content_type: FAVICON_CONTENT_TYPES,
80
- dimension: { max: 256..256 },
81
- aspect_ratio: :square,
82
- size: { less_than_or_equal_to: 1.megabyte }
86
+ has_one :favicon_image, class_name: 'Spree::StoreFaviconImage', dependent: :destroy, as: :viewable
87
+ accepts_nested_attributes_for :favicon_image, reject_if: :all_blank
83
88
 
84
89
  before_save :ensure_default_exists_and_is_unique
85
90
  before_save :ensure_supported_currencies, :ensure_supported_locales, :ensure_default_country
86
- before_destroy :validate_not_last
91
+ before_destroy :validate_not_last, unless: :skip_validate_not_last
87
92
  before_destroy :pass_default_flag_to_other_store
88
93
 
89
94
  scope :by_url, ->(url) { where('url like ?', "%#{url}%") }
@@ -176,9 +181,9 @@ module Spree
176
181
  end
177
182
 
178
183
  def favicon
179
- return unless favicon_image.attached?
184
+ return unless favicon_image&.attachment&.attached?
180
185
 
181
- favicon_image.variant(resize: '32x32')
186
+ favicon_image.attachment.variant(resize_to_limit: [32, 32])
182
187
  end
183
188
 
184
189
  def can_be_deleted?
@@ -2,6 +2,9 @@ module Spree
2
2
  class StoreCredit < Spree::Base
3
3
  include SingleStoreResource
4
4
  include Metadata
5
+ if defined?(Spree::Webhooks)
6
+ include Spree::Webhooks::HasWebhooks
7
+ end
5
8
 
6
9
  acts_as_paranoid
7
10
 
@@ -15,7 +18,7 @@ module Spree
15
18
 
16
19
  DEFAULT_CREATED_BY_EMAIL = 'spree@example.com'.freeze
17
20
 
18
- belongs_to :user, class_name: Spree.user_class.to_s, foreign_key: 'user_id'
21
+ belongs_to :user, class_name: "::#{Spree.user_class}", foreign_key: 'user_id'
19
22
  belongs_to :category, class_name: 'Spree::StoreCreditCategory'
20
23
  belongs_to :created_by, class_name: Spree.admin_user_class.to_s, foreign_key: 'created_by_id'
21
24
  belongs_to :credit_type, class_name: 'Spree::StoreCreditType', foreign_key: 'type_id'
@@ -0,0 +1,17 @@
1
+ module Spree
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
+ VALID_CONTENT_TYPES = ['image/png', 'image/x-icon', 'image/vnd.microsoft.icon'].freeze
10
+
11
+ validates :attachment,
12
+ content_type: VALID_CONTENT_TYPES,
13
+ dimension: { max: 256..256 },
14
+ aspect_ratio: :square,
15
+ size: { less_than_or_equal_to: 1.megabyte }
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Spree
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
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Spree
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
+ VALID_CONTENT_TYPES = ['image/png', 'image/jpg', 'image/jpeg'].freeze
10
+
11
+ validates :attachment, content_type: VALID_CONTENT_TYPES
12
+ end
13
+ end
@@ -1,9 +1,15 @@
1
1
  module Spree
2
2
  class TaxCategory < Spree::Base
3
+ if defined?(Spree::Webhooks)
4
+ include Spree::Webhooks::HasWebhooks
5
+ end
6
+
3
7
  acts_as_paranoid
4
8
  validates :name, presence: true, uniqueness: { case_sensitive: false, scope: spree_base_uniqueness_scope.push(:deleted_at) }
5
9
 
6
10
  has_many :tax_rates, dependent: :destroy, inverse_of: :tax_category
11
+ has_many :products, dependent: :nullify
12
+ has_many :variants, dependent: :nullify
7
13
 
8
14
  before_save :set_default_category
9
15
 
@@ -4,6 +4,10 @@ module Spree
4
4
 
5
5
  include Spree::CalculatedAdjustments
6
6
  include Spree::AdjustmentSource
7
+ include Spree::Metadata
8
+ if defined?(Spree::Webhooks)
9
+ include Spree::Webhooks::HasWebhooks
10
+ end
7
11
 
8
12
  with_options inverse_of: :tax_rates do
9
13
  belongs_to :zone, class_name: 'Spree::Zone', optional: true
@@ -114,7 +118,8 @@ module Spree
114
118
 
115
119
  ' ' + ActiveSupport::NumberHelper::NumberToPercentageConverter.convert(
116
120
  amount * 100,
117
- locale: I18n.locale
121
+ locale: I18n.locale,
122
+ strip_insignificant_zeros: true
118
123
  )
119
124
  end
120
125
  end
@@ -4,6 +4,9 @@ require 'stringex'
4
4
  module Spree
5
5
  class Taxon < Spree::Base
6
6
  include Metadata
7
+ if defined?(Spree::Webhooks)
8
+ include Spree::Webhooks::HasWebhooks
9
+ end
7
10
 
8
11
  extend FriendlyId
9
12
  friendly_id :permalink, slug_column: :permalink, use: :history
@@ -39,7 +42,7 @@ module Spree
39
42
 
40
43
  before_validation :copy_taxonomy_from_parent
41
44
  after_save :touch_ancestors_and_taxonomy
42
- after_save :sync_taxonomy_name
45
+ after_update :sync_taxonomy_name
43
46
  after_touch :touch_ancestors_and_taxonomy
44
47
 
45
48
  has_one :icon, as: :viewable, dependent: :destroy, class_name: 'Spree::TaxonImage'
@@ -5,7 +5,11 @@ module Spree
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- has_one_attached :attachment
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
9
13
 
10
14
  validates :attachment, content_type: /\Aimage\/.*\z/
11
15
 
@@ -6,10 +6,11 @@ module Spree
6
6
 
7
7
  def styles
8
8
  self.class.styles.map do |_, size|
9
- width, height = size[/(\d+)x(\d+)/].split('x')
9
+ width, height = size[/(\d+)x(\d+)/].split('x').map(&:to_i)
10
10
 
11
11
  {
12
- url: polymorphic_path(attachment.variant(resize: size), only_path: true),
12
+ url: generate_url(size: size),
13
+ size: size,
13
14
  width: width,
14
15
  height: height
15
16
  }
@@ -1,6 +1,9 @@
1
1
  module Spree
2
2
  class Taxonomy < Spree::Base
3
3
  include Metadata
4
+ if defined?(Spree::Webhooks)
5
+ include Spree::Webhooks::HasWebhooks
6
+ end
4
7
 
5
8
  acts_as_list
6
9
 
@@ -12,7 +15,7 @@ module Spree
12
15
  belongs_to :store, class_name: 'Spree::Store'
13
16
 
14
17
  after_create :set_root
15
- after_save :set_root_taxon_name
18
+ after_update :set_root_taxon_name
16
19
 
17
20
  default_scope { order("#{table_name}.position, #{table_name}.created_at") }
18
21
 
@@ -5,20 +5,26 @@ module Spree
5
5
 
6
6
  include MemoizedData
7
7
  include Metadata
8
+ if defined?(Spree::Webhooks)
9
+ include Spree::Webhooks::HasWebhooks
10
+ end
8
11
 
9
12
  MEMOIZED_METHODS = %w(purchasable in_stock backorderable tax_category options_text compare_at_price)
10
13
 
11
14
  belongs_to :product, -> { with_deleted }, touch: true, class_name: 'Spree::Product', inverse_of: :variants
12
15
  belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
13
16
 
14
- delegate :name, :name=, :description, :slug, :available_on, :shipping_category_id,
17
+ delegate :name, :name=, :description, :slug, :available_on, :make_active_at, :shipping_category_id,
15
18
  :meta_description, :meta_keywords, :shipping_category, to: :product
16
19
 
20
+ auto_strip_attributes :sku, nullify: false
21
+
17
22
  # we need to have this callback before any dependent: :destroy associations
18
23
  # https://github.com/rails/rails/issues/3458
19
- before_destroy :ensure_no_line_items
24
+ before_destroy :ensure_not_in_complete_orders
25
+ after_destroy :remove_line_items_from_incomplete_orders
20
26
 
21
- # must include this after ensure_no_line_items to make sure price won't be deleted before validation
27
+ # must include this after ensure_not_in_complete_orders to make sure price won't be deleted before validation
22
28
  include Spree::DefaultPrice
23
29
 
24
30
  with_options inverse_of: :variant do
@@ -140,7 +146,7 @@ module Spree
140
146
  @tax_category ||= if self[:tax_category_id].nil?
141
147
  product.tax_category
142
148
  else
143
- Spree::TaxCategory.find(self[:tax_category_id])
149
+ Spree::TaxCategory.find_by(id: self[:tax_category_id]) || product.tax_category
144
150
  end
145
151
  end
146
152
 
@@ -166,6 +172,8 @@ module Spree
166
172
 
167
173
  def options=(options = {})
168
174
  options.each do |option|
175
+ next if option[:name].blank? || option[:value].blank?
176
+
169
177
  set_option_value(option[:name], option[:value])
170
178
  end
171
179
  end
@@ -174,12 +182,12 @@ module Spree
174
182
  # no option values on master
175
183
  return if is_master
176
184
 
177
- option_type = Spree::OptionType.where(name: opt_name).first_or_initialize do |o|
178
- o.presentation = opt_name
185
+ option_type = Spree::OptionType.where(['LOWER(name) = ?', opt_name.downcase.strip]).first_or_initialize do |o|
186
+ o.name = o.presentation = opt_name
179
187
  o.save!
180
188
  end
181
189
 
182
- current_value = option_values.detect { |o| o.option_type.name == opt_name }
190
+ current_value = find_option_value(opt_name)
183
191
 
184
192
  if current_value.nil?
185
193
  # then we have to check to make sure that the product has the option type
@@ -187,13 +195,13 @@ module Spree
187
195
  product.option_types << option_type
188
196
  end
189
197
  else
190
- return if current_value.name == opt_value
198
+ return if current_value.name.downcase.strip == opt_value.downcase.strip
191
199
 
192
200
  option_values.delete(current_value)
193
201
  end
194
202
 
195
- option_value = Spree::OptionValue.where(option_type_id: option_type.id, name: opt_value).first_or_initialize do |o|
196
- o.presentation = opt_value
203
+ option_value = option_type.option_values.where(['LOWER(name) = ?', opt_value.downcase.strip]).first_or_initialize do |o|
204
+ o.name = o.presentation = opt_value
197
205
  o.save!
198
206
  end
199
207
 
@@ -201,12 +209,29 @@ module Spree
201
209
  save
202
210
  end
203
211
 
212
+ def find_option_value(opt_name)
213
+ option_values.detect { |o| o.option_type.name.downcase.strip == opt_name.downcase.strip }
214
+ end
215
+
204
216
  def option_value(opt_name)
205
- option_values.detect { |o| o.option_type.name == opt_name }.try(:presentation)
217
+ find_option_value(opt_name).try(:presentation)
206
218
  end
207
219
 
208
220
  def price_in(currency)
209
- prices.detect { |price| price.currency == currency&.upcase } || prices.build(currency: currency&.upcase)
221
+ currency = currency&.upcase
222
+ find_or_build_price = lambda do
223
+ if prices.loaded?
224
+ prices.detect { |price| price.currency == currency } || prices.build(currency: currency)
225
+ else
226
+ prices.find_or_initialize_by(currency: currency)
227
+ end
228
+ end
229
+
230
+ Rails.cache.fetch("spree/prices/#{cache_key_with_version}/price_in/#{currency}") do
231
+ find_or_build_price.call
232
+ end
233
+ rescue TypeError
234
+ find_or_build_price.call
210
235
  end
211
236
 
212
237
  def amount_in(currency)
@@ -312,13 +337,17 @@ module Spree
312
337
 
313
338
  private
314
339
 
315
- def ensure_no_line_items
316
- if line_items.any?
340
+ def ensure_not_in_complete_orders
341
+ if orders.complete.any?
317
342
  errors.add(:base, :cannot_destroy_if_attached_to_line_items)
318
343
  throw(:abort)
319
344
  end
320
345
  end
321
346
 
347
+ def remove_line_items_from_incomplete_orders
348
+ Spree::Variants::RemoveFromIncompleteOrdersJob.perform_later(self)
349
+ end
350
+
322
351
  def quantifier
323
352
  Spree::Stock::Quantifier.new(self)
324
353
  end
@@ -1,5 +1,9 @@
1
1
  module Spree
2
2
  class WishedItem < Spree::Base
3
+ if defined?(Spree::Webhooks)
4
+ include Spree::Webhooks::HasWebhooks
5
+ end
6
+
3
7
  extend DisplayMoney
4
8
  money_methods :total, :price
5
9
 
@@ -1,10 +1,13 @@
1
1
  module Spree
2
2
  class Wishlist < Spree::Base
3
3
  include SingleStoreResource
4
+ if defined?(Spree::Webhooks)
5
+ include Spree::Webhooks::HasWebhooks
6
+ end
4
7
 
5
8
  has_secure_token
6
9
 
7
- belongs_to :user, class_name: Spree.user_class.name, touch: true
10
+ belongs_to :user, class_name: "::#{Spree.user_class}", touch: true
8
11
  belongs_to :store, class_name: 'Spree::Store'
9
12
 
10
13
  has_many :wished_items, class_name: 'Spree::WishedItem', dependent: :destroy
@@ -1,6 +1,9 @@
1
1
  module Spree
2
2
  class Zone < Spree::Base
3
3
  include UniqueName
4
+ if defined?(Spree::Webhooks)
5
+ include Spree::Webhooks::HasWebhooks
6
+ end
4
7
 
5
8
  with_options dependent: :destroy, inverse_of: :zone do
6
9
  has_many :zone_members, class_name: 'Spree::ZoneMember'
@@ -7,7 +7,7 @@ module Spree
7
7
  attr_accessor :country
8
8
 
9
9
  def call(address_params: {}, user: nil)
10
- fill_country_and_state_ids(address_params)
10
+ address_params = fill_country_and_state_ids(address_params)
11
11
 
12
12
  address = Spree::Address.new(address_params)
13
13
  address.user = user if user.present?
@@ -8,12 +8,17 @@ module Spree
8
8
 
9
9
  def call(address:, address_params:)
10
10
  address_params[:country_id] ||= address.country_id
11
- fill_country_and_state_ids(address_params)
11
+ address_params = fill_country_and_state_ids(address_params)
12
12
 
13
13
  if address&.editable?
14
14
  address.update(address_params) ? success(address) : failure(address)
15
15
  else
16
- new_address(address_params).valid? ? address.destroy && success(new_address) : failure(new_address)
16
+ if new_address(address_params).valid?
17
+ address.destroy
18
+ success(new_address)
19
+ else
20
+ failure(new_address)
21
+ end
17
22
  end
18
23
  end
19
24
 
@@ -11,6 +11,7 @@ module Spree
11
11
  line_item: line_item,
12
12
  options: options)
13
13
  end
14
+ order.reload
14
15
  success(line_item)
15
16
  end
16
17
  end
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ module Variants
3
+ class RemoveLineItems
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(variant:)
7
+ variant.line_items.joins(:order).where.not(spree_orders: { state: 'complete' }).find_each do |line_item|
8
+ Spree::Variants::RemoveLineItemJob.perform_later(line_item: line_item)
9
+ end
10
+
11
+ success(true)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -91,6 +91,7 @@ en:
91
91
  name: Name
92
92
  spree/product:
93
93
  available_on: Available On
94
+ make_active_at: Make Active At
94
95
  discontinue_on: Discontinue On
95
96
  cost_currency: Cost Currency
96
97
  cost_price: Cost Price
@@ -360,7 +361,7 @@ en:
360
361
  discontinue_on:
361
362
  invalid_date_range: must be later than available date
362
363
  base:
363
- cannot_destroy_if_attached_to_line_items: Cannot delete products once they are attached to line items.
364
+ cannot_destroy_if_attached_to_line_items: Cannot delete Products that are added to placed Orders. In such cases, please discontinue them.
364
365
  spree/refund:
365
366
  attributes:
366
367
  amount:
@@ -397,7 +398,7 @@ en:
397
398
  spree/variant:
398
399
  attributes:
399
400
  base:
400
- cannot_destroy_if_attached_to_line_items: Cannot delete variants once they are attached to line items.
401
+ cannot_destroy_if_attached_to_line_items: Cannot delete Variants that are added to placed Orders. In such cases, please discontinue them.
401
402
  no_master_variant_found_to_infer_price: No master variant found to infer price
402
403
  must_supply_price_for_variant_or_master: Must supply price for variant or master price for product.
403
404
  spree/image:
@@ -583,6 +584,7 @@ en:
583
584
  auto_capture: Auto Capture
584
585
  availability: availability
585
586
  available_on: Available On
587
+ make_active_at: Make Active At
586
588
  available: Available
587
589
  average_order_value: Average Order Value
588
590
  avs_response: AVS Response
@@ -1120,6 +1122,7 @@ en:
1120
1122
  number: Number
1121
1123
  ok: OK
1122
1124
  on_hand: On Hand
1125
+ only_active_products_can_be_added_to_cart: Draft and archived products cannot be added to cart, please mark the product as active before.
1123
1126
  open: Open
1124
1127
  open_all_adjustments: Open All Adjustments
1125
1128
  option_type: Option Type