spree_core 3.7.14.1 → 4.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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