spree_core 0.40.4 → 0.50.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -1
- data/app/controllers/admin/base_controller.rb +0 -57
- data/app/controllers/admin/configurations_controller.rb +1 -1
- data/app/controllers/admin/general_settings_controller.rb +1 -1
- data/app/controllers/admin/images_controller.rb +18 -8
- data/app/controllers/admin/inventory_settings_controller.rb +1 -1
- data/app/controllers/admin/mail_settings_controller.rb +1 -1
- data/app/controllers/admin/option_types_controller.rb +11 -0
- data/app/controllers/admin/orders_controller.rb +16 -20
- data/app/controllers/admin/overview_controller.rb +1 -1
- data/app/controllers/admin/payment_methods_controller.rb +6 -6
- data/app/controllers/admin/product_groups_controller.rb +10 -11
- data/app/controllers/admin/product_properties_controller.rb +5 -5
- data/app/controllers/admin/product_scopes_controller.rb +12 -4
- data/app/controllers/admin/products_controller.rb +17 -12
- data/app/controllers/admin/properties_controller.rb +7 -7
- data/app/controllers/admin/prototypes_controller.rb +15 -15
- data/app/controllers/admin/reports_controller.rb +12 -11
- data/app/controllers/admin/shipments_controller.rb +0 -5
- data/app/controllers/admin/shipping_categories_controller.rb +6 -6
- data/app/controllers/admin/shipping_methods_controller.rb +1 -2
- data/app/controllers/admin/states_controller.rb +12 -12
- data/app/controllers/admin/tax_categories_controller.rb +2 -2
- data/app/controllers/admin/tax_rates_controller.rb +8 -8
- data/app/controllers/admin/tax_settings_controller.rb +1 -1
- data/app/controllers/admin/taxonomies_controller.rb +3 -3
- data/app/controllers/admin/taxons_controller.rb +1 -8
- data/app/controllers/admin/trackers_controller.rb +1 -1
- data/app/controllers/admin/users_controller.rb +6 -9
- data/app/controllers/admin/variants_controller.rb +13 -2
- data/app/controllers/admin/zones_controller.rb +9 -9
- data/app/controllers/checkout_controller.rb +5 -5
- data/app/controllers/products_controller.rb +1 -1
- data/app/controllers/taxons_controller.rb +3 -1
- data/app/helpers/admin/navigation_helper.rb +36 -19
- data/app/helpers/admin/payments_helper.rb +1 -1
- data/app/helpers/admin/product_groups_helper.rb +1 -1
- data/app/helpers/admin/product_properties_helper.rb +5 -5
- data/app/helpers/admin/products_helper.rb +2 -2
- data/app/helpers/admin/users_helper.rb +5 -2
- data/app/helpers/hook_helper.rb +3 -3
- data/app/helpers/products_helper.rb +0 -19
- data/app/helpers/spree/base_helper.rb +33 -2
- data/app/helpers/taxons_helper.rb +3 -3
- data/app/mailers/order_mailer.rb +2 -2
- data/app/mailers/shipment_mailer.rb +2 -2
- data/app/models/address.rb +42 -9
- data/app/models/adjustment.rb +8 -8
- data/app/models/app_configuration.rb +0 -6
- data/app/models/billing_integration.rb +1 -1
- data/app/models/calculator/sales_tax.rb +3 -3
- data/app/models/configuration.rb +1 -1
- data/app/models/country.rb +4 -5
- data/app/models/creditcard.rb +38 -31
- data/app/models/gateway.rb +14 -14
- data/app/models/gateway/beanstream.rb +4 -4
- data/app/models/gateway/bogus.rb +6 -6
- data/app/models/gateway/braintree.rb +88 -0
- data/app/models/gateway/eway.rb +3 -3
- data/app/models/image.rb +5 -4
- data/app/models/option_type.rb +1 -0
- data/app/models/order.rb +60 -21
- data/app/models/payment.rb +5 -32
- data/app/models/preference.rb +7 -7
- data/app/models/product.rb +22 -7
- data/app/models/product_group.rb +22 -26
- data/app/models/product_property.rb +5 -5
- data/app/models/product_scope.rb +26 -6
- data/app/models/property.rb +1 -1
- data/app/models/state.rb +2 -3
- data/app/models/tax_category.rb +1 -0
- data/app/models/tax_rate.rb +1 -2
- data/app/models/taxon.rb +12 -10
- data/app/models/taxonomy.rb +7 -4
- data/app/models/tracker.rb +1 -1
- data/app/models/user.rb +4 -0
- data/app/models/variant.rb +1 -1
- data/app/models/zone.rb +1 -1
- data/app/models/zone_member.rb +3 -3
- data/app/views/admin/{shared → adjustments}/_adjustments_table.html.erb +7 -4
- data/app/views/admin/adjustments/edit.html.erb +1 -1
- data/app/views/admin/adjustments/index.html.erb +2 -2
- data/app/views/admin/adjustments/new.html.erb +2 -1
- data/app/views/admin/general_settings/edit.html.erb +4 -12
- data/app/views/admin/general_settings/show.html.erb +0 -5
- data/app/views/admin/images/index.html.erb +8 -5
- data/app/views/admin/inventory_settings/show.html.erb +1 -1
- data/app/views/admin/mail_methods/index.html.erb +4 -4
- data/app/views/admin/option_types/_form.html.erb +4 -4
- data/app/views/admin/option_types/_option_value_fields.html.erb +2 -2
- data/app/views/admin/option_types/edit.html.erb +4 -2
- data/app/views/admin/option_types/index.html.erb +5 -5
- data/app/views/admin/orders/_line_item.html.erb +2 -1
- data/app/views/admin/orders/history.html.erb +6 -2
- data/app/views/admin/orders/index.html.erb +22 -19
- data/app/views/admin/orders/show.html.erb +1 -1
- data/app/views/admin/orders/user.html.erb +1 -1
- data/app/views/admin/payment_methods/index.html.erb +7 -5
- data/app/views/admin/payments/_list.html.erb +3 -3
- data/app/views/admin/payments/index.html.erb +1 -1
- data/app/views/admin/payments/show.html.erb +2 -2
- data/app/views/admin/product_groups/edit.html.erb +7 -7
- data/app/views/admin/product_groups/index.html.erb +5 -3
- data/app/views/admin/product_groups/update.js.erb +4 -3
- data/app/views/admin/product_properties/_product_property_fields.html.erb +3 -3
- data/app/views/admin/product_properties/index.html.erb +10 -5
- data/app/views/admin/product_scopes/destroy.js.erb +1 -0
- data/app/views/admin/products/index.html.erb +32 -33
- data/app/views/admin/properties/_form.html.erb +2 -2
- data/app/views/admin/properties/index.html.erb +4 -4
- data/app/views/admin/prototypes/index.html.erb +4 -4
- data/app/views/admin/shared/_address_form.html.erb +1 -1
- data/app/views/admin/shared/_calculator_fields.html.erb +1 -1
- data/app/views/admin/shared/_destroy.js.erb +15 -2
- data/app/views/admin/shared/_order_tabs.html.erb +1 -1
- data/app/views/admin/shared/_report_criteria.html.erb +1 -1
- data/app/views/admin/shipments/_form.html.erb +6 -2
- data/app/views/admin/shipments/edit.html.erb +1 -1
- data/app/views/admin/shipments/index.html.erb +4 -2
- data/app/views/admin/shipping_methods/_form.html.erb +2 -0
- data/app/views/admin/shipping_methods/index.html.erb +3 -2
- data/app/views/admin/states/_state_list.html.erb +11 -5
- data/app/views/admin/tax_categories/index.html.erb +9 -4
- data/app/views/admin/tax_settings/show.html.erb +2 -2
- data/app/views/admin/taxonomies/_list.html.erb +4 -2
- data/app/views/admin/taxonomies/index.html.erb +2 -2
- data/app/views/admin/taxons/_form.html.erb +1 -1
- data/app/views/admin/trackers/index.html.erb +5 -5
- data/app/views/admin/users/_form.html.erb +3 -4
- data/app/views/admin/users/index.html.erb +7 -6
- data/app/views/admin/users/show.html.erb +3 -3
- data/app/views/admin/variants/index.html.erb +21 -6
- data/app/views/admin/zones/_form.html.erb +9 -9
- data/app/views/admin/zones/_member_type.html.erb +5 -5
- data/app/views/admin/zones/index.html.erb +7 -5
- data/app/views/checkout/_address.html.erb +2 -2
- data/app/views/checkout/_payment.html.erb +3 -6
- data/app/views/layouts/admin.html.erb +3 -9
- data/app/views/layouts/spree_application.html.erb +2 -1
- data/app/views/orders/_line_item.html.erb +1 -1
- data/app/views/orders/edit.html.erb +17 -16
- data/app/views/orders/show.html.erb +1 -1
- data/app/views/shared/_admin_head.html.erb +1 -1
- data/app/views/shared/_error_messages.html.erb +2 -2
- data/app/views/shared/_filters.html.erb +4 -4
- data/app/views/shared/_head.html.erb +2 -2
- data/app/views/shared/_nav_bar.html.erb +2 -2
- data/app/views/shared/_products.html.erb +4 -2
- data/app/views/shared/_taxonomies.html.erb +15 -8
- data/app/views/shipment_mailer/shipped_email.text.erb +2 -2
- data/config/cucumber.yml +10 -0
- data/config/initializers/form_builder.rb +1 -5
- data/config/initializers/workarounds_for_ruby19.rb +5 -5
- data/config/locales/en.yml +33 -6
- data/config/routes.rb +18 -13
- data/db/migrate/20090923100315_add_count_on_hand_to_variants_and_products.rb +5 -5
- data/db/migrate/20091213222815_creditcard_last_four_digits.rb +5 -5
- data/db/migrate/20100105132138_shipment_id_for_inventory_units.rb +2 -2
- data/db/migrate/20100209025806_create_payment_methods.rb +3 -3
- data/db/migrate/20100209144531_polymorphic_payments.rb +1 -1
- data/db/migrate/20100214212536_assign_creditcard_txns_to_payment.rb +2 -2
- data/db/migrate/20100224153127_deleted_at_for_payment_methods.rb +1 -1
- data/db/migrate/20100506185838_add_description_to_taxons.rb +1 -1
- data/db/migrate/20100816212146_shipping_method_id_for_orders.rb +1 -1
- data/db/migrate/20101026184808_migrate_checkout_to_orders.rb +2 -2
- data/db/migrate/20101223215658_add_position_to_variants.rb +9 -0
- data/db/migrate/20110110130847_add_next_state_to_state_events.rb +9 -0
- data/db/migrate/20110111122537_add_position_to_option_types.rb +9 -0
- data/db/migrate/20110314192118_remove_trailing_slashes_in_taxon_permalinks.rb +17 -0
- data/lib/custom_fixtures.rb +1 -1
- data/lib/{seo_assist.rb → middleware/seo_assist.rb} +14 -8
- data/lib/product_filters.rb +49 -43
- data/lib/redirect_legacy_product_url.rb +5 -5
- data/lib/scopes.rb +2 -2
- data/lib/scopes/dynamic.rb +9 -16
- data/lib/scopes/product.rb +33 -16
- data/lib/scopes/variant.rb +4 -3
- data/lib/spree/calculated_adjustments.rb +5 -2
- data/lib/spree/config.rb +2 -0
- data/lib/spree/current_order.rb +4 -4
- data/lib/spree/mail_settings.rb +3 -2
- data/lib/spree/search/base.rb +9 -10
- data/lib/spree_base.rb +22 -23
- data/lib/spree_core.rb +10 -69
- data/lib/spree_core/authorize_net_cim_hack.rb +1 -1
- data/lib/spree_core/delegate_belongs_to.rb +18 -24
- data/lib/spree_core/enumerable_constants.rb +38 -38
- data/lib/spree_core/find_by_param.rb +8 -6
- data/lib/spree_core/preferences/preference_definition.rb +7 -7
- data/lib/spree_core/railtie.rb +58 -0
- data/lib/spree_core/ssl_requirement.rb +4 -3
- data/lib/spree_core/testing_support/factories.rb +13 -0
- data/lib/spree_core/testing_support/factories/address_factory.rb +20 -0
- data/lib/spree_core/testing_support/factories/adjustment_factory.rb +6 -0
- data/lib/spree_core/testing_support/factories/calculator_factory.rb +5 -0
- data/lib/spree_core/testing_support/factories/configuraion_factory.rb +4 -0
- data/lib/spree_core/testing_support/factories/country_factory.rb +7 -0
- data/lib/spree_core/testing_support/factories/creditcard_factory.rb +11 -0
- data/lib/spree_core/testing_support/factories/inventory_unit_factory.rb +7 -0
- data/lib/spree_core/testing_support/factories/line_item_factory.rb +8 -0
- data/lib/spree_core/testing_support/factories/mail_method_factory.rb +4 -0
- data/lib/spree_core/testing_support/factories/options_factory.rb +10 -0
- data/lib/spree_core/testing_support/factories/order_factory.rb +18 -0
- data/lib/spree_core/testing_support/factories/payment_factory.rb +26 -0
- data/lib/spree_core/testing_support/factories/payment_method_factory.rb +17 -0
- data/lib/spree_core/testing_support/factories/product_factory.rb +16 -0
- data/lib/spree_core/testing_support/factories/product_group_factory.rb +3 -0
- data/lib/spree_core/testing_support/factories/product_option_type_factory.rb +4 -0
- data/lib/spree_core/testing_support/factories/product_property_factory.rb +4 -0
- data/lib/spree_core/testing_support/factories/product_scope_factory.rb +6 -0
- data/lib/spree_core/testing_support/factories/property_factory.rb +4 -0
- data/lib/spree_core/testing_support/factories/prototype_factory.rb +4 -0
- data/lib/spree_core/testing_support/factories/return_authorization_factory.rb +8 -0
- data/lib/spree_core/testing_support/factories/role_factory.rb +9 -0
- data/lib/spree_core/testing_support/factories/shipment_factory.rb +9 -0
- data/lib/spree_core/testing_support/factories/shipping_category_factory.rb +5 -0
- data/lib/spree_core/testing_support/factories/shipping_method_factory.rb +7 -0
- data/lib/spree_core/testing_support/factories/state_factory.rb +11 -0
- data/lib/spree_core/testing_support/factories/tax_category_factory.rb +8 -0
- data/lib/spree_core/testing_support/factories/tax_rate_factory.rb +5 -0
- data/lib/spree_core/testing_support/factories/taxon_factory.rb +5 -0
- data/lib/spree_core/testing_support/factories/taxonomy_factory.rb +3 -0
- data/lib/spree_core/testing_support/factories/tracker_factory.rb +5 -0
- data/lib/spree_core/testing_support/factories/user_factory.rb +15 -0
- data/lib/spree_core/testing_support/factories/variant_factory.rb +14 -0
- data/lib/spree_core/testing_support/factories/zone_factory.rb +18 -0
- data/lib/spree_core/theme_support/hook.rb +1 -1
- data/lib/spree_core/theme_support/more_patches.rb +20 -20
- data/lib/spree_core/version.rb +5 -0
- data/lib/tasks/common.rb +30 -0
- data/lib/tasks/install.rake +1 -1
- data/lib/tasks/rake_util.rb +19 -0
- data/lib/tasks/taxon.rake +14 -0
- data/public/images/reorder.jpg +0 -0
- data/public/javascripts/admin.js +0 -6
- data/public/javascripts/admin/unobtrusive_handlers.js +28 -0
- data/public/javascripts/checkout.js +3 -3
- data/public/stylesheets/admin/admin-forms.css +1 -6
- data/public/stylesheets/admin/admin.css +0 -28
- data/public/stylesheets/screen.css +0 -280
- metadata +81 -43
- data/app/controllers/countries_controller.rb +0 -11
- data/app/models/spree/alert.rb +0 -13
- data/app/models/state_monitor.rb +0 -25
- data/app/views/admin/shared/_alert.html.erb +0 -6
- data/app/views/countries/index.js.erb +0 -1
- data/app/views/shared/_doc_and_xmlns.html.erb +0 -2
- data/app/views/users/edit.html.erb +0 -9
- data/app/views/users/show.html.erb +0 -46
- data/lib/spree_core/validation_group.rb +0 -143
- data/public/stylesheets/scaffold.css +0 -54
data/app/models/gateway/eway.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
class Gateway::Eway < Gateway
|
2
2
|
preference :login, :string
|
3
|
-
|
3
|
+
|
4
4
|
# Note: EWay supports purchase method only (no authorize method).
|
5
5
|
# Ensure Spree::Config[:auto_capture] is set to true
|
6
|
-
|
6
|
+
|
7
7
|
def provider_class
|
8
8
|
ActiveMerchant::Billing::EwayGateway
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
end
|
data/app/models/image.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Image < Asset
|
2
2
|
validate :no_attachement_errors
|
3
|
-
has_attached_file :attachment,
|
4
|
-
:styles => { :mini => '48x48>', :small => '100x100>', :product => '240x240>', :large => '600x600>' },
|
3
|
+
has_attached_file :attachment,
|
4
|
+
:styles => { :mini => '48x48>', :small => '100x100>', :product => '240x240>', :large => '600x600>' },
|
5
5
|
:default_style => :product,
|
6
6
|
:url => "/assets/products/:id/:style/:basename.:extension",
|
7
7
|
:path => ":rails_root/public/assets/products/:id/:style/:basename.:extension"
|
@@ -9,8 +9,9 @@ class Image < Asset
|
|
9
9
|
# save the w,h of the original image (from which others can be calculated)
|
10
10
|
# we need to look at the write-queue for images which have not been saved yet
|
11
11
|
after_post_process :find_dimensions
|
12
|
+
|
12
13
|
def find_dimensions
|
13
|
-
temporary = attachment.queued_for_write[:original]
|
14
|
+
temporary = attachment.queued_for_write[:original]
|
14
15
|
filename = temporary.path unless temporary.nil?
|
15
16
|
filename = attachment.path if filename.blank?
|
16
17
|
geometry = Paperclip::Geometry.from_file(filename)
|
@@ -22,7 +23,7 @@ class Image < Asset
|
|
22
23
|
def no_attachement_errors
|
23
24
|
unless attachment.errors.empty?
|
24
25
|
# uncomment this to get rid of the less-than-useful interrim messages
|
25
|
-
# errors.clear
|
26
|
+
# errors.clear
|
26
27
|
errors.add :attachment, "Paperclip returned errors for file '#{attachment_file_name}' - check ImageMagick installation or image source file."
|
27
28
|
false
|
28
29
|
end
|
data/app/models/option_type.rb
CHANGED
@@ -3,6 +3,7 @@ class OptionType < ActiveRecord::Base
|
|
3
3
|
has_many :product_option_types, :dependent => :destroy
|
4
4
|
has_and_belongs_to_many :prototypes
|
5
5
|
validates :name, :presentation, :presence => true
|
6
|
+
default_scope :order => "option_types.position"
|
6
7
|
|
7
8
|
accepts_nested_attributes_for :option_values, :reject_if => lambda { |ov| ov[:name].blank? || ov[:presentation].blank? }, :allow_destroy => true
|
8
9
|
end
|
data/app/models/order.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
class Order < ActiveRecord::Base
|
2
2
|
|
3
|
-
attr_accessible :line_items, :bill_address_attributes, :ship_address_attributes, :payments_attributes,
|
3
|
+
attr_accessible :line_items, :bill_address_attributes, :ship_address_attributes, :payments_attributes,
|
4
|
+
:ship_address, :line_items_attributes,
|
4
5
|
:shipping_method_id, :email, :use_billing, :special_instructions
|
5
6
|
|
6
7
|
belongs_to :user
|
@@ -26,6 +27,7 @@ class Order < ActiveRecord::Base
|
|
26
27
|
before_create :generate_order_number
|
27
28
|
|
28
29
|
validates_presence_of :email, :if => :require_email
|
30
|
+
validate :has_available_shipment
|
29
31
|
|
30
32
|
#delegate :ip_address, :to => :checkout
|
31
33
|
def ip_address
|
@@ -33,8 +35,8 @@ class Order < ActiveRecord::Base
|
|
33
35
|
end
|
34
36
|
|
35
37
|
scope :by_number, lambda {|number| where("orders.number = ?", number)}
|
36
|
-
scope :between, lambda {|*dates| where("orders.created_at between
|
37
|
-
scope :by_customer, lambda {|customer| where("
|
38
|
+
scope :between, lambda {|*dates| where("orders.created_at between ? and ?", dates.first.to_date, dates.last.to_date)}
|
39
|
+
scope :by_customer, lambda {|customer| joins(:user).where("users.email =?", customer)}
|
38
40
|
scope :by_state, lambda {|state| where("state = ?", state)}
|
39
41
|
scope :complete, where("orders.completed_at IS NOT NULL")
|
40
42
|
scope :incomplete, where("orders.completed_at IS NULL")
|
@@ -43,7 +45,7 @@ class Order < ActiveRecord::Base
|
|
43
45
|
|
44
46
|
attr_accessor :out_of_stock_items
|
45
47
|
|
46
|
-
|
48
|
+
class_attribute :update_hooks
|
47
49
|
self.update_hooks = Set.new
|
48
50
|
|
49
51
|
# Use this method in other gems that wish to register their own custom logic that should be called after Order#updat
|
@@ -75,12 +77,15 @@ class Order < ActiveRecord::Base
|
|
75
77
|
state_machine :initial => 'cart', :use_transactions => false do
|
76
78
|
|
77
79
|
event :next do
|
78
|
-
transition :from => 'cart',
|
79
|
-
transition :from => 'address',
|
80
|
+
transition :from => 'cart', :to => 'address'
|
81
|
+
transition :from => 'address', :to => 'delivery'
|
80
82
|
transition :from => 'delivery', :to => 'payment'
|
81
|
-
transition :from => 'confirm',
|
83
|
+
transition :from => 'confirm', :to => 'complete'
|
84
|
+
|
82
85
|
# note: some payment methods will not support a confirm step
|
83
|
-
transition :from => 'payment',
|
86
|
+
transition :from => 'payment', :to => 'confirm',
|
87
|
+
:if => Proc.new { Gateway.current && Gateway.current.payment_profiles_supported? }
|
88
|
+
|
84
89
|
transition :from => 'payment', :to => 'complete'
|
85
90
|
end
|
86
91
|
|
@@ -221,13 +226,15 @@ class Order < ActiveRecord::Base
|
|
221
226
|
current_item
|
222
227
|
end
|
223
228
|
|
229
|
+
# FIXME refactor this method and implement validation using validates_* utilities
|
224
230
|
def generate_order_number
|
225
231
|
record = true
|
226
232
|
while record
|
227
233
|
random = "R#{Array.new(9){rand(9)}.join}"
|
228
|
-
record =
|
234
|
+
record = self.class.find(:first, :conditions => ["number = ?", random])
|
229
235
|
end
|
230
|
-
self.number = random
|
236
|
+
self.number = random if self.number.blank?
|
237
|
+
self.number
|
231
238
|
end
|
232
239
|
|
233
240
|
# convenience method since many stores will not allow user to create multiple shipments
|
@@ -250,12 +257,8 @@ class Order < ActiveRecord::Base
|
|
250
257
|
# Creates a new tax charge if applicable. Uses the highest possible matching rate and destroys any previous
|
251
258
|
# tax charges if they were created by rates that no longer apply.
|
252
259
|
def create_tax_charge!
|
253
|
-
|
254
|
-
|
255
|
-
old_charge.destroy unless old_charge.originator == rate
|
256
|
-
return
|
257
|
-
end
|
258
|
-
rate.create_adjustment(I18n.t(:tax), self, self, true)
|
260
|
+
adjustments.tax.each {|e| e.destroy }
|
261
|
+
TaxRate.match(ship_address).each {|r| r.create_adjustment(I18n.t(:tax), self, self, true) }
|
259
262
|
end
|
260
263
|
|
261
264
|
# Creates a new shipment (adjustment is created by shipment model)
|
@@ -264,7 +267,9 @@ class Order < ActiveRecord::Base
|
|
264
267
|
if shipment.present?
|
265
268
|
shipment.update_attributes(:shipping_method => shipping_method)
|
266
269
|
else
|
267
|
-
self.shipments << Shipment.create(:order => self,
|
270
|
+
self.shipments << Shipment.create(:order => self,
|
271
|
+
:shipping_method => shipping_method,
|
272
|
+
:address => self.ship_address)
|
268
273
|
end
|
269
274
|
|
270
275
|
end
|
@@ -283,8 +288,9 @@ class Order < ActiveRecord::Base
|
|
283
288
|
end
|
284
289
|
|
285
290
|
def name
|
286
|
-
address = bill_address || ship_address
|
287
|
-
|
291
|
+
if (address = bill_address || ship_address)
|
292
|
+
"#{address.firstname} #{address.lastname}"
|
293
|
+
end
|
288
294
|
end
|
289
295
|
|
290
296
|
def creditcards
|
@@ -304,6 +310,13 @@ class Order < ActiveRecord::Base
|
|
304
310
|
# lock any optional adjustments (coupon promotions, etc.)
|
305
311
|
adjustments.optional.each { |adjustment| adjustment.update_attribute("locked", true) }
|
306
312
|
OrderMailer.confirm_email(self).deliver
|
313
|
+
|
314
|
+
self.state_events.create({
|
315
|
+
:previous_state => "cart",
|
316
|
+
:next_state => "complete",
|
317
|
+
:name => "order" ,
|
318
|
+
:user_id => (User.respond_to?(:current) && User.current.try(:id)) || self.user_id
|
319
|
+
})
|
307
320
|
end
|
308
321
|
|
309
322
|
|
@@ -316,12 +329,13 @@ class Order < ActiveRecord::Base
|
|
316
329
|
|
317
330
|
def rate_hash
|
318
331
|
@rate_hash ||= available_shipping_methods(:front_end).collect do |ship_method|
|
332
|
+
next unless cost = ship_method.calculator.compute(self)
|
319
333
|
{ :id => ship_method.id,
|
320
334
|
:shipping_method => ship_method,
|
321
335
|
:name => ship_method.name,
|
322
|
-
:cost =>
|
336
|
+
:cost => cost
|
323
337
|
}
|
324
|
-
end.sort_by{|r| r[:cost]}
|
338
|
+
end.compact.sort_by{|r| r[:cost]}
|
325
339
|
end
|
326
340
|
|
327
341
|
def payment
|
@@ -383,6 +397,16 @@ class Order < ActiveRecord::Base
|
|
383
397
|
"partial"
|
384
398
|
end
|
385
399
|
self.shipment_state = "backorder" if backordered?
|
400
|
+
|
401
|
+
if old_shipment_state = self.changed_attributes["shipment_state"]
|
402
|
+
self.state_events.create({
|
403
|
+
:previous_state => old_shipment_state,
|
404
|
+
:next_state => self.shipment_state,
|
405
|
+
:name => "shipment" ,
|
406
|
+
:user_id => (User.respond_to?(:current) && User.current && User.current.id) || self.user_id
|
407
|
+
})
|
408
|
+
end
|
409
|
+
|
386
410
|
end
|
387
411
|
|
388
412
|
# Updates the +payment_state+ attribute according to the following logic:
|
@@ -402,6 +426,15 @@ class Order < ActiveRecord::Base
|
|
402
426
|
else
|
403
427
|
self.payment_state = "paid"
|
404
428
|
end
|
429
|
+
|
430
|
+
if old_payment_state = self.changed_attributes["payment_state"]
|
431
|
+
self.state_events.create({
|
432
|
+
:previous_state => old_payment_state,
|
433
|
+
:next_state => self.payment_state,
|
434
|
+
:name => "payment" ,
|
435
|
+
:user_id => (User.respond_to?(:current) && User.current && User.current.id) || self.user_id
|
436
|
+
})
|
437
|
+
end
|
405
438
|
end
|
406
439
|
|
407
440
|
# Updates the following Order total values:
|
@@ -433,6 +466,12 @@ class Order < ActiveRecord::Base
|
|
433
466
|
return true unless new_record? or state == 'cart'
|
434
467
|
end
|
435
468
|
|
469
|
+
def has_available_shipment
|
470
|
+
return unless :address == state_name.to_sym
|
471
|
+
return unless ship_address && ship_address.valid?
|
472
|
+
errors.add(:base, :no_shipping_methods_available) if available_shipping_methods.empty?
|
473
|
+
end
|
474
|
+
|
436
475
|
def after_cancel
|
437
476
|
# TODO: make_shipments_pending
|
438
477
|
# TODO: restock_inventory
|
data/app/models/payment.rb
CHANGED
@@ -6,26 +6,19 @@ class Payment < ActiveRecord::Base
|
|
6
6
|
has_many :offsets, :class_name => 'Payment', :foreign_key => 'source_id', :conditions => "source_type = 'Payment' AND amount < 0 AND state = 'completed'"
|
7
7
|
has_many :log_entries, :as => :source
|
8
8
|
|
9
|
-
after_save :create_payment_profile, :if => :
|
9
|
+
after_save :create_payment_profile, :if => :profiles_supported?
|
10
10
|
|
11
11
|
# update the order totals, etc.
|
12
12
|
after_save :update_order
|
13
13
|
|
14
|
-
#after_save :check_payments
|
15
|
-
#after_destroy :check_payments
|
16
|
-
|
17
14
|
accepts_nested_attributes_for :source
|
18
15
|
|
19
|
-
#validate :amount_is_valid_for_outstanding_balance_or_credit
|
20
|
-
#validates :payment_method, :presence => true, :if => Proc.new { |payable| payable.is_a? Checkout }
|
21
|
-
|
22
16
|
scope :from_creditcard, where(:source_type => 'Creditcard')
|
23
17
|
scope :with_state, lambda {|s| where(:state => s)}
|
24
18
|
scope :completed, with_state('completed')
|
25
19
|
scope :pending, with_state('pending')
|
26
20
|
scope :failed, with_state('failed')
|
27
21
|
|
28
|
-
|
29
22
|
# order state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
|
30
23
|
state_machine :initial => 'checkout' do
|
31
24
|
# With card payments, happens before purchase or authorization happens
|
@@ -93,26 +86,6 @@ class Payment < ActiveRecord::Base
|
|
93
86
|
|
94
87
|
private
|
95
88
|
|
96
|
-
# def check_payments
|
97
|
-
# return unless order and order.complete?
|
98
|
-
# #sorting by created_at.to_f to ensure millisecond percsision, plus ID - just in case
|
99
|
-
# events = order.state_events.sort_by { |e| [e.created_at.to_f, e.id] }.reverse
|
100
|
-
# # TODO: think the below implementation will need replacing
|
101
|
-
# # if order.returnable_units.nil? && order.return_authorizations.size >0
|
102
|
-
# # order.return!
|
103
|
-
# # elsif events.present? and %w(over_paid under_paid).include?(events.first.name)
|
104
|
-
# # events.each do |event|
|
105
|
-
# # if %w(shipped paid new).include?(event.previous_state)
|
106
|
-
# # order.pay!
|
107
|
-
# # order.update_attribute("state", event.previous_state) if %w(shipped returned).include?(event.previous_state)
|
108
|
-
# # return
|
109
|
-
# # end
|
110
|
-
# # end
|
111
|
-
# # elsif order.payment_total >= order.total
|
112
|
-
# # order.pay!
|
113
|
-
# # end
|
114
|
-
# end
|
115
|
-
|
116
89
|
def amount_is_valid_for_outstanding_balance_or_credit
|
117
90
|
return unless order
|
118
91
|
if amount != order.outstanding_balance
|
@@ -120,13 +93,13 @@ class Payment < ActiveRecord::Base
|
|
120
93
|
end
|
121
94
|
end
|
122
95
|
|
123
|
-
def
|
124
|
-
|
96
|
+
def profiles_supported?
|
97
|
+
payment_method.respond_to?(:payment_profiles_supported?) && payment_method.payment_profiles_supported?
|
125
98
|
end
|
126
99
|
|
127
100
|
def create_payment_profile
|
128
|
-
return unless
|
129
|
-
|
101
|
+
return unless source.is_a?(Creditcard) && source.number && !source.has_payment_profile?
|
102
|
+
payment_method.create_profile(self)
|
130
103
|
rescue ActiveMerchant::ConnectionError => e
|
131
104
|
gateway_error I18n.t(:unable_to_connect_to_gateway)
|
132
105
|
end
|
data/app/models/preference.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Represents a preferred value for a particular preference on a model.
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# == Targeted preferences
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# In addition to simple named preferences, preferences can also be targeted for
|
6
6
|
# a particular record. For example, a User may have a preferred color for a
|
7
7
|
# particular Car. In this case, the +owner+ is the User, the +preference+ is
|
@@ -10,10 +10,10 @@
|
|
10
10
|
class Preference < ActiveRecord::Base
|
11
11
|
belongs_to :owner, :polymorphic => true
|
12
12
|
belongs_to :group, :polymorphic => true
|
13
|
-
|
13
|
+
|
14
14
|
validates :name, :owner_id, :owner_type, :presence => true
|
15
15
|
validates :group_type, :presence => true, :if => :group_id?
|
16
|
-
|
16
|
+
|
17
17
|
class << self
|
18
18
|
# Splits the given group into its corresponding id and type
|
19
19
|
def split_group(group = nil)
|
@@ -25,19 +25,19 @@ class Preference < ActiveRecord::Base
|
|
25
25
|
return group_id, group_type
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
# The definition for the attribute
|
30
30
|
def definition
|
31
31
|
owner.preference_definitions[name] unless owner_type.blank?
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
# Typecasts the value depending on the preference definition's declared type
|
35
35
|
def value
|
36
36
|
value = read_attribute(:value)
|
37
37
|
value = definition.type_cast(value) if definition
|
38
38
|
value
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Only searches for the group record if the group id is specified
|
42
42
|
def group_with_optional_lookup
|
43
43
|
group_id ? group_without_optional_lookup : group_type
|
data/app/models/product.rb
CHANGED
@@ -43,7 +43,8 @@ class Product < ActiveRecord::Base
|
|
43
43
|
after_save :save_master
|
44
44
|
|
45
45
|
has_many :variants,
|
46
|
-
:conditions => ["variants.is_master = ? AND variants.deleted_at IS NULL", false]
|
46
|
+
:conditions => ["variants.is_master = ? AND variants.deleted_at IS NULL", false],
|
47
|
+
:order => 'variants.position ASC'
|
47
48
|
|
48
49
|
|
49
50
|
has_many :variants_including_master,
|
@@ -51,9 +52,15 @@ class Product < ActiveRecord::Base
|
|
51
52
|
:conditions => ["variants.deleted_at IS NULL"],
|
52
53
|
:dependent => :destroy
|
53
54
|
|
55
|
+
has_many :variants_with_only_master,
|
56
|
+
:class_name => 'Variant',
|
57
|
+
:conditions => ["variants.deleted_at IS NULL AND variants.is_master = ?", true],
|
58
|
+
:dependent => :destroy
|
59
|
+
|
60
|
+
|
54
61
|
validates :name, :price, :permalink, :presence => true
|
55
62
|
|
56
|
-
accepts_nested_attributes_for :product_properties, :allow_destroy => true, :reject_if => lambda { |pp| pp[:property_name].blank? }
|
63
|
+
accepts_nested_attributes_for :product_properties, :allow_destroy => true, :reject_if => lambda { |pp| pp[:property_name].blank? }
|
57
64
|
|
58
65
|
make_permalink
|
59
66
|
|
@@ -64,18 +71,26 @@ class Product < ActiveRecord::Base
|
|
64
71
|
#RAILS3 TODO - scopes are duplicated here and in scopres/product.rb - can we DRY it up?
|
65
72
|
# default product scope only lists available and non-deleted products
|
66
73
|
scope :not_deleted, where("products.deleted_at is NULL")
|
74
|
+
|
67
75
|
scope :available, lambda { |*on| where("products.available_on <= ?", on.first || Time.zone.now ) }
|
68
|
-
scope :active, not_deleted.available #RAILS 3 TODO - this scope doesn't match the original 2.3.x version, needs attention (but it works)
|
69
|
-
scope :on_hand, where("products.count_on_hand > 0")
|
70
76
|
|
77
|
+
#RAILS 3 TODO - this scope doesn't match the original 2.3.x version, needs attention (but it works)
|
78
|
+
scope :active, lambda{ not_deleted.available }
|
71
79
|
|
80
|
+
scope :on_hand, where("products.count_on_hand > 0")
|
72
81
|
|
73
82
|
if (ActiveRecord::Base.connection.adapter_name == 'PostgreSQL')
|
74
|
-
|
83
|
+
if ActiveRecord::Base.connection.tables.include?("products")
|
84
|
+
scope :group_by_products_id, { :group => "products." + Product.column_names.join(", products.") }
|
85
|
+
end
|
75
86
|
else
|
76
87
|
scope :group_by_products_id, { :group => "products.id" }
|
77
88
|
end
|
89
|
+
search_methods :group_by_products_id
|
90
|
+
|
91
|
+
scope :id_equals, lambda { |input_id| where("products.id = ?", input_id) }
|
78
92
|
|
93
|
+
scope :taxons_name_eq, lambda { |name| joins(:taxons).where("taxons.name = ?", name) }
|
79
94
|
|
80
95
|
# ----------------------------------------------------------------------------------------------------------
|
81
96
|
#
|
@@ -108,7 +123,7 @@ class Product < ActiveRecord::Base
|
|
108
123
|
# ----------------------------------------------------------------------------------------------------------
|
109
124
|
|
110
125
|
def to_param
|
111
|
-
return permalink
|
126
|
+
return permalink if permalink.present?
|
112
127
|
name.to_url
|
113
128
|
end
|
114
129
|
|
@@ -148,7 +163,7 @@ class Product < ActiveRecord::Base
|
|
148
163
|
end
|
149
164
|
|
150
165
|
def add_properties_and_option_types_from_prototype
|
151
|
-
if prototype_id
|
166
|
+
if prototype_id && prototype = Prototype.find_by_id(prototype_id)
|
152
167
|
prototype.properties.each do |property|
|
153
168
|
product_properties.create(:property => property)
|
154
169
|
end
|
data/app/models/product_group.rb
CHANGED
@@ -24,10 +24,10 @@
|
|
24
24
|
# without retriving all records.
|
25
25
|
#
|
26
26
|
# ProductGroup operates on named scopes defined for product in Scopes::Product,
|
27
|
-
# or generated automatically by
|
27
|
+
# or generated automatically by meta_search
|
28
28
|
#
|
29
29
|
class ProductGroup < ActiveRecord::Base
|
30
|
-
validates :name, :presence => true
|
30
|
+
validates :name, :presence => true # TODO ensure that this field is defined as not_null
|
31
31
|
validates_associated :product_scopes
|
32
32
|
|
33
33
|
before_save :set_permalink
|
@@ -36,7 +36,7 @@ class ProductGroup < ActiveRecord::Base
|
|
36
36
|
has_and_belongs_to_many :cached_products, :class_name => "Product"
|
37
37
|
# name
|
38
38
|
has_many :product_scopes
|
39
|
-
accepts_nested_attributes_for :product_scopes
|
39
|
+
accepts_nested_attributes_for :product_scopes
|
40
40
|
|
41
41
|
# Testing utility: creates new *ProductGroup* from search permalink url.
|
42
42
|
# Follows conventions for accessing PGs from URLs, as decoded in routes
|
@@ -58,7 +58,7 @@ class ProductGroup < ActiveRecord::Base
|
|
58
58
|
end
|
59
59
|
taxon = taxons && taxons.split("/").last
|
60
60
|
pg.add_scope("in_taxon", taxon) if taxon
|
61
|
-
|
61
|
+
|
62
62
|
pg
|
63
63
|
end
|
64
64
|
|
@@ -75,7 +75,7 @@ class ProductGroup < ActiveRecord::Base
|
|
75
75
|
def from_route(attrs)
|
76
76
|
self.order_scope = attrs.pop if attrs.length % 2 == 1
|
77
77
|
attrs.each_slice(2) do |scope|
|
78
|
-
next unless Product.
|
78
|
+
next unless Product.respond_to?(scope.first)
|
79
79
|
add_scope(scope.first, scope.last.split(","))
|
80
80
|
end
|
81
81
|
self
|
@@ -85,19 +85,15 @@ class ProductGroup < ActiveRecord::Base
|
|
85
85
|
search_hash.each_pair do |scope_name, scope_attribute|
|
86
86
|
add_scope(scope_name, scope_attribute)
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
self
|
90
90
|
end
|
91
91
|
|
92
92
|
def add_scope(scope_name, arguments=[])
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
})
|
98
|
-
else
|
99
|
-
raise ArgumentError.new("'#{scope_name}` can't be used as scope")
|
100
|
-
end
|
93
|
+
self.product_scopes << ProductScope.new({
|
94
|
+
:name => scope_name.to_s,
|
95
|
+
:arguments => [*arguments]
|
96
|
+
})
|
101
97
|
self
|
102
98
|
end
|
103
99
|
|
@@ -106,15 +102,14 @@ class ProductGroup < ActiveRecord::Base
|
|
106
102
|
# from first nested_scope so we have to apply ordering FIRST.
|
107
103
|
# see #2253 on rails LH
|
108
104
|
base_product_scope = scopish
|
109
|
-
if use_order && !self.order_scope.blank? && Product.
|
105
|
+
if use_order && !self.order_scope.blank? && Product.respond_to?(self.order_scope.intern)
|
110
106
|
base_product_scope = base_product_scope.send(self.order_scope)
|
111
107
|
end
|
112
108
|
|
113
|
-
return self.product_scopes.reject {|s|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
}
|
109
|
+
return self.product_scopes.reject {|s| s.is_ordering? }.inject(base_product_scope) do |result, scope|
|
110
|
+
scope.apply_on(result)
|
111
|
+
end
|
112
|
+
|
118
113
|
end
|
119
114
|
|
120
115
|
# returns chain of named scopes generated from order scope and product scopes.
|
@@ -131,9 +126,9 @@ class ProductGroup < ActiveRecord::Base
|
|
131
126
|
elsif !use_order
|
132
127
|
cached_group
|
133
128
|
else
|
134
|
-
product_scopes.select {|s|
|
129
|
+
product_scopes.select {|s|
|
135
130
|
s.is_ordering?
|
136
|
-
}.inject(cached_group) {|res,order|
|
131
|
+
}.inject(cached_group) {|res,order|
|
137
132
|
order.apply_on(res)
|
138
133
|
}
|
139
134
|
end
|
@@ -160,7 +155,7 @@ class ProductGroup < ActiveRecord::Base
|
|
160
155
|
[ps.name, ps.arguments.join(",")]
|
161
156
|
}.flatten.join('/')
|
162
157
|
result+= self.order_scope if self.order_scope
|
163
|
-
|
158
|
+
|
164
159
|
result
|
165
160
|
else
|
166
161
|
name.to_url
|
@@ -170,7 +165,7 @@ class ProductGroup < ActiveRecord::Base
|
|
170
165
|
def set_permalink
|
171
166
|
self.permalink = self.name.to_url
|
172
167
|
end
|
173
|
-
|
168
|
+
|
174
169
|
def update_memberships
|
175
170
|
# wipe everything directly to avoid expensive in-rails sorting
|
176
171
|
ActiveRecord::Base.connection.execute "DELETE FROM product_groups_products WHERE product_group_id = #{self.id}"
|
@@ -188,18 +183,19 @@ class ProductGroup < ActiveRecord::Base
|
|
188
183
|
def to_s
|
189
184
|
"<ProductGroup" + (id && "[#{id}]").to_s + ":'#{to_url}'>"
|
190
185
|
end
|
191
|
-
|
186
|
+
|
192
187
|
def order_scope
|
193
188
|
if scope = product_scopes.detect {|s| s.is_ordering?}
|
194
189
|
scope.name
|
195
190
|
end
|
196
191
|
end
|
192
|
+
|
197
193
|
def order_scope=(scope_name)
|
198
194
|
if scope = product_scopes.detect {|s| s.is_ordering?}
|
199
195
|
scope.update_attribute(:name, scope_name)
|
200
196
|
else
|
201
197
|
self.product_scopes.build(:name => scope_name, :arguments => [])
|
202
|
-
end
|
198
|
+
end
|
203
199
|
end
|
204
200
|
|
205
201
|
# Build a new product group with a scope to filter by specified products
|