spree_core 1.0.7 → 1.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/javascripts/admin/admin.js.erb +9 -18
- data/app/assets/javascripts/admin/calculator.js +7 -6
- data/app/assets/javascripts/admin/checkouts/edit.js +2 -2
- data/app/assets/javascripts/admin/image_settings.js +49 -0
- data/app/assets/javascripts/admin/orders/edit_form.js +14 -14
- data/app/assets/stylesheets/admin/admin.css.erb +36 -54
- data/app/assets/stylesheets/store/screen.css.scss +25 -12
- data/app/controllers/spree/admin/base_controller.rb +3 -3
- data/app/controllers/spree/admin/image_settings_controller.rb +71 -0
- data/app/controllers/spree/admin/images_controller.rb +22 -21
- data/app/controllers/spree/admin/mail_methods_controller.rb +1 -1
- data/app/controllers/spree/admin/orders/customer_details_controller.rb +2 -2
- data/app/controllers/spree/admin/orders_controller.rb +13 -13
- data/app/controllers/spree/admin/payment_methods_controller.rb +1 -1
- data/app/controllers/spree/admin/payments_controller.rb +1 -1
- data/app/controllers/spree/admin/product_properties_controller.rb +3 -4
- data/app/controllers/spree/admin/products_controller.rb +49 -47
- data/app/controllers/spree/admin/properties_controller.rb +1 -1
- data/app/controllers/spree/admin/prototypes_controller.rb +12 -6
- data/app/controllers/spree/admin/reports_controller.rb +15 -16
- data/app/controllers/spree/admin/resource_controller.rb +1 -1
- data/app/controllers/spree/admin/return_authorizations_controller.rb +1 -1
- data/app/controllers/spree/admin/shipments_controller.rb +0 -3
- data/app/controllers/spree/admin/states_controller.rb +9 -9
- data/app/controllers/spree/admin/users_controller.rb +18 -42
- data/app/controllers/spree/admin/variants_controller.rb +22 -21
- data/app/controllers/spree/admin/zones_controller.rb +11 -11
- data/app/controllers/spree/content_controller.rb +1 -1
- data/app/controllers/spree/locale_controller.rb +2 -2
- data/app/controllers/spree/orders_controller.rb +4 -6
- data/app/controllers/spree/products_controller.rb +1 -1
- data/app/controllers/spree/states_controller.rb +4 -7
- data/app/helpers/spree/admin/base_helper.rb +4 -0
- data/app/helpers/spree/admin/navigation_helper.rb +4 -16
- data/app/helpers/spree/base_helper.rb +22 -3
- data/app/models/spree/activator.rb +4 -1
- data/app/models/spree/address.rb +45 -45
- data/app/models/spree/adjustment.rb +4 -4
- data/app/models/spree/app_configuration.rb +12 -0
- data/app/models/spree/calculator/default_tax.rb +1 -3
- data/app/models/spree/calculator/flat_percent_item_total.rb +0 -2
- data/app/models/spree/calculator/flat_rate.rb +0 -3
- data/app/models/spree/calculator/flexi_rate.rb +1 -3
- data/app/models/spree/calculator/per_item.rb +1 -6
- data/app/models/spree/calculator/price_sack.rb +0 -2
- data/app/models/spree/country.rb +1 -1
- data/app/models/spree/creditcard.rb +9 -10
- data/app/models/spree/gateway.rb +0 -2
- data/app/models/spree/image.rb +18 -2
- data/app/models/spree/inventory_unit.rb +4 -2
- data/app/models/spree/mail_method.rb +1 -6
- data/app/models/spree/option_type.rb +3 -1
- data/app/models/spree/order.rb +68 -62
- data/app/models/spree/payment.rb +1 -1
- data/app/models/spree/payment_method.rb +0 -6
- data/app/models/spree/product/scopes.rb +44 -44
- data/app/models/spree/product.rb +44 -26
- data/app/models/spree/product_property.rb +0 -2
- data/app/models/spree/property.rb +0 -16
- data/app/models/spree/prototype.rb +0 -1
- data/app/models/spree/return_authorization.rb +0 -2
- data/app/models/spree/shipment.rb +2 -3
- data/app/models/spree/shipping_category.rb +0 -2
- data/app/models/spree/shipping_method.rb +1 -2
- data/app/models/spree/shipping_rate.rb +9 -0
- data/app/models/spree/state.rb +10 -2
- data/app/models/spree/{state_event.rb → state_change.rb} +1 -4
- data/app/models/spree/tax_rate.rb +3 -3
- data/app/models/spree/taxon.rb +1 -6
- data/app/models/spree/taxonomy.rb +3 -6
- data/app/models/spree/user.rb +1 -0
- data/app/models/spree/variant.rb +3 -4
- data/app/models/spree/zone.rb +0 -10
- data/app/views/spree/admin/adjustments/_adjustments_table.html.erb +1 -1
- data/app/views/spree/admin/configurations/index.html.erb +4 -0
- data/app/views/spree/admin/image_settings/edit.html.erb +91 -0
- data/app/views/spree/admin/image_settings/show.html.erb +11 -0
- data/app/views/spree/admin/images/index.html.erb +3 -3
- data/app/views/spree/admin/mail_methods/_form.html.erb +1 -1
- data/app/views/spree/admin/mail_methods/index.html.erb +1 -1
- data/app/views/spree/admin/option_types/_option_value_fields.html.erb +1 -1
- data/app/views/spree/admin/option_types/index.html.erb +1 -1
- data/app/views/spree/admin/option_types/new.js.erb +1 -1
- data/app/views/spree/admin/orders/_form.html.erb +3 -5
- data/app/views/spree/admin/orders/_line_item.html.erb +1 -1
- data/app/views/spree/admin/orders/customer_details/_form.html.erb +1 -1
- data/app/views/spree/admin/orders/history.html.erb +2 -2
- data/app/views/spree/admin/orders/index.html.erb +19 -19
- data/app/views/spree/admin/payment_methods/_form.html.erb +1 -1
- data/app/views/spree/admin/payment_methods/index.html.erb +1 -3
- data/app/views/spree/admin/payments/_list.html.erb +1 -1
- data/app/views/spree/admin/product_properties/index.html.erb +1 -1
- data/app/views/spree/admin/products/_form.html.erb +30 -27
- data/app/views/spree/admin/products/index.html.erb +9 -9
- data/app/views/spree/admin/products/new.html.erb +28 -4
- data/app/views/spree/admin/products/new.js.erb +1 -1
- data/app/views/spree/admin/properties/index.html.erb +1 -1
- data/app/views/spree/admin/properties/new.js.erb +1 -1
- data/app/views/spree/admin/prototypes/index.html.erb +1 -1
- data/app/views/spree/admin/prototypes/new.js.erb +1 -1
- data/app/views/spree/admin/prototypes/show.html.erb +42 -0
- data/app/views/spree/admin/return_authorizations/index.html.erb +1 -1
- data/app/views/spree/admin/shared/_address_form.html.erb +1 -1
- data/app/views/spree/admin/shared/_calculator_fields.html.erb +2 -2
- data/app/views/spree/admin/shared/_head.html.erb +3 -3
- data/app/views/spree/admin/shared/_order_details.html.erb +1 -1
- data/app/views/spree/admin/shared/_order_tabs.html.erb +6 -6
- data/app/views/spree/admin/shared/_product_sub_menu.html.erb +0 -1
- data/app/views/spree/admin/shared/_report_criteria.html.erb +3 -3
- data/app/views/spree/admin/shared/_tabs.html.erb +1 -1
- data/app/views/spree/admin/shared/_update_order_state.js +5 -4
- data/app/views/spree/admin/shipments/index.html.erb +1 -1
- data/app/views/spree/admin/shipping_categories/index.html.erb +1 -1
- data/app/views/spree/admin/shipping_methods/index.html.erb +1 -1
- data/app/views/spree/admin/states/_state_list.html.erb +1 -1
- data/app/views/spree/admin/states/new.js.erb +1 -1
- data/app/views/spree/admin/tax_categories/index.html.erb +2 -2
- data/app/views/spree/admin/tax_rates/index.html.erb +2 -2
- data/app/views/spree/admin/taxonomies/_list.html.erb +1 -1
- data/app/views/spree/admin/trackers/_form.html.erb +1 -1
- data/app/views/spree/admin/trackers/index.html.erb +1 -1
- data/app/views/spree/admin/users/_form.html.erb +1 -16
- data/app/views/spree/admin/users/index.html.erb +3 -4
- data/app/views/spree/admin/variants/index.html.erb +2 -2
- data/app/views/spree/admin/variants/new.js.erb +1 -1
- data/app/views/spree/admin/zones/index.html.erb +1 -1
- data/app/views/spree/checkout/_address.html.erb +1 -1
- data/app/views/spree/checkout/edit.html.erb +1 -1
- data/app/views/spree/checkout/registration.html.erb +2 -2
- data/app/views/spree/order_mailer/confirm_email.text.erb +3 -3
- data/app/views/spree/orders/show.html.erb +2 -2
- data/app/views/spree/products/_cart_form.html.erb +1 -2
- data/app/views/spree/products/_image.html.erb +1 -1
- data/app/views/spree/products/show.html.erb +3 -3
- data/app/views/spree/shared/_filters.html.erb +26 -24
- data/app/views/spree/shared/_google_analytics.html.erb +26 -26
- data/app/views/spree/shared/_products.html.erb +2 -2
- data/app/views/spree/shared/_search.html.erb +1 -1
- data/app/views/spree/shared/_store_menu.html.erb +1 -1
- data/app/views/spree/states/index.js.erb +1 -1
- data/config/initializers/rails_3_1.rb +3 -3
- data/config/locales/en.yml +40 -35
- data/config/routes.rb +5 -13
- data/db/migrate/20101026184714_migrate_transactions_to_payment_state.rb +4 -4
- data/db/migrate/20111007143030_namespace_top_level_models.rb +0 -3
- data/db/migrate/20120203001428_rename_state_events_to_state_changes.rb +9 -0
- data/db/migrate/20120315064358_migrate_images_from_products_to_variants.rb +35 -0
- data/lib/generators/spree/dummy/templates/rails/database.yml +48 -20
- data/lib/generators/spree/install/install_generator.rb +17 -1
- data/lib/generators/spree/install/templates/app/assets/javascripts/admin/all.js +0 -1
- data/lib/generators/spree/install/templates/app/assets/javascripts/store/all.js +0 -1
- data/lib/generators/spree/install/templates/app/assets/stylesheets/admin/all.css +0 -1
- data/lib/generators/spree/install/templates/app/assets/stylesheets/store/all.css +0 -1
- data/lib/generators/spree/sandbox/sandbox_generator.rb +9 -2
- data/lib/spree/core/calculated_adjustments.rb +29 -34
- data/lib/spree/core/controller_helpers.rb +36 -34
- data/lib/spree/core/custom_fixtures.rb +1 -1
- data/lib/spree/core/delegate_belongs_to.rb +22 -24
- data/lib/spree/core/engine.rb +3 -3
- data/lib/spree/core/environment_extension.rb +12 -15
- data/lib/spree/core/mail_settings.rb +1 -1
- data/lib/spree/core/permalinks.rb +24 -16
- data/lib/spree/core/preference_rescue.rb +1 -1
- data/lib/spree/core/respond_with.rb +13 -8
- data/lib/spree/core/responder.rb +1 -2
- data/lib/spree/core/search/base.rb +36 -19
- data/lib/spree/core/ssl_requirement.rb +18 -10
- data/lib/spree/core/testing_support/common_rake.rb +1 -1
- data/lib/spree/core/testing_support/factories/product_factory.rb +9 -9
- data/lib/spree/core/testing_support/factories/role_factory.rb +1 -1
- data/lib/spree/core/testing_support/factories/shipping_category_factory.rb +1 -1
- data/lib/spree/core/testing_support/factories/shipping_method_factory.rb +3 -3
- data/lib/spree/core/testing_support/factories/user_factory.rb +1 -1
- data/lib/spree/core/testing_support/factories/zone_factory.rb +4 -2
- data/lib/spree/core/validators/email.rb +23 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +2 -2
- data/lib/spree/product_filters.rb +10 -19
- data/lib/tasks/core.rake +1 -1
- data/vendor/assets/javascripts/jquery.alerts/jquery.alerts.css.erb +5 -5
- data/vendor/assets/stylesheets/jquery-ui.datepicker.css.erb +1 -1
- metadata +68 -140
- data/app/assets/images/noimage/large.png +0 -0
- data/app/controllers/spree/admin/product_groups_controller.rb +0 -49
- data/app/controllers/spree/admin/product_scopes_controller.rb +0 -39
- data/app/helpers/spree/admin/product_groups_helper.rb +0 -14
- data/app/helpers/spree/admin/product_properties_helper.rb +0 -24
- data/app/models/spree/product_group.rb +0 -200
- data/app/models/spree/product_scope.rb +0 -79
- data/app/views/spree/admin/banners/_gateway.html.erb +0 -14
- data/app/views/spree/admin/product_groups/_preview.html.erb +0 -33
- data/app/views/spree/admin/product_groups/_product_scope.html.erb +0 -24
- data/app/views/spree/admin/product_groups/edit.html.erb +0 -59
- data/app/views/spree/admin/product_groups/index.html.erb +0 -37
- data/app/views/spree/admin/product_groups/new.html.erb +0 -12
- data/app/views/spree/admin/product_groups/show.html.erb +0 -32
- data/app/views/spree/admin/product_scopes/create.js.erb +0 -6
- data/app/views/spree/admin/product_scopes/destroy.js.erb +0 -3
- data/app/views/spree/admin/product_scopes/new.html.erb +0 -1
- data/app/views/spree/admin/shared/_group_from_products_form.html.erb +0 -12
- data/db/migrate/20091012120519_product_groups_and_scopes.rb +0 -18
- data/db/migrate/20100126103714_create_products_product_groups.rb +0 -8
- data/db/migrate/20100306153445_fix_by_popularity.rb +0 -9
- data/db/migrate/20120523061241_convert_sales_tax_to_default_tax.rb +0 -9
- data/lib/spree/core/testing_support/factories/product_group_factory.rb +0 -5
- data/lib/spree/core/testing_support/factories/product_scope_factory.rb +0 -7
- data/lib/spree/core/theme_support/hook_listener.rb +0 -145
- data/lib/spree/core/theme_support.rb +0 -1
- data/vendor/assets/javascripts/jquery.alerts/jquery.alerts.css +0 -57
data/app/models/spree/order.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'spree/core/validators/email'
|
2
|
+
|
1
3
|
module Spree
|
2
4
|
class Order < ActiveRecord::Base
|
3
5
|
attr_accessible :line_items, :bill_address_attributes, :ship_address_attributes, :payments_attributes,
|
@@ -5,11 +7,18 @@ module Spree
|
|
5
7
|
:shipping_method_id, :email, :use_billing, :special_instructions
|
6
8
|
|
7
9
|
belongs_to :user
|
10
|
+
|
8
11
|
belongs_to :bill_address, :foreign_key => 'bill_address_id', :class_name => 'Spree::Address'
|
12
|
+
alias_method :billing_address, :bill_address
|
13
|
+
alias_method :billing_address=, :bill_address=
|
14
|
+
|
9
15
|
belongs_to :ship_address, :foreign_key => 'ship_address_id', :class_name => 'Spree::Address'
|
16
|
+
alias_method :shipping_address, :ship_address
|
17
|
+
alias_method :shipping_address=, :ship_address=
|
18
|
+
|
10
19
|
belongs_to :shipping_method
|
11
20
|
|
12
|
-
has_many :
|
21
|
+
has_many :state_changes, :as => :stateful
|
13
22
|
has_many :line_items, :dependent => :destroy
|
14
23
|
has_many :inventory_units
|
15
24
|
has_many :payments, :dependent => :destroy
|
@@ -30,7 +39,7 @@ module Spree
|
|
30
39
|
after_create :create_tax_charge!
|
31
40
|
|
32
41
|
# TODO: validate the format of the email as well (but we can't rely on authlogic anymore to help with validation)
|
33
|
-
validates :email, :presence => true, :
|
42
|
+
validates :email, :presence => true, :email => true, :if => :require_email
|
34
43
|
validate :has_available_shipment
|
35
44
|
validate :has_available_payment
|
36
45
|
|
@@ -51,41 +60,6 @@ module Spree
|
|
51
60
|
class_attribute :update_hooks
|
52
61
|
self.update_hooks = Set.new
|
53
62
|
|
54
|
-
# Use this method in other gems that wish to register their own custom logic that should be called after Order#updat
|
55
|
-
def self.register_update_hook(hook)
|
56
|
-
self.update_hooks.add(hook)
|
57
|
-
end
|
58
|
-
|
59
|
-
# For compatiblity with Calculator::PriceSack
|
60
|
-
def amount
|
61
|
-
line_items.map(&:amount).sum
|
62
|
-
end
|
63
|
-
|
64
|
-
def to_param
|
65
|
-
number.to_s.to_url.upcase
|
66
|
-
end
|
67
|
-
|
68
|
-
def completed?
|
69
|
-
!! completed_at
|
70
|
-
end
|
71
|
-
|
72
|
-
# Indicates whether or not the user is allowed to proceed to checkout. Currently this is implemented as a
|
73
|
-
# check for whether or not there is at least one LineItem in the Order. Feel free to override this logic
|
74
|
-
# in your own application if you require additional steps before allowing a checkout.
|
75
|
-
def checkout_allowed?
|
76
|
-
line_items.count > 0
|
77
|
-
end
|
78
|
-
|
79
|
-
# Is this a free order in which case the payment step should be skipped
|
80
|
-
def payment_required?
|
81
|
-
total.to_f > 0.0
|
82
|
-
end
|
83
|
-
|
84
|
-
# Indicates the number of items in the order
|
85
|
-
def item_count
|
86
|
-
line_items.map(&:quantity).sum
|
87
|
-
end
|
88
|
-
|
89
63
|
# order state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
|
90
64
|
state_machine :initial => 'cart', :use_transactions => false do
|
91
65
|
|
@@ -120,11 +94,7 @@ module Spree
|
|
120
94
|
begin
|
121
95
|
order.process_payments!
|
122
96
|
rescue Core::GatewayError
|
123
|
-
|
124
|
-
true
|
125
|
-
else
|
126
|
-
false
|
127
|
-
end
|
97
|
+
!!Spree::Config[:allow_checkout_on_gateway_error]
|
128
98
|
end
|
129
99
|
end
|
130
100
|
|
@@ -140,6 +110,41 @@ module Spree
|
|
140
110
|
|
141
111
|
end
|
142
112
|
|
113
|
+
# Use this method in other gems that wish to register their own custom logic that should be called after Order#updat
|
114
|
+
def self.register_update_hook(hook)
|
115
|
+
self.update_hooks.add(hook)
|
116
|
+
end
|
117
|
+
|
118
|
+
# For compatiblity with Calculator::PriceSack
|
119
|
+
def amount
|
120
|
+
line_items.map(&:amount).sum
|
121
|
+
end
|
122
|
+
|
123
|
+
def to_param
|
124
|
+
number.to_s.to_url.upcase
|
125
|
+
end
|
126
|
+
|
127
|
+
def completed?
|
128
|
+
!! completed_at
|
129
|
+
end
|
130
|
+
|
131
|
+
# Indicates whether or not the user is allowed to proceed to checkout. Currently this is implemented as a
|
132
|
+
# check for whether or not there is at least one LineItem in the Order. Feel free to override this logic
|
133
|
+
# in your own application if you require additional steps before allowing a checkout.
|
134
|
+
def checkout_allowed?
|
135
|
+
line_items.count > 0
|
136
|
+
end
|
137
|
+
|
138
|
+
# Is this a free order in which case the payment step should be skipped
|
139
|
+
def payment_required?
|
140
|
+
total.to_f > 0.0
|
141
|
+
end
|
142
|
+
|
143
|
+
# Indicates the number of items in the order
|
144
|
+
def item_count
|
145
|
+
line_items.map(&:quantity).sum
|
146
|
+
end
|
147
|
+
|
143
148
|
# Indicates whether there are any backordered InventoryUnits associated with the Order.
|
144
149
|
def backordered?
|
145
150
|
return false unless Spree::Config[:track_inventory_levels]
|
@@ -220,8 +225,8 @@ module Spree
|
|
220
225
|
|
221
226
|
def restore_state
|
222
227
|
# pop the resume event so we can see what the event before that was
|
223
|
-
|
224
|
-
update_attribute('state',
|
228
|
+
state_changes.pop if state_changes.last.name == 'resume'
|
229
|
+
update_attribute('state', state_changes.last.previous_state)
|
225
230
|
|
226
231
|
if paid?
|
227
232
|
raise 'do something with inventory'
|
@@ -250,7 +255,7 @@ module Spree
|
|
250
255
|
|
251
256
|
def allow_resume?
|
252
257
|
# we shouldn't allow resume for legacy orders b/c we lack the information necessary to restore to a previous state
|
253
|
-
return false if
|
258
|
+
return false if state_changes.empty? || state_changes.last.previous_state.nil?
|
254
259
|
true
|
255
260
|
end
|
256
261
|
|
@@ -319,8 +324,8 @@ module Spree
|
|
319
324
|
# include taxes then price adjustments are created instead.
|
320
325
|
def create_tax_charge!
|
321
326
|
# destroy any previous adjustments (eveything is recalculated from scratch)
|
322
|
-
adjustments.tax.each
|
323
|
-
price_adjustments.each
|
327
|
+
adjustments.tax.each(&:destroy)
|
328
|
+
price_adjustments.each(&:destroy)
|
324
329
|
|
325
330
|
TaxRate.match(self).each { |rate| rate.adjust(self) }
|
326
331
|
end
|
@@ -331,10 +336,10 @@ module Spree
|
|
331
336
|
if shipment.present?
|
332
337
|
shipment.update_attributes!(:shipping_method => shipping_method)
|
333
338
|
else
|
334
|
-
self.shipments << Shipment.create!(:order => self,
|
339
|
+
self.shipments << Shipment.create!({ :order => self,
|
335
340
|
:shipping_method => shipping_method,
|
336
341
|
:address => self.ship_address,
|
337
|
-
:inventory_units => self.inventory_units)
|
342
|
+
:inventory_units => self.inventory_units}, :without_protection => true)
|
338
343
|
end
|
339
344
|
|
340
345
|
end
|
@@ -371,12 +376,12 @@ module Spree
|
|
371
376
|
adjustments.optional.each { |adjustment| adjustment.update_attribute('locked', true) }
|
372
377
|
OrderMailer.confirm_email(self).deliver
|
373
378
|
|
374
|
-
self.
|
379
|
+
self.state_changes.create({
|
375
380
|
:previous_state => 'cart',
|
376
381
|
:next_state => 'complete',
|
377
382
|
:name => 'order' ,
|
378
383
|
:user_id => (User.respond_to?(:current) && User.current.try(:id)) || self.user_id
|
379
|
-
})
|
384
|
+
}, :without_protection => true)
|
380
385
|
end
|
381
386
|
|
382
387
|
# Helper methods for checkout steps
|
@@ -393,12 +398,11 @@ module Spree
|
|
393
398
|
def rate_hash
|
394
399
|
@rate_hash ||= available_shipping_methods(:front_end).collect do |ship_method|
|
395
400
|
next unless cost = ship_method.calculator.compute(self)
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
end.compact.sort_by { |r| r[:cost] }
|
401
|
+
ShippingRate.new( :id => ship_method.id,
|
402
|
+
:shipping_method => ship_method,
|
403
|
+
:name => ship_method.name,
|
404
|
+
:cost => cost)
|
405
|
+
end.compact.sort_by { |r| r.cost }
|
402
406
|
end
|
403
407
|
|
404
408
|
def payment
|
@@ -466,12 +470,12 @@ module Spree
|
|
466
470
|
self.shipment_state = 'backorder' if backordered?
|
467
471
|
|
468
472
|
if old_shipment_state = self.changed_attributes['shipment_state']
|
469
|
-
self.
|
473
|
+
self.state_changes.create({
|
470
474
|
:previous_state => old_shipment_state,
|
471
475
|
:next_state => self.shipment_state,
|
472
476
|
:name => 'shipment',
|
473
477
|
:user_id => (User.respond_to?(:current) && User.current && User.current.id) || self.user_id
|
474
|
-
})
|
478
|
+
}, :without_protection => true)
|
475
479
|
end
|
476
480
|
end
|
477
481
|
|
@@ -484,7 +488,9 @@ module Spree
|
|
484
488
|
#
|
485
489
|
# The +payment_state+ value helps with reporting, etc. since it provides a quick and easy way to locate Orders needing attention.
|
486
490
|
def update_payment_state
|
487
|
-
|
491
|
+
|
492
|
+
#line_item are empty when user empties cart
|
493
|
+
if self.line_items.empty? || round_money(payment_total) < round_money(total)
|
488
494
|
self.payment_state = 'balance_due'
|
489
495
|
self.payment_state = 'failed' if payments.present? and payments.last.state == 'failed'
|
490
496
|
elsif round_money(payment_total) > round_money(total)
|
@@ -494,12 +500,12 @@ module Spree
|
|
494
500
|
end
|
495
501
|
|
496
502
|
if old_payment_state = self.changed_attributes['payment_state']
|
497
|
-
self.
|
503
|
+
self.state_changes.create({
|
498
504
|
:previous_state => old_payment_state,
|
499
505
|
:next_state => self.payment_state,
|
500
506
|
:name => 'payment',
|
501
507
|
:user_id => (User.respond_to?(:current) && User.current && User.current.id) || self.user_id
|
502
|
-
})
|
508
|
+
}, :without_protection => true)
|
503
509
|
end
|
504
510
|
end
|
505
511
|
|
data/app/models/spree/payment.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Spree
|
2
2
|
class Payment < ActiveRecord::Base
|
3
3
|
belongs_to :order
|
4
|
-
belongs_to :source, :polymorphic => true
|
4
|
+
belongs_to :source, :polymorphic => true, :validate => true
|
5
5
|
belongs_to :payment_method
|
6
6
|
|
7
7
|
has_many :offsets, :class_name => 'Spree::Payment', :foreign_key => 'source_id', :conditions => "source_type = 'Spree::Payment' AND amount < 0 AND state = 'completed'"
|
@@ -30,12 +30,6 @@ module Spree
|
|
30
30
|
self.count(:conditions => { :type => self.to_s, :environment => Rails.env, :active => true }) > 0
|
31
31
|
end
|
32
32
|
|
33
|
-
# TODO: Remove this method by 1.0
|
34
|
-
def self.current
|
35
|
-
ActiveSupport::Deprecation.warn "Gateway.current is deprecated and will be removed in Spree > 1.0. Use current_order.payment_method instead."
|
36
|
-
first(:conditions => { :active => true, :environment => Rails.env })
|
37
|
-
end
|
38
|
-
|
39
33
|
def method_type
|
40
34
|
type.demodulize.downcase
|
41
35
|
end
|
@@ -1,25 +1,11 @@
|
|
1
1
|
module Spree
|
2
2
|
class Product < ActiveRecord::Base
|
3
|
-
cattr_accessor :search_scopes do
|
4
|
-
[]
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.add_search_scope(name, &block)
|
8
|
-
self.singleton_class.send(:define_method, name.to_sym, &block)
|
9
|
-
search_scopes << name.to_sym
|
10
|
-
end
|
11
|
-
|
12
3
|
def self.simple_scopes
|
13
4
|
[
|
14
5
|
:ascend_by_updated_at,
|
15
6
|
:descend_by_updated_at,
|
16
7
|
:ascend_by_name,
|
17
|
-
:descend_by_name
|
18
|
-
# Need to have master price scopes here
|
19
|
-
# This makes them appear in admin/product_groups/edit
|
20
|
-
:ascend_by_master_price,
|
21
|
-
:descend_by_master_price,
|
22
|
-
:descend_by_popularity
|
8
|
+
:descend_by_name
|
23
9
|
]
|
24
10
|
end
|
25
11
|
|
@@ -31,23 +17,37 @@ module Spree
|
|
31
17
|
self.scope(name.to_s, relation.order(order_text))
|
32
18
|
end
|
33
19
|
|
34
|
-
|
20
|
+
def self.ascend_by_master_price
|
35
21
|
joins(:variants_with_only_master).order("#{variant_table_name}.price ASC")
|
36
22
|
end
|
37
23
|
|
38
|
-
|
24
|
+
def self.descend_by_master_price
|
39
25
|
joins(:variants_with_only_master).order("#{variant_table_name}.price DESC")
|
40
26
|
end
|
41
27
|
|
42
|
-
|
28
|
+
# Ryan Bates - http://railscasts.com/episodes/112
|
29
|
+
# general merging of conditions, names following the searchlogic pattern
|
30
|
+
scope :conditions, lambda { |*args| { :conditions => args } }
|
31
|
+
|
32
|
+
# conditions_all is a more descriptively named enhancement of the above
|
33
|
+
scope :conditions_all, lambda { |*args| { :conditions => [args].flatten } }
|
34
|
+
|
35
|
+
# forming the disjunction of a list of conditions (as strings)
|
36
|
+
scope :conditions_any, lambda { |*args|
|
37
|
+
args = [args].flatten
|
38
|
+
raise "non-strings in conditions_any" unless args.all? { |s| s.is_a? String }
|
39
|
+
{ :conditions => args.map { |c| "(#{c})"}.join(" OR ") }
|
40
|
+
}
|
41
|
+
|
42
|
+
def self.price_between(low, high)
|
43
43
|
joins(:master).where(Variant.table_name => { :price => low..high })
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
def self.master_price_lte(price)
|
47
47
|
joins(:master).where("#{variant_table_name}.price <= ?", price)
|
48
48
|
end
|
49
49
|
|
50
|
-
|
50
|
+
def self.master_price_gte(price)
|
51
51
|
joins(:master).where("#{variant_table_name}.price >= ?", price)
|
52
52
|
end
|
53
53
|
|
@@ -55,7 +55,7 @@ module Spree
|
|
55
55
|
# If you need products only within one taxon use
|
56
56
|
#
|
57
57
|
# Spree::Product.taxons_id_eq(x)
|
58
|
-
|
58
|
+
def self.in_taxon(taxon)
|
59
59
|
joins(:taxons).where(Taxon.table_name => { :id => taxon.self_and_descendants.map(&:id) })
|
60
60
|
end
|
61
61
|
|
@@ -63,17 +63,18 @@ module Spree
|
|
63
63
|
# If you need products only within one taxon use
|
64
64
|
#
|
65
65
|
# Spree::Product.taxons_id_eq([x,y])
|
66
|
-
|
66
|
+
#
|
67
|
+
def self.in_taxons(*taxons)
|
67
68
|
taxons = get_taxons(taxons)
|
68
69
|
taxons.first ? prepare_taxon_conditions(taxons) : scoped
|
69
70
|
end
|
70
71
|
|
71
|
-
def self.in_cached_group(product_group)
|
72
|
-
|
73
|
-
end
|
72
|
+
# def self.in_cached_group(product_group)
|
73
|
+
# joins(:product_groups).where('spree_product_groups_products.product_group_id' => product_group)
|
74
|
+
# end
|
74
75
|
|
75
76
|
# a scope that finds all products having property specified by name, object or id
|
76
|
-
|
77
|
+
def self.with_property(property)
|
77
78
|
properties = Property.table_name
|
78
79
|
conditions = case property
|
79
80
|
when String then { "#{properties}.name" => property }
|
@@ -86,7 +87,7 @@ module Spree
|
|
86
87
|
|
87
88
|
# a simple test for product with a certain property-value pairing
|
88
89
|
# note that it can test for properties with NULL values, but not for absent values
|
89
|
-
|
90
|
+
def self.with_property_value(property, value)
|
90
91
|
properties = Spree::Property.table_name
|
91
92
|
conditions = case property
|
92
93
|
when String then ["#{properties}.name = ?", property]
|
@@ -98,7 +99,8 @@ module Spree
|
|
98
99
|
joins(:properties).where(conditions)
|
99
100
|
end
|
100
101
|
|
101
|
-
|
102
|
+
# a scope that finds all products having an option_type specified by name, object or id
|
103
|
+
def self.with_option(option)
|
102
104
|
option_types = OptionType.table_name
|
103
105
|
conditions = case option
|
104
106
|
when String then { "#{option_types}.name" => option }
|
@@ -109,7 +111,8 @@ module Spree
|
|
109
111
|
joins(:option_types).where(conditions)
|
110
112
|
end
|
111
113
|
|
112
|
-
|
114
|
+
# a scope that finds all products having an option value specified by name, object or id
|
115
|
+
def self.with_option_value(option, value)
|
113
116
|
option_values = OptionValue.table_name
|
114
117
|
option_type_id = case option
|
115
118
|
when String then OptionType.find_by_name(option) || option.to_i
|
@@ -124,30 +127,30 @@ module Spree
|
|
124
127
|
# Finds all products which have either:
|
125
128
|
# 1) have an option value with the name matching the one given
|
126
129
|
# 2) have a product property with a value matching the one given
|
127
|
-
|
130
|
+
def self.with(value)
|
128
131
|
includes(:variants_including_master => :option_values).
|
129
132
|
includes(:product_properties).
|
130
133
|
where("#{OptionValue.table_name}.name = ? OR #{ProductProperty.table_name}.value = ?", value, value)
|
131
134
|
end
|
132
135
|
|
133
136
|
# Finds all products that have a name containing the given words.
|
134
|
-
|
137
|
+
def self.in_name(words)
|
135
138
|
like_any([:name], prepare_words(words))
|
136
139
|
end
|
137
140
|
|
138
141
|
# Finds all products that have a name or meta_keywords containing the given words.
|
139
|
-
|
142
|
+
def self.in_name_or_keywords(words)
|
140
143
|
like_any([:name, :meta_keywords], prepare_words(words))
|
141
144
|
end
|
142
145
|
|
143
146
|
# Finds all products that have a name, description, meta_description or meta_keywords containing the given keywords.
|
144
|
-
|
147
|
+
def self.in_name_or_description(words)
|
145
148
|
like_any([:name, :description, :meta_description, :meta_keywords], prepare_words(words))
|
146
149
|
end
|
147
150
|
|
148
151
|
# Finds all products that have the ids matching the given collection of ids.
|
149
152
|
# Alternatively, you could use find(collection_of_ids), but that would raise an exception if one product couldn't be found
|
150
|
-
|
153
|
+
def self.with_ids(*ids)
|
151
154
|
where(:id => ids)
|
152
155
|
end
|
153
156
|
|
@@ -159,7 +162,7 @@ module Spree
|
|
159
162
|
#
|
160
163
|
# :joins => "LEFT OUTER JOIN (SELECT line_items.variant_id as vid, COUNT(*) as cnt FROM line_items GROUP BY line_items.variant_id) AS popularity_count ON variants.id = vid",
|
161
164
|
# :order => 'COALESCE(cnt, 0) DESC'
|
162
|
-
|
165
|
+
def self.descend_by_popularity
|
163
166
|
joins(:master).
|
164
167
|
order(%Q{
|
165
168
|
COALESCE((
|
@@ -177,26 +180,25 @@ module Spree
|
|
177
180
|
})
|
178
181
|
end
|
179
182
|
|
180
|
-
|
183
|
+
def self.not_deleted
|
181
184
|
where(:deleted_at => nil)
|
182
185
|
end
|
183
186
|
|
184
|
-
# Can't use add_search_scope for this as it needs a default argument
|
185
187
|
def self.available(available_on = nil)
|
186
188
|
where('available_on <= ?', available_on || Time.now)
|
187
189
|
end
|
188
|
-
search_scopes << :available
|
189
190
|
|
190
|
-
|
191
|
+
#RAILS 3 TODO - this scope doesn't match the original 2.3.x version, needs attention (but it works)
|
192
|
+
def self.active
|
191
193
|
not_deleted.available
|
192
194
|
end
|
193
195
|
|
194
|
-
|
196
|
+
def self.on_hand
|
195
197
|
variants_table = Variant.table_name
|
196
|
-
where("#{table_name}.id in (select product_id from #{variants_table} where product_id = #{table_name}.id
|
198
|
+
where("#{table_name}.id in (select product_id from #{variants_table} where product_id = #{table_name}.id group by product_id having sum(count_on_hand) > 0)")
|
197
199
|
end
|
198
200
|
|
199
|
-
|
201
|
+
def self.taxons_name_eq(name)
|
200
202
|
joins(:taxons).where(Taxon.arel_table[:name].eq(name))
|
201
203
|
end
|
202
204
|
|
@@ -207,8 +209,6 @@ module Spree
|
|
207
209
|
else
|
208
210
|
scope :group_by_products_id, { :group => "#{self.quoted_table_name}.id" }
|
209
211
|
end
|
210
|
-
search_methods :group_by_products_id
|
211
|
-
search_scopes << :group_by_products_id
|
212
212
|
|
213
213
|
private
|
214
214
|
def self.variant_table_name
|
data/app/models/spree/product.rb
CHANGED
@@ -23,26 +23,24 @@ module Spree
|
|
23
23
|
has_many :option_types, :through => :product_option_types
|
24
24
|
has_many :product_properties, :dependent => :destroy
|
25
25
|
has_many :properties, :through => :product_properties
|
26
|
-
has_many :images, :as => :viewable, :order => :position, :dependent => :destroy
|
27
|
-
has_and_belongs_to_many :product_groups, :join_table => 'spree_product_groups_products'
|
28
26
|
belongs_to :tax_category
|
29
27
|
has_and_belongs_to_many :taxons, :join_table => 'spree_products_taxons'
|
30
28
|
belongs_to :shipping_category
|
31
29
|
|
32
30
|
has_one :master,
|
33
|
-
:class_name =>
|
34
|
-
:conditions => {
|
31
|
+
:class_name => 'Spree::Variant',
|
32
|
+
:conditions => ["#{Variant.quoted_table_name}.is_master = ? AND #{Variant.quoted_table_name}.deleted_at IS NULL", true]
|
35
33
|
|
36
34
|
delegate_belongs_to :master, :sku, :price, :weight, :height, :width, :depth, :is_master
|
37
35
|
delegate_belongs_to :master, :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price')
|
38
36
|
|
39
37
|
after_create :set_master_variant_defaults
|
40
38
|
after_create :add_properties_and_option_types_from_prototype
|
39
|
+
after_create :build_variants_from_option_values_hash, :if => :option_values_hash
|
41
40
|
before_save :recalculate_count_on_hand
|
42
|
-
after_save :update_memberships if ProductGroup.table_exists?
|
43
41
|
after_save :save_master
|
44
42
|
after_save :set_master_on_hand_to_zero_when_product_has_variants
|
45
|
-
|
43
|
+
|
46
44
|
has_many :variants,
|
47
45
|
:class_name => 'Spree::Variant',
|
48
46
|
:conditions => ["#{::Spree::Variant.quoted_table_name}.is_master = ? AND #{::Spree::Variant.quoted_table_name}.deleted_at IS NULL", false],
|
@@ -58,23 +56,29 @@ module Spree
|
|
58
56
|
:conditions => ["#{::Spree::Variant.quoted_table_name}.deleted_at IS NULL AND #{::Spree::Variant.quoted_table_name}.is_master = ?", true],
|
59
57
|
:dependent => :destroy
|
60
58
|
|
59
|
+
accepts_nested_attributes_for :variants, :allow_destroy => true
|
60
|
+
|
61
61
|
def variant_images
|
62
62
|
Image.find_by_sql("SELECT #{Asset.quoted_table_name}.* FROM #{Asset.quoted_table_name} LEFT JOIN #{Variant.quoted_table_name} ON (#{Variant.quoted_table_name}.id = #{Asset.quoted_table_name}.viewable_id) WHERE (#{Variant.quoted_table_name}.product_id = #{self.id})")
|
63
63
|
end
|
64
64
|
|
65
|
+
alias_method :images, :variant_images
|
66
|
+
|
65
67
|
validates :name, :price, :permalink, :presence => true
|
66
68
|
|
69
|
+
attr_accessor :option_values_hash
|
70
|
+
|
67
71
|
attr_accessible :name, :description, :available_on, :permalink, :meta_description,
|
68
72
|
:meta_keywords, :price, :sku, :deleted_at, :prototype_id,
|
69
73
|
:option_values_hash, :on_hand, :weight, :height, :width, :depth,
|
70
|
-
:shipping_category_id, :tax_category_id
|
71
|
-
:variants_attributes, :taxon_ids
|
74
|
+
:shipping_category_id, :tax_category_id
|
72
75
|
|
73
76
|
attr_accessible :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price')
|
74
77
|
|
78
|
+
|
75
79
|
accepts_nested_attributes_for :product_properties, :allow_destroy => true, :reject_if => lambda { |pp| pp[:property_name].blank? }
|
76
80
|
|
77
|
-
make_permalink
|
81
|
+
make_permalink :order => :name
|
78
82
|
|
79
83
|
alias :options :product_option_types
|
80
84
|
|
@@ -124,12 +128,12 @@ module Spree
|
|
124
128
|
@prototype_id = value.to_i
|
125
129
|
end
|
126
130
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
self.
|
131
|
+
# Ensures option_types and product_option_types exist for keys in option_values_hash
|
132
|
+
def ensure_option_types_exist_for_values_hash
|
133
|
+
return if option_values_hash.nil?
|
134
|
+
option_values_hash.keys.map(&:to_i).each do |id|
|
135
|
+
self.option_type_ids << id unless self.option_type_ids.include?(id)
|
136
|
+
self.product_option_types.create({:option_type_id => id}, :without_protection => true) unless product_option_types.map(&:option_type_id).include?(id)
|
133
137
|
end
|
134
138
|
end
|
135
139
|
|
@@ -145,20 +149,16 @@ module Spree
|
|
145
149
|
p.product_properties = self.product_properties.map { |q| r = q.dup; r.created_at = r.updated_at = nil; r }
|
146
150
|
|
147
151
|
image_dup = lambda { |i| j = i.dup; j.attachment = i.attachment.clone; j }
|
148
|
-
p.images = self.images.map { |i| image_dup.call i }
|
149
152
|
|
150
|
-
master = Spree::Variant.find_by_product_id_and_is_master(self.id, true)
|
151
153
|
variant = master.dup
|
152
154
|
variant.sku = 'COPY OF ' + master.sku
|
153
155
|
variant.deleted_at = nil
|
154
156
|
variant.images = master.images.map { |i| image_dup.call i }
|
155
157
|
p.master = variant
|
156
158
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
else
|
161
|
-
end
|
159
|
+
# don't dup the actual variants, just the characterising types
|
160
|
+
p.option_types = self.option_types if self.has_variants?
|
161
|
+
|
162
162
|
# allow site to do some customization
|
163
163
|
p.send(:duplicate_extra, self) if p.respond_to?(:duplicate_extra)
|
164
164
|
p.save!
|
@@ -199,6 +199,28 @@ module Spree
|
|
199
199
|
end
|
200
200
|
|
201
201
|
private
|
202
|
+
|
203
|
+
# Builds variants from a hash of option types & values
|
204
|
+
def build_variants_from_option_values_hash
|
205
|
+
ensure_option_types_exist_for_values_hash
|
206
|
+
values = option_values_hash.values
|
207
|
+
values = values.inject(values.shift) { |memo, value| memo.product(value).map(&:flatten) }
|
208
|
+
|
209
|
+
values.each do |ids|
|
210
|
+
variant = self.variants.create({:option_value_ids => ids, :price => self.master.price}, :without_protection => true)
|
211
|
+
end
|
212
|
+
save
|
213
|
+
end
|
214
|
+
|
215
|
+
def add_properties_and_option_types_from_prototype
|
216
|
+
if prototype_id && prototype = Spree::Prototype.find_by_id(prototype_id)
|
217
|
+
prototype.properties.each do |property|
|
218
|
+
product_properties.create({:property => property}, :without_protection => true)
|
219
|
+
end
|
220
|
+
self.option_types = prototype.option_types
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
202
224
|
def recalculate_count_on_hand
|
203
225
|
product_count_on_hand = has_variants? ?
|
204
226
|
variants.sum(:count_on_hand) : (master ? master.count_on_hand : 0)
|
@@ -221,10 +243,6 @@ module Spree
|
|
221
243
|
def save_master
|
222
244
|
master.save if master && (master.changed? || master.new_record?)
|
223
245
|
end
|
224
|
-
|
225
|
-
def update_memberships
|
226
|
-
self.product_groups = ProductGroup.all.select { |pg| pg.include?(self) }
|
227
|
-
end
|
228
246
|
end
|
229
247
|
end
|
230
248
|
|
@@ -5,8 +5,6 @@ module Spree
|
|
5
5
|
has_many :product_properties, :dependent => :destroy
|
6
6
|
has_many :products, :through => :product_properties
|
7
7
|
|
8
|
-
after_destroy :recalculate_product_group_products
|
9
|
-
|
10
8
|
attr_accessible :name, :presentation
|
11
9
|
|
12
10
|
validates :name, :presentation, :presence => true
|
@@ -20,19 +18,5 @@ module Spree
|
|
20
18
|
end
|
21
19
|
joins("LEFT JOIN properties_prototypes ON property_id = #{self.table_name}.id").where(:prototype_id => id)
|
22
20
|
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
# A fix for #774
|
27
|
-
# What was happening was that when a property was deleted, any product group
|
28
|
-
# that used the property to calculate included properties was not recalculated
|
29
|
-
#
|
30
|
-
# Recalculates product group products after the property has been deleted
|
31
|
-
def recalculate_product_group_products
|
32
|
-
ProductScope.where(:name => "with_property", :arguments => [self.name].to_yaml).each do |scope|
|
33
|
-
# Triggers ProductGroup#update_memberships callback to recalculate products
|
34
|
-
scope.product_group.save!
|
35
|
-
end
|
36
|
-
end
|
37
21
|
end
|
38
22
|
end
|