# frozen_string_literal: true # Provides methods to generate Invoice, Avoir or PaymentSchedule references class PaymentDocumentService class << self def generate_reference(document, date: DateTime.current) pattern = Setting.get('invoice_reference') reference = replace_invoice_number_pattern(pattern) reference = replace_date_pattern(reference, date) if document.class == Avoir # information about refund/avoir (R[text]) reference.gsub!(/R\[([^\]]+)\]/, '\1') # remove information about online selling (X[text]) reference.gsub!(/X\[([^\]]+)\]/, ''.to_s) # remove information about payment schedule (S[text]) reference.gsub!(/S\[([^\]]+)\]/, ''.to_s) elsif document.class == PaymentSchedule # information about payment schedule reference.gsub!(/S\[([^\]]+)\]/, '\1') # remove information about online selling (X[text]) reference.gsub!(/X\[([^\]]+)\]/, ''.to_s) # remove information about refunds (R[text]) reference.gsub!(/R\[([^\]]+)\]/, ''.to_s) elsif document.class == Invoice # information about online selling (X[text]) if document.paid_with_stripe? reference.gsub!(/X\[([^\]]+)\]/, '\1') else reference.gsub!(/X\[([^\]]+)\]/, ''.to_s) end # remove information about refunds (R[text]) reference.gsub!(/R\[([^\]]+)\]/, ''.to_s) # remove information about payment schedule (S[text]) reference.gsub!(/S\[([^\]]+)\]/, ''.to_s) else raise TypeError end reference end def generate_order_number(invoice) pattern = Setting.get('invoice_order-nb') # global document number (nn..nn) reference = pattern.gsub(/n+(?![^\[]*\])/) do |match| pad_and_truncate(number_of_invoices('global'), match.to_s.length) end reference = replace_invoice_number_pattern(reference) replace_date_pattern(reference, invoice.created_at) end private ## # Output the given integer with leading zeros. If the given value is longer than the given # length, it will be truncated. # @param value {Integer} the integer to pad # @param length {Integer} the length of the resulting string. ## def pad_and_truncate(value, length) value.to_s.rjust(length, '0').gsub(/^.*(.{#{length},}?)$/m, '\1') end ## # Returns the number of current invoices in the given range around the current date. # If range is invalid or not specified, the total number of invoices is returned. # @param range {String} 'day', 'month', 'year' # @return {Integer} ## def number_of_invoices(range) case range.to_s when 'day' start = DateTime.current.beginning_of_day ending = DateTime.current.end_of_day when 'month' start = DateTime.current.beginning_of_month ending = DateTime.current.end_of_month when 'year' start = DateTime.current.beginning_of_year ending = DateTime.current.end_of_year else return get_max_id(Invoice) + get_max_id(PaymentSchedule) end return Invoice.count + PaymentSchedule.count unless defined? start && defined? ending Invoice.where('created_at >= :start_date AND created_at < :end_date', start_date: start, end_date: ending).length + PaymentSchedule.where('created_at >= :start_date AND created_at < :end_date', start_date: start, end_date: ending).length end ## # Replace the date elements in the provided pattern with the date values, from the provided date # @param reference {string} # @param date {DateTime} ## def replace_date_pattern(reference, date) copy = reference.dup # full year (YYYY) copy.gsub!(/YYYY(?![^\[]*\])/, date.strftime('%Y')) # year without century (YY) copy.gsub!(/YY(?![^\[]*\])/, date.strftime('%y')) # abbreviated month name (MMM) copy.gsub!(/MMM(?![^\[]*\])/, date.strftime('%^b')) # month of the year, zero-padded (MM) copy.gsub!(/MM(?![^\[]*\])/, date.strftime('%m')) # month of the year, non zero-padded (M) copy.gsub!(/M(?![^\[]*\])/, date.strftime('%-m')) # day of the month, zero-padded (DD) copy.gsub!(/DD(?![^\[]*\])/, date.strftime('%d')) # day of the month, non zero-padded (DD) copy.gsub!(/DD(?![^\[]*\])/, date.strftime('%-d')) copy end ## # Replace the document number elements in the provided pattern with counts from the database # @param reference {string} ## def replace_invoice_number_pattern(reference) copy = reference.dup # document number per year (yy..yy) copy.gsub!(/y+(?![^\[]*\])/) do |match| pad_and_truncate(number_of_invoices('year'), match.to_s.length) end # document number per month (mm..mm) copy.gsub!(/m+(?![^\[]*\])/) do |match| pad_and_truncate(number_of_invoices('month'), match.to_s.length) end # document number per day (dd..dd) copy.gsub!(/d+(?![^\[]*\])/) do |match| pad_and_truncate(number_of_invoices('day'), match.to_s.length) end copy end ## # Return the maximum ID from the database, for the given class # @param klass {ActiveRecord::Base} ## def get_max_id(klass) ActiveRecord::Base.connection.execute("SELECT max(id) FROM #{klass.table_name}").getvalue(0, 0) || 0 end end end