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,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class DirectionRateHistoryInterval < ApplicationRecord
|
|
5
|
+
include HistoryIntervalConcern
|
|
6
|
+
|
|
7
|
+
# Их не надо подключать, потому что иначе при создании записи
|
|
8
|
+
# ActiveRercord проверяет есить ли они в базе
|
|
9
|
+
#
|
|
10
|
+
# belongs_to :payment_system_from, class_name: 'PaymentSystem'
|
|
11
|
+
# belongs_to :payment_system_to, class_name: 'PaymentSystem'
|
|
12
|
+
|
|
13
|
+
def self.create_by_interval!(interval_from, interval_to = nil)
|
|
14
|
+
interval_to ||= interval_from + INTERVAL
|
|
15
|
+
DirectionRate
|
|
16
|
+
.where('created_at >= ? and created_at < ?', interval_from, interval_to)
|
|
17
|
+
.group(:ps_from_id, :ps_to_id)
|
|
18
|
+
.pluck(:ps_from_id, :ps_to_id, 'min(rate_value)', 'max(rate_value)', 'min(rate_percent)', 'max(rate_percent)')
|
|
19
|
+
.each do |ps_from_id, ps_to_id, min_rate, max_rate, min_comission, max_comission|
|
|
20
|
+
|
|
21
|
+
next if ps_from_id == ps_to_id
|
|
22
|
+
|
|
23
|
+
create!(
|
|
24
|
+
payment_system_from_id: ps_from_id,
|
|
25
|
+
payment_system_to_id: ps_to_id,
|
|
26
|
+
min_rate: min_rate, max_rate: max_rate,
|
|
27
|
+
min_comission: min_comission, max_comission: max_comission,
|
|
28
|
+
interval_from: interval_from, interval_to: interval_to
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class DirectionRateHistoryIntervalFilter
|
|
5
|
+
include Virtus.model strict: true
|
|
6
|
+
include ActiveModel::Conversion
|
|
7
|
+
extend ActiveModel::Naming
|
|
8
|
+
include ActiveModel::Validations
|
|
9
|
+
|
|
10
|
+
attribute :payment_system_from_id, Integer, default: ->(_a, _b) { Gera::PaymentSystem.first.id }
|
|
11
|
+
attribute :payment_system_to_id, Integer, default: ->(_a, _b) { Gera::PaymentSystem.first.id }
|
|
12
|
+
attribute :value_type, String, default: 'rate'
|
|
13
|
+
|
|
14
|
+
def payment_system_from
|
|
15
|
+
@payment_system_from ||= Gera::PaymentSystem.find payment_system_from_id
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def payment_system_to
|
|
19
|
+
@payment_system_to ||= Gera::PaymentSystem.find payment_system_to_id
|
|
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,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Комиссия для расчета конечного курса между платежными системами
|
|
4
|
+
# TODO: Пеерименовать в Direction
|
|
5
|
+
# Комиссии по направлениям платежных систем
|
|
6
|
+
# если в парсере операторы изменили курс/комиссию, то эта комиссия
|
|
7
|
+
# устанвливается сначала сюда, потом растекается по остальным
|
|
8
|
+
#
|
|
9
|
+
# * value - само значение комиссии
|
|
10
|
+
# * cor1/cor2 - границы коридора
|
|
11
|
+
#
|
|
12
|
+
# * position - позиция в best которую нужно установить?
|
|
13
|
+
# * on_notif - ???
|
|
14
|
+
# * on_corridor - в коридоре?
|
|
15
|
+
module Gera
|
|
16
|
+
class ExchangeRate < ApplicationRecord
|
|
17
|
+
include Authority::Abilities
|
|
18
|
+
|
|
19
|
+
DEFAULT_COMISSION = 50
|
|
20
|
+
|
|
21
|
+
include Mathematic
|
|
22
|
+
include DirectionSupport
|
|
23
|
+
|
|
24
|
+
belongs_to :payment_system_from, foreign_key: :income_payment_system_id, class_name: 'Gera::PaymentSystem'
|
|
25
|
+
belongs_to :payment_system_to, foreign_key: :outcome_payment_system_id, class_name: 'Gera::PaymentSystem'
|
|
26
|
+
|
|
27
|
+
scope :ordered, -> { order :id }
|
|
28
|
+
scope :enabled, -> { where is_enabled: true }
|
|
29
|
+
|
|
30
|
+
scope :with_payment_systems, lambda {
|
|
31
|
+
includes(:payment_system_from, :payment_system_to)
|
|
32
|
+
.joins(:payment_system_from, :payment_system_to)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
scope :available, lambda {
|
|
36
|
+
with_payment_systems
|
|
37
|
+
.enabled
|
|
38
|
+
.where("#{PaymentSystem.table_name}.income_enabled and payment_system_tos_gera_exchange_rates.outcome_enabled")
|
|
39
|
+
.where("#{table_name}.income_payment_system_id <> #{table_name}.outcome_payment_system_id")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
after_commit :update_direction_rates, if: -> { previous_changes.key?('value') }
|
|
43
|
+
|
|
44
|
+
before_create do
|
|
45
|
+
self.in_cur = payment_system_from.currency.to_s
|
|
46
|
+
self.out_cur = payment_system_to.currency.to_s
|
|
47
|
+
self.comission ||= DEFAULT_COMISSION
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
validates :commission, presence: true
|
|
51
|
+
|
|
52
|
+
delegate :rate, :currency_rate, to: :direction_rate
|
|
53
|
+
|
|
54
|
+
alias_attribute :ps_from_id, :income_payment_system_id
|
|
55
|
+
alias_attribute :ps_to_id, :outcome_payment_system_id
|
|
56
|
+
alias_attribute :payment_system_from_id, :income_payment_system_id
|
|
57
|
+
alias_attribute :payment_system_to_id, :outcome_payment_system_id
|
|
58
|
+
|
|
59
|
+
alias_attribute :comission, :value
|
|
60
|
+
alias_attribute :commission, :value
|
|
61
|
+
alias_attribute :comission_percents, :value
|
|
62
|
+
|
|
63
|
+
alias_attribute :income_payment_system, :payment_system_from
|
|
64
|
+
alias_attribute :outcome_payment_system, :payment_system_to
|
|
65
|
+
|
|
66
|
+
def self.list_rates
|
|
67
|
+
order('id asc').each_with_object({}) do |er, h|
|
|
68
|
+
h[er.income_payment_system_id] ||= {}
|
|
69
|
+
h[er.income_payment_system_id][er.outcome_payment_system_id] = h.value
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def available?
|
|
74
|
+
is_enabled?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update_finite_rate!(finite_rate)
|
|
78
|
+
update! comission: calculate_comission(finite_rate, currency_rate.rate_value)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def custom_inspect
|
|
82
|
+
{
|
|
83
|
+
value: value,
|
|
84
|
+
exchange_rate_id: id,
|
|
85
|
+
payment_system_to: payment_system_to.to_s,
|
|
86
|
+
payment_system_from: payment_system_from.to_s,
|
|
87
|
+
out_currency: out_currency.to_s,
|
|
88
|
+
in_currency: in_currency.to_s
|
|
89
|
+
}.to_s
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def currency_pair
|
|
93
|
+
@currency_pair ||= CurrencyPair.new in_currency, out_currency
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def out_currency
|
|
97
|
+
Money::Currency.find out_cur
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def currency_to
|
|
101
|
+
out_currency
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def currency_from
|
|
105
|
+
in_currency
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def in_currency
|
|
109
|
+
Money::Currency.find in_cur
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def finite_rate
|
|
113
|
+
direction_rate.rate
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def to_s
|
|
117
|
+
[in_currency, out_currency].join '/'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def direction_rate
|
|
121
|
+
Universe.direction_rates_repository.find_direction_rate_by_exchange_rate_id id
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def update_direction_rates
|
|
127
|
+
DirectionsRatesWorker.perform_async(exchange_rate_id: id)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Курсы внешних систем
|
|
4
|
+
module Gera
|
|
5
|
+
class ExternalRate < ApplicationRecord
|
|
6
|
+
include CurrencyPairSupport
|
|
7
|
+
|
|
8
|
+
belongs_to :source, class_name: 'RateSource'
|
|
9
|
+
belongs_to :snapshot, class_name: 'ExternalRateSnapshot'
|
|
10
|
+
|
|
11
|
+
scope :ordered, -> { order :cur_from, :cur_to }
|
|
12
|
+
|
|
13
|
+
before_validation do
|
|
14
|
+
self.source ||= snapshot.try :rate_source
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
before_validation :upcase_currencies
|
|
18
|
+
|
|
19
|
+
# TODO: validate cur_from, cur_to из списка разрешенных
|
|
20
|
+
|
|
21
|
+
delegate :actual_for, to: :snapshot
|
|
22
|
+
|
|
23
|
+
def direction_rate
|
|
24
|
+
Universe.direction_rates_repository.find_direction_rate_by_exchange_rate_id id
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def dump
|
|
28
|
+
as_json(only: %i[id cur_from cur_to rate_value source_id created_at])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def upcase_currencies
|
|
34
|
+
self.cur_from = cur_from.to_s.upcase if cur_from.present?
|
|
35
|
+
self.cur_to = cur_to.to_s.upcase if cur_to.present?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class ExternalRateSnapshot < ApplicationRecord
|
|
5
|
+
belongs_to :rate_source
|
|
6
|
+
|
|
7
|
+
has_many :external_rates, foreign_key: :snapshot_id
|
|
8
|
+
|
|
9
|
+
scope :ordered, -> { order 'actual_for desc' }
|
|
10
|
+
scope :last_actuals_by_rate_sources, -> { where id: group(:rate_source_id).maximum(:id).values }
|
|
11
|
+
|
|
12
|
+
before_save do
|
|
13
|
+
self.actual_for ||= Time.zone.now
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_s
|
|
17
|
+
"snapshot[#{id}]:#{rate_source}:#{actual_for}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class PaymentSystem < ApplicationRecord
|
|
5
|
+
include ::Archivable
|
|
6
|
+
include Gera::Mathematic
|
|
7
|
+
include Authority::Abilities
|
|
8
|
+
|
|
9
|
+
scope :ordered, -> { order :priority }
|
|
10
|
+
scope :enabled, -> { where 'income_enabled>0 or outcome_enabled>0' }
|
|
11
|
+
scope :disabled, -> { where income_enabled: false, outcome_enabled: false }
|
|
12
|
+
scope :available, -> { where is_available: true }
|
|
13
|
+
|
|
14
|
+
# TODO: move to kassa-admin
|
|
15
|
+
enum total_computation_method: %i[regular_fee reverse_fee]
|
|
16
|
+
enum transfer_comission_payer: %i[user shop], _prefix: :transfer_comission_payer
|
|
17
|
+
|
|
18
|
+
validates :name, presence: true, uniqueness: true
|
|
19
|
+
validates :currency, presence: true
|
|
20
|
+
|
|
21
|
+
before_create do
|
|
22
|
+
self.priority = self.class.maximum(:priority).to_i + 1
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
after_create :create_exchange_rates
|
|
26
|
+
|
|
27
|
+
delegate :iso_code, to: :currency, prefix: true, allow_nil: true
|
|
28
|
+
|
|
29
|
+
alias_attribute :archived_at, :deleted_at
|
|
30
|
+
alias_attribute :enable_income, :income_enabled
|
|
31
|
+
alias_attribute :enable_outcome, :outcome_enabled
|
|
32
|
+
|
|
33
|
+
# TODO: rename type_cy to currency
|
|
34
|
+
def currency
|
|
35
|
+
return unless type_cy
|
|
36
|
+
|
|
37
|
+
@currency ||= Money::Currency.find_by_local_id(type_cy) || raise("Не найдена валюта #{type_cy}")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def currency=(cur)
|
|
41
|
+
cur = Money::Currency.find cur unless cur.is_a? Money::Currency
|
|
42
|
+
self.type_cy = cur.is_a?(Money::Currency) ? cur.local_id : nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_s
|
|
46
|
+
name
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# TODO: move to kassa-admin
|
|
50
|
+
def total_with_fee(money)
|
|
51
|
+
calculate_total(money: money, fee: transfer_fee)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def unverified_total_with_fee(money)
|
|
55
|
+
calculate_total(money: money, fee: unverified_transfer_fee)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def calculate_total(money:, fee:)
|
|
61
|
+
if fee.computation_method == 'regular_fee'
|
|
62
|
+
calculate_total_using_regular_comission(money, fee.amount)
|
|
63
|
+
elsif fee.computation_method == 'reverse_fee'
|
|
64
|
+
calculate_total_using_reverse_comission(money, fee.amount)
|
|
65
|
+
else
|
|
66
|
+
raise NotImplementedError, "Нет расчета для #{fee.computation_method}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def transfer_fee
|
|
71
|
+
OpenStruct.new(
|
|
72
|
+
amount: income_fee,
|
|
73
|
+
computation_method: total_computation_method
|
|
74
|
+
).freeze
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def unverified_transfer_fee
|
|
78
|
+
OpenStruct.new(
|
|
79
|
+
amount: unverified_income_fee,
|
|
80
|
+
computation_method: total_computation_method
|
|
81
|
+
).freeze
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
DEFAULT_COMMISSION = 10
|
|
85
|
+
|
|
86
|
+
def create_exchange_rates
|
|
87
|
+
PaymentSystem.pluck(:id).each do |foreign_id|
|
|
88
|
+
ExchangeRate
|
|
89
|
+
.create_with(commission: DEFAULT_COMMISSION)
|
|
90
|
+
.find_or_create_by(payment_system_from_id: id, payment_system_to_id: foreign_id)
|
|
91
|
+
|
|
92
|
+
ExchangeRate
|
|
93
|
+
.create_with(commission: DEFAULT_COMMISSION)
|
|
94
|
+
.find_or_create_by(payment_system_from_id: foreign_id, payment_system_to_id: id)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class RateSource < ApplicationRecord
|
|
5
|
+
include Authority::Abilities
|
|
6
|
+
extend CurrencyPairGenerator
|
|
7
|
+
RateNotFound = Class.new StandardError
|
|
8
|
+
|
|
9
|
+
has_many :snapshots, class_name: 'ExternalRateSnapshot'
|
|
10
|
+
has_many :external_rates, foreign_key: :source_id
|
|
11
|
+
|
|
12
|
+
belongs_to :actual_snapshot, class_name: 'ExternalRateSnapshot', optional: true
|
|
13
|
+
|
|
14
|
+
scope :ordered, -> { order :priority }
|
|
15
|
+
scope :enabled, -> { where is_enabled: true }
|
|
16
|
+
|
|
17
|
+
scope :enabled_for_cross_rates, -> { enabled }
|
|
18
|
+
|
|
19
|
+
validates :key, presence: true, uniqueness: true
|
|
20
|
+
|
|
21
|
+
before_create do
|
|
22
|
+
self.priority ||= RateSource.maximum(:priority).to_i + 1
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
before_validation do
|
|
26
|
+
self.title ||= self.class.name.underscore
|
|
27
|
+
self.key ||= self.class.name.underscore
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
delegate :supported_currencies, :available_pairs, to: :class
|
|
31
|
+
|
|
32
|
+
def self.supported_currencies
|
|
33
|
+
raise 'not implemented'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.available_pairs
|
|
37
|
+
generate_pairs_from_currencies supported_currencies
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.get!
|
|
41
|
+
where(type: name).take!
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def find_rate_by_currency_pair!(pair)
|
|
45
|
+
find_rate_by_currency_pair(pair) || raise(RateNotFound, pair)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def find_rate_by_currency_pair(pair)
|
|
49
|
+
actual_rates.find_by_currency_pair pair
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_s
|
|
53
|
+
name
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def actual_rates
|
|
57
|
+
external_rates.where(snapshot_id: actual_snapshot_id)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_s
|
|
61
|
+
title
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def is_currency_supported?(cur)
|
|
65
|
+
cur = Money::Currency.find cur unless cur.is_a? Money::Currency
|
|
66
|
+
supported_currencies.include? cur
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def validate_currency!(*curs)
|
|
72
|
+
curs.each do |cur|
|
|
73
|
+
raise "Источник #{self} не поддерживает валюту #{cur}" unless is_currency_supported? cur
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gera
|
|
4
|
+
class RateSourceAuto < RateSource
|
|
5
|
+
def build_currency_rate(pair)
|
|
6
|
+
build_same(pair) ||
|
|
7
|
+
build_from_source(manual, pair) ||
|
|
8
|
+
build_from_source(cbr, pair) ||
|
|
9
|
+
build_from_source(exmo, pair) ||
|
|
10
|
+
build_cross(pair)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def build_from_source(source, pair, allow_inverse = true)
|
|
16
|
+
source.build_currency_rate pair, allow_inverse
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def build_same(pair)
|
|
20
|
+
return unless pair.same?
|
|
21
|
+
|
|
22
|
+
CurrencyRate.new currency_pair: pair, rate_value: 1, mode: :same
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|