I have a project where people need to trade credits between them.
I'm doing this
User model
class User < ApplicationRecord
after_create :create_bank_account
has_one :bank_account, inverse_of: :user, dependent: :destroy
has_many :account_transactions, inverse_of: :user, through: :bank_account
end
Bank model
class BankAccount < ApplicationRecord
belongs_to :user, inverse_of: :bank_account
validates :balance, presence: true, numericality: true
validates :user, presence: true
has_many :account_transactions, inverse_of: :bank_account, dependent: :destroy
accepts_nested_attributes_for :account_transactions
before_validation :load_defaults
def load_defaults
if self.new_record?
self.balance = 4.0
end
end
Account transactions model
class AccountTransaction < ApplicationRecord
after_initialize :set_default_status, if: :new_record?
after_commit :transfer, on: :create
belongs_to :bank_account, inverse_of: :account_transactions
enum transaction_type: [ :Received, :Sent ]
enum status: [ :Approved, :Canceled ]
def set_default_status
self.status ||= :"Approved"
end
private
def transfer
source_account = BankAccount.find(source)
target_account = BankAccount.find(target)
ActiveRecord::Base.transaction do
source_account.balance -= amount
target_account.balance += amount
source_account.save!
target_account.save!
end
end
end
The credit transfer is working fine.
But I need to create the records with the bank_id of each user.
How would i create those 2 account transactions records?
A record with bank_id of user A and another record with bank_id of user B.
I do not know if the best way to handle transactions between users would be this way
Why you need 2 records for AccountTransaction?
I strongly recommend you to save 2 bank_account ids in the AccountTransaction model, see:
# acount_transaction.rb
class AccountTransaction < ApplicationRecord
after_initialize :set_default_status, if: :new_record?
after_commit :transfer, on: :create
belongs_to :target_bank_account, foreign_key: 'target_id', class_name: 'BankAccount'
belongs_to :source_bank_account, foreign_key: 'source_id', class_name: 'BankAccount'
enum transaction_type: [ :Received, :Sent ]
enum status: [ :Approved, :Canceled ]
def set_default_status
self.status ||= :"Approved"
end
private
def transfer
ActiveRecord::Base.transaction do
self.source_bank_account.balance -= amount
self.target_bank_account.balance += amount
source_bank_account.save!
target_bank_account.save!
end
end
end
You just need to create a new migration adding the target_id and source_id to AcountTransaction.
If you do it, you will be able to save in just 1 record the source_account and the target_account, this makes more sense, what do you think?
Update
You should remove the bank_id and create 2 new foreing_keys: target_bank_id and source_bank_id for AccountModel.
In the BankAccount model, you can add the following lines:
# bank_account.rb
has_many incoming_transfers, foreign_key: 'target_bank_id', class_name: 'AccountTransaction'
has_many made_transfers
, foreign_key: 'source_bank_id', class_name: 'AccountTransaction'
After it, you can check for each BankAccount how many transfers are made and received!
You can see more about this kind of association clicking here!
Related
I'm trying to figure out how to automatically set up an invoice with invoice_rows, once a reservation is saved.
Attempts
Before even including the order_rows, I tried generating an invoice for order:
I tried including #order.invoices.create(order_contact_id: #order.order_contact_id) after saving the order in create, but this resulted in an empty array:
Order.last.invoice => []
Afterwards I probably should iterate over all products belonging to a order and include them as invoice_rows in invoice. But not sure how.
Note
The actual structure is more complex and consequently I need all my tables.
Code
models
class Order < ApplicationRecord
has_many :invoices
has_many :order_products, dependent: :destroy
end
class OrderProduct < ApplicationRecord
belongs_to :product
belongs_to :order
accepts_nested_attributes_for :product
end
class Product < ApplicationRecord
has_many :orders, through: :order_products
has_many :product_prices, dependent: :destroy, inverse_of: :product
accepts_nested_attributes_for :product_prices, allow_destroy: true
end
class ProductPrice < ApplicationRecord
belongs_to :product, inverse_of: :product_prices
end
orders_controller
class OrdersController < ApplicationController
def create
#order = #shop.orders.new(order_params)
authorize #order
if #order.save
authorize #order
# #order.invoices.create(order_contact_id: #order.order_contact_id)
redirect_to new_second_part_shop_order_path(#shop, #order)
end
end
private
def order_params
params.require(:order).permit(:order_contact_id,
order_products_attributes: [:id, :product_id, :product_quantity, :_destroy,
products_attributes: [:id, :name, :description]])
end
end
As suggested in the comments, I found the error message by using #order.invoices.create!.
Afterwards I iterated over each product and created an invoice_row for the created invoice.
#invoice = #order.invoices.create!(order_contact_id: #order.order_contact_id)
#order.order_products.each do |o_product|
#invoice.invoice_rows.create!(
description: o_product.product.name,
total_price: #reservation.total_product_price(#reservation, o_product)
)
end
I'm trying to make a method that gets the date and adds onto it an amount of days specified.
At present I cannot call the days specified.
I have a Plant Class that has_many DaysTillSellables, The Period class has_many DaysTillSellable also.
When the user creates a plant they can add a DaysTillSellables and then select a period and then enter an amount of days.
I first need to check to see which period the date is in, then return that period. Currently attempting like so
def current_period
return unless completed_at_week.between?(period.start_week, period.finish_week)
index_days_till_sellables_on_period_id
end
Then Find the days till sellable that is connected to that period and finally call the days from that
Below is the code for the class I'm trying to call it in
class Potting < ApplicationRecord
belongs_to :batch, inverse_of: :pottings
validates :plant_count, :completed_at, presence: true
enum germination_result: { light: 0, medium: 1, full: 2 }
def pottings_completed_at
"Week #{completed_at.strftime('%U').to_i}/#{completed_at.strftime('%Y').to_i}"
end
def completed_at
super || Time.zone.today
end
def completed_at_week
completed_at.strftime('%U')
end
def current_period
return unless completed_at_week.between?(period.start_week, period.finish_week)
index_days_till_sellables_on_period_id
end
def period_days
plant.days_till_sellables.find_by(period: :current_period).¤t_period.days
end
def ready_for_sale
completed_at + period_days
end
end
I've added more Code below to give better context for classes
class DaysTillSellable < ApplicationRecord
belongs_to :period, inverse_of: :days_till_sellables, foreign_key: :period_id
belongs_to :plant, inverse_of: :days_till_sellables, foreign_key: :plant_id
validates :days, presence: true
end
.
class Period < ApplicationRecord
has_many :days_till_sellables, dependent: :destroy, inverse_of: :period
belongs_to :organization
.
class Plant < ApplicationRecord
has_many :batches, dependent: :destroy
has_many :goals, dependent: :destroy
has_many :days_till_sellables, dependent: :destroy, inverse_of: :plant
belongs_to :organization
accepts_nested_attributes_for :days_till_sellables, allow_destroy: true
validates :genus, :species, :period_id, presence: true
end
I think you are looking for:
class Potting
belongs_to :plant, through: :batch
...
end
I am building a ROR app with Users and Groups and I want to be able to link them with an associative model called Memberships.
My problem is that when I try to create a group with one member, the members array for the new group is always empty.
I tried creating a group like this:
def create (user)
#group = Group.new(create_group_params)
user.join(#group)
user.save
#group.save
end
but #group.members is empty when I print it out. How can I make the user a member of the group?
Here are my models:
Group.rb
class Group < ActiveRecord::Base
has_many :passive_memberships, class_name: "Membership",
foreign_key: "club_id",
dependent: :destroy
has_many :members, through: :passive_memberships, source: :member
end
User.rb
class User < ActiveRecord::Base
has_many :active_memberships, class_name: "Membership",
foreign_key: "member_id",
dependent: :destroy
has_many :memberships, through: :active_memberships, source: :club
def join(group)
active_memberships.create(club_id: group.id)
end
end
Membership.rb
class Membership < ActiveRecord::Base
belongs_to :member, class_name: "User"
belongs_to :club, class_name: "Group"
validates :member_id, presence: true
validates :club_id, presence: true
end
Oh. I have to save the group before the user can join it.
def create (user)
#group = Group.new(create_group_params)
#group.save
user.join(#group)
user.save
end
I have 2 Models: Post, User. The User cannot like his post, so how can i prevent creating the instance of the Model Like (user_id: creator, post_id:created by the "creator") ?
You can validate that in your Like model:
class Like < ActiveRecord::Base
validates_presence_of :user_id, :post_id
validate :voter_not_author
private
def voter_not_author
if self.user_id == self.post.try(:user_id)
self.errors[:base] << "Author can't be the voter"
end
end
end
Another implementation I found...
#app/models/like.rb
class Like < ActiveRecord::Base
validates :user_id, exclusion: {in: ->(u) { [Post.find(u.post_id).user_id] }} #-> user_id cannot equal post.user_id
end
If you wanted to get rid of the db query, you'd have to associate the models and use inverse_of:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :likes
end
#app/models/like.rb
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :post, inverse_of: :likes
validates :user_id, exclusion: {in: ->(u) { u.post.user_id }}
end
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :likes, inverse_of: :post
end
I'm wondering if there is a cleaner way to validate multiple relationships in rails. I don't mean validating an association, rather ensuring relationship integrity between two or more belongs_to associations. Here is some code as an example:
class User < ActiveRecord::Base
has_many :products, inverse_of: :user
has_many :purchases, inverse_of: :user
end
class Purchase < ActiveRecord::Base
belongs_to :user, inverse_of: :purchases
has_many :products, inverse_of: :purchase
end
class Product < ActiveRecord::Base
belongs_to :user, inverse_of: :products
belongs_to :purchase, inverse_of: :products
validates :user, :purchase, presence: true
validate :purchase_user
private
def purchase_user
errors.add(:purchase, 'purchase user does not match user') if user != purchase.user
end
end
The purchase_user validation method here checks that the user is the same as purchase.user and adds an error if they are not.
I could change it to use :inclusion like so:
validates :purchase, inclusion: { in: proc { |record| record.user.purchases } }
But that seems even more inefficient, any suggestions?