Forum: Ruby on Rails Adding multiple invoice items to an invoice on the same form

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Naroor R. (Guest)
on 2005-12-15 13:34
Hi Friends ,
     Got a unique requirement .I am designing a invoice printing system
.So right now I have the NEW page for adding the invoice details to the
table .Now I have the requirement of adding Invoice Items In the same
form .I have added multiple text boxes to enter the values of the Items
using
 <%= text_field 'invoiceitems[]', 'item_price'  %>
 <%= text_field 'invoiceitems[]', 'item_qty'  %> etc.

How can I save the multiple items in the same form .I have to insert the
invoice details and the invoice items on the same button click.

Waiting for the comments and suggestions ,

Thanx and regards,
Naroor R.,
www.naroor.blogspot.com
Andrew F. (Guest)
on 2005-12-15 14:45
(Received via mailing list)
Not an expert but I will take a shot here. The form looks right to me
however I did things a little different. I used different names for
each field type (so that I had different arrays returned to the
server) and than did a loop through the array by something like this:

0.upto.(params[invoiceprice].length - 1) do |x|
	invoice << Invoice.new(:invoiceprice => params[:invoiceprice][x],
						  :invoiceqty => params[invoiceqty][x])
end


I am doing something slimilar however I used text_field_tag and
various other _tag helpers in my view because my form was dynamically
created and I felt that I could not use the non tag fields due to the
fact that I have no preexisting knowledge of what the form is going
to look like.

Hope this helps, if I did something wrong here would love to be
corrected as well but it seems to work may not be the most DRY way to
do it.

Andrew
Wilson B. (Guest)
on 2005-12-15 16:51
(Received via mailing list)
On 12/15/05, naroor rathish <removed_email_address@domain.invalid> wrote:
> invoice details and the invoice items on the same button click.
>
One way would be to move the text fields you want generated to a
partial.
Let's pretend it's called _invoiceitem.rhtml
Then, in your RHTML file, where you currently have those text_field
statements..
# Assuming @invoice_items is an Enumerable containing the AR objects
you want to work with.
<%= render(:partial => 'invoiceitem', :collection => @invoice_items) %>

_invoiceitem.rhtml would look something like: (First line is
important, if you want to use the form helpers.)
<% @invoiceitem = invoiceitem -%>
<%= text_field 'invoiceitem', 'item_price', 'index' =>
invoiceitem_counter %>
<%= text_field 'invoiceitem', 'item_qty', 'index' => invoiceitem_counter
%>
# And so on, for the various attributes you want to display.
# nameofpartial_counter is a local variable created for you that keeps
track of the index.

In the controller action that your form submits to, you'd do something
like:

@items = []
params[:invoiceitem].each do |key, item|
   @items << InvoiceItem.new(item)
end

Now you've got an array of all the invoice item objects, and you can
do whatever you want with it.. maybe stuffing it in the session for
later use, or calling ".valid?" on each entry and putting something in
the flash if any of them aren't correct, etc.

Hopefully someone will come along and show a more elegant solution
that I just don't know about yet, but this is roughly how I do it.

--Wilson.
Steve K. (Guest)
on 2005-12-15 21:05
I've been doing roughly the same thing, using the :index in each form
field to pass an index for each line item.

Since the same partial is going to get used for both new invoices and
editing existing invoices, what you may want to do is name the variable
that contains the :index value more generically so that when calling
"new.rhtml" you pass :index that invoice_item counter's value (3 rows?
then pass it 1, 2, and 3 in a loop), and when you're calling the partial
from "edit.rhtml" you're passing it invoice_item.id.

You'll have to go through a bit more contortion if you want to mix new
lineitems with edits of old ones on the same edit form, obviously, since
you'll need a way for your update action to distinguish between the
different purposes you're using :index for.

--
-sk


Wilson B. wrote:
> On 12/15/05, naroor rathish <removed_email_address@domain.invalid> wrote:
>> invoice details and the invoice items on the same button click.
>>
> One way would be to move the text fields you want generated to a
> partial.
> Let's pretend it's called _invoiceitem.rhtml
> Then, in your RHTML file, where you currently have those text_field
> statements..
> # Assuming @invoice_items is an Enumerable containing the AR objects
> you want to work with.
> <%= render(:partial => 'invoiceitem', :collection => @invoice_items) %>
>
> _invoiceitem.rhtml would look something like: (First line is
> important, if you want to use the form helpers.)
> <% @invoiceitem = invoiceitem -%>
> <%= text_field 'invoiceitem', 'item_price', 'index' =>
> invoiceitem_counter %>
> <%= text_field 'invoiceitem', 'item_qty', 'index' => invoiceitem_counter
> %>
> # And so on, for the various attributes you want to display.
> # nameofpartial_counter is a local variable created for you that keeps
> track of the index.
>
> In the controller action that your form submits to, you'd do something
> like:
>
> @items = []
> params[:invoiceitem].each do |key, item|
>    @items << InvoiceItem.new(item)
> end
>
> Now you've got an array of all the invoice item objects, and you can
> do whatever you want with it.. maybe stuffing it in the session for
> later use, or calling ".valid?" on each entry and putting something in
> the flash if any of them aren't correct, etc.
>
> Hopefully someone will come along and show a more elegant solution
> that I just don't know about yet, but this is roughly how I do it.
>
> --Wilson.
Naroor R. (Guest)
on 2005-12-20 07:43
Hi Wilson ,
   I was trying your code to be implemented in mine but some errors are
coming .
I added a partial ::
<% @invoiceitem = invoiceitem -%>
 <%= text_field 'invoiceitem', 'item_price', 'index' =>
 invoiceitem_counter %>
 <%= text_field 'invoiceitem', 'item_qty', 'index' =>
invoiceitem_counter
 %>
Where are we initialising the counter? Please tell how is the first line
working .

In the rhtml file I have added  :

    <%= form_remote_tag(:update => "my_list",
                       :url => { :action => :add_new_item },
                       :position => "bottom" ) %>

            <%= submit_tag "Add New Item" %>
       <%= end_form_tag %>
   <form action="/ajax/save_items" method="post">
    <ul id="my_list">
      <li>Original item... please add more!</li>
    </ul>

    <%= submit_tag 'Update' %>
   </form>
I have added like this for creating a dynamic generation of text boxes
using ajax.

And in the controller class ,

  def add_new_item

   render(:partial => 'invoiceitems', :collection => @invoice_items)

  end
  def save_items

	 @items = []
	params[:invoiceitem].each do |key, item|
		@items = InvoiceItem.new(item)
	end
   end

Please help my in implementing this requirement .

Thanks and regards,
Naroor R..


Wilson B. wrote:
> On 12/15/05, naroor rathish <removed_email_address@domain.invalid> wrote:
>> invoice details and the invoice items on the same button click.
>>
> One way would be to move the text fields you want generated to a
> partial.
> Let's pretend it's called _invoiceitem.rhtml
> Then, in your RHTML file, where you currently have those text_field
> statements..
> # Assuming @invoice_items is an Enumerable containing the AR objects
> you want to work with.
> <%= render(:partial => 'invoiceitem', :collection => @invoice_items) %>
>
> _invoiceitem.rhtml would look something like: (First line is
> important, if you want to use the form helpers.)
> <% @invoiceitem = invoiceitem -%>
> <%= text_field 'invoiceitem', 'item_price', 'index' =>
> invoiceitem_counter %>
> <%= text_field 'invoiceitem', 'item_qty', 'index' => invoiceitem_counter
> %>
> # And so on, for the various attributes you want to display.
> # nameofpartial_counter is a local variable created for you that keeps
> track of the index.
>
> In the controller action that your form submits to, you'd do something
> like:
>
> @items = []
> params[:invoiceitem].each do |key, item|
>    @items << InvoiceItem.new(item)
> end
>
> Now you've got an array of all the invoice item objects, and you can
> do whatever you want with it.. maybe stuffing it in the session for
> later use, or calling ".valid?" on each entry and putting something in
> the flash if any of them aren't correct, etc.
>
> Hopefully someone will come along and show a more elegant solution
> that I just don't know about yet, but this is roughly how I do it.
>
> --Wilson.
Wilson B. (Guest)
on 2005-12-20 18:43
(Received via mailing list)
When you render a partial by passing in a collection, the render()
call automatically provides a counter object for you.
It gives this object the name #{partialname}_counter.. In this case,
invoiceitem_counter.
The first line (@invoiceitem = invoiceitem) is necessary because there
is no way in Ruby to ask a variable what its name is.
To work around this, the Rails helpers (such as text_field) take the
name of the object as a string ('invoiceitem'), and then use that when
they need to access the object.
The helpers, however, are coded to expect an instance variable.
text_field('blah', 'blah_property') requires that there be an instance
variable @blah.

When working with a collection, the objects are passed in as locals
(invoiceitem), so we need to turn them into instance variables
(@invoiceitem) before using helpers on them.

The problem with your code, at first glance, seems to be that you're
not actually working with a collection.  Your first partial call only
has one item, and subsequent invoice items are added one at a time,
via Ajax.  This means that you need to keep track of the 'item count'
separately, probably via an instance variable in the controller.
The Ajax action will need to increment that number by one when it
fires, and you'll use that number as the index of the next item to be
added.

Good luck,
--Wilson.
Naroor R. (Guest)
on 2005-12-21 15:46
Hi wilson,
 Got a strange error.

NameError in Ajax#add_new_item
Showing app/views/ajax/_invoiceitems.rhtml where line #2 raised:

undefined local variable or method `invoiceitem' for
#<#<Class:0x3873970>:0x3873898>

Extracted source (around line #2):

1:
2: <% @invoiceitem = invoiceitem %>
3:  <%= text_field 'invoiceitem', 'item_price', 'index' =>
invoiceitem_counter %>
4:  <%= text_field 'invoiceitem', 'item_qty', 'index' =>
invoiceitem_counter %>

Request
Parameters: {"commit"=>"Add", "_"=>""}

Show session dump

---
flash: !ruby/hash:ActionController::Flash::FlashHash {}
Response
Headers: {"cookie"=>[], "Cache-Control"=>"no-cache"}


Can't we use ajx with partials .Please let me know the result.

Thanking you,
Naroor R.
Wilson B. (Guest)
on 2005-12-21 18:35
(Received via mailing list)
If your partial is named _invoiceitems.rhtml, the object will be
'invoiceitems', not 'invoiceitem'.
Take the 's' off the end of the filename, and you should be good.
Naroor R. (Guest)
on 2005-12-22 10:17
Hi Wilson,
  Thanx for the comments .That got cleared but final saving issues .
The error is
NameError in Ajax#save_items
uninitialized constant Invoiceitem
RAILS_ROOT: ./script/../config/..

Request
Parameters: {"commit"=>"Update", "invoice"=>{"inv_number"=>"rest"},
"invoiceitem"=>{"1"=>{"item_price"=>"12", "item_qty"=>"12"},
"2"=>{"item_price"=>"13", "item_qty"=>"13"}}}

The code I have written for saving is (i mean in the controller is )


  def save_items

	@invoice = Invoice.new(params[:invoice])
	params[:invoiceitem].each do |key, val|
	        @invoice.invoiceitems << Invoiceitem.new(val)
	end
  end

Calling the partial is like
    render(:partial => 'invoiceitems', :collection => @invoiceitems )

In the partial I have given,
    <% $count = $count + 1 %>
    <% @invoiceitem = invoiceitems %>
    <%= text_field 'invoiceitem', 'item_price', 'index' => $count %>
    <%= text_field 'invoiceitem', 'item_qty', 'index' => $count %>
    <br>

Please help me out.
Thanx and regards,
Naroor R.
Wilson B. (Guest)
on 2005-12-22 17:37
(Received via mailing list)
@invoice.invoiceitems << Invoiceitem.new(val)

..should instead be:
@invoice.invoice_items << InvoiceItem.new(val)
Naroor R. (Guest)
on 2005-12-23 06:28
Hi Wilson ,
   The same error
NoMethodError in Ajax#save_items
undefined method `invoice_items' for #<Invoice:0x385a920>
with
Request
Parameters: {"commit"=>"Update", "invoice"=>{"inv_number"=>"inv123"},
"invoiceitem"=>{"1"=>{"item_price"=>"12", "item_qty"=>"12"},
"2"=>{"item_price"=>"13", "item_qty"=>"13"}}}

Should the name of the table be invoice_items ??Right now I have the
name of the table as invoiceitems .

Thanx,
Naroor R.
Naroor R. (Guest)
on 2005-12-23 11:46
Hi Wilson ,
  The results I am getting in each variables are

params[:invoice] = inv_number123

params[:invoiceitem] =
1item_priceprice1item_qtyqty12item_priceprice2item_qtyqty23item_priceprice3item_qtyqty3

Is the output correct when I dynamically added 3 rows with two columns
for entering intem price and quantity .

Regards,
Naroor R.
Wilson B. (Guest)
on 2005-12-23 20:29
(Received via mailing list)
On 12/22/05, Naroor R. <removed_email_address@domain.invalid> wrote:
> Should the name of the table be invoice_items ??Right now I have the
> name of the table as invoiceitems .
>
Yeah, the table needs to be called invoice_items, or else you need to
override the table with set_table_name in your model class.
params[:invoiceitem] contains a Hash that has index numbers from the
view as its keys, and hashes as values.  Those hashes it contains use
field names as their keys.
e.g.
items = params[:invoiceitem]
second_item = items[:2]
second_item is now a Hash with two keys.. :item_price and :item_qty

second_item[:item_price] == '13'

--Wilson.
This topic is locked and can not be replied to.