spree_core 2.4.10 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/app/assets/images/logo/spree_50.png +0 -0
  4. data/app/controllers/spree/base_controller.rb +1 -2
  5. data/app/helpers/spree/base_helper.rb +41 -131
  6. data/app/helpers/spree/products_helper.rb +1 -1
  7. data/app/models/concerns/spree/adjustment_source.rb +51 -15
  8. data/app/models/concerns/spree/calculated_adjustments.rb +1 -0
  9. data/app/models/concerns/spree/display_money.rb +32 -0
  10. data/app/models/concerns/spree/named_type.rb +1 -1
  11. data/app/models/concerns/spree/number_generator.rb +39 -0
  12. data/app/models/concerns/spree/user_reporting.rb +3 -8
  13. data/app/models/spree/address.rb +0 -2
  14. data/app/models/spree/adjustable/adjustments_updater.rb +70 -0
  15. data/app/models/spree/adjustable/promotion_accumulator.rb +75 -0
  16. data/app/models/spree/adjustment.rb +17 -26
  17. data/app/models/spree/app_configuration.rb +4 -33
  18. data/app/models/spree/base.rb +0 -3
  19. data/app/models/spree/calculator.rb +0 -5
  20. data/app/models/spree/classification.rb +1 -1
  21. data/app/models/spree/country.rb +10 -4
  22. data/app/models/spree/credit_card.rb +14 -15
  23. data/app/models/spree/customer_return.rb +18 -25
  24. data/app/models/spree/gateway/bogus.rb +0 -4
  25. data/app/models/spree/line_item.rb +2 -5
  26. data/app/models/spree/option_type.rb +2 -4
  27. data/app/models/spree/option_value.rb +0 -2
  28. data/app/models/spree/order.rb +74 -134
  29. data/app/models/spree/order/checkout.rb +1 -1
  30. data/app/models/spree/order_contents.rb +9 -8
  31. data/app/models/spree/order_updater.rb +8 -1
  32. data/app/models/spree/payment.rb +13 -23
  33. data/app/models/spree/payment/gateway_options.rb +86 -0
  34. data/app/models/spree/payment/processing.rb +8 -39
  35. data/app/models/spree/payment_method.rb +3 -6
  36. data/app/models/spree/price.rb +2 -6
  37. data/app/models/spree/product.rb +27 -16
  38. data/app/models/spree/product_property.rb +1 -5
  39. data/app/models/spree/promotion.rb +7 -15
  40. data/app/models/spree/promotion/actions/create_adjustment.rb +4 -45
  41. data/app/models/spree/promotion/actions/create_item_adjustments.rb +8 -63
  42. data/app/models/spree/promotion/actions/create_line_items.rb +3 -12
  43. data/app/models/spree/promotion/actions/free_shipping.rb +4 -24
  44. data/app/models/spree/promotion/rules/option_value.rb +49 -0
  45. data/app/models/spree/promotion_action.rb +6 -0
  46. data/app/models/spree/promotion_handler/cart.rb +14 -18
  47. data/app/models/spree/promotion_handler/coupon.rb +1 -1
  48. data/app/models/spree/promotion_rule.rb +0 -1
  49. data/app/models/spree/property.rb +0 -2
  50. data/app/models/spree/refund.rb +0 -15
  51. data/app/models/spree/reimbursement.rb +4 -4
  52. data/app/models/spree/reimbursement_type/credit.rb +1 -1
  53. data/app/models/spree/reimbursement_type/original_payment.rb +1 -1
  54. data/app/models/spree/return_authorization.rb +4 -11
  55. data/app/models/spree/return_item.rb +16 -11
  56. data/app/models/spree/return_item/eligibility_validator/default.rb +2 -0
  57. data/app/models/spree/return_item/eligibility_validator/inventory_shipped.rb +16 -0
  58. data/app/models/spree/return_item/eligibility_validator/no_reimbursements.rb +16 -0
  59. data/app/models/spree/shipment.rb +20 -31
  60. data/app/models/spree/shipment_handler.rb +1 -1
  61. data/app/models/spree/shipping_method.rb +12 -13
  62. data/app/models/spree/state.rb +7 -0
  63. data/app/models/spree/state_change.rb +1 -6
  64. data/app/models/spree/stock/availability_validator.rb +9 -10
  65. data/app/models/spree/stock/coordinator.rb +1 -1
  66. data/app/models/spree/stock/estimator.rb +6 -6
  67. data/app/models/spree/stock/quantifier.rb +1 -1
  68. data/app/models/spree/stock_item.rb +1 -7
  69. data/app/models/spree/stock_location.rb +3 -1
  70. data/app/models/spree/stock_movement.rb +1 -8
  71. data/app/models/spree/stock_transfer.rb +11 -5
  72. data/app/models/spree/store.rb +2 -2
  73. data/app/models/spree/tax_rate.rb +30 -55
  74. data/app/models/spree/taxon.rb +8 -8
  75. data/app/models/spree/taxonomy.rb +8 -13
  76. data/app/models/spree/tracker.rb +1 -1
  77. data/app/models/spree/variant.rb +6 -15
  78. data/app/models/spree/zone.rb +43 -9
  79. data/config/initializers/user_class_extensions.rb +0 -12
  80. data/config/locales/en.yml +76 -85
  81. data/config/routes.rb +1 -1
  82. data/db/default/spree/countries.rb +23 -13
  83. data/db/default/spree/states.rb +24 -12
  84. data/db/default/spree/stores.rb +1 -1
  85. data/db/migrate/20120831092320_spree_one_two.rb +36 -36
  86. data/db/migrate/20120831092359_spree_promo_one_two.rb +1 -1
  87. data/db/migrate/20130211190146_create_spree_stock_items.rb +1 -1
  88. data/db/migrate/20130211191120_create_spree_stock_locations.rb +1 -1
  89. data/db/migrate/20130301162924_create_shipping_method_categories.rb +1 -1
  90. data/db/migrate/20130304162240_create_spree_shipping_rates.rb +1 -1
  91. data/db/migrate/20130305143310_create_stock_movements.rb +1 -1
  92. data/db/migrate/20130418125341_create_spree_stock_transfers.rb +1 -1
  93. data/db/migrate/20140205120320_create_spree_payment_capture_events.rb +1 -1
  94. data/db/migrate/20140309024355_create_spree_stores.rb +1 -1
  95. data/db/migrate/20140309033438_create_store_from_preferences.rb +0 -7
  96. data/db/migrate/20140625214618_create_spree_refunds.rb +1 -1
  97. data/db/migrate/20140702140656_create_spree_return_authorization_inventory_unit.rb +1 -1
  98. data/db/migrate/20140713140455_create_spree_return_authorization_reasons.rb +1 -1
  99. data/db/migrate/20140713140527_create_spree_refund_reasons.rb +1 -1
  100. data/db/migrate/20140715182625_create_spree_promotion_categories.rb +1 -1
  101. data/db/migrate/20140718133010_create_spree_customer_returns.rb +1 -1
  102. data/db/migrate/20140725131539_create_spree_reimbursements.rb +1 -1
  103. data/db/migrate/20140731150017_create_spree_reimbursement_types.rb +1 -1
  104. data/db/migrate/20140911173301_add_kind_to_zone.rb +11 -0
  105. data/db/migrate/20141215232040_remove_token_permissions_table.rb +6 -0
  106. data/db/migrate/20141215235502_remove_extra_products_slug_index.rb +5 -0
  107. data/db/migrate/20141217215630_update_product_slug_index.rb +6 -0
  108. data/db/migrate/20141218025915_rename_identifier_to_number_for_payment.rb +5 -0
  109. data/db/migrate/20150121022521_remove_environment_from_payment_method.rb +6 -0
  110. data/db/migrate/20150122145607_add_resellable_to_return_items.rb +5 -0
  111. data/db/migrate/20150122202432_add_code_to_spree_promotion_categories.rb +5 -0
  112. data/db/migrate/20150128032538_remove_environment_from_tracker.rb +6 -0
  113. data/db/migrate/20150128060325_remove_spree_configurations.rb +16 -0
  114. data/lib/generators/spree/dummy/templates/rails/test.rb +1 -1
  115. data/lib/generators/spree/install/templates/config/initializers/spree.rb +4 -0
  116. data/lib/spree/core.rb +2 -12
  117. data/lib/spree/core/controller_helpers/respond_with.rb +8 -18
  118. data/lib/spree/core/engine.rb +1 -7
  119. data/lib/spree/core/routes.rb +1 -1
  120. data/lib/spree/core/version.rb +1 -1
  121. data/lib/spree/money.rb +10 -10
  122. data/lib/spree/permitted_attributes.rb +4 -8
  123. data/lib/spree/testing_support/authorization_helpers.rb +3 -5
  124. data/lib/spree/testing_support/capybara_ext.rb +3 -3
  125. data/lib/spree/testing_support/common_rake.rb +2 -2
  126. data/lib/spree/testing_support/factories/order_factory.rb +3 -13
  127. data/lib/spree/testing_support/factories/payment_method_factory.rb +0 -3
  128. data/lib/spree/testing_support/factories/prototype_factory.rb +5 -0
  129. data/lib/spree/testing_support/factories/return_item_factory.rb +5 -1
  130. data/lib/spree/testing_support/factories/stock_location_factory.rb +3 -3
  131. data/lib/spree/testing_support/factories/tracker_factory.rb +0 -1
  132. data/lib/spree/testing_support/factories/user_factory.rb +1 -1
  133. data/lib/spree/testing_support/factories/zone_factory.rb +8 -0
  134. data/lib/tasks/core.rake +1 -1
  135. data/lib/tasks/email.rake +6 -3
  136. metadata +48 -35
  137. data/app/helpers/spree/checkout_helper.rb +0 -31
  138. data/app/helpers/spree/orders_helper.rb +0 -17
  139. data/app/helpers/spree/store_helper.rb +0 -16
  140. data/app/helpers/spree/taxons_helper.rb +0 -19
  141. data/app/models/concerns/spree/ransackable_attributes.rb +0 -19
  142. data/app/models/friendly_id/slug_decorator.rb +0 -3
  143. data/app/models/spree/billing_integration.rb +0 -21
  144. data/app/models/spree/configuration.rb +0 -5
  145. data/app/models/spree/item_adjustments.rb +0 -82
  146. data/app/models/spree/order_merger.rb +0 -65
  147. data/app/models/spree/order_populator.rb +0 -43
  148. data/app/models/spree/product_scope/scopes.rb +0 -47
  149. data/app/models/spree/variant/scopes.rb +0 -42
  150. data/db/migrate/20150515211137_fix_adjustment_order_id.rb +0 -70
  151. data/db/migrate/20150522181728_add_deleted_at_to_friendly_id_slugs.rb +0 -6
  152. data/db/migrate/20150707204155_enable_acts_as_paranoid_on_calculators.rb +0 -6
  153. data/lib/spree/core/controller_helpers/ssl.rb +0 -60
  154. data/lib/spree/core/permalinks.rb +0 -71
  155. data/lib/spree/testing_support/factories/configuration_factory.rb +0 -6
@@ -31,7 +31,7 @@ module Spree
31
31
  # Returns an array of Package instances
32
32
  def build_packages(packages = Array.new)
33
33
  StockLocation.active.each do |stock_location|
34
- next unless stock_location.stock_items.where(:variant_id => inventory_units.map(&:variant_id)).exists?
34
+ next unless stock_location.stock_items.where(:variant_id => inventory_units.map(&:variant_id).uniq).exists?
35
35
 
36
36
  packer = build_packer(stock_location, inventory_units)
37
37
  packages += packer.packages
@@ -8,9 +8,8 @@ module Spree
8
8
  @currency = order.currency
9
9
  end
10
10
 
11
- def shipping_rates(package, frontend_only = true)
12
- rates = calculate_shipping_rates(package)
13
- rates.select! { |rate| rate.shipping_method.frontend? } if frontend_only
11
+ def shipping_rates(package, shipping_method_filter = ShippingMethod::DISPLAY_ON_FRONT_END)
12
+ rates = calculate_shipping_rates(package, shipping_method_filter)
14
13
  choose_default_shipping_rate(rates)
15
14
  sort_shipping_rates(rates)
16
15
  end
@@ -26,8 +25,8 @@ module Spree
26
25
  shipping_rates.sort_by!(&:cost)
27
26
  end
28
27
 
29
- def calculate_shipping_rates(package)
30
- shipping_methods(package).map do |shipping_method|
28
+ def calculate_shipping_rates(package, ui_filter)
29
+ shipping_methods(package, ui_filter).map do |shipping_method|
31
30
  cost = shipping_method.calculator.compute(package)
32
31
  tax_category = shipping_method.tax_category
33
32
  if tax_category
@@ -48,10 +47,11 @@ module Spree
48
47
  end.compact
49
48
  end
50
49
 
51
- def shipping_methods(package)
50
+ def shipping_methods(package, display_filter)
52
51
  package.shipping_methods.select do |ship_method|
53
52
  calculator = ship_method.calculator
54
53
  begin
54
+ ship_method.available_to_display(display_filter) &&
55
55
  ship_method.include?(order.ship_address) &&
56
56
  calculator.available?(package) &&
57
57
  (calculator.preferences[:currency].blank? ||
@@ -5,7 +5,7 @@ module Spree
5
5
 
6
6
  def initialize(variant)
7
7
  @variant = variant
8
- @stock_items = Spree::StockItem.joins(:stock_location).where(:variant_id => @variant, Spree::StockLocation.table_name =>{ :active => true})
8
+ @stock_items = Spree::StockItem.joins(:stock_location).where(variant_id: @variant, Spree::StockLocation.table_name => { active: true })
9
9
  end
10
10
 
11
11
  def total_on_hand
@@ -8,19 +8,13 @@ module Spree
8
8
 
9
9
  validates_presence_of :stock_location, :variant
10
10
  validates_uniqueness_of :variant_id, scope: [:stock_location_id, :deleted_at]
11
-
12
- validates_numericality_of :count_on_hand,
13
- greater_than_or_equal_to: 0,
14
- less_than_or_equal_to: 2**31 - 1,
15
- only_integer: true, if: :verify_count_on_hand?
11
+ validates :count_on_hand, numericality: { greater_than_or_equal_to: 0 }, if: :verify_count_on_hand?
16
12
 
17
13
  delegate :weight, :should_track_inventory?, to: :variant
18
14
 
19
15
  after_save :conditional_variant_touch, if: :changed?
20
16
  after_touch { variant.touch }
21
17
 
22
- self.whitelisted_ransackable_attributes = ['count_on_hand', 'stock_location_id']
23
-
24
18
  def backordered_inventory_units
25
19
  Spree::InventoryUnit.backordered_for_stock_item(self)
26
20
  end
@@ -107,7 +107,9 @@ module Spree
107
107
 
108
108
  private
109
109
  def create_stock_items
110
- Variant.find_each { |variant| self.propagate_variant(variant) }
110
+ Variant.includes(:product).find_each do |variant|
111
+ propagate_variant(variant)
112
+ end
111
113
  end
112
114
 
113
115
  def ensure_one_default
@@ -6,17 +6,10 @@ module Spree
6
6
  after_create :update_stock_item_quantity
7
7
 
8
8
  validates :stock_item, presence: true
9
- validates :quantity, presence: true, numericality: {
10
- greater_than_or_equal_to: -2**31,
11
- less_than_or_equal_to: 2**31-1,
12
- only_integer: true,
13
- allow_nil: true
14
- }
9
+ validates :quantity, presence: true
15
10
 
16
11
  scope :recent, -> { order('created_at DESC') }
17
12
 
18
- self.whitelisted_ransackable_attributes = ['quantity']
19
-
20
13
  def readonly?
21
14
  !new_record?
22
15
  end
@@ -1,13 +1,19 @@
1
1
  module Spree
2
2
  class StockTransfer < Spree::Base
3
- has_many :stock_movements, :as => :originator
3
+ extend FriendlyId
4
+ friendly_id :number, slug_column: :number, use: :slugged
4
5
 
5
- belongs_to :source_location, :class_name => 'StockLocation'
6
- belongs_to :destination_location, :class_name => 'StockLocation'
6
+ include Spree::NumberGenerator
7
7
 
8
- make_permalink field: :number, prefix: 'T'
8
+ def generate_number(options = {})
9
+ options[:prefix] ||= 'T'
10
+ super(options)
11
+ end
12
+
13
+ has_many :stock_movements, as: :originator
9
14
 
10
- self.whitelisted_ransackable_attributes = %w[reference source_location_id destination_location_id closed_at created_at number]
15
+ belongs_to :source_location, class_name: 'StockLocation'
16
+ belongs_to :destination_location, class_name: 'StockLocation'
11
17
 
12
18
  def to_param
13
19
  number
@@ -5,7 +5,7 @@ module Spree
5
5
  validates :url, presence: true
6
6
  validates :mail_from_address, presence: true
7
7
 
8
- before_save :ensure_default_exists_and_is_unique
8
+ before_create :ensure_default_exists_and_is_unique
9
9
  before_destroy :validate_not_default
10
10
 
11
11
  scope :by_url, lambda { |url| where("url like ?", "%#{url}%") }
@@ -23,7 +23,7 @@ module Spree
23
23
 
24
24
  def ensure_default_exists_and_is_unique
25
25
  if default
26
- Store.where.not(id: id).update_all(default: false)
26
+ Store.update_all(default: false)
27
27
  elsif Store.where(default: true).count == 0
28
28
  self.default = true
29
29
  end
@@ -12,27 +12,31 @@ module Spree
12
12
  class TaxRate < Spree::Base
13
13
  acts_as_paranoid
14
14
 
15
- # Need to deal with adjustments before calculator is destroyed.
16
- before_destroy :deals_with_adjustments_for_deleted_source
17
-
18
15
  include Spree::CalculatedAdjustments
19
16
  include Spree::AdjustmentSource
20
17
 
21
18
  belongs_to :zone, class_name: "Spree::Zone", inverse_of: :tax_rates
22
19
  belongs_to :tax_category, class_name: "Spree::TaxCategory", inverse_of: :tax_rates
23
20
 
24
- has_many :adjustments, as: :source
25
-
26
21
  validates :amount, presence: true, numericality: true
27
22
  validates :tax_category_id, presence: true
28
23
  validates_with DefaultTaxZoneValidator
29
24
 
30
25
  scope :by_zone, ->(zone) { where(zone_id: zone) }
31
26
 
27
+ def self.potential_rates_for_zone(zone)
28
+ select("spree_tax_rates.*, spree_zones.default_tax").
29
+ joins(:zone).
30
+ merge(Spree::Zone.potential_matching_zones(zone)).
31
+ order("spree_zones.default_tax DESC")
32
+ end
33
+
32
34
  # Gets the array of TaxRates appropriate for the specified order
33
35
  def self.match(order_tax_zone)
34
36
  return [] unless order_tax_zone
35
- rates = includes(zone: { zone_members: :zoneable }).load.select do |rate|
37
+
38
+ potential_rates = potential_rates_for_zone(order_tax_zone)
39
+ rates = potential_rates.includes(zone: { zone_members: :zoneable }).load.select do |rate|
36
40
  # Why "potentially"?
37
41
  # Go see the documentation for that method.
38
42
  rate.potentially_applicable?(order_tax_zone)
@@ -75,20 +79,16 @@ module Spree
75
79
  end
76
80
 
77
81
  # This method is best described by the documentation on #potentially_applicable?
78
- def self.adjust(order_tax_zone, items)
79
- rates = self.match(order_tax_zone)
82
+ def self.adjust(order, items)
83
+ rates = match(order.tax_zone)
80
84
  tax_categories = rates.map(&:tax_category)
81
85
  relevant_items, non_relevant_items = items.partition { |item| tax_categories.include?(item.tax_category) }
82
-
83
- if relevant_items.present?
84
- Spree::Adjustment.where(adjustable: relevant_items).tax.destroy_all # using destroy_all to ensure adjustment destroy callback fires.
85
- end
86
-
86
+ Spree::Adjustment.where(adjustable: relevant_items).tax.destroy_all # using destroy_all to ensure adjustment destroy callback fires.
87
87
  relevant_items.each do |item|
88
88
  relevant_rates = rates.select { |rate| rate.tax_category == item.tax_category }
89
89
  store_pre_tax_amount(item, relevant_rates)
90
90
  relevant_rates.each do |rate|
91
- rate.adjust(order_tax_zone, item)
91
+ rate.adjust(order, item)
92
92
  end
93
93
  end
94
94
  non_relevant_items.each do |item|
@@ -148,54 +148,29 @@ module Spree
148
148
  (self.included_in_price? && self.zone.default_tax)
149
149
  end
150
150
 
151
- # Creates necessary tax adjustments for the order.
152
- def adjust(order_tax_zone, item)
153
- amount = compute_amount(item)
154
- return if amount == 0
155
-
156
- included = included_in_price && default_zone_or_zone_match?(order_tax_zone)
157
-
158
- if amount < 0
159
- label = Spree.t(:refund) + ' ' + create_label
160
- end
161
-
162
- self.adjustments.create!({
163
- :adjustable => item,
164
- :amount => amount,
165
- :order_id => item.order_id,
166
- :label => label || create_label,
167
- :included => included
168
- })
151
+ def adjust(order, item)
152
+ included = included_in_price && default_zone_or_zone_match?(order)
153
+ create_adjustment(order, item, included)
169
154
  end
170
155
 
171
- # This method is used by Adjustment#update to recalculate the cost.
172
156
  def compute_amount(item)
173
- if included_in_price
174
- if default_zone_or_zone_match?(item.order.tax_zone)
175
- calculator.compute(item)
176
- else
177
- # In this case, it's a refund.
178
- calculator.compute(item) * - 1
179
- end
180
- else
181
- calculator.compute(item)
182
- end
183
- end
184
-
185
- def default_zone_or_zone_match?(order_tax_zone)
186
- default_tax = Zone.default_tax
187
- (default_tax && default_tax.contains?(order_tax_zone)) || order_tax_zone == self.zone
157
+ refund = included_in_price && !default_zone_or_zone_match?(item.order)
158
+ compute(item) * (refund ? -1 : 1)
188
159
  end
189
160
 
190
161
  private
191
162
 
192
- def create_label
193
- label = ""
194
- label << (name.present? ? name : tax_category.name) + " "
195
- label << (show_rate_in_label? ? "#{amount * 100}%" : "")
196
- label << " (#{Spree.t(:included_in_price)})" if included_in_price?
197
- label
198
- end
163
+ def default_zone_or_zone_match?(order)
164
+ Zone.default_tax.try(:contains?, order.tax_zone) || order.tax_zone == zone
165
+ end
199
166
 
167
+ def label(adjustment_amount)
168
+ label = ""
169
+ label << Spree.t(:refund) << ' ' if adjustment_amount < 0
170
+ label << (name.present? ? name : tax_category.name) + " "
171
+ label << (show_rate_in_label? ? "#{amount * 100}%" : "")
172
+ label << " (#{Spree.t(:included_in_price)})" if included_in_price?
173
+ label
174
+ end
200
175
  end
201
176
  end
@@ -1,5 +1,12 @@
1
+ # TODO let friendly id take care of sanitizing the url
2
+ require 'stringex'
3
+
1
4
  module Spree
2
5
  class Taxon < Spree::Base
6
+ extend FriendlyId
7
+ friendly_id :permalink, slug_column: :permalink, use: :slugged
8
+ before_create :set_permalink
9
+
3
10
  acts_as_nested_set dependent: :destroy
4
11
 
5
12
  belongs_to :taxonomy, class_name: 'Spree::Taxonomy', inverse_of: :taxons
@@ -8,8 +15,6 @@ module Spree
8
15
 
9
16
  has_and_belongs_to_many :prototypes, join_table: :spree_taxons_prototypes
10
17
 
11
- before_create :set_permalink
12
-
13
18
  validates :name, presence: true
14
19
  validates :meta_keywords, length: { maximum: 255 }
15
20
  validates :meta_description, length: { maximum: 255 }
@@ -49,7 +54,7 @@ module Spree
49
54
  end
50
55
  end
51
56
 
52
- # Creates permalink based on Stringex's .to_url method
57
+ # Creates permalink base for friendly_id
53
58
  def set_permalink
54
59
  if parent.present?
55
60
  self.permalink = [parent.permalink, (permalink.blank? ? name.to_url : permalink.split('/').last)].join('/')
@@ -58,11 +63,6 @@ module Spree
58
63
  end
59
64
  end
60
65
 
61
- # For #2759
62
- def to_param
63
- permalink
64
- end
65
-
66
66
  def active_products
67
67
  products.active
68
68
  end
@@ -1,27 +1,22 @@
1
1
  module Spree
2
2
  class Taxonomy < Spree::Base
3
- acts_as_list
4
-
5
3
  validates :name, presence: true
6
4
 
7
5
  has_many :taxons, inverse_of: :taxonomy
8
6
  has_one :root, -> { where parent_id: nil }, class_name: "Spree::Taxon", dependent: :destroy
9
7
 
10
- after_save :set_name
8
+ after_create :set_root
9
+ after_save :set_root_taxon_name
11
10
 
12
- default_scope -> { order("#{self.table_name}.position") }
11
+ default_scope { order("#{self.table_name}.position, #{self.table_name}.created_at") }
13
12
 
14
13
  private
15
- def set_name
16
- if root
17
- root.update_columns(
18
- name: name,
19
- updated_at: Time.now,
20
- )
21
- else
22
- self.root = Taxon.create!(taxonomy_id: id, name: name)
23
- end
14
+ def set_root
15
+ self.root ||= Taxon.create!(taxonomy_id: id, name: name)
24
16
  end
25
17
 
18
+ def set_root_taxon_name
19
+ root.update_attributes(name: name)
20
+ end
26
21
  end
27
22
  end
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class Tracker < Spree::Base
3
3
  def self.current
4
- tracker = where(active: true, environment: Rails.env).first
4
+ tracker = where(active: true).first
5
5
  tracker.analytics_id.present? ? tracker : nil if tracker
6
6
  end
7
7
  end
@@ -1,7 +1,6 @@
1
1
  module Spree
2
2
  class Variant < Spree::Base
3
3
  acts_as_paranoid
4
- acts_as_list scope: :product
5
4
 
6
5
  include Spree::DefaultPrice
7
6
 
@@ -37,23 +36,17 @@ module Spree
37
36
  validates_uniqueness_of :sku, allow_blank: true, conditions: -> { where(deleted_at: nil) }
38
37
 
39
38
  after_create :create_stock_items
39
+ after_create :set_position
40
40
  after_create :set_master_out_of_stock, unless: :is_master?
41
41
 
42
42
  after_touch :clear_in_stock_cache
43
43
 
44
44
  scope :in_stock, -> { joins(:stock_items).where('count_on_hand > ? OR track_inventory = ?', 0, false) }
45
45
 
46
- self.whitelisted_ransackable_associations = %w[option_values product prices default_price]
47
- self.whitelisted_ransackable_attributes = %w[weight sku]
48
-
49
46
  def self.active(currency = nil)
50
47
  joins(:prices).where(deleted_at: nil).where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL')
51
48
  end
52
49
 
53
- def self.having_orders
54
- joins(:line_items).distinct
55
- end
56
-
57
50
  def tax_category
58
51
  if self[:tax_category_id].nil?
59
52
  product.tax_category
@@ -96,10 +89,6 @@ module Spree
96
89
  is_master? ? name : options_text
97
90
  end
98
91
 
99
- def descriptive_name
100
- is_master? ? name + ' - Master' : name + ' - ' + options_text
101
- end
102
-
103
92
  # use deleted? rather than checking the attribute directly. this
104
93
  # allows extensions to override deleted? if they want to provide
105
94
  # their own definition.
@@ -179,7 +168,7 @@ module Spree
179
168
  return 0 unless options.present?
180
169
 
181
170
  options.keys.map { |key|
182
- m = "#{key}_price_modifier_amount".to_sym
171
+ m = "#{options[key]}_price_modifier_amount".to_sym
183
172
  if self.respond_to? m
184
173
  self.send(m, options[key])
185
174
  else
@@ -247,6 +236,10 @@ module Spree
247
236
  end
248
237
  end
249
238
 
239
+ def set_position
240
+ self.update_column(:position, product.variants.maximum(:position).to_i + 1)
241
+ end
242
+
250
243
  def in_stock_cache_key
251
244
  "variant-#{id}-in_stock"
252
245
  end
@@ -256,5 +249,3 @@ module Spree
256
249
  end
257
250
  end
258
251
  end
259
-
260
- require_dependency 'spree/variant/scopes'
@@ -2,21 +2,48 @@ module Spree
2
2
  class Zone < Spree::Base
3
3
  has_many :zone_members, dependent: :destroy, class_name: "Spree::ZoneMember", inverse_of: :zone
4
4
  has_many :tax_rates, dependent: :destroy, inverse_of: :zone
5
+ has_many :countries, through: :zone_members, source: :zoneable,
6
+ source_type: "Spree::Country"
7
+ has_many :states, through: :zone_members, source: :zoneable,
8
+ source_type: "Spree::State"
9
+
5
10
  has_and_belongs_to_many :shipping_methods, :join_table => 'spree_shipping_methods_zones'
6
11
 
7
12
  validates :name, presence: true, uniqueness: { allow_blank: true }
13
+
8
14
  after_save :remove_defunct_members
9
15
  after_save :remove_previous_default
10
16
 
11
17
  alias :members :zone_members
12
18
  accepts_nested_attributes_for :zone_members, allow_destroy: true, reject_if: proc { |a| a['zoneable_id'].blank? }
13
19
 
14
- self.whitelisted_ransackable_attributes = ['description']
15
-
16
20
  def self.default_tax
17
21
  where(default_tax: true).first
18
22
  end
19
23
 
24
+ def self.potential_matching_zones(zone)
25
+ if zone.country?
26
+ # Match zones of the same kind with simialr countries
27
+ joins(countries: :zones).
28
+ where('zone_members_spree_countries_join.zone_id = ? OR ' +
29
+ 'spree_zones.default_tax = ?', zone.id, true).
30
+ uniq
31
+ else
32
+ # Match zones of the same kind with similar states in AND match zones
33
+ # that have the states countries in
34
+ joins(:zone_members).where(
35
+ "(spree_zone_members.zoneable_type = 'Spree::State' AND
36
+ spree_zone_members.zoneable_id IN (?))
37
+ OR (spree_zone_members.zoneable_type = 'Spree::Country' AND
38
+ spree_zone_members.zoneable_id IN (?))
39
+ OR default_tax = ?",
40
+ zone.state_ids,
41
+ zone.states.pluck(:country_id),
42
+ true
43
+ ).uniq
44
+ end
45
+ end
46
+
20
47
  # Returns the matching zone with the highest priority zone type (State, Country, Zone.)
21
48
  # Returns nil in the case of no matches.
22
49
  def self.match(address)
@@ -34,13 +61,21 @@ module Spree
34
61
  end
35
62
 
36
63
  def kind
37
- if members.any? && !members.any? { |member| member.try(:zoneable_type).nil? }
38
- members.last.zoneable_type.demodulize.underscore
64
+ if kind?
65
+ super
66
+ else
67
+ not_nil_scope = members.where.not(zoneable_type: nil)
68
+ zone_type = not_nil_scope.order('created_at ASC').pluck(:zoneable_type).last
69
+ zone_type.demodulize.underscore if zone_type
39
70
  end
40
71
  end
41
72
 
42
- def kind=(value)
43
- # do nothing - just here to satisfy the form
73
+ def country?
74
+ kind == 'country'
75
+ end
76
+
77
+ def state?
78
+ kind == 'state'
44
79
  end
45
80
 
46
81
  def include?(address)
@@ -108,15 +143,14 @@ module Spree
108
143
  return false if zone_members.empty? || target.zone_members.empty?
109
144
 
110
145
  if kind == target.kind
111
- return false if (target.zoneables.collect(&:id) - zoneables.collect(&:id)).present?
146
+ return false if (target.countries.pluck(:id) - countries.pluck(:id)).present?
112
147
  else
113
- return false if (target.zoneables.collect(&:country).collect(&:id) - zoneables.collect(&:id)).present?
148
+ return false if (target.states.pluck(:country_id) - countries.pluck(:id)).present?
114
149
  end
115
150
  true
116
151
  end
117
152
 
118
153
  private
119
-
120
154
  def remove_defunct_members
121
155
  if zone_members.any?
122
156
  zone_members.where('zoneable_id IS NULL OR zoneable_type != ?', "Spree::#{kind.classify}").destroy_all