piggybak 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +32 -25
- data/VERSION +1 -1
- data/app/assets/javascripts/piggybak.js +11 -1
- data/app/assets/javascripts/piggybak.states.js +6 -5
- data/app/controllers/piggybak/orders_controller.rb +14 -22
- data/app/models/piggybak/address.rb +4 -1
- data/app/models/piggybak/adjustment.rb +1 -25
- data/app/models/piggybak/cart.rb +18 -17
- data/app/models/piggybak/line_item.rb +106 -19
- data/app/models/piggybak/order.rb +94 -108
- data/app/models/piggybak/payment.rb +25 -27
- data/app/models/piggybak/payment_method.rb +3 -1
- data/app/models/piggybak/payment_method_value.rb +3 -1
- data/app/models/piggybak/{variant.rb → sellable.rb} +6 -4
- data/app/models/piggybak/shipment.rb +4 -10
- data/app/models/piggybak/shipping_calculator/flat_rate.rb +4 -0
- data/app/models/piggybak/shipping_calculator/free.rb +4 -0
- data/app/models/piggybak/shipping_calculator/range.rb +4 -0
- data/app/models/piggybak/shipping_method.rb +1 -1
- data/app/models/piggybak/tax_method.rb +1 -1
- data/app/views/piggybak/cart/_form.html.erb +7 -7
- data/app/views/piggybak/cart/_items.html.erb +14 -5
- data/app/views/piggybak/notifier/order_notification.text.erb +10 -2
- data/app/views/piggybak/orders/_details.html.erb +14 -5
- data/app/views/piggybak/orders/_google_analytics.html.erb +3 -3
- data/app/views/piggybak/orders/download.text.erb +6 -6
- data/app/views/piggybak/orders/submit.html.erb +55 -49
- data/app/views/rails_admin/main/_location_select.html.haml +3 -3
- data/app/views/rails_admin/main/_order_details.html.erb +12 -24
- data/app/views/rails_admin/main/_polymorphic_nested.html.haml +29 -0
- data/bin/piggybak +12 -0
- data/config/routes.rb +0 -2
- data/db/migrate/20121008160425_rename_variants_to_sellables.rb +11 -0
- data/db/migrate/20121008175144_line_item_rearchitecture.rb +96 -0
- data/lib/acts_as_sellable.rb +21 -0
- data/lib/formatted_changes.rb +2 -2
- data/lib/piggybak.rb +80 -126
- data/lib/piggybak/cli.rb +81 -0
- data/lib/piggybak/config.rb +21 -0
- data/piggybak.gemspec +10 -7
- data/spec/dummy_app/app/models/image.rb +1 -1
- data/spec/factories.rb +1 -1
- metadata +52 -49
- data/app/controllers/piggybak/payments_controller.rb +0 -14
- data/app/views/rails_admin/main/_order_notes.html.erb +0 -1
- data/app/views/rails_admin/main/_payment_refund.html.haml +0 -6
- data/lib/acts_as_variant.rb +0 -15
data/README.md
CHANGED
@@ -11,46 +11,47 @@ Modular / mountable ecommerce gem. Features:
|
|
11
11
|
|
12
12
|
* Fully defined backend RailsAdmin interface for adding orders on the backend
|
13
13
|
|
14
|
+
|
15
|
+
Announcements
|
16
|
+
========
|
17
|
+
|
18
|
+
* Variants were recently changed to sellables, to provide the opportunity for advanced variant support via an extension.
|
19
|
+
|
20
|
+
* Significant recent rearchitecture has been applied to the order line items. Stay tuned for the documentation.
|
21
|
+
|
22
|
+
* Review the new installation process below.
|
23
|
+
|
24
|
+
|
14
25
|
Installation
|
15
26
|
========
|
16
27
|
|
17
|
-
* First
|
28
|
+
* First create a new rails project:
|
29
|
+
rails new webstore
|
30
|
+
|
31
|
+
* Config your database.yml and create the databases
|
32
|
+
|
33
|
+
* Add to Gemfile:
|
18
34
|
|
19
35
|
gem "piggybak"
|
20
|
-
gem "piggybak", '0.4.19'
|
21
|
-
gem "piggybak", :git => "git://github.com/stephskardal/piggybak.git"
|
22
36
|
|
23
|
-
* Next, run
|
37
|
+
* Next, run bundle install:
|
24
38
|
|
25
|
-
|
39
|
+
bundle install
|
26
40
|
|
27
|
-
* Next, run
|
41
|
+
* Next, run the piggybak install command:
|
28
42
|
|
29
|
-
|
43
|
+
piggybak install
|
30
44
|
|
31
|
-
|
45
|
+
(NOTE: If you run into an error saying that piggybak gem is missing, use bundle exec piggybak install)
|
32
46
|
|
33
|
-
|
34
|
-
|
35
|
-
* Add acts_as_variant to any model that will become a sellable item.
|
47
|
+
* Piggybak is now installed and ready to be added to whatever model class will be sold.
|
36
48
|
|
37
49
|
class Product < ActiveRecord::Base
|
38
|
-
|
50
|
+
acts_as_sellable
|
39
51
|
end
|
40
52
|
|
41
|
-
*
|
42
|
-
|
43
|
-
//= require jquery_ujs
|
44
|
-
|
45
|
-
* You must add the following to your application layout:
|
46
|
-
|
47
|
-
<% if "#{params[:controller]}##{params[:action]}" == "piggybak/orders#submit" -%>
|
48
|
-
<%= javascript_include_tag "piggybak-application" %>
|
49
|
-
<% end -%>
|
53
|
+
* Piggybak checkout is located at /checkout
|
50
54
|
|
51
|
-
* And you must add this to your production configuration, in order for this asset to be precompiled (and in some cases, ensure that it is served via SSL):
|
52
|
-
|
53
|
-
config.assets.precompile += %w( piggybak-application.js )
|
54
55
|
|
55
56
|
More Details
|
56
57
|
========
|
@@ -62,7 +63,13 @@ Visit the project website [here][project-website] to see more documentation and
|
|
62
63
|
TODO
|
63
64
|
========
|
64
65
|
|
65
|
-
|
66
|
+
* Ensure that changes in nested addresses are recorded on order notes.
|
67
|
+
|
68
|
+
* Add admin side validation to limit 1 payment at a time
|
69
|
+
|
70
|
+
* Add/check validation to ensure sufficient inventory
|
71
|
+
|
72
|
+
* Add copy from billing above shipping address section button
|
66
73
|
|
67
74
|
Copyright
|
68
75
|
========
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
@@ -4,7 +4,8 @@ var page_load = 1;
|
|
4
4
|
var shipping_field;
|
5
5
|
|
6
6
|
$(function() {
|
7
|
-
|
7
|
+
piggybak.prevent_double_click();
|
8
|
+
shipping_field = $('#piggybak_order_line_items_attributes_0_shipment_attributes_shipping_method_id');
|
8
9
|
shipping_els = $('#piggybak_order_shipping_address_attributes_state_id,#piggybak_order_shipping_address_attributes_country_id,#piggybak_order_shipping_address_attributes_zip');
|
9
10
|
piggybak.initialize_listeners();
|
10
11
|
piggybak.update_shipping_options($('#piggybak_order_shipping_address_attributes_state_id'), function() {
|
@@ -14,6 +15,12 @@ $(function() {
|
|
14
15
|
});
|
15
16
|
|
16
17
|
var piggybak = {
|
18
|
+
prevent_double_click: function() {
|
19
|
+
$('#new_piggybak_order').find('input:submit').removeAttr('disabled');
|
20
|
+
$('#new_piggybak_order').submit(function() {
|
21
|
+
$(this).find('input:submit').attr('disabled', 'disabled');
|
22
|
+
});
|
23
|
+
},
|
17
24
|
initialize_listeners: function() {
|
18
25
|
shipping_els.live('change', function() {
|
19
26
|
piggybak.update_shipping_options($(this));
|
@@ -117,6 +124,9 @@ var piggybak = {
|
|
117
124
|
}
|
118
125
|
$('#shipping_total').html('$' + shipping_total.toFixed(2));
|
119
126
|
var order_total = subtotal + tax_total + shipping_total;
|
127
|
+
$.each($('.extra_totals'), function(i, el) {
|
128
|
+
order_total += parseFloat($(el).html().replace(/\$/, ''));
|
129
|
+
});
|
120
130
|
$('#order_total').html('$' + order_total.toFixed(2));
|
121
131
|
},
|
122
132
|
retrieve_shipping_data: function() {
|
@@ -1,10 +1,5 @@
|
|
1
1
|
var geodata;
|
2
2
|
|
3
|
-
$(function() {
|
4
|
-
piggybak_states.populate_geodata();
|
5
|
-
piggybak_states.initialize_listeners();
|
6
|
-
});
|
7
|
-
|
8
3
|
var piggybak_states = {
|
9
4
|
initialize_listeners: function() {
|
10
5
|
$('#piggybak_order_shipping_address_attributes_country_id').change(function() {
|
@@ -53,3 +48,9 @@ var piggybak_states = {
|
|
53
48
|
return;
|
54
49
|
}
|
55
50
|
};
|
51
|
+
|
52
|
+
$(function() {
|
53
|
+
piggybak_states.populate_geodata();
|
54
|
+
piggybak_states.initialize_listeners();
|
55
|
+
});
|
56
|
+
|
@@ -10,12 +10,14 @@ module Piggybak
|
|
10
10
|
begin
|
11
11
|
ActiveRecord::Base.transaction do
|
12
12
|
@order = Piggybak::Order.new(params[:piggybak_order])
|
13
|
+
@order.create_payment_shipment
|
13
14
|
|
14
15
|
if Piggybak.config.logging
|
15
|
-
|
16
|
-
clean_params
|
17
|
-
clean_params["payments_attributes"]["0"]["
|
18
|
-
|
16
|
+
# TODO: Reimplement on correctly filtered params
|
17
|
+
#clean_params = params[:piggybak_order].clone
|
18
|
+
#clean_params["payments_attributes"]["0"]["number"] = clean_params["payments_attributes"]["0"]["number"].mask_cc_number
|
19
|
+
#clean_params["payments_attributes"]["0"]["verification_value"] = clean_params["payments_attributes"]["0"]["verification_value"].mask_csv
|
20
|
+
#logger.info "#{request.remote_ip}:#{Time.now.strftime("%Y-%m-%d %H:%M")} Order received with params #{clean_params.inspect}"
|
19
21
|
end
|
20
22
|
@order.initialize_user(current_user, true)
|
21
23
|
|
@@ -28,6 +30,7 @@ module Piggybak
|
|
28
30
|
end
|
29
31
|
|
30
32
|
if @order.save
|
33
|
+
# TODO: Imporant: figure out how to have notifications not trigger rollback here. Instead log failed order notification sent.
|
31
34
|
Piggybak::Notifier.order_notification(@order).deliver
|
32
35
|
|
33
36
|
if Piggybak.config.logging
|
@@ -52,8 +55,9 @@ module Piggybak
|
|
52
55
|
@order.errors[:base] << "Your order could not go through. Please try again."
|
53
56
|
end
|
54
57
|
end
|
55
|
-
|
58
|
+
else
|
56
59
|
@order = Piggybak::Order.new
|
60
|
+
@order.create_payment_shipment
|
57
61
|
@order.initialize_user(current_user, false)
|
58
62
|
end
|
59
63
|
end
|
@@ -95,18 +99,6 @@ module Piggybak
|
|
95
99
|
redirect_to rails_admin.edit_path('Piggybak::Order', order.id)
|
96
100
|
end
|
97
101
|
|
98
|
-
def restore
|
99
|
-
order = Order.find(params[:id])
|
100
|
-
order.recorded_changer = current_user.id
|
101
|
-
|
102
|
-
if can?(:restore, order)
|
103
|
-
order.status = "new"
|
104
|
-
order.save
|
105
|
-
end
|
106
|
-
|
107
|
-
redirect_to rails_admin.edit_path('Piggybak::Order', order.id)
|
108
|
-
end
|
109
|
-
|
110
102
|
def cancel
|
111
103
|
order = Order.find(params[:id])
|
112
104
|
|
@@ -115,18 +107,18 @@ module Piggybak
|
|
115
107
|
order.disable_order_notes = true
|
116
108
|
|
117
109
|
order.line_items.each do |line_item|
|
118
|
-
line_item.
|
119
|
-
|
120
|
-
|
121
|
-
shipment.mark_for_destruction
|
110
|
+
if line_item.line_item_type != "payment"
|
111
|
+
line_item.mark_for_destruction
|
112
|
+
end
|
122
113
|
end
|
123
|
-
order.update_attribute(:tax_charge, 0.00)
|
124
114
|
order.update_attribute(:total, 0.00)
|
125
115
|
order.update_attribute(:to_be_cancelled, true)
|
126
116
|
|
127
117
|
OrderNote.create(:order_id => order.id, :note => "Order set to cancelled. Line items, shipments, tax removed.", :user_id => current_user.id)
|
128
118
|
|
129
119
|
flash[:notice] = "Order #{order.id} set to cancelled. Order is now in unbalanced state."
|
120
|
+
else
|
121
|
+
flash[:error] = "You do not have permission to cancel this order."
|
130
122
|
end
|
131
123
|
|
132
124
|
redirect_to rails_admin.edit_path('Piggybak::Order', order.id)
|
@@ -15,7 +15,10 @@ module Piggybak
|
|
15
15
|
|
16
16
|
after_initialize :set_default_country
|
17
17
|
after_save :document_address_changes
|
18
|
-
|
18
|
+
|
19
|
+
attr_accessible :firstname, :lastname, :address1, :location,
|
20
|
+
:address2, :city, :state_id, :zip, :country_id
|
21
|
+
|
19
22
|
def set_default_country
|
20
23
|
self.country ||= Country.find_by_abbr(Piggybak.config.default_country)
|
21
24
|
end
|
@@ -1,29 +1,5 @@
|
|
1
1
|
module Piggybak
|
2
2
|
class Adjustment < ActiveRecord::Base
|
3
|
-
|
4
|
-
belongs_to :source, :polymorphic => true
|
5
|
-
attr_accessor :user_id
|
6
|
-
acts_as_changer
|
7
|
-
|
8
|
-
validates_presence_of :order_id, :total
|
9
|
-
validates_numericality_of :total
|
10
|
-
validates_presence_of :source
|
11
|
-
|
12
|
-
before_validation :set_source
|
13
|
-
|
14
|
-
def set_source
|
15
|
-
if self.source.nil? && self.user_id.present?
|
16
|
-
self.source = User.find(self.user_id)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def admin_label
|
21
|
-
if !self.new_record?
|
22
|
-
return "Adjustment ##{self.id} (#{self.created_at.strftime("%m-%d-%Y")}): " +
|
23
|
-
"$#{"%.2f" % self.total}"
|
24
|
-
else
|
25
|
-
return ""
|
26
|
-
end
|
27
|
-
end
|
3
|
+
#TODO: Note this is deprecated after 0.5.5
|
28
4
|
end
|
29
5
|
end
|
data/app/models/piggybak/cart.rb
CHANGED
@@ -4,18 +4,19 @@ module Piggybak
|
|
4
4
|
attr_accessor :total
|
5
5
|
attr_accessor :errors
|
6
6
|
attr_accessor :extra_data
|
7
|
+
alias :subtotal :total
|
7
8
|
|
8
9
|
def initialize(cookie='')
|
9
10
|
self.items = []
|
10
11
|
self.errors = []
|
11
12
|
cookie ||= ''
|
12
13
|
cookie.split(';').each do |item|
|
13
|
-
|
14
|
-
if
|
15
|
-
self.items << { :
|
14
|
+
item_sellable = Piggybak::Sellable.find_by_id(item.split(':')[0])
|
15
|
+
if item_sellable.present?
|
16
|
+
self.items << { :sellable => item_sellable, :quantity => (item.split(':')[1]).to_i }
|
16
17
|
end
|
17
18
|
end
|
18
|
-
self.total = self.items.sum { |item| item[:quantity]*item[:
|
19
|
+
self.total = self.items.sum { |item| item[:quantity]*item[:sellable].price }
|
19
20
|
|
20
21
|
self.extra_data = {}
|
21
22
|
end
|
@@ -38,14 +39,14 @@ module Piggybak
|
|
38
39
|
|
39
40
|
def self.add(cookie, params)
|
40
41
|
cart = to_hash(cookie)
|
41
|
-
cart["#{params[:
|
42
|
-
cart["#{params[:
|
42
|
+
cart["#{params[:sellable_id]}"] ||= 0
|
43
|
+
cart["#{params[:sellable_id]}"] += params[:quantity].to_i
|
43
44
|
to_string(cart)
|
44
45
|
end
|
45
46
|
|
46
|
-
def self.remove(cookie,
|
47
|
+
def self.remove(cookie, sellable_id)
|
47
48
|
cart = to_hash(cookie)
|
48
|
-
cart[
|
49
|
+
cart[sellable_id] = 0
|
49
50
|
to_string(cart)
|
50
51
|
end
|
51
52
|
|
@@ -58,7 +59,7 @@ module Piggybak
|
|
58
59
|
def to_cookie
|
59
60
|
cookie = ''
|
60
61
|
self.items.each do |item|
|
61
|
-
cookie += "#{item[:
|
62
|
+
cookie += "#{item[:sellable].id.to_s}:#{item[:quantity].to_s};" if item[:quantity].to_i > 0
|
62
63
|
end
|
63
64
|
cookie
|
64
65
|
end
|
@@ -67,20 +68,20 @@ module Piggybak
|
|
67
68
|
self.errors = []
|
68
69
|
new_items = []
|
69
70
|
self.items.each do |item|
|
70
|
-
if !item[:
|
71
|
-
self.errors << ["Sorry, #{item[:
|
72
|
-
elsif item[:
|
71
|
+
if !item[:sellable].active
|
72
|
+
self.errors << ["Sorry, #{item[:sellable].description} is no longer for sale"]
|
73
|
+
elsif item[:sellable].unlimited_inventory || item[:sellable].quantity >= item[:quantity]
|
73
74
|
new_items << item
|
74
|
-
elsif item[:
|
75
|
-
self.errors << ["Sorry, #{item[:
|
75
|
+
elsif item[:sellable].quantity == 0
|
76
|
+
self.errors << ["Sorry, #{item[:sellable].description} is no longer available"]
|
76
77
|
else
|
77
|
-
self.errors << ["Sorry, only #{item[:
|
78
|
-
item[:quantity] = item[:
|
78
|
+
self.errors << ["Sorry, only #{item[:sellable].quantity} available for #{item[:sellable].description}"]
|
79
|
+
item[:quantity] = item[:sellable].quantity
|
79
80
|
new_items << item if item[:quantity] > 0
|
80
81
|
end
|
81
82
|
end
|
82
83
|
self.items = new_items
|
83
|
-
self.total = self.items.sum { |item| item[:quantity]*item[:
|
84
|
+
self.total = self.items.sum { |item| item[:quantity]*item[:sellable].price }
|
84
85
|
end
|
85
86
|
|
86
87
|
def set_extra_data(form_params)
|
@@ -2,39 +2,126 @@ module Piggybak
|
|
2
2
|
class LineItem < ActiveRecord::Base
|
3
3
|
belongs_to :order
|
4
4
|
acts_as_changer
|
5
|
-
belongs_to :
|
6
|
-
|
7
|
-
validates_presence_of :
|
8
|
-
validates_presence_of :total
|
9
|
-
validates_presence_of :price
|
10
|
-
validates_presence_of :description
|
11
|
-
validates_presence_of :quantity
|
5
|
+
belongs_to :sellable
|
6
|
+
|
7
|
+
validates_presence_of :price, :description, :quantity
|
12
8
|
validates_numericality_of :quantity, :only_integer => true, :greater_than_or_equal_to => 0
|
13
9
|
|
14
|
-
after_create :decrease_inventory, :if => Proc.new { |line_item| !line_item.
|
15
|
-
after_destroy :increase_inventory, :if => Proc.new { |line_item| !line_item.
|
16
|
-
after_update :update_inventory, :if => Proc.new { |line_item| !line_item.
|
17
|
-
|
10
|
+
after_create :decrease_inventory, :if => Proc.new { |line_item| line_item.line_item_type == 'sellable' && !line_item.sellable.unlimited_inventory }
|
11
|
+
after_destroy :increase_inventory, :if => Proc.new { |line_item| line_item.line_item_type == 'sellable' && !line_item.sellable.unlimited_inventory }
|
12
|
+
after_update :update_inventory, :if => Proc.new { |line_item| line_item.line_item_type == 'sellable' && !line_item.sellable.unlimited_inventory }
|
13
|
+
|
14
|
+
attr_accessible :sellable_id, :price, :unit_price, :description, :quantity, :line_item_type
|
15
|
+
|
16
|
+
after_initialize :initialize_line_item
|
17
|
+
before_validation :preprocess
|
18
|
+
before_destroy :destroy_associated_item
|
19
|
+
|
20
|
+
def initialize_line_item
|
21
|
+
self.quantity ||= 1
|
22
|
+
self.price ||= 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def preprocess
|
26
|
+
# TODO: Investigate if this is unnecessary if you use reject_if on accepts_nested_attributes_for
|
27
|
+
Piggybak.config.line_item_types.each do |k, v|
|
28
|
+
if v.has_key?(:nested_attrs) && k != self.line_item_type.to_sym
|
29
|
+
self.send("#{k}=", nil)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
method = "preprocess_#{self.line_item_type}"
|
34
|
+
self.send(method) if self.respond_to?(method)
|
35
|
+
end
|
36
|
+
|
37
|
+
def preprocess_sellable
|
38
|
+
sellable = Piggybak::Sellable.find(self.sellable_id)
|
39
|
+
|
40
|
+
return if sellable.nil?
|
41
|
+
|
42
|
+
self.description = sellable.description
|
43
|
+
self.unit_price = sellable.price
|
44
|
+
self.price = self.unit_price*self.quantity.to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
def preprocess_shipment
|
48
|
+
if !self._destroy
|
49
|
+
if (self.new_record? || self.shipment.status != 'shipped') && self.shipment && self.shipment.shipping_method
|
50
|
+
calculator = self.shipment.shipping_method.klass.constantize
|
51
|
+
self.price = calculator.rate(self.shipment.shipping_method, self)
|
52
|
+
self.price = ((self.price*100).to_i).to_f/100
|
53
|
+
self.description = self.shipment.shipping_method.description
|
54
|
+
end
|
55
|
+
if self.shipment.nil? || self.shipment.shipping_method.nil?
|
56
|
+
self.price = 0.00
|
57
|
+
self.description = "Shipping"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def preprocess_payment
|
63
|
+
if self.new_record?
|
64
|
+
self.payment.payment_method_id ||= Piggybak::PaymentMethod.find_by_active(true).id if self.payment
|
65
|
+
self.description = "Payment"
|
66
|
+
self.price = 0
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def postprocess_payment
|
71
|
+
return true if !self.new_record?
|
72
|
+
|
73
|
+
if self.payment.process(self.order)
|
74
|
+
self.price = -1*self.order.total_due
|
75
|
+
self.order.total_due = 0
|
76
|
+
return true
|
77
|
+
else
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Dependent destroy is not working as expected, so this is in place
|
83
|
+
def destroy_associated_item
|
84
|
+
line_item_type_sym = self.line_item_type.to_sym
|
85
|
+
if Piggybak.config.line_item_types[line_item_type_sym].has_key?(:nested_attrs)
|
86
|
+
if Piggybak.config.line_item_types[line_item_type_sym][:nested_attrs]
|
87
|
+
b = self.send("#{line_item_type_sym}")
|
88
|
+
b.destroy if b.present?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.line_item_type_select
|
94
|
+
Piggybak.config.line_item_types.select { |k, v| v[:visible] }.collect { |k, v| [k.to_s.humanize.titleize, k] }
|
95
|
+
end
|
96
|
+
|
97
|
+
def sellable_id_enum
|
98
|
+
::Piggybak::Sellable.all.collect { |s| ["#{s.description}: $#{s.price}", s.id ] }
|
99
|
+
end
|
100
|
+
|
18
101
|
def admin_label
|
19
|
-
|
102
|
+
if self.line_item_type == 'sellable'
|
103
|
+
"#{self.quantity} x #{self.description} ($#{sprintf("%.2f", self.unit_price)}): $#{sprintf("%.2f", self.price)}".gsub('"', '"')
|
104
|
+
else
|
105
|
+
"#{self.description}: $#{sprintf("%.2f", self.price)}".gsub('"', '"')
|
106
|
+
end
|
20
107
|
end
|
21
108
|
|
22
109
|
def decrease_inventory
|
23
|
-
self.
|
110
|
+
self.sellable.update_inventory(-1 * self.quantity)
|
24
111
|
end
|
25
112
|
|
26
113
|
def increase_inventory
|
27
|
-
self.
|
114
|
+
self.sellable.update_inventory(self.quantity)
|
28
115
|
end
|
29
116
|
|
30
117
|
def update_inventory
|
31
|
-
if self.
|
32
|
-
|
33
|
-
|
34
|
-
self.
|
118
|
+
if self.sellable_id != self.sellable_id_was
|
119
|
+
old_sellable = Sellable.find(self.sellable_id_was)
|
120
|
+
old_sellable.update_inventory(self.quantity_was)
|
121
|
+
self.sellable.update_inventory(-1*self.quantity)
|
35
122
|
else
|
36
123
|
quantity_diff = self.quantity_was - self.quantity
|
37
|
-
self.
|
124
|
+
self.sellable.update_inventory(quantity_diff)
|
38
125
|
end
|
39
126
|
end
|
40
127
|
end
|