gera 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +67 -0
- data/Rakefile +43 -0
- data/app/assets/config/gera_manifest.js +2 -0
- data/app/assets/javascripts/gera/application.js +14 -0
- data/app/assets/stylesheets/gera/application.css +15 -0
- data/app/authorizers/gera/application_authorizer.rb +6 -0
- data/app/authorizers/gera/currency_rate_authorizer.rb +9 -0
- data/app/authorizers/gera/currency_rate_mode_authorizer.rb +7 -0
- data/app/authorizers/gera/direction_rate_authorizer.rb +8 -0
- data/app/authorizers/gera/exchange_rate_authorizer.rb +8 -0
- data/app/authorizers/gera/payment_system_authorizer.rb +13 -0
- data/app/authorizers/gera/rate_source_authorizer.rb +7 -0
- data/app/controllers/gera/application_controller.rb +30 -0
- data/app/controllers/gera/currencies_controller.rb +8 -0
- data/app/controllers/gera/currency_rate_history_intervals_controller.rb +53 -0
- data/app/controllers/gera/currency_rate_mode_snapshots_controller.rb +83 -0
- data/app/controllers/gera/currency_rate_modes_controller.rb +60 -0
- data/app/controllers/gera/currency_rates_controller.rb +70 -0
- data/app/controllers/gera/direction_rate_history_intervals_controller.rb +62 -0
- data/app/controllers/gera/direction_rates_controller.rb +28 -0
- data/app/controllers/gera/exchange_rates_controller.rb +25 -0
- data/app/controllers/gera/external_rate_snapshots_controller.rb +35 -0
- data/app/controllers/gera/external_rates_controller.rb +28 -0
- data/app/controllers/gera/payment_systems_controller.rb +78 -0
- data/app/controllers/gera/rate_sources_controller.rb +8 -0
- data/app/decorators/gera/currency_rate_decorator.rb +148 -0
- data/app/decorators/gera/direction_rate_decorator.rb +47 -0
- data/app/decorators/gera/payment_system_decorator.rb +19 -0
- data/app/helpers/gera/application_helper.rb +110 -0
- data/app/helpers/gera/currency_rate_helper.rb +19 -0
- data/app/helpers/gera/currency_rate_modes_helper.rb +73 -0
- data/app/helpers/gera/direction_rate_helper.rb +36 -0
- data/app/helpers/gera/exchange_rates_helper.rb +21 -0
- data/app/jobs/gera/application_job.rb +6 -0
- data/app/models/concerns/gera/currency_pair_generator.rb +14 -0
- data/app/models/concerns/gera/currency_pair_support.rb +42 -0
- data/app/models/concerns/gera/currency_rate_mode_builder_support.rb +35 -0
- data/app/models/concerns/gera/direction_support.rb +14 -0
- data/app/models/concerns/gera/history_interval_concern.rb +46 -0
- data/app/models/gera/application_record.rb +8 -0
- data/app/models/gera/cbr_external_rate.rb +13 -0
- data/app/models/gera/cross_rate_mode.rb +13 -0
- data/app/models/gera/currency_rate.rb +78 -0
- data/app/models/gera/currency_rate_history_interval.rb +25 -0
- data/app/models/gera/currency_rate_history_interval_filter.rb +30 -0
- data/app/models/gera/currency_rate_mode.rb +20 -0
- data/app/models/gera/currency_rate_mode_snapshot.rb +26 -0
- data/app/models/gera/currency_rate_snapshot.rb +12 -0
- data/app/models/gera/direction.rb +44 -0
- data/app/models/gera/direction_rate.rb +136 -0
- data/app/models/gera/direction_rate_history_interval.rb +33 -0
- data/app/models/gera/direction_rate_history_interval_filter.rb +30 -0
- data/app/models/gera/direction_rate_snapshot.rb +7 -0
- data/app/models/gera/exchange_rate.rb +130 -0
- data/app/models/gera/external_rate.rb +38 -0
- data/app/models/gera/external_rate_snapshot.rb +20 -0
- data/app/models/gera/payment_system.rb +98 -0
- data/app/models/gera/rate_source.rb +77 -0
- data/app/models/gera/rate_source_auto.rb +25 -0
- data/app/models/gera/rate_source_bitfinex.rb +9 -0
- data/app/models/gera/rate_source_cbr.rb +13 -0
- data/app/models/gera/rate_source_cbr_avg.rb +6 -0
- data/app/models/gera/rate_source_exmo.rb +9 -0
- data/app/models/gera/rate_source_manual.rb +13 -0
- data/app/views/application/_exchange_rate_monitor.slim +1 -0
- data/app/views/gera/application/_attributes_table.slim +5 -0
- data/app/views/gera/application/_currencies_table.slim +18 -0
- data/app/views/gera/application/_currency_rate_mode_snapshots.slim +6 -0
- data/app/views/gera/application/_direction_rate.slim +6 -0
- data/app/views/gera/application/_directions_submenu.slim +11 -0
- data/app/views/gera/application/_directions_table.slim +15 -0
- data/app/views/gera/application/_exchange_rate.slim +6 -0
- data/app/views/gera/application/_external_rate.slim +6 -0
- data/app/views/gera/application/_header.slim +12 -0
- data/app/views/gera/application/_table.slim +28 -0
- data/app/views/gera/application/_topnav.slim +21 -0
- data/app/views/gera/application/direction_details.slim +75 -0
- data/app/views/gera/application/page.html.slim +1 -0
- data/app/views/gera/currencies/index.html.slim +20 -0
- data/app/views/gera/currency_rate_history_intervals/_filter.slim +11 -0
- data/app/views/gera/currency_rate_history_intervals/index.slim +39 -0
- data/app/views/gera/currency_rate_mode_snapshots/_snapshot.slim +20 -0
- data/app/views/gera/currency_rate_mode_snapshots/edit.html.slim +30 -0
- data/app/views/gera/currency_rate_modes/_cross_rate_mode_fields.slim +21 -0
- data/app/views/gera/currency_rate_modes/_currency_rate_mode.slim +7 -0
- data/app/views/gera/currency_rate_modes/_form.slim +29 -0
- data/app/views/gera/currency_rate_modes/edit.slim +11 -0
- data/app/views/gera/currency_rate_modes/index.html.slim +12 -0
- data/app/views/gera/currency_rates/_currency_rate.slim +22 -0
- data/app/views/gera/currency_rates/_currency_rate_humanized.slim +35 -0
- data/app/views/gera/currency_rates/diff.slim +12 -0
- data/app/views/gera/currency_rates/index.slim +27 -0
- data/app/views/gera/currency_rates/modes.slim +14 -0
- data/app/views/gera/currency_rates/show.slim +4 -0
- data/app/views/gera/direction_rate_history_intervals/_filter.slim +11 -0
- data/app/views/gera/direction_rate_history_intervals/index.slim +41 -0
- data/app/views/gera/direction_rates/_direction_rate_example_calculator.slim +28 -0
- data/app/views/gera/direction_rates/diff.slim +19 -0
- data/app/views/gera/direction_rates/index.slim +26 -0
- data/app/views/gera/direction_rates/legacy.html.slim +5 -0
- data/app/views/gera/direction_rates/minimals.slim +5 -0
- data/app/views/gera/direction_rates/show.slim +14 -0
- data/app/views/gera/external_rate_snapshots/index.slim +25 -0
- data/app/views/gera/external_rate_snapshots/show.slim +22 -0
- data/app/views/gera/external_rates/_flat_list.slim +22 -0
- data/app/views/gera/external_rates/index.html.slim +28 -0
- data/app/views/gera/external_rates/show.slim +1 -0
- data/app/views/gera/payment_systems/_fields.slim +4 -0
- data/app/views/gera/payment_systems/_fields_table.slim +18 -0
- data/app/views/gera/payment_systems/_form.slim +9 -0
- data/app/views/gera/payment_systems/edit.slim +7 -0
- data/app/views/gera/payment_systems/index.slim +3 -0
- data/app/views/gera/payment_systems/new.slim +9 -0
- data/app/views/gera/payment_systems/show.slim +1 -0
- data/app/views/gera/rate_sources/_list.slim +13 -0
- data/app/views/gera/rate_sources/_rate_source.slim +23 -0
- data/app/views/gera/rate_sources/index.html.slim +6 -0
- data/app/views/layouts/application.html.slim +15 -0
- data/app/views/layouts/gera/blank.html.slim +10 -0
- data/app/workers/concerns/gera/rates_worker.rb +87 -0
- data/app/workers/gera/bitfinex_rates_worker.rb +65 -0
- data/app/workers/gera/cbr_avg_rates_worker.rb +42 -0
- data/app/workers/gera/cbr_rates_worker.rb +192 -0
- data/app/workers/gera/create_history_intervals_worker.rb +38 -0
- data/app/workers/gera/currency_rates_worker.rb +64 -0
- data/app/workers/gera/directions_rates_worker.rb +51 -0
- data/app/workers/gera/exmo_rates_worker.rb +62 -0
- data/app/workers/gera/purge_currency_rates_worker.rb +21 -0
- data/app/workers/gera/purge_direction_rates_worker.rb +30 -0
- data/config/currencies.yml +559 -0
- data/config/routes.rb +30 -0
- data/db/migrate/20180912130000_setup.rb +228 -0
- data/db/migrate/20190314114844_add_is_available_to_payment_systems.rb +7 -0
- data/db/migrate/20190315113046_add_icon_url_to_payment_systems.rb +7 -0
- data/db/migrate/20190315132137_add_commission_to_payment_systems.rb +7 -0
- data/lib/banks/currency_exchange.rb +12 -0
- data/lib/builders/currency_rate_auto_builder.rb +38 -0
- data/lib/builders/currency_rate_builder.rb +60 -0
- data/lib/builders/currency_rate_cross_builder.rb +70 -0
- data/lib/builders/currency_rate_direct_builder.rb +26 -0
- data/lib/gera.rb +32 -0
- data/lib/gera/bitfinex_fetcher.rb +36 -0
- data/lib/gera/configuration.rb +47 -0
- data/lib/gera/currencies_purger.rb +28 -0
- data/lib/gera/currency_pair.rb +93 -0
- data/lib/gera/engine.rb +14 -0
- data/lib/gera/mathematic.rb +156 -0
- data/lib/gera/money_support.rb +89 -0
- data/lib/gera/numeric.rb +25 -0
- data/lib/gera/railtie.rb +8 -0
- data/lib/gera/rate.rb +22 -0
- data/lib/gera/rate_from_multiplicator.rb +54 -0
- data/lib/gera/repositories/currency_rate_modes_repository.rb +37 -0
- data/lib/gera/repositories/currency_rates_repository.rb +25 -0
- data/lib/gera/repositories/direction_rates_repository.rb +46 -0
- data/lib/gera/repositories/exchange_rates_repository.rb +23 -0
- data/lib/gera/repositories/payment_systems_repository.rb +21 -0
- data/lib/gera/repositories/universe.rb +47 -0
- data/lib/gera/version.rb +3 -0
- data/lib/tasks/auto_generate_diagram.rake +6 -0
- data/lib/tasks/gera_tasks.rake +4 -0
- metadata +765 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
module HistoryIntervalConcern
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
INTERVAL = 5.minutes
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
extend AutoLogger::Named.new(name: 'history_intervals')
|
|
10
|
+
before_save do
|
|
11
|
+
self.avg_rate = (max_rate + min_rate) / 2.0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
before_save do
|
|
15
|
+
raise "Time must be even to #{INTERVAL}" unless interval_from.min % INTERVAL.parts[:minutes] == 0
|
|
16
|
+
raise "Time must be even to #{INTERVAL}" unless interval_to.min % INTERVAL.parts[:minutes] == 0
|
|
17
|
+
raise 'Time must have zero seconds' unless interval_from.sec == 0
|
|
18
|
+
raise 'Time must have zero seconds' unless interval_to.sec == 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
before_save do
|
|
22
|
+
raise "min_rate (#{min_rate}) must be less or equal to max_rate (#{max_rate})" if min_rate > max_rate
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class_methods do
|
|
27
|
+
def create_multiple_intervals_from!(interval_from, interval_to = nil)
|
|
28
|
+
interval_from -= interval_from.sec # Обнуляем секунды
|
|
29
|
+
interval_from -= interval_from.min % INTERVAL.parts[:minutes]
|
|
30
|
+
interval_to ||= Time.zone.now
|
|
31
|
+
((interval_to - interval_from) / 5.minutes).to_i.times do |i|
|
|
32
|
+
f = interval_from + i * INTERVAL
|
|
33
|
+
begin
|
|
34
|
+
logger.info "#{self}: Create HistoryInterval #{f} -> #{f + INTERVAL}"
|
|
35
|
+
transaction do
|
|
36
|
+
create_by_interval! f, f + INTERVAL
|
|
37
|
+
end
|
|
38
|
+
logger.info "#{self}: Create HistoryInterval #{f} -> #{f + INTERVAL} - done"
|
|
39
|
+
rescue ActiveRecord::RecordNotUnique => err
|
|
40
|
+
logger.error err
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class CrossRateMode < ApplicationRecord
|
|
5
|
+
include CurrencyPairSupport
|
|
6
|
+
belongs_to :currency_rate_mode
|
|
7
|
+
belongs_to :rate_source, optional: true
|
|
8
|
+
|
|
9
|
+
def title
|
|
10
|
+
"#{currency_pair}(#{rate_source || 'auto'})"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
# Базовый курс
|
|
5
|
+
# Basic currency rate
|
|
6
|
+
class CurrencyRate < ApplicationRecord
|
|
7
|
+
include CurrencyPairSupport
|
|
8
|
+
include Authority::Abilities
|
|
9
|
+
|
|
10
|
+
belongs_to :snapshot, class_name: 'Gera::CurrencyRateSnapshot'
|
|
11
|
+
belongs_to :external_rate, optional: true
|
|
12
|
+
belongs_to :rate_source, optional: true
|
|
13
|
+
|
|
14
|
+
belongs_to :external_rate1, class_name: 'Gera::ExternalRate', optional: true
|
|
15
|
+
belongs_to :external_rate2, class_name: 'Gera::ExternalRate', optional: true
|
|
16
|
+
belongs_to :external_rate3, class_name: 'Gera::ExternalRate', optional: true
|
|
17
|
+
|
|
18
|
+
has_many :direction_rates, class_name: 'Gera::DirectionRate'
|
|
19
|
+
|
|
20
|
+
scope :by_exchange_rate, ->(er) { by_currency_pair er.currency_pair }
|
|
21
|
+
|
|
22
|
+
enum mode: %i[direct inverse same cross], _prefix: true
|
|
23
|
+
|
|
24
|
+
before_save do
|
|
25
|
+
raise("У кросс-курса (#{currency_pair}) должно быть несколько external_rates (#{external_rates.count})") if mode_cross? && !external_rates.many?
|
|
26
|
+
|
|
27
|
+
self.metadata ||= {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def external_rates=(rates)
|
|
31
|
+
self.external_rate, self.external_rate1, self.external_rate2, self.external_rate3 = rates
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def external_rates
|
|
35
|
+
[external_rate, external_rate1, external_rate2, external_rate3].compact
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_s
|
|
39
|
+
currency_pair.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def inspect
|
|
43
|
+
"#{currency_pair}:#{humanized_rate}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def in_money
|
|
47
|
+
reverse_rate_money.to_d > 1 ? reverse_rate_money : 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def out_money
|
|
51
|
+
rate_money.to_d > 1 ? rate_money : 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def rate_money
|
|
55
|
+
Money.from_amount(rate_value, cur_to)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def reverse_rate_money
|
|
59
|
+
Money.from_amount(1.0 / rate_value, cur_from)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def humanized_rate
|
|
63
|
+
if rate_value < 1
|
|
64
|
+
"#{rate_value} (1/#{1.0 / rate_value})"
|
|
65
|
+
else
|
|
66
|
+
rate_value
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def dump
|
|
71
|
+
as_json(only: %i[created_at cur_from cur_to mode rate_value metadata rate_source_id]).merge external_rates: external_rates.map(&:dump)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def meta
|
|
75
|
+
@meta ||= OpenStruct.new metadata.deep_symbolize_keys
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class CurrencyRateHistoryInterval < ApplicationRecord
|
|
5
|
+
include HistoryIntervalConcern
|
|
6
|
+
def self.create_by_interval!(interval_from, interval_to = nil)
|
|
7
|
+
interval_to ||= interval_from + INTERVAL
|
|
8
|
+
CurrencyRate
|
|
9
|
+
.where('created_at >= ? and created_at < ?', interval_from, interval_to)
|
|
10
|
+
.group(:cur_from, :cur_to)
|
|
11
|
+
.pluck(:cur_from, :cur_to, 'min(rate_value)', 'max(rate_value)')
|
|
12
|
+
.each do |cur_from, cur_to, min_rate, max_rate|
|
|
13
|
+
|
|
14
|
+
next if cur_from == cur_to
|
|
15
|
+
|
|
16
|
+
create!(
|
|
17
|
+
cur_from_id: Money::Currency.find(cur_from).local_id,
|
|
18
|
+
cur_to_id: Money::Currency.find(cur_to).local_id,
|
|
19
|
+
min_rate: min_rate, max_rate: max_rate,
|
|
20
|
+
interval_from: interval_from, interval_to: interval_to
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class CurrencyRateHistoryIntervalFilter
|
|
5
|
+
include Virtus.model strict: true
|
|
6
|
+
include ActiveModel::Conversion
|
|
7
|
+
extend ActiveModel::Naming
|
|
8
|
+
include ActiveModel::Validations
|
|
9
|
+
|
|
10
|
+
attribute :cur_from, String, default: ->(_a, _b) { Money::Currency.first }
|
|
11
|
+
attribute :cur_to, String, default: ->(_a, _b) { Money::Currency.first }
|
|
12
|
+
attribute :value_type, String, default: 'rate'
|
|
13
|
+
|
|
14
|
+
def currency_from
|
|
15
|
+
Money::Currency.find cur_from
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def currency_to
|
|
19
|
+
Money::Currency.find cur_to
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_param
|
|
23
|
+
to_hash
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def persisted?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class CurrencyRateMode < ApplicationRecord
|
|
5
|
+
include CurrencyPairSupport
|
|
6
|
+
include CurrencyRateModeBuilderSupport
|
|
7
|
+
include Authority::Abilities
|
|
8
|
+
|
|
9
|
+
belongs_to :snapshot, class_name: 'CurrencyRateModeSnapshot', foreign_key: :currency_rate_mode_snapshot_id
|
|
10
|
+
has_many :cross_rate_modes
|
|
11
|
+
|
|
12
|
+
# Тут режими из ключей rate_source
|
|
13
|
+
# TODO выделить привязку к rate_source в отедельную ассоциацию
|
|
14
|
+
enum mode: %i[auto cbr cbr_avg exmo cross bitfinex], _prefix: true
|
|
15
|
+
|
|
16
|
+
accepts_nested_attributes_for :cross_rate_modes, reject_if: :all_blank, allow_destroy: true
|
|
17
|
+
|
|
18
|
+
delegate :to_s, to: :mode
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class CurrencyRateModeSnapshot < ApplicationRecord
|
|
5
|
+
has_many :currency_rate_modes, dependent: :destroy
|
|
6
|
+
|
|
7
|
+
scope :ordered, -> { order('status desc').order('created_at desc') }
|
|
8
|
+
|
|
9
|
+
enum status: %i[draft active deactive], _prefix: true
|
|
10
|
+
|
|
11
|
+
accepts_nested_attributes_for :currency_rate_modes
|
|
12
|
+
|
|
13
|
+
before_validation do
|
|
14
|
+
self.title = Time.zone.now.to_s if title.blank?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
validates :title, presence: true, uniqueness: true
|
|
18
|
+
|
|
19
|
+
def create_modes!
|
|
20
|
+
CurrencyPair.all.each do |pair|
|
|
21
|
+
currency_rate_modes.create! currency_pair: pair
|
|
22
|
+
end
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class CurrencyRateSnapshot < ApplicationRecord
|
|
5
|
+
has_many :rates, class_name: 'Gera::CurrencyRate', foreign_key: :snapshot_id
|
|
6
|
+
belongs_to :currency_rate_mode_snapshot
|
|
7
|
+
|
|
8
|
+
def currency_rates
|
|
9
|
+
rates
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
# Exchange Direction
|
|
5
|
+
#
|
|
6
|
+
class Direction
|
|
7
|
+
include Virtus.model
|
|
8
|
+
|
|
9
|
+
attribute :ps_from # , PaymentSystem
|
|
10
|
+
attribute :ps_to # , PaymentSystem
|
|
11
|
+
|
|
12
|
+
alias_attribute :payment_system_from, :ps_from
|
|
13
|
+
alias_attribute :payment_system_to, :ps_to
|
|
14
|
+
alias_attribute :income_payment_system, :ps_from
|
|
15
|
+
alias_attribute :outcome_payment_system, :ps_to
|
|
16
|
+
|
|
17
|
+
delegate :id, to: :ps_to, prefix: true
|
|
18
|
+
delegate :id, to: :ps_from, prefix: true
|
|
19
|
+
|
|
20
|
+
def currency_from
|
|
21
|
+
payment_system_from.currency
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def currency_to
|
|
25
|
+
payment_system_to.currency
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def inspect
|
|
29
|
+
to_s
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_s
|
|
33
|
+
"direction:#{payment_system_from.try(:id) || '???'}-#{payment_system_to.try(:id) || '???'}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def exchange_rate
|
|
37
|
+
Universe.exchange_rates_repository.find_by_direction self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def direction_rate
|
|
41
|
+
Universe.direction_rates_repository.find_by_direction self
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
# Конечный курс
|
|
5
|
+
# Finite direction rate
|
|
6
|
+
class DirectionRate < ApplicationRecord
|
|
7
|
+
include Mathematic
|
|
8
|
+
include AutoLogger
|
|
9
|
+
include DirectionSupport
|
|
10
|
+
include Authority::Abilities
|
|
11
|
+
|
|
12
|
+
UnknownExchangeRate = Class.new StandardError
|
|
13
|
+
|
|
14
|
+
belongs_to :ps_from, class_name: 'Gera::PaymentSystem'
|
|
15
|
+
belongs_to :ps_to, class_name: 'Gera::PaymentSystem'
|
|
16
|
+
belongs_to :currency_rate, class_name: 'Gera::CurrencyRate'
|
|
17
|
+
belongs_to :exchange_rate
|
|
18
|
+
belongs_to :snapshot, class_name: 'Gera::DirectionRateSnapshot'
|
|
19
|
+
|
|
20
|
+
before_validation do
|
|
21
|
+
self.ps_from = exchange_rate.payment_system_from
|
|
22
|
+
self.ps_to = exchange_rate.payment_system_to
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
before_validation :calculate_rate, unless: :finite_rate
|
|
26
|
+
|
|
27
|
+
validates :base_rate_value, presence: true
|
|
28
|
+
validates :rate_percent, presence: true
|
|
29
|
+
validates :finite_rate, presence: true
|
|
30
|
+
|
|
31
|
+
alias_attribute :payment_system_from, :ps_from
|
|
32
|
+
alias_attribute :payment_system_to, :ps_to
|
|
33
|
+
alias_attribute :income_payment_system, :ps_from
|
|
34
|
+
alias_attribute :outcome_payment_system, :ps_to
|
|
35
|
+
alias_attribute :income_payment_system_id, :ps_from_id
|
|
36
|
+
alias_attribute :outcome_payment_system_id, :ps_to_id
|
|
37
|
+
|
|
38
|
+
alias_attribute :comission, :rate_percent
|
|
39
|
+
alias_attribute :finite_rate, :rate_value
|
|
40
|
+
|
|
41
|
+
def exchange(amount)
|
|
42
|
+
rate.exchange amount, outcome_currency
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def reverse_exchange(amount)
|
|
46
|
+
rate.reverse_exchange amount, income_currency
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def currency_pair
|
|
50
|
+
@currency_pair ||= CurrencyPair.new income_currency, outcome_currency
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def income_currency
|
|
54
|
+
ps_from.currency
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def outcome_currency
|
|
58
|
+
ps_to.currency
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def ps_comission
|
|
62
|
+
ps_to.commission
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def in_money
|
|
66
|
+
return 1 if rate_value < 1
|
|
67
|
+
|
|
68
|
+
rate_value
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def out_money
|
|
72
|
+
return 1.0 / rate_value if rate_value < 1
|
|
73
|
+
|
|
74
|
+
1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def rate
|
|
78
|
+
RateFromMultiplicator.new(rate_value).freeze
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def rate_money
|
|
82
|
+
Money.from_amount(rate_value, currency_rate.currency_to)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def base_rate
|
|
86
|
+
RateFromMultiplicator.new(base_rate_value).freeze
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def inverse_direction_rate
|
|
90
|
+
Universe.direction_rates_repository.get_matrix[ps_to_id][ps_from_id]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def get_profit_result(income_amount)
|
|
94
|
+
res = calculate_profits(
|
|
95
|
+
base_rate: base_rate_value,
|
|
96
|
+
comission: rate_percent,
|
|
97
|
+
ps_interest: ps_comission,
|
|
98
|
+
income_amount: income_amount
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
diff = res.finite_rate.to_f.as_percentage_of(rate_value.to_f) - 100
|
|
102
|
+
|
|
103
|
+
logger.warn "direction_rate_id=#{id} Calculates finite rate (#{res.finite_rate}) does not equal to current (#{rate_value}). Difference is #{diff}" if diff.abs > 0
|
|
104
|
+
|
|
105
|
+
res
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def dump
|
|
109
|
+
as_json(only: %i[id ps_from_id ps_to_id currency_rate_id rate_value base_rate_value rate_percent created_at])
|
|
110
|
+
.merge currency_rate: currency_rate.dump, dump_version: 1
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def exchange_notification
|
|
114
|
+
ExchangeNotification.find_by(
|
|
115
|
+
income_payment_system_id: income_payment_system_id,
|
|
116
|
+
outcome_payment_system_id: outcome_payment_system_id
|
|
117
|
+
) ||
|
|
118
|
+
ExchangeNotification.find_by(
|
|
119
|
+
income_payment_system_id: income_payment_system_id,
|
|
120
|
+
outcome_payment_system_id: nil
|
|
121
|
+
) ||
|
|
122
|
+
ExchangeNotification.find_by(
|
|
123
|
+
income_payment_system_id: nil,
|
|
124
|
+
outcome_payment_system_id: outcome_payment_system_id
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def calculate_rate
|
|
129
|
+
self.base_rate_value = currency_rate.rate_value
|
|
130
|
+
raise UnknownExchangeRate, "No exchange_rate for #{ps_from}->#{ps_to}" unless exchange_rate
|
|
131
|
+
|
|
132
|
+
self.rate_percent = exchange_rate.comission_percents
|
|
133
|
+
self.rate_value = calculate_finite_rate base_rate_value, rate_percent unless rate_percent.nil?
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|