Reputation: 19929
I am writing a simple accounting system for managing costs. The structure is like this:
Invoice - can have many products
Product - can have many costs, also can act_as_tree
LineItem -
Product
LineItem
LineItem
Product
LineItem
Product
LineItem
LineItem
I had this set up as a has_many and belongs_to for the three classes but think that the following is more appropriate (based upon reading Rails 3 Way - any shortcomings are my lack of understanding; just trying to give context)
Class Invoice < ActiveRecord::Base
has_many :products
has_many :line_items, :through => :products
end
Class Product < ActiveRecord::Base
belongs_to :invoice
belongs_to :line_item
end
class LineItem < ActiveRecord::Base
has_many :products
has_many :invoices, :through => :invoices
end
But I don't this is working correctly.
if I do the following:
>@i=Invoice.find(1)
>@i.products # this works
>@i.line_items # doesn't work, unidentified method line_items
This is the first time I'm using has_many :through. Is this set up correctly for my data model? Also, is it possible to use it in conjunction with acts_as_tree - I'd like to be able to say:
>@i.line_items
and get back all the line items for that specific invoice. Possible?
thx for help
Upvotes: 1
Views: 410
Reputation: 19239
Your Invoice should only have line items! These can be a tree structure but you should not have products directly referenced from your invoice (what if a product price changes: this would affect existing invoices!)
So, to fix your structure:
invoice
line_item # references a product, but has its own fields (price!)
line_item
line_item
Each line_item
should reference one product using belong_to
. It's your join model between invoices and products:
class Invoice
has_many :line_items
end
class LineItem
belongs_to :invoice
belongs_to :product
acts_as_tree # (implies has_many :line_items with the parent invoice_id, etc.)
end
class Product
has_many :line_items
end
This is basically all you need to build an invoice. You can think of the tree structure as being independent from the Invoice:LineItem:Product
relationship. It can work as a flat list, or enhanced to become a tree.
If your products normally contain other products and you need to know which children to add to the invoice when the parent product is added (as line items), you'll need to tree your products too:
class Product
has_many :line_items
acts_as_tree
end
This tree structure is independent of the tree structure in line items. Remember: products can change, and this shouldn't affect existing line items in your invoices.
Upvotes: 0
Reputation: 6354
why did you choose this structure? in such cases i usually do
Class Invoice < ActiveRecord::Base
has_many :line_items
has_many :products, :through => :line_items
end
Class Product < ActiveRecord::Base
has_many :line_items
# if you need
has_many :products, :through => :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :products
belongs_to :invoice
end
this fullfills following requirements:
Invoice and Product have a Many2Many relationship The relationship between an Invoice and a Product is a LineItem, which provides further information like price, amount, applied taxes, etc. You can create a LineItem by adding a Product:
invoice = Invoice.create
invoice.products << Product.find_by_name('awesome')
Upvotes: 1
Reputation: 15525
First a question: What is your relation between Product
and LineItem
: Has 1 product many line items or is 1 and the same line item referenced in many products? The rest of this answer is based on the assumption that every product should have multiple line items.
I think your models should be defined like that:
# No change
Class Invoice < ActiveRecord::Base
has_many :products
has_many :line_items, :through => :products
end
# Changed the relation to :line_items
Class Product < ActiveRecord::Base
belongs_to :invoice
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :products
# The following then does not make sense
##has_many :invoices, :through => :invoices
end
Upvotes: 1