spree_zone_pricing 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ class ZonePrice < ActiveRecord::Base
2
+ belongs_to :zone
3
+ belongs_to :variant
4
+
5
+ validates_presence_of :price
6
+ validates_numericality_of :price
7
+ validates_presence_of :variant
8
+ validates_presence_of :zone
9
+
10
+ end
@@ -0,0 +1,20 @@
1
+ <table class="index">
2
+ <thead>
3
+ <tr>
4
+ <th><%= t("zone") %></th>
5
+ <th><%= t("price") %></th>
6
+ </tr>
7
+ </thead>
8
+ <tbody id="zone_prices">
9
+ <% for zone in Zone.all %>
10
+ <tr>
11
+ <td>
12
+ <%= zone.name %>
13
+ </td>
14
+ <td>
15
+ <%= text_field_tag "variant[zone_price_attributes][#{zone.id}][price]", zone.zone_prices.find_by_variant_id(@variant.id).try(:price) %>
16
+ </td>
17
+ </tr>
18
+ <% end %>
19
+ </tbody>
20
+ </table>
@@ -0,0 +1,5 @@
1
+ <% if url_options_authenticate?(:controller => 'admin/taxons') %>
2
+ <li <%= ' class="active"' if current == t("zone_pricing") %>>
3
+ <%= link_to(t("zone_pricing"), zone_prices_admin_product_variant_url(@product, @product.master)) %>
4
+ </li>
5
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <%= render :partial => 'admin/shared/product_sub_menu' %>
2
+
3
+ <%= render :partial => 'admin/shared/product_tabs', :locals => {:current => t("zone_pricing")} %>
4
+
5
+ <%= error_messages_for :variant %>
6
+
7
+ <% form_for(:variant, :url => admin_product_variant_url(@product, @variant), :html => { :method => :put }) do |f| %>
8
+
9
+ <h3><%= t("zone_pricing") %></h3>
10
+ <%= render 'admin/shared/zone_prices' %>
11
+ <br/><br/>
12
+
13
+ <%= render :partial => 'admin/shared/edit_resource_links' %>
14
+ <% end %>
15
+
@@ -0,0 +1,7 @@
1
+ <% form_tag :controller => 'countries', :action => 'set' do %>
2
+ <%= label_tag t('country') %>
3
+ <%= select_tag 'id',
4
+ options_for_select(Country.order_by_name.collect{ |c| [c.name, c.id] }, get_user_country_id),
5
+ :onchange => "this.form.submit();" %>
6
+ <%= content_tag(:noscript, submit_tag(t('change'))) %>
7
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <li><%= link_to t("home") , root_path %></li>
2
+ <li class="cart-indicator"><%= link_to_cart %></li>
3
+ <br>
4
+ <li><%= render 'shared/country_select' %></li>
@@ -0,0 +1,5 @@
1
+ ---
2
+ en:
3
+ zone_pricing: "Zone Pricing"
4
+ country_changed: "Country changed"
5
+ country_not_changed: "Country was not changed"
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ map.namespace :admin do |admin|
2
+ admin.resources :products do |product|
3
+ product.resources :variants, :member => {:zone_prices => :get}
4
+ end
5
+ end
data/db/seeds.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Use this file to load your own seed data from extensions.
2
+ # See the db/seeds.rb file in the Spree core for some ideas on what you can do here.
@@ -0,0 +1,30 @@
1
+ module Spree::ZonePricing::Admin::VariantsController
2
+
3
+ def self.included(target)
4
+ target.class_eval do
5
+
6
+ create.before do
7
+ # Set zone product to default if they exist
8
+ object.set_default_zone_prices(get_user_country_id)
9
+ # Make sure to call original code to save
10
+ create_before
11
+ end
12
+
13
+ update.response do |wants|
14
+ wants.html do
15
+ redirect_to object.is_master ? zone_prices_admin_product_variant_url(object.product, object) : collection_url
16
+ end
17
+ end
18
+
19
+ def object
20
+ @object ||= Variant.find(params[:id])
21
+ end
22
+
23
+ def zone_prices
24
+ @variant = object
25
+ @product = @variant.product
26
+ end
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,21 @@
1
+ module Spree::ZonePricing::CheckoutsController
2
+ def self.included(target)
3
+ target.class_eval do
4
+ address.update_hook :update_zone_prices
5
+ end
6
+ end
7
+
8
+ private
9
+
10
+ # Check the ship country and compare against the currently selected country,
11
+ # if different update the order line items with the prices from the ship country
12
+ def update_zone_prices
13
+ # Same country do not update prices
14
+ return if get_user_country_id == object.ship_address.country.id
15
+ # Update the order prices using the ship to country
16
+ @order.update_zone_prices(object.ship_address.country.id)
17
+ # Update selected country with ship to country
18
+ session[:zone_pricing_country] = object.ship_address.country.id
19
+ end
20
+
21
+ end
@@ -0,0 +1,30 @@
1
+ module Spree::ZonePricing::CountriesController
2
+
3
+ def self.included(target)
4
+ target.class_eval do
5
+
6
+ # Change the currently selected country
7
+ def set
8
+ country = Country.find(params[:id])
9
+ if request.referer && request.referer.starts_with?("http://" + request.host)
10
+ session[:return_to] = request.referer
11
+ end
12
+ if country
13
+ # Store country in the session
14
+ session[:zone_pricing_country] = country.id
15
+ # Update prices for the current order
16
+ if session[:order_id]
17
+ order = Order.find(session[:order_id])
18
+ order.update_zone_prices(get_user_country_id)
19
+ end
20
+ flash[:notice] = t("country_changed")
21
+ else
22
+ flash[:error] = t("country_not_changed")
23
+ end
24
+ redirect_back_or_default(root_path)
25
+ end
26
+
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,17 @@
1
+ module Spree::ZonePricing::GetCountry
2
+ def self.included(base)
3
+ base.class_eval do
4
+ # This method will retrieve the users country
5
+ def get_user_country_id
6
+ # Check if set in session
7
+ country = session[:zone_pricing_country] if session.has_key?(:zone_pricing_country)
8
+ # If no country in session, check if user has a bill address, if so use
9
+ # the country from the address
10
+ country ||= (current_user && current_user.respond_to?('ship_address') &&
11
+ current_user.ship_address) ? current_user.ship_address.country.id : nil
12
+ # If no bill address country use default country
13
+ country ||= Spree::Config[:default_country_id]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,65 @@
1
+ module Spree::ZonePricing::Order
2
+
3
+ def self.included(target)
4
+ target.class_eval do
5
+ # Override add_variant method so that we can use zone pricing
6
+ alias :spree_add_variant :add_variant unless method_defined?(:spree_add_variant)
7
+ alias :add_variant :site_add_variant
8
+
9
+ # Method to update prices in the current order when country changes
10
+ def update_zone_prices(country_id); order_update_zone_prices(country_id); end
11
+
12
+ # We need to know what the currently selected country is so that we can
13
+ # determine the zone price
14
+ attr_accessor :country_id
15
+ end
16
+ end
17
+
18
+ # Override the add_variant functionality so that we can adjust the price based on
19
+ # the country and therefore zone selected
20
+ def site_add_variant(variant, quantity=1)
21
+ current_item = contains?(variant)
22
+ # Added: Check if country specified, if so check if zone price defined,
23
+ # if no country specified use normal price
24
+ if @country_id
25
+ price = variant.zone_price(@country_id)
26
+ else
27
+ price = variant.price
28
+ end
29
+ if current_item
30
+ current_item.increment_quantity unless quantity > 1
31
+ current_item.quantity = (current_item.quantity + quantity) if quantity > 1
32
+ current_item.price = price # Added
33
+ current_item.save
34
+ else
35
+ current_item = LineItem.new(:quantity => quantity)
36
+ current_item.variant = variant
37
+ current_item.price = price
38
+ self.line_items << current_item
39
+ end
40
+
41
+ # populate line_items attributes for additional_fields entries
42
+ # that have populate => [:line_item]
43
+ Variant.additional_fields.select{|f| !f[:populate].nil? && f[:populate].include?(:line_item) }.each do |field|
44
+ value = ""
45
+
46
+ if field[:only].nil? || field[:only].include?(:variant)
47
+ value = variant.send(field[:name].gsub(" ", "_").downcase)
48
+ elsif field[:only].include?(:product)
49
+ value = variant.product.send(field[:name].gsub(" ", "_").downcase)
50
+ end
51
+ current_item.update_attribute(field[:name].gsub(" ", "_").downcase, value)
52
+ end
53
+
54
+ current_item
55
+ end
56
+
57
+ private
58
+
59
+ # Update price of line items in current order on change
60
+ # of country
61
+ def order_update_zone_prices(country_id)
62
+ self.line_items.each {|li| li.update_attribute(:price, li.variant.zone_price(country_id))}
63
+ update_totals!
64
+ end
65
+ end
@@ -0,0 +1,19 @@
1
+ module Spree::ZonePricing::OrdersController
2
+ def self.included(target)
3
+ target.class_eval do
4
+ alias_method :spree_create_before, :create_before unless method_defined?(:spree_create_before)
5
+ alias_method :create_before, :site_create_before
6
+ end
7
+ end
8
+
9
+ # The order object needs to know which country has been selected
10
+ # by the user so that we can calculate the zone prices. This informations
11
+ # may be stored in the session which we do not have access to in the model
12
+ # so this method will get the currently selected country and provide it to
13
+ # the Order model
14
+ def site_create_before
15
+ @order.country_id = get_user_country_id if @order
16
+ spree_create_before
17
+ end
18
+
19
+ end
@@ -0,0 +1,30 @@
1
+ module Spree::ZonePricing::ProductsHelper
2
+ def self.included(base)
3
+ base.class_eval do
4
+ alias_method :spree_product_price, :product_price unless method_defined?(:spree_product_price)
5
+ alias_method :product_price, :site_product_price
6
+ end
7
+ end
8
+
9
+ # returns the price of the product to show for display purposes
10
+ def site_product_price(product_or_variant, options={})
11
+ options.assert_valid_keys(:format_as_currency, :show_vat_text)
12
+ options.reverse_merge! :format_as_currency => true, :show_vat_text => Spree::Config[:show_price_inc_vat]
13
+
14
+ # Check if this object is a product, if so then access the master variant
15
+ # record for the product to get the zone pricing
16
+ object = product_or_variant.is_a?(Product) ? product_or_variant.master : product_or_variant
17
+
18
+ # Get the zone price for this variant/product if one is defined
19
+ # otherwise use the normal price
20
+ if object.respond_to?(:zone_price)
21
+ amount = object.zone_price(get_user_country_id)
22
+ else
23
+ amount = object.price
24
+ end
25
+
26
+ amount += Calculator::Vat.calculate_tax_on(product_or_variant) if Spree::Config[:show_price_inc_vat]
27
+ options.delete(:format_as_currency) ? format_price(amount, options) : ("%0.2f" % amount).to_f
28
+ end
29
+
30
+ end
@@ -0,0 +1,52 @@
1
+ module Spree::ZonePricing::Variant
2
+
3
+ def self.included(target)
4
+ target.class_eval do
5
+ has_many :zone_prices, :dependent => :destroy
6
+ has_many :zones, :through => :zone_prices
7
+
8
+ # Update zone prices for variant from attributes
9
+ # passed to us from the form
10
+ def zone_price_attributes=(price_attributes)
11
+ price_attributes.each do |key, value|
12
+ # Check if record exists, if so update. If value of price
13
+ # is blank or 0 then delete record
14
+ zone_price = ZonePrice.first(:conditions => {:zone_id => key, :variant_id => self.id})
15
+ if zone_price
16
+ # Record exists, update or delete if no price specified
17
+ if value['price'].to_f != 0
18
+ zone_price.update_attribute(:price, value['price'].to_f)
19
+ else
20
+ zone_price.destroy
21
+ end
22
+ elsif value['price'].to_f != 0
23
+ # Record does not exist, create
24
+ ZonePrice.create(:zone_id => key, :variant => self, :price => value['price'].to_f)
25
+ end
26
+ end
27
+ end
28
+
29
+ # Return zone price if defined otherwise return normal price
30
+ def zone_price(country_id)
31
+ # Find zone and check if zone price set for the product/variant
32
+ zone = Country.find(country_id).zone
33
+ zone_price = self.zone_prices.find_by_zone_id(zone.id) if zone && self.respond_to?('zone_prices')
34
+ if zone_price
35
+ # Zone price set us it
36
+ amount = zone_price.price
37
+ else
38
+ amount = self.price
39
+ end
40
+ end
41
+
42
+ # Method to default variant zone prices to the product zone prices
43
+ def set_default_zone_prices(country_id)
44
+ return if self.product.master.zone_prices.size <= 0 # No zone prices specified for product
45
+ # Assign product zone prices as defaults
46
+ self.product.master.zone_prices.each {|zp| self.zone_prices.build(zp.attributes)}
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,10 @@
1
+ module Spree::ZonePricing::Zone
2
+
3
+ def self.included(target)
4
+ target.class_eval do
5
+ has_many :zone_prices, :dependent => :destroy
6
+ has_many :variants, :through => :zone_prices
7
+ end
8
+ end
9
+
10
+ end
@@ -0,0 +1,5 @@
1
+ class SpreeZonePrincing
2
+ def self.hi
3
+ puts "Hello world!"
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ namespace :spree do
2
+ namespace :extensions do
3
+ namespace :zone_pricing do
4
+ desc "Copies public assets of the Zone Pricing to the instance public/ directory."
5
+ task :update => :environment do
6
+ is_svn_git_or_dir = proc {|path| path =~ /\.svn/ || path =~ /\.git/ || File.directory?(path) }
7
+ Dir[ZonePricingExtension.root + "/public/**/*"].reject(&is_svn_git_or_dir).each do |file|
8
+ path = file.sub(ZonePricingExtension.root, '')
9
+ directory = File.dirname(path)
10
+ puts "Copying #{path}..."
11
+ mkdir_p RAILS_ROOT + directory
12
+ cp file, RAILS_ROOT + path
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --format
3
+ progress
4
+ --loadby
5
+ mtime
6
+ --reverse
@@ -0,0 +1,37 @@
1
+ unless defined? SPREE_ROOT
2
+ ENV["RAILS_ENV"] = "test"
3
+ case
4
+ when ENV["SPREE_ENV_FILE"]
5
+ require ENV["SPREE_ENV_FILE"]
6
+ when File.dirname(__FILE__) =~ %r{vendor/SPREE/vendor/extensions}
7
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment"
8
+ else
9
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
10
+ end
11
+ end
12
+ require "#{SPREE_ROOT}/spec/spec_helper"
13
+
14
+ if File.directory?(File.dirname(__FILE__) + "/scenarios")
15
+ Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios"
16
+ end
17
+ if File.directory?(File.dirname(__FILE__) + "/matchers")
18
+ Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
19
+ end
20
+
21
+ Spec::Runner.configure do |config|
22
+ # config.use_transactional_fixtures = true
23
+ # config.use_instantiated_fixtures = false
24
+ # config.fixture_path = RAILS_ROOT + '/spec/fixtures'
25
+
26
+ # You can declare fixtures for each behaviour like this:
27
+ # describe "...." do
28
+ # fixtures :table_a, :table_b
29
+ #
30
+ # Alternatively, if you prefer to declare them only once, you can
31
+ # do so here, like so ...
32
+ #
33
+ # config.global_fixtures = :table_a, :table_b
34
+ #
35
+ # If you declare global fixtures, be aware that they will be declared
36
+ # for all of your examples, even those that don't use them.
37
+ end
@@ -0,0 +1,44 @@
1
+ # Uncomment this if you reference any of your controllers in activate
2
+ # require_dependency 'application'
3
+
4
+ class ZonePricingExtension < Spree::Extension
5
+ version "1.0"
6
+ description "Allows prices to be set by zone"
7
+ url "http://yourwebsite.com/zone_pricing"
8
+
9
+ # Please use zone_pricing/config/routes.rb instead for extension routes.
10
+
11
+ # def self.require_gems(config)
12
+ # config.gem "gemname-goes-here", :version => '1.2.3'
13
+ # end
14
+
15
+ def activate
16
+
17
+ # Add helper to retrieve the users country
18
+ ApplicationHelper.send(:include, Spree::ZonePricing::GetCountry)
19
+ # Add helper to retrieve the users country, used by a number of controllers
20
+ Spree::BaseController.send(:include, Spree::ZonePricing::GetCountry)
21
+
22
+ # Add additional associations to allow m:m relationship
23
+ # between zones<->variants
24
+ Zone.send(:include, Spree::ZonePricing::Zone)
25
+ Variant.send(:include, Spree::ZonePricing::Variant)
26
+
27
+ # Override add_variant method so that we can use zone pricing
28
+ Order.send(:include, Spree::ZonePricing::Order)
29
+
30
+ # Override price
31
+ ProductsHelper.send(:include, Spree::ZonePricing::ProductsHelper)
32
+
33
+ # Add action to countries controller to handle country selection
34
+ CountriesController.send(:include, Spree::ZonePricing::CountriesController)
35
+ # Add code to set the currently country in the order
36
+ OrdersController.send(:include, Spree::ZonePricing::OrdersController)
37
+ # Add code to save zone prices
38
+ Admin::VariantsController.send(:include, Spree::ZonePricing::Admin::VariantsController)
39
+ # Add code to check zone prices after address is saved during checkout, if ship
40
+ # country not the same change prices in order
41
+ CheckoutsController.send(:include, Spree::ZonePricing::CheckoutsController)
42
+
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ class ZonePricingHooks < Spree::ThemeSupport::HookListener
2
+
3
+ # Add zone pricing tab to products admin area
4
+ insert_after :admin_product_tabs, :partial => "admin/shared/zp_product_tab"
5
+
6
+ # Add zone pricing to variant edit form
7
+ insert_after :admin_variant_edit_form, :partial => 'admin/shared/zone_prices'
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spree_zone_pricing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Henrique Ferreira
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-18 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Set different price zones within Spree (Based in spree-zone-pricing)
15
+ email: henriquebf@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - app/models/zone_price.rb
21
+ - app/views/admin/shared/_zone_prices.html.erb
22
+ - app/views/admin/shared/_zp_product_tab.html.erb
23
+ - app/views/admin/variants/zone_prices.html.erb
24
+ - app/views/shared/_country_select.html.erb
25
+ - app/views/shared/_store_menu.html.erb
26
+ - config/routes.rb
27
+ - config/locales/en_spree.yml
28
+ - db/seeds.rb
29
+ - lib/spree_zone_pricing.rb
30
+ - lib/spree/zone_pricing/checkouts_controller.rb
31
+ - lib/spree/zone_pricing/countries_controller.rb
32
+ - lib/spree/zone_pricing/get_country.rb
33
+ - lib/spree/zone_pricing/order.rb
34
+ - lib/spree/zone_pricing/orders_controller.rb
35
+ - lib/spree/zone_pricing/products_helper.rb
36
+ - lib/spree/zone_pricing/variant.rb
37
+ - lib/spree/zone_pricing/zone.rb
38
+ - lib/spree/zone_pricing/admin/variants_controller.rb
39
+ - lib/tasks/zone_pricing_extension_tasks.rake
40
+ - spec/spec.opts
41
+ - spec/spec_helper.rb
42
+ - zone_pricing_extension.rb
43
+ - zone_pricing_hooks.rb
44
+ homepage: http://rubygems.org/gems/spree_zone_pricing
45
+ licenses: []
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 1.8.24
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Spree Zone Pricing
68
+ test_files: []