Picture a normal invoice: On it, you have several items. Each item has a quantity and a price per unit, among other things (unit and description).
The total amount for each item is calculated like this: quantity * price per unit.
This is done for each item. Then, the overall invoice’s net amount is the sum of all the totals. Add VAT, and you have the invoice’s gross amount.
This is what I am trying to do with my Rails app. An invoice has many items and accepts nested attributes for them. Generally, this all works fine.
Following the logic, all I need to enter in manually is the price per unit and the quantity for each item as well as the invoice’s VAT. The totals and the resulting net and gross amount should be calculated automatically. I want to achieve this using the before_save filter.
Here is my invoice model:
before_save :calculate_net_amount, :calculate_gross_amount
def calculate_net_amount
self.items do |item|
self.net_amount += item.total
end
end
def calculate_gross_amount
self.gross_amount = self.net_amount * (1 + self.vat_rate)
end
This is the item model:
before_save :calculate_total
def calculate_total
self.total = self.quantity * self.price_per_unit
end
And here is my spec that is failing:
it "calculates the net amount from all item totals" do
invoice = FactoryGirl.build(:invoice)
item = invoice.items.build(quantity: 2, unit: "Unit", description: "Desc", price_per_unit: 2)
invoice.save
invoice.net_amount.should == 4
end
It uses this invoice factory:
FactoryGirl.define do
factory :invoice do
association :client
currency "EUR"
vat_rate 0.19
net_amount 1
payment_on "2013-01-01"
status "paid"
end
end
The test basically does the following: An invoice with 2 things that both cost USD 2 should have a net amount of USD 4. Instead, the test returns 1, which seems to come from the factory and apparently isn’t overwritten. If I remove it from the fixture, it says that it cannot calculate the gross amount anymore, since it can’t use * on any nil object.
I am assuming I am doing something wrong with the filters and the order in which they are called – the total amounts are calculated correctly, so it has to be something about the calculate_net_amount method that’s going wrong and as a result it can’t calculate the gross amount anymore.
Can you see what I am doing wrong?
should be
Since
itemsis an accessor, which is a method, it can technically take a block, but that block isn’t getting called, so no summing is happening. It’s an easy typo to make.As an aside, it’s better is to sum using
inject: