spree_core 2.0.7 → 2.0.8

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +5 -2
  3. data/app/models/spree/adjustment.rb +2 -2
  4. data/app/models/spree/gateway.rb +1 -1
  5. data/app/models/spree/inventory_unit.rb +5 -4
  6. data/app/models/spree/line_item.rb +2 -3
  7. data/app/models/spree/option_value.rb +1 -1
  8. data/app/models/spree/order/checkout.rb +4 -0
  9. data/app/models/spree/order.rb +22 -17
  10. data/app/models/spree/order_inventory.rb +1 -1
  11. data/app/models/spree/payment.rb +2 -2
  12. data/app/models/spree/price.rb +5 -0
  13. data/app/models/spree/product/scopes.rb +1 -1
  14. data/app/models/spree/product.rb +3 -3
  15. data/app/models/spree/promotion.rb +1 -8
  16. data/app/models/spree/shipment.rb +9 -14
  17. data/app/models/spree/shipping_method.rb +2 -1
  18. data/app/models/spree/shipping_rate.rb +4 -0
  19. data/app/models/spree/stock/estimator.rb +20 -6
  20. data/app/models/spree/stock/packer.rb +1 -1
  21. data/app/models/spree/stock/quantifier.rb +11 -2
  22. data/app/models/spree/stock_item.rb +1 -1
  23. data/app/models/spree/stock_location.rb +5 -0
  24. data/app/models/spree/stock_movement.rb +3 -1
  25. data/app/models/spree/variant.rb +15 -2
  26. data/app/models/spree/zone.rb +1 -1
  27. data/config/locales/en.yml +4 -0
  28. data/db/migrate/20130417120034_add_index_to_source_columns_on_adjustments.rb +5 -0
  29. data/db/migrate/20131026154747_add_track_inventory_to_variant.rb +5 -0
  30. data/db/migrate/20140120160805_add_index_to_variant_id_and_currency_on_prices.rb +5 -0
  31. data/lib/generators/spree/dummy/dummy_generator.rb +8 -3
  32. data/lib/generators/spree/dummy/templates/rails/database.yml +10 -0
  33. data/lib/spree/core/delegate_belongs_to.rb +2 -0
  34. data/lib/spree/core/engine.rb +5 -1
  35. data/lib/spree/core/permalinks.rb +5 -1
  36. data/lib/spree/core/version.rb +1 -1
  37. data/lib/spree/i18n.rb +1 -0
  38. data/lib/spree/money.rb +171 -1
  39. data/lib/spree/testing_support/capybara_ext.rb +6 -5
  40. data/lib/spree/testing_support/factories/product_factory.rb +4 -0
  41. data/lib/spree/testing_support/factories/variant_factory.rb +15 -0
  42. metadata +147 -158
@@ -27,21 +27,31 @@ production:
27
27
  database: <%= database_prefix %>spree_production
28
28
  encoding: utf8
29
29
  <% when 'postgres' %>
30
+ <% db_host = ENV['DB_HOST'] -%>
30
31
  development:
31
32
  adapter: postgresql
32
33
  database: <%= database_prefix %>spree_development
33
34
  username: postgres
34
35
  min_messages: warning
36
+ <% unless db_host.blank? %>
37
+ host: <%= db_host %>
38
+ <% end %>
35
39
  test:
36
40
  adapter: postgresql
37
41
  database: <%= database_prefix %>spree_test
38
42
  username: postgres
39
43
  min_messages: warning
44
+ <% unless db_host.blank? %>
45
+ host: <%= db_host %>
46
+ <% end %>
40
47
  production:
41
48
  adapter: postgresql
42
49
  database: <%= database_prefix %>spree_production
43
50
  username: postgres
44
51
  min_messages: warning
52
+ <% unless db_host.blank? %>
53
+ host: <%= db_host %>
54
+ <% end %>
45
55
  <% else %>
46
56
  development:
47
57
  adapter: sqlite3
@@ -37,6 +37,8 @@ module DelegateBelongsTo
37
37
  attrs = get_association_column_names(association) if attrs.empty?
38
38
  attrs.concat get_association_column_names(association) if attrs.delete :defaults
39
39
  attrs.each do |attr|
40
+ next if attribute_method?(attr)
41
+
40
42
  class_def attr do |*args|
41
43
  if args.empty?
42
44
  send(:delegator_for, association).send(attr)
@@ -86,7 +86,11 @@ module Spree
86
86
  ]
87
87
  end
88
88
 
89
- initializer 'spree.promo.register.promotion.calculators' do
89
+ # Promotion rules need to be evaluated on after initialize otherwise
90
+ # Spree.user_class would be nil and users might experience errors related
91
+ # to malformed model associations (Spree.user_class is only defined on
92
+ # the app initializer)
93
+ config.after_initialize do
90
94
  Rails.application.config.spree.promotions.rules.concat [
91
95
  Spree::Promotion::Rules::ItemTotal,
92
96
  Spree::Promotion::Rules::Product,
@@ -37,6 +37,10 @@ module Spree
37
37
  permalink_options[:prefix] || ""
38
38
  end
39
39
 
40
+ def permalink_length
41
+ permalink_options[:length] || 9
42
+ end
43
+
40
44
  def permalink_order
41
45
  order = permalink_options[:order]
42
46
  "#{order} ASC," if order
@@ -44,7 +48,7 @@ module Spree
44
48
  end
45
49
 
46
50
  def generate_permalink
47
- "#{self.class.permalink_prefix}#{Array.new(9){rand(9)}.join}"
51
+ "#{self.class.permalink_prefix}#{Array.new(self.class.permalink_length){rand(9)}.join}"
48
52
  end
49
53
 
50
54
  def save_permalink(permalink_value=self.to_param)
@@ -1,5 +1,5 @@
1
1
  module Spree
2
2
  def self.version
3
- "2.0.7"
3
+ "2.0.8"
4
4
  end
5
5
  end
data/lib/spree/i18n.rb CHANGED
@@ -4,6 +4,7 @@ require 'spree/i18n/base'
4
4
 
5
5
  module Spree
6
6
  extend ActionView::Helpers::TranslationHelper
7
+ extend ActionView::Helpers::TagHelper
7
8
 
8
9
  class << self
9
10
  # Add spree namespace and delegate to Rails TranslationHelper for some nice
data/lib/spree/money.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'money'
2
4
 
3
5
  module Spree
@@ -7,7 +9,7 @@ module Spree
7
9
  delegate :cents, :to => :money
8
10
 
9
11
  def initialize(amount, options={})
10
- @money = ::Money.parse([amount, (options[:currency] || Spree::Config[:currency])].join)
12
+ @money = self.class.parse([amount, (options[:currency] || Spree::Config[:currency])].join)
11
13
  @options = {}
12
14
  @options[:with_currency] = Spree::Config[:display_currency]
13
15
  @options[:symbol_position] = Spree::Config[:currency_symbol_position].to_sym
@@ -20,6 +22,174 @@ module Spree
20
22
  @options[:symbol_position] = @options[:symbol_position].to_sym
21
23
  end
22
24
 
25
+ # This method is being deprecated in Money 6.1.0, so now lives here.
26
+ def self.parse(input, currency = nil)
27
+ i = input.to_s.strip
28
+
29
+ # raise Money::Currency.table.collect{|c| c[1][:symbol]}.inspect
30
+
31
+ # Check the first character for a currency symbol, alternatively get it
32
+ # from the stated currency string
33
+ c = if ::Money.assume_from_symbol && i =~ /^(\$|€|£)/
34
+ case i
35
+ when /^\$/ then "USD"
36
+ when /^€/ then "EUR"
37
+ when /^£/ then "GBP"
38
+ end
39
+ else
40
+ i[/[A-Z]{2,3}/]
41
+ end
42
+
43
+ # check that currency passed and embedded currency are the same,
44
+ # and negotiate the final currency
45
+ if currency.nil? and c.nil?
46
+ currency = ::Money.default_currency
47
+ elsif currency.nil?
48
+ currency = c
49
+ elsif c.nil?
50
+ currency = currency
51
+ elsif currency != c
52
+ # TODO: ParseError
53
+ raise ArgumentError, "Mismatching Currencies"
54
+ end
55
+ currency = ::Money::Currency.wrap(currency)
56
+
57
+ fractional = extract_cents(i, currency)
58
+ ::Money.new(fractional, currency)
59
+ end
60
+
61
+ # This method is being deprecated in Money 6.1.0, so now lives here.
62
+ def self.extract_cents(input, currency = Money.default_currency)
63
+ # remove anything that's not a number, potential thousands_separator, or minus sign
64
+ num = input.gsub(/[^\d.,'-]/, '')
65
+
66
+ # set a boolean flag for if the number is negative or not
67
+ negative = num =~ /^-|-$/ ? true : false
68
+
69
+ # decimal mark character
70
+ decimal_char = currency.decimal_mark
71
+
72
+ # if negative, remove the minus sign from the number
73
+ # if it's not negative, the hyphen makes the value invalid
74
+ if negative
75
+ num = num.sub(/^-|-$/, '')
76
+ end
77
+
78
+ raise ArgumentError, "Invalid currency amount (hyphen)" if num.include?('-')
79
+
80
+ #if the number ends with punctuation, just throw it out. If it means decimal,
81
+ #it won't hurt anything. If it means a literal period or comma, this will
82
+ #save it from being mis-interpreted as a decimal.
83
+ num.chop! if num.match(/[\.|,]$/)
84
+
85
+ # gather all decimal_marks within the result number
86
+ used_delimiters = num.scan(/[^\d]/)
87
+
88
+ # determine the number of unique decimal_marks within the number
89
+ #
90
+ # e.g.
91
+ # $1,234,567.89 would return 2 (, and .)
92
+ # $125,00 would return 1
93
+ # $199 would return 0
94
+ # $1 234,567.89 would raise an error (decimal_marks are space, comma, and period)
95
+ case used_delimiters.uniq.length
96
+ # no decimal_mark or thousands_separator; major (dollars) is the number, and minor (cents) is 0
97
+ when 0 then major, minor = num, 0
98
+
99
+ # two decimal_marks, so we know the last item in this array is the
100
+ # major/minor thousands_separator and the rest are decimal_marks
101
+ when 2
102
+ thousands_separator, decimal_mark = used_delimiters.uniq
103
+
104
+ # remove all thousands_separator, split on the decimal_mark
105
+ major, minor = num.gsub(thousands_separator, '').split(decimal_mark)
106
+ min = 0 unless min
107
+ when 1
108
+ # we can't determine if the comma or period is supposed to be a decimal_mark or a thousands_separator
109
+ # e.g.
110
+ # 1,00 - comma is a thousands_separator
111
+ # 1.000 - period is a thousands_separator
112
+ # 1,000 - comma is a decimal_mark
113
+ # 1,000,000 - comma is a decimal_mark
114
+ # 10000,00 - comma is a thousands_separator
115
+ # 1000,000 - comma is a thousands_separator
116
+
117
+ # assign first decimal_mark for reusability
118
+ decimal_mark = used_delimiters.first
119
+
120
+ # When we have identified the decimal mark character
121
+ if decimal_char == decimal_mark
122
+ major, minor = num.split(decimal_char)
123
+
124
+ else
125
+ # decimal_mark is used as a decimal_mark when there are multiple instances, always
126
+ if num.scan(decimal_mark).length > 1 # multiple matches; treat as decimal_mark
127
+ major, minor = num.gsub(decimal_mark, ''), 0
128
+ else
129
+ # ex: 1,000 - 1.0000 - 10001.000
130
+ # split number into possible major (dollars) and minor (cents) values
131
+ possible_major, possible_minor = num.split(decimal_mark)
132
+ possible_major ||= "0"
133
+ possible_minor ||= "00"
134
+
135
+ # if the minor (cents) length isn't 3, assign major/minor from the possibles
136
+ # e.g.
137
+ # 1,00 => 1.00
138
+ # 1.0000 => 1.00
139
+ # 1.2 => 1.20
140
+ if possible_minor.length != 3 # thousands_separator
141
+ major, minor = possible_major, possible_minor
142
+ else
143
+ # minor length is three
144
+ # let's try to figure out intent of the thousands_separator
145
+
146
+ # the major length is greater than three, which means
147
+ # the comma or period is used as a thousands_separator
148
+ # e.g.
149
+ # 1000,000
150
+ # 100000,000
151
+ if possible_major.length > 3
152
+ major, minor = possible_major, possible_minor
153
+ else
154
+ # number is in format ###{sep}### or ##{sep}### or #{sep}###
155
+ # handle as , is sep, . is thousands_separator
156
+ if decimal_mark == '.'
157
+ major, minor = possible_major, possible_minor
158
+ else
159
+ major, minor = "#{possible_major}#{possible_minor}", 0
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ else
166
+ # TODO: ParseError
167
+ raise ArgumentError, "Invalid currency amount"
168
+ end
169
+
170
+ # build the string based on major/minor since decimal_mark/thousands_separator have been removed
171
+ # avoiding floating point arithmetic here to ensure accuracy
172
+ cents = (major.to_i * currency.subunit_to_unit)
173
+ # Because of an bug in JRuby, we can't just call #floor
174
+ minor = minor.to_s
175
+ minor = if minor.size < currency.decimal_places
176
+ (minor + ("0" * currency.decimal_places))[0,currency.decimal_places].to_i
177
+ elsif minor.size > currency.decimal_places
178
+ if minor[currency.decimal_places,1].to_i >= 5
179
+ minor[0,currency.decimal_places].to_i+1
180
+ else
181
+ minor[0,currency.decimal_places].to_i
182
+ end
183
+ else
184
+ minor.to_i
185
+ end
186
+
187
+ cents += minor
188
+
189
+ # if negative, multiply by -1; otherwise, return positive cents
190
+ negative ? cents * -1 : cents
191
+ end
192
+
23
193
  def to_s
24
194
  @money.format(@options)
25
195
  end
@@ -36,8 +36,8 @@ module CapybaraExt
36
36
  label = find_label_by_text(options[:from])
37
37
  within label.first(:xpath,".//..") do
38
38
  options[:from] = "##{find(".select2-container")["id"]}"
39
- targetted_select2_search(value, options)
40
39
  end
40
+ targetted_select2_search(value, options)
41
41
  end
42
42
 
43
43
  def targetted_select2_search(value, options)
@@ -51,8 +51,8 @@ module CapybaraExt
51
51
 
52
52
  within label.first(:xpath,".//..") do
53
53
  options[:from] = "##{find(".select2-container")["id"]}"
54
- targetted_select2(value, options)
55
54
  end
55
+ targetted_select2(value, options)
56
56
  end
57
57
 
58
58
  def select2_no_label value, options={}
@@ -73,9 +73,10 @@ module CapybaraExt
73
73
  end
74
74
 
75
75
  def select_select2_result(value)
76
- #p %Q{$("div.select2-result-label:contains('#{value}')").mouseup()}
77
- sleep(1)
78
- page.execute_script(%Q{$("div.select2-result-label:contains('#{value}')").mouseup()})
76
+ # results are in a div appended to the end of the document
77
+ within(:xpath, '//body') do
78
+ page.find("div.select2-result-label", text: %r{#{Regexp.escape(value)}}i).click
79
+ end
79
80
  end
80
81
 
81
82
  def find_label_by_text(text)
@@ -12,6 +12,10 @@ FactoryGirl.define do
12
12
  # ensure stock item will be created for this products master
13
13
  before(:create) { create(:stock_location) if Spree::StockLocation.count == 0 }
14
14
 
15
+ after(:create) do |p|
16
+ p.variants_including_master.each { |v| v.save! }
17
+ end
18
+
15
19
  factory :custom_product do
16
20
  name 'Custom Product'
17
21
  price 17.99
@@ -9,6 +9,8 @@ FactoryGirl.define do
9
9
  height { generate(:random_float) }
10
10
  width { generate(:random_float) }
11
11
  depth { generate(:random_float) }
12
+ is_master 0
13
+ track_inventory true
12
14
 
13
15
  product { |p| p.association(:base_product) }
14
16
  option_values { [create(:option_value)] }
@@ -20,5 +22,18 @@ FactoryGirl.define do
20
22
  # on_hand 5
21
23
  product { |p| p.association(:product) }
22
24
  end
25
+
26
+ factory :master_variant do
27
+ is_master 1
28
+ end
29
+
30
+ factory :on_demand_variant do
31
+ track_inventory false
32
+
33
+ factory :on_demand_master_variant do
34
+ is_master 1
35
+ end
36
+ end
37
+
23
38
  end
24
39
  end