spree_advanced_calculators 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ class Admin::BucketRatesController < Admin::BaseController
2
+ resource_controller
3
+ before_filter :load_data
4
+ layout 'admin'
5
+
6
+ update.response do |wants|
7
+ wants.html { redirect_to collection_url }
8
+ end
9
+
10
+ create.response do |wants|
11
+ wants.html {
12
+ if params["create_another"]
13
+ rate = params["bucket_rate"].dup
14
+ rate[:floor] = rate.delete(:ceiling)
15
+ redirect_to new_object_url+"?"+({"bucket_rate" => rate}.to_param)
16
+ else
17
+ redirect_to collection_url
18
+ end
19
+ }
20
+ end
21
+
22
+ private
23
+ def collection
24
+ conditions = params[:calculator_id] ? {:calculator_id => params[:calculator_id]} : {}
25
+ @collection ||= end_of_association_chain.all(
26
+ :conditions => conditions,
27
+ :order => 'calculator_id DESC, floor ASC'
28
+ )
29
+ end
30
+
31
+ def load_data
32
+ @available_calculators = Calculator.find(
33
+ :all,
34
+ :conditions => {:advanced => true},
35
+ :order => 'created_at DESC'
36
+ )
37
+ end
38
+ end
@@ -0,0 +1,28 @@
1
+ class BucketRate < ActiveRecord::Base
2
+ belongs_to :calculator
3
+
4
+ validates_presence_of :floor, :ceiling, :rate, :calculator_id
5
+
6
+ scope :order_by_floor, :order => "floor"
7
+ scope :for_calculator, lambda{ |calc|
8
+ if calc.is_a?(Calculator)
9
+ {:conditions => {:calculator_id => calc.id}}
10
+ else
11
+ {:conditions => {:calculator_id => calc.to_i}}
12
+ end
13
+ }
14
+
15
+ def unit
16
+ calculator && calculator.unit
17
+ end
18
+
19
+ def validate
20
+ if !ceiling.blank? && !floor.blank? && ceiling.to_i < floor.to_i
21
+ errors.add(:ceiling, :higher_or_equal)
22
+ end
23
+ end
24
+
25
+ def <=>(other)
26
+ self.floor <=> other.floor
27
+ end
28
+ end
@@ -0,0 +1,50 @@
1
+ class Calculator::Advanced < Calculator
2
+ has_many :bucket_rates, :foreign_key => :calculator_id, :dependent => :destroy
3
+ preference :default_amount, :decimal, :default => 0
4
+
5
+ before_save :set_advanced
6
+
7
+ def self.register
8
+ super
9
+ # Chetan -> to use with promotions remove below comment
10
+ # Coupon.register_calculator(self)
11
+ ShippingMethod.register_calculator(self)
12
+ # Chetan -> I suppose we are not using shipping rates anymore in recent versions of Spree
13
+ # ShippingRate.register_calculator(self)
14
+ end
15
+
16
+ def set_advanced
17
+ self.advanced = true
18
+ end
19
+
20
+ def name
21
+ calculable.respond_to?(:name) ? calculable.name : calculable.to_s
22
+ end
23
+
24
+ def unit
25
+ self.class.unit
26
+ end
27
+
28
+ def get_rate(value)
29
+ # First try to find where price falls within price floor and ceiling
30
+ bucket = BucketRate.find(:first,
31
+ :conditions => [
32
+ "calculator_id = ? and floor <= ? and ceiling > ?",
33
+ self.id, value, value
34
+ ])
35
+
36
+ if bucket
37
+ return(bucket.rate)
38
+ else
39
+ # find largest one
40
+ bucket = BucketRate.find(:last, :conditions => ['calculator_id = ?', self.id], :order => "ceiling ASC")
41
+ # check if we've found largest one, and item_total is higher then ceiling
42
+ if bucket && value > bucket.ceiling
43
+ return(bucket.rate)
44
+ else
45
+ return(false) # if there's no rates, or we've hit a hole, let calculator use default rate.
46
+ end
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,15 @@
1
+ class Calculator::QuantityBucket < Calculator::Advanced
2
+ def self.description
3
+ I18n.t("quantity_bucket", :scope => :calculator_names)
4
+ end
5
+
6
+ def self.unit
7
+ I18n.t(:qty)
8
+ end
9
+
10
+ # as object we always get line items, as calculable we have Coupon, ShippingMethod
11
+ def compute(object)
12
+ total_quantity = object.line_items.map(&:quantity).sum
13
+ get_rate(total_quantity) || self.preferred_default_amount
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ class Calculator::WeightBucket < Calculator::Advanced
2
+ preference :default_weight, :decimal, :default => 0
3
+
4
+ def self.description
5
+ I18n.t("weight_bucket", :scope => :calculator_names)
6
+ end
7
+
8
+ def self.unit
9
+ I18n.t('weight_unit')
10
+ end
11
+
12
+ # as object we always get line items, as calculable we have Coupon, ShippingMethod
13
+ def compute(object)
14
+ total_weight = object.line_items.map{|li|
15
+ (li.variant.weight || self.preferred_default_weight) * li.quantity
16
+ }.sum
17
+
18
+ get_rate(total_weight) || self.preferred_default_amount
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ <table>
2
+ <% if @bucket_rate.calculator_id.blank? %>
3
+ <tr>
4
+ <td><%=t("calculator")%>:</td>
5
+ <td>
6
+ <%= collection_select(
7
+ :bucket_rate,
8
+ :calculator_id, @available_calculators,
9
+ :id, :name, {}, {"style" => "width:200px"}
10
+ ) %>
11
+ </td>
12
+ </tr>
13
+ <% else %>
14
+ <%= f.hidden_field :calculator_id %>
15
+ <% end %>
16
+ <tr>
17
+ <td><%= t("floor", :scope => 'activerecord.attributes.bucket_rate') %>:</td>
18
+ <td>
19
+ <%= f.text_field :floor, {"style" => "width:60px"} %>
20
+ <%= @bucket_rate.unit %>
21
+ </td>
22
+ </tr>
23
+ <tr>
24
+ <td><%= t("ceiling", :scope => 'activerecord.attributes.bucket_rate') %>:</td>
25
+ <td>
26
+ <%= f.text_field :ceiling, {"style" => "width:60px"} %>
27
+ <%= @bucket_rate.unit %>
28
+ </td>
29
+ </tr>
30
+ <tr>
31
+ <td><%= t("rate", :scope => 'activerecord.attributes.bucket_rate') %>:</td>
32
+ <td>
33
+ <%= f.text_field :rate, {"style" => "width:60px"} %>
34
+ <%= t(:'number.currency.format.unit') %>
35
+ </td>
36
+ </tr>
37
+ </table>
@@ -0,0 +1,8 @@
1
+ <h1><%= t("edit_bucket_rate") %></h1>
2
+ <%= error_messages_for :bucket_rate %>
3
+ <% form_for(:bucket_rate, :url => object_url, :html => { :method => :put }) do |f| %>
4
+ <%= render :partial => "form", :locals => { :f => f } %>
5
+ <p>
6
+ <%=submit_tag t("actions.update")%>
7
+ </p>
8
+ <% end %>
@@ -0,0 +1,49 @@
1
+ <div class='toolbar'>
2
+ <ul class='actions'>
3
+ <li>
4
+ <%= button_link_to t("new_bucket_rate"), new_object_url, :icon => 'add' %>
5
+ </li>
6
+ </ul>
7
+ <br class='clear' />
8
+ </div>
9
+
10
+ <h1><%= t("bucket_rate") %></h1>
11
+ <p>
12
+ In order to use the Bucket Rate calculator, you need to create intervals without gaps between them.
13
+ <br />
14
+ When the order total does not fall within an interval, the default rate will be used.
15
+ Order amounts larger than the largest ceiling will use the last interval rate.
16
+ <br />
17
+ NOTE: The FLOOR value is inclusive and the CEILING value is exclusive.
18
+ </p>
19
+
20
+ <table class="index">
21
+ <thead>
22
+ <tr>
23
+ <th><%= t("name")%></th>
24
+ <th><%= t("shipping_calculator")%></th>
25
+ <th><%= t("floor", :scope => 'activerecord.attributes.bucket_rate') %></th>
26
+ <th><%= t("ceiling", :scope => 'activerecord.attributes.bucket_rate')%></th>
27
+ <th><%= t("rate", :scope => 'activerecord.attributes.bucket_rate') %></th>
28
+ <th><%= t("action")%></th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ <% @bucket_rates.sort.each do |rate| %>
33
+ <tr>
34
+ <td><%=rate.calculator ? rate.calculator.name: "-" %></td>
35
+ <td><%=rate.calculator ? rate.calculator.class.description: "-" %></td>
36
+ <td><%=rate.floor %> <%= rate.unit %></td>
37
+ <td><%=rate.ceiling %> <%= rate.unit %></td>
38
+ <td><%=number_to_currency(rate.rate) %></td>
39
+ <td>
40
+ <%= link_to_edit rate %> &nbsp;
41
+ <%= link_to_delete rate %>
42
+ </td>
43
+ </tr>
44
+ <% end %>
45
+ <% if @bucket_rates.empty? %>
46
+ <tr><td colspan="5"><%= t(:none) %></td></tr>
47
+ <% end %>
48
+ </tbody>
49
+ </table>
@@ -0,0 +1,11 @@
1
+ <h1><%= t("new_bucket_rate") %></h1>
2
+
3
+ <%= error_messages_for :bucket_rate %>
4
+
5
+ <% form_for(:bucket_rate, :url => collection_url) do |f| %>
6
+ <%= render :partial => "form", :locals => { :f => f } %>
7
+ <p>
8
+ <%= submit_tag t("actions.create") %>
9
+ <%= submit_tag t("actions.create_another"), :name => "create_another" %>
10
+ </p>
11
+ <% end %>
@@ -0,0 +1,39 @@
1
+ <fieldset id="calculator_fields">
2
+ <legend><%= t('calculator') %></legend>
3
+ <div id="preference-settings">
4
+
5
+ <p>
6
+ <%= f.label(:calc_type, t("calculator")) %>
7
+ <%= f.collection_select(:calculator_type, @calculators, :to_s, :description, {}, {:id => "calc-type"}) %>
8
+ </p>
9
+
10
+ <% if !@object.new_record? %>
11
+ <div class="calculator-settings">
12
+ <%= f.fields_for :calculator do |calculator_form| %>
13
+ <%= preference_fields(@object.calculator, calculator_form) %>
14
+ <% end %>
15
+ </div>
16
+ <% if @object.calculator.respond_to?(:preferences) %>
17
+ <div id="calculator-settings-warning"><%= t('calculator_settings_warning')%></div>
18
+ <% end %>
19
+ <% if @object.calculator.advanced? && !@object.calculator.name.blank? %>
20
+ <% if BucketRate.for_calculator(@calculator).count == 0 %>
21
+ <%= t(:configure_new_bucket_rate) %>
22
+ <%= link_to t(:click_here), {
23
+ :controller => 'admin/bucket_rates',
24
+ :action => 'new',
25
+ :bucket_rate => {:calculator_id => @object.calculator.id}
26
+ } %>
27
+ <% else %>
28
+ <%= t(:configure_bucket_rates) %>
29
+ <%= link_to t(:click_here), {
30
+ :controller => 'admin/bucket_rates',
31
+ :action => 'index',
32
+ :calculator_id => @object.calculator.id
33
+ } %>
34
+ <% end %>
35
+ <% end %>
36
+ <% end %>
37
+
38
+ </div>
39
+ </fieldset>
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do
2
+ namespace :admin do
3
+ resources :bucket_rates
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ require 'spree_core'
2
+ require 'spree_advanced_calculators_hooks'
3
+
4
+ module SpreeAdvancedCalculators
5
+ class Engine < Rails::Engine
6
+ def self.activate
7
+ [
8
+ Calculator::WeightBucket,
9
+ Calculator::QuantityBucket
10
+ ].each(&:register)
11
+ end
12
+ config.to_prepare &method(:activate).to_proc
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ class SpreeAdvancedCalculatorsHooks < Spree::ThemeSupport::HookListener
2
+ insert_after(:admin_configurations_menu) do
3
+ '<tr>
4
+ <td><%= link_to t("bucket_rates"), admin_bucket_rates_url %></td>
5
+ <td><%= t("bucket_rates") %></td>
6
+ </tr>'
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ namespace :spree_advanced_calculators do
2
+ desc "Copies all migrations and assets (NOTE: This will be obsolete with Rails 3.1)"
3
+ task :install do
4
+ Rake::Task['spree_advanced_calculators:install:migrations'].invoke
5
+ end
6
+
7
+ namespace :install do
8
+ desc "Copies all migrations (NOTE: This will be obsolete with Rails 3.1)"
9
+ task :migrations do
10
+ source = File.join(File.dirname(__FILE__), '..', '..', 'db')
11
+ destination = File.join(Rails.root, 'db')
12
+ Spree::FileUtilz.mirror_files(source, destination)
13
+ end
14
+ end
15
+
16
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spree_advanced_calculators
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Chetan Mittal
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-02 00:00:00 +05:30
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: spree_core
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.40.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ description: Allows advanced calculators such as weight etc to be applied on shipping methods
28
+ email: chetan.mittal@niamtech.com
29
+ executables: []
30
+
31
+ extensions: []
32
+
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - lib/tasks/install.rake
37
+ - lib/spree_advanced_calculators_hooks.rb
38
+ - lib/spree_advanced_calculators.rb
39
+ - app/views/admin/bucket_rates/edit.html.erb
40
+ - app/views/admin/bucket_rates/new.html.erb
41
+ - app/views/admin/bucket_rates/_form.html.erb
42
+ - app/views/admin/bucket_rates/index.html.erb
43
+ - app/views/admin/shared/_calculator_fields.html.erb
44
+ - app/models/bucket_rate.rb
45
+ - app/models/calculator/advanced.rb
46
+ - app/models/calculator/weight_bucket.rb
47
+ - app/models/calculator/quantity_bucket.rb
48
+ - app/controllers/admin/bucket_rates_controller.rb
49
+ - config/routes.rb
50
+ has_rdoc: true
51
+ homepage: http://www.niamtech.com
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 1.8.7
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ requirements:
72
+ - none
73
+ rubyforge_project: spree_advanced_calculators
74
+ rubygems_version: 1.5.0
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Allows advanced calculators such as weight etc to be applied on shipping methods
78
+ test_files: []
79
+