Adding a Money object transformation to a sum query in Rails 3.1 - sql

Another noob question that seems like it should be simple:
Thanks to the help received here, I can easily get a sum of selected transactions:
#trip_hash = transactions.sum(:amount_cents, :group => :trip_id)
The issue, however, is that the :amount_cents column represents a raw Money object that needs to be transformed before summing in order to accommodate currency exchange. The Money "composed of" Procs look like this:
composed_of :amount,
:class_name => "Money",
:mapping => [%w(amount_cents cents), %w(currency currency_as_string)],
:constructor => Proc.new { |cents, currency| Money.new(cents || 0, currency || Money.default_currency) },
:converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }
I can easily call:
transactions.map(&:amount).inject(:+)
to get a transformed grand total, but I can't figure out how to do it in the context of the groupings.
Many thanks, again, for the help!

Took a lot of canoodling and reading, but finally figured out the following:
trip_hash = bankroll.transactions.group_by(&:trip_id).map {|tr,t| Hash[tr, t.map(&:amount).inject(:+)]}
=>[{0=>#<Money cents:137693 currency:USD>}, {7=>#<Money cents:-39509 currency:USD>}, {10=>#<Money cents:50009 currency:USD>}]
Map within the map did it! Hashifying makes it view friendly, and it retains the Money object for formatting purposes....

Related

Shopify Order API - Passing Discount

I'm getting some very strange results from Shopify API and I'm hoping someone can help me out.
I'm trying to create an order, with a discount. Its actually saving the order, with the discount information... however the amount is ALWAYS wrong
order_params = {
:browser_ip => webhook[:browser_ip],
:buyer_accepts_marketing => webhook[:buyer_accepts_marketing],
:currency => webhook[:currency],
:email => webhook[:email],
:financial_status => webhook[:financial_status],
:landing_site => webhook[:landing_site],
:note => webhook[:note],
:referring_site => webhook[:referring_site],
:line_items => line_items,
:tag => tags,
:transactions => transactions,
:discount_codes => webhook[:discount_codes],
:total_discounts => webhook[:total_discounts],
:shipping_address => webhook[:shipping_address],
:shipping_lines => webhook[:shipping_lines],
:customer_id => #options[:customer_id],
:billing_address => webhook[:billing_address]
}
#shopify_order = ShopifyAPI::Order.create(order_params)
As you can see its created from webhook data. This is giving me back...(truncated)
"reference"=>nil,
"user_id"=>nil,
"subtotal_price"=>"55.00",
"total_discounts"=>"55.00",
"location_id"=>nil,
"source_identifier"=>nil,
"source_url"=>nil,
"processed_at"=>"2017-05-31T15:53:03-04:00",
"device_id"=>nil,
"phone"=>nil,
"browser_ip"=>nil,
"landing_site_ref"=>nil,
"order_number"=>1140,
"discount_codes"=>
[#<ShopifyAPI::Order::DiscountCode:0x007ffbec42ccb0
#attributes={"code"=>"50% OFF", "amount"=>"55.00", "type"=>""},
#persisted=true,
#prefix_options={}>]
So far, so good all data is correct.. then I save and this happens.. the amount discounted is incorrect... it should be £55.
I haven't dug too deep into this, but I'm pretty sure the code needs to be unique.
i.e. you probably already have a Discount Code with the same code 50% OFF with amount: 60.50 defined somewhere else on that shop.
Try using a new, unique discount code and testing with that.
I would suggest creating a new discount code like 50OFF that has amount: 50 with type: percentage, then you can re-use that for orders with a 50% discount.
See the Price Rules reference for more information >

How do I clear a Model's :has_many associations without writing to the database in ActiveRecord?

For the sake of this question, let's say I have a very simple model:
class DystopianFuture::Human < ActiveRecord::Base
has_many :hobbies
validates :hobbies, :presence => {message: 'Please pick at least 1 Hobby!!'}
end
The problem is that when a human is updating their hobbies on a form and they don't pick any hobbies, there's no way for me to reflect this in the code without actually deleting all the associations.
So, say the action looks like this:
def update
hobbies = params[:hobbies]
human = Human.find(params[:id])
#ideally here I'd like to go
human.hobbies.clear
#but this updates the db immediately
if hobbies && hobbies.any?
human.hobbies.build(hobbies)
end
if human.save
#great
else
#crap
end
end
Notice the human.hobbies.clear line. I'd like to call this to make sure I'm only saving the new hobbies. It means I can also check to see if the user hasn't checked any hobbies on the form.
But I can't do that as it clears the db. I don't want to write anything to the database unless I know the model is valid.
What am I doing wrong here?
Initialy I also did this same way. Then found out one solution for this issue.
You need to do something like this
params[:heman][:hobby_ids]=[] if params[:human][:hobby_ids].nil?
Then check
if human.update_attributes(params[:human])
Hope you will get some idea...
EDIT:
Make hobbies params like this
hobbies = { hobbies_attributes: [
{ title: 'h1' },
{ title: 'h2' },
{ title: 'h3', _destroy: '1' } # existing hobby
]}
if Human.update_atttributes(hobbies) # use this condition
For this you need to declare accepts_nested_attributes_for :hobbies, allow_destroy: true in your Human model.
See more about this here http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
You can try https://github.com/ryanb/nested_form for this purpose..

Rails 3 ActiveRecord Query questions

I've implemented "following" function. Showing "people user A is following" was simple, but showing "people who are following user A" is giving me troubles.
I have follow_links, which have id, user_id, and follow_you_id column. When user A begins following user B, the columns will be like (user_id = A, follow_you_id = B).
To show users that A(#user) is following, I can simply do
#follow_yous = #user.follow_yous
But I'm not sure how to show users who are following A(#user)
To do this, I first found all the related links.
#follow_links = FollowLink.where(:follow_you_id => #user.id)
Now I thought I could just do #follow_mes = #follow_links.users, but it says user is an undefined method. So I guess I can either call user.follow_yous or follow_you.users.
My next approach was
#follow_links = FollowLink.where(:follow_you_id => #user.id)
#follow_mes = User.where(:id => #user.id, :include => #follow_links)
I intended to find all the User objects that had the provided #follow_links objects, but I think the syntax was wrong. I couldn't find a decent solution after a bit of research. I'd appreciate any help.
Update:
FollowLink model
belongs_to :user
belongs_to :follow_you, :class_name => "User"
You can use joins like this:
#users = User.joins(:follow_links).where(:follow_links => { :follow_you_id => #user.id })
you can use following:
#follow_links = FollowLink.where(:follow_you_id => #user.id)
#follow_links.collect(&:user) # :user should be the name of your relation to user in your followlink model
=> [User1, User2,...]

Configuring rails database query so that blank string parameters are ignored

I'm making a rails application so that users can search a database of midi records and find midi files that correspond to the attributes that I've given them.
For example, a user might enter data into an html form for a midi file with name = "blah" composer= "buh" and difficulty = "insane".
This is all fine and well, except that I would like when the user enters no data for a field, that field is ignored when doing the select statement on the database.
Right now this is what my select statement looks like:
#midis=Midi.where(:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer=> params[:midi][:composer],
:difficulty => params[:midi[:difficulty])
This works as expected, but if for example he/she leaves :composer blank, the composer field should not considered at all. This is probably a simple syntax thing but i wasn't able to find any pages on it.
Thanks very much!
Not sure if Arel supports that directly, but you could always do something like:
conditions = {
:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer=> params[:midi][:composer],
:difficulty => params[:midi[:difficulty]
}
#midis=Midi.where(conditions.select{|k,v| v.present?})
Try this:
# Select the key/value pairs which are actually set and then convert the array back to Hash
c = Hash[{
:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer => params[:midi][:composer],
:difficulty => params[:midi][:difficulty]
}.select{|k, v| v.present?}]
Midi.where(c)

Will_Paginate and order clause not working

I'm calling a pretty simple function, and can't seem to figure out whats going on. (I'm using rails 3.0.3 and the master branch of 'will_paginate' gem). I have the following code:
results = Article.search(params) # returns an array of articles
#search_results = results.paginate :page => params[:page], :per_page=>8, :order => order_clause
No matter what I make the order_clause (for example 'article_title desc' and 'article_title asc'), the results are always the same in the same order. So when I check using something like #search_results[0], the element is always the same. In my view, they are obviously always the same as well. Am I totally missing something?
I'm sure its something silly, but I've been banging my head against the wall all night. Any help would be much appreciated!
Edited to Add: The search clause does the following:
def self.search(params)
full_text_search(params[:query].to_s).
category_search(params[:article_category].blank? ? '' : params[:article_category][:name]).
payout_search(params[:payout_direction], params[:payout_value]).
length_search(params[:length_direction], params[:length_value]).
pending.
distinct.
all
end
where each of these guys is a searchlogic based function like this:
#scopes
scope :text_search, lambda {|query|
{
:joins => "INNER JOIN users ON users.id IN (articles.writer_id, articles.buyer_id)",
:conditions => ["(articles.article_title LIKE :query) OR
(articles.description LIKE :query) OR
(users.first_name LIKE :query) OR
(users.last_name LIKE :query)", { :query => "%#{query}%" }]
}
}
scope :distinct, :select => "distinct articles.*"
#methods
def self.payout_search(dir, val)
return no_op if val.blank?
send("payment_amount_#{dir.gsub(/\s+/,'').underscore}", val)
end
def self.length_search(dir, val)
return no_op if val.blank?
send("min_words_#{dir.gsub(/\s+/,'').underscore}", val)
end
Thanks.
If you look at the example from the will_paginate github page you can spot one important difference between their use of the :order clause and yours:
#posts = Post.paginate :page => params[:page], :order => 'created_at DESC'
This calls paginate on the Post object (with no objects being selected yet - no SQL has been executed before paginate comes along). This is different in your example: as you state in the first line of code "returns an array of articles". The simplest I can come up with showing the problem is
results = Model.limit(5).all
#results = results.paginate :order => :doesnt_matter_anymore
won't sort, but this will:
results = Model.limit(5)
#results = results.paginate :order => :matters
It should suffice to take the all out of the search method. It makes ActiveRecord actually perform the SQL query when calling this method. Will_paginate will do that for you when you call paginate (if you let it...). Check out the section on Lazy Loading in this post about Active Record Query Interface 3.0