Search code examples
ruby-on-railsdesign-patternsmany-to-many

Design pattern for many-to-many association with additional fields and partial satisfaction


I am working on a project in which customers can get loans, that will be divided in n instalments.

The customer will make payments.

class Instalment
  has_and_belongs_to_many :payments
end

class Payment
  has_and_belongs_to_many :instalments
end

He can pay the whole loan or a single instalment, but the idea is that he can pay any kind of quantity.

This means that some instalments will be partial paid.

I am not sure about how to express this in the code.

E.g., The customer get a loan of 50$, which will be divided in 5 instalments of 10$. The customer decides to pay 25$ two times.

Instalment
--------------
ID | quantity
 1 |      10$
 2 |      10$
 3 |      10$
 4 |      10$
 5 |      10$

Payment 
----------------------
ID | quantity
 1 |      25$
 2 |      25$

Instalments_Payments 
----------------------
payment_id | instalment_id
         1 |             1
         1 |             2
         1 |             3    # note that this instalment_id and next one
         2 |             3    # are the same, but we don't know how much quantity it satisfied in each one.
         2 |             4
         2 |             5

I have two ideas, but I don't like any of them:

Add a two new field in the Instalments_Payments table. One will tag if the instalment has been totally paid and the other how much it was paid.

Instalments_Payments 
----------------------
payment_id | instalment_id | full_paid | paid
         1 |             1 |      true |  10$ 
         1 |             2 |      true |  10$
         1 |             3 |     false |   5$
         2 |             3 |     false |   5$
         2 |             4 |      true |  10$
         2 |             5 |      true |  10$

Add a new model PartialPayments in which a payment has_many :partial_payments

I am looking for the best approach of this issue.


Solution

  • If you want time efficiency then you should create a column in database table recording the status, which will serve frequent queries on a better performance level. And this is the common solution.

    Otherwise, you may create a helper function to compare the money a customer should pay with the money he has paid, which consumes more time during runtime.

    Since under most conditions time is more precious than space, we usually choose to record the status and do the comparison asynchronously in the background, so the users will not need to wait for your runtime action finished. Thus the first solution is usually preferred.