db architechture of a time sheet application in RoR - ruby-on-rails

As I am reading the rails 3 way I wanted to make a small app that will help me learn along with the book. I am thinking of making a simple timesheet application in rails, however, I can't figure out where to start as far as calendar functionality goes. This is what I have so far.
User has_many Timesheets
Timesheet belongs_to user
Timesheet will have date:datetime, hours:datetime and comments:string attributes.
how can I map a whole month to the timesheet?
For example.
if the user wants to fill timesheet for 06/01/2011 - 06/07/2011 will he have seven rows in the Timesheet table?
I would like someone to explain a brief architecture of ActiveRecord for this.

I would at least start out with something like this:
class User < ActiveRecord::Base
has_many :timesheets
end
class Timesheet < ActiveRecord::Base
belongs_to :user
has_many :work_days
end
class WorkDay < ActiveRecord::Base
belongs_to :timesheet
end
Timesheet will have a user_id:integer attribute.
WorkDay will have day:date, hours:integer, comment:text, and timesheet_id:integer attributes.
Then you can create timesheets with any collection of individual workdays you wish.
The form for a WorkDay could look something like this:
<%= form_for #work_day do |f| %>
<%= f.label(:timesheet, 'Timesheet') %>:
<%= f.collection_select(:timesheet_id, Timesheet.all, :id, :name) %><br />
<%= f.label :day, 'Date' %>:
<%= f.date_select :day %><br />
<%= f.label :hours, 'Hours worked' %>:
<%= f.text_field :hours %><br />
<%= f.label :comment, 'Comments' %>:
<%= f.text_area :comment %><br />
<% end %>
This will allow you to create a new WorkDay with all of its attributes and attach it to any existing Timesheet.
Note: This will look for a name method in Timesheet, which you could implement like this, for example:
def name
"Timesheet ##{self.id}"
end

Related

Nested model form not submitting everything

I heard about this community while listening to Hypercritical and I am excited to join in with my first question. I am working on my first rails App and I have run into an issue that I cannot seem to crack. I have been watching Railscast, Lynda.com, and Googling for days but I still cannot comprehend how to create a form that that will update my has_many :through associations at once. Allow me to try an explain what I am doing.
My Goal:
The firm I work for provides many "Service Offerings" and I want to be able to create a new service offering on one page and have it create the contacts and other information that is associated with it. The additional information such as "contacts" will live in their own tables because they may need to be referenced by many "Service Offerings."
Problem:
When I submit the form the "Service Offering" fields submit and are entered into the database, but the fields for the "Business Developer" do not. Obviously, I would like everything to be entered into its appropriate table and the for the IDs to be linked in the join table. I would really appreciate any insight that you could provide.
What I Have So Far: What you see below is Service Offerings and Business Developers. Eventually I will be adding Contacts, Photos, and Files but I thought I would start simply and work my way up.
Models:
class ServiceOffering < ActiveRecord::Base
attr_accessible :name, :description
has_many :business_developer_service_offerings
has_many :business_developers, :through => :business_developer_service_offerings
accepts_nested_attributes_for :business_developer_service_offerings
end
class BusinessDeveloper < ActiveRecord::Base
attr_accessible :first_name, :last_name
has_many :business_developer_service_offerings
has_many :service_offerings, :through => :business_developer_service_offerings
end
class BusinessDeveloperServiceOffering < ActiveRecord::Base
belongs_to :business_developer
belongs_to :service_offering
end
Controller:
def new
#service_offering = ServiceOffering.new
#service_offering.business_developers.build
end
def create
#service_offering = ServiceOffering.new(params[:service_offering])
if #service_offering.save
redirect_to(:action => 'list')
else
render('new')
end
end
View:
<%= form_for((#service_offering), :url => {:action => 'create'}) do |f|%>
<p>
<%= f.label :name%>
<%= f.text_field :name %>
<%= f.label :description%>
<%= f.text_field :description %>
</p>
<%= f.fields_for :business_developer do |builder| %>
<p>
<%= builder.label :first_name%>
<%= builder.text_field :first_name %>
<%= builder.label :last_name%>
<%= builder.text_field :last_name %>
</p>
<%end%>
<%= f.submit "Submit" %>
<%end%>
I figured it out. It turns out a few things were wrong and needed to be changed in addition to the two suggestions #Delba made.
The Form:
I took a look at RailsCasts #196 again and noticed that my form looked different than the one used there, so I tried to match it up:
<%= form_for #service_offering do |f|%>
<p>
<%= f.label :name%>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_field :description %>
</p>
<%= f.fields_for :business_developers do |builder| %>
<p>
<%= builder.label :first_name %>
<%= builder.text_field :first_name %>
<%= builder.label :last_name %>
<%= builder.text_field :last_name %>
</p>
<%end%>
<%= f.submit "Submit" %>
<%end%>
Initially, this presented an error:
undefined method `service_offerings_path'
Routes:
This lead me to learn about RESTful Routes because I was using the old routing style:
match ':controller(/:action(/:id(.:format)))'
So I updated my routes to the new RESTful Routes style:
get "service_offerings/list"
resource :service_offerings
resource :business_developers
attr_accessible:
That got the form visible but it was still not working. So I did some searching around on this site and found this post that talked about adding "something_attributes" to your parent objects model under attr_accessible. So I did:
class ServiceOffering < ActiveRecord::Base
has_many :business_developer_service_offerings
has_many :business_developers, :through => :business_developer_service_offerings
accepts_nested_attributes_for :business_developers
attr_accessible :name, :description, :business_developers_attributes
end
That change along with #Delba's suggestion shown in the above model and controller listed below solved it.
def new
#service_offering = ServiceOffering.new
#business_developer = #service_offering.business_developers.build
end
You just forgot to assign #business_developper.
def new
#service_offering = ServiceOffering.new
#business_developper = #service_offering.business_developpers.build
end
-
#business_developer = #service_offering.business_developers.build
initializes an instance of biz_dev which is then available in the view.
fields_for :biz_dev isn't really tied to this instance but to the many-to-many relationship btw serv_off and biz_dev.
In this way, you can add multiple input for additional biz_dev if you initialize another biz_dev instance in your controller. For instance:
5.times { #service_offering.biz_dev.build }
will add additional fields in your form without you having to declare them in your view.
I hope it helped.

Rails updating multiple models on a single form

Im writing a form which uses formtastic to manage the BusinessUnit model, however when creating a new BusinessUnit it also has to create a number of other record types. The associations between the models are as below:
class BusinessUnit < ActiveRecord::Base
has_many :business_unit_sites
has_many :locations
class BusinessUnitSite < ActiveRecord::Base
belongs_to :site
belongs_to :business_unit
class Site < ActiveRecord::Base
has_many :locations
has_many :business_unit_sites
class Location < ActiveRecord::Base
belongs_to :business_unit
belongs_to :site
When a BusinessUnit is created, a Site must also be created using BusinessUnitSite as a join table. In addition a Location record should be created which must hold a foreign key to the new Site record and this is where Im having problems.
I can create a new Location using a nested form (below) but the Site will have to be created manually.
<%= semantic_form_for #business_unit do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<%= f.input :business_unit_id %>
<%= f.input :business_unit_group, :include_blank => false %>
<%= f.input :business_unit_type %>
<%= f.input :tax_region, :include_blank => false %>
<%= f.semantic_fields_for :locations do |l| %>
<%= l.input :name, :label => "Location Name" %>
<% end %>
<% end %>
<%= f.buttons %>
<% end %>
What is the best way to create the Location, Site records and ensure that Location holds the foreign key of the newly created Site?
You probably want to do something like using the "fields_for" approach for the sub-objects in your form.
See this related answer:
Multiple objects in a Rails form
More info about fields_for:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for

Help with rails belongs_to

I'm stuck -
I'm building an accounting app in rails, and have a model where a customer will have multiple invoices as well as multiple payments (related to those invoices)
Here's a quick look at a simplified model:
class Customer < ActiveRecord::Base
has_many :customer_payments
has_many :invoices
accepts_nested_attributes_for :customer_payments, :allow_destroy => true
end
class CustomerPayment < ActiveRecord::Base
has_many :customer_payment_items
belongs_to :customer
belongs_to :invoice
accepts_nested_attributes_for :customer_payment_items
end
class CustomerPaymentItem < ActiveRecord::Base
belongs_to :invoice, :inverse_of => :customer_payment_items
belongs_to :customer_payment
end
class Invoice < ActiveRecord::Base
has_many :invoice_lines, :dependent => :destroy
has_many :customer_payment_items, :inverse_of => :invoice
belongs_to :customer
accepts_nested_attributes_for :invoice_lines, :allow_destroy => true
end
I have a nested form where I want to show Customer attributes, CustomerPayment attributes and CustomerPaymentItem attributes - which all works fine.
I also want to show Invoice attributes for each CustomerPaymentItem (each CustomerPaymentItem relates back to a single invoice) and while I can get a form to show the CustomerPaymentItem info, I can't get it to show Invoice information which is needed to give reference to the user. - I'm having trouble getting data from a belongs_to association to show on the form.
I'm at a loss - Shouldn't I be able to traverse the belongs_to association? FYI - I can send data to the log where I know the invoice data is populated during the CustomerPayment.new call, it seems to just get lost between the controller and the form.
How should I access that data? Here's the form info -- (coming from a couple of rendered forms) the stuff that doesn't show is in between the ---.
<p>
<%= f.label :amount %><br />
<%= f.text_field :amount %>
</p>
<p>
<%= f.label :invoice_id %><br />
<%= f.text_field :invoice_id %>
</p>
<p>
<% f.fields_for :invoice do |builder| %>
--- doesn't show
Invoice Detail
<p>
<%= builder.label :number %><br />
<%= builder.text_field :number %>
</p>
<p>
<%= builder.label :sub_total %><br />
<%= builder.text_field :sub_total %>
</p>
--- doesn't show
<% end %>
</p>
Am I missing something in my fields_for to show the :invoice reference data? Is my model too complex for rails to make sense of?
#corroded was right - the issue was my lack of an = sign in
<% f.fields_for :invoice do |builder| %>
I guess everyone needs to learn that lesson

One-To-Many Model Update RoR

I've got a one-to-many relationship from Treatment to Cost. The reason for this is because for invoicing and tax purposes we need to keep a record of changes in price for a treatment. So this is implemented by saying that the cost is equal to the most recent cost entry associated with that treatment.
This feature needs to be completely transparent to the user, they shouldn't know anything about the historical costs, just that there's a current one.
So when they hit edit, and do an update, if the cost of the treatment were to change, I then want to update the cost table also. The problem I have is with the models being represented in a form.
<% form_for([:admin, #treatment]) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_field :description %>
</p>
<p>
<%= f.label :duration %><br />
<%= f.text_field :duration %>
</p>
<p>
<%= f.submit 'Save' %>
</p>
<% end %>
So Treatment has_many :cost in the model, and in this form I want to have the cost field with the latest cost in it. For a start, how do I do that? Additionally, how do I update the cost model if the cost has changed? I'm assuming it's done in the controller, but if I have a text_field for the cost, how do I disassociate it with the #treatment model?
Cheers
I had a similar issue, so I had my has_many association but then I had a has_one association that pointed to the most recent version.
NOTE: I am using MSSQL, so your syntax might be a little different.
class Treatment < ActiveRecord::Base
has_many :costs
has_one :current_cost, :class_name => "Cost",
:conditions => "costs.id = (SELECT MAX(c.id)
FROM costs c
WHERE c.treatment_id = costs.treatment_id)"
end
I started with this:
class Treatment < ActiveRecord::Base
has_many :costs
def current_cost
self.costs.last(:order => "id")
end
end
But after it got large enough, I needed to minimize database dips and this was becoming a bottleneck.
If you had the ability of setting an enabled field in the costs model and using a validation to ensure that only one cost is enabled per treatment. Then I would recommend:
class Treatment < ActiveRecord::Base
has_many :costs
has_one :current_cost, :class_name => "Cost", :conditions => ["costs.enabled = ?", true]
end
I hope this helps!

Rails form with three models and namespace

Banging my head against this one for a long time. On Rails 2.3.2, Ruby 1.9.1.
Trying to use one form to create three objects that have these relations:
class Person
has_one :goat
end
class Goat
belongs_to :person
has_many :kids
end
class Goat::Kid
belongs_to :goat
end
Here's a summary of the schema:
Person
first_name
last_name
Goat
name
color
Goat::Kid
nickname
age
I'd like my #create action to instantiate new instances of all three models with the specified associations. However, while it appears that my params hash is being passed to the controller as it should (based on the backtrace logs in the browser when it blows up), the Goat::Kid object is not collecting the params.
irb (irb session is just a psuedo-representation of what I'm trying to accomplish so if it doesn't call #save! or any other necessities it's not really meant to be correct. I'm trying to do this all through the browser/web form.)
a = Person.new :first_name => 'Leopold', :last_name => 'Bloom'
b = Goat.new :name => 'Billy', :color => 'white'
c = Goat::Kid.new :nickname => 'Jr.', :age => 2
a.goat.kids
>> []
Now, I cannot figure out how to get the view to pass the params to each object and to get the controller to save these params to the db.
My questions: A) is this a good place to use nested_attributes_for and if so how do I declare that with a namespace? B) is there a much simpler, easier to understand way to do this?
Passing params to three models has just been very challenging to me and no matter how much documentation I read I can't wrap my head around it (#form_for and #fields_for). The namespace further complexifies this. Thanks for any help!
Addendum: if I end up declaring
accepts_nested_attributes_for
what's the proper way to use the symbol argument for a namespaced model?
accepts_nested_attributes_for :kids, :through => :goats
or
accepts_nested_attributes_for :goats_kids, :through => :goats
or
accepts_nested_attributes_for :goats::kids, :through => :goats
I'm not sure how namespaced models translate to their symbol identifiers. Thanks!
Well, this is my first time playing with accepts_nested_attributes_for, but with a little playing around I was able to get something to work.
First the model setup:
class Person < ActiveRecord::Base
has_one :goat
accepts_nested_attributes_for :goat
end
class Goat < ActiveRecord::Base
belongs_to :person
has_many :kids
accepts_nested_attributes_for :kids
end
class Goat::Kid < ActiveRecord::Base
belongs_to :goat
end
With a simple restful controller:
ActionController::Routing::Routes.draw do |map|
map.resources :farm
end
class FarmController < ApplicationController
def new
end
def create
person = Person.new params[:person]
person.save
render :text => person.inspect
end
end
Then comes the semi-complex form:
Next, the form setup:
<% form_for :person, :url => farm_index_path do |p| %>
<%= p.label :first_name %>: <%= p.text_field :first_name %><br />
<%= p.label :last_name %>: <%= p.text_field :last_name %><br />
<% p.fields_for :goat_attributes do |g| %>
<%= g.label :name %>: <%= g.text_field :name %><br />
<%= g.label :color %>: <%= g.text_field :color %><br />
<% g.fields_for 'kids_attributes[]', Goat::Kid.new do |k| %>
<%= k.label :nickname %>: <%= k.text_field :nickname %><br />
<%= k.label :age %>: <%= k.text_field :age %><br />
<% end %>
<% end %>
<%= p.submit %>
<% end %>
From looking at the source for accepts_nested_attributes_for, it looks like it will create a method for you called #{attr_name}_attributes=, so I needed to setup my fields_for to reflect that (Rails 2.3.3). Next, getting the has_many :kids working with accepts_nested_attributes_for. The kids_attributes= method was looking for an array of objects, so I needed to specify the array association in the form manually and tell fields_for what type of model to use.
Hope this helps.

Resources