piggybak 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/Gemfile.lock +5 -0
- data/README.md +117 -0
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/app/assets/javascripts/piggybak.js +62 -2
- data/app/controllers/piggybak/orders_controller.rb +26 -5
- data/{lib/application_helper.rb → app/helpers/piggybak_helper.rb} +6 -1
- data/app/models/piggybak/address.rb +14 -1
- data/app/models/piggybak/cart.rb +19 -12
- data/app/models/piggybak/country.rb +5 -0
- data/app/models/piggybak/credit.rb +8 -0
- data/app/models/piggybak/line_item.rb +13 -6
- data/app/models/piggybak/order.rb +12 -6
- data/app/models/piggybak/payment.rb +5 -3
- data/app/models/piggybak/payment_calculator/authorize_net.rb +4 -0
- data/app/models/piggybak/payment_calculator/fake.rb +4 -0
- data/app/models/piggybak/payment_method.rb +8 -3
- data/app/models/piggybak/shipping_calculator/free.rb +13 -0
- data/app/models/piggybak/shipping_calculator/pickup.rb +1 -1
- data/app/models/piggybak/shipping_method.rb +9 -7
- data/app/models/piggybak/state.rb +1 -0
- data/app/models/piggybak/tax_calculator/{flat_rate.rb → percent.rb} +1 -1
- data/app/models/piggybak/tax_method.rb +8 -2
- data/app/models/piggybak/{product.rb → variant.rb} +5 -4
- data/app/views/piggybak/cart/_form.html.erb +7 -7
- data/app/views/piggybak/cart/_items.html.erb +9 -9
- data/app/views/piggybak/cart/show.html.erb +2 -0
- data/app/views/piggybak/notifier/order_notification.text.erb +1 -1
- data/app/views/piggybak/orders/_address_form.html.erb +18 -14
- data/app/views/piggybak/orders/_details.html.erb +48 -0
- data/app/views/piggybak/orders/download.text.erb +19 -0
- data/app/views/piggybak/orders/list.html.erb +12 -12
- data/app/views/piggybak/orders/no_access.text.erb +1 -0
- data/app/views/piggybak/orders/receipt.html.erb +1 -49
- data/app/views/piggybak/orders/show.html.erb +36 -31
- data/app/views/rails_admin/main/_actions.html.erb +9 -2
- data/config/routes.rb +12 -3
- data/db/migrate/20111227150106_create_orders.rb +4 -3
- data/db/migrate/20111227150322_create_addresses.rb +2 -1
- data/db/migrate/20111227150432_create_line_items.rb +2 -2
- data/db/migrate/{20111227213558_create_products.rb → 20111227213558_create_variants.rb} +3 -3
- data/db/migrate/20111228231829_create_payments.rb +3 -1
- data/db/migrate/20111228231838_create_shipments.rb +1 -1
- data/db/migrate/20120102162414_create_countries.rb +10 -0
- data/db/migrate/20120102162415_create_states.rb +1 -0
- data/db/migrate/20120104020930_populate_countries_and_states.rb +18 -0
- data/db/migrate/20120106010412_create_credits.rb +14 -0
- data/lib/{acts_as_product → acts_as_variant}/base.rb +6 -6
- data/lib/piggybak.rb +59 -23
- data/piggybak.gemspec +26 -11
- metadata +64 -62
- data/README.rdoc +0 -19
@@ -1,9 +1,9 @@
|
|
1
1
|
module Piggybak
|
2
2
|
class LineItem < ActiveRecord::Base
|
3
3
|
belongs_to :order
|
4
|
-
belongs_to :
|
4
|
+
belongs_to :variant
|
5
5
|
|
6
|
-
validates_presence_of :
|
6
|
+
validates_presence_of :variant_id
|
7
7
|
validates_presence_of :total
|
8
8
|
validates_presence_of :quantity
|
9
9
|
validates_numericality_of :quantity, :only_integer => true, :greater_than_or_equal_to => 0
|
@@ -13,19 +13,26 @@ module Piggybak
|
|
13
13
|
after_update :update_inventory
|
14
14
|
|
15
15
|
def admin_label
|
16
|
-
"#{self.quantity} x #{self.
|
16
|
+
"#{self.quantity} x #{self.variant.description}"
|
17
17
|
end
|
18
18
|
|
19
19
|
def decrease_inventory
|
20
|
-
self.
|
20
|
+
self.variant.update_inventory(-1 * self.quantity)
|
21
21
|
end
|
22
22
|
|
23
23
|
def increase_inventory
|
24
|
-
self.
|
24
|
+
self.variant.update_inventory(self.quantity)
|
25
25
|
end
|
26
26
|
|
27
27
|
def update_inventory
|
28
|
-
|
28
|
+
if self.variant_id != self.variant_id_was
|
29
|
+
old_variant = Variant.find(self.variant_id_was)
|
30
|
+
old_variant.update_inventory(self.quantity_was)
|
31
|
+
self.variant.update_inventory(-1*self.quantity)
|
32
|
+
else
|
33
|
+
quantity_diff = self.quantity_was - self.quantity
|
34
|
+
self.variant.update_inventory(quantity_diff)
|
35
|
+
end
|
29
36
|
end
|
30
37
|
end
|
31
38
|
end
|
@@ -3,6 +3,8 @@ module Piggybak
|
|
3
3
|
has_many :line_items, :inverse_of => :order
|
4
4
|
has_many :payments, :inverse_of => :order
|
5
5
|
has_many :shipments, :inverse_of => :order
|
6
|
+
has_many :credits, :inverse_of => :order
|
7
|
+
|
6
8
|
belongs_to :billing_address, :class_name => "Piggybak::Address"
|
7
9
|
belongs_to :shipping_address, :class_name => "Piggybak::Address"
|
8
10
|
belongs_to :user
|
@@ -11,7 +13,7 @@ module Piggybak
|
|
11
13
|
accepts_nested_attributes_for :shipping_address, :allow_destroy => true
|
12
14
|
accepts_nested_attributes_for :shipments, :allow_destroy => true
|
13
15
|
accepts_nested_attributes_for :line_items, :allow_destroy => true
|
14
|
-
accepts_nested_attributes_for :payments
|
16
|
+
accepts_nested_attributes_for :payments # test :allow_destroy
|
15
17
|
|
16
18
|
validates_presence_of :status
|
17
19
|
validates_presence_of :email
|
@@ -58,8 +60,8 @@ module Piggybak
|
|
58
60
|
def add_line_items(cart)
|
59
61
|
cart.update_quantities
|
60
62
|
cart.items.each do |item|
|
61
|
-
line_item = Piggybak::LineItem.new({ :
|
62
|
-
:total => item[:
|
63
|
+
line_item = Piggybak::LineItem.new({ :variant_id => item[:variant].id,
|
64
|
+
:total => item[:variant].price*item[:quantity],
|
63
65
|
:quantity => item[:quantity] })
|
64
66
|
self.line_items << line_item
|
65
67
|
end
|
@@ -73,7 +75,7 @@ module Piggybak
|
|
73
75
|
self.tax_charge = 0
|
74
76
|
|
75
77
|
self.line_items.each do |line_item|
|
76
|
-
line_item.total = line_item.
|
78
|
+
line_item.total = line_item.variant.price * line_item.quantity
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
@@ -92,10 +94,14 @@ module Piggybak
|
|
92
94
|
calculator = shipment.shipping_method.klass.constantize
|
93
95
|
shipment.total = calculator.rate(shipment.shipping_method, self)
|
94
96
|
end
|
95
|
-
logger.warn "steph shipment total is #{shipment.total}"
|
96
97
|
self.total += shipment.total
|
97
98
|
end
|
98
|
-
|
99
|
+
|
100
|
+
# Hook in credits, TBD
|
101
|
+
credits.each do |credit|
|
102
|
+
self.total -= credit.total
|
103
|
+
end
|
104
|
+
|
99
105
|
self.total = self.total.to_c
|
100
106
|
|
101
107
|
self.total_due = self.total
|
@@ -10,7 +10,7 @@ module Piggybak
|
|
10
10
|
validates_presence_of :year
|
11
11
|
|
12
12
|
def status_enum
|
13
|
-
["paid"]
|
13
|
+
["paid", "refunded"]
|
14
14
|
end
|
15
15
|
|
16
16
|
def month_enum
|
@@ -39,7 +39,8 @@ module Piggybak
|
|
39
39
|
if gateway_response.success?
|
40
40
|
self.attributes = { :total => self.order.total_due,
|
41
41
|
:number => 'hidden',
|
42
|
-
:verification_value => 'hidden'
|
42
|
+
:verification_value => 'hidden',
|
43
|
+
:transaction_id => payment_gateway.transaction_id(gateway_response) }
|
43
44
|
gateway.capture(1000, gateway_response.authorization)
|
44
45
|
return true
|
45
46
|
else
|
@@ -56,6 +57,7 @@ module Piggybak
|
|
56
57
|
return "Payment ##{self.id} (#{self.created_at.strftime("%m-%d-%Y")})<br />" +
|
57
58
|
"Payment Method: #{self.payment_method.description}<br />" +
|
58
59
|
"Status: #{self.status}<br />" +
|
60
|
+
"Transaction ID: #{self.transaction_id}<br />" +
|
59
61
|
"$#{"%.2f" % self.total}"
|
60
62
|
else
|
61
63
|
return ""
|
@@ -70,7 +72,7 @@ module Piggybak
|
|
70
72
|
if !credit_card.valid?
|
71
73
|
credit_card.errors.each do |key, value|
|
72
74
|
if value.any? && !["first_name", "last_name", "type"].include?(key)
|
73
|
-
record.errors.add key, value
|
75
|
+
record.errors.add key, (value.is_a?(Array) ? value.join(', ') : value)
|
74
76
|
end
|
75
77
|
end
|
76
78
|
end
|
@@ -2,5 +2,9 @@ module Piggybak
|
|
2
2
|
class PaymentCalculator::AuthorizeNet < PaymentCalculator
|
3
3
|
KEYS = ["login", "password"]
|
4
4
|
KLASS = ::ActiveMerchant::Billing::AuthorizeNetGateway
|
5
|
+
|
6
|
+
def self.transaction_id(gateway_response)
|
7
|
+
gateway_response.params["transaction_id"]
|
8
|
+
end
|
5
9
|
end
|
6
10
|
end
|
@@ -1,5 +1,12 @@
|
|
1
1
|
module Piggybak
|
2
2
|
class PaymentMethod < ActiveRecord::Base
|
3
|
+
|
4
|
+
# klass_enum requires the ShippingCalculator subclasses to be loaded
|
5
|
+
shipping_calcs_path = File.expand_path("../payment_calculator", __FILE__)
|
6
|
+
Dir.glob(shipping_calcs_path + "**/*.rb").each do |subclass|
|
7
|
+
ActiveSupport::Dependencies.require_or_load subclass
|
8
|
+
end
|
9
|
+
|
3
10
|
has_many :payment_method_values, :dependent => :destroy
|
4
11
|
alias :metadata :payment_method_values
|
5
12
|
|
@@ -9,9 +16,7 @@ module Piggybak
|
|
9
16
|
validates_presence_of :description
|
10
17
|
|
11
18
|
def klass_enum
|
12
|
-
|
13
|
-
[Piggybak::PaymentCalculator::AuthorizeNet,
|
14
|
-
Piggybak::PaymentCalculator::Fake]
|
19
|
+
Piggybak::PaymentCalculator.subclasses
|
15
20
|
end
|
16
21
|
|
17
22
|
validates_each :payment_method_values do |record, attr, value|
|
@@ -7,7 +7,7 @@ module Piggybak
|
|
7
7
|
|
8
8
|
if object.is_a?(Cart)
|
9
9
|
state = State.find(object.extra_data["state_id"])
|
10
|
-
return true if state.abbr == abbr
|
10
|
+
return true if state && state.abbr == abbr
|
11
11
|
else
|
12
12
|
if object.billing_address && object.billing_address.state
|
13
13
|
return object.billing_address.state.abbr == abbr
|
@@ -1,5 +1,12 @@
|
|
1
1
|
module Piggybak
|
2
2
|
class ShippingMethod < ActiveRecord::Base
|
3
|
+
|
4
|
+
# klass_enum requires the ShippingCalculator subclasses to be loaded
|
5
|
+
shipping_calcs_path = File.expand_path("../shipping_calculator", __FILE__)
|
6
|
+
Dir.glob(shipping_calcs_path + "**/*.rb").each do |subclass|
|
7
|
+
ActiveSupport::Dependencies.require_or_load subclass
|
8
|
+
end
|
9
|
+
|
3
10
|
has_many :shipping_method_values, :dependent => :destroy
|
4
11
|
alias :metadata :shipping_method_values
|
5
12
|
|
@@ -18,11 +25,8 @@ module Piggybak
|
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
21
|
-
def klass_enum
|
22
|
-
|
23
|
-
[Piggybak::ShippingCalculator::FlatRate,
|
24
|
-
Piggybak::ShippingCalculator::Range,
|
25
|
-
Piggybak::ShippingCalculator::Pickup]
|
28
|
+
def klass_enum
|
29
|
+
Piggybak::ShippingCalculator.subclasses
|
26
30
|
end
|
27
31
|
|
28
32
|
def self.available_methods(cart)
|
@@ -36,10 +40,8 @@ module Piggybak
|
|
36
40
|
|
37
41
|
active_methods.inject([]) do |arr, method|
|
38
42
|
klass = method.klass.constantize
|
39
|
-
logger.warn "steph: inside here!! #{method.inspect}"
|
40
43
|
if klass.available?(method, cart)
|
41
44
|
rate = klass.rate(method, cart)
|
42
|
-
logger.warn "steph rate is #{rate.inspect}"
|
43
45
|
arr << {
|
44
46
|
:label => "#{method.description} $#{"%.2f" % rate}",
|
45
47
|
:id => method.id,
|
@@ -1,5 +1,12 @@
|
|
1
1
|
module Piggybak
|
2
2
|
class TaxMethod < ActiveRecord::Base
|
3
|
+
|
4
|
+
# klass_enum requires the ShippingCalculator subclasses to be loaded
|
5
|
+
shipping_calcs_path = File.expand_path("../tax_calculator", __FILE__)
|
6
|
+
Dir.glob(shipping_calcs_path + "**/*.rb").each do |subclass|
|
7
|
+
ActiveSupport::Dependencies.require_or_load subclass
|
8
|
+
end
|
9
|
+
|
3
10
|
has_many :tax_method_values, :dependent => :destroy
|
4
11
|
alias :metadata :tax_method_values
|
5
12
|
|
@@ -19,8 +26,7 @@ module Piggybak
|
|
19
26
|
end
|
20
27
|
|
21
28
|
def klass_enum
|
22
|
-
|
23
|
-
[Piggybak::TaxCalculator::FlatRate]
|
29
|
+
Piggybak::TaxCalculator.subclasses
|
24
30
|
end
|
25
31
|
|
26
32
|
def self.calculate_tax(object)
|
@@ -1,6 +1,7 @@
|
|
1
|
-
class Piggybak::
|
2
|
-
belongs_to :item, :polymorphic => true, :inverse_of => :
|
3
|
-
attr_accessible :sku, :description, :price, :quantity, :active, :unlimited_inventory, :item_id, :item_type
|
1
|
+
class Piggybak::Variant < ActiveRecord::Base
|
2
|
+
belongs_to :item, :polymorphic => true, :inverse_of => :piggybak_variant
|
3
|
+
attr_accessible :sku, :description, :price, :quantity, :active, :unlimited_inventory, :item_id, :item_type
|
4
|
+
attr_accessible :item # to allow direct assignment from code or console
|
4
5
|
|
5
6
|
validates_presence_of :sku
|
6
7
|
validates_uniqueness_of :sku
|
@@ -10,7 +11,7 @@ class Piggybak::Product < ActiveRecord::Base
|
|
10
11
|
validates_numericality_of :quantity, :only_integer => true, :greater_than_or_equal_to => 0
|
11
12
|
|
12
13
|
def admin_label
|
13
|
-
"
|
14
|
+
"Variant: #{self.description}"
|
14
15
|
end
|
15
16
|
|
16
17
|
def update_inventory(purchased)
|
@@ -1,14 +1,14 @@
|
|
1
|
-
<% if object.reflections.keys.include?(:
|
2
|
-
<% if object.
|
3
|
-
<% if object.
|
1
|
+
<% if object.reflections.keys.include?(:piggybak_variant) -%>
|
2
|
+
<% if object.piggybak_variant && object.piggybak_variant.active -%>
|
3
|
+
<% if object.piggybak_variant.quantity > 0 || object.piggybak_variant.unlimited_inventory -%>
|
4
4
|
<%= form_tag piggybak.cart_add_url do -%>
|
5
|
-
<div id="
|
6
|
-
<span id="title"><%= object.
|
7
|
-
<span id="price"><%= number_to_currency object.
|
5
|
+
<div id="variant_details">
|
6
|
+
<span id="title"><%= object.piggybak_variant.description %></span>
|
7
|
+
<span id="price"><%= number_to_currency object.piggybak_variant.price %></span>
|
8
8
|
</div>
|
9
9
|
<label>Quantity</label>
|
10
10
|
<%= text_field_tag :quantity %>
|
11
|
-
<%= hidden_field_tag :
|
11
|
+
<%= hidden_field_tag :variant_id, object.piggybak_variant.id %>
|
12
12
|
<%= submit_tag "Add to Cart", :id => "submit" %>
|
13
13
|
<% end -%>
|
14
14
|
<% else -%>
|
@@ -2,29 +2,29 @@
|
|
2
2
|
<%= form_tag piggybak.cart_update_url do -%>
|
3
3
|
<table cellpadding="5" cellspacing="0" width="100%">
|
4
4
|
<tr>
|
5
|
-
<th>Item</th>
|
6
|
-
<th>Price</th>
|
7
|
-
<th>Quantity</th>
|
8
|
-
<th>Subtotal</th>
|
5
|
+
<th id="item_col">Item</th>
|
6
|
+
<th id="price_col">Price</th>
|
7
|
+
<th id="quantity_col">Quantity</th>
|
8
|
+
<th id="subtotal_col">Subtotal</th>
|
9
9
|
<% if page == "cart" -%>
|
10
10
|
<th></th>
|
11
11
|
<% end -%>
|
12
12
|
</tr>
|
13
13
|
<% @cart.items.each do |item| %>
|
14
14
|
<tr>
|
15
|
-
<td><%= item[:
|
16
|
-
<td><%= number_to_currency item[:
|
15
|
+
<td><%= item[:variant].description %></td>
|
16
|
+
<td><%= number_to_currency item[:variant].price %></td>
|
17
17
|
<td>
|
18
18
|
<% if page == "cart" -%>
|
19
|
-
<%= text_field_tag "quantity[#{item[:
|
19
|
+
<%= text_field_tag "quantity[#{item[:variant].id}]", item[:quantity] %>
|
20
20
|
<% else -%>
|
21
21
|
<%= item[:quantity] %>
|
22
22
|
<% end -%>
|
23
23
|
</td>
|
24
|
-
<td><%= number_to_currency item[:quantity]*item[:
|
24
|
+
<td><%= number_to_currency item[:quantity]*item[:variant].price %></td>
|
25
25
|
<% if page == "cart" -%>
|
26
26
|
<td>
|
27
|
-
<%= link_to "Remove", piggybak.remove_item_url(item[:
|
27
|
+
<%= link_to "Remove", piggybak.remove_item_url(item[:variant].id), :method => :delete %>
|
28
28
|
</td>
|
29
29
|
<% end -%>
|
30
30
|
</tr>
|
@@ -3,7 +3,7 @@ Thanks for your order!
|
|
3
3
|
Items
|
4
4
|
|
5
5
|
<% @order.line_items.each do |line_item| %>
|
6
|
-
<%= line_item.
|
6
|
+
<%= line_item.variant.description %> x <%= line_item.quantity %> = <%= number_to_currency line_item.total %>
|
7
7
|
<% end -%>
|
8
8
|
|
9
9
|
Total
|
@@ -1,24 +1,28 @@
|
|
1
|
-
<
|
1
|
+
<div class="item">
|
2
2
|
<%= address.label :firstname %>
|
3
3
|
<%= address.text_field :firstname %>
|
4
|
-
</
|
5
|
-
<
|
4
|
+
</div>
|
5
|
+
<div class="item">
|
6
6
|
<%= address.label :lastname %>
|
7
7
|
<%= address.text_field :lastname %>
|
8
|
-
</
|
9
|
-
<
|
8
|
+
</div>
|
9
|
+
<div class="item">
|
10
10
|
<%= address.label :address1 %>
|
11
11
|
<%= address.text_field :address1 %>
|
12
|
-
</
|
13
|
-
<
|
12
|
+
</div>
|
13
|
+
<div class="item">
|
14
14
|
<%= address.label :city %>
|
15
15
|
<%= address.text_field :city %>
|
16
|
-
</
|
17
|
-
<
|
18
|
-
|
19
|
-
|
20
|
-
</
|
21
|
-
<
|
16
|
+
</div>
|
17
|
+
<div class="item">
|
18
|
+
<%= address.label :country_id %>
|
19
|
+
<%= address.collection_select :country_id, Piggybak::Country.all, :id, :name, { :selected => address.object.country_id.to_s } %>
|
20
|
+
</div>
|
21
|
+
<div class="item">
|
22
|
+
<%= address.label :state_id %>
|
23
|
+
<%= address.collection_select :state_id, address.object.country.states, :id, :name, { :selected => address.object.state_id.to_s } %>
|
24
|
+
</div>
|
25
|
+
<div class="item">
|
22
26
|
<%= address.label :zip %>
|
23
27
|
<%= address.text_field :zip %>
|
24
|
-
</
|
28
|
+
</div>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<table id="items" cellpadding="5" cellspacing="0">
|
2
|
+
<tr>
|
3
|
+
<th>Item</th>
|
4
|
+
<th>Price</th>
|
5
|
+
<th>Quantity</th>
|
6
|
+
<th>Subtotal</th>
|
7
|
+
</tr>
|
8
|
+
<% order.line_items.each do |line_item| %>
|
9
|
+
<tr>
|
10
|
+
<td><%= line_item.variant.description %></td>
|
11
|
+
<td><%= line_item.variant.price %></td>
|
12
|
+
<td><%= line_item.quantity %></td>
|
13
|
+
<td><%= number_to_currency line_item.total %></td>
|
14
|
+
</tr>
|
15
|
+
<% end -%>
|
16
|
+
<tr>
|
17
|
+
<td colspan="4"></td>
|
18
|
+
</tr>
|
19
|
+
<tr>
|
20
|
+
<td colspan="2"></td>
|
21
|
+
<td>Subtotal</td>
|
22
|
+
<td><%= number_to_currency order.line_items.inject(0) { |subtotal, li| subtotal + li.total } %></td>
|
23
|
+
</tr>
|
24
|
+
<tr>
|
25
|
+
<td colspan="2"></td>
|
26
|
+
<td>Tax</td>
|
27
|
+
<td><%= number_to_currency order.tax_charge %></td>
|
28
|
+
</tr>
|
29
|
+
<tr>
|
30
|
+
<td colspan="2"></td>
|
31
|
+
<td>Shipping</td>
|
32
|
+
<td><%= number_to_currency order.shipments.inject(0) { |shipping, shipment| shipping + shipment.total } %></td>
|
33
|
+
</tr>
|
34
|
+
<tr>
|
35
|
+
<td colspan="2"></td>
|
36
|
+
<td>Total</td>
|
37
|
+
<td><%= number_to_currency order.total %></td>
|
38
|
+
</tr>
|
39
|
+
</table>
|
40
|
+
|
41
|
+
Email: <%= order.email %><br />
|
42
|
+
Phone: <%= order.phone %>
|
43
|
+
|
44
|
+
<h3>Billing Information</h3>
|
45
|
+
<%= raw order.billing_address.display %>
|
46
|
+
|
47
|
+
<h3>Shipping Information</h3>
|
48
|
+
<%= raw order.shipping_address.display %>
|