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.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +67 -0
  4. data/Rakefile +43 -0
  5. data/app/assets/config/gera_manifest.js +2 -0
  6. data/app/assets/javascripts/gera/application.js +14 -0
  7. data/app/assets/stylesheets/gera/application.css +15 -0
  8. data/app/authorizers/gera/application_authorizer.rb +6 -0
  9. data/app/authorizers/gera/currency_rate_authorizer.rb +9 -0
  10. data/app/authorizers/gera/currency_rate_mode_authorizer.rb +7 -0
  11. data/app/authorizers/gera/direction_rate_authorizer.rb +8 -0
  12. data/app/authorizers/gera/exchange_rate_authorizer.rb +8 -0
  13. data/app/authorizers/gera/payment_system_authorizer.rb +13 -0
  14. data/app/authorizers/gera/rate_source_authorizer.rb +7 -0
  15. data/app/controllers/gera/application_controller.rb +30 -0
  16. data/app/controllers/gera/currencies_controller.rb +8 -0
  17. data/app/controllers/gera/currency_rate_history_intervals_controller.rb +53 -0
  18. data/app/controllers/gera/currency_rate_mode_snapshots_controller.rb +83 -0
  19. data/app/controllers/gera/currency_rate_modes_controller.rb +60 -0
  20. data/app/controllers/gera/currency_rates_controller.rb +70 -0
  21. data/app/controllers/gera/direction_rate_history_intervals_controller.rb +62 -0
  22. data/app/controllers/gera/direction_rates_controller.rb +28 -0
  23. data/app/controllers/gera/exchange_rates_controller.rb +25 -0
  24. data/app/controllers/gera/external_rate_snapshots_controller.rb +35 -0
  25. data/app/controllers/gera/external_rates_controller.rb +28 -0
  26. data/app/controllers/gera/payment_systems_controller.rb +78 -0
  27. data/app/controllers/gera/rate_sources_controller.rb +8 -0
  28. data/app/decorators/gera/currency_rate_decorator.rb +148 -0
  29. data/app/decorators/gera/direction_rate_decorator.rb +47 -0
  30. data/app/decorators/gera/payment_system_decorator.rb +19 -0
  31. data/app/helpers/gera/application_helper.rb +110 -0
  32. data/app/helpers/gera/currency_rate_helper.rb +19 -0
  33. data/app/helpers/gera/currency_rate_modes_helper.rb +73 -0
  34. data/app/helpers/gera/direction_rate_helper.rb +36 -0
  35. data/app/helpers/gera/exchange_rates_helper.rb +21 -0
  36. data/app/jobs/gera/application_job.rb +6 -0
  37. data/app/models/concerns/gera/currency_pair_generator.rb +14 -0
  38. data/app/models/concerns/gera/currency_pair_support.rb +42 -0
  39. data/app/models/concerns/gera/currency_rate_mode_builder_support.rb +35 -0
  40. data/app/models/concerns/gera/direction_support.rb +14 -0
  41. data/app/models/concerns/gera/history_interval_concern.rb +46 -0
  42. data/app/models/gera/application_record.rb +8 -0
  43. data/app/models/gera/cbr_external_rate.rb +13 -0
  44. data/app/models/gera/cross_rate_mode.rb +13 -0
  45. data/app/models/gera/currency_rate.rb +78 -0
  46. data/app/models/gera/currency_rate_history_interval.rb +25 -0
  47. data/app/models/gera/currency_rate_history_interval_filter.rb +30 -0
  48. data/app/models/gera/currency_rate_mode.rb +20 -0
  49. data/app/models/gera/currency_rate_mode_snapshot.rb +26 -0
  50. data/app/models/gera/currency_rate_snapshot.rb +12 -0
  51. data/app/models/gera/direction.rb +44 -0
  52. data/app/models/gera/direction_rate.rb +136 -0
  53. data/app/models/gera/direction_rate_history_interval.rb +33 -0
  54. data/app/models/gera/direction_rate_history_interval_filter.rb +30 -0
  55. data/app/models/gera/direction_rate_snapshot.rb +7 -0
  56. data/app/models/gera/exchange_rate.rb +130 -0
  57. data/app/models/gera/external_rate.rb +38 -0
  58. data/app/models/gera/external_rate_snapshot.rb +20 -0
  59. data/app/models/gera/payment_system.rb +98 -0
  60. data/app/models/gera/rate_source.rb +77 -0
  61. data/app/models/gera/rate_source_auto.rb +25 -0
  62. data/app/models/gera/rate_source_bitfinex.rb +9 -0
  63. data/app/models/gera/rate_source_cbr.rb +13 -0
  64. data/app/models/gera/rate_source_cbr_avg.rb +6 -0
  65. data/app/models/gera/rate_source_exmo.rb +9 -0
  66. data/app/models/gera/rate_source_manual.rb +13 -0
  67. data/app/views/application/_exchange_rate_monitor.slim +1 -0
  68. data/app/views/gera/application/_attributes_table.slim +5 -0
  69. data/app/views/gera/application/_currencies_table.slim +18 -0
  70. data/app/views/gera/application/_currency_rate_mode_snapshots.slim +6 -0
  71. data/app/views/gera/application/_direction_rate.slim +6 -0
  72. data/app/views/gera/application/_directions_submenu.slim +11 -0
  73. data/app/views/gera/application/_directions_table.slim +15 -0
  74. data/app/views/gera/application/_exchange_rate.slim +6 -0
  75. data/app/views/gera/application/_external_rate.slim +6 -0
  76. data/app/views/gera/application/_header.slim +12 -0
  77. data/app/views/gera/application/_table.slim +28 -0
  78. data/app/views/gera/application/_topnav.slim +21 -0
  79. data/app/views/gera/application/direction_details.slim +75 -0
  80. data/app/views/gera/application/page.html.slim +1 -0
  81. data/app/views/gera/currencies/index.html.slim +20 -0
  82. data/app/views/gera/currency_rate_history_intervals/_filter.slim +11 -0
  83. data/app/views/gera/currency_rate_history_intervals/index.slim +39 -0
  84. data/app/views/gera/currency_rate_mode_snapshots/_snapshot.slim +20 -0
  85. data/app/views/gera/currency_rate_mode_snapshots/edit.html.slim +30 -0
  86. data/app/views/gera/currency_rate_modes/_cross_rate_mode_fields.slim +21 -0
  87. data/app/views/gera/currency_rate_modes/_currency_rate_mode.slim +7 -0
  88. data/app/views/gera/currency_rate_modes/_form.slim +29 -0
  89. data/app/views/gera/currency_rate_modes/edit.slim +11 -0
  90. data/app/views/gera/currency_rate_modes/index.html.slim +12 -0
  91. data/app/views/gera/currency_rates/_currency_rate.slim +22 -0
  92. data/app/views/gera/currency_rates/_currency_rate_humanized.slim +35 -0
  93. data/app/views/gera/currency_rates/diff.slim +12 -0
  94. data/app/views/gera/currency_rates/index.slim +27 -0
  95. data/app/views/gera/currency_rates/modes.slim +14 -0
  96. data/app/views/gera/currency_rates/show.slim +4 -0
  97. data/app/views/gera/direction_rate_history_intervals/_filter.slim +11 -0
  98. data/app/views/gera/direction_rate_history_intervals/index.slim +41 -0
  99. data/app/views/gera/direction_rates/_direction_rate_example_calculator.slim +28 -0
  100. data/app/views/gera/direction_rates/diff.slim +19 -0
  101. data/app/views/gera/direction_rates/index.slim +26 -0
  102. data/app/views/gera/direction_rates/legacy.html.slim +5 -0
  103. data/app/views/gera/direction_rates/minimals.slim +5 -0
  104. data/app/views/gera/direction_rates/show.slim +14 -0
  105. data/app/views/gera/external_rate_snapshots/index.slim +25 -0
  106. data/app/views/gera/external_rate_snapshots/show.slim +22 -0
  107. data/app/views/gera/external_rates/_flat_list.slim +22 -0
  108. data/app/views/gera/external_rates/index.html.slim +28 -0
  109. data/app/views/gera/external_rates/show.slim +1 -0
  110. data/app/views/gera/payment_systems/_fields.slim +4 -0
  111. data/app/views/gera/payment_systems/_fields_table.slim +18 -0
  112. data/app/views/gera/payment_systems/_form.slim +9 -0
  113. data/app/views/gera/payment_systems/edit.slim +7 -0
  114. data/app/views/gera/payment_systems/index.slim +3 -0
  115. data/app/views/gera/payment_systems/new.slim +9 -0
  116. data/app/views/gera/payment_systems/show.slim +1 -0
  117. data/app/views/gera/rate_sources/_list.slim +13 -0
  118. data/app/views/gera/rate_sources/_rate_source.slim +23 -0
  119. data/app/views/gera/rate_sources/index.html.slim +6 -0
  120. data/app/views/layouts/application.html.slim +15 -0
  121. data/app/views/layouts/gera/blank.html.slim +10 -0
  122. data/app/workers/concerns/gera/rates_worker.rb +87 -0
  123. data/app/workers/gera/bitfinex_rates_worker.rb +65 -0
  124. data/app/workers/gera/cbr_avg_rates_worker.rb +42 -0
  125. data/app/workers/gera/cbr_rates_worker.rb +192 -0
  126. data/app/workers/gera/create_history_intervals_worker.rb +38 -0
  127. data/app/workers/gera/currency_rates_worker.rb +64 -0
  128. data/app/workers/gera/directions_rates_worker.rb +51 -0
  129. data/app/workers/gera/exmo_rates_worker.rb +62 -0
  130. data/app/workers/gera/purge_currency_rates_worker.rb +21 -0
  131. data/app/workers/gera/purge_direction_rates_worker.rb +30 -0
  132. data/config/currencies.yml +559 -0
  133. data/config/routes.rb +30 -0
  134. data/db/migrate/20180912130000_setup.rb +228 -0
  135. data/db/migrate/20190314114844_add_is_available_to_payment_systems.rb +7 -0
  136. data/db/migrate/20190315113046_add_icon_url_to_payment_systems.rb +7 -0
  137. data/db/migrate/20190315132137_add_commission_to_payment_systems.rb +7 -0
  138. data/lib/banks/currency_exchange.rb +12 -0
  139. data/lib/builders/currency_rate_auto_builder.rb +38 -0
  140. data/lib/builders/currency_rate_builder.rb +60 -0
  141. data/lib/builders/currency_rate_cross_builder.rb +70 -0
  142. data/lib/builders/currency_rate_direct_builder.rb +26 -0
  143. data/lib/gera.rb +32 -0
  144. data/lib/gera/bitfinex_fetcher.rb +36 -0
  145. data/lib/gera/configuration.rb +47 -0
  146. data/lib/gera/currencies_purger.rb +28 -0
  147. data/lib/gera/currency_pair.rb +93 -0
  148. data/lib/gera/engine.rb +14 -0
  149. data/lib/gera/mathematic.rb +156 -0
  150. data/lib/gera/money_support.rb +89 -0
  151. data/lib/gera/numeric.rb +25 -0
  152. data/lib/gera/railtie.rb +8 -0
  153. data/lib/gera/rate.rb +22 -0
  154. data/lib/gera/rate_from_multiplicator.rb +54 -0
  155. data/lib/gera/repositories/currency_rate_modes_repository.rb +37 -0
  156. data/lib/gera/repositories/currency_rates_repository.rb +25 -0
  157. data/lib/gera/repositories/direction_rates_repository.rb +46 -0
  158. data/lib/gera/repositories/exchange_rates_repository.rb +23 -0
  159. data/lib/gera/repositories/payment_systems_repository.rb +21 -0
  160. data/lib/gera/repositories/universe.rb +47 -0
  161. data/lib/gera/version.rb +3 -0
  162. data/lib/tasks/auto_generate_diagram.rake +6 -0
  163. data/lib/tasks/gera_tasks.rake +4 -0
  164. 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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ # @abstract
5
+ class ApplicationRecord < ::ApplicationRecord
6
+ self.abstract_class = true
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ class CbrExternalRate < ApplicationRecord
5
+ before_save do
6
+ raise 'нет значения' unless rate > 0
7
+ end
8
+
9
+ def <=>(other)
10
+ rate <=> other.rate
11
+ end
12
+ end
13
+ 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