Search code examples
ruby-on-railsformsmany-to-many

How do i correctly implement a three way many-to-many relationship in rails 4?


I'm using rails 4 and i have a possible three way many-to-many relationship situation that looks like this: To register a sale, i need a Client and a Product, along with its quantity. The threeway comes when i need a fixed id for the Sale because of another table Installments, with due dates and values for each installment and to check if they are paid or not.

I got a little confused with the relationships between them, but i did something like this (i called the middle table i used here as SCP, sale_client_product):

class Client < ActiveRecord::Base
  has_many :scps
end

class Sale < ActiveRecord::Base
  has_many :scps
  has_many :installments
end

class Installment < ActiveRecord::Base
  belongs_to :sale
end

class Product < ActiveRecord::Base
  has_many :scps
end

class SCP < ActiveRecord::Base
  belongs_to :client
  belongs_to :product
  belongs_to :sale
end

I would like to know if this is correct or if there is a better way of doing it. I'm also in a picle when actually saving, as for any one sale i may have to save many SCP instances with one form.


Solution

  • If you want to have Product, Client, Sale, Installment information in a single document, maybe you can use the Invoice.

    In business, if a company want to ask money from a customer, he will periodically send him an invoice which includes all the following infos:

    • Product details, names, quantity, price for each unit
    • Client details, names, address, phone number
    • Sale details, total price, date of sale or Installment due date, installment amount

    Class Client
      has_many :invoices
    end
    
    Class Product
      has_many :invoices
    end
    
    Class Sale
      has_many :installments
      has_many :invoices, through: :installments 
    end
    
    Class Installment
      belongs_to :sale
    end
    
    Class Invoice
      belongs_to :client
      belongs_to :product
      belongs_to :installment
    end
    

    Payment will be what you need to reconcile with Invoice, so that you can understand which Invoices (and consequently Installments) were paid.

    Class Payment
      belongs_to :invoice
    end
    

    when you do @invoice.payment you get the payment for that invoice (which belongs_to :installment), if @invoice.payment is nil, then the installment was not paid.

    If you want to register a Payment it should have an existing Invoice

    @payment = Payment.new(payment_params)
    @payment.invoice_id = @invoice.id
    @payment.save
    

    This is kind of how I would register Accounting Transactions when I was working in the Accounting Field. Feel free to improve your app and maybe in the future we can edit this post for improvements

    A possible improvement could be enstablishing that Installment has_one :payment, through: :invoice, so you figure that out!