spree_core 2.0.13 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/spree/base_controller.rb +3 -0
- data/app/helpers/spree/base_helper.rb +6 -16
- data/app/helpers/spree/products_helper.rb +3 -8
- data/app/helpers/spree/taxons_helper.rb +1 -1
- data/app/mailers/spree/base_mailer.rb +0 -5
- data/app/models/spree/ability.rb +10 -7
- data/app/models/spree/address.rb +7 -17
- data/app/models/spree/adjustment.rb +15 -11
- data/app/models/spree/app_configuration.rb +0 -5
- data/app/models/spree/billing_integration.rb +0 -1
- data/app/models/spree/calculator/flat_percent_item_total.rb +1 -3
- data/app/models/spree/calculator/flat_rate.rb +2 -4
- data/app/models/spree/calculator/flexi_rate.rb +6 -9
- data/app/models/spree/calculator/per_item.rb +2 -4
- data/app/models/spree/calculator/percent_per_item.rb +1 -3
- data/app/models/spree/calculator/price_sack.rb +4 -9
- data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +1 -2
- data/app/models/spree/calculator/shipping/flat_rate.rb +2 -4
- data/app/models/spree/calculator/shipping/flexi_rate.rb +4 -9
- data/app/models/spree/calculator/shipping/per_item.rb +2 -3
- data/app/models/spree/calculator/shipping/price_sack.rb +4 -9
- data/app/models/spree/classification.rb +0 -3
- data/app/models/spree/country.rb +1 -3
- data/app/models/spree/credit_card.rb +37 -38
- data/app/models/spree/gateway/bogus_simple.rb +0 -8
- data/app/models/spree/gateway.rb +1 -3
- data/app/models/spree/image.rb +1 -3
- data/app/models/spree/inventory_unit.rb +5 -8
- data/app/models/spree/legacy_user.rb +0 -4
- data/app/models/spree/line_item.rb +2 -15
- data/app/models/spree/option_type.rb +2 -5
- data/app/models/spree/option_value.rb +1 -3
- data/app/models/spree/order/checkout.rb +4 -13
- data/app/models/spree/order.rb +47 -99
- data/app/models/spree/order_contents.rb +4 -7
- data/app/models/spree/order_inventory.rb +4 -8
- data/app/models/spree/order_updater.rb +13 -12
- data/app/models/spree/payment/processing.rb +12 -19
- data/app/models/spree/payment.rb +17 -30
- data/app/models/spree/payment_method.rb +2 -3
- data/app/models/spree/preference.rb +1 -1
- data/app/models/spree/preferences/configuration.rb +1 -1
- data/app/models/spree/preferences/preferable.rb +1 -1
- data/app/models/spree/preferences/store.rb +1 -1
- data/app/models/spree/price.rb +0 -7
- data/app/models/spree/product/scopes.rb +16 -17
- data/app/models/spree/product.rb +27 -62
- data/app/models/spree/product_property.rb +3 -5
- data/app/models/spree/promotion/actions/create_adjustment.rb +9 -8
- data/app/models/spree/promotion/actions/create_line_items.rb +1 -2
- data/app/models/spree/promotion/rules/first_order.rb +1 -1
- data/app/models/spree/promotion/rules/item_total.rb +2 -4
- data/app/models/spree/promotion/rules/product.rb +2 -2
- data/app/models/spree/promotion/rules/user.rb +1 -3
- data/app/models/spree/promotion.rb +23 -24
- data/app/models/spree/promotion_action.rb +0 -2
- data/app/models/spree/promotion_action_line_item.rb +1 -3
- data/app/models/spree/promotion_rule.rb +0 -2
- data/app/models/spree/property.rb +2 -4
- data/app/models/spree/prototype.rb +0 -2
- data/app/models/spree/return_authorization.rb +6 -9
- data/app/models/spree/role.rb +0 -2
- data/app/models/spree/shipment.rb +19 -25
- data/app/models/spree/shipping_calculator.rb +0 -2
- data/app/models/spree/shipping_category.rb +0 -2
- data/app/models/spree/shipping_method.rb +6 -20
- data/app/models/spree/shipping_rate.rb +12 -10
- data/app/models/spree/state.rb +2 -4
- data/app/models/spree/stock/availability_validator.rb +2 -2
- data/app/models/spree/stock/estimator.rb +6 -20
- data/app/models/spree/stock/packer.rb +1 -1
- data/app/models/spree/stock/quantifier.rb +2 -3
- data/app/models/spree/stock/splitter/base.rb +1 -1
- data/app/models/spree/stock_item.rb +8 -18
- data/app/models/spree/stock_location.rb +2 -11
- data/app/models/spree/stock_movement.rb +2 -5
- data/app/models/spree/stock_transfer.rb +0 -2
- data/app/models/spree/tax_category.rb +0 -2
- data/app/models/spree/tax_rate.rb +12 -12
- data/app/models/spree/taxon.rb +1 -13
- data/app/models/spree/taxonomy.rb +3 -6
- data/app/models/spree/tracker.rb +0 -2
- data/app/models/spree/variant/scopes.rb +2 -2
- data/app/models/spree/variant.rb +13 -31
- data/app/models/spree/zone.rb +2 -7
- data/app/models/spree/zone_member.rb +0 -2
- data/app/views/spree/payments/_payment.html.erb +1 -3
- data/config/locales/en.yml +11 -26
- data/db/default/spree/countries.rb +230 -229
- data/db/default/spree/states.rb +57 -56
- data/db/default/spree/zones.rb +5 -5
- data/db/migrate/20130213191427_create_default_stock.rb +4 -7
- data/db/migrate/20130417120035_update_adjustment_states.rb +2 -2
- data/db/migrate/20130417123427_add_shipping_rates_to_shipments.rb +1 -1
- data/db/migrate/20130509115210_add_number_to_stock_transfer.rb +1 -1
- data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +0 -5
- data/db/migrate/20130611185927_add_user_id_index_to_spree_orders.rb +5 -0
- data/db/migrate/20130618041418_add_updated_at_to_spree_countries.rb +9 -0
- data/db/migrate/20130619012236_add_updated_at_to_spree_states.rb +9 -0
- data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +4 -5
- data/db/migrate/20130806145853_set_default_stock_location_on_shipments.rb +1 -1
- data/lib/generators/spree/dummy/dummy_generator.rb +3 -14
- data/lib/generators/spree/dummy/templates/rails/database.yml +0 -10
- data/lib/generators/spree/dummy/templates/rails/test.rb +2 -7
- data/lib/generators/spree/install/install_generator.rb +11 -8
- data/lib/spree/core/calculated_adjustments.rb +9 -8
- data/lib/spree/core/controller_helpers/auth.rb +2 -3
- data/lib/spree/core/controller_helpers/order.rb +8 -13
- data/lib/spree/core/controller_helpers/ssl.rb +13 -22
- data/lib/spree/core/controller_helpers/strong_parameters.rb +36 -0
- data/lib/spree/core/delegate_belongs_to.rb +0 -2
- data/lib/spree/core/engine.rb +1 -5
- data/lib/spree/core/ext/active_record.rb +2 -9
- data/lib/spree/core/permalinks.rb +1 -5
- data/lib/spree/core/product_duplicator.rb +2 -16
- data/lib/spree/core/product_filters.rb +37 -33
- data/lib/spree/core/search/base.rb +1 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +3 -31
- data/lib/spree/i18n.rb +0 -1
- data/lib/spree/money.rb +2 -177
- data/lib/spree/permitted_attributes.rb +95 -0
- data/lib/spree/promo/coupon_applicator.rb +4 -12
- data/lib/spree/testing_support/capybara_ext.rb +13 -17
- data/lib/spree/testing_support/common_rake.rb +1 -1
- data/lib/spree/testing_support/controller_requests.rb +3 -3
- data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
- data/lib/spree/testing_support/factories/product_factory.rb +0 -4
- data/lib/spree/testing_support/factories/shipping_method_factory.rb +1 -3
- data/lib/spree/testing_support/factories/user_factory.rb +1 -1
- data/lib/spree/testing_support/factories/variant_factory.rb +0 -15
- data/lib/spree/testing_support/factories.rb +1 -1
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- data/lib/tasks/core.rake +2 -2
- data/vendor/assets/javascripts/jquery.payment.js +497 -0
- metadata +166 -172
- data/app/views/spree/admin/shared/_report_order_criteria.html.erb +0 -17
- data/db/migrate/20130417120034_add_index_to_source_columns_on_adjustments.rb +0 -5
- data/db/migrate/20130830001033_add_shipping_category_to_shipping_methods_and_products.rb +0 -15
- data/db/migrate/20130830001159_migrate_old_shipping_calculators.rb +0 -19
- data/db/migrate/20130909115621_change_states_required_for_countries.rb +0 -9
- data/db/migrate/20131001013410_remove_unused_credit_card_fields.rb +0 -12
- data/db/migrate/20131026154747_add_track_inventory_to_variant.rb +0 -5
- data/db/migrate/20131113035136_add_channel_to_spree_orders.rb +0 -5
- data/db/migrate/20140120160805_add_index_to_variant_id_and_currency_on_prices.rb +0 -5
- data/db/migrate/20140205181631_default_variant_weight_to_zero.rb +0 -11
- data/db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb +0 -5
- data/lib/spree/core/preference_rescue.rb +0 -25
@@ -8,14 +8,14 @@ module Spree
|
|
8
8
|
|
9
9
|
# Get current line item for variant if exists
|
10
10
|
# Add variant qty to line_item
|
11
|
-
def add(variant, quantity=1, currency=nil, shipment=nil)
|
11
|
+
def add(variant, quantity = 1, currency = nil, shipment = nil)
|
12
12
|
line_item = order.find_line_item_by_variant(variant)
|
13
13
|
add_to_line_item(line_item, variant, quantity, currency, shipment)
|
14
14
|
end
|
15
15
|
|
16
16
|
# Get current line item for variant
|
17
17
|
# Remove variant qty from line_item
|
18
|
-
def remove(variant, quantity=1, shipment=nil)
|
18
|
+
def remove(variant, quantity = 1, shipment = nil)
|
19
19
|
line_item = order.find_line_item_by_variant(variant)
|
20
20
|
|
21
21
|
unless line_item
|
@@ -32,21 +32,18 @@ module Spree
|
|
32
32
|
line_item.target_shipment = shipment
|
33
33
|
line_item.quantity += quantity.to_i
|
34
34
|
line_item.currency = currency unless currency.nil?
|
35
|
-
line_item.save
|
36
35
|
else
|
37
|
-
line_item =
|
36
|
+
line_item = order.line_items.new(quantity: quantity, variant: variant)
|
38
37
|
line_item.target_shipment = shipment
|
39
|
-
line_item.variant = variant
|
40
38
|
if currency
|
41
39
|
line_item.currency = currency unless currency.nil?
|
42
40
|
line_item.price = variant.price_in(currency).amount
|
43
41
|
else
|
44
42
|
line_item.price = variant.price
|
45
43
|
end
|
46
|
-
order.line_items << line_item
|
47
|
-
line_item
|
48
44
|
end
|
49
45
|
|
46
|
+
line_item.save
|
50
47
|
order.reload
|
51
48
|
line_item
|
52
49
|
end
|
@@ -32,7 +32,7 @@ module Spree
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def inventory_units_for(variant)
|
35
|
-
units = order.shipments.collect{|s| s.inventory_units.
|
35
|
+
units = order.shipments.collect{|s| s.inventory_units.to_a}.flatten
|
36
36
|
units.group_by(&:variant_id)[variant.id] || []
|
37
37
|
end
|
38
38
|
|
@@ -65,14 +65,10 @@ module Spree
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def add_to_shipment(shipment, variant, quantity)
|
68
|
-
|
69
|
-
on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
|
68
|
+
on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
|
70
69
|
|
71
|
-
|
72
|
-
|
73
|
-
else
|
74
|
-
quantity.times { shipment.set_up_inventory('on_hand', variant, order) }
|
75
|
-
end
|
70
|
+
on_hand.times { shipment.set_up_inventory('on_hand', variant, order) }
|
71
|
+
back_order.times { shipment.set_up_inventory('backordered', variant, order) }
|
76
72
|
|
77
73
|
# adding to this shipment, and removing from stock_location
|
78
74
|
if order.completed?
|
@@ -25,7 +25,8 @@ module Spree
|
|
25
25
|
update_shipment_state
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
update_promotion_adjustments
|
29
|
+
update_shipping_adjustments
|
29
30
|
# update totals a second time in case updated adjustments have an effect on the total
|
30
31
|
update_totals
|
31
32
|
|
@@ -101,16 +102,10 @@ module Spree
|
|
101
102
|
# The +payment_state+ value helps with reporting, etc. since it provides a quick and easy way to locate Orders needing attention.
|
102
103
|
def update_payment_state
|
103
104
|
|
104
|
-
#
|
105
|
+
#line_item are empty when user empties cart
|
105
106
|
if line_items.empty? || round_money(order.payment_total) < round_money(order.total)
|
106
|
-
if payments.present?
|
107
|
-
|
108
|
-
order.payment_state = 'failed'
|
109
|
-
elsif payments.last.state == 'completed'
|
110
|
-
order.payment_state = 'credit_owed'
|
111
|
-
else
|
112
|
-
order.payment_state = 'balance_due'
|
113
|
-
end
|
107
|
+
if payments.present? && payments.last.state == 'failed'
|
108
|
+
order.payment_state = 'failed'
|
114
109
|
else
|
115
110
|
order.payment_state = 'balance_due'
|
116
111
|
end
|
@@ -130,11 +125,17 @@ module Spree
|
|
130
125
|
#
|
131
126
|
# Adjustments will check if they are still eligible. Ineligible adjustments
|
132
127
|
# are preserved but not counted towards adjustment_total.
|
133
|
-
def
|
134
|
-
order.adjustments.reload.each { |adjustment| adjustment.update!(order) }
|
128
|
+
def update_promotion_adjustments
|
129
|
+
order.adjustments.reload.promotion.each { |adjustment| adjustment.update!(order) }
|
135
130
|
choose_best_promotion_adjustment
|
136
131
|
end
|
137
132
|
|
133
|
+
# Shipping adjustments don't receive order on update! because they calculated
|
134
|
+
# over a shipping / package object rather than an order object
|
135
|
+
def update_shipping_adjustments
|
136
|
+
order.adjustments.reload.shipping.each { |adjustment| adjustment.update! }
|
137
|
+
end
|
138
|
+
|
138
139
|
private
|
139
140
|
|
140
141
|
# Picks one (and only one) promotion to be eligible for this order
|
@@ -83,37 +83,30 @@ module Spree
|
|
83
83
|
|
84
84
|
credit_amount ||= credit_allowed >= order.outstanding_balance.abs ? order.outstanding_balance.abs : credit_allowed.abs
|
85
85
|
credit_amount = credit_amount.to_f
|
86
|
-
credit_cents = Spree::Money.new(credit_amount, currency: currency).money.cents
|
87
86
|
|
88
87
|
if payment_method.payment_profiles_supported?
|
89
|
-
response = payment_method.credit(
|
88
|
+
response = payment_method.credit((credit_amount * 100).round, source, response_code, gateway_options)
|
90
89
|
else
|
91
|
-
response = payment_method.credit(
|
90
|
+
response = payment_method.credit((credit_amount * 100).round, response_code, gateway_options)
|
92
91
|
end
|
93
92
|
|
94
93
|
record_response(response)
|
95
94
|
|
96
95
|
if response.success?
|
97
|
-
self.class.create(
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
96
|
+
self.class.create(
|
97
|
+
:order => order,
|
98
|
+
:source => self,
|
99
|
+
:payment_method => payment_method,
|
100
|
+
:amount => credit_amount.abs * -1,
|
101
|
+
:response_code => response.authorization,
|
102
|
+
:state => 'completed'
|
103
|
+
)
|
103
104
|
else
|
104
105
|
gateway_error(response)
|
105
106
|
end
|
106
107
|
end
|
107
108
|
end
|
108
109
|
|
109
|
-
def cancel!
|
110
|
-
if payment_method.respond_to?(:cancel)
|
111
|
-
payment_method.cancel(response_code)
|
112
|
-
else
|
113
|
-
credit!
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
110
|
def partial_credit(amount)
|
118
111
|
return if amount > credit_allowed
|
119
112
|
started_processing!
|
@@ -148,7 +141,7 @@ module Spree
|
|
148
141
|
protect_from_connection_error do
|
149
142
|
check_environment
|
150
143
|
|
151
|
-
response = payment_method.send(action,
|
144
|
+
response = payment_method.send(action, (amount * 100).round,
|
152
145
|
source,
|
153
146
|
gateway_options)
|
154
147
|
handle_response(response, success_state, :failure)
|
@@ -176,7 +169,7 @@ module Spree
|
|
176
169
|
end
|
177
170
|
|
178
171
|
def record_response(response)
|
179
|
-
log_entries.create(
|
172
|
+
log_entries.create(:details => response.to_yaml)
|
180
173
|
end
|
181
174
|
|
182
175
|
def protect_from_connection_error
|
data/app/models/spree/payment.rb
CHANGED
@@ -4,15 +4,16 @@ module Spree
|
|
4
4
|
|
5
5
|
IDENTIFIER_CHARS = (('A'..'Z').to_a + ('0'..'9').to_a - %w(0 1 I O)).freeze
|
6
6
|
|
7
|
-
belongs_to :order, class_name: 'Spree::Order'
|
7
|
+
belongs_to :order, class_name: 'Spree::Order'
|
8
8
|
belongs_to :source, polymorphic: true
|
9
9
|
belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
|
10
10
|
|
11
|
-
has_many :offsets,
|
11
|
+
has_many :offsets, -> { where("source_type = 'Spree::Payment' AND amount < 0 AND state = 'completed'") },
|
12
|
+
class_name: "Spree::Payment", foreign_key: :source_id
|
12
13
|
has_many :log_entries, as: :source
|
13
14
|
|
14
15
|
before_validation :validate_source
|
15
|
-
|
16
|
+
before_save :set_unique_identifier
|
16
17
|
|
17
18
|
after_save :create_payment_profile, if: :profiles_supported?
|
18
19
|
|
@@ -24,49 +25,45 @@ module Spree
|
|
24
25
|
attr_accessor :source_attributes
|
25
26
|
after_initialize :build_source
|
26
27
|
|
27
|
-
attr_accessible :amount, :payment_method_id, :source_attributes
|
28
|
-
|
29
28
|
scope :from_credit_card, -> { where(source_type: 'Spree::CreditCard') }
|
30
29
|
scope :with_state, ->(s) { where(state: s.to_s) }
|
31
|
-
scope :completed, with_state('completed')
|
32
|
-
scope :pending, with_state('pending')
|
33
|
-
scope :failed, with_state('failed')
|
34
|
-
scope :valid, where(
|
30
|
+
scope :completed, -> { with_state('completed') }
|
31
|
+
scope :pending, -> { with_state('pending') }
|
32
|
+
scope :failed, -> { with_state('failed') }
|
33
|
+
scope :valid, -> { where('state NOT IN (?)', %w(failed invalid)) }
|
35
34
|
|
36
35
|
after_rollback :persist_invalid
|
37
36
|
|
38
|
-
validates :amount, numericality: true
|
39
|
-
|
40
37
|
def persist_invalid
|
41
38
|
return unless ['failed', 'invalid'].include?(state)
|
42
39
|
state_will_change!
|
43
|
-
save
|
40
|
+
save
|
44
41
|
end
|
45
42
|
|
46
43
|
# order state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
|
47
|
-
state_machine initial:
|
44
|
+
state_machine initial: :checkout do
|
48
45
|
# With card payments, happens before purchase or authorization happens
|
49
46
|
event :started_processing do
|
50
|
-
transition from: [
|
47
|
+
transition from: [:checkout, :pending, :completed, :processing], to: :processing
|
51
48
|
end
|
52
49
|
# When processing during checkout fails
|
53
50
|
event :failure do
|
54
|
-
transition from: [
|
51
|
+
transition from: [:pending, :processing], to: :failed
|
55
52
|
end
|
56
53
|
# With card payments this represents authorizing the payment
|
57
54
|
event :pend do
|
58
|
-
transition from: [
|
55
|
+
transition from: [:checkout, :processing], to: :pending
|
59
56
|
end
|
60
57
|
# With card payments this represents completing a purchase or capture transaction
|
61
58
|
event :complete do
|
62
|
-
transition from: [
|
59
|
+
transition from: [:processing, :pending, :checkout], to: :completed
|
63
60
|
end
|
64
61
|
event :void do
|
65
|
-
transition from: [
|
62
|
+
transition from: [:pending, :completed, :checkout], to: :void
|
66
63
|
end
|
67
64
|
# when the card brand isnt supported
|
68
65
|
event :invalidate do
|
69
|
-
transition from: [
|
66
|
+
transition from: [:checkout], to: :invalid
|
70
67
|
end
|
71
68
|
end
|
72
69
|
|
@@ -79,22 +76,12 @@ module Spree
|
|
79
76
|
end
|
80
77
|
alias display_amount money
|
81
78
|
|
82
|
-
def amount=(amount)
|
83
|
-
self[:amount] =
|
84
|
-
case amount
|
85
|
-
when String
|
86
|
-
separator = I18n.t('number.currency.format.separator')
|
87
|
-
number = amount.delete("^0-9-#{separator}").tr(separator, '.')
|
88
|
-
number.to_d if number.present?
|
89
|
-
end || amount
|
90
|
-
end
|
91
|
-
|
92
79
|
def offsets_total
|
93
80
|
offsets.pluck(:amount).sum
|
94
81
|
end
|
95
82
|
|
96
83
|
def credit_allowed
|
97
|
-
amount - offsets_total
|
84
|
+
amount - offsets_total
|
98
85
|
end
|
99
86
|
|
100
87
|
def can_credit?
|
@@ -2,11 +2,10 @@ module Spree
|
|
2
2
|
class PaymentMethod < ActiveRecord::Base
|
3
3
|
acts_as_paranoid
|
4
4
|
DISPLAY = [:both, :front_end, :back_end]
|
5
|
-
default_scope where(deleted_at: nil)
|
5
|
+
default_scope -> { where(deleted_at: nil) }
|
6
6
|
|
7
7
|
scope :production, -> { where(environment: 'production') }
|
8
8
|
|
9
|
-
attr_accessible :name, :description, :environment, :display_on, :active
|
10
9
|
validates :name, presence: true
|
11
10
|
|
12
11
|
def self.providers
|
@@ -41,7 +40,7 @@ module Spree
|
|
41
40
|
end
|
42
41
|
|
43
42
|
def self.find_with_destroyed *args
|
44
|
-
|
43
|
+
unscoped { find(*args) }
|
45
44
|
end
|
46
45
|
|
47
46
|
def payment_profiles_supported?
|
@@ -78,7 +78,7 @@ module Spree::Preferences::Preferable
|
|
78
78
|
|
79
79
|
def preference_cache_key(name)
|
80
80
|
return unless id
|
81
|
-
[self.class.name, name, id].join('::').underscore
|
81
|
+
[ENV["RAILS_CACHE_ID"], self.class.name, name, id].join('::').underscore
|
82
82
|
end
|
83
83
|
|
84
84
|
def save_pending_preferences
|
data/app/models/spree/price.rb
CHANGED
@@ -5,8 +5,6 @@ module Spree
|
|
5
5
|
validate :check_price
|
6
6
|
validates :amount, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
7
7
|
|
8
|
-
attr_accessible :variant_id, :currency, :amount
|
9
|
-
|
10
8
|
def display_amount
|
11
9
|
money
|
12
10
|
end
|
@@ -24,11 +22,6 @@ module Spree
|
|
24
22
|
self[:amount] = parse_price(price)
|
25
23
|
end
|
26
24
|
|
27
|
-
# Remove variant default_scope `deleted_at: nil`
|
28
|
-
def variant
|
29
|
-
Spree::Variant.unscoped { super }
|
30
|
-
end
|
31
|
-
|
32
25
|
private
|
33
26
|
def check_price
|
34
27
|
raise "Price must belong to a variant" if variant.nil?
|
@@ -24,7 +24,7 @@ module Spree
|
|
24
24
|
next if name.to_s.include?("master_price")
|
25
25
|
parts = name.to_s.match(/(.*)_by_(.*)/)
|
26
26
|
order_text = "#{Product.quoted_table_name}.#{parts[2]} #{parts[1] == 'ascend' ? "ASC" : "DESC"}"
|
27
|
-
self.scope(name.to_s, order(order_text))
|
27
|
+
self.scope(name.to_s, -> { relation.order(order_text) })
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -68,11 +68,14 @@ module Spree
|
|
68
68
|
#
|
69
69
|
# SELECT COUNT(*) ...
|
70
70
|
add_search_scope :in_taxon do |taxon|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
71
|
+
if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
72
|
+
scope = select("DISTINCT ON (spree_products.id) spree_products.*")
|
73
|
+
else
|
74
|
+
scope = select("DISTINCT(spree_products.id), spree_products.*")
|
75
|
+
end
|
76
|
+
|
77
|
+
scope.joins(:taxons).
|
78
|
+
where(Taxon.table_name => { :id => taxon.self_and_descendants.map(&:id) })
|
76
79
|
end
|
77
80
|
|
78
81
|
# This scope selects products in all taxons AND all its descendants
|
@@ -124,20 +127,20 @@ module Spree
|
|
124
127
|
add_search_scope :with_option_value do |option, value|
|
125
128
|
option_values = OptionValue.table_name
|
126
129
|
option_type_id = case option
|
127
|
-
when String then OptionType.
|
130
|
+
when String then OptionType.find_by(name: option) || option.to_i
|
128
131
|
when OptionType then option.id
|
129
132
|
else option.to_i
|
130
133
|
end
|
131
134
|
|
132
135
|
conditions = "#{option_values}.name = ? AND #{option_values}.option_type_id = ?", value, option_type_id
|
133
|
-
group(
|
136
|
+
group('spree_products.id').joins(variants_including_master: :option_values).where(conditions)
|
134
137
|
end
|
135
138
|
|
136
139
|
# Finds all products which have either:
|
137
140
|
# 1) have an option value with the name matching the one given
|
138
141
|
# 2) have a product property with a value matching the one given
|
139
142
|
add_search_scope :with do |value|
|
140
|
-
includes(:
|
143
|
+
includes(variants_including_master: :option_values).
|
141
144
|
includes(:product_properties).
|
142
145
|
where("#{OptionValue.table_name}.name = ? OR #{ProductProperty.table_name}.value = ?", value, value)
|
143
146
|
end
|
@@ -160,7 +163,7 @@ module Spree
|
|
160
163
|
# Finds all products that have the ids matching the given collection of ids.
|
161
164
|
# Alternatively, you could use find(collection_of_ids), but that would raise an exception if one product couldn't be found
|
162
165
|
add_search_scope :with_ids do |*ids|
|
163
|
-
where(:
|
166
|
+
where(id: ids)
|
164
167
|
end
|
165
168
|
|
166
169
|
# Sorts products from most popular (popularity is extracted from how many
|
@@ -195,11 +198,7 @@ module Spree
|
|
195
198
|
|
196
199
|
# Can't use add_search_scope for this as it needs a default argument
|
197
200
|
def self.available(available_on = nil, currency = nil)
|
198
|
-
|
199
|
-
unless Spree::Config.show_products_without_price
|
200
|
-
scope = scope.where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL')
|
201
|
-
end
|
202
|
-
scope
|
201
|
+
joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now)
|
203
202
|
end
|
204
203
|
search_scopes << :available
|
205
204
|
|
@@ -249,10 +248,10 @@ module Spree
|
|
249
248
|
taxons = Taxon.table_name
|
250
249
|
ids_or_records_or_names.flatten.map { |t|
|
251
250
|
case t
|
252
|
-
when Integer then Taxon.
|
251
|
+
when Integer then Taxon.find_by(id: t)
|
253
252
|
when ActiveRecord::Base then t
|
254
253
|
when String
|
255
|
-
Taxon.
|
254
|
+
Taxon.find_by(name: t) ||
|
256
255
|
Taxon.where("#{taxons}.permalink LIKE ? OR #{taxons}.permalink = ?", "%/#{t}/", "#{t}/").first
|
257
256
|
end
|
258
257
|
}.compact.flatten.uniq
|
data/app/models/spree/product.rb
CHANGED
@@ -34,22 +34,22 @@ module Spree
|
|
34
34
|
belongs_to :shipping_category, class_name: 'Spree::ShippingCategory'
|
35
35
|
|
36
36
|
has_one :master,
|
37
|
+
-> { where is_master: true },
|
37
38
|
class_name: 'Spree::Variant',
|
38
|
-
conditions: { is_master: true },
|
39
39
|
dependent: :destroy
|
40
40
|
|
41
41
|
has_many :variants,
|
42
|
-
|
43
|
-
|
44
|
-
order: "#{::Spree::Variant.quoted_table_name}.position ASC"
|
42
|
+
-> { where(is_master: false).order("#{::Spree::Variant.quoted_table_name}.position ASC") },
|
43
|
+
class_name: 'Spree::Variant'
|
45
44
|
|
46
45
|
has_many :variants_including_master,
|
46
|
+
-> { order("#{::Spree::Variant.quoted_table_name}.position ASC") },
|
47
47
|
class_name: 'Spree::Variant',
|
48
|
-
dependent: :destroy
|
49
|
-
|
48
|
+
dependent: :destroy
|
49
|
+
|
50
|
+
has_many :prices, -> { order('spree_variants.position, spree_variants.id, currency') }, through: :variants
|
50
51
|
|
51
|
-
has_many :
|
52
|
-
has_many :stock_items, through: :variants_including_master
|
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')
|
@@ -62,7 +62,7 @@ module Spree
|
|
62
62
|
delegate :images, to: :master, prefix: true
|
63
63
|
alias_method :images, :master_images
|
64
64
|
|
65
|
-
has_many :variant_images, source: :images, through: :variants_including_master
|
65
|
+
has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
|
66
66
|
|
67
67
|
accepts_nested_attributes_for :variants, allow_destroy: true
|
68
68
|
|
@@ -73,31 +73,6 @@ module Spree
|
|
73
73
|
|
74
74
|
attr_accessor :option_values_hash
|
75
75
|
|
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
|
98
|
-
|
99
|
-
attr_accessible :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price')
|
100
|
-
|
101
76
|
accepts_nested_attributes_for :product_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? }
|
102
77
|
|
103
78
|
make_permalink order: :name
|
@@ -108,11 +83,6 @@ module Spree
|
|
108
83
|
|
109
84
|
before_destroy :punch_permalink
|
110
85
|
|
111
|
-
def variants_with_only_master
|
112
|
-
ActiveSupport::Deprecation.warn("[SPREE] Spree::Product#variants_with_only_master will be deprecated in Spree 1.3. Please use Spree::Product#master instead.")
|
113
|
-
master
|
114
|
-
end
|
115
|
-
|
116
86
|
def to_param
|
117
87
|
permalink.present? ? permalink : (permalink_was || name.to_s.to_url)
|
118
88
|
end
|
@@ -141,7 +111,7 @@ module Spree
|
|
141
111
|
return if option_values_hash.nil?
|
142
112
|
option_values_hash.keys.map(&:to_i).each do |id|
|
143
113
|
self.option_type_ids << id unless option_type_ids.include?(id)
|
144
|
-
product_option_types.create(
|
114
|
+
product_option_types.create(option_type_id: id) unless product_option_types.pluck(:option_type_id).include?(id)
|
145
115
|
end
|
146
116
|
end
|
147
117
|
|
@@ -159,11 +129,8 @@ module Spree
|
|
159
129
|
!!deleted_at
|
160
130
|
end
|
161
131
|
|
162
|
-
# determine if product is available.
|
163
|
-
# deleted products and products with nil or future available_on date
|
164
|
-
# are not available
|
165
132
|
def available?
|
166
|
-
!(available_on.nil? || available_on.future?)
|
133
|
+
!(available_on.nil? || available_on.future?)
|
167
134
|
end
|
168
135
|
|
169
136
|
# split variants list into hash which shows mapping of opt value onto matching variants
|
@@ -199,19 +166,14 @@ module Spree
|
|
199
166
|
end
|
200
167
|
|
201
168
|
def property(property_name)
|
202
|
-
return nil unless prop = properties.
|
203
|
-
product_properties.
|
169
|
+
return nil unless prop = properties.find_by(name: property_name)
|
170
|
+
product_properties.find_by(property: prop).try(:value)
|
204
171
|
end
|
205
172
|
|
206
173
|
def set_property(property_name, property_value)
|
207
174
|
ActiveRecord::Base.transaction do
|
208
|
-
|
209
|
-
|
210
|
-
Property.where(name: property_name).first
|
211
|
-
else
|
212
|
-
Property.create(name: property_name, presentation: property_name)
|
213
|
-
end
|
214
|
-
product_property = ProductProperty.where(product_id: self.id, property_id: property.id).first_or_initialize
|
175
|
+
property = Property.where(name: property_name).first_or_create!(presentation: property_name)
|
176
|
+
product_property = ProductProperty.where(product: self, property: property).first_or_initialize
|
215
177
|
product_property.value = property_value
|
216
178
|
product_property.save!
|
217
179
|
end
|
@@ -223,10 +185,10 @@ module Spree
|
|
223
185
|
end
|
224
186
|
|
225
187
|
def total_on_hand
|
226
|
-
if
|
227
|
-
Float::INFINITY
|
228
|
-
else
|
188
|
+
if Spree::Config.track_inventory_levels
|
229
189
|
self.stock_items.sum(&:count_on_hand)
|
190
|
+
else
|
191
|
+
Float::INFINITY
|
230
192
|
end
|
231
193
|
end
|
232
194
|
|
@@ -234,7 +196,7 @@ module Spree
|
|
234
196
|
# which would make AR's default finder return nil.
|
235
197
|
# This is a stopgap for that little problem.
|
236
198
|
def master
|
237
|
-
super || variants_including_master.with_deleted.where(:
|
199
|
+
super || variants_including_master.with_deleted.where(is_master: true).first
|
238
200
|
end
|
239
201
|
|
240
202
|
private
|
@@ -246,15 +208,18 @@ module Spree
|
|
246
208
|
values = values.inject(values.shift) { |memo, value| memo.product(value).map(&:flatten) }
|
247
209
|
|
248
210
|
values.each do |ids|
|
249
|
-
variant = variants.create(
|
211
|
+
variant = variants.create(
|
212
|
+
option_value_ids: ids,
|
213
|
+
price: master.price
|
214
|
+
)
|
250
215
|
end
|
251
216
|
save
|
252
217
|
end
|
253
218
|
|
254
219
|
def add_properties_and_option_types_from_prototype
|
255
|
-
if prototype_id && prototype = Spree::Prototype.
|
220
|
+
if prototype_id && prototype = Spree::Prototype.find_by(id: prototype_id)
|
256
221
|
prototype.properties.each do |property|
|
257
|
-
product_properties.create(
|
222
|
+
product_properties.create(property: property)
|
258
223
|
end
|
259
224
|
self.option_types = prototype.option_types
|
260
225
|
end
|
@@ -268,7 +233,7 @@ module Spree
|
|
268
233
|
# there's a weird quirk with the delegate stuff that does not automatically save the delegate object
|
269
234
|
# when saving so we force a save using a hook.
|
270
235
|
def save_master
|
271
|
-
master.save if master && (master.changed? || master.new_record? || (master.default_price && (master.default_price.changed
|
236
|
+
master.save if master && (master.changed? || master.new_record? || (master.default_price && (master.default_price.changed || master.default_price.new_record)))
|
272
237
|
end
|
273
238
|
|
274
239
|
def ensure_master
|
@@ -278,7 +243,7 @@ module Spree
|
|
278
243
|
|
279
244
|
def punch_permalink
|
280
245
|
update_attribute :permalink, "#{Time.now.to_i}_#{permalink}" # punch permalink with date prefix
|
281
|
-
end
|
246
|
+
end
|
282
247
|
end
|
283
248
|
end
|
284
249
|
|