Reputation: 12663
I have an Invoice model in a Rails app. The Invoice has_many Products and Services (two separate models). The Invoice show view displays these associated records.
Both Products and Services have a similar structure, i.e., Item, Price, Quantity. I can calculate line totals as price * quantity
and I can calculate the total price of Products as all.collect { |product| product.price * product.quantity }
, same for Services.
What if I now want to calculate the total cost of Products + Services on this Invoice?
How can I perform calculations across multiple models? And where should the calculation logic go?
I realize one solution would be to combine Products and Services into a single model, but this is not an option here.
I'm lost as to where to start with this, and would really appreciate some pointers, ideas or links to useful documentation.
Upvotes: 3
Views: 1099
Reputation: 21584
Just to check:
and you're asking where to put the logic for total price of products and services of an invoice. Correct?
In that case, you are correct to put it in Invoice:
def total_cost_of_products
products.collect { |product| product.price * product.quantity }
end
def total_cost_of_services
services.collect { |services| service.price * service.quantity }
end
def total_price_of_products_and_services
total_cost_of_products + total_cost_of_services
end
Of course you can DRY this up more and you probably want some unit tests on your invoice model. See lucapette's answer for a good way to refactor and DRY it up
Upvotes: 2
Reputation: 20724
In short: Use modules. I'm thinking something like:
module TotalCalculator
def sum
map { |e| e.price * e.quantity }
end
end
And then you can:
class Product
include TotalCalculator
end
Of course, it's just a trivial example and the naming is terrible. By the way, you can get the picture. I would put all the logic in a dir like app/logic
or in the lib
directory. Sometimes I call this technique module as business logic pattern but I don't know if it's a proper name.
Upvotes: 2
Reputation: 2478
Can you not just make a virtual attribute on your invoice model total_cost and then put your code in there?
def total_cost
cost = #do your calculation here....
return cost
end
Upvotes: 0