spree_core 2.0.3 → 2.0.4

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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/logo/spree_50.png +0 -0
  3. data/app/assets/images/noimage/large.png +0 -0
  4. data/app/assets/images/noimage/mini.png +0 -0
  5. data/app/assets/images/noimage/product.png +0 -0
  6. data/app/assets/images/noimage/small.png +0 -0
  7. data/app/controllers/spree/base_controller.rb +3 -0
  8. data/app/helpers/spree/base_helper.rb +15 -11
  9. data/app/models/spree/adjustment.rb +5 -0
  10. data/app/models/spree/app_configuration.rb +3 -3
  11. data/app/models/spree/calculator/default_tax.rb +2 -2
  12. data/app/models/spree/calculator/percent_per_item.rb +9 -5
  13. data/app/models/spree/calculator.rb +1 -1
  14. data/app/models/spree/credit_card.rb +25 -0
  15. data/app/models/spree/line_item.rb +9 -0
  16. data/app/models/spree/new_adjustment.rb +4 -0
  17. data/app/models/spree/order/checkout.rb +4 -5
  18. data/app/models/spree/order.rb +52 -25
  19. data/app/models/spree/order_contents.rb +8 -7
  20. data/app/models/spree/order_inventory.rb +13 -13
  21. data/app/models/spree/order_populator.rb +4 -14
  22. data/app/models/spree/order_updater.rb +1 -1
  23. data/app/models/spree/payment/processing.rb +5 -0
  24. data/app/models/spree/payment.rb +12 -1
  25. data/app/models/spree/payment_method.rb +1 -4
  26. data/app/models/spree/product.rb +44 -17
  27. data/app/models/spree/promotion/actions/create_adjustment.rb +1 -1
  28. data/app/models/spree/promotion/actions/create_line_items.rb +32 -2
  29. data/app/models/spree/promotion.rb +2 -1
  30. data/app/models/spree/shipment.rb +5 -5
  31. data/app/models/spree/shipping_adjustment.rb +4 -0
  32. data/app/models/spree/shipping_method.rb +6 -0
  33. data/app/models/spree/shipping_rate.rb +1 -0
  34. data/app/models/spree/stock/availability_validator.rb +5 -3
  35. data/app/models/spree/stock/coordinator.rb +16 -4
  36. data/app/models/spree/stock/estimator.rb +16 -6
  37. data/app/models/spree/stock/packer.rb +2 -1
  38. data/app/models/spree/stock_location.rb +16 -6
  39. data/app/models/spree/tax_category.rb +1 -7
  40. data/app/models/spree/tax_rate.rb +1 -0
  41. data/app/models/spree/variant.rb +16 -3
  42. data/app/models/spree/zone.rb +1 -1
  43. data/config/initializers/user_class_extensions.rb +1 -1
  44. data/config/locales/en.yml +15 -1
  45. data/db/migrate/20130228210442_create_shipping_method_zone.rb +6 -1
  46. data/db/migrate/20130306181701_add_address_fields_to_stock_location.rb +4 -1
  47. data/db/migrate/20130515180736_add_backorderable_default_to_spree_stock_location.rb +5 -0
  48. data/db/migrate/20130516151222_add_propage_all_variants_to_spree_stock_location.rb +5 -0
  49. data/db/migrate/20130626232741_add_cvv_result_code_and_cvv_result_message_to_spree_payments.rb +6 -0
  50. data/db/migrate/20130628021056_add_unique_index_to_permalink_on_spree_products.rb +5 -0
  51. data/db/migrate/20130628022817_add_unique_index_to_orders_shipments_and_stock_transfers.rb +7 -0
  52. data/db/migrate/20130708052307_add_deleted_at_to_spree_tax_rates.rb +5 -0
  53. data/db/migrate/20130711200933_remove_lock_version_from_inventory_units.rb +6 -0
  54. data/db/migrate/20130718042445_add_cost_price_to_line_item.rb +5 -0
  55. data/db/migrate/20130718233855_set_backorderable_to_default_to_false.rb +6 -0
  56. data/db/migrate/20130725031716_add_created_by_id_to_spree_orders.rb +5 -0
  57. data/db/migrate/20130729214043_index_completed_at_on_spree_orders.rb +5 -0
  58. data/db/migrate/20130802014537_add_tax_category_id_to_spree_line_items.rb +5 -0
  59. data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +9 -0
  60. data/db/migrate/20130805043440_create_spree_new_adjustments.rb +8 -0
  61. data/lib/generators/spree/dummy/templates/rails/database.yml +8 -7
  62. data/lib/generators/spree/install/install_generator.rb +1 -1
  63. data/lib/spree/core/calculated_adjustments.rb +12 -1
  64. data/lib/spree/core/controller_helpers/order.rb +5 -3
  65. data/lib/spree/core/controller_helpers/search.rb +14 -0
  66. data/lib/spree/core/permalinks.rb +13 -13
  67. data/lib/spree/core/s3_support.rb +1 -1
  68. data/lib/spree/core/version.rb +1 -1
  69. data/lib/spree/core.rb +7 -5
  70. data/lib/spree/i18n/base.rb +17 -0
  71. data/lib/spree/i18n/initializer.rb +1 -0
  72. data/lib/spree/i18n.rb +28 -7
  73. data/lib/spree/money.rb +2 -0
  74. data/lib/spree/testing_support/authorization_helpers.rb +6 -15
  75. data/lib/spree/testing_support/capybara_ext.rb +20 -11
  76. data/lib/spree/testing_support/common_rake.rb +13 -0
  77. data/lib/spree/testing_support/factories/country_factory.rb +1 -1
  78. data/lib/spree/testing_support/factories/order_factory.rb +0 -2
  79. data/lib/spree/testing_support/factories/payment_method_factory.rb +1 -1
  80. data/lib/spree/testing_support/factories/product_factory.rb +8 -18
  81. data/lib/spree/testing_support/factories/return_authorization_factory.rb +4 -0
  82. data/lib/spree/testing_support/factories/shipment_factory.rb +1 -1
  83. data/lib/spree/testing_support/factories/stock_item_factory.rb +2 -1
  84. data/lib/spree/testing_support/factories/stock_location_factory.rb +5 -2
  85. data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
  86. data/lib/spree/testing_support/order_walkthrough.rb +14 -1
  87. data/vendor/assets/javascripts/jquery.validate/localization/messages_et.js +23 -0
  88. data/vendor/assets/javascripts/jquery.validate/localization/messages_eu.js +25 -0
  89. data/vendor/assets/javascripts/jquery.validate/localization/messages_hr.js +25 -0
  90. data/vendor/assets/javascripts/jquery.validate/localization/messages_ka.js +25 -0
  91. data/vendor/assets/javascripts/jquery.validate/localization/messages_ko.js +25 -0
  92. data/vendor/assets/javascripts/jquery.validate/localization/messages_my.js +25 -0
  93. data/vendor/assets/javascripts/jquery.validate/localization/messages_pt_BR.js +26 -0
  94. data/vendor/assets/javascripts/jquery.validate/localization/messages_pt_PT.js +26 -0
  95. data/vendor/assets/javascripts/jquery.validate/localization/messages_sl.js +25 -0
  96. data/vendor/assets/javascripts/jquery.validate/localization/messages_sv.js +23 -0
  97. data/vendor/assets/javascripts/jquery.validate/localization/messages_uk.js +25 -0
  98. data/vendor/assets/javascripts/jquery.validate/localization/messages_zh.js +25 -0
  99. data/vendor/assets/javascripts/jquery.validate/localization/messages_zh_TW.js +26 -0
  100. metadata +118 -67
@@ -20,6 +20,7 @@
20
20
 
21
21
  module Spree
22
22
  class Product < ActiveRecord::Base
23
+ acts_as_paranoid
23
24
  has_many :product_option_types, dependent: :destroy
24
25
  has_many :option_types, through: :product_option_types
25
26
  has_many :product_properties, dependent: :destroy
@@ -44,12 +45,11 @@ module Spree
44
45
 
45
46
  has_many :variants_including_master,
46
47
  class_name: 'Spree::Variant',
47
- conditions: { deleted_at: nil },
48
- dependent: :destroy
49
-
50
- has_many :variants_including_master_and_deleted, class_name: 'Spree::Variant'
48
+ dependent: :destroy,
49
+ order: "#{::Spree::Variant.quoted_table_name}.position ASC"
51
50
 
52
51
  has_many :prices, through: :variants, order: 'spree_variants.position, spree_variants.id, currency'
52
+ has_many :stock_items, through: :variants
53
53
 
54
54
  delegate_belongs_to :master, :sku, :price, :currency, :display_amount, :display_price, :weight, :height, :width, :depth, :is_master, :has_default_price?, :cost_currency, :price_in, :amount_in
55
55
  delegate_belongs_to :master, :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price')
@@ -66,16 +66,35 @@ module Spree
66
66
 
67
67
  accepts_nested_attributes_for :variants, allow_destroy: true
68
68
 
69
- validates :name, :permalink, presence: true
69
+ validates :name, presence: true
70
+ validates :permalink, presence: true
70
71
  validates :price, presence: true, if: proc { Spree::Config[:require_master_price] }
72
+ validates :shipping_category_id, presence: true
71
73
 
72
74
  attr_accessor :option_values_hash
73
75
 
74
- attr_accessible :name, :description, :available_on, :permalink, :meta_description,
75
- :meta_keywords, :price, :sku, :deleted_at, :prototype_id,
76
- :option_values_hash, :weight, :height, :width, :depth,
77
- :shipping_category_id, :tax_category_id, :product_properties_attributes,
78
- :variants_attributes, :taxon_ids, :option_type_ids, :cost_currency
76
+ attr_accessible :available_on,
77
+ :cost_currency,
78
+ :deleted_at,
79
+ :depth,
80
+ :description,
81
+ :height,
82
+ :meta_description,
83
+ :meta_keywords,
84
+ :name,
85
+ :option_type_ids,
86
+ :option_values_hash,
87
+ :permalink,
88
+ :price,
89
+ :product_properties_attributes,
90
+ :prototype_id,
91
+ :shipping_category_id,
92
+ :sku,
93
+ :tax_category_id,
94
+ :taxon_ids,
95
+ :weight,
96
+ :width,
97
+ :variants_attributes
79
98
 
80
99
  attr_accessible :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price')
81
100
 
@@ -109,13 +128,6 @@ module Spree
109
128
  end
110
129
  end
111
130
 
112
- # override the delete method to set deleted_at value
113
- # instead of actually deleting the product.
114
- def delete
115
- self.update_column(:deleted_at, Time.now)
116
- variants_including_master.update_all(deleted_at: Time.now)
117
- end
118
-
119
131
  # Adding properties and option types on creation based on a chosen prototype
120
132
  attr_reader :prototype_id
121
133
  def prototype_id=(value)
@@ -197,6 +209,21 @@ module Spree
197
209
  Spree::Promotion.advertised.where(id: promotion_ids).reject(&:expired?)
198
210
  end
199
211
 
212
+ def total_on_hand
213
+ if Spree::Config.track_inventory_levels
214
+ self.stock_items.sum(&:count_on_hand)
215
+ else
216
+ Float::INFINITY
217
+ end
218
+ end
219
+
220
+ # Master variant may be deleted (i.e. when the product is deleted)
221
+ # which would make AR's default finder return nil.
222
+ # This is a stopgap for that little problem.
223
+ def master
224
+ super || variants_including_master.with_deleted.where(:is_master => true).first
225
+ end
226
+
200
227
  private
201
228
 
202
229
  # Builds variants from a hash of option types & values
@@ -17,7 +17,7 @@ module Spree
17
17
  # through options hash
18
18
  def perform(options = {})
19
19
  order = options[:order]
20
- return if order.promotion_credit_exists?(self.promotion)
20
+ return if order.promotion_credit_exists?(self)
21
21
 
22
22
  self.create_adjustment("#{Spree.t(:promotion)} (#{promotion.name})", order, order)
23
23
  end
@@ -6,14 +6,44 @@ module Spree
6
6
  accepts_nested_attributes_for :promotion_action_line_items
7
7
  attr_accessible :promotion_action_line_items_attributes
8
8
 
9
+ delegate :eligible?, :to => :promotion
9
10
 
11
+ # Adds a line item to the Order if the promotion is eligible
12
+ #
13
+ # This doesn't play right with Add to Cart events because at the moment
14
+ # the item was added to cart the promo may not be eligible. However it
15
+ # might become eligible as the order gets updated.
16
+ #
17
+ # e.g.
18
+ # - A promo adds a line item to cart if order total greater then $30
19
+ # - Customer add 1 item of $10 to cart
20
+ # - This action shouldn't perform because the order is not eligible
21
+ # - Customer increases item quantity to 5 (order total goes to $50)
22
+ # - Now the order is eligible for the promo and the action should perform
23
+ #
24
+ # Another complication is when the same line item created by the promo
25
+ # is also added to cart on a separate action.
26
+ #
27
+ # e.g.
28
+ # - Promo adds 1 item A to cart if order total greater then $30
29
+ # - Customer add 2 items B to cart, current order total is $40
30
+ # - This action performs adding item A to cart since order is eligible
31
+ # - Customer changes his mind and updates item B quantity to 1
32
+ # - At this point order is no longer eligible and one might expect
33
+ # that item A should be removed
34
+ #
35
+ # It doesn't remove items from the order here because there's no way
36
+ # it can know whether that item was added via this promo action or if
37
+ # it was manually populated somewhere else. In that case the item
38
+ # needs to be manually removed from the order by the customer
10
39
  def perform(options = {})
11
- return unless order = options[:order]
40
+ order = options[:order]
41
+ return unless self.eligible? order
42
+
12
43
  promotion_action_line_items.each do |item|
13
44
  current_quantity = order.quantity_of(item.variant)
14
45
  if current_quantity < item.quantity
15
46
  order.contents.add(item.variant, item.quantity - current_quantity)
16
- order.update!
17
47
  end
18
48
  end
19
49
  end
@@ -44,9 +44,10 @@ module Spree
44
44
  def activate(payload)
45
45
  return unless order_activatable? payload[:order]
46
46
 
47
+ # make sure code is always downcased (old databases might have mixed case codes)
47
48
  if code.present?
48
49
  event_code = payload[:coupon_code]
49
- return unless event_code == self.code
50
+ return unless event_code == self.code.downcase.strip
50
51
  end
51
52
 
52
53
  if path.present?
@@ -6,7 +6,7 @@ module Spree
6
6
  belongs_to :address, class_name: 'Spree::Address'
7
7
  belongs_to :stock_location, class_name: 'Spree::StockLocation'
8
8
 
9
- has_many :shipping_rates
9
+ has_many :shipping_rates, dependent: :destroy
10
10
  has_many :shipping_methods, through: :shipping_rates
11
11
  has_many :state_changes, as: :stateful
12
12
  has_many :inventory_units, dependent: :destroy
@@ -16,7 +16,7 @@ module Spree
16
16
  after_save :ensure_correct_adjustment, :update_order
17
17
 
18
18
  attr_accessor :special_instructions
19
- attr_accessible :order, :special_instructions, :stock_location_id,
19
+ attr_accessible :order, :special_instructions, :stock_location_id, :number,
20
20
  :tracking, :address, :inventory_units, :selected_shipping_rate_id
21
21
 
22
22
  accepts_nested_attributes_for :address
@@ -158,7 +158,7 @@ module Spree
158
158
  end
159
159
 
160
160
  def manifest
161
- inventory_units.group_by(&:variant).map do |variant, units|
161
+ inventory_units.includes(:variant).group_by(&:variant).map do |variant, units|
162
162
  states = {}
163
163
  units.group_by(&:state).each { |state, iu| states[state] = iu.count }
164
164
  OpenStruct.new(variant: variant, quantity: units.length, states: states)
@@ -223,8 +223,8 @@ module Spree
223
223
 
224
224
  def to_package
225
225
  package = Stock::Package.new(stock_location, order)
226
- inventory_units.each do |inventory_unit|
227
- package.add inventory_unit.variant, 1, inventory_unit.state
226
+ inventory_units.includes(:variant).each do |inventory_unit|
227
+ package.add inventory_unit.variant, 1, inventory_unit.state_name
228
228
  end
229
229
  package
230
230
  end
@@ -0,0 +1,4 @@
1
+ module Spree
2
+ class ShippingAdjustment < Spree::Adjustment
3
+ end
4
+ end
@@ -8,6 +8,7 @@ module Spree
8
8
  has_many :shipments
9
9
  has_many :shipping_method_categories
10
10
  has_many :shipping_categories, through: :shipping_method_categories
11
+ has_many :shipping_rates
11
12
 
12
13
  has_and_belongs_to_many :zones, :join_table => 'spree_shipping_methods_zones',
13
14
  :class_name => 'Spree::Zone',
@@ -49,6 +50,11 @@ module Spree
49
50
  spree_calculators.send(model_name_without_spree_namespace).select{|c| c.name.start_with?("Spree::Calculator::Shipping::")}
50
51
  end
51
52
 
53
+ # Some shipping methods are only meant to be set via backend
54
+ def frontend?
55
+ self.display_on != "back_end"
56
+ end
57
+
52
58
  private
53
59
  def at_least_one_shipping_category
54
60
  if self.shipping_categories.empty?
@@ -21,5 +21,6 @@ module Spree
21
21
 
22
22
  Spree::Money.new(price, { currency: currency })
23
23
  end
24
+ alias_method :display_cost, :display_price
24
25
  end
25
26
  end
@@ -1,14 +1,16 @@
1
1
  module Spree
2
2
  module Stock
3
3
  class AvailabilityValidator < ActiveModel::Validator
4
-
5
4
  def validate(line_item)
6
5
  quantifier = Stock::Quantifier.new(line_item.variant_id)
7
6
 
8
7
  unless quantifier.can_supply? line_item.quantity
9
- line_item.errors[:quantity] << I18n.t('validation.exceeds_available_stock')
10
- end
8
+ variant = line_item.variant
9
+ display_name = %Q{#{variant.name}}
10
+ display_name += %Q{ (#{variant.options_text})} unless variant.options_text.blank?
11
11
 
12
+ line_item.errors[:quantity] << Spree.t(:out_of_stock, :scope => :order_populator, :item => display_name.inspect)
13
+ end
12
14
  end
13
15
  end
14
16
  end
@@ -8,21 +8,33 @@ module Spree
8
8
  end
9
9
 
10
10
  def packages
11
- packages = Array.new
12
- packages = build_packages(packages)
11
+ packages = build_packages
13
12
  packages = prioritize_packages(packages)
14
13
  packages = estimate_packages(packages)
15
14
  end
16
15
 
17
- private
18
- def build_packages(packages)
16
+ # Build packages as per stock location
17
+ #
18
+ # It needs to check whether each stock location holds at least one stock
19
+ # item for the order. In case none is found it wouldn't make any sense
20
+ # to build a package because it would be empty. Plus we avoid errors down
21
+ # the stack because it would assume the stock location has stock items
22
+ # for the given order
23
+ #
24
+ # Returns an array of Package instances
25
+ def build_packages(packages = Array.new)
19
26
  StockLocation.active.each do |stock_location|
27
+ next unless order.line_items.any? do |item|
28
+ stock_location.stock_item(item.variant)
29
+ end
30
+
20
31
  packer = build_packer(stock_location, order)
21
32
  packages += packer.packages
22
33
  end
23
34
  packages
24
35
  end
25
36
 
37
+ private
26
38
  def prioritize_packages(packages)
27
39
  prioritizer = Prioritizer.new(order, packages)
28
40
  prioritizer.prioritized_packages
@@ -8,25 +8,35 @@ module Spree
8
8
  @currency = order.currency
9
9
  end
10
10
 
11
- def shipping_rates(package)
11
+ def shipping_rates(package, frontend_only = true)
12
12
  shipping_rates = Array.new
13
13
  shipping_methods = shipping_methods(package)
14
14
  return [] unless shipping_methods
15
+
15
16
  shipping_methods.each do |shipping_method|
16
17
  cost = calculate_cost(shipping_method, package)
17
-
18
- shipping_rates << ShippingRate.new( :shipping_method => shipping_method,
19
- :cost => cost)
18
+ shipping_rates << shipping_method.shipping_rates.new(:cost => cost)
20
19
  end
20
+
21
21
  shipping_rates.sort_by! { |r| r.cost || 0 }
22
- shipping_rates.first.selected = true unless shipping_rates.empty?
22
+
23
+ unless shipping_rates.empty?
24
+ if frontend_only
25
+ shipping_rates.each do |rate|
26
+ rate.selected = true and break if rate.shipping_method.frontend?
27
+ end
28
+ else
29
+ shipping_rates.first.selected = true
30
+ end
31
+ end
32
+
23
33
  shipping_rates
24
34
  end
25
35
 
26
36
  private
27
37
  def shipping_methods(package)
28
38
  shipping_methods = package.shipping_methods
29
- shipping_methods.delete_if { |ship_method| !ship_method.calculator.available?(package.contents) }
39
+ shipping_methods.delete_if { |ship_method| !ship_method.calculator.available?(package) }
30
40
  shipping_methods.delete_if { |ship_method| !ship_method.include?(order.ship_address) }
31
41
  shipping_methods.delete_if { |ship_method| !(ship_method.calculator.preferences[:currency].nil? || ship_method.calculator.preferences[:currency] == currency) }
32
42
  shipping_methods
@@ -16,6 +16,8 @@ module Spree
16
16
  def default_package
17
17
  package = Package.new(stock_location, order)
18
18
  order.line_items.each do |line_item|
19
+ next unless stock_location.stock_item(line_item.variant)
20
+
19
21
  on_hand, backordered = stock_location.fill_status(line_item.variant, line_item.quantity)
20
22
  package.add line_item.variant, on_hand, :on_hand if on_hand > 0
21
23
  package.add line_item.variant, backordered, :backordered if backordered > 0
@@ -34,4 +36,3 @@ module Spree
34
36
  end
35
37
  end
36
38
  end
37
-
@@ -9,11 +9,24 @@ module Spree
9
9
  validates_presence_of :name
10
10
 
11
11
  attr_accessible :name, :active, :address1, :address2, :city, :zipcode,
12
- :state_name, :state_id, :country_id, :phone, :country_id
12
+ :backorderable_default, :state_name, :state_id, :country_id, :phone,
13
+ :country_id, :propagate_all_variants
13
14
 
14
15
  scope :active, -> { where(active: true) }
15
16
 
16
- after_create :create_stock_items
17
+ after_create :create_stock_items, :if => "self.propagate_all_variants?"
18
+
19
+ # Wrapper for creating a new stock item respecting the backorderable config
20
+ def propagate_variant(variant)
21
+ self.stock_items.create!(variant: variant, backorderable: self.backorderable_default)
22
+ end
23
+
24
+ # Return either an existing stock item or create a new one. Useful in
25
+ # scenarios where the user might not know whether there is already a stock
26
+ # item for a given variant
27
+ def set_up_stock_item(variant)
28
+ self.stock_item(variant) || propagate_variant(variant)
29
+ end
17
30
 
18
31
  def stock_item(variant)
19
32
  stock_items.where(variant_id: variant).order(:id).first
@@ -63,11 +76,8 @@ module Spree
63
76
  end
64
77
 
65
78
  private
66
-
67
79
  def create_stock_items
68
- Spree::Variant.find_each do |v|
69
- self.stock_items.create!(variant: v)
70
- end
80
+ Variant.find_each { |variant| self.propagate_variant(variant) }
71
81
  end
72
82
  end
73
83
  end
@@ -1,5 +1,6 @@
1
1
  module Spree
2
2
  class TaxCategory < ActiveRecord::Base
3
+ acts_as_paranoid
3
4
  validates :name, presence: true, uniqueness: { scope: :deleted_at }
4
5
 
5
6
  has_many :tax_rates, dependent: :destroy
@@ -8,8 +9,6 @@ module Spree
8
9
 
9
10
  before_save :set_default_category
10
11
 
11
- default_scope where(deleted_at: nil)
12
-
13
12
  def set_default_category
14
13
  #set existing default tax category to false if this one has been marked as default
15
14
 
@@ -17,10 +16,5 @@ module Spree
17
16
  tax_category.update_column(:is_default, false) unless tax_category == self
18
17
  end
19
18
  end
20
-
21
- def mark_deleted!
22
- self.deleted_at = Time.now
23
- save
24
- end
25
19
  end
26
20
  end
@@ -10,6 +10,7 @@ end
10
10
 
11
11
  module Spree
12
12
  class TaxRate < ActiveRecord::Base
13
+ acts_as_paranoid
13
14
  include Spree::Core::CalculatedAdjustments
14
15
  belongs_to :zone, class_name: "Spree::Zone"
15
16
  belongs_to :tax_category, class_name: "Spree::TaxCategory"
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class Variant < ActiveRecord::Base
3
+ acts_as_paranoid
4
+
3
5
  belongs_to :product, touch: true, class_name: 'Spree::Product'
4
6
 
5
7
  delegate_belongs_to :product, :name, :description, :permalink, :available_on,
@@ -14,7 +16,7 @@ module Spree
14
16
  has_many :inventory_units
15
17
  has_many :line_items
16
18
 
17
- has_many :stock_items, dependent: :destroy
19
+ has_many :stock_items, dependent: :destroy, :order => "id ASC"
18
20
  has_many :stock_locations, through: :stock_items
19
21
  has_many :stock_movements
20
22
 
@@ -133,6 +135,17 @@ module Spree
133
135
  Spree::Stock::Quantifier.new(self).can_supply?(quantity)
134
136
  end
135
137
 
138
+ def total_on_hand
139
+ Spree::Stock::Quantifier.new(self).total_on_hand
140
+ end
141
+
142
+ # Product may be created with deleted_at already set,
143
+ # which would make AR's default finder return nil.
144
+ # This is a stopgap for that little problem.
145
+ def product
146
+ Spree::Product.unscoped { super }
147
+ end
148
+
136
149
  private
137
150
  # strips all non-price-like characters from the price, taking into account locale settings
138
151
  def parse_price(price)
@@ -167,8 +180,8 @@ module Spree
167
180
  end
168
181
 
169
182
  def create_stock_items
170
- Spree::StockLocation.all.each do |stock_location|
171
- stock_location.stock_items.create!(variant: self)
183
+ StockLocation.all.each do |stock_location|
184
+ stock_location.propagate_variant(self) if stock_location.propagate_all_variants?
172
185
  end
173
186
  end
174
187
 
@@ -18,7 +18,7 @@ module Spree
18
18
 
19
19
  def kind
20
20
  if members.any? && !members.any? { |member| member.try(:zoneable_type).nil? }
21
- members.last.zoneable_type.demodulize.downcase
21
+ members.last.zoneable_type.demodulize.underscore
22
22
  end
23
23
  end
24
24
 
@@ -18,7 +18,7 @@ Spree::Core::Engine.config.to_prepare do
18
18
  end
19
19
 
20
20
  def last_incomplete_spree_order
21
- spree_orders.incomplete.order('created_at DESC').first
21
+ spree_orders.incomplete.where(:created_by_id => self.id).order('created_at DESC').first
22
22
  end
23
23
  end
24
24
  end
@@ -193,6 +193,12 @@ en:
193
193
  spree/zone:
194
194
  one: Zone
195
195
  other: Zones
196
+ errors:
197
+ models:
198
+ spree/credit_card:
199
+ attributes:
200
+ base:
201
+ card_expired: "Card has expired"
196
202
  devise:
197
203
  confirmations:
198
204
  confirmed: Your account was successfully confirmed. You are now signed in.
@@ -295,6 +301,7 @@ en:
295
301
  reports: "Reports"
296
302
  configuration: "Configuration"
297
303
  promotions: "Promotions"
304
+ users: "Users"
298
305
  administration: Administration
299
306
  agree_to_privacy_policy: Agree to Privacy Policy
300
307
  agree_to_terms_of_service: Agree to Terms of Service
@@ -343,7 +350,7 @@ en:
343
350
  back_to_shipping_categories: Back To Shipping Categories
344
351
  back_to_shipping_methods_list: Back To Shipping Methods List
345
352
  back_to_states_list: Back To States List
346
- back_to_stock_locations_list: Back to Stock Movements List
353
+ back_to_stock_locations_list: Back to Stock Locations List
347
354
  back_to_stock_movements_list: Back to Stock Movements List
348
355
  back_to_stock_transfers_list: Back to Stock Transfers List
349
356
  back_to_store: Go Back To Store
@@ -583,6 +590,7 @@ en:
583
590
  operators:
584
591
  gt: greater than
585
592
  gte: greater than or equal to
593
+ items_cannot_be_shipped: We are unable to ship the selected items to your shipping address. Please choose another shipping address.
586
594
  jirafe: Jirafe
587
595
  landing_page_rule:
588
596
  path: Path
@@ -661,12 +669,14 @@ en:
661
669
  no_actions_added: No actions added
662
670
  no_orders_found: No orders found
663
671
  no_payment_methods_found: No payment methods found
672
+ no_pending_payments: No pending payments
664
673
  no_products_found: No products found
665
674
  no_promotions_found: No promotions found
666
675
  no_results: No results
667
676
  no_rules_added: No rules added
668
677
  no_shipping_methods_found: No shipping methods found
669
678
  no_trackers_found: No Trackers Found
679
+ no_stock_locations_found: No stock locations found
670
680
  no_tracking_present: No tracking details provided.
671
681
  none: None
672
682
  normal_amount: Normal Amount
@@ -685,6 +695,7 @@ en:
685
695
  open: Open
686
696
  open_all_adjustments: Open All Adjustments
687
697
  option_type: Option Type
698
+ option_type_placeholder: Choose an option type
688
699
  option_types: Option Types
689
700
  option_value: Option Value
690
701
  option_values: Option Values
@@ -965,6 +976,7 @@ en:
965
976
  stock_location_info: Stock location info
966
977
  stock_locations: Stock Locations
967
978
  stock_management: Stock Management
979
+ stock_management_requires_a_stock_location: Please create a stock location in order to manage stock.
968
980
  stock_movements: Stock Movements
969
981
  stock_movements_for_stock_location: Stock Movements for %{stock_location_name}
970
982
  stock_successfully_transferred: Stock was successfully transferred between locations.
@@ -1004,6 +1016,7 @@ en:
1004
1016
  subject: Test Mail
1005
1017
  test_mode: Test Mode
1006
1018
  thank_you_for_your_order: Thank you for your business. Please print out a copy of this confirmation page for your records.
1019
+ there_are_no_items_for_this_order: There are no items for this order. Please add an item to the order to continue.
1007
1020
  there_were_problems_with_the_following_fields: There were problems with the following fields
1008
1021
  thumbnail: Thumbnail
1009
1022
  time: Time
@@ -1043,6 +1056,7 @@ en:
1043
1056
  must_be_non_negative: must be a non-negative value
1044
1057
  value: Value
1045
1058
  variant: Variant
1059
+ variant_placeholder: Choose a variant
1046
1060
  variants: Variants
1047
1061
  version: Version
1048
1062
  void: Void
@@ -4,7 +4,12 @@ class CreateShippingMethodZone < ActiveRecord::Migration
4
4
  t.integer :shipping_method_id
5
5
  t.integer :zone_id
6
6
  end
7
-
7
+ # This association has been corrected in a latter migration
8
+ # but when this database migration runs, the table is still incorrectly named
9
+ # 'shipping_methods_zones' instead of 'spre_shipping_methods_zones'
10
+ Spree::ShippingMethod.has_and_belongs_to_many :zones, :join_table => 'shipping_methods_zones',
11
+ :class_name => 'Spree::Zone',
12
+ :foreign_key => 'shipping_method_id'
8
13
  Spree::ShippingMethod.all.each{|sm| sm.zones << Spree::Zone.find(sm.zone_id)}
9
14
 
10
15
  remove_column :spree_shipping_methods, :zone_id
@@ -13,7 +13,10 @@ class AddAddressFieldsToStockLocation < ActiveRecord::Migration
13
13
 
14
14
 
15
15
  usa = Spree::Country.where(:iso => 'US').first
16
+ # In case USA isn't found.
17
+ # See #3115
18
+ country = usa || Spree::Country.first
16
19
  Spree::Country.reset_column_information
17
- Spree::StockLocation.update_all(:country_id => usa)
20
+ Spree::StockLocation.update_all(:country_id => country)
18
21
  end
19
22
  end
@@ -0,0 +1,5 @@
1
+ class AddBackorderableDefaultToSpreeStockLocation < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_stock_locations, :backorderable_default, :boolean, default: true
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddPropageAllVariantsToSpreeStockLocation < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_stock_locations, :propagate_all_variants, :boolean, default: true
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddCvvResultCodeAndCvvResultMessageToSpreePayments < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_payments, :cvv_response_code, :string
4
+ add_column :spree_payments, :cvv_response_message, :string
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddUniqueIndexToPermalinkOnSpreeProducts < ActiveRecord::Migration
2
+ def change
3
+ add_index "spree_products", ["permalink"], :name => "permalink_idx_unique", :unique => true
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class AddUniqueIndexToOrdersShipmentsAndStockTransfers < ActiveRecord::Migration
2
+ def add
3
+ add_index "spree_orders", ["number"], :name => "number_idx_unique", :unique => true
4
+ add_index "spree_shipments", ["number"], :name => "number_idx_unique", :unique => true
5
+ add_index "spree_stock_transfers", ["number"], :name => "number_idx_unique", :unique => true
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class AddDeletedAtToSpreeTaxRates < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_tax_rates, :deleted_at, :datetime
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class RemoveLockVersionFromInventoryUnits < ActiveRecord::Migration
2
+ def change
3
+ # we are moving to pessimistic locking on stock_items
4
+ remove_column :spree_inventory_units, :lock_version
5
+ end
6
+ end