spree_core 4.8.3 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +57 -0
  3. data/app/finders/spree/line_items/find_by_variant.rb +4 -2
  4. data/app/finders/spree/orders/find_complete.rb +3 -13
  5. data/app/finders/spree/orders/find_current.rb +3 -1
  6. data/app/finders/spree/orders/finder_helper.rb +17 -0
  7. data/app/finders/spree/products/find.rb +1 -1
  8. data/app/finders/spree/variants/find.rb +30 -0
  9. data/app/models/spree/address.rb +12 -2
  10. data/app/models/spree/adjustment.rb +5 -0
  11. data/app/models/spree/asset.rb +8 -0
  12. data/app/models/spree/calculator/flat_rate.rb +3 -0
  13. data/app/models/spree/calculator/flexi_rate.rb +3 -0
  14. data/app/models/spree/calculator/percent_on_line_item.rb +3 -0
  15. data/app/models/spree/calculator/shipping/flat_rate.rb +13 -1
  16. data/app/models/spree/cms_section_image.rb +0 -6
  17. data/app/models/spree/customer_return.rb +1 -0
  18. data/app/models/spree/fulfilment_changer.rb +1 -0
  19. data/app/models/spree/icon.rb +0 -6
  20. data/app/models/spree/image/configuration/active_storage.rb +0 -8
  21. data/app/models/spree/image.rb +17 -0
  22. data/app/models/spree/line_item.rb +26 -7
  23. data/app/models/spree/option_value_variant.rb +4 -0
  24. data/app/models/spree/order/currency_updater.rb +2 -2
  25. data/app/models/spree/order/webhooks.rb +19 -0
  26. data/app/models/spree/order.rb +17 -6
  27. data/app/models/spree/order_merger.rb +6 -0
  28. data/app/models/spree/payment/gateway_options.rb +4 -0
  29. data/app/models/spree/payment/webhooks.rb +15 -0
  30. data/app/models/spree/payment.rb +7 -7
  31. data/app/models/spree/price.rb +53 -1
  32. data/app/models/spree/product/webhooks.rb +17 -0
  33. data/app/models/spree/product.rb +7 -9
  34. data/app/models/spree/promotion/actions/free_shipping.rb +13 -0
  35. data/app/models/spree/promotion/rules/first_order.rb +6 -1
  36. data/app/models/spree/promotion.rb +2 -2
  37. data/app/models/spree/promotion_handler/coupon.rb +3 -2
  38. data/app/models/spree/refund.rb +14 -2
  39. data/app/models/spree/reimbursement/emails.rb +11 -0
  40. data/app/models/spree/reimbursement.rb +1 -6
  41. data/app/models/spree/role_user.rb +1 -1
  42. data/app/models/spree/shipment/emails.rb +11 -0
  43. data/app/models/spree/shipment/webhooks.rb +11 -0
  44. data/app/models/spree/shipment.rb +60 -7
  45. data/app/models/spree/shipment_handler.rb +2 -6
  46. data/app/models/spree/shipping_method.rb +24 -2
  47. data/app/models/spree/shipping_method_zone.rb +4 -2
  48. data/app/models/spree/shipping_rate.rb +1 -1
  49. data/app/models/spree/stock/package.rb +4 -0
  50. data/app/models/spree/stock/quantifier.rb +22 -4
  51. data/app/models/spree/stock_item/webhooks.rb +6 -0
  52. data/app/models/spree/stock_item.rb +1 -3
  53. data/app/models/spree/stock_location.rb +15 -0
  54. data/app/models/spree/stock_movement/webhooks.rb +6 -0
  55. data/app/models/spree/stock_movement.rb +1 -3
  56. data/app/models/spree/store.rb +2 -9
  57. data/app/models/spree/store_credit.rb +11 -12
  58. data/app/models/spree/store_favicon_image.rb +0 -6
  59. data/app/models/spree/store_logo.rb +0 -5
  60. data/app/models/spree/store_mailer_logo.rb +0 -6
  61. data/app/models/spree/tax_rate.rb +3 -1
  62. data/app/models/spree/taxon.rb +83 -17
  63. data/app/models/spree/taxon_image/configuration/active_storage.rb +0 -6
  64. data/app/models/spree/taxonomy.rb +1 -0
  65. data/app/models/spree/variant/webhooks.rb +6 -0
  66. data/app/models/spree/variant.rb +29 -6
  67. data/app/models/spree/wishlist.rb +9 -0
  68. data/app/presenters/spree/variants/options_presenter.rb +34 -2
  69. data/app/services/spree/cart/add_item.rb +2 -0
  70. data/app/services/spree/cart/destroy.rb +14 -4
  71. data/app/services/spree/seeds/all.rb +3 -0
  72. data/app/services/spree/seeds/payment_methods.rb +18 -0
  73. data/app/services/spree/seeds/stock_locations.rb +1 -1
  74. data/app/services/spree/seeds/zones.rb +19 -19
  75. data/app/services/spree/tracking_numbers/base_service.rb +19 -0
  76. data/config/locales/en.yml +2 -0
  77. data/db/migrate/20240623172111_add_deleted_at_to_spree_stock_locations.rb +6 -0
  78. data/db/migrate/20240725124530_add_refunder_to_spree_refunds.rb +6 -0
  79. data/db/migrate/20240822163534_add_pretty_name_to_spree_taxons.rb +9 -0
  80. data/lib/spree/core/components.rb +6 -0
  81. data/lib/spree/core/controller_helpers/order.rb +5 -5
  82. data/lib/spree/core/dependencies.rb +5 -1
  83. data/lib/spree/core/engine.rb +1 -1
  84. data/lib/spree/core/preferences/preferable.rb +4 -2
  85. data/lib/spree/core/preferences/preferable_class_methods.rb +3 -2
  86. data/lib/spree/core/runtime_configuration.rb +1 -0
  87. data/lib/spree/core/version.rb +1 -1
  88. data/lib/spree/core/webhooks.rb +15 -7
  89. data/lib/spree/core.rb +1 -0
  90. data/lib/spree/money.rb +1 -1
  91. data/lib/spree/permitted_attributes.rb +1 -0
  92. data/lib/spree/testing_support/authorization_helpers.rb +2 -2
  93. data/lib/spree/testing_support/capybara_config.rb +8 -4
  94. data/lib/spree/testing_support/common_rake.rb +1 -1
  95. data/spree_core.gemspec +5 -3
  96. metadata +54 -8
  97. data/LICENSE +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ee4d4cee87ddfaa3a840606e85a256ec3751fd79f509e481fc08557c908f0a5
4
- data.tar.gz: ef15433c0b5ac981d367bf1ee209d5ff126f9930e986bc824b74a554cb56e171
3
+ metadata.gz: ef1a05dcfe29482a9f538a00cbf53f30fb26b25cc789e6935b1c502fa7d670ba
4
+ data.tar.gz: 0b685feeca4b4de334ef20c454edf4b83476d7275dca33617780b03df659023e
5
5
  SHA512:
6
- metadata.gz: 563286ea01a9de90ed7d4fa10398715511835028bf0ec274a69ea482841793bab3cb7441778b574e2a4add3c128b38b1bb65c31a0e1b1e669361244fbf756b11
7
- data.tar.gz: a0fa015209ed89a6c2d69cef0bc17e01d2eb7002b869b0fccff2aab8b0ede4f443a6933d15d0832589b5eeae91b2b80364f625af36b6d3a0e50a641c5a85b1f6
6
+ metadata.gz: d18e35dc1286e0053e044ae360e0b626ccab034e356a8ca6396303a8c90c18041c22ee94fdfdcb7e08ed8856f56416a81efed9e864088b351a68684d972b95c0
7
+ data.tar.gz: e9338463f6da5a8b40353001e24285c0cf5b0fea045a66ff536531022c7ea1ab82c9507ce456652e5afcee6d8a8166d577645b083f79371a95d3d0c30be7eeca
data/LICENSE.md ADDED
@@ -0,0 +1,57 @@
1
+ # License
2
+
3
+ Copyright © 2024-present, Vendo Connect Inc.
4
+
5
+ Copyright © 2015-2024, Spark Solutions Sp. z o.o.
6
+
7
+ Copyright © 2007-2015, Spree Commerce Inc.
8
+
9
+ Spree Commerce is a free, open-source eCommerce framework giving you full control and customizability.
10
+
11
+ For **Spree Commerce versions 4.10** and later in the [spree/spree](https://github.com/spree/spree) repository two licenses apply simultaneously and users are required to comply with the terms of these two licenses at the same time:
12
+
13
+ * [AGPL-3.0](https://opensource.org/license/agpl-v3) - for all contributions from version 4.10 onwards
14
+
15
+ * [BSD-3-Clause](https://opensource.org/license/bsd-3-clause) - for all other contributions predating version 4.10
16
+ Effectively, for versions 4.10 and upwards **AGPL-3.0** license applies.
17
+
18
+ **Spree Commerce versions 4.9** and earlier in the [spree/spree](https://github.com/spree/spree) repository are available under the **BSD-3-Clause** license and users are required to comply with its terms.
19
+
20
+ If you’d like to use Spree Commerce without the AGPL-3.0 restrictions e.g. for a **SaaS** business, please talk to us about obtaining a [Commercial License](#commercial-license).
21
+
22
+ All third party components incorporated into this software are licensed under the original license provided by the owner of the applicable component.
23
+
24
+ Please refer to our [Licensing FAQ](https://spreecommerce.org/why-spree-is-changing-its-open-source-license-to-agpl-3-0-and-introducing-a-commercial-license/) in case of questions.
25
+
26
+ ## AGPL-3.0 License - Spree 4.10 upwards
27
+
28
+ If you decide to accept the AGPL-3.0 license by using Spree Commerce version 4.10 or later, you must comply with the following terms:
29
+
30
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
31
+
32
+ This program is distributed in the hope that it will be useful,
33
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
34
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35
+ GNU Affero General Public License for more details.
36
+
37
+ You should have received a copy of the GNU Affero General Public License
38
+ along with this program. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).
39
+
40
+ ## BSD 3-Clause License
41
+
42
+ If you decide to accept the BSD-3-Clause license by using Spree Commerce, you must comply with the following terms:
43
+
44
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
45
+ Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
46
+ Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
47
+ Neither the name of Spree Commerce Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
48
+
49
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50
+
51
+ ## Commercial License
52
+
53
+ Should you decide to use Spree Commerce version 4.10 or later for commercial redistribution (eg. SaaS) - also known as Commercial Distribution - you must do so in accordance with the terms and conditions contained in a separate written agreement between you and [Vendo Connect Inc.](https://www.getvendo.com/).
54
+
55
+ For more information about the Commercial License (CL) please contact [sales@getvendo.com](mailto:sales@getvendo.com).
56
+
57
+ Please refer to our [Licensing FAQ](https://spreecommerce.org/why-spree-is-changing-its-open-source-license-to-agpl-3-0-and-introducing-a-commercial-license/) in case of questions.
@@ -2,11 +2,13 @@ module Spree
2
2
  module LineItems
3
3
  class FindByVariant
4
4
  def execute(order:, variant:, options: {})
5
- order.line_items.detect do |line_item|
6
- next unless line_item.variant_id == variant.id
5
+ line_item = order.line_items.loaded? ? order.line_items.detect { |li| li.variant_id == variant.id } : order.line_items.find_by(variant_id: variant.id)
7
6
 
7
+ if line_item
8
8
  Spree::Dependencies.cart_compare_line_items_service.constantize.call(order: order, line_item: line_item, options: options).value
9
9
  end
10
+
11
+ line_item
10
12
  end
11
13
  end
12
14
  end
@@ -1,6 +1,8 @@
1
1
  module Spree
2
2
  module Orders
3
3
  class FindComplete
4
+ include Spree::Orders::FinderHelper
5
+
4
6
  attr_reader :user, :number, :token, :store
5
7
 
6
8
  def initialize(user: nil, number: nil, token: nil, store: nil)
@@ -22,7 +24,7 @@ module Spree
22
24
  private
23
25
 
24
26
  def scope
25
- user? ? user.orders.complete.includes(scope_includes) : Spree::Order.complete.includes(scope_includes)
27
+ user? ? user.orders.complete.includes(order_includes) : Spree::Order.complete.includes(order_includes)
26
28
  end
27
29
 
28
30
  def user?
@@ -64,18 +66,6 @@ module Spree
64
66
 
65
67
  orders.where(store: store)
66
68
  end
67
-
68
- def scope_includes
69
- {
70
- line_items: [
71
- variant: [
72
- :images,
73
- option_values: :option_type,
74
- product: :product_properties,
75
- ]
76
- ]
77
- }
78
- end
79
69
  end
80
70
  end
81
71
  end
@@ -1,6 +1,8 @@
1
1
  module Spree
2
2
  module Orders
3
3
  class FindCurrent
4
+ include Spree::Orders::FinderHelper
5
+
4
6
  def execute(user:, store:, **params)
5
7
  params = params.merge(store_id: store.id)
6
8
 
@@ -15,7 +17,7 @@ module Spree
15
17
  private
16
18
 
17
19
  def incomplete_orders
18
- Spree::Order.incomplete.not_canceled
20
+ Spree::Order.incomplete.not_canceled.includes(order_includes)
19
21
  end
20
22
  end
21
23
  end
@@ -0,0 +1,17 @@
1
+ module Spree
2
+ module Orders
3
+ module FinderHelper
4
+ def order_includes
5
+ {
6
+ line_items: [
7
+ variant: [
8
+ :product,
9
+ :images,
10
+ { option_values: :option_type }
11
+ ]
12
+ ]
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -263,7 +263,7 @@ module Spree
263
263
  def taxon_ids(taxons_ids)
264
264
  return if taxons_ids.nil? || taxons_ids.to_s.blank?
265
265
 
266
- taxons = store.taxons.where(id: taxons_ids.to_s.split(','))
266
+ taxons = Spree::Taxon.for_store(store).where(id: taxons_ids.to_s.split(','))
267
267
  taxons.map(&:cached_self_and_descendants_ids).flatten.compact.uniq.map(&:to_s)
268
268
  end
269
269
 
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ module Variants
3
+ class Find
4
+ def initialize(scope:, params:)
5
+ @scope = scope
6
+ @options = params.dig(:filter, :options).try(:to_unsafe_hash)
7
+ end
8
+
9
+ def execute
10
+ variants = by_options(scope)
11
+ variants.distinct
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :scope, :options
17
+
18
+ def options?
19
+ options.present?
20
+ end
21
+
22
+ def by_options(variants)
23
+ return variants unless options?
24
+
25
+ variants_ids = options.map { |key, value| variants.with_option_value(key, value)&.ids }.compact.uniq
26
+ variants.where(id: variants_ids.reduce(&:intersection))
27
+ end
28
+ end
29
+ end
30
+ end
@@ -46,7 +46,9 @@ module Spree
46
46
  before_validation :clear_invalid_state_entities, if: -> { country.present? }, on: :update
47
47
 
48
48
  with_options presence: true do
49
- validates :firstname, :lastname, :address1, :city, :country
49
+ validates :firstname, :lastname, if: :require_name?
50
+ validates :address1, if: :require_street?
51
+ validates :city, :country
50
52
  validates :zipcode, if: :require_zipcode?
51
53
  validates :phone, if: :require_phone?
52
54
  end
@@ -137,12 +139,20 @@ module Spree
137
139
  country ? country.zipcode_required? : true
138
140
  end
139
141
 
142
+ def require_name?
143
+ true
144
+ end
145
+
146
+ def require_street?
147
+ true
148
+ end
149
+
140
150
  def editable?
141
151
  new_record? || (shipments.empty? && !Order.complete.where('bill_address_id = ? OR ship_address_id = ?', id, id).exists?)
142
152
  end
143
153
 
144
154
  def can_be_deleted?
145
- shipments.empty? && !Order.where('bill_address_id = ? OR ship_address_id = ?', id, id).exists?
155
+ shipments.empty? && !Order.complete.where('bill_address_id = ? OR ship_address_id = ?', id, id).exists?
146
156
  end
147
157
 
148
158
  def check
@@ -64,6 +64,7 @@ module Spree
64
64
  scope :charge, -> { where("#{quoted_table_name}.amount >= 0") }
65
65
  scope :credit, -> { where("#{quoted_table_name}.amount < 0") }
66
66
  scope :nonzero, -> { where("#{quoted_table_name}.amount != 0") }
67
+ scope :non_zero, -> { where.not(amount: [nil, 0]) }
67
68
  scope :promotion, -> { where(source_type: 'Spree::PromotionAction') }
68
69
  scope :return_authorization, -> { where(source_type: 'Spree::ReturnAuthorization') }
69
70
  scope :is_included, -> { where(included: true) }
@@ -87,6 +88,10 @@ module Spree
87
88
  source_type == 'Spree::PromotionAction'
88
89
  end
89
90
 
91
+ def tax?
92
+ source_type == 'Spree::TaxRate'
93
+ end
94
+
90
95
  # Passing a target here would always be recommended as it would avoid
91
96
  # hitting the database again and would ensure you're compute values over
92
97
  # the specific object amount passed here.
@@ -8,5 +8,13 @@ module Spree
8
8
 
9
9
  belongs_to :viewable, polymorphic: true, touch: true
10
10
  acts_as_list scope: [:viewable_id, :viewable_type]
11
+
12
+ if Spree.public_storage_service_name
13
+ has_one_attached :attachment, service: Spree.public_storage_service_name
14
+ else
15
+ has_one_attached :attachment
16
+ end
17
+
18
+ default_scope { includes(attachment_attachment: :blob) }
11
19
  end
12
20
  end
@@ -4,12 +4,15 @@ module Spree
4
4
  class Calculator::FlatRate < Calculator
5
5
  preference :amount, :decimal, default: 0
6
6
  preference :currency, :string, default: -> { Spree::Store.default.default_currency }
7
+ preference :apply_only_on_full_priced_items, :boolean, default: false
7
8
 
8
9
  def self.description
9
10
  Spree.t(:flat_rate_per_order)
10
11
  end
11
12
 
12
13
  def compute(object = nil)
14
+ return 0 if preferred_apply_only_on_full_priced_items && object&.variant&.compare_at_amount_in(object.currency).present?
15
+
13
16
  if object && preferred_currency.casecmp(object.currency.upcase).zero?
14
17
  preferred_amount
15
18
  else
@@ -6,6 +6,7 @@ module Spree
6
6
  preference :additional_item, :decimal, default: 0.0
7
7
  preference :max_items, :integer, default: 0
8
8
  preference :currency, :string, default: -> { Spree::Store.default.default_currency }
9
+ preference :apply_only_on_full_priced_items, :boolean, default: false
9
10
 
10
11
  def self.description
11
12
  Spree.t(:flexible_rate)
@@ -16,6 +17,8 @@ module Spree
16
17
  end
17
18
 
18
19
  def compute(object)
20
+ return 0 if preferred_apply_only_on_full_priced_items && object.variant.compare_at_amount_in(object.currency).present?
21
+
19
22
  compute_from_quantity(object.quantity)
20
23
  end
21
24
 
@@ -2,12 +2,15 @@ module Spree
2
2
  class Calculator
3
3
  class PercentOnLineItem < Calculator
4
4
  preference :percent, :decimal, default: 0
5
+ preference :apply_only_on_full_priced_items, :boolean, default: false
5
6
 
6
7
  def self.description
7
8
  Spree.t(:percent_per_item)
8
9
  end
9
10
 
10
11
  def compute(object)
12
+ return 0 if preferred_apply_only_on_full_priced_items && object.variant.compare_at_amount_in(object.currency).present?
13
+
11
14
  computed_amount = (object.amount * preferred_percent / 100).round(2)
12
15
 
13
16
  # We don't want to cause the promotion adjustments to push the order into a negative total.
@@ -6,11 +6,23 @@ module Spree
6
6
  preference :amount, :decimal, default: 0
7
7
  preference :currency, :string, default: -> { Spree::Store.default.default_currency }
8
8
 
9
+ preference :minimum_item_total, :decimal, default: nil, nullable: true
10
+ preference :maximum_item_total, :decimal, default: nil, nullable: true
11
+
12
+ preference :minimum_weight, :decimal, default: nil, nullable: true
13
+ preference :maximum_weight, :decimal, default: nil, nullable: true
14
+
9
15
  def self.description
10
16
  Spree.t(:shipping_flat_rate_per_order)
11
17
  end
12
18
 
13
- def compute_package(_package)
19
+ def compute_package(package)
20
+ return nil if preferred_minimum_weight.present? && preferred_minimum_weight >= package.weight
21
+ return nil if preferred_maximum_weight.present? && preferred_maximum_weight < package.weight
22
+
23
+ return nil if preferred_minimum_item_total.present? && preferred_minimum_item_total >= package.item_total
24
+ return nil if preferred_maximum_item_total.present? && preferred_maximum_item_total < package.item_total
25
+
14
26
  preferred_amount
15
27
  end
16
28
  end
@@ -1,11 +1,5 @@
1
1
  module Spree
2
2
  class CmsSectionImage < Asset
3
- if Spree.public_storage_service_name
4
- has_one_attached :attachment, service: Spree.public_storage_service_name
5
- else
6
- has_one_attached :attachment
7
- end
8
-
9
3
  IMAGE_COUNT = ['one', 'two', 'three']
10
4
  IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].freeze
11
5
  IMAGE_SIZE = ['sm', 'md', 'lg', 'xl']
@@ -45,6 +45,7 @@ module Spree
45
45
  # Temporarily tie a customer_return to one order
46
46
  def order
47
47
  return nil if return_items.blank?
48
+ return nil if return_items.first.inventory_unit.blank?
48
49
 
49
50
  return_items.first.inventory_unit.order
50
51
  end
@@ -109,6 +109,7 @@ module Spree
109
109
  end
110
110
 
111
111
  desired_shipment.refresh_rates
112
+ desired_shipment.update_amounts
112
113
 
113
114
  desired_shipment.order.reload
114
115
  desired_shipment.order.update_with_updater!
@@ -1,11 +1,5 @@
1
1
  module Spree
2
2
  class Icon < Spree::Asset
3
- if Spree.public_storage_service_name
4
- has_one_attached :attachment, service: Spree.public_storage_service_name
5
- else
6
- has_one_attached :attachment
7
- end
8
-
9
3
  ICON_TYPES = %i[png jpg jpeg gif svg]
10
4
 
11
5
  validates :attachment, attached: true, content_type: ICON_TYPES
@@ -5,16 +5,8 @@ module Spree
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- if Spree.public_storage_service_name
9
- has_one_attached :attachment, service: Spree.public_storage_service_name
10
- else
11
- has_one_attached :attachment
12
- end
13
-
14
8
  validates :attachment, attached: true, content_type: /\Aimage\/.*\z/
15
9
 
16
- default_scope { includes(attachment_attachment: :blob) }
17
-
18
10
  def self.styles
19
11
  @styles ||= {
20
12
  mini: '48x48>',
@@ -4,6 +4,8 @@ module Spree
4
4
  include Rails.application.routes.url_helpers
5
5
  include Spree::ImageMethods
6
6
 
7
+ after_commit :touch_product_variants, if: :should_touch_product_variants?, on: :update
8
+
7
9
  # In Rails 5.x class constants are being undefined/redefined during the code reloading process
8
10
  # in a rails development environment, after which the actual ruby objects stored in those class constants
9
11
  # are no longer equal (subclass == self) what causes error ActiveRecord::SubclassNotFound
@@ -51,5 +53,20 @@ module Spree
51
53
  def plp_url
52
54
  generate_url(size: self.class.styles[:plp_and_carousel])
53
55
  end
56
+
57
+ private
58
+
59
+ def touch_product_variants
60
+ viewable.product.variants.touch_all
61
+ end
62
+
63
+ def should_touch_product_variants?
64
+ return false unless viewable.is_a?(Spree::Variant)
65
+ return false unless viewable.is_master?
66
+ return false unless viewable.product.has_variants?
67
+ return false unless saved_change_to_position?
68
+
69
+ true
70
+ end
54
71
  end
55
72
  end
@@ -5,6 +5,8 @@ module Spree
5
5
  include Spree::Webhooks::HasWebhooks
6
6
  end
7
7
 
8
+ attribute :quantity, :integer, default: 1
9
+
8
10
  before_validation :ensure_valid_quantity
9
11
 
10
12
  with_options inverse_of: :line_items do
@@ -13,7 +15,7 @@ module Spree
13
15
  end
14
16
  belongs_to :tax_category, -> { with_deleted }, class_name: 'Spree::TaxCategory'
15
17
 
16
- has_one :product, through: :variant
18
+ has_one :product, -> { with_deleted }, class_name: 'Spree::Product', through: :variant
17
19
 
18
20
  has_many :adjustments, as: :adjustable, dependent: :destroy
19
21
  has_many :inventory_units, inverse_of: :line_item
@@ -27,7 +29,7 @@ module Spree
27
29
  # numericality: :less_than_or_equal_to validation is due to the restriction at the database level
28
30
  # https://github.com/spree/spree/issues/2695#issuecomment-143314161
29
31
  validates :quantity, numericality: {
30
- less_than_or_equal_to: DatabaseTypeUtilities.maximum_value_for(:integer),
32
+ in: 0..DatabaseTypeUtilities.maximum_value_for(:integer),
31
33
  only_integer: true, message: Spree.t('validation.must_be_int')
32
34
  }
33
35
 
@@ -70,11 +72,7 @@ module Spree
70
72
  def update_price
71
73
  currency_price = variant.price_in(order.currency)
72
74
 
73
- self.price = if currency_price.amount.present?
74
- currency_price.price_including_vat_for(tax_zone: tax_zone)
75
- else
76
- 0
77
- end
75
+ self.price = currency_price.price_including_vat_for(tax_zone: tax_zone) if currency_price.present?
78
76
  end
79
77
 
80
78
  def copy_tax_category
@@ -99,6 +97,13 @@ module Spree
99
97
  amount + taxable_adjustment_total
100
98
  end
101
99
 
100
+ # returns the total tax amount
101
+ #
102
+ # @return [BigDecimal]
103
+ def tax_total
104
+ included_tax_total + additional_tax_total
105
+ end
106
+
102
107
  alias discounted_money display_discounted_amount
103
108
  alias discounted_amount taxable_amount
104
109
 
@@ -117,6 +122,20 @@ module Spree
117
122
  !sufficient_stock?
118
123
  end
119
124
 
125
+ # returns true if any of the inventory units are shipped
126
+ #
127
+ # @return [Boolean]
128
+ def any_shipped?
129
+ inventory_units.any?(&:shipped?)
130
+ end
131
+
132
+ # returns true if all of the inventory units are shipped
133
+ #
134
+ # @return [Boolean]
135
+ def fully_shipped?
136
+ inventory_units.all?(&:shipped?)
137
+ end
138
+
120
139
  def options=(options = {})
121
140
  return unless options.present?
122
141
 
@@ -5,5 +5,9 @@ module Spree
5
5
 
6
6
  validates :option_value, :variant, presence: true
7
7
  validates :option_value_id, uniqueness: { scope: :variant_id }
8
+
9
+ scope :for_option_types, lambda { |option_types|
10
+ joins(:option_value).merge(Spree::OptionValue.where(option_types: option_types))
11
+ }
8
12
  end
9
13
  end
@@ -26,10 +26,10 @@ module Spree
26
26
  def update_line_item_price!(line_item)
27
27
  price = price_from_line_item(line_item)
28
28
 
29
- if price
29
+ if price&.currency && price.amount
30
30
  line_item.update!(currency: price.currency, price: price.amount)
31
31
  else
32
- raise "no #{currency} price found for #{line_item.product.name} (#{line_item.variant.sku})"
32
+ line_item.destroy
33
33
  end
34
34
  end
35
35
  end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ class Order < Spree::Base
3
+ module Webhooks
4
+ extend ActiveSupport::Concern
5
+
6
+ def send_order_canceled_webhook
7
+ # Implement your logic of sending cancale webhooks
8
+ end
9
+
10
+ def send_order_placed_webhook
11
+ # Implement your logic of sending order placed webhooks
12
+ end
13
+
14
+ def send_order_resumed_webhook
15
+ # Implement your logic of sending after resume webhooks
16
+ end
17
+ end
18
+ end
19
+ end
@@ -17,6 +17,7 @@ module Spree
17
17
  include Spree::Order::StoreCredit
18
18
  include Spree::Order::AddressBook
19
19
  include Spree::Order::Emails
20
+ include Spree::Order::Webhooks
20
21
  include Spree::Core::NumberGenerator.new(prefix: 'R')
21
22
  include Spree::Core::TokenGenerator
22
23
 
@@ -25,9 +26,6 @@ module Spree
25
26
  include Spree::SingleStoreResource
26
27
  include Spree::MemoizedData
27
28
  include Spree::Metadata
28
- if defined?(Spree::Webhooks::HasWebhooks)
29
- include Spree::Webhooks::HasWebhooks
30
- end
31
29
  if defined?(Spree::Security::Orders)
32
30
  include Spree::Security::Orders
33
31
  end
@@ -91,9 +89,9 @@ module Spree
91
89
  belongs_to :user, optional: true
92
90
  end
93
91
  if Spree.admin_user_class
94
- belongs_to :created_by, class_name: Spree.admin_user_class.to_s, optional: true
95
- belongs_to :approver, class_name: Spree.admin_user_class.to_s, optional: true
96
- belongs_to :canceler, class_name: Spree.admin_user_class.to_s, optional: true
92
+ belongs_to :created_by, class_name: "::#{Spree.admin_user_class}", optional: true
93
+ belongs_to :approver, class_name: "::#{Spree.admin_user_class}", optional: true
94
+ belongs_to :canceler, class_name: "::#{Spree.admin_user_class}", optional: true
97
95
  else
98
96
  belongs_to :created_by, optional: true
99
97
  belongs_to :approver, optional: true
@@ -401,6 +399,8 @@ module Spree
401
399
  deliver_order_confirmation_email unless confirmation_delivered?
402
400
  deliver_store_owner_order_notification_email if deliver_store_owner_order_notification_email?
403
401
 
402
+ send_order_placed_webhook
403
+
404
404
  consider_risk
405
405
  end
406
406
 
@@ -522,6 +522,14 @@ module Spree
522
522
  self.shipments = Spree::Stock::Coordinator.new(self).shipments
523
523
  end
524
524
 
525
+ # Returns the total weight of the inventory units in the order
526
+ # This is used to calculate the shipping rates for the order
527
+ #
528
+ # @return [BigDecimal] the total weight of the inventory units in the order
529
+ def total_weight
530
+ @total_weight ||= line_items.joins(:variant).includes(:variant).map { |li| li.variant.weight * li.quantity }.sum
531
+ end
532
+
525
533
  def apply_free_shipping_promotions
526
534
  Spree::PromotionHandler::FreeShipping.new(self).activate
527
535
  shipments.each { |shipment| Spree::Adjustable::AdjustmentsUpdater.update(shipment) }
@@ -566,6 +574,7 @@ module Spree
566
574
  def set_shipments_cost
567
575
  shipments.each(&:update_amounts)
568
576
  updater.update_shipment_total
577
+ updater.update_adjustment_total
569
578
  persist_totals
570
579
  end
571
580
 
@@ -726,11 +735,13 @@ module Spree
726
735
 
727
736
  send_cancel_email
728
737
  update_with_updater!
738
+ send_order_canceled_webhook
729
739
  end
730
740
 
731
741
  def after_resume
732
742
  shipments.each(&:resume!)
733
743
  consider_risk
744
+ send_order_resumed_webhook
734
745
  end
735
746
 
736
747
  def use_billing?
@@ -16,6 +16,7 @@ module Spree
16
16
  end
17
17
 
18
18
  set_user(user)
19
+ clear_addresses(other_order)
19
20
  persist_merge
20
21
 
21
22
  # So that the destroy doesn't take out line items which may have been re-assigned
@@ -39,6 +40,11 @@ module Spree
39
40
  order.associate_user!(user) if !order.user && !user.blank?
40
41
  end
41
42
 
43
+ def clear_addresses(other_order)
44
+ other_order.ship_address = nil
45
+ other_order.bill_address = nil
46
+ end
47
+
42
48
  # The idea is the end developer can choose to override the merge
43
49
  # to their own choosing. Default is merge with errors.
44
50
  def handle_merge(current_line_item, other_order_line_item)
@@ -5,6 +5,10 @@ module Spree
5
5
  @payment = payment
6
6
  end
7
7
 
8
+ def statement_descriptor_suffix
9
+ order.number
10
+ end
11
+
8
12
  def email
9
13
  order.email
10
14
  end