I have a Task Order that has_many Invoices. Task Order has an attribute called "invoicedAmount". Invoice has an attribute called "amount". I am trying to make a callback where whenever I add or delete an invoice, the "invoicedAmount" field updates accordingly. Right now I am able to make it calculate on the fly, but now I want to save it to the database. Here is my Invoice model:
class Invoice < ActiveRecord::Base
belongs_to :task_order
validates_presence_of :task_order_id
after_save :update_task_order_invoiced_amount, notice: ':)!'
after_destroy :update_task_order_invoiced_amount
def update_task_order_invoiced_amount
#task_orders = TaskOrder.all
#invoices = Invoice.all
#task_orders.each do |task_order|
task_order.invoicedAmount = task_order.invoices.sum(:amount)
end
end
end
Any guidance would be greatly appreciated!
You probably don't want to recalculate all TaskOrder records, but only changed one. So your update_task_order_invoiced_amount should look something like this:
def update_task_order_invoiced_amount
task_order.invoicedAmount = task_order.invoices.sum(:amount)
task_order.save
end
Related
Please I am new to Rails. I want to populate a model database table from another model form. I have a customer model and form and I want to save the form data into the customer database table and also add data to another customer account database table. The customer fields are
Name,
Address,
Phone,
Current_balance,
Outstanding_balance.
Customer account fields,
customer name,
transaction date,
balance,
mode of payment
Below is my code but I cannot seem to solve the problem.
Customer model
class Customer < ApplicationRecord
after_create :add_customer_account
has_many :customer_accounts
def add_customer_account
b= CustomerAccount.new
b.transaction_date = Customer.created_at
b.balance = Customer.current_balance.to_i - Customer.outstanding_balance.to_i
b.customer_id = Customer.id
b.invoice = "0"
b.create
end
end
Customer Account model
class CustomerAccount < ApplicationRecord
belongs_to :customer
end
What how do I get it done
you can try like this:
# app/models/customer.rb
class Customer < ApplicationRecord
after_create :add_customer_account
has_many :customer_accounts
def add_customer_account
b = customer_accounts.new
b.transaction_date = self.created_at
b.balance = self.balance
b.invoice = "0"
b.save!
end
def balance
(current_balance - outstanding_balance).to_i
end
end
I'm not a huge fan of after_create callback.
Just try to put the method call inside the if #customer.save inside the create action.
if #customer.save
add_customer_account <---------
format.html {render ..... blabla
else
...
end
The second thing that i see is that you're putting this code inside the Model, try to keep the business logic inside the controller.
Try to define the add_customer_account under private inside the customers_controller, and then call it as i mentioned before.
Use the method from the Jhumur Chatterjee and put a byebug just before the b.save! action. Then in the console you can just do b.errors.messages
I'm looking to create a callback where update a object if find the id attribute of another model.
in this case if find update Odata model if find the order_id.
someone know how to find the object based on another model id attribute?
class Order < ActiveRecord::Base
after_update :update_odata
def update_odata
order = Order.find_by_id(attributes['id'])
od = Odata.find_by_id(attributes['order_id'])
od.shipping_cost = order.shipping_cost
od.shipping_method = order.shipping_method
od.status = order.status
od.feedback_id = order.feedback_id
od.track_number = order.track_number
od.seller_name = order.seller_name
od.buyer_name = order.buyer_name
od.save
end
end
In general you should check the docs and at least make an effort to learn the tools you're using before resorting to asking for someone to help explain it to you on StackOverflow.
To answer your question, find(1) is effectively a shortcut method for find_by(id: 1). Thusly, if you want to find an order by customer_id you could do this: Order.find_by(customer_id: 42).
Or, if you're trying to make this contingent on order (making some assumptions based on how Rails apps are built vs this unusual attributes stuff you have in your example):
order = Order.find(params[:id])
od = Odata.find_by(order_id: order.id)
In which case, you should probably just use relations:
class Order < ApplicationRecord
has_one :odata
end
class Odata < ApplicationRecord
belongs_to :order
end
# controller:
order = Order.find params[:id]
od = order.odata
If you wanted to do exactly what you are above, which is probably a bad path to go down, you would probably want to do something like this:
class Order < ApplicationRecord
has_one :odata
def attributes_for_odata
%w{ shipping_cost shipping_method status feedback_id track_number seller_name buyer_name }
end
def update_order_data
odata.update attributes.slice(*attributes_for_odata)
end
end
I've got a model called Brand, on which several things rely including in this example a model called User. If a Brand is deleted then a lot of things will fail. What's the best way to set a default Brand for all its relationships in the event that a Brand is deleted?
I thought writing stuff like this might work:
class User < ActiveRecord::Base
after_save :assign_to_default_brand, :if => :not_branded?
def not_branded?
!self.brand_id?
end
def assign_to_default_brand
self.brand_id = Brand.first
end
end
But it doesn't seem to behave the way I want it to. Is there a best-practice established here? Cheers.
UPDATED
I've thrown a default boolean onto Brand and written this but again it seems to have no effect. Am I missing something?
class Brand < ActiveRecord::Base
after_save :assign_users_to_default
def assign_users_to_default
self.users.all.each { |user| user.brand_id = Brand.where(:default => true).first.id if user.not_branded? }
end
end
It should be a before_save instead of after_save That way the value will be persisted to the database when the instance is saved.
For deletion on a brand you could use after_destroy
class Brand
after_destroy :switch_assigned_users
def switch_assigned_users
User.where(:brand_id => id).update_all(:brand_id => Brand.first)
end
end
This finds all users that assigned to that brand and switches them to the first one.
Before everything i would like to thank you for your help
I have a model like this:
attr_protected nil
belongs_to :product
belongs_to :user
before_create :add_ammount
def carted_product_price(ammount, price)
ammount * price
end
def add_ammount
carted_product = CartedProduct.where(:product_id => self.product_id, :user_id => self.user_id)
if carted_product
carted_product.first.ammount += self.ammount
carted_product.first.update_attributes(:ammount => carted_product.first.ammount)
else
self.save
end
end
it saves buying orders in a table called Carted_Products connected to Users and Products in the belogings
the problem is that when the Before create executes i want it to update the record in the table adding the ammount passed by the controller if the record already exists and if not, create one, as far as iv done, it updates the ammount but STILL CREATES A NEW one with the passed params in the order, i want it only to update, not to do both actions when the record is found
thnx for your patience
EDIT:
Tried returning false after the update attributes, it cancels the filter, and dont create or update attributes
Return false in the before_create filter to prevent the object form being saved. add_amount is not responsible for saving the object, and shouldn't call save by itself.
You cannot do this in before_create filter. You need to fetch existing CartedProduct in controller where you're calling create.
I have the following table and a corresponding model:
Orders
ID|ORDER_REF|....
The order ref is of the format 'ORDER#000-00'+ORDER.ID The thing is that I need to enable it such that the order ref is set on insertion. Is there a way to do this without having to do an update after the insertion, I'm using RoR here.
Do you really need that data in your database? The best way would be to just have a method on your model that returns the order ref in the desired format, based on the id in the database.
class Order < ActiveRecord::Base
def order_ref
"ORDER#000-#{self.id.to_s.rjust(3, '0')}"
end
end
With the abobe you can do this:
order = Order.create(params[:order])
order.id #=> 12
order.order_ref #=> "ORDER#000-012"
If you do need the order ref in the database, I recommend using an after_create callback:
class Order < ActiveRecord::Base
after_create :generate_order_ref
def generate_order_ref
self.order_ref = "ORDER#000-#{self.id.to_s.rjust(3, '0')}"
save
end
end
This does do an update after inserting, but I don't see any problem with that.