Skip to content

Instantly share code, notes, and snippets.

@loicginoux
Created February 13, 2019 17:55
Show Gist options
  • Save loicginoux/52eefb5750fd6c5107144bb6279aec93 to your computer and use it in GitHub Desktop.
Save loicginoux/52eefb5750fd6c5107144bb6279aec93 to your computer and use it in GitHub Desktop.
invoice number generator
# generate invoice number in a sequential mode
# using redis and redlock gem
# ex:
# Accounting::InvoiceNumberGenerator::Base.new.lock do |next_number|
#. # create your invoice here
# end
class Accounting::InvoiceNumberGenerator::Base
LOCK_TIMEOUT = 10000
FIRST_NUM_SEQUENCE = 100
# pass the new generated invoice number to block
def lock(&block)
Invoice.transaction do
lock_info = lock_manager.lock(lock_name, LOCK_TIMEOUT)
if lock_info
begin
yield(get_next_number) if block_given?
ensure
lock_manager.unlock(lock_info)
end
else
raise "LockTimeout #{lock_name}"
end
end
end
private
# Warning: not thread safe
def get_next_number
"#{yearly_sequence}#{num_sequence}"
end
def yearly_sequence
"#{year}#{type}"
end
def year
Time.now.utc.strftime("%y")
end
def type
''
end
def lock_name
"lock_#{self.class.to_s.underscore}_invoice_number"
end
def num_sequence
last_invoice = Invoice.where("number LIKE ?", "%#{yearly_sequence}%").order('number').last
if last_invoice.nil?
FIRST_NUM_SEQUENCE
else
last_number = last_invoice.number.gsub(yearly_sequence, '').to_i
last_number + 1
end
end
def lock_manager
@lock_manager ||= Redlock::Client.new([ENV['REDIS_URL']], {
retry_count: 3,
retry_delay: 1000,
retry_jitter: 50,
redis_timeout: 0.1
})
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment