I have a collection of objects. What I would like to do is iterate over the entire collection, but show each object on a page/view by itself, and allow the user to interact with each object individually. Ideally, I would prefer not to use a multi-part form if I can avoid it, for reasons I spell out at the end of my question.
I am trying to implement screens like the images below.
Basically the user of the app, will go to a location to do a reconciliation of inventory (of each product) in that location. That's what the screens are showing. For each product, they have to update the inventory.
The summary & requirements are as follows:
- A
location has_many inventory_items
. - A
user
begins areconciliation
whenever they want to do an inventory check. - A
reconciliation habtm inventory_items
&&belongs_to :location
. - An
inventory_item habtm reconciliations
&&belongs_to :location
. - I can't predict in advance how many
inventory_items
there are. - There could be dozens or hundreds of
inventory_items
. - I can break up the
inventory_items
into different groups if the number becomes unwieldy....similar to a pagination approach.
So my models look like this:
Reconciliation
# == Schema Information
#
# Table name: reconciliations
#
# id :integer not null, primary key
# location_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Reconciliation < ApplicationRecord
belongs_to :location
has_and_belongs_to_many :inventory_items
end
Location
# == Schema Information
#
# Table name: locations
#
# id :integer not null, primary key
# name :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Location < ApplicationRecord
has_and_belongs_to_many :products
has_many :inventory_items, inverse_of: :location
accepts_nested_attributes_for :inventory_items
has_many :reconciliations
end
Inventory Item
# == Schema Information
#
# Table name: inventory_items
#
# id :integer not null, primary key
# product_id :integer
# location_id :integer
# quantity_left :integer
# quantity_delivered :integer
# quantity_on_hand :integer
# date_of_last_delivery :datetime
# created_at :datetime not null
# updated_at :datetime not null
#
class InventoryItem < ApplicationRecord
belongs_to :product
belongs_to :location, inverse_of: :inventory_items
has_and_belongs_to_many :reconciliations
end
Here is my inventory_items_reconciliations Join Table
:
create_table "inventory_items_reconciliations", id: false, force: :cascade do |t|
t.bigint "inventory_item_id", null: false
t.bigint "reconciliation_id", null: false
t.index ["inventory_item_id", "reconciliation_id"], name: "index_inventory_item_id_reconciliation_id_join"
t.index ["reconciliation_id", "inventory_item_id"], name: "index_reconciliation_id_inventory_item_id_join"
end
My routes.rb
:
resources :locations, shallow: true do
resources :inventory_items
resources :reconciliations
end
My ReconciliationsController#New
:
def new
@location = Location.find(params[:location_id])
@reconciliation = @location.reconciliations.new
@inventory_items = @location.inventory_items
@num_of_inventory_items = @inventory_items.coun
end
My app/views/reconciliations/new.html.erb
:
<% @inventory_items.each do |inventory_item| %>
<%= render 'form', reconciliation: @reconciliation, inventory_item: inventory_item %>
<% end %>
My app/views/reconciliations/_form.html.erb
:
<%= simple_form_for @reconciliation, url: :location_reconciliations do |f| %>
<%= f.error_notification %>
<strong>Name</strong>: <%= inventory_item.product.name %> <br />
<strong>Quantity Left:</strong> <%= inventory_item.quantity_left %> <br />
<strong>Quantity Delivered:</strong> <%= inventory_item.quantity_delivered %> <br />
<div class="form-actions">
<%= f.button :submit, "Update", class: "btn btn-primary" %>
</div>
<% end %>
What this does is just displays all of the location.inventory_items
on that reconciliation page, when all I want is for 1 to be displayed.
So, what I would like to do is this:
- Get the collection of
inventory_items
in the location that the user has chosen. - Begin iteration over that collection and show the user each object, in their own view, one at a time.
- As the user progresses (i.e. once they press 'Next'), essentially mark that
inventory_item
asreconciled
even if the user didn't update the quantity (i.e. say there have been no sold since it was delivered). - Once all
inventory_items
in this collection are iterated over, then save thereconciliation
record to the database that accurately reflects the quantity information for eachinventory_item
within thisreconiliation cycle
.
I looked at the Wicked Gem, but it seems that I need to be able to statically declare the number of steps in advance. As you can see above, if each inventory_item
in my collection is a step, there needs to be a dynamic number of steps.
I also came across similar constraints with other multi-step wizard approaches.
How do I achieve what I am trying to do?