effective_orders 4.6.2 → 5.0.3
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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +14 -86
- data/app/controllers/admin/customers_controller.rb +5 -16
- data/app/controllers/admin/order_items_controller.rb +6 -9
- data/app/controllers/admin/orders_controller.rb +18 -82
- data/app/controllers/effective/carts_controller.rb +10 -6
- data/app/controllers/effective/concerns/purchase.rb +12 -19
- data/app/controllers/effective/customers_controller.rb +4 -2
- data/app/controllers/effective/orders_controller.rb +29 -26
- data/app/controllers/effective/providers/cheque.rb +3 -1
- data/app/controllers/effective/providers/free.rb +3 -2
- data/app/controllers/effective/providers/mark_as_paid.rb +5 -4
- data/app/controllers/effective/providers/moneris.rb +3 -1
- data/app/controllers/effective/providers/paypal.rb +3 -2
- data/app/controllers/effective/providers/phone.rb +3 -1
- data/app/controllers/effective/providers/pretend.rb +4 -3
- data/app/controllers/effective/providers/refund.rb +4 -3
- data/app/controllers/effective/providers/stripe.rb +4 -3
- data/app/controllers/effective/subscripter_controller.rb +4 -2
- data/app/controllers/effective/webhooks_controller.rb +12 -3
- data/app/datatables/admin/effective_customers_datatable.rb +7 -3
- data/app/datatables/admin/effective_orders_datatable.rb +2 -2
- data/app/datatables/effective_orders_datatable.rb +1 -3
- data/app/helpers/effective_orders_helper.rb +1 -7
- data/app/mailers/effective/orders_mailer.rb +131 -96
- data/app/models/concerns/acts_as_purchasable.rb +0 -11
- data/app/models/concerns/acts_as_subscribable.rb +0 -6
- data/app/models/effective/cart.rb +7 -5
- data/app/models/effective/cart_item.rb +7 -4
- data/app/models/effective/customer.rb +7 -6
- data/app/models/effective/order.rb +61 -61
- data/app/models/effective/order_item.rb +20 -8
- data/app/models/effective/product.rb +11 -6
- data/app/models/effective/subscription.rb +13 -12
- data/app/views/admin/orders/_form.html.haml +5 -9
- data/app/views/admin/orders/_order_item_fields.html.haml +8 -12
- data/app/views/effective/orders/_checkout_step2.html.haml +1 -2
- data/app/views/effective/orders/_order_actions.html.haml +2 -2
- data/app/views/effective/orders/show.html.haml +4 -0
- data/config/effective_orders.rb +8 -32
- data/config/routes.rb +16 -17
- data/db/migrate/01_create_effective_orders.rb.erb +4 -0
- data/lib/effective_orders.rb +34 -76
- data/lib/effective_orders/engine.rb +0 -7
- data/lib/effective_orders/version.rb +1 -1
- data/lib/generators/templates/effective_orders_mailer_preview.rb +13 -13
- data/lib/tasks/effective_orders_tasks.rake +2 -2
- metadata +2 -3
- data/app/models/effective/access_denied.rb +0 -17
@@ -4,17 +4,6 @@ module ActsAsPurchasable
|
|
4
4
|
module Base
|
5
5
|
def acts_as_purchasable(*options)
|
6
6
|
@acts_as_purchasable = options || []
|
7
|
-
|
8
|
-
# if table_exists?
|
9
|
-
# instance = new()
|
10
|
-
# raise 'must respond_to price' unless instance.respond_to?(:price)
|
11
|
-
# raise 'must respond_to purchased_order_id' unless instance.respond_to?(:purchased_order_id)
|
12
|
-
|
13
|
-
# if defined?(EffectiveQbSync)
|
14
|
-
# raise 'must respond to qb_item_name' unless instance.respond_to?(:qb_item_name)
|
15
|
-
# end
|
16
|
-
# end
|
17
|
-
|
18
7
|
include ::ActsAsPurchasable
|
19
8
|
end
|
20
9
|
end
|
@@ -7,12 +7,6 @@ module ActsAsSubscribable
|
|
7
7
|
def acts_as_subscribable(*options)
|
8
8
|
@acts_as_subscribable = options || []
|
9
9
|
|
10
|
-
# if table_exists?
|
11
|
-
# instance = new()
|
12
|
-
# raise 'must respond to trialing_until' unless instance.respond_to?(:trialing_until) || !EffectiveOrders.trial?
|
13
|
-
# raise 'must respond to subscription_status' unless instance.respond_to?(:subscription_status)
|
14
|
-
# end
|
15
|
-
|
16
10
|
include ::ActsAsSubscribable
|
17
11
|
(ActsAsSubscribable.descendants ||= []) << self
|
18
12
|
end
|
@@ -2,14 +2,16 @@ module Effective
|
|
2
2
|
class Cart < ActiveRecord::Base
|
3
3
|
self.table_name = EffectiveOrders.carts_table_name.to_s
|
4
4
|
|
5
|
-
belongs_to :user, optional: true
|
6
|
-
has_many :cart_items, -> { order(:id) }, dependent: :delete_all, class_name: 'Effective::CartItem'
|
5
|
+
belongs_to :user, polymorphic: true, optional: true # Optional. We want non-logged-in users to have carts too.
|
7
6
|
|
7
|
+
has_many :cart_items, -> { order(:id) }, inverse_of: :cart, dependent: :delete_all
|
8
8
|
accepts_nested_attributes_for :cart_items
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
effective_resource do
|
11
|
+
cart_items_count :integer
|
12
|
+
|
13
|
+
timestamps
|
14
|
+
end
|
13
15
|
|
14
16
|
scope :deep, -> { includes(cart_items: :purchasable) }
|
15
17
|
|
@@ -2,12 +2,15 @@ module Effective
|
|
2
2
|
class CartItem < ActiveRecord::Base
|
3
3
|
self.table_name = EffectiveOrders.cart_items_table_name.to_s
|
4
4
|
|
5
|
-
belongs_to :cart, counter_cache: true
|
5
|
+
belongs_to :cart, counter_cache: true
|
6
6
|
belongs_to :purchasable, polymorphic: true
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
effective_resource do
|
9
|
+
unique :string
|
10
|
+
quantity :integer
|
11
|
+
|
12
|
+
timestamps
|
13
|
+
end
|
11
14
|
|
12
15
|
validates :purchasable, presence: true
|
13
16
|
validates :quantity, presence: true
|
@@ -4,16 +4,17 @@ module Effective
|
|
4
4
|
|
5
5
|
attr_accessor :stripe_customer
|
6
6
|
|
7
|
-
belongs_to :user
|
7
|
+
belongs_to :user, polymorphic: true
|
8
8
|
has_many :subscriptions, -> { includes(:subscribable) }, class_name: 'Effective::Subscription', foreign_key: 'customer_id'
|
9
9
|
accepts_nested_attributes_for :subscriptions
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
effective_resource do
|
12
|
+
stripe_customer_id :string # cus_xja7acoa03
|
13
|
+
payment_method_id :string # Last payment method used
|
14
|
+
active_card :string # **** **** **** 4242 Visa 05/12
|
15
15
|
|
16
|
-
|
16
|
+
timestamps
|
17
|
+
end
|
17
18
|
|
18
19
|
scope :deep, -> { includes(subscriptions: :subscribable) }
|
19
20
|
|
@@ -31,35 +31,37 @@ module Effective
|
|
31
31
|
# If we want to use orders in a has_many way
|
32
32
|
belongs_to :parent, polymorphic: true, optional: true
|
33
33
|
|
34
|
-
belongs_to :user, validate: false # This is the buyer/user of the order. We validate it below.
|
35
|
-
has_many :order_items, -> { order(:id) }, inverse_of: :order,
|
34
|
+
belongs_to :user, polymorphic: true, validate: false # This is the buyer/user of the order. We validate it below.
|
35
|
+
has_many :order_items, -> { order(:id) }, inverse_of: :order, dependent: :delete_all
|
36
36
|
|
37
37
|
accepts_nested_attributes_for :order_items, allow_destroy: false, reject_if: :all_blank
|
38
38
|
accepts_nested_attributes_for :user, allow_destroy: false, update_only: true
|
39
39
|
|
40
40
|
# Attributes
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
41
|
+
effective_resource do
|
42
|
+
state :string
|
43
|
+
purchased_at :datetime
|
44
|
+
|
45
|
+
note :text # From buyer to admin
|
46
|
+
note_to_buyer :text # From admin to buyer
|
47
|
+
note_internal :text # Internal admin only
|
48
|
+
|
49
|
+
billing_name :string # name of buyer
|
50
|
+
email :string # same as user.email
|
51
|
+
cc :string # can be set by admin
|
52
|
+
|
53
|
+
payment :text # serialized hash containing all the payment details.
|
54
|
+
payment_provider :string
|
55
|
+
payment_card :string
|
56
|
+
|
57
|
+
tax_rate :decimal, precision: 6, scale: 3
|
58
|
+
|
59
|
+
subtotal :integer
|
60
|
+
tax :integer
|
61
|
+
total :integer
|
62
|
+
|
63
|
+
timestamps
|
64
|
+
end
|
63
65
|
|
64
66
|
serialize :payment, Hash
|
65
67
|
|
@@ -73,7 +75,7 @@ module Effective
|
|
73
75
|
end
|
74
76
|
|
75
77
|
before_save(if: -> { state_was == EffectiveOrders::PURCHASED }) do
|
76
|
-
raise
|
78
|
+
raise EffectiveOrders::AlreadyPurchasedException.new('cannot unpurchase an order') unless purchased?
|
77
79
|
end
|
78
80
|
|
79
81
|
# Order validations
|
@@ -314,15 +316,15 @@ module Effective
|
|
314
316
|
end
|
315
317
|
|
316
318
|
def send_payment_request_to_buyer?
|
317
|
-
truthy?(send_payment_request_to_buyer) && !free? && !refund?
|
319
|
+
EffectiveResources.truthy?(send_payment_request_to_buyer) && !free? && !refund?
|
318
320
|
end
|
319
321
|
|
320
322
|
def send_mark_as_paid_email_to_buyer?
|
321
|
-
truthy?(send_mark_as_paid_email_to_buyer)
|
323
|
+
EffectiveResources.truthy?(send_mark_as_paid_email_to_buyer)
|
322
324
|
end
|
323
325
|
|
324
326
|
def skip_buyer_validations?
|
325
|
-
truthy?(skip_buyer_validations)
|
327
|
+
EffectiveResources.truthy?(skip_buyer_validations)
|
326
328
|
end
|
327
329
|
|
328
330
|
# This is called from admin/orders#create
|
@@ -330,6 +332,8 @@ module Effective
|
|
330
332
|
# It skips any address or bad user validations
|
331
333
|
# It's basically the same as save! on a new order, except it might send the payment request to buyer
|
332
334
|
def pending!
|
335
|
+
return false if purchased?
|
336
|
+
|
333
337
|
self.state = EffectiveOrders::PENDING
|
334
338
|
self.addresses.clear if addresses.any? { |address| address.valid? == false }
|
335
339
|
save!
|
@@ -340,6 +344,7 @@ module Effective
|
|
340
344
|
|
341
345
|
# Used by admin checkout only
|
342
346
|
def confirm!
|
347
|
+
return false if purchased?
|
343
348
|
update!(state: EffectiveOrders::CONFIRMED)
|
344
349
|
end
|
345
350
|
|
@@ -357,38 +362,31 @@ module Effective
|
|
357
362
|
|
358
363
|
# Effective::Order.new(items: Product.first, user: User.first).purchase!(email: false)
|
359
364
|
def purchase!(payment: 'none', provider: 'none', card: 'none', email: true, skip_buyer_validations: false)
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
assign_attributes(
|
364
|
-
state: EffectiveOrders::PURCHASED,
|
365
|
-
payment: payment_to_h(payment),
|
366
|
-
payment_provider: provider,
|
367
|
-
payment_card: (card.presence || 'none'),
|
368
|
-
skip_buyer_validations: skip_buyer_validations
|
369
|
-
)
|
365
|
+
# Assign attributes
|
366
|
+
self.state = EffectiveOrders::PURCHASED
|
367
|
+
self.skip_buyer_validations = skip_buyer_validations
|
370
368
|
|
369
|
+
self.payment_provider ||= provider
|
370
|
+
self.payment_card ||= (card.presence || 'none')
|
371
371
|
self.purchased_at ||= Time.zone.now
|
372
|
+
self.payment = payment_to_h(payment) if self.payment.blank?
|
372
373
|
|
373
|
-
|
374
|
-
|
374
|
+
begin
|
375
|
+
Effective::Order.transaction do
|
375
376
|
run_purchasable_callbacks(:before_purchase)
|
376
377
|
save!
|
377
378
|
update_purchasables_purchased_order!
|
378
|
-
rescue => e
|
379
|
-
self.state = state_was
|
380
|
-
self.purchased_at = nil
|
381
|
-
|
382
|
-
error = e.message
|
383
|
-
raise ::ActiveRecord::Rollback
|
384
379
|
end
|
385
|
-
|
380
|
+
rescue => e
|
381
|
+
Effective::Order.transaction do
|
382
|
+
save!(validate: false)
|
383
|
+
update_purchasables_purchased_order!
|
384
|
+
end
|
386
385
|
|
387
|
-
|
386
|
+
raise(e)
|
387
|
+
end
|
388
388
|
|
389
389
|
run_purchasable_callbacks(:after_purchase)
|
390
|
-
|
391
|
-
send_refund_notification! if email && refund?
|
392
390
|
send_order_receipts! if email
|
393
391
|
|
394
392
|
true
|
@@ -451,6 +449,7 @@ module Effective
|
|
451
449
|
def send_order_receipts!
|
452
450
|
send_order_receipt_to_admin! if EffectiveOrders.mailer[:send_order_receipt_to_admin]
|
453
451
|
send_order_receipt_to_buyer! if EffectiveOrders.mailer[:send_order_receipt_to_buyer]
|
452
|
+
send_refund_notification! if refund?
|
454
453
|
end
|
455
454
|
|
456
455
|
def send_order_receipt_to_admin!
|
@@ -474,7 +473,7 @@ module Effective
|
|
474
473
|
end
|
475
474
|
|
476
475
|
def skip_qb_sync!
|
477
|
-
|
476
|
+
EffectiveOrders.use_effective_qb_sync ? EffectiveQbSync.skip_order!(self) : true
|
478
477
|
end
|
479
478
|
|
480
479
|
protected
|
@@ -541,22 +540,23 @@ module Effective
|
|
541
540
|
order_items.each { |oi| oi.purchasable.public_send(name, self, oi) if oi.purchasable.respond_to?(name) }
|
542
541
|
end
|
543
542
|
|
544
|
-
def send_email(email, *
|
543
|
+
def send_email(email, *args)
|
544
|
+
raise('expected args to be an Array') unless args.kind_of?(Array)
|
545
|
+
|
546
|
+
if defined?(Tenant)
|
547
|
+
tenant = Tenant.current || raise('expected a current tenant')
|
548
|
+
args << { tenant: tenant }
|
549
|
+
end
|
550
|
+
|
551
|
+
deliver_method = EffectiveOrders.mailer[:deliver_method] || EffectiveResources.deliver_method
|
552
|
+
|
545
553
|
begin
|
546
|
-
|
554
|
+
EffectiveOrders.mailer_klass.send(email, *args).send(deliver_method)
|
547
555
|
rescue => e
|
548
556
|
raise if Rails.env.development? || Rails.env.test?
|
549
557
|
end
|
550
558
|
end
|
551
559
|
|
552
|
-
def truthy?(value)
|
553
|
-
if defined?(::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES) # Rails <5
|
554
|
-
::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(value)
|
555
|
-
else
|
556
|
-
::ActiveRecord::Type::Boolean.new.cast(value)
|
557
|
-
end
|
558
|
-
end
|
559
|
-
|
560
560
|
def payment_to_h(payment)
|
561
561
|
if payment.respond_to?(:to_unsafe_h)
|
562
562
|
payment.to_unsafe_h.to_h
|
@@ -2,15 +2,21 @@ module Effective
|
|
2
2
|
class OrderItem < ActiveRecord::Base
|
3
3
|
self.table_name = EffectiveOrders.order_items_table_name.to_s
|
4
4
|
|
5
|
-
belongs_to :order
|
5
|
+
belongs_to :order
|
6
6
|
belongs_to :purchasable, polymorphic: true
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
if defined?(EffectiveQbSync)
|
9
|
+
has_one :qb_order_item
|
10
|
+
end
|
11
|
+
|
12
|
+
effective_resource do
|
13
|
+
name :string
|
14
|
+
quantity :integer
|
15
|
+
price :integer
|
16
|
+
tax_exempt :boolean
|
17
|
+
|
18
|
+
timestamps
|
19
|
+
end
|
14
20
|
|
15
21
|
validates :purchasable, associated: true, presence: true
|
16
22
|
accepts_nested_attributes_for :purchasable
|
@@ -24,7 +30,7 @@ module Effective
|
|
24
30
|
scope :purchased_by, lambda { |user| where(order_id: Effective::Order.purchased_by(user)) }
|
25
31
|
|
26
32
|
def to_s
|
27
|
-
(quantity || 0) > 1 ? "#{quantity}x #{name}" : name
|
33
|
+
((quantity || 0) > 1 ? "#{quantity}x #{name}" : name) || 'order item'
|
28
34
|
end
|
29
35
|
|
30
36
|
def purchased_download_url
|
@@ -59,5 +65,11 @@ module Effective
|
|
59
65
|
end
|
60
66
|
end
|
61
67
|
|
68
|
+
# first or build
|
69
|
+
def qb_item_name
|
70
|
+
raise('expected EffectiveQbSync gem') unless defined?(EffectiveQbSync)
|
71
|
+
(qb_order_item || build_qb_order_item(name: purchasable.qb_item_name)).name
|
72
|
+
end
|
73
|
+
|
62
74
|
end
|
63
75
|
end
|
@@ -4,12 +4,17 @@ module Effective
|
|
4
4
|
|
5
5
|
acts_as_purchasable
|
6
6
|
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
# belongs_to :purchased_order_id
|
8
|
+
|
9
|
+
effective_resource do
|
10
|
+
name :string
|
11
|
+
qb_item_name :string
|
12
|
+
|
13
|
+
price :integer
|
14
|
+
tax_exempt :boolean
|
15
|
+
|
16
|
+
timestamps
|
17
|
+
end
|
13
18
|
|
14
19
|
validates :name, presence: true
|
15
20
|
validates :price, presence: true
|
@@ -4,20 +4,21 @@ module Effective
|
|
4
4
|
|
5
5
|
attr_accessor :stripe_subscription
|
6
6
|
|
7
|
-
belongs_to :customer,
|
7
|
+
belongs_to :customer, counter_cache: true
|
8
8
|
belongs_to :subscribable, polymorphic: true
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
10
|
+
effective_resource do
|
11
|
+
stripe_plan_id :string
|
12
|
+
stripe_subscription_id :string
|
13
|
+
name :string
|
14
|
+
description :string
|
15
|
+
interval :string
|
16
|
+
quantity :integer
|
17
|
+
|
18
|
+
status :string
|
19
|
+
|
20
|
+
timestamps
|
21
|
+
end
|
21
22
|
|
22
23
|
before_validation(if: -> { plan && (stripe_plan_id_changed? || new_record?) }) do
|
23
24
|
self.name = plan[:name]
|
@@ -1,19 +1,15 @@
|
|
1
1
|
= effective_form_with(model: [:admin, order], url: (order.persisted? ? effective_orders.admin_order_path(order) : effective_orders.admin_orders_path)) do |f|
|
2
2
|
- if f.object.new_record?
|
3
|
-
=
|
3
|
+
- user_collection = current_user.class.respond_to?(:sorted) ? current_user.class.sorted : current_user.class.all
|
4
|
+
|
5
|
+
= f.select :user_id, (@users || user_collection),
|
4
6
|
label: 'Buyer', required: true, hint: 'The user that should purchase this order.'
|
5
7
|
|
6
8
|
= f.email_cc_field :cc, hint: "Cc the above on any emailed receipts or payment requests."
|
7
9
|
|
8
10
|
%h2 Order Items
|
9
|
-
.order_items
|
10
|
-
|
11
|
-
|
12
|
-
= f.fields_for :order_items, f.object.order_items do |order_item|
|
13
|
-
= render 'order_item_fields', f: order_item
|
14
|
-
|
15
|
-
.links
|
16
|
-
= link_to_add_association (icon('plus') + 'Add item'), f, :order_items, class: 'btn btn-secondary', partial: 'order_item_fields'
|
11
|
+
= f.has_many :order_items do |fc|
|
12
|
+
= render 'order_item_fields', f: fc
|
17
13
|
|
18
14
|
%hr
|
19
15
|
|
@@ -1,14 +1,10 @@
|
|
1
|
-
.
|
2
|
-
.
|
3
|
-
= f.
|
4
|
-
|
5
|
-
|
6
|
-
.col-md-2= pf.price_field :price
|
1
|
+
.row.align-items-center
|
2
|
+
= f.fields_for :purchasable, (f.object.purchasable || Effective::Product.new) do |pf|
|
3
|
+
.col= f.number_field :quantity, input_html: { value: f.object.quantity || 1, min: 1 }
|
4
|
+
.col= pf.text_field :name
|
5
|
+
.col= pf.price_field :price
|
7
6
|
|
8
|
-
|
9
|
-
|
7
|
+
- if EffectiveOrders.use_effective_qb_sync
|
8
|
+
.col= pf.text_field :qb_item_name, label: 'Quickbooks Item'
|
10
9
|
|
11
|
-
|
12
|
-
.col-md-1
|
13
|
-
= link_to_remove_association(f, 'data-confirm': 'Remove?') do
|
14
|
-
= icon('trash-2', class: 'text-danger')
|
10
|
+
.col= pf.check_box :tax_exempt, label: "Tax Exempt", title: 'When checked, tax will not be applied to this item'
|