Rails multiply joined columns with foreign key null - ruby-on-rails

I have these tables:
STOCKDIARY
- ID, DATE, PRODUCT, UNITS, PRICE
PRODUCTS
- ID, NAME, CONSIGNOR
CONSIGNORS
- ID, NAME, COMMISSION
I have setup all associations. So that if in my view I use:
stockdiary.product.consignor.try(:COMMISSION)
I get the right commission for each product in a stockdiary line.
Note that I use try because not all products have a consignor.
But now I need to show stockdiary.PRICE * stockdiary.product.consignor.COMMISSION
So in Stockdiary model I set
def total_commission
(self.PRICE * self.product.consignor.COMMISSION)
end
But this gives me an error:
undefined method 'COMMISSION' for nil:NilClass
I think the problem comes from the fact that some product has empty value in CONSIGNOR column. Just to make sure I have entered a value for each row on table CONSIGNORS column COMMISSION. But error is still there.
I would like stockdiaries row with products without a consignor to show 0 as commission.
How to fix?

Method1:
Give the try column to consignor instead of COMMISSION or give for both.
def total_commission
(self.PRICE * self.try(:product).try(:consignor).try(:COMMISSION))
end
Method2:
Also you can use,
def total_commission
consignor = self.product.consignor.present? ? self.product.consignor.COMMISSION : 0
(self.PRICE * consignor)
end
what it does is, if there is consignor, it takes it else it takes 0 as you mentioned.

As you said before:
Note that I use try because not all products have a consignor.
So you need:
def total_commission
commission = self.product&.consignor&.COMMISSION
if self.PRICE && commission
(self.PRICE * commission)
else
nil
end
end

def total_commission
commission = product.consignor.try(:COMMISSION)
commission ? (PRICE * commission) : 0
end

Related

Rails show total column when data comes from model method

I have a model Invoices
Table Invoices:
ID, PRODUCT, UNITS, PRICE
In my view, I want to show total for some columns.
For instance to show total quantity I can set in my controller:
#invoices = Invoice.group(:PRODUCT).select(ID, PRODUCT, UNITS, SUM(UNITS) AS TOTALUNITS, PRICE").order('PRODUCT ASC')
#units_total = #invoices.map(&:UNITS).sum
and in my view
<%= #units_total %>
and it returns the total in column UNITS. This works fine.
If I define in my model:
def total_amount
(self.PRICE * self.TOTALUNITS)
end
and then on the same way I want to show the total of total_amount, I tried in my controller:
#amount_total = #invoices.map(&:total_amount).sum
it doesn't work, as I assume that if I'm not using a column name the syntax must be different.
What should I enter then?
UPDATE
The problem comes form the fact that I'm using in model (self.PRICE * self.TOTALUNITS). I didn't include it when I posted the question as I thought it didn't matter. But if I replace(self.PRICE * self.TOTALUNITS) with (self.PRICE * self.UNITS) there's no error but values are obviously wrong.
What you have taken is correct, I think some of the invoices doesnot have the value, check using try,
def total_amount
price = self.try(:PRICE) || 0
units = self.try(:UNITS) || 0
price * units
end
make 0 if the corresponding field value is absent, so that you will not get an error.
You can check if this is working or not
#amount_total = #invoices.map{ |invoice| invoice.PRICE.to_f * invoice.UNITS.to_i }.sum
I don't know what error you are getting but it might be due to null values in the column.
Hope that helps!

ActiveRecord sum

I have a model which contains items for sale. The model is mirroring an API, so I can't change it's structure.
In this model I have the fields price and sold_quantity.
It doesn't seem right to iterate through the query with a loop.
current_user.items.each do |item|
total += item.price * item.sold_quantity
end
Is there a way to get the same total using only ActiveRecord? Like .sum(:price) but multiplying by sold_quantity?
You probably want this:
current_user.items
.select('items.*, items.price * items.sold AS total')
.all
Or, if you want the totals and nothing else:
current_user.items.pluck('items.price * items.sold')
You can do this:
current_user.items.sum { |item| item.price * item.sold_quantity }
I hope this help you.
For more information sum in AR
One way you could approach this would be to move this logic into the Item class:
class Item < ActiveRecord::Base
...
def total
self.price * self.sold_quantity
end
def self.sold_value
all.map{|a| a.total}.sum
end
end
Here each instance of item is calculating it's own total, and a class method allows you get the sold value on any collection of items.
#user.items.sold_value

Getting the percentage of a relationship

Orders have many line_items, which in turn have an employee_id and amount_cents. In the second line of this query, I try creating a line_item_perc variable by gathering all the line_items with a certain employee_id and dividing their amounts_cents by all the other line_items:
Order.joins(:tips, :line_items)
.select('line_items WHERE(custom_attributes -> "employee_id" = ?) AS employee_line_items,
SUM(employee_line_items.amount_cents) / SUM(line_items.amount_cents) AS line_item_perc',
params[:employee_id])
.sum('tips.amount_cents * line_item_perc')
Unfortunately, this doesn't work. The select statement doesn't even appear in the final SQL. (I'm assuming this is because sum overwrites it.) So what else could I do to get line_item_perc?
def line_item_percent(employee_id, line_item_id)
total_amount_in_cents = Order.joins(:line_items).where("employee_id = ?", employee_id).pluck(:amount_cents).sum
line_item_cents = LineItem.find(line_item_id).amount_cents
answer = line_item_cents / total_amount_in_cents
end #returns decimal
Try this. Did it off top of head with rails not SQL.

How to return the next record?

In my Rails app I have a function next which I am using in my show templates to link to the next product (ordered by price):
class Product < ActiveRecord::Base
belongs_to :user
def next
Product.where("user_id = ? AND price > ?", user_id, price).order("price ASC").first
end
end
The problem is that this function works only when all products have different prices. The moment there are multiple products with the same price, the next function picks one product randomly (?) thereby skipping all the others.
Is there a way to make this function return the next product, even though it has the same price? (if the price is the same, the products could be ordered simply by ID or date)
I tried replacing > with >= but that didn't work.
Thanks for any help.
You have to use id in where clause and in order as well
def next
Product.where("user_id = ? AND price >= ? AND id > ?", user_id, price, id).order("total, id").first
end

Filter order needing changes for invoice calculation?

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?
self.items do |item|
should be
self.items.each do |item|
Since items is 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:
self.net_amount = self.items.inject(0){|sum, item| sum + item.total}

Resources