Module: ActsAsPurchasableWizard

Extended by:
ActiveSupport::Concern
Defined in:
app/models/concerns/acts_as_purchasable_wizard.rb

Overview

ActsAsPurchasableWizard

Defined Under Namespace

Modules: Base, ClassMethods

Instance Method Summary collapse

Instance Method Details

#after_submit_deferred!Object



239
240
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 239

def after_submit_deferred!
end

#after_submit_purchased!Object



255
256
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 255

def after_submit_purchased!
end

#assign_order_delayed_payment_attributes(order) ⇒ Object

This is used by effective_events and deluxe_delayed effective_orders provider



128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 128

def assign_order_delayed_payment_attributes(order)
  return unless order.respond_to?(:delayed_payment)

  atts = delayed_payment_attributes()
  return unless atts.present?

  unless atts.kind_of?(Hash) && atts.key?(:delayed_payment) && atts.key?(:delayed_payment_date)
    raise('expected delayed payment attributes') 
  end

  order.assign_attributes(atts)
  order
end

#before_submit_deferred!Object

A hook to extend



236
237
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 236

def before_submit_deferred!
end

#before_submit_order_save(order) ⇒ Object



118
119
120
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 118

def before_submit_order_save(order)
  order
end

#before_submit_purchased!Object

A hook to extend



252
253
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 252

def before_submit_purchased!
end

#billing!Object

Owner clicks on the Billing step. Next step is Checkout



205
206
207
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 205

def billing!
  ready! && save!
end

#build_effective_orderObject



57
58
59
60
61
62
63
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 57

def build_effective_order
  if EffectiveOrders.organization_enabled? && respond_to?(:organization) # New style
    orders.build(organization: organization)
  else
    orders.build(user: owner) # This is polymorphic user, might be an organization. Old style.
  end
end

#build_submit_fees_and_order(force: false) ⇒ Object

Should be indempotent.



174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 174

def build_submit_fees_and_order(force: false)
  return false if  && !force

  fees = find_or_build_submit_fees()
  raise('already has purchased submit fees') if Array(fees).any?(&:purchased?)

  order = find_or_build_submit_order()
  raise('expected an Effective::Order') unless order.kind_of?(Effective::Order)
  raise('already has purchased submit order') if order.purchased?
  raise('unable to proceed with a voided submit order') if order.try(:voided?)

  true
end

#delayed_payment_attributesObject

Override this in your wizard to enable the delayed payments



123
124
125
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 123

def delayed_payment_attributes
  { delayed_payment: nil, delayed_payment_date: nil }
end

#find_or_build_submit_feesObject



53
54
55
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 53

def find_or_build_submit_fees
  submit_fees
end

#find_or_build_submit_orderObject



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 65

def find_or_build_submit_order
  order = submit_order || build_effective_order()
  order = build_effective_order() if order.declined? # Make a new order, if the previous one was declined

  # Update the order with the current owner
  if EffectiveOrders.organization_enabled? && respond_to?(:organization)
    order.organization = organization
  else
    # A membership could go from individual to organization
    order.user = owner 
  end

  # Consider fees
  fees = submit_fees().reject { |fee| fee.marked_for_destruction? }

  # Make sure all Fees are valid
  fees.each do |fee|
    raise("expected a valid fee but #{fee.id} had errors #{fee.errors.inspect}") unless fee.valid?
  end

  # Adds fees, but does not overwrite any existing price.
  fees.each do |fee|
    order.add(fee) unless order.purchasables.include?(fee)
  end

  # Remove any order items that no longer have fees for them
  order.order_items.each do |order_item|
    fee = fees.find { |fee| fee == order_item.purchasable }
    order.remove(order_item) unless fee.present?
  end

  # From Billing Step
  order.billing_address = owner.billing_address if owner.try(:billing_address).present?

  # This will update all order items to match the prices from their purchasable
  order.try(:update_purchasable_attributes)

  # Handle effective_memberships coupon fees price reduction
  reduce_order_item_coupon_fee_price(order)

  # Handle effective_events date delayed payments
  assign_order_delayed_payment_attributes(order)

  # Hook to extend for coupon fees
  order = before_submit_order_save(order)
  raise('before_submit_order_save must return an Effective::Order') unless order.kind_of?(Effective::Order)

  # Important to add/remove anything
  order.save!

  order
end

#ready!Object

Ready to check out This is called by the “ready_checkout” before_action in wizard_controller/before_actions.rb



211
212
213
214
215
216
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 211

def ready!
  without_current_step do
    build_submit_fees_and_order
    save!
  end
end

#reduce_order_item_coupon_fee_price(order) ⇒ Object

This is used by effective_memberships and effective_events Which both add coupon_fees to their submit_fees



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 144

def reduce_order_item_coupon_fee_price(order)
  # This only applies to orders with coupon fees
  order_items = order.order_items.select { |oi| oi.purchasable.try(:coupon_fee?) }
  return order unless order_items.present?
  raise('multiple coupon fees not supported') if order_items.length > 1

  # Get the coupon fee
  order_item = order_items.first
  coupon_fee = order_item.purchasable
  raise('expected order item for coupon fee to be a negative price') unless coupon_fee.price.to_i < 0

  # Calculate price
  subtotal = order.order_items.reject { |oi| oi.purchasable.try(:coupon_fee?) }.sum(&:subtotal)

  price = 0 if subtotal <= 0
  price ||= [coupon_fee.price, (0 - subtotal)].max

  # Assign the price to this order item. Underlying fee price stays the same.
  order_item.assign_attributes(price: price)

  # Return the order
  order
end

#submit!Object

Draft -> Submitted requirements



259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 259

def submit!
  raise('already submitted') if 

  if submit_wizard_on_deferred_order?
    raise('expected a purchased or deferred order') unless (submit_order&.purchased? || submit_order&.deferred?)
  else
    raise('expected a purchased order') unless submit_order&.purchased?
  end

  wizard_steps[:checkout] ||= Time.zone.now
  wizard_steps[:submitted] = Time.zone.now
  
end

#submit_deferred!Object

Called automatically via after_defer hook above



227
228
229
230
231
232
233
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 227

def submit_deferred!
  return unless submit_wizard_on_deferred_order?
  return false if 

  wizard_steps[:checkout] = Time.zone.now
  submit!
end

#submit_feesObject

All Fees and Orders



45
46
47
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 45

def submit_fees
  raise('to be implemented by caller')
end

#submit_orderObject



49
50
51
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 49

def submit_order
  orders.last
end

#submit_purchased!Object

Called automatically via after_purchase hook above If previously submitted, possibly with deferred order, just save so any before_save or validate can run.



244
245
246
247
248
249
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 244

def submit_purchased!
  return save! if 

  wizard_steps[:checkout] = Time.zone.now
  submit!
end

#submit_wizard_on_deferred_order?Boolean

False by default - do not call submit

Returns:

  • (Boolean)


222
223
224
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 222

def submit_wizard_on_deferred_order?
  false
end

#update_submit_fees_and_order!Object



168
169
170
171
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 168

def update_submit_fees_and_order!
  build_submit_fees_and_order(force: true) 
  save!
end

#with_outstanding_coupon_fees(purchasables) ⇒ Object

Called by effective_memberships and effective_events



189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'app/models/concerns/acts_as_purchasable_wizard.rb', line 189

def with_outstanding_coupon_fees(purchasables)
  return purchasables unless owner.respond_to?(:outstanding_coupon_fees) # effective_memberships_owner
  raise('expected has_many fees') unless respond_to?(:fees)

  price = purchasables.reject { |p| p.try(:coupon_fee?) }.map { |p| p.price || 0 }.sum

  if price > 0
    Array(owner.outstanding_coupon_fees).each { |fee| fees << fee unless fees.include?(fee) }
  else
    Array(owner.outstanding_coupon_fees).each { |fee| fees.delete(fee) if fees.include?(fee) }
  end

  (purchasables + fees).uniq
end