How to find product before_save Rails 4.2 - ruby-on-rails

I've app where I've got Ingredients and also have deliveries of Ingredients, after delivery ingredient is increasing value. I have a problem to find Ingredient before save.
On my delivery I store ingredient_id so I should have a in my controller function to find Ingredient via
before_save :find_ingredient
private
def find_ingredient
self.find_ingredient = find(:ingredient_id)
end
How to find this Ingredient before_save?
In my controller
if #delivery.save
#ingredient.quantity += #delivery.unloaded
#ingredient.save

Try this:
private
def find_ingredient
Ingredient.find(:ingredient_id).increment!(:quantity)
end

You'd do something like this:
#app/models/delivery.rb
class Delivery < ActiveRecord::Base
belongs_to :ingredient, inverse_of: :deliveries
before_save :increase_ingredient
private
def increase_ingredient
ingredient.increment! :quantity, unloaded
end
end

Related

rails validate dependent model

There have 2 tables: Orders and Arrivals. There can be many arrivals on an order. I want to validate the creation of arrivals for a specific order.
Orders has fields book_id and quantity:integer
Arrivals has fields order:belongs_to and quantity:integer
Order.rb:
class Order < ActiveRecord::Base
has_many :arrivals
def total_arrival_quantity
arrivals.map(&:quantity).sum
end
def order_quantity_minus_arrival_quantity
quantity - total_arrival_quantity
end
end
Arrival.rb:
class Arrival < ActiveRecord::Base
belongs_to :order
validates :total_arrival_quantity_less_or_equal_to_order_quantity, on: create
validates :current_arrival_quantity_less_or_equal_to_order_quantity, on: create
def current_arrival_quantity_less_or_equal_to_order_quantity
self.quantity <= order.quantity
end
end
How can I make the two validations work?
Something like this should work,
validate :order_quantity, on: :create
private
def order_quantity
if quantity > order.order_quantity_minus_arrival_quantity
errors.add(:quantity, 'cannot be greater than ordered quantity.')
end
end

Rails update product quantity

I followed Ryan Bates screencasts using paypal standard payments and i basically now have to send checkout info from the Cart model.
I'm a bit stuck with trying to update product quantity after the transaction is completed.
I tried using a callback but to no avail. Any help would be appreciated
The closest i got was to use this update quantity callback but for some reason, it is updating the wrong cart. Not sure whether it picks up the wrong line item or its when it checks the cart it goes wrong
class PaymentNotification < ActiveRecord::Base
belongs_to :cart
serialize :params
after_create :mark_cart_as_purchased, :update_quantity
private
def mark_cart_as_purchased
if status == "Completed"
cart.update_attribute(:purchased_at, Time.now)
end
end
def update_quantity
#line_item = LineItem.find(params[:id])
#line_item.upd
end
end
Line Item Class
class LineItem < ActiveRecord::Base
belongs_to :order
belongs_to :product
belongs_to :cart
belongs_to :stock
after_create :stock_stat
def total_price
product.price * quantity
end
def upd
if cart.purchased_at
product.decrement!(quantity: params[:quantity])
end
end
end
The params hash is only available in the controller. You cannot access it in the model. You have to pass params[:quantity] to the upd method as a method parameter as such:
def update_quantity
#line_item = LineItem.find(params[:id])
#line_item.upd(params[:quantity])
end
def upd(quantity)
if cart.purchased_at
product.decrement!(quantity: quantity)
end
end
Also, you should consider using Time.current instead of Time.now in order to account for the time zone your application is configured with in application.rb unless you just want to use whatever time is local.

Prevent parent object to be saved when saving child object fails

I have two associated classes like this:
class Purchase < ActiveRecord::Base
has_many :actions
before_create do |p|
self.actions.build
end
end
class Action < ActiveRecord::Base
belongs_to :purchase
before_save do |a|
false
end
end
The block in the Action class prevents it from saving. I was thinking doing Purchase.create will fail because it cannot save the child object. But while it does not save the Action, it commits the Purchase. How can i prevent the parent object to be saved when there is an error in the child object?
It turns out you have to rollback the transaction explicitly, errors from the child objects does not propagate. So i ended up with:
class Purchase < ActiveRecord::Base
has_many :actions
after_create do |p|
a = Action.new(purchase: p)
if !a.save
raise ActiveRecord::Rollback
end
end
end
class Action < ActiveRecord::Base
belongs_to :purchase
before_save do |a|
false
end
end
Take note that i also changed the before_create callback to after_create. Otherwise, since belongs_to also causes the parent to be saved, you will get a SystemStackError: stack level too deep.
I ran into this problem when dealing with race conditions where the child objects would pass a uniqueness validation, but then fail the database constraint (when trying to save the parent object), leading to childless (invalid) parent objects in the database.
A slightly more general solution to the one suggested by #lunr:
class Purchase < ActiveRecord::Base
has_many :actions
after_save do
actions.each do |action|
raise ActiveRecord::Rollback unless action.save
end
end
end
class Action < ActiveRecord::Base
belongs_to :purchase
before_save do |a|
false
end
end
Try to use this code in Purchase class:
validate :all_children_are_valid
def all_children_are_valid
self.actions.each do |action|
unless action.valid?
self.errors.add(:actions, "aren't valid")
break
end
end
end
Or use validates_associated in Purchase class:
validates_associated :actions
If in your business logic you can't save purchase without any action, then add a presence validator on actions inside purchases
validates :actions, length: {minimum: 1}, presence: true

Rails accept_nested_attributes still throws "Can't mass-assign protected attributes"

I think I'm either missing something really simple or something really obscure. Hoping someone can spot it for me or explain my muppetry.
Ok, So there are two models, Basket and BasketItem.
I've set Basket to accept_nested_attributes :basket_items with the intention of using fields_for in an edit view of Basket.
However when run up it still screams that
Error: Can't mass-assign protected attributes: basket_items_attributes
For the sake of this question I've boiled down to the same issue if I do a manual basket.update_attributes in the console with just one or two basket_item attributes. So I know it's a model issue, not a view or controller issue.
e.g.:
basket.update_attributes("basket_items_attributes"=>[{"qty"=>"1", "id"=>"29"}, {"qty"=>"7", "id"=>"30"}])
or similarly with a hash more like fields_for makes
basket.update_attributes( "basket_items_attributes"=>{
"0"=>{"qty"=>"1", "id"=>"29"},
"1"=>{"qty"=>"7", "id"=>"30"}
})
I've ensured that the associates in defined before the accepts_nested_attibutes_for, that the child model has the appropriate attributes accesable too, tried removing additional attributes for the nested data, lots of fiddling to no avail.
basket.rb
class Basket < ActiveRecord::Base
has_many :basket_items
attr_accessible :user_id
accepts_nested_attributes_for :basket_items
belongs_to :user
def total
total = 0
basket_items.each do |line_item|
total += line_item.total
end
return total
end
# Add new Variant or increment existing Item with new Quantity
def add_variant(variant_id = nil, qty = 0)
variant = Variant.find(variant_id)
# Find if already listed
basket_item = basket_items.find(:first, :conditions => {:variant_id => variant.id})
if (basket_item.nil?) then
basket_item = basket_items.new(:variant => variant, :qty => qty)
else
basket_item.qty += qty
end
basket_item.save
end
end
basket_item.rb
class BasketItem < ActiveRecord::Base
belongs_to :basket
belongs_to :variant
attr_accessible :id, :qty, :variant, :basket_id
def price
variant.price
end
def sku
return variant.sku
end
def description
variant.short_description
end
def total
price * qty
end
end
As the error says, you just need to add basket_items_attributes to your list of accepted attributes.
So you'd have
attr_accessible :user_id, :basket_items_attributes
at the top of your basket.rb file

How to perform calculations on nested form items?

I am fairly new to Rails and I have these two models...
class Invoice < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :items
...
end
class Item < ActiveRecord::Base
belongs_to :invoice
def self.total
price * quantity
end
...
end
... and a nested (!) form that creates new invoice records and their associated items.
However, I find it very difficult to perform calculations on these items. For example next to each item I would like to put the total for this item using the total method above.
Unfortunately, it's not working. In my form I put this next to each item:
<%= #invoice.items.amount %>
which is derived from my controller:
def new
#invoice = Invoice.new
3.times {#invoice.items.build}
end
It keeps throwing an error saying undefined local variable or method price
What am I missing here??
Thanks for any help.
You have created a class method on Item, when I think what you want is an instance method.
class Item < ActiveRecord::Base
belongs_to :invoice
def total
price * quantity
end
...
end
which you can call on an individual item #item.total or, if you do you the total of all the items, then you'd need to do something like this:
class Item < ActiveRecord::Base
belongs_to :invoice
def self.total
all.collect { |item| item.price * item.quantity }
end
...
end
#invoice.items.total
Hope that helps.

Resources