spree_core 1.0.7 → 1.1.0.rc1
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.
- 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
|