spree_core 4.4.0 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) 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/fulfilment_changer.rb +1 -1
  28. data/app/models/spree/gateway/bogus.rb +1 -1
  29. data/app/models/spree/icon.rb +5 -1
  30. data/app/models/spree/image/configuration/active_storage.rb +5 -1
  31. data/app/models/spree/image.rb +3 -3
  32. data/app/models/spree/inventory_unit.rb +5 -2
  33. data/app/models/spree/legacy_user.rb +1 -2
  34. data/app/models/spree/line_item.rb +4 -1
  35. data/app/models/spree/linkable/homepage.rb +3 -0
  36. data/app/models/spree/linkable/uri.rb +3 -0
  37. data/app/models/spree/log_entry.rb +4 -0
  38. data/app/models/spree/menu.rb +3 -0
  39. data/app/models/spree/menu_item.rb +7 -11
  40. data/app/models/spree/option_type.rb +4 -0
  41. data/app/models/spree/option_value.rb +5 -0
  42. data/app/models/spree/order/address_book.rb +1 -0
  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/{app/models/spree → lib/spree/core}/preferences/preferable_class_methods.rb +0 -0
  127. data/{app/models/spree → lib/spree/core}/preferences/scoped_store.rb +0 -0
  128. data/{app/models/spree → lib/spree/core}/preferences/store.rb +0 -0
  129. data/lib/spree/core/search/base.rb +9 -5
  130. data/lib/spree/core/version.rb +1 -1
  131. data/lib/spree/core.rb +25 -5
  132. data/lib/spree/permitted_attributes.rb +9 -7
  133. data/lib/spree/testing_support/common_rake.rb +1 -0
  134. data/lib/spree/testing_support/factories/favicon_image_factory.rb +9 -0
  135. data/lib/spree/testing_support/factories/icon_factory.rb +3 -1
  136. data/lib/spree/testing_support/factories/image_factory.rb +3 -1
  137. data/lib/spree/testing_support/factories/menu_item_factory.rb +1 -1
  138. data/lib/spree/testing_support/factories/order_factory.rb +2 -1
  139. data/lib/spree/testing_support/factories/product_factory.rb +6 -1
  140. data/lib/spree/testing_support/factories/product_property_factory.rb +1 -0
  141. data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
  142. data/lib/spree/testing_support/factories/return_authorization_factory.rb +1 -1
  143. data/lib/spree/testing_support/factories/role_factory.rb +1 -1
  144. data/lib/spree/testing_support/factories/shipping_category_factory.rb +1 -1
  145. data/lib/spree/testing_support/factories/stock_location_factory.rb +3 -2
  146. data/lib/spree/testing_support/factories/store_factory.rb +1 -1
  147. data/lib/spree/testing_support/factories/taxon_image_factory.rb +3 -1
  148. data/lib/spree/testing_support/factories/user_factory.rb +4 -0
  149. data/lib/spree/testing_support/factories/variant_factory.rb +4 -0
  150. data/lib/spree_core.rb +2 -1
  151. data/lib/tasks/core.rake +12 -0
  152. data/spree_core.gemspec +2 -3
  153. metadata +63 -51
  154. data/app/models/friendly_id/slug_decorator.rb +0 -9
  155. data/app/models/spree/app_configuration.rb +0 -86
  156. data/lib/friendly_id/slug_rails5_patch.rb +0 -11
  157. data/lib/spree/core/app_dependencies.rb +0 -126
@@ -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