Forum: Ruby on Rails How to model "Expense Report" in Rails MVC

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.
Dave S. (Guest)
on 2005-12-20 19:08
1st: I am a newbie to Rails & to pure OO.
Q: I want to use rails for creating a "master-detail" form. page layout
will allow users to type in an "expense report header" and as many
"expense report lines" as they need to. I understand how to wrap the
final submit action using "transaction" to ensure the inserts to the
database happen within the same commit cycle. But how should I model the
"expense report" (which consists of 1 expense report header and N
expense report lines) in rails? should there be an MVC for the "expense
report"?  i admit i get a little lost in rails generated MVC once i am
dealing with master-detail. for an app requiring action on only a single
table at a time, i'm golden.
p.s. Thank you!
Dave S. (Guest)
on 2005-12-20 21:19
p.s. I am willing to write a nice, well documented article on how to do
a master-detail form on rails if someone is kind enough to show me how!!
Wilson B. (Guest)
on 2005-12-20 21:29
(Received via mailing list)
On 12/20/05, Dave S. <removed_email_address@domain.invalid> wrote:
> table at a time, i'm golden.
table: expense_reports
id
other_stuff

table: expense_items
id
expense_report_id
other_stuff

ExpenseReport < ActiveRecord::Base
has_many :expense_items
end

ExpenseItem < ActiveRecord::Base
belongs_to :expense_report
end

Put the View junk for Expense Items in a partial, and render it on a
collection in some action.

In the controller, have the form submit to, let's say, create_report.

def create_report
  report = ExpenseReport.new(params[:expense_report])  # Only one of
these
  params[:expense_item].each do |key, val|
     report.expense_items << ExpenseItem.new(val)
  end
  # report.expense_items is now a collection of all the items.
  # You still need to check everything with object.valid?, and decide
where to send the user.
  # When you've verified everything, you can call report.save, and
everything will be saved.
end
Ed W. (Guest)
on 2005-12-20 22:06
(Received via mailing list)
I have a related question:

Lets suppose that we have the code below:

>  # When you've verified everything, you can call report.save, and
>everything will be saved.
>end
>
>

And lets suppose that we want to revalidate some of the ExpenseItems.
eg Consider an accounting app, we might have a nice UI which lets people
type in an approx name for each item and then we popup a number of
search boxes which tries to find the exact item name from the DB,
correcting spelling etc and making sure each entry is standardised
(means you can type in a few letters for each product for speed and then
pick the exact items from a list on the next screen)

How can we stash all this data once we have recovered it while we go to
the search screen to correct each item.  We will then redirect back to
the main create_report page before saving.

Does this make sense?  Picture some invoicing app for a small business
which for speed lets you type in the details for each line, rather than
picking from some huge list, but then we need to make sure that the
lines are all standardised and match the descriptions in the DB, so one
by one we offer a search box to let the user choose the nearest item.
How to code this in Rails? (It's the stashing of the params that I am
unsure about)

Thanks

Ed W
Wilson B. (Guest)
on 2005-12-20 23:57
(Received via mailing list)
On 12/20/05, Ed W <removed_email_address@domain.invalid> wrote:
> >  end
> eg Consider an accounting app, we might have a nice UI which lets people
> Does this make sense?  Picture some invoicing app for a small business
> which for speed lets you type in the details for each line, rather than
> picking from some huge list, but then we need to make sure that the
> lines are all standardised and match the descriptions in the DB, so one
> by one we offer a search box to let the user choose the nearest item.
> How to code this in Rails? (It's the stashing of the params that I am
> unsure about)
>

Yep, I do something similar in one system, with Ajax searches for
individual demographics (name, date of birth, etc, etc).  I decided to
just stuff an array of the relevant objects into the session, ordered
the same way they were on the page.
session[:current_blahs] = []
params[:blah].each etc etc
   session[:current_blahs] << blah
end

That let me use the helpers that support the :index => option, and
preserve the order of things during edits and whatnot.
Things are simplified greatly if you can make the users click on the
items they want to manipulate.  Unfortunately in my case, I had to
allow the users to, say, add 5 new items to the bottom of the list all
at once, which made it 'fun.'
If each item has a 'Remove' button, and there's an 'Add' button that
they have to click 5 times to add 5 more entries, you're pretty much
home free.


--Wilson.
Dave S. (Guest)
on 2005-12-21 03:01
Wilson B. wrote:
...
>   # When you've verified everything, you can call report.save, and
> everything will be saved.
> end
thanks wilson, i will give this a go!
Hiroshi T. (Guest)
on 2005-12-21 04:01
(Received via mailing list)
On Tue, 20 Dec 2005 14:01:11 -0500
Wilson B. <removed_email_address@domain.invalid> wrote:

> > dealing with master-detail. for an app requiring action on only a single
>
>
>   # When you've verified everything, you can call report.save, and
> everything will be saved.
> end


Hi,

this discussion reminds me of another related problem I have.
I've already developped master-detail type form like Dave detailed
elaborately. To create a new master-detail ( like an invoice ) is ok,
but how about edit/update an invoice which has already recorded in the
tables.
there can be collision possibilities that two or more ppl might edit the
details of the same master-detail invoice and result in some
inconsitencies.
creating/updating of master-detail type data is a kind of long
transaction compared to that of single master table, so risks are
higher.

I know Web/DB app cannot have a [Perfect] transaction locking 'cause
there is no 'active' session though, is there any workaround to
minimize/avoid such an inconsistency?

So far I don't develop edit feature to master-detail type transaction,
but I realize soon the oprator ladies will claim me that why they can't
be updated.

Thanks in advance,

--
Hiroshi T. <removed_email_address@domain.invalid>
Cuong T. (Guest)
on 2005-12-21 04:20
(Received via mailing list)
There is where optimistic locking comes in handy.  Check out:

 http://api.rubyonrails.com/classes/ActiveRecord/Locking.html
Hiroshi T. (Guest)
on 2005-12-21 08:26
(Received via mailing list)
On Tue, 20 Dec 2005 20:18:41 -0600
Cuong T. <removed_email_address@domain.invalid> wrote:

>   There is where optimistic locking comes in handy.  Check out:
>
>  http://api.rubyonrails.com/classes/ActiveRecord/Locking.html

Thank you, Cuong!
its a great help to me.
All what I have to do is add  a column named lock_version and rescue
ActiveRecord::StaleObjectError
with some Transaction.

Thanks again!

--
Hiroshi T. <removed_email_address@domain.invalid>
Ed W. (Guest)
on 2005-12-21 10:23
(Received via mailing list)
Hi

>Yep, I do something similar in one system, with Ajax searches for
>individual demographics (name, date of birth, etc, etc).  I decided to
>just stuff an array of the relevant objects into the session, ordered
>the same way they were on the page.
>session[:current_blahs] = []
>params[:blah].each etc etc
>   session[:current_blahs] << blah
>end
>
>

I can see multiple ways it might be tackled using the session, but
doesn't this have problems with a single user logged into the app
multiple times through different browser windows?  I'm not entirely sure
how sessions work, but I thought that you ended up sharing a session
between multiple browser windows under some circumstances?

Compare with the approach of finding a way to serialise the data and
shove it in some hidden fields on the next view and grab it out again
later...?  (Trivial way to do this might be to have two form partials,
one that you use for create and edit with all the fields visible, and
another where the fields are all hidden which is used to add-on to the
next form in order to port the fields forward easily.

Does the second approach have any hidden problems compared with the
session, and what problems will the session approach have with a user
and multiple windows?

Thanks

Ed W
Wilson B. (Guest)
on 2005-12-21 18:32
(Received via mailing list)
On 12/21/05, Ed W <removed_email_address@domain.invalid> wrote:
> >
> later...?  (Trivial way to do this might be to have two form partials,
> one that you use for create and edit with all the fields visible, and
> another where the fields are all hidden which is used to add-on to the
> next form in order to port the fields forward easily.
>
> Does the second approach have any hidden problems compared with the
> session, and what problems will the session approach have with a user
> and multiple windows?
>

Hrm. That's very true, and luckily I haven't run into that yet.
Another approach might be to store a hash in the session that uses the
primary keys of your domain object as the key.
If you can guarantee that the user isn't editing one "expense report"
in two different windows, that would work.
Interesting. I'll need to think about this some more.
Naroor R. (Guest)
on 2005-12-22 15:00
Hi ,
 I tried the same Expense report problem in rails .The code is
Controller

class ExpensereportController < ApplicationController

	def index

	end
	def create_report
	  report = ExpenseReport.new(params[:expense_report])
	  params[:expense_item].each do |key, val|
	     report.expense_items << ExpenseItem.new(val)
	  end
	  report.save
	end


	def add_new_item

	end
end

View is
<html>
  <head>
    <title>Expsense Report</title>
  </head>
  <body>

   <form action="/ajax/save_items" method="post">
    <ul id="my_list">
    <input type="text" id="expense_report_name"
name="expense_report[name]" >

	<%= render(:partial => 'expense_items', :collection =>
report.expense_items ) %>

    </ul>
    <%= submit_tag 'Update' %>
    </form>
  </body>
</html>

Partial is

<% @expense_item = expense_items %>
 <%= text_field 'expense_item', 'notes', 'index' => 1 %> <br>
 <%= text_field 'expense_item', 'notes', 'index' => 2 %><br>

But I am getting the following error ::

LoadError in <controller not set>#<action not set>
Already loaded file
'./script/../config/../app/controllers/expensereport.rb' but
'Expensereport' was not set, perhaps you need to rename
'./script/../config/../app/controllers/expensereport.rb'?
RAILS_ROOT: ./script/../config/..

Please let me know whats the issue here.

Bye ,
Rathish
Wilson B. (Guest)
on 2005-12-22 17:43
(Received via mailing list)
In create_report, try calling it:
@expense_report instead of 'report'.
It needs to be an instance variable, and the name of your text field
is expense_report, not report.  You will need to update this name in
the render(:partial) call, as well.

I'm not sure if this is part of the problem, but try naming your
controller ExpenseReportController, rather than
ExpensereportController.

Finally, in the partial form, 'index' should be the same for every
loop through the partial.
Doing this:
>  <%= text_field 'expense_item', 'notes', 'index' => 1 %> <br>
>  <%= text_field 'expense_item', 'notes', 'index' => 2 %><br>
..will create a pair of items with index '1' and '2' for every entry
in the collection you pass to render(:partial).
Unless that's what you want, you should change those to be variables.

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