The Tradeline credit dashboard is a project I worked on for around a year. I led the development for a private business investor in developing this full-stack financial services application, using Ruby on Rails.
The frontend was an inherited design from the previous developer, and it took some considerable work to get it into a functional state. I made extensive use of the Datatables framework for returning results from the database in an asynchronous and efficient fashion. All filter operations are pushed down into the database layer as best possible
tl = Listing.accessible_by(current_ability).active
tl = tl.where(bank_id: listing_filter_params[:bank]) if listing_filter_params[:bank].present?
tl = tl.where(state_id: listing_filter_params[:state]) if listing_filter_params[:state].present?
tl = tl.where('credit_limit_cents >= ?', listing_filter_params[:min_credit_limit].to_money.fractional) if listing_filter_params[:min_credit_limit].present?
tl = tl.where('price_cents <= ?', listing_filter_params[:max_price].to_money.fractional) if listing_filter_params[:max_price].present?
tl = tl.where('? >= history', listing_filter_params[:age].to_i.years.ago) if listing_filter_params[:age].present?
tl = tl.where('active_slots >= ?', listing_filter_params[:slots].to_i) if listing_filter_params[:slots].present?
tl = tl.where(bpars) unless bpars.empty?
tl
The core four operations of the site were Deposits, Purchases, Withdrawals, and Refunds. Each of these had a discrete model, but were themselves attached polymorphically to a general LedgerEntry to decouple the concerns of affecting wallet balance and presenting transaction data.
On the frontend, users are given forms to fill out including AWS-S3 based file fields. Most complex form validations are done on the server side to increase overall responsiveness, but some basic ones are implemented in the view, as well as the Dropzone based uploader. Asynchronous image uploads done via the Shrine gem help significantly in speeding up form submission times.
The Administrate based backend provides the staff quick access to all the information they need to confirm and reject transactions, and confirm essential user details according to business logic.
Keeping track of which Tradelines were being listed in which month requires a "template" and "instance" pattern, with Tradelines generating Listings that last for a period of time before expiring on the noon of their statement date. Automatic workers handle promoting and demoting listings from tradelines who have been set to launch.
scope :ready_for_launch, -> { where(is_active: false).where('launch_date <= :date', date: Time.now) }
scope :expiring, -> { where(is_active: true).where('expiration_date <= :date', date: Time.now) }
Reference numbers for transactions were necessary for customer service operations, as well as validating deposit authenticity. Each Ledger Entry has its own reference number, as do listings and tradelines. These were created using the same concern code, and added on to all of the classes that needed them.
module Reference
extend ActiveSupport::Concern
included do
before_validation :create_ref
after_create :confirm_ref
end
def create_ref
self.ref ||= gen_ref_code
end
def confirm_ref
if self.id && self.ref
rn = ReferenceNumber.create(ref: self.ref, reference_object: self)
end
end
def gen_ref_code
loop do
token = SecureRandom.hex.first(8).tr('ef', 'xz').upcase
break token unless ReferenceNumber.exists?(ref: token)
end
end
end
Since handling Secure data like SSNs and Drivers licenses is essential, two seperate image uploading models were created, one for public images such as bank logos and profile images, and a seperate one for SSN, Drivers Licenses, and Receipts. Using AWS-S3 Access Control Lists, single-use URLs are generated for sensitive documents.
module SecureAttachment
extend ActiveSupport::Concern
@@attachments = {}
def self.attachments
@@attachments
end
class_methods do
def secure_attachment(atch, klass=self)
@secured_attachments ||= {}
@secured_attachments[atch] = klass
@@attachments[klass] ||= []
@@attachments[klass] << atch
include SecureFileUploader::Attachment.new(atch)
define_method("#{atch}?".to_sym) do
self.method(atch).call.nil? ? false : true
end
end
def secured_attachments
@secured_attachments ||= {}
end
end
end