4
votes

There are three types of invoice items with following tables

1) SubscriptionItems,

2) Prorations,

3) UsageItems,

Those have the same attributes below

invoice_id
amount
stripe_invoie_id

However

only SubscriptionItem and Proration

period_start_at
period_end_at

and only Proration and UsageItem has

title

and only UsageItem has

uuid
account_id
description

To achieve this model I've been using polymorphic relation.

class InvoiceItem < ActiveRecord::Base
  belongs_to :invoice
  belongs_to :itemable, polymorphic: true
end

class SubscriptionItem < ActiveRecord::Base
  belongs_to :plan
  has_one :invoice_item, as: :itemable
end

class UsageItem < ActiveRecord::Base
  belongs_to :account
  has_one :invoice_item, as: :itemable
end

class Invoice < ActiveRecord::Base
  belongs_to :account
  has_many :invoice_items
end

class Account < ActiveRecord::Base
  has_many :invoices
  has_many :usage_items
end

For now it works. However As far as I understand polymorphic should have has_many relation. So this resides in the middle of Polymorphic and STI. Because those three types of invoice items are always be subscriptionitem, proration, or usageitem.

It's hard decision that I could keep using this models (polymorphic with has_one) or should I use STI instead? Or class table inheritance should be fit?

EDIT

I'd love to hear the reason why I could use some design. Maybe those types pros and cons.

As far as I know,

If I apply STI

That leads many NULLable columns, but RoR supports STI. So it's easy to use.

If I apply polymorphic with has_one

It stills the rails way but the original polymorphic definition is different. It should have has_many relationship instead of has_one. Also it's impossible to add foreign key.

Ref: Blog post for STI to polymorphic

If I apply Class table inheritance,

It's more efficient for relational database, but it's not rails way.

Ref: Blog post for STI to class table inheritance

1

1 Answers

0
votes

I think STI with a hidden_field passing the appropriate value for each attribute that should determine the invoice type, could be the way to go here. It's simple and efficient.

Let's say you added a field called :invoice_type to your invoice model,

Then just loop through the items in an array like (Rough example):

<% @invoices.where(:invoice_type => "proration").find_each do |invoice| %>
   <% @invoices.where(:start_date => "#{@invoice.start_date}").find_each do |invoice| %>
         <!--Will only show the start_date of invoices where the invoice_type is propration. -->
    <% end %>
<% end %>