328 lines
9.4 KiB
Ruby
328 lines
9.4 KiB
Ruby
# encoding: UTF-8
|
|
# frozen_string_literal: true
|
|
|
|
require 'securerandom'
|
|
|
|
class Member < ApplicationRecord
|
|
include ModelCaching
|
|
MAX_GROUP = 4
|
|
has_many :orders
|
|
has_many :accounts
|
|
has_many :stats_member_pnl
|
|
has_many :payment_addresses
|
|
has_many :withdraws, -> { order(id: :desc) }
|
|
has_many :deposits, -> { order(id: :desc) }
|
|
has_many :beneficiaries, -> { order(id: :desc) }
|
|
has_many :bonus
|
|
|
|
serialize :cards, Array
|
|
serialize :ibans, Array
|
|
|
|
scope :enabled, -> { where(state: 'active') }
|
|
|
|
before_validation :downcase_email
|
|
|
|
validates :uid, length: { maximum: 32 }
|
|
validates :referral_uid, length: { maximum: 32 }
|
|
validates :email, presence: true, uniqueness: true, email: true
|
|
validates :level, numericality: { greater_than_or_equal_to: 0 }
|
|
validates :role, inclusion: { in: ::Ability.roles }
|
|
|
|
before_create { self.group = self.group.strip.downcase }
|
|
|
|
class << self
|
|
def groups
|
|
TradingFee.distinct.pluck(:group)
|
|
end
|
|
|
|
|
|
def group_name_by_number(number)
|
|
"vip-#{number}"
|
|
end
|
|
|
|
def min_max_group(group_name)
|
|
return [ENV.fetch('VIP_4_AMOUNT').to_d, 0] if group_name == 'vip-4'
|
|
|
|
return [ENV.fetch('VIP_3_AMOUNT').to_d, ENV.fetch('VIP_4_AMOUNT').to_d] if group_name == 'vip-3'
|
|
|
|
return [ENV.fetch('VIP_2_AMOUNT').to_d, ENV.fetch('VIP_3_AMOUNT').to_d] if group_name == 'vip-2'
|
|
|
|
return [ENV.fetch('VIP_1_AMOUNT').to_d, ENV.fetch('VIP_2_AMOUNT').to_d] if group_name == 'vip-1'
|
|
|
|
return [0, ENV.fetch('VIP_1_AMOUNT').to_d] if group_name == 'vip-0'
|
|
end
|
|
end
|
|
|
|
def group_update(order)
|
|
|
|
|
|
return true if self.group == 'bot'
|
|
|
|
return true unless self.group.include?('vip')
|
|
|
|
all = self.trades.last_month.inject(0){ |sum, x| sum + x.rls }
|
|
|
|
return self.update(group: 'vip-4') if all > ENV.fetch('VIP_4_AMOUNT').to_d
|
|
|
|
return self.update(group: 'vip-3') if all > ENV.fetch('VIP_3_AMOUNT').to_d
|
|
|
|
return self.update(group: 'vip-2') if all > ENV.fetch('VIP_2_AMOUNT').to_d
|
|
|
|
return self.update(group: 'vip-1') if all > ENV.fetch('VIP_1_AMOUNT').to_d
|
|
|
|
self.update(group: 'vip-0')
|
|
|
|
end
|
|
|
|
|
|
def next_group
|
|
return group if group == Member.group_name_by_number(MAX_GROUP)
|
|
|
|
Member.group_name_by_number(group[4].to_i + 1)
|
|
end
|
|
|
|
def trades
|
|
Trade.where('maker_id = ? OR taker_id = ?', id, id)
|
|
end
|
|
|
|
def role
|
|
super&.inquiry
|
|
end
|
|
|
|
def admin?
|
|
role == "admin"
|
|
end
|
|
|
|
def get_account(model_or_id_or_code)
|
|
if model_or_id_or_code.is_a?(String) || model_or_id_or_code.is_a?(Symbol)
|
|
accounts.find_or_create_by(currency_id: model_or_id_or_code)
|
|
elsif model_or_id_or_code.is_a?(Currency)
|
|
accounts.find_or_create_by(currency: model_or_id_or_code)
|
|
end
|
|
# Thread Safe Account creation
|
|
rescue ActiveRecord::RecordNotUnique
|
|
if model_or_id_or_code.is_a?(String) || model_or_id_or_code.is_a?(Symbol)
|
|
accounts.find_by(currency_id: model_or_id_or_code)
|
|
elsif model_or_id_or_code.is_a?(Currency)
|
|
accounts.find_by(currency: model_or_id_or_code)
|
|
end
|
|
end
|
|
|
|
def referral_user
|
|
self.class.find_by(uid: referral_uid) if referral_uid.present?
|
|
end
|
|
|
|
# @deprecated
|
|
def touch_accounts
|
|
Currency.find_each do |currency|
|
|
next if accounts.where(currency: currency).exists?
|
|
accounts.create!(currency: currency)
|
|
end
|
|
end
|
|
|
|
def balance_for(currency:, kind:)
|
|
account_code = Operations::Account.find_by(
|
|
type: :liability,
|
|
kind: kind,
|
|
currency_type: currency.type
|
|
).code
|
|
liabilities = Operations::Liability.where(member_id: id, currency: currency, code: account_code)
|
|
liabilities.sum('credit - debit')
|
|
end
|
|
|
|
def legacy_balance_for(currency:, kind:)
|
|
if kind.to_sym == :main
|
|
get_account(currency).balance
|
|
elsif kind.to_sym == :locked
|
|
get_account(currency).locked
|
|
else
|
|
raise Operations::Exception, "Account for #{options} doesn't exists."
|
|
end
|
|
end
|
|
|
|
def revert_trading_activity!(trades)
|
|
trades.each(&:revert_trade!)
|
|
end
|
|
|
|
def payment_address(wallet_id, remote = false)
|
|
wallet = Wallet.find(wallet_id)
|
|
|
|
return if wallet.blank?
|
|
|
|
pa = PaymentAddress.find_by(member: self, wallet: wallet, remote: remote)
|
|
|
|
if pa.blank?
|
|
pa = payment_addresses.create!(wallet: wallet)
|
|
elsif pa.address.blank?
|
|
pa.enqueue_address_generation
|
|
end
|
|
|
|
pa
|
|
end
|
|
|
|
# Attempts to create additional deposit address for account.
|
|
def payment_address!(wallet_id, remote = false)
|
|
wallet = Wallet.find(wallet_id)
|
|
|
|
return if wallet.blank?
|
|
|
|
pa = PaymentAddress.find_by(member: self, wallet: wallet)
|
|
|
|
# The address generation process is in progress.
|
|
if pa.present? && pa.address.blank?
|
|
pa
|
|
else
|
|
# allows user to have multiple addresses
|
|
pa = payment_addresses.create!(wallet: wallet, remote: remote)
|
|
end
|
|
pa
|
|
end
|
|
|
|
def as_json_for_event_api
|
|
{
|
|
uid: uid,
|
|
email: email,
|
|
role: role,
|
|
level: level,
|
|
otp: otp,
|
|
state: state,
|
|
referral_uid: referral_uid
|
|
}
|
|
end
|
|
|
|
def sync_beneficiaries
|
|
fiat_currency = Currency.find('irt')
|
|
fiat_beneficiaries = beneficiaries.active_to_member.where(currency: fiat_currency)
|
|
array_beneficiaries = fiat_beneficiaries.map { |beneficiary| beneficiary&.data.dig('account_number') }&.compact
|
|
|
|
# delete extra beneficiary that was deleted in the Dalan
|
|
fiat_beneficiaries.each { |b| b.delete unless b.data.dig('account_number')&.in?(flat_cards + flat_ibans) }
|
|
|
|
# add beneficiary that was added in the Dalan
|
|
cards&.each { |c| create_beneficiary('card', c&.keys&.last, c&.values&.last, fiat_currency) unless c&.values&.last.in? (array_beneficiaries) }
|
|
ibans&.each { |i| create_beneficiary('iban', i&.keys&.last, i&.values&.last, fiat_currency) unless i&.values&.last.in? (array_beneficiaries) }
|
|
end
|
|
|
|
def flat_cards
|
|
return [] unless cards.present?
|
|
|
|
cards.map(&:values).flatten
|
|
end
|
|
|
|
def flat_ibans
|
|
return [] unless ibans.present?
|
|
|
|
ibans.map(&:values).flatten
|
|
end
|
|
|
|
def create_beneficiary(kind, name, data, currency, state: 'active')
|
|
beneficiaries.create!(currency: currency,
|
|
data: { full_name: name, account_number: data, kind: kind },
|
|
name: name, state: state)
|
|
end
|
|
|
|
private
|
|
|
|
def downcase_email
|
|
self.email = email.try(:downcase)
|
|
end
|
|
|
|
class << self
|
|
def uid(member_id)
|
|
Member.find_by(id: member_id)&.uid
|
|
end
|
|
|
|
def find_by_username_or_uid(uid_or_username)
|
|
if Member.find_by(uid: uid_or_username).present?
|
|
Member.find_by(uid: uid_or_username)
|
|
elsif Member.find_by(username: uid_or_username).present?
|
|
Member.find_by(username: uid_or_username)
|
|
end
|
|
end
|
|
|
|
def from_payload(p)
|
|
params = filter_payload(p)
|
|
validate_payload(params)
|
|
member = ::Member.find_or_create_by(uid: p[:uid]) do |m|
|
|
m.email = params[:email]
|
|
m.username = params[:username]
|
|
m.role = params[:role]
|
|
m.state = params[:state]
|
|
m.level = params[:level]
|
|
m.referral_uid = params.dig(:referral_uid)
|
|
m.last_change_pass = params.dig(:last_change_pass)
|
|
m.cards = params.dig(:cards)
|
|
m.ibans = params.dig(:ibans)
|
|
m.otp = params.dig(:otp)
|
|
end
|
|
member.assign_attributes(params)
|
|
# comment if dont want sync beneficiary
|
|
# flag_beneficiary = member.ibans_changed? || member.cards_changed? ? true : false
|
|
member.save! if member.changed?
|
|
# member.reload.sync_beneficiaries if flag_beneficiary
|
|
member
|
|
end
|
|
|
|
|
|
# Filter and validate payload params
|
|
def filter_payload(payload)
|
|
payload.slice(:email, :username, :uid, :role, :state, :level, :referral_uid, :last_change_pass, :cards, :ibans, :otp)
|
|
end
|
|
|
|
def validate_payload(p)
|
|
fetch_email(p)
|
|
p.fetch(:uid).tap { |uid| raise(Peatio::Auth::Error, 'UID is blank.') if uid.blank? }
|
|
p.fetch(:role).tap { |role| raise(Peatio::Auth::Error, 'Role is blank.') if role.blank? }
|
|
p.fetch(:level).tap { |level| raise(Peatio::Auth::Error, 'Level is blank.') if level.blank? }
|
|
p.fetch(:state).tap do |state|
|
|
raise(Peatio::Auth::Error, 'State is blank.') if state.blank?
|
|
raise(Peatio::Auth::Error, 'State is not active.') unless state == 'active'
|
|
end
|
|
end
|
|
|
|
def fetch_email(payload)
|
|
payload[:email].to_s.tap do |email|
|
|
raise(Peatio::Auth::Error, 'E-Mail is blank.') if email.blank?
|
|
raise(Peatio::Auth::Error, 'E-Mail is invalid.') unless EmailValidator.valid?(email)
|
|
end
|
|
end
|
|
|
|
def search(field: nil, term: nil)
|
|
term = "%#{term}%"
|
|
case field
|
|
when 'email'
|
|
where("email LIKE ?", term)
|
|
when 'uid'
|
|
where('uid LIKE ?', term)
|
|
when 'wallet_address'
|
|
joins(:payment_addresses).where('payment_addresses.address LIKE ?', term)
|
|
else
|
|
all
|
|
end.order(:id).reverse_order
|
|
end
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
# Schema version: 20201207134745
|
|
#
|
|
# Table name: members
|
|
#
|
|
# id :integer not null, primary key
|
|
# uid :string(32) not null
|
|
# email :string(255) not null
|
|
# username :string(255)
|
|
# level :integer not null
|
|
# role :string(16) not null
|
|
# group :string(32) default("vip-0"), not null
|
|
# state :string(16) not null
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_members_on_email (email) UNIQUE
|
|
# index_members_on_uid (uid) UNIQUE
|
|
# index_members_on_username (username) UNIQUE
|
|
#
|