effective_orders 1.8.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +150 -60
  4. data/Rakefile +0 -3
  5. data/active_admin/effective_orders.rb +14 -9
  6. data/app/assets/javascripts/effective_orders.js +2 -0
  7. data/app/assets/javascripts/effective_orders/{stripe_charges.js.coffee → providers/stripe_charges.js.coffee} +1 -1
  8. data/app/assets/javascripts/effective_orders/{stripe_subscriptions.js.coffee → providers/stripe_subscriptions.js.coffee} +1 -1
  9. data/app/assets/stylesheets/effective_orders/_order.scss +9 -3
  10. data/app/controllers/admin/orders_controller.rb +87 -3
  11. data/app/controllers/concerns/acts_as_active_admin_controller.rb +2 -1
  12. data/app/controllers/effective/carts_controller.rb +4 -2
  13. data/app/controllers/effective/orders_controller.rb +101 -60
  14. data/app/controllers/effective/providers/app_checkout.rb +10 -2
  15. data/app/controllers/effective/providers/ccbill.rb +34 -0
  16. data/app/controllers/effective/providers/cheque.rb +30 -0
  17. data/app/controllers/effective/providers/moneris.rb +7 -7
  18. data/app/controllers/effective/providers/paypal.rb +7 -5
  19. data/app/controllers/effective/providers/pretend.rb +22 -0
  20. data/app/controllers/effective/providers/stripe.rb +26 -24
  21. data/app/controllers/effective/webhooks_controller.rb +1 -1
  22. data/app/helpers/effective_carts_helper.rb +59 -29
  23. data/app/helpers/effective_ccbill_helper.rb +25 -0
  24. data/app/helpers/effective_orders_helper.rb +50 -41
  25. data/app/helpers/effective_paypal_helper.rb +11 -11
  26. data/app/mailers/effective/orders_mailer.rb +95 -20
  27. data/app/models/concerns/acts_as_purchasable.rb +14 -22
  28. data/app/models/effective/cart.rb +9 -15
  29. data/app/models/effective/cart_item.rb +15 -13
  30. data/app/models/effective/customer.rb +9 -9
  31. data/app/models/effective/datatables/order_items.rb +14 -8
  32. data/app/models/effective/datatables/orders.rb +56 -69
  33. data/app/models/effective/order.rb +250 -139
  34. data/app/models/effective/order_item.rb +23 -16
  35. data/app/models/effective/product.rb +23 -0
  36. data/app/models/effective/providers/ccbill_postback.rb +85 -0
  37. data/app/models/effective/{stripe_charge.rb → providers/stripe_charge.rb} +1 -1
  38. data/app/models/effective/subscription.rb +16 -12
  39. data/app/models/effective/tax_rate_calculator.rb +45 -0
  40. data/app/views/admin/customers/index.html.haml +2 -5
  41. data/app/views/admin/order_items/index.html.haml +2 -5
  42. data/app/views/admin/orders/_actions.html.haml +2 -0
  43. data/app/views/admin/orders/_form.html.haml +28 -0
  44. data/app/views/admin/orders/_order_item_fields.html.haml +9 -0
  45. data/app/views/admin/orders/index.html.haml +9 -5
  46. data/app/views/admin/orders/new.html.haml +3 -0
  47. data/app/views/effective/carts/_cart.html.haml +3 -12
  48. data/app/views/effective/carts/_cart_actions.html.haml +4 -0
  49. data/app/views/effective/carts/show.html.haml +10 -12
  50. data/app/views/effective/orders/_checkout_step1.html.haml +46 -0
  51. data/app/views/effective/orders/_checkout_step2.html.haml +33 -0
  52. data/app/views/effective/orders/_order.html.haml +2 -0
  53. data/app/views/effective/orders/_order_actions.html.haml +10 -5
  54. data/app/views/effective/orders/_order_footer.html.haml +1 -0
  55. data/app/views/effective/orders/_order_items.html.haml +11 -9
  56. data/app/views/effective/orders/_order_note.html.haml +8 -0
  57. data/app/views/effective/orders/_order_note_fields.html.haml +5 -0
  58. data/app/views/effective/orders/_order_shipping.html.haml +4 -4
  59. data/app/views/effective/orders/_order_user_fields.html.haml +1 -0
  60. data/app/views/effective/orders/_orders_table.html.haml +27 -0
  61. data/app/views/effective/orders/ccbill/_form.html.haml +24 -0
  62. data/app/views/effective/orders/checkout_step1.html.haml +3 -0
  63. data/app/views/effective/orders/checkout_step2.html.haml +3 -0
  64. data/app/views/effective/orders/cheque/_form.html.haml +2 -0
  65. data/app/views/effective/orders/cheque/pay_by_cheque.html.haml +9 -0
  66. data/app/views/effective/orders/declined.html.haml +4 -2
  67. data/app/views/effective/orders/my_purchases.html.haml +1 -1
  68. data/app/views/effective/orders/my_sales.html.haml +1 -1
  69. data/app/views/effective/orders/purchased.html.haml +3 -2
  70. data/app/views/effective/orders/show.html.haml +1 -1
  71. data/app/views/effective/orders/stripe/_form.html.haml +5 -5
  72. data/app/views/effective/orders_mailer/order_error.html.haml +11 -0
  73. data/app/views/effective/orders_mailer/payment_request_to_buyer.html.haml +13 -0
  74. data/app/views/effective/orders_mailer/pending_order_invoice_to_buyer.html.haml +13 -0
  75. data/app/views/layouts/effective_orders_mailer_layout.html.haml +7 -7
  76. data/config/routes.rb +39 -24
  77. data/db/migrate/01_create_effective_orders.rb.erb +20 -1
  78. data/db/upgrade/03_upgrade_effective_orders_from1x.rb.erb +95 -0
  79. data/lib/effective_orders.rb +67 -9
  80. data/lib/effective_orders/app_checkout_service.rb +1 -2
  81. data/lib/effective_orders/engine.rb +46 -14
  82. data/lib/effective_orders/version.rb +1 -1
  83. data/lib/generators/effective_orders/install_generator.rb +1 -0
  84. data/lib/generators/effective_orders/upgrade_from03x_generator.rb +1 -0
  85. data/lib/generators/effective_orders/upgrade_from1x_generator.rb +31 -0
  86. data/lib/generators/templates/effective_orders.rb +131 -66
  87. data/lib/generators/templates/effective_orders_mailer_preview.rb +28 -13
  88. data/spec/controllers/admin/orders_controller_spec.rb +242 -0
  89. data/spec/controllers/ccbill_orders_controller_spec.rb +103 -0
  90. data/spec/controllers/moneris_orders_controller_spec.rb +23 -23
  91. data/spec/controllers/orders_controller_spec.rb +167 -79
  92. data/spec/controllers/stripe_orders_controller_spec.rb +7 -7
  93. data/spec/dummy/app/models/product.rb +0 -8
  94. data/spec/dummy/app/models/product_with_float_price.rb +0 -8
  95. data/spec/dummy/app/models/user.rb +2 -19
  96. data/spec/dummy/config/application.rb +2 -1
  97. data/spec/dummy/config/environments/test.rb +1 -0
  98. data/spec/dummy/config/initializers/effective_orders.rb +109 -64
  99. data/spec/dummy/db/schema.rb +15 -2
  100. data/spec/dummy/db/test.sqlite3 +0 -0
  101. data/spec/dummy/log/test.log +0 -258
  102. data/spec/models/acts_as_purchasable_spec.rb +8 -6
  103. data/spec/models/factories_spec.rb +7 -1
  104. data/spec/models/order_item_spec.rb +1 -1
  105. data/spec/models/order_spec.rb +165 -46
  106. data/spec/models/stripe_charge_spec.rb +5 -5
  107. data/spec/spec_helper.rb +2 -0
  108. data/spec/support/factories.rb +49 -33
  109. metadata +47 -64
  110. data/app/views/effective/orders/_checkout_step_1.html.haml +0 -43
  111. data/app/views/effective/orders/_checkout_step_2.html.haml +0 -25
  112. data/app/views/effective/orders/_my_purchases.html.haml +0 -17
  113. data/app/views/effective/orders/checkout.html.haml +0 -3
  114. data/app/views/effective/orders/new.html.haml +0 -4
  115. data/spec/dummy/log/development.log +0 -82
@@ -6,24 +6,27 @@ module Effective
6
6
  belongs_to :purchasable, :polymorphic => true
7
7
  belongs_to :seller, :class_name => 'User'
8
8
 
9
- structure do
10
- title :string, :validates => [:presence]
11
- quantity :integer, :validates => [:presence, :numericality]
12
- price :integer, :default => 0, :validates => [:numericality]
13
- tax_exempt :boolean, :validates => [:inclusion => {:in => [true, false]}]
14
- tax_rate :decimal, :precision => 5, :scale => 3, :default => 0.000, :validates => [:presence]
9
+ delegate :purchased_download_url, :to => :purchasable
10
+ delegate :purchased?, :declined?, :to => :order
15
11
 
16
- timestamps
17
- end
12
+ # structure do
13
+ # title :string
14
+ # quantity :integer
15
+ # price :integer, default: 0
16
+ # tax_exempt :boolean
17
+ #
18
+ # timestamps
19
+ # end
18
20
 
19
- validates_associated :purchasable
20
- validates_presence_of :purchasable
21
- accepts_nested_attributes_for :purchasable, :allow_destroy => false, :reject_if => :all_blank, :update_only => true
21
+ validates :purchasable, associated: true, presence: true
22
+ accepts_nested_attributes_for :purchasable, allow_destroy: false, reject_if: :all_blank, update_only: true
22
23
 
23
- validates_presence_of :seller_id, :if => Proc.new { |order_item| EffectiveOrders.stripe_connect_enabled }
24
+ validates :title, presence: true
25
+ validates :quantity, presence: true, numericality: { greater_than: 0 }
26
+ validates :price, numericality: true
27
+ validates :tax_exempt, inclusion: { in: [true, false] }
24
28
 
25
- delegate :purchased_download_url, :to => :purchasable
26
- delegate :purchased?, :declined?, :to => :order
29
+ validates :seller_id, presence: true, if: Proc.new { |order_item| EffectiveOrders.stripe_connect_enabled }
27
30
 
28
31
  scope :sold, -> { joins(:order).where(:orders => {:purchase_state => EffectiveOrders::PURCHASED}) }
29
32
  scope :sold_by, lambda { |user| sold().where(:seller_id => user.try(:id)) }
@@ -36,11 +39,15 @@ module Effective
36
39
  price * quantity
37
40
  end
38
41
 
39
- def tax # This is the total tax, for 3 items if quantity is 3
40
- tax_exempt ? 0 : (subtotal * tax_rate).round(0).to_i
42
+ def tax
43
+ return 0 if tax_exempt?
44
+ raise 'parent Effective::Order must have a tax_rate to compute order item tax' unless order.try(:tax_rate).present?
45
+ (subtotal * order.tax_rate / 100.0).round(0).to_i
41
46
  end
42
47
 
43
48
  def total
49
+ return subtotal if tax_exempt?
50
+ raise 'parent Effective::Order must have a tax_rate to compute order item total' unless order.try(:tax_rate).present?
44
51
  subtotal + tax
45
52
  end
46
53
 
@@ -0,0 +1,23 @@
1
+ module Effective
2
+ class Product < ActiveRecord::Base
3
+ self.table_name = EffectiveOrders.products_table_name.to_s
4
+
5
+ acts_as_purchasable
6
+
7
+ # structure do
8
+ # title :string
9
+ # price :integer, default: 0
10
+ # tax_exempt :boolean, default: false
11
+ #
12
+ # timestamps
13
+ # end
14
+
15
+ validates :title, presence: true
16
+ validates :price, numericality: { greater_than: 0 }
17
+
18
+ def to_s
19
+ self[:title] || 'New Product'
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,85 @@
1
+ # https://www.ccbill.com/cs/wiki/tiki-index.php?page=Background+Post
2
+ class Effective::Providers::CcbillPostback
3
+ attr_reader :params
4
+
5
+ def initialize(params)
6
+ @params = params
7
+ end
8
+
9
+ def verified?
10
+ params[:responseDigest] == digest(digest_value)
11
+ end
12
+
13
+ def matches?(order)
14
+ price == order.total && order_id == order.to_param
15
+ end
16
+
17
+ def approval?
18
+ !denial?
19
+ end
20
+
21
+ def denial?
22
+ # denialId is for subscriptions only
23
+ [:reasonForDeclineCode, :reasonForDecline, :denialId].any? {|key| params[key].present?}
24
+ end
25
+
26
+ def order_details
27
+ @order_details ||= get_order_details
28
+ end
29
+
30
+ def order_id
31
+ params[:order_id]
32
+ end
33
+
34
+ private
35
+
36
+ # https://www.ccbill.com/cs/wiki/tiki-index.php?page=Dynamic+Pricing+User+Guide#Postback
37
+ def digest_value
38
+ if approval?
39
+ "#{params[:subscription_id]}1#{salt}" # the `subscriptionId` param in the linked docs is a typo
40
+ else
41
+ "#{params[:denialId]}0#{salt}"
42
+ end
43
+ end
44
+
45
+ def digest(value)
46
+ Digest::MD5.hexdigest(value)
47
+ end
48
+
49
+ def salt
50
+ EffectiveOrders.ccbill[:dynamic_pricing_salt]
51
+ end
52
+
53
+ # in cents
54
+ def price
55
+ return @price if @price.present?
56
+ formatted_price = params[:initialFormattedPrice] # example: "&#36;10.00"
57
+ formatted_price = Nokogiri::HTML::DocumentFragment.parse(formatted_price).to_s # => "$10.00"
58
+ match = formatted_price.match(/^\D(\d*.\d\d)$/) # {a currency symbol}(digits.two digits)
59
+ return false unless match.present?
60
+ formatted_price = match[1] # => "10.00"
61
+ @price = formatted_price.sub('.', '').to_i # => 1000
62
+ end
63
+
64
+ def get_order_details
65
+ # ignore some params
66
+ keepable = params.except(*ignorable_params)
67
+ # remove blanks
68
+ keepable.inject({}) do |details, kv_pair|
69
+ details[kv_pair[0]] = kv_pair[1] if kv_pair[1].present?
70
+ details
71
+ end
72
+ end
73
+
74
+ def ignorable_params
75
+ [
76
+ # we have these already
77
+ :clientAccnum,
78
+ :clientSubacc,
79
+ :productDesc,
80
+ # a hash of billing information which CCBill doesn't share the format of (afaik)
81
+ :paymentAccount
82
+ ]
83
+ end
84
+ end
85
+
@@ -1,5 +1,5 @@
1
1
  # This is an object for the stripe charge form
2
- module Effective
2
+ module Effective::Providers
3
3
  class StripeCharge
4
4
  extend ActiveModel::Naming
5
5
  include ActiveModel::Conversion
@@ -8,21 +8,25 @@ module Effective
8
8
 
9
9
  belongs_to :customer
10
10
 
11
- structure do
12
- stripe_plan_id :string, :validates => [:presence] # This will be 'Weekly' or something like that
13
- stripe_subscription_id :string
14
- stripe_coupon_id :string
15
-
16
- title :string, :validates => [:presence]
17
- price :integer, :default => 0, :validates => [:numericality => {:greater_than => 0}]
18
-
19
- timestamps
20
- end
11
+ # structure do
12
+ # stripe_plan_id :string # This will be 'Weekly' or something like that
13
+ # stripe_subscription_id :string
14
+ # stripe_coupon_id :string
15
+ #
16
+ # title :string
17
+ # price :integer, default: 0
18
+ #
19
+ # timestamps
20
+ # end
21
21
 
22
22
  delegate :user, :user_id, :to => :customer
23
23
 
24
- validates_presence_of :customer
25
- validates_uniqueness_of :customer_id, :scope => [:stripe_plan_id] # Can only be on each plan once.
24
+ validates :stripe_plan_id, presence: true
25
+ validates :title, presence: true
26
+ validates :price, numericality: { greater_than: 0 }
27
+
28
+ validates :customer, presence: true
29
+ validates :customer_id, uniqueness: { scope: [:stripe_plan_id] } # Can only be on each plan once.
26
30
 
27
31
  before_validation do
28
32
  self.errors.add(:stripe_plan_id, "is an invalid Plan") if stripe_plan_id.present? && stripe_plan.blank?
@@ -0,0 +1,45 @@
1
+ module Effective
2
+ class TaxRateCalculator
3
+ attr_reader :order
4
+
5
+ RATES = {
6
+ 'CA' => { # Canada
7
+ 'AB' => 5.00, # Alberta
8
+ 'BC' => 5.00, # British Columbia
9
+ 'MB' => 5.00, # Manitoba
10
+ 'NB' => 13.0, # New Brunswick
11
+ 'NL' => 13.0, # Newfoundland and Labrador
12
+ 'NT' => 5.00, # Northwest Territories
13
+ 'NS' => 15.0, # Nova Scotia
14
+ 'ON' => 13.0, # Ontario
15
+ 'PE' => 14.0, # Prince Edward Island
16
+ 'QC' => 5.00, # Quebec
17
+ 'SK' => 5.00, # Saskatchewan
18
+ 'YT' => 5.00, # Yukon Territory
19
+ 'NU' => 5.00 # Nunavut
20
+ }
21
+ }
22
+
23
+ def initialize(order:)
24
+ @order = order
25
+ end
26
+
27
+ def tax_rate
28
+ return nil unless order.billing_address.try(:country_code).present?
29
+
30
+ country = order.billing_address.country_code
31
+ state = order.billing_address.state_code
32
+
33
+ rate = RATES[country]
34
+ return rate if rate.kind_of?(Numeric)
35
+ return unknown_tax_rate() if rate.nil?
36
+
37
+ rate[state].presence || unknown_tax_rate()
38
+ end
39
+
40
+ def unknown_tax_rate
41
+ order.skip_buyer_validations? ? nil : 0
42
+ end
43
+
44
+ end
45
+ end
@@ -1,12 +1,9 @@
1
- %h2.effective-orders-page-title Customers
1
+ %h2.effective-orders-page-title= (@page_title || 'Customers')
2
2
 
3
3
  %p.text-right= link_to 'Stripe Dashboard: Customers', 'https://manage.stripe.com/customers', :class => 'btn btn-primary'
4
4
 
5
5
  - if @datatable.nil?
6
- %p
7
- Please install
8
- = link_to 'effective_datatables', 'https://github.com/code-and-effect/effective_datatables'
9
- to see this page.
6
+ %p Please install #{link_to 'effective_datatables', 'https://github.com/code-and-effect/effective_datatables'} to see this page.
10
7
  - elsif @datatable.present?
11
8
  = render_datatable @datatable
12
9
  - else
@@ -1,10 +1,7 @@
1
- %h2.effective-orders-page-title Order Items
1
+ %h2.effective-orders-page-title= (@page_title || 'Order Items')
2
2
 
3
3
  - if @datatable.nil?
4
- %p
5
- Please install
6
- = link_to 'effective_datatables', 'https://github.com/code-and-effect/effective_datatables'
7
- to see this page.
4
+ %p Please install #{link_to 'effective_datatables', 'https://github.com/code-and-effect/effective_datatables'} to see this page.
8
5
  - elsif @datatable.present?
9
6
  = render_datatable @datatable
10
7
  - else
@@ -0,0 +1,2 @@
1
+ = link_to (datatables_admin_path? ? effective_orders.admin_order_path(order) : effective_orders.order_path(order)), title: 'View' do
2
+ %span.glyphicon.glyphicon-eye-open
@@ -0,0 +1,28 @@
1
+ - unless defined?(Cocoon)
2
+ Please install #{link_to 'cocoon', 'https://github.com/nathanvda/cocoon'} to see this page.
3
+ - else
4
+ = simple_form_for [:admin, order], (EffectiveOrders.admin_simple_form_options || {}).merge(url: effective_orders.admin_orders_path) do |f|
5
+ = f.association :user,
6
+ label: 'Buyer',
7
+ required: true,
8
+ as: (defined?(EffectiveFormInputs) ? :effective_select : :select),
9
+ collection: @users || User.all.to_a.sort { |user1, user2| user1.to_s <=> user2.to_s }
10
+
11
+ %h3 Order Items
12
+ .order_items
13
+ - f.object.order_items.build unless f.object.order_items.present?
14
+ = f.simple_fields_for :order_items do |order_item|
15
+ = render 'order_item_fields', f: order_item
16
+
17
+ .links
18
+ = link_to_add_association '+ Add line item', f, :order_items, partial: 'order_item_fields'
19
+
20
+ = f.input :send_payment_request_to_buyer,
21
+ as: :boolean,
22
+ label: 'Yes, send a payment request email to the buyer.',
23
+ value: (f.object.send_payment_request_to_buyer.nil? ? EffectiveOrders.mailer[:send_payment_request_to_buyer] : f.object.send_payment_request_to_buyer?)
24
+
25
+ .form-group.pull-right
26
+ = f.button :submit, 'Save', data: { disable_with: 'Saving...' }
27
+ = f.button :submit, 'Save and Add New', data: { disable_with: 'Saving...' }
28
+ = link_to 'Cancel', effective_orders.admin_orders_path
@@ -0,0 +1,9 @@
1
+ .nested-fields.order-item
2
+ .row
3
+ - f.object.purchasable ||= Effective::Product.new
4
+ = f.simple_fields_for :purchasable do |purchasable|
5
+ .col-sm-2= f.input :quantity, input_html: { value: f.object.quantity || 1, min: 1 }
6
+ .col-sm-4= purchasable.input :title, input_html: { maxlength: 255 }
7
+ .col-sm-2= purchasable.input :price, as: (defined?(EffectiveFormInputs) ? :effective_price : :price)
8
+ .col-sm-2= purchasable.input :tax_exempt, label: 'Tax Exempt', input_html: { title: 'When checked, tax will not be applied to this item' }
9
+ .col-sm-2.remove-nested-fields= link_to_remove_association('Remove line item', f)
@@ -1,11 +1,15 @@
1
- %h2.effective-orders-page-title Orders
1
+ %h2.effective-orders-page-title= (@page_title || 'Orders')
2
+
3
+ %p.text-right.effective-orders-page-action= link_to 'New Order', effective_orders.new_admin_order_path, :class => 'btn btn-primary'
2
4
 
3
5
  - if @datatable.nil?
4
- %p
5
- Please install
6
- = link_to 'effective_datatables', 'https://github.com/code-and-effect/effective_datatables'
7
- to see this page.
6
+ %p Please install #{link_to 'effective_datatables', 'https://github.com/code-and-effect/effective_datatables'} to see this page.
8
7
  - elsif @datatable.present?
9
8
  = render_datatable @datatable
10
9
  - else
11
10
  %p There are no orders present
11
+
12
+ - if @datatable.present?
13
+ %p.text-right= link_to 'New Order', effective_orders.new_admin_order_path, :class => 'btn btn-primary'
14
+
15
+
@@ -0,0 +1,3 @@
1
+ %h2.effective-orders-page-title= (@page_title || 'New Order')
2
+
3
+ = render partial: 'form', locals: { order: @order }
@@ -19,19 +19,10 @@
19
19
  - if cart.empty?
20
20
  %tr
21
21
  %td.quantity
22
- %td.item There are no items in your shopping cart
22
+ %td.item Your cart is empty.
23
23
  %td.price
24
24
  %tfoot
25
- - if cart.tax > 0
26
- %tr
27
- %th.quantity
28
- %th.subtotal Subtotal
29
- %td.price.subtotal-price= price_to_currency(cart.subtotal)
30
- %tr
31
- %th.quantity
32
- %th.tax Tax
33
- %td.price.tax-price= price_to_currency(cart.tax)
34
25
  %tr
35
26
  %th.quantity
36
- %th.total Total Due
37
- %td.price.total-price= price_to_currency(cart.total)
27
+ %th.subtotal Subtotal
28
+ %td.price.subtotal-price= price_to_currency(cart.subtotal)
@@ -0,0 +1,4 @@
1
+ .effective-order-actions.pull-right
2
+ %p
3
+ = link_to_checkout
4
+ = link_to_empty_cart
@@ -1,20 +1,18 @@
1
- %h2.effective-orders-page-title Shopping Cart
1
+ %h2.effective-orders-page-title= (@page_title || 'My Cart')
2
2
 
3
3
  - if @cart.empty?
4
4
  %p Your cart is empty.
5
-
6
- %hr
7
- = link_to 'Home', root_url, :class => 'btn btn-primary'
8
- - if EffectiveOrders.show_order_history_button
9
- = link_to 'Order History', effective_orders.my_purchases_path, class: 'btn btn-default'
10
-
11
5
  - else
12
6
  %p Your cart has #{@cart.cart_items.length} items.
13
7
 
14
8
  = render_cart(@cart)
15
-
16
9
  %hr
17
- = link_to_checkout :class => 'btn btn-primary pull-right'
18
- = link_to_empty_cart
19
- - if EffectiveOrders.show_order_history_button
20
- = link_to 'Order History', effective_orders.my_purchases_path, class: 'btn btn-default', rel: :nofollow
10
+ = render partial: '/effective/carts/cart_actions', locals: { cart: @cart }
11
+
12
+ - if @pending_orders.present?
13
+ %h3 Pending Orders
14
+ %p The following orders are ready for you to purchase:
15
+ = render_orders_table(@pending_orders)
16
+
17
+ - if EffectiveOrders.show_order_history_button
18
+ = link_to_my_purchases(label: 'Order History')
@@ -0,0 +1,46 @@
1
+ .effective-order
2
+ = simple_form_for(order, (EffectiveOrders.simple_form_options || {}).merge(url: (order.new_record? ? effective_orders.orders_path : effective_orders.order_path(order)))) do |f|
3
+ = render partial: 'effective/orders/order_items', locals: {order: order, form: f}
4
+
5
+ - if order.errors[:order_items].present?
6
+ %p.inline-errors= order.errors[:order_items].first
7
+
8
+ - if order.errors[:total].present?
9
+ %p.inline-errors= order.errors[:total].first.gsub(EffectiveOrders.minimum_charge.to_i.to_s, price_to_currency(EffectiveOrders.minimum_charge.to_i))
10
+
11
+ - if EffectiveOrders.collect_user_fields.present? && (f.object.user rescue nil).present?
12
+ = render partial: 'effective/orders/order_user_fields', locals: {form: f, user: f.object.user }
13
+
14
+ - num_addresses = [EffectiveOrders.require_billing_address, EffectiveOrders.require_shipping_address].count(true)
15
+
16
+ - if num_addresses > 0
17
+ .row
18
+ - if EffectiveOrders.require_billing_address
19
+ %div{class: "col-sm-#{12 / num_addresses}"}
20
+ %h3 Billing Address
21
+ = effective_address_fields(f, :billing_address)
22
+ - if f.object.user.respond_to?(:billing_address=)
23
+ = f.input :save_billing_address, as: :boolean, label: 'Save as my default billing address', required: false
24
+
25
+ - if EffectiveOrders.require_shipping_address
26
+ %div{class: "col-sm-#{12 / num_addresses}"}
27
+ %h3 Shipping Address
28
+ - if EffectiveOrders.require_billing_address && EffectiveOrders.require_shipping_address
29
+ .shipping_address_fields{style: (f.object.shipping_address_same_as_billing? && (f.object.errors[:shipping_address] || []).blank?) ? 'display: none;' : 'display: block;'}
30
+ = effective_address_fields(f, :shipping_address)
31
+ - if f.object.user.respond_to?(:shipping_address=)
32
+ = f.input :save_shipping_address, as: :boolean, label: 'Save as my default shipping address', required: false
33
+
34
+ = f.input :shipping_address_same_as_billing, as: :boolean, label: 'My shipping address is the same as my billing address', required: false
35
+
36
+ - if EffectiveOrders.collect_note
37
+ = render partial: 'effective/orders/order_note_fields', locals: {form: f}
38
+
39
+ - unless f.object.pending? || current_cart.try(:empty?)
40
+ = link_to_current_cart(label: 'Change Items')
41
+
42
+ .pull-right
43
+ - if order.total == 0 && EffectiveOrders.allow_free_orders
44
+ = f.submit order_checkout_label(:free), class: 'btn btn-primary', rel: :nofollow, data: {'disable_with' => 'Processing...' }
45
+ - else
46
+ = f.submit 'Save and Continue', class: 'btn btn-primary', rel: :nofollow, data: {'disable_with' => 'Saving...' }