Rails: modelling balance movement - ruby-on-rails

Hello I have 2 models here:
class Account < ActiveRecord::Base
belongs_to :user
has_many :transactions
end
class Transaction < ActiveRecord::Base
belongs_to :account
after_create :update_balance
def update_balance
balance = self.account.balance
self.account.update(balance: balance - self.price)
end
end
1) Currently, I have an account with balance, and able to update the balance everytime I have created a transaction. But in the future, I will have a updated transaction, where it could get a new price, which I store in another field call new_price, and once we update the new price, how can we add to the account balance?
2) We are going to chart an account balance movement, so we need to store every account balance history, is it a good way if we create a new account field, for everytime we got a new transaction?
Thanks.

There should be a bit more complex logic. About your questions:
1) You shouldn't update payment transaction because you would get a side affects (like not valid balance on some accounts, transaction history, etc.). The better way is a rollback your transaction. There you would pass validation logic (e.g. cannot rollback if future balance of asset account will be negative, etc.)
2) For storing payment history you should use your Transaction model.
Also there a lot of another interesting moments. If you want you can see my gem for this job. You can install it (if you use Rails) or use some business logic for your payment system.

Related

Can I get a callback hook for destroy on an object from another object?

Rails 3.1.3 - ruby 1.9.3p194
I have 2 objects: Patient & Bill.
When Patient gets destroyed the corresponding Bill gets destroyed. Which is fine; however, these objects are also held remotely on Quickbooks and are updated through my application.
If a Bill is destroyed locally, my application deletes the object in Quickbooks. I do not destroy Patients in Quickbooks because there is most likely associated billing history stored there.
The problem arises when someone destroys a Patient locally, it calls destroy for all associated Bills, which fires the destroy method for Quickbooks Bills.
Is there a way to see if patient.destroy has been called from Bill model?
I am assuming you have something like this
class Patient < ActiveRecord::Base
has_many :bills, dependent: :destroy
end
class Bills < ActiveRecord::Base
belongs_to :patient
end
You could change this to
class Patient < ActiveRecord::Base
has_many :bills, dependent: :delete_all
end
destroy has callbacks which you are using to remove bills from quickbooks.
delete has no callbacks it is just a deletion from the database straight through.
Thus if you destroy a patient it will delete all the Bills locally but will not run the callbacks to remove it from quickbooks.
Another approach to this issue would be to remove the callbacks and to perform the deletion with a separate coordinating object, which would get called from your controller:
class PatientAndBillRemoteCleanup
def initialize(patient)
#patient = patient
end
def delete
Patient.transaction do
# fill out these methods
# transaction rewinds if you have failures
delete_patients_bills_in_quickbooks
delete_patients_bills_locally
delete_patient_locally
end
end
end
The advantages of this approach are:
It's much easier to test
You can isolate remote deletion to a known
process (so you don't get unintended effects at the Rails console
Any place where this code is used (such as the controller where web
users trigger deletion) is easy to understand, and someone else
won't accidentally write patient-deleting code elsewhere that
unexpectedly manipulates quickbooks
Callbacks are great for situations where you want to do something every time (such as set some default data that relies on another attribute), but you have a situation here where sometimes you want the callback behavior, and sometimes you don't. Move it out to a coordinating object and then you can easily control the behavior.

Rails default data

Every time I create a new company record in rails, I need to add some default (blank) contact records at that company. Front Desk, Receiving, HR, IT and so on...they won't have any data in them besides the name, just a placeholder for the user to fill in later.
So, my company model has_many contacts, and contacts belong_to company. The contact records are static and the same for every new company that gets added, but I need to pre-populate the contacts table with data, so my users don't have to.
I've read a lot about seeding the database, but I won't be able to use the terminal every time a user dynamically creates a company, and it needs to be dynamically tied to that company, the records are not agnostic. Seeding doesn't seem to be the right thing. How should this be done?
you should use a before_save filter, which checks if an attribute is empty, and otherwise set it to the default.
Using a before_save will guard against deletions later on.
But be careful only to do this for fields which will never be empty.
class Company < ActiveRecord::Base
has_many :contacts
before_save :add_defaults
def add_defaults
contacts ||= Contact.default_list # this only sets it if it's nil
# you can implement Contact#default_list as a method, or as a scope in the contacts model
end
end
What about after_create callback in Company Model?
Smth like this:
class Company < ActiveRecord::Base
has_many :contacts
after_create :add_contacts
def add_contacts
contacts.create(name: "Some name", phone: "...", ....)
end
end
Although it notionally exists for generating test data, the FactoryGirl gem is very useful for this purpose. Use it in conjunction with the after_save approach mentioned here, and you'll have a nice place to centrally define your blank records.

Handling different user types in Rails

I'm designing an application that has two [three including administrators] user types: Buyers and Sellers. When a user signs up, it's assumed that they're signing up to simply purchase something [a Buyer account]. However, if they wish to become a Seller, there should be an option for them to post their items for sale, [become a Seller]. This becomes important, as users can switch back and forth between account types.
Of course, a Seller can buy items as well.
The problem
The problem I'm facing is that Sellers have profile pages where buyers can go to view their items for sale, whereas Buyers do not. However, both user types have a My Account page that they can use to update their information.
Possible design choices
Single table inheritence
class User < ActiveRecord::Base
has_one :profile
end
class Seller < User
has_many :sale_items
end
class Buyer < User
# nothing here.. I guess this is the "default" user type
end
I thought about this approach because then I could clearly separate the user types. For example, the show page for each user is clearly separated. However, this could lead to repeated code in each controller, and introduce a problem when switching between user types.
Just use declarative_authorization or CanCan to add functionality to the base user type
class User < ActiveRecord::Base
has_one :profile
has_many :sale_items # only for Sellers
# if the user is a seller, allow them to add sale_items
end
I thought of this approach because a Seller is basically a Buyer with additional functionality, such as posting items for sale. This could lead to a lot of view logic, though. For example if #user.role == "seller" render _some_seller_partial. I also don't like the idea of checking for a hard coded string in the view. Well, I guess I could do if #user.seller?
Other design choices?
I'm really interested in hearing how other people would model this application. I've been thinking about this for a couple days now.
I would use the second option, but with declarative_authorization instead of cancan, and I'd use the role_model Gem to store the role in the user model.
class User < ActiveRecord::Base
has_one :profile
has_many :sale_items # only for Sellers
# if the user is a seller, allow them to add sale_items
# t.integer :roles_mask , :default => 0 # for role_model
end
declarative_authorization is a bit more powerful than CanCan, and I found it to scale better once a project needs more / more complex roles..
If you store your roles using the role_model Gem, it stores them as a bitmap in the roles_mask attribute.
This means that a user can have many roles, e.g. can be a Seller and a Buyer at the same time, and an Admin or Moderator if you like.
See:
http://railscasts.com/episodes/188-declarative-authorization
http://railscasts.com/episodes/189-embedded-association
http://railscasts.com/episodes/192-authorization-with-cancan
http://railscasts.com/episodes/193-tableless-model
And:
http://everydayrails.com/2011/10/06/rails-authorization.html
http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/
I would pick the cancan option. Is a wonderful gem, well documented, easy to use, actively mantained, and you have lots of tutorials (since it a gem created by Ryan Bates, from Railscasts, you can be sure that you have an episode for it)
I would go with the cancan option, and use the draper gem for the view logic. There are Railscasts for both gems.
http://railscasts.com/episodes/286-draper

Rails: Relationship between two loosely related models

I am working on a Ruby on Rails 3 web application and am not sure how to relate two of the models.
In our organization sales reps go out on appointments. If the appointment is successful, it will result in creating an order (which then has the items ordered related to it, but that's for another day.) If this appointment is not successful, it will be marked as no sale and as you might have guessed, no order is created.
On the other hand, sometimes sales happen without an appointment. For example, a customer may call into the store and order something. In this case, an order can exist without an appointment.
It would be simple if there were no relationship between orders and appointments, but there has to be for ease of use for the end user. For example, if an appointment generates an order, but later the buyer cancels, they will mark the appointment as sale cancelled and then the system should automatically set the order as cancelled. Likewise,they may choose to cancel the order, then the appointment would have to be cancelled automatically by the system.
How does a developer handle something like this? Does the appointment :have_many => orders? does the order :belong_to => appointments? I don't know what to do!
Please help me with this, I am a pretty new rails developer and I feel in over my head! Thank you!
As you already said, the following will work fine:
class Appointment < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :appointment
end
belongs_to requires the field appointment_id to be present in the orders table. But, if the order is not associated with an order then appointment_id does not need to be set. You can have multiple belongs_to associations for a given class.

Validate max amount af associated objects

I have an Account model and a User model:
class Account < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :account
end
Users belong to an account and an account have a user maximum (different for each account). But how do I validate that this maximum have not been reached when adding new users to an account?
First I tried to add a validation on the user:
class User < ActiveRecord::Base
belongs_to :account
validate :validate_max_users_have_not_been_reached
def validate_max_users_have_not_been_reached
return unless account_id_changed? # nothing to validate
errors.add_to_base("can not be added to this account since its user maximum have been reached") unless account.users.count < account.maximum_amount_of_users
end
end
But this only works if I'm adding one user at a time.
If I add multiple users via #account.update_attributes(:users_attributes => ...) it just goes directly through even if there is only room for one more user.
Update:
Just to clarify: The current validation method validates that account.users.count is less than account.maximum_amount_of_users. So say for instance that account.users.count is 9 and account.maximum_amount_of_users is 10, then the validation will pass because 9 < 10.
The problem is that the count returned from account.users.count will not increase until all the users have been written to the database. This means adding multiple users at the same time will pass validations since the user count will be the same until after they are all validated.
So as askegg points out, should I add validation to the Account model as well? And how should that be done?
If you call account.users.size instead of account.users.count it will also include users which have been built but not saved to the database.
HOWEVER this will not fully solve your problem. When you call account in a user it is not returning the same account instance that #account is pointing to so it does not know about the new users. I believe this will be "fixed" in Rails 3, but in the meantime I can think of a couple solutions.
If you are saving the account the same time you are adding users (which I assume so since you are calling update_attributes) then the validation can go in there.
# in account.rb
def validate_max_users_have_not_been_reached
errors.add_to_base("You cannot have more than #{maximum_amount_of_users} users on this account.") unless users.size < maximum_amount_of_users
end
I'm not sure how you are saving the associated models, but if account validation fails they should not be saved.
The other solution is to reset the user.account instance to self when updating user attributes. You could do this in the users_attributes setter method.
# in account.rb
def users_attributes=(attributes)
#...
user.account = self
#...
end
This way user's account will point to the same account instance so account.users.size should return the amount. In this case you would keep the validations in the user model.
It's a tricky problem but hopefully this gave you some ideas on how to solve it.
The reason it is passing is because update_attributes does not go through validations.
Also - your logic only checks for the existing number of account against their maximum permitted. There is no calculation considering the number of users attempting to be added. I would think this logic belongs more in the Account model (?).

Resources