spree_advanced_calculators 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/app/controllers/admin/bucket_rates_controller.rb +38 -0
- data/app/models/bucket_rate.rb +28 -0
- data/app/models/calculator/advanced.rb +50 -0
- data/app/models/calculator/quantity_bucket.rb +15 -0
- data/app/models/calculator/weight_bucket.rb +20 -0
- data/app/views/admin/bucket_rates/_form.html.erb +37 -0
- data/app/views/admin/bucket_rates/edit.html.erb +8 -0
- data/app/views/admin/bucket_rates/index.html.erb +49 -0
- data/app/views/admin/bucket_rates/new.html.erb +11 -0
- data/app/views/admin/shared/_calculator_fields.html.erb +39 -0
- data/config/routes.rb +5 -0
- data/lib/spree_advanced_calculators.rb +14 -0
- data/lib/spree_advanced_calculators_hooks.rb +8 -0
- data/lib/tasks/install.rake +16 -0
- metadata +79 -0
@@ -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 %>
|
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,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,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
|
+
|