Rails activerecord query - sql

I have two models:
class Invoice < ActiveRecord::Base
has_many :payments_received
end
class PaymentReceived < ActiveRecord::Base
belongs_to :invoice
end
I need to write a query to find invoices that have either no payments_received or the sum of the payments_received.amount is less than the invoice.amount
I am able to find invoices with no payments_received:
#invoices = Invoice.includes(:payments_received).where( :payments_received => { :invoice_id => nil } )
But I need to find invoices where the sum of all of its associated payments_received is less than invoice.amount.
This is a class method that accomplishes what I need, but I'd like to do this without pulling all of the invoices out of the database and iterating through each one of them.
def self.unpaid
unpaid_invoices = []
total_payments = 0
invoices = Invoice.all
invoices.each do |invoice|
if invoice.payments_received.empty?
unpaid_invoices << invoice
else
invoice.payments_received.each do |payment_received|
total_payments += payment_received.amount
end
if total_payments < invoice.amount
unpaid_invoices << invoice
end
end
end
unpaid_invoices
end

you can chain where clauses:
#invoices = Invoice.includes(:payments_received).where( :payments_received => { :invoice_id => nil } ).where("payments_received.amount > ?", amount)
try writing them both individually and after you're getting what you want to return chain the query together.

I hope you want something like this.
#invoices.collect do |invoice|
invoice.payments_received.pluck(:amount).sum < invoice.amount ? invoice : next
end.compact

Related

How to group by attribute but display value of nested attribute?

I've got these models in my Rails 6 application:
class Client < ApplicationRecord
belongs_to :account
has_many :people
end
class Person < ApplicationRecord
belongs_to :client
end
class Payment < ApplicationRecord
belongs_to :client
end
In my SharesController I am trying to generate the total payments for each client and show them as a pie chart:
class SharesController < ApplicationController
def index
#clients = current_account.clients.joins(:payments)
.where(:payments => {:date => #range, :currency => #currency})
.order("sum_payments_#{#part} DESC")
.group("clients.id", "clients.name")
.having("sum_payments_#{#part} > 0")
.sum("payments.#{#part}")
end
end
The problem with this is that it groups by client correctly. However, rather than showing each client's name I want to show the last_name of each client's first nested person.
How can this be done?
Thanks for any help.
You should try to create a join between Client and Person and then use uniq to avoid duplicated clients.
You could try something like this (I'm not sure if this code works but just to make it clearer what I mean)
#clients = current_account.clients.joins(:payments, :people)
.where(:payments => {:date => #range, :currency => #currency})
.order("sum_payments_#{#part} DESC")
.group("clients.id", "people.last_name")
.having("sum_payments_#{#part} > 0")
.sum("payments.#{#part}")

Get count of one-to-many records efficiently

I have two tables: App (with a release_id field) and User.
Models
class App < ActiveRecord::Base
has_one :user
end
class User < ActiveRecord::Base
belongs_to :app
end
Class
class Stats::AppOverview
def initialize(from:, apps:)
#from = from || Date.new(2010)
#apps = apps
end
def total_count
{ apps: { total: total_app, with_user: app_with_user } }
end
private
def app_since
#apps.where('created_at >= ?', #from)
end
def total_app
devices_since.count
end
def app_with_user
User.where(app: app_since).count
end
end
I would like to return
the number of app records for a given array of release_id
the number of app records that belong to each user, satisfying other criteria
This is how I use the class
Stats::AppOverview.new(from: 1.month.ago.iso8601,
apps: App.where(release_id: [1,2,3,4,5,19,235]).total_count
#=> { apps: { total: 65, with_user: 42 } }
For the moment I do it in two queries, but is it possible to put them in the same query? Is this possible using active record?

ActiveRecord query based on multiple objects via has_many relationship

I have a Product class that has_many Gender through Connection class instances. I want to query to find products that have both end_a and end_b present. The current class method works with 2 caveats:
Fails to return correctly if searching where end_a and end_b are the same. Instead should search if product has 2 instances, not just one of object.
Returns an Array when I want an ActiveRecord_Relation.
The class method .query is below, any feedback or ideas are appreciated.
class Product < ActiveRecord::Base
has_many :connections, dependent: :destroy, as: :connectionable
has_many :genders, through: :connections
def self.query(end_a, end_b)
search_base = active.joins(:connections)
end_a_search = search_base.where(connections: { gender_id: end_a } )
end_a_search & search_base.where(connections: { gender_id: end_b } )
end
end
ps: Once this is figured out will likely move this to a scope for Product
class Product < ActiveRecord::Base
has_many :connections, dependent: :destroy, as: :connectionable
has_many :genders, through: :connections
scope :with_genders, -> (end_a, end_b) {
relation = joins('INNER JOIN connections c1 ON c1.connectionable_id = products.id AND c1.connectionable_type = \'Product\'')
.joins('INNER JOIN connections c2 ON c1.connectionable_id = c2.connectionable_id AND c2.connectionable_type = \'Product\'')
.where(c1: {gender_id: end_a}, c2: {gender_id: end_b})
.group('products.id')
end_a == end_b ? relation.having('COUNT(products.id) > 1') : relation
}
end

Group By multiple column for sql count statement , in rails environment

I have two Relations (class)
class RecommendedForType < ActiveRecord::Base
attr_accessible :description, :name
has_many :recommended_for_type_restaurants
end
And
class RecommendedForTypeRestaurant < ActiveRecord::Base
attr_accessible :recommended_for_type_id, :restaurant_id, :user_id
belongs_to :restaurant
belongs_to :user
belongs_to :recommended_for_type
def self.get_count(rest_id)
r = RecommendedForTypeRestaurant.where(restaurant_id: rest_id)
#result = r.includes(:recommended_for_type)
.select("recommended_for_type_id, recommended_for_types.name")
.group ("recommended_tor_type_id, recommended_for_types.name")
.count
end
end
if I call
RecommendedForTypeRestaurant.get_count(1) # get stat of restaurant_id: 1
I get
{"dinner"=>1, "fast food"=>1, "lunch"=>3, "romantic dinner"=>1}
My goal is to get both id and name of RecommendTypeFor in the return result as well. currently i can only make it return either id or name. Something like this
{{"id" => 1, "name" => "dinner", "count" => 1}, {"id" =>2, "name" => "lunch", "count" => 3} }
For sure i can do another round of sql once i get id or name to create that hash but i think that not the efficient way. Appreciate any help or suggestion :)
You need to group separately, try the following group.
.group ("recommended_tor_type_id", "recommended_for_types.name").count
I believe that if you remove the .count at the end and change the select to:
.select("count(*), recommended_for_type_id, recommended_for_types.name")
You will get an array of models that will have the attributes you need and the count.
You should be able to test it out in the console by do something like this:
r = RecommendedForTypeRestaurant.where(restaurant_id: rest_id)
#result = r.includes(:recommended_for_type)
.select("recommended_for_type_id, recommended_for_types.name, count(*)")
.group ("recommended_tor_type_id, recommended_for_types.name")
#result.each do |r|
puts r.recommended_for_type_id
puts r.name
puts r.count
end
Hope it helps!

Improve Efficiency of Ruby Model Queries

Problem:
In my customer model, I want to sum up all transactions where each payments' status is successful and either the customer_id matches the id of the current instance or the current instance's parent_id. First, I was unable to get this to work at all because I was trying to do it all in one query. Now I have what I'm looking for, but I'm afraid I'm not being nearly as efficient as I could be.
Context:
The transactions table has a customer_id column
the customers table has a parent_id column.
Customer class has_many :transactions
Transactions class belongs_to :customer
Transactions class has_many :payments
Here are my models:
class Customer < ActiveRecord::Base
has_many :transactions
end
class Transaction < ActiveRecord::Base
belongs_to :customer
has_many :payments
end
class Payment < ActiveRecord::Base
has_one :transaction
end
Question:
If this is the model property I'm using currently, how can I improve its performance, if at all?
def total_spent
if customer_type == 1 # "child" customer
Transaction.joins(:payments).where('payments.status' => 3).sum(:amount, :conditions => ['customer_id = ?', id])
elsif customer_type == 2 # "parent" customer
temp_transactions = Transaction.joins(:payments).where('payments.status' => 3)
temp = temp_transactions.sum(:amount, :conditions => ['customer_id = ?', id])
Customer.find_all_by_parent_group_id(id).each do |c|
temp = temp + temp_transactions.sum(:amount, :conditions => ['customer_id = ?', c.id])
end
temp
end
end
Transaction.joins(:payments)
.joins(:customer)
.where(:payments => {:status => 3})
.where("customers.id = ? or customers.parent_id = ?", 5, 5)
.sum(:amount)
SELECT SUM(amount) AS sum_id
FROM "transactions"
INNER JOIN "payments" ON "payments"."transaction_id" = "transactions"."id"
INNER JOIN "customers" ON "customers"."id" = "transactions"."customer_id"
WHERE
"payments"."status" = 3
AND (customers.id = 5 or customers.parent_id = 5)