spree_core 0.40.4 → 0.50.0
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/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
|