I'm trying to add an extra step to my Spree 1.2 store which will allow a customer to create a subscription. I've inserted the step, and rendered the correct view, but when the user clicks 'save and continue' the next step is rendered, but nothing is actually saved.
I understand that I need to add a state_callback, but I'm not sure how to do this and the Spree documentation is very lacking around this (presumably because it's quite new)
At present I've got the following in my extension:
models/spree/order_decorator.rb
Spree::Order.class_eval do
belongs_to :subscription
accepts_nested_attributes_for :subscription
# This doesn't appear to be called
Spree::Order.state_machine.after_transition :from => :subscription,
:do => :valid_subs?
checkout_flow do
go_to_state :address
go_to_state :subscription
go_to_state :payment, :if => lambda { |order| order.payment_required? }
go_to_state :confirm, :if => lambda { |order| order.confirmation_required? }
go_to_state :complete
remove_transition :from => :delivery, :to => :confirm
end
end
Not entirely sure that accepts_nested_attributes is necessary, but my dev approach for this has been trial and error so far, so it ended up staying there.
In models/subscription.rb
class Subscription < ActiveRecord::Base
attr_accessible :start_date, :frequency
belongs_to :user
has_many :orders
has_many :products
validates :start_date, :frequency, :presence => true
def schedule
...code that returns a list of dates rendered on FE...
end
private #----
... some methods used in schedule ...
def valid_subs?
binding.pry # never called
end
def after_subscription
binding.pry # never called either...
end
end
views/spree/checkout/_subscription.html.erb
<h1><%= t(:"subscription.title") %></h1>
<div class="columns alpha six" data-hook="subscription_calendar_fieldset_wrapper">
<fieldset id="subscription_calendar" data-hook>
<%= form.fields_for :subscription_picker do |subscription_picker| %>
<legend><%= t(:"subscription.first_delivery") %></legend>
<%= render :partial => 'subscription/picker' %>
<% end %>
</fieldset>
</div>
<div class="columns omega six" data-hook="subscription_dates_fieldset_wrapper">
<fieldset id="subscription_dates" data-hook>
<legend align="center"><%= t(:"subscription.next_deliveries") %></legend>
<div class='dates'></div>
</fieldset>
</div>
<div class="form-buttons" data-hook="buttons" style='clear:both;'>
<%= submit_tag t(:save_and_continue), :class => 'continue button primary' %>
</div>
views/subscription/_picker.html.erb
<div class='row'>
<label for="subscription_frequency">Occurs every:</label>
<% frequency_options = [["2 Weeks", 14], ["3 Weeks", 21], ["Month", 30], ["2 Months", 60], ["3 Months", 90]] %>
<%= select(:subscription, :frequency, options_for_select(frequency_options, 30), {}) %>
</div>
<div id="start-date-picker" class="calendar"></div>
<%= hidden_field(:subscription, :start_date, {value: (DateTime.now + 14).to_date.iso8601}) %>
... JS that creates the calendar ...
On clicking 'save and continue' I see the following params sent:
{
"utf8" => "✓",
"_method" => "put",
"authenticity_token" => "...BLAH...",
"subscription" => {
"frequency" => "30",
"start_date" => "2012-11-17"
},
"commit" => "Save and Continue",
"controller" => "spree/checkout",
"action" => "update",
"state" => "subscription"
}
You have two problems. Your callback isn't firing because the call to checkout_flow erases the current state machine and replaces it. Move this code to after your call to checkout_flow:
Spree::Order.state_machine.after_transition :from => :subscription,
:do => :valid_subs?
Second, your subscription information isn't saving because the parameters need to be passed as part of the order. The params should appear nested like this:
"order" => {
"subscription" => {
"frequency" => "30",
"start_date" => "2012-11-17"
}
}
You can do that by attaching your call to select and hidden_field to the appropriate form 'subscription_picker':
<%= subscription_picker.select(:frequency, options_for_select(frequency_options, 30), {}) %>
and
<%= subscription_picker.hidden_field(:start_date, {value: (DateTime.now + 14).to_date.iso8601}) %>
You should probably pass the form object to your partial as an explicit parameter for style and understandability.
Cheers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With