Flauwekeul
Flauwekeul

Reputation: 1009

How to increment an attribute so that its value is (at least) 1 higher than the other records and unique

I have a Payment model with an attribute invoice_nr. This attribute should increment (by 1) before it's saved. It's important that all payments have a unique invoice_nr.

I could use a before_save callback that increments invoice_nr by 1 relative to Payment.maximum("invoice_nr"):

class Payment < ActiveRecord::Base
  before_save :increment_invoice_nr

  private
    def increment_invoice_nr
      self.invoice_nr = Payment.maximum("invoice_nr") + 1
    end
end

But I think this doesn't ensure the uniqueness of invoice_nr. If two payments get saved at the same time they theoretically could get the same invoice_nr...right?

It's okay if there are gaps in between invoice_nrs, but you'll get bonus points if you know a way to prevent this :)

EDIT

Some folks suggest using the auto increment feature that most databases have. This works but it would tie my app to the specific database I'm using. Therefore the auto increment logic belongs in the app imo.

Upvotes: 2

Views: 1725

Answers (5)

Salil
Salil

Reputation: 47482

If you have a primary key with auto increment following should work

class Payment < ActiveRecord::Base
  after_save :increment_invoice_nr

  private
    def increment_invoice_nr
      some_high_integer = 10000000
      self.update_attribute('invoice_nr', self.id + some_high_integer)
    end
end

Upvotes: 1

Deepika
Deepika

Reputation: 826

You can try this one.

def increment_invoice_nr
  invoice_nr_arr = Payment.all.map(&:invoice_nr)
  invoice = Payment.maximum("invoice_nr") 
  until invoice_nr_arr.include?(invoice) == false
    invoice += 1
  end
  self.invoice_nr = invoice
end

This method will first collect all invoice_nr. Then it will check the incremented invoice_nr includes/exists in your payments table or not. If it exists then it will go on incrementing the invoice_nr by 1 till it gets the unique invoice_nr.

Upvotes: 0

manoj
manoj

Reputation: 1675

You can use a database sequence.

migration:

  def up
    execute 'CREATE SEQUENCE tr_num_seq START 10000000;'
  end

model:

class model < ActiveRecord:Base
  after_initialize :set_omd_id
  def set_unique_number
        if self.tr_number.nil?
      self.tr_number = ActiveRecord::Base.connection.select_value("select nextval('tr_number_seq')")
    end
  end
end

Everytime a model object is created, it will set a unqiue "invoice number id" if it is not already set

Upvotes: 2

user2323194
user2323194

Reputation: 81

You can have a unique validation in your model.That will prevent it save duplicate values. In your payment method add the following:

validates :invoice_nr, :uniqueness => true

You can also use auto increment if you are using mysql or other RDBMS

Upvotes: 0

tbuehlmann
tbuehlmann

Reputation: 9110

I suggest using the auto increment feature from the RDBMS of your choice. Using it you wouldn't have to do it yourself and it'd be reliable.

Upvotes: 0

Related Questions