spree_core 3.7.14.1 → 4.0.0.beta

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/app/controllers/spree/base_controller.rb +0 -1
  4. data/app/finders/spree/taxons/find.rb +6 -5
  5. data/app/helpers/spree/base_helper.rb +0 -9
  6. data/app/helpers/spree/products_helper.rb +1 -1
  7. data/app/models/concerns/spree/product_scopes.rb +254 -0
  8. data/app/models/concerns/spree/user_address.rb +5 -2
  9. data/app/models/friendly_id/slug_decorator.rb +8 -2
  10. data/app/models/spree/ability.rb +21 -22
  11. data/app/models/spree/address.rb +37 -24
  12. data/app/models/spree/adjustment.rb +0 -10
  13. data/app/models/spree/app_dependencies.rb +2 -2
  14. data/app/models/spree/asset.rb +1 -12
  15. data/app/models/spree/country.rb +2 -2
  16. data/app/models/spree/credit_card.rb +4 -5
  17. data/app/models/spree/gateway/bogus.rb +1 -1
  18. data/app/models/spree/image.rb +8 -1
  19. data/app/models/spree/image/configuration/active_storage.rb +0 -2
  20. data/app/models/spree/order.rb +13 -78
  21. data/app/models/spree/order/address_book.rb +86 -0
  22. data/app/models/spree/order/checkout.rb +4 -22
  23. data/app/models/spree/order/currency_updater.rb +1 -1
  24. data/app/models/spree/order/store_credit.rb +0 -18
  25. data/app/models/spree/order_merger.rb +1 -0
  26. data/app/models/spree/payment.rb +4 -2
  27. data/app/models/spree/payment/gateway_options.rb +1 -1
  28. data/app/models/spree/payment/processing.rb +2 -0
  29. data/app/models/spree/preferences/configuration.rb +1 -1
  30. data/app/models/spree/preferences/preferable.rb +1 -1
  31. data/app/models/spree/product.rb +2 -3
  32. data/app/models/spree/promotion_handler/coupon.rb +2 -1
  33. data/app/models/spree/reimbursement_tax_calculator.rb +1 -1
  34. data/app/models/spree/return_item.rb +1 -1
  35. data/app/models/spree/return_item/eligibility_validator/default.rb +2 -0
  36. data/app/models/spree/return_item/eligibility_validator/{rma_required.rb → r_m_a_required.rb} +0 -0
  37. data/app/models/spree/shipment.rb +1 -1
  38. data/app/models/spree/store_credit.rb +4 -5
  39. data/app/models/spree/taxon_image.rb +1 -1
  40. data/app/models/spree/taxonomy.rb +1 -1
  41. data/app/services/spree/cart/add_item.rb +3 -1
  42. data/app/services/spree/cart/update.rb +1 -1
  43. data/config/locales/en.yml +15 -0
  44. data/db/default/spree/stores.rb +1 -0
  45. data/db/default/spree/zones.rb +16 -0
  46. data/db/migrate/20140806144901_add_type_to_reimbursement_type.rb +1 -1
  47. data/db/migrate/20141101231208_fix_adjustment_order_presence.rb +2 -2
  48. data/db/migrate/20170323151450_add_missing_unique_indexes_for_unique_attributes.rb +1 -1
  49. data/db/migrate/20190305121659_add_iso_and_iso3_validation_on_presence_and_uniqueness.rb +18 -0
  50. data/db/migrate/20190523092729_add_user_id_and_deleted_at_to_spree_addresses.rb +12 -0
  51. data/lib/generators/spree/dummy/dummy_generator.rb +3 -1
  52. data/lib/spree/core.rb +0 -2
  53. data/lib/spree/core/importer/order.rb +64 -103
  54. data/lib/spree/core/importer/product.rb +2 -2
  55. data/lib/spree/core/product_duplicator.rb +1 -5
  56. data/lib/spree/core/version.rb +1 -3
  57. data/lib/spree/permitted_attributes.rb +3 -1
  58. data/lib/spree/testing_support/ability_helpers.rb +3 -11
  59. data/lib/spree/testing_support/capybara_config.rb +4 -1
  60. data/lib/spree/testing_support/capybara_ext.rb +22 -98
  61. data/lib/spree/testing_support/controller_requests.rb +36 -26
  62. data/lib/spree/testing_support/factories/image_factory.rb +2 -6
  63. data/lib/spree/testing_support/factories/return_item_factory.rb +2 -2
  64. data/lib/spree/testing_support/factories/taxon_factory.rb +1 -1
  65. data/lib/spree/testing_support/i18n.rb +6 -8
  66. data/lib/spree/testing_support/image_helpers.rb +5 -11
  67. data/lib/tasks/exchanges.rake +35 -37
  68. data/spree_core.gemspec +11 -14
  69. metadata +63 -103
  70. data/app/models/concerns/spree/acts_as_taggable.rb +0 -11
  71. data/app/models/spree/image/configuration/paperclip.rb +0 -64
  72. data/app/models/spree/order_contents.rb +0 -54
  73. data/app/models/spree/product/scopes.rb +0 -250
  74. data/app/models/spree/tag.rb +0 -4
  75. data/app/models/spree/taxon_image/configuration/paperclip.rb +0 -27
  76. data/config/initializers/acts_as_taggable_on.rb +0 -9
  77. data/config/initializers/use_paperclip.rb +0 -3
  78. data/db/migrate/20160511071954_acts_as_taggable_on_spree_migration.rb +0 -40
  79. data/db/migrate/20160511072249_change_collation_for_spree_tag_names.rb +0 -9
  80. data/db/migrate/20160511072335_add_missing_indexes_to_spree_taggings.rb +0 -14
  81. data/lib/spree/core/controller_helpers/respond_with.rb +0 -67
  82. data/lib/spree/responder.rb +0 -44
  83. data/lib/spree/testing_support/factories/tag_factory.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9cc428b39c96e67b7e2e583875d442facf63b1bd6c9c26330db42ca684eb99d
4
- data.tar.gz: 3b79b1afc23ccf9ae7a68799564d1030c1a8dd8de24f317b94ea70a81fd351b1
3
+ metadata.gz: 5064fe41cb92fc49b65d326a8104d304e34e53b14cd8ad841207bef116b35464
4
+ data.tar.gz: c5288fc5c3b503961af981fe4c374e4bafcb93cb117dcd6e9be983d2c06f96e8
5
5
  SHA512:
6
- metadata.gz: 0f2ed5997f20f2d97d8311cc279b87f7ec86df92dbfa177bdfd32ed5875d5ae3038e663318ca505bac3b0e7b5dd535b74950146d0810051a189de35723482f64
7
- data.tar.gz: fd547459e0f2a51a96de5de296ee228e9ca80f70f28e5020b7bf7d460a26520c565a2e6a78aba2296b570d752da3f7c4bdf850ef3d951ecc76c4681f4c5138ea
6
+ metadata.gz: b70df4383270e04fc466c704e7d97ca152793bb74eec3ca7f66cbe901700671255eb2d94016b66031f43f57fa40ab9817f3498a272624a383a392d998ff675ed
7
+ data.tar.gz: 2eaf71128b93b12a4502d4bdb0ef0719ab39c480968a8436b422a723d6c7c43e97c323df636259fe03891fbf924c3043fafab4dc1e505fafa13317717a4c6fbf
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- eval(File.read(File.dirname(__FILE__) + '/../common_spree_dependencies.rb'))
1
+ eval_gemfile('../common_spree_dependencies.rb')
2
2
 
3
3
  gemspec
@@ -3,7 +3,6 @@ require_dependency 'spree/core/controller_helpers/strong_parameters'
3
3
 
4
4
  class Spree::BaseController < ApplicationController
5
5
  include Spree::Core::ControllerHelpers::Auth
6
- include Spree::Core::ControllerHelpers::RespondWith
7
6
  include Spree::Core::ControllerHelpers::Common
8
7
  include Spree::Core::ControllerHelpers::Search
9
8
  include Spree::Core::ControllerHelpers::Store
@@ -3,11 +3,12 @@ module Spree
3
3
  class Find
4
4
  def initialize(scope:, params:)
5
5
  @scope = scope
6
- @ids = String(params.dig(:filter, :ids)).split(',')
7
- @parent = params.dig(:filter, :parent_id)
8
- @taxonomy = params.dig(:filter, :taxonomy_id)
9
- @name = params.dig(:filter, :name)
10
- @roots = params.dig(:filter, :roots)
6
+
7
+ @ids = String(params[:ids]).split(',')
8
+ @parent = params[:parent_id]
9
+ @taxonomy = params[:taxonomy_id]
10
+ @name = params[:name]
11
+ @roots = params[:roots]
11
12
  end
12
13
 
13
14
  def execute
@@ -79,15 +79,6 @@ module Spree
79
79
  spree.nested_taxons_path(taxon.permalink)
80
80
  end
81
81
 
82
- # human readable list of variant options
83
- def variant_options(variant, _options = {})
84
- ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
85
- BaseHelper#variant_options is deprecated and will be removed in Spree 4.0.
86
- Please use Variant#options_text or LineItem#options_text
87
- DEPRECATION
88
- variant.options_text
89
- end
90
-
91
82
  def frontend_available?
92
83
  Spree::Core::Engine.frontend_available?
93
84
  end
@@ -36,7 +36,7 @@ module Spree
36
36
  else
37
37
  product.description.to_s.gsub(/(.*?)\r?\n\r?\n/m, '<p>\1</p>')
38
38
  end
39
- description.blank? ? Spree.t(:product_has_no_description) : raw(description)
39
+ description.blank? ? Spree.t(:product_has_no_description) : description
40
40
  end
41
41
 
42
42
  def line_item_description_text(description_text)
@@ -0,0 +1,254 @@
1
+ module Spree
2
+ module ProductScopes
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ cattr_accessor :search_scopes do
7
+ []
8
+ end
9
+
10
+ def self.add_search_scope(name, &block)
11
+ singleton_class.send(:define_method, name.to_sym, &block)
12
+ search_scopes << name.to_sym
13
+ end
14
+
15
+ def self.simple_scopes
16
+ [
17
+ :ascend_by_updated_at,
18
+ :descend_by_updated_at,
19
+ :ascend_by_name,
20
+ :descend_by_name
21
+ ]
22
+ end
23
+
24
+ def self.add_simple_scopes(scopes)
25
+ scopes.each do |name|
26
+ # We should not define price scopes here, as they require something slightly different
27
+ next if name.to_s.include?('master_price')
28
+
29
+ parts = name.to_s.match(/(.*)_by_(.*)/)
30
+ scope(name.to_s, -> { order(Arel.sql("#{Product.quoted_table_name}.#{parts[2]} #{parts[1] == 'ascend' ? 'ASC' : 'DESC'}")) })
31
+ end
32
+ end
33
+
34
+ def self.property_conditions(property)
35
+ properties = Property.table_name
36
+ case property
37
+ when String then { "#{properties}.name" => property }
38
+ when Property then { "#{properties}.id" => property.id }
39
+ else { "#{properties}.id" => property.to_i }
40
+ end
41
+ end
42
+
43
+ add_simple_scopes simple_scopes
44
+
45
+ add_search_scope :ascend_by_master_price do
46
+ joins(master: :default_price).order("#{price_table_name}.amount ASC")
47
+ end
48
+
49
+ add_search_scope :descend_by_master_price do
50
+ joins(master: :default_price).order("#{price_table_name}.amount DESC")
51
+ end
52
+
53
+ add_search_scope :price_between do |low, high|
54
+ joins(master: :default_price).where(Price.table_name => { amount: low..high })
55
+ end
56
+
57
+ add_search_scope :master_price_lte do |price|
58
+ joins(master: :default_price).where("#{price_table_name}.amount <= ?", price)
59
+ end
60
+
61
+ add_search_scope :master_price_gte do |price|
62
+ joins(master: :default_price).where("#{price_table_name}.amount >= ?", price)
63
+ end
64
+
65
+ # This scope selects products in taxon AND all its descendants
66
+ # If you need products only within one taxon use
67
+ #
68
+ # Spree::Product.joins(:taxons).where(Taxon.table_name => { id: taxon.id })
69
+ #
70
+ # If you're using count on the result of this scope, you must use the
71
+ # `:distinct` option as well:
72
+ #
73
+ # Spree::Product.in_taxon(taxon).count(distinct: true)
74
+ #
75
+ # This is so that the count query is distinct'd:
76
+ #
77
+ # SELECT COUNT(DISTINCT "spree_products"."id") ...
78
+ #
79
+ # vs.
80
+ #
81
+ # SELECT COUNT(*) ...
82
+ add_search_scope :in_taxon do |taxon|
83
+ includes(:classifications).
84
+ where('spree_products_taxons.taxon_id' => taxon.self_and_descendants.pluck(:id)).
85
+ order('spree_products_taxons.position ASC')
86
+ end
87
+
88
+ # This scope selects products in all taxons AND all its descendants
89
+ # If you need products only within one taxon use
90
+ #
91
+ # Spree::Product.taxons_id_eq([x,y])
92
+ add_search_scope :in_taxons do |*taxons|
93
+ taxons = get_taxons(taxons)
94
+ taxons.first ? prepare_taxon_conditions(taxons) : where(nil)
95
+ end
96
+
97
+ # a scope that finds all products having property specified by name, object or id
98
+ add_search_scope :with_property do |property|
99
+ joins(:properties).where(property_conditions(property))
100
+ end
101
+
102
+ # a simple test for product with a certain property-value pairing
103
+ # note that it can test for properties with NULL values, but not for absent values
104
+ add_search_scope :with_property_value do |property, value|
105
+ joins(:properties).
106
+ where("#{ProductProperty.table_name}.value = ?", value).
107
+ where(property_conditions(property))
108
+ end
109
+
110
+ add_search_scope :with_option do |option|
111
+ option_types = OptionType.table_name
112
+ conditions = case option
113
+ when String then { "#{option_types}.name" => option }
114
+ when OptionType then { "#{option_types}.id" => option.id }
115
+ else { "#{option_types}.id" => option.to_i }
116
+ end
117
+
118
+ joins(:option_types).where(conditions)
119
+ end
120
+
121
+ add_search_scope :with_option_value do |option, value|
122
+ option_values = OptionValue.table_name
123
+ option_type_id = case option
124
+ when String then OptionType.find_by(name: option) || option.to_i
125
+ when OptionType then option.id
126
+ else option.to_i
127
+ end
128
+
129
+ conditions = "#{option_values}.name = ? AND #{option_values}.option_type_id = ?", value, option_type_id
130
+ group('spree_products.id').joins(variants_including_master: :option_values).where(conditions)
131
+ end
132
+
133
+ # Finds all products which have either:
134
+ # 1) have an option value with the name matching the one given
135
+ # 2) have a product property with a value matching the one given
136
+ add_search_scope :with do |value|
137
+ includes(variants_including_master: :option_values).
138
+ includes(:product_properties).
139
+ where("#{OptionValue.table_name}.name = ? OR #{ProductProperty.table_name}.value = ?", value, value)
140
+ end
141
+
142
+ # Finds all products that have a name containing the given words.
143
+ add_search_scope :in_name do |words|
144
+ like_any([:name], prepare_words(words))
145
+ end
146
+
147
+ # Finds all products that have a name or meta_keywords containing the given words.
148
+ add_search_scope :in_name_or_keywords do |words|
149
+ like_any([:name, :meta_keywords], prepare_words(words))
150
+ end
151
+
152
+ # Finds all products that have a name, description, meta_description or meta_keywords containing the given keywords.
153
+ add_search_scope :in_name_or_description do |words|
154
+ like_any([:name, :description, :meta_description, :meta_keywords], prepare_words(words))
155
+ end
156
+
157
+ # Finds all products that have the ids matching the given collection of ids.
158
+ # Alternatively, you could use find(collection_of_ids), but that would raise an exception if one product couldn't be found
159
+ add_search_scope :with_ids do |*ids|
160
+ where(id: ids)
161
+ end
162
+
163
+ # Sorts products from most popular (popularity is extracted from how many
164
+ # times use has put product in cart, not completed orders)
165
+ #
166
+ # there is alternative faster and more elegant solution, it has small drawback though,
167
+ # it doesn stack with other scopes :/
168
+ #
169
+ # joins: "LEFT OUTER JOIN (SELECT line_items.variant_id as vid, COUNT(*) as cnt FROM line_items GROUP BY line_items.variant_id) AS popularity_count ON variants.id = vid",
170
+ # order: 'COALESCE(cnt, 0) DESC'
171
+ add_search_scope :descend_by_popularity do
172
+ joins(:master).
173
+ order(%Q{
174
+ COALESCE((
175
+ SELECT
176
+ COUNT(#{LineItem.quoted_table_name}.id)
177
+ FROM
178
+ #{LineItem.quoted_table_name}
179
+ JOIN
180
+ #{Variant.quoted_table_name} AS popular_variants
181
+ ON
182
+ popular_variants.id = #{LineItem.quoted_table_name}.variant_id
183
+ WHERE
184
+ popular_variants.product_id = #{Product.quoted_table_name}.id
185
+ ), 0) DESC
186
+ })
187
+ end
188
+
189
+ add_search_scope :not_deleted do
190
+ where("#{Product.quoted_table_name}.deleted_at IS NULL or #{Product.quoted_table_name}.deleted_at >= ?", Time.zone.now)
191
+ end
192
+
193
+ def self.not_discontinued(only_not_discontinued = true)
194
+ if only_not_discontinued != '0' && only_not_discontinued
195
+ where("#{Product.quoted_table_name}.discontinue_on IS NULL or #{Product.quoted_table_name}.discontinue_on >= ?", Time.zone.now)
196
+ else
197
+ all
198
+ end
199
+ end
200
+ search_scopes << :not_discontinued
201
+ # Can't use add_search_scope for this as it needs a default argument
202
+ def self.available(available_on = nil, _currency = nil)
203
+ available_on ||= Time.current
204
+ not_discontinued.joins(master: :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on)
205
+ end
206
+ search_scopes << :available
207
+
208
+ def self.active(currency = nil)
209
+ available(nil, currency)
210
+ end
211
+ search_scopes << :active
212
+
213
+ add_search_scope :taxons_name_eq do |name|
214
+ group('spree_products.id').joins(:taxons).where(Taxon.arel_table[:name].eq(name))
215
+ end
216
+
217
+ def self.price_table_name
218
+ Price.quoted_table_name
219
+ end
220
+ private_class_method :price_table_name
221
+
222
+ # specifically avoid having an order for taxon search (conflicts with main order)
223
+ def self.prepare_taxon_conditions(taxons)
224
+ ids = taxons.map { |taxon| taxon.self_and_descendants.pluck(:id) }.flatten.uniq
225
+ joins(:classifications).where(Classification.table_name => { taxon_id: ids })
226
+ end
227
+ private_class_method :prepare_taxon_conditions
228
+
229
+ # Produce an array of keywords for use in scopes.
230
+ # Always return array with at least an empty string to avoid SQL errors
231
+ def self.prepare_words(words)
232
+ return [''] if words.blank?
233
+
234
+ a = words.split(/[,\s]/).map(&:strip)
235
+ a.any? ? a : ['']
236
+ end
237
+ private_class_method :prepare_words
238
+
239
+ def self.get_taxons(*ids_or_records_or_names)
240
+ taxons = Taxon.table_name
241
+ ids_or_records_or_names.flatten.map do |t|
242
+ case t
243
+ when Integer then Taxon.find_by(id: t)
244
+ when ApplicationRecord then t
245
+ when String
246
+ Taxon.find_by(name: t) ||
247
+ Taxon.where("#{taxons}.permalink LIKE ? OR #{taxons}.permalink = ?", "%/#{t}/", "#{t}/").first
248
+ end
249
+ end.compact.flatten.uniq
250
+ end
251
+ private_class_method :get_taxons
252
+ end
253
+ end
254
+ end
@@ -13,18 +13,21 @@ module Spree
13
13
 
14
14
  accepts_nested_attributes_for :ship_address, :bill_address
15
15
 
16
+ has_many :addresses, -> { where(deleted_at: nil).order('updated_at DESC') },
17
+ class_name: 'Spree::Address', foreign_key: :user_id
18
+
16
19
  def persist_order_address(order)
17
20
  b_address = bill_address || build_bill_address
18
21
  b_address.attributes = order.bill_address.value_attributes
19
22
  b_address.save
20
- update_attributes(bill_address_id: b_address.id)
23
+ update(bill_address_id: b_address.id)
21
24
 
22
25
  # May not be present if delivery step has been removed
23
26
  if order.ship_address
24
27
  s_address = ship_address || build_ship_address
25
28
  s_address.attributes = order.ship_address.value_attributes
26
29
  s_address.save
27
- update_attributes(ship_address_id: s_address.id)
30
+ update(ship_address_id: s_address.id)
28
31
  end
29
32
  end
30
33
  end
@@ -1,3 +1,9 @@
1
- FriendlyId::Slug.class_eval do
2
- acts_as_paranoid
1
+ module FriendlyId
2
+ module SlugDecorator
3
+ def self.prepended(base)
4
+ base.acts_as_paranoid
5
+ end
6
+ end
3
7
  end
8
+
9
+ FriendlyId::Slug.prepend FriendlyId::SlugDecorator
@@ -23,15 +23,8 @@ module Spree
23
23
  end
24
24
 
25
25
  def initialize(user)
26
- clear_aliased_actions
27
-
28
- # override cancan default aliasing (we don't want to differentiate between read and index)
26
+ # add cancancan aliasing
29
27
  alias_action :delete, to: :destroy
30
- alias_action :edit, to: :update
31
- alias_action :new, to: :create
32
- alias_action :new_action, to: :create
33
- alias_action :show, to: :read
34
- alias_action :index, :read, to: :display
35
28
  alias_action :create, :update, :destroy, to: :modify
36
29
 
37
30
  user ||= Spree.user_class.new
@@ -39,27 +32,33 @@ module Spree
39
32
  if user.respond_to?(:has_spree_role?) && user.has_spree_role?('admin')
40
33
  can :manage, :all
41
34
  else
42
- can :display, Country
43
- can :display, OptionType
44
- can :display, OptionValue
35
+ can :read, Country
36
+ can :read, OptionType
37
+ can :read, OptionValue
45
38
  can :create, Order
46
- can :read, Order do |order, token|
39
+ can :show, Order do |order, token|
47
40
  order.user == user || order.token && token == order.token
48
41
  end
49
42
  can :update, Order do |order, token|
50
43
  !order.completed? && (order.user == user || order.token && token == order.token)
51
44
  end
52
- can :display, CreditCard, user_id: user.id
53
- can :display, Product
54
- can :display, ProductProperty
55
- can :display, Property
45
+ can :manage, Spree::Address do |address|
46
+ address.user == user
47
+ end
48
+ can :create, Spree::Address do |_address|
49
+ user.id.present?
50
+ end
51
+ can :read, CreditCard, user_id: user.id
52
+ can :read, Product
53
+ can :read, ProductProperty
54
+ can :read, Property
56
55
  can :create, Spree.user_class
57
- can [:read, :update, :destroy], Spree.user_class, id: user.id
58
- can :display, State
59
- can :display, Taxon
60
- can :display, Taxonomy
61
- can :display, Variant
62
- can :display, Zone
56
+ can [:show, :update, :destroy], Spree.user_class, id: user.id
57
+ can :read, State
58
+ can :read, Taxon
59
+ can :read, Taxonomy
60
+ can :read, Variant
61
+ can :read, Zone
63
62
  end
64
63
 
65
64
  # Include any abilities registered by extensions, etc.
@@ -12,10 +12,12 @@ module Spree
12
12
 
13
13
  # we're not freezing this on purpose so developers can extend and manage
14
14
  # those attributes depending of the logic of their applications
15
- EXCLUDED_KEYS_FOR_COMPARISION = %w(id updated_at created_at)
15
+ ADDRESS_FIELDS = %w(firstname lastname company address1 address2 city state zipcode country phone)
16
+ EXCLUDED_KEYS_FOR_COMPARISION = %w(id updated_at created_at deleted_at user_id)
16
17
 
17
18
  belongs_to :country, class_name: 'Spree::Country'
18
19
  belongs_to :state, class_name: 'Spree::State', optional: true
20
+ belongs_to :user, class_name: Spree.user_class.name, optional: true
19
21
 
20
22
  has_many :shipments, inverse_of: :address
21
23
 
@@ -49,12 +51,10 @@ module Spree
49
51
  end
50
52
  end
51
53
 
52
- def iso_name
53
- ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
54
- Address#iso_name is deprecated and will be removed in Spree 4.0.
55
- Please use Address#country_iso_name instead
56
- DEPRECATION
57
- country_iso_name
54
+ def self.required_fields
55
+ Spree::Address.validators.map do |v|
56
+ v.is_a?(ActiveModel::Validations::PresenceValidator) ? v.attributes : []
57
+ end.flatten
58
58
  end
59
59
 
60
60
  def full_name
@@ -69,24 +69,15 @@ module Spree
69
69
  state_name.present? ? state_name : state&.name
70
70
  end
71
71
 
72
- def same_as?(other)
73
- ActiveSupport::Deprecation.warn(<<-EOS, caller)
74
- Address#same_as? is deprecated and will be removed in Spree 4.0. Please use Address#== instead"
75
- EOS
76
-
77
- self == other
78
- end
79
-
80
- def same_as(other)
81
- ActiveSupport::Deprecation.warn(<<-EOS, caller)
82
- Address#same_as is deprecated and will be removed in Spree 4.0. Please use Address#== instead"
83
- EOS
84
-
85
- self == other
86
- end
87
-
88
72
  def to_s
89
- "#{full_name}: #{address1}"
73
+ [
74
+ full_name,
75
+ company,
76
+ address1,
77
+ address2,
78
+ "#{city}, #{state_text} #{zipcode}",
79
+ country.to_s
80
+ ].reject(&:blank?).map { |attribute| ERB::Util.html_escape(attribute) }.join('<br/>')
90
81
  end
91
82
 
92
83
  def clone
@@ -129,6 +120,28 @@ module Spree
129
120
  country ? country.zipcode_required? : true
130
121
  end
131
122
 
123
+ def editable?
124
+ new_record? || (shipments.empty? && !Order.complete.where('bill_address_id = ? OR ship_address_id = ?', id, id).exists?)
125
+ end
126
+
127
+ def can_be_deleted?
128
+ shipments.empty? && !Order.where('bill_address_id = ? OR ship_address_id = ?', id, id).exists?
129
+ end
130
+
131
+ def check
132
+ attrs = attributes.except('id', 'updated_at', 'created_at')
133
+ the_same_address = user&.addresses&.find_by(attrs)
134
+ the_same_address || self
135
+ end
136
+
137
+ def destroy
138
+ if can_be_deleted?
139
+ super
140
+ else
141
+ update_column :deleted_at, Time.current
142
+ end
143
+ end
144
+
132
145
  private
133
146
 
134
147
  def clear_state